Skip to content

Commit 1244b42

Browse files
authored
Make DurableAIAgentContext delegate to the underlying DurableOrchestrationContext automatically (#10)
1 parent b9711b3 commit 1244b42

File tree

2 files changed

+380
-11
lines changed

2 files changed

+380
-11
lines changed

azure/durable_functions/openai_agents/context.py

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Any, Callable, Optional
1+
from typing import Any, Callable, Optional, TYPE_CHECKING
22

33
from azure.durable_functions.models.DurableOrchestrationContext import (
44
DurableOrchestrationContext,
@@ -11,8 +11,39 @@
1111
from .task_tracker import TaskTracker
1212

1313

14-
class DurableAIAgentContext:
15-
"""Context for AI agents running in Azure Durable Functions orchestration."""
14+
if TYPE_CHECKING:
15+
# At type-check time we want all members / signatures for IDE & linters.
16+
_BaseDurableContext = DurableOrchestrationContext
17+
else:
18+
class _BaseDurableContext: # lightweight runtime stub
19+
"""Runtime stub base class for delegation; real context is wrapped.
20+
21+
At runtime we avoid inheriting from DurableOrchestrationContext so that
22+
attribute lookups for its members are delegated via __getattr__ to the
23+
wrapped ``_context`` instance.
24+
"""
25+
26+
__slots__ = ()
27+
28+
29+
class DurableAIAgentContext(_BaseDurableContext):
30+
"""Context for AI agents running in Azure Durable Functions orchestration.
31+
32+
Design
33+
------
34+
* Static analysis / IDEs: Appears to subclass ``DurableOrchestrationContext`` so
35+
you get autocompletion and type hints (under TYPE_CHECKING branch).
36+
* Runtime: Inherits from a trivial stub. All durable orchestration operations
37+
are delegated to the real ``DurableOrchestrationContext`` instance provided
38+
as ``context`` and stored in ``_context``.
39+
40+
Consequences
41+
------------
42+
* ``isinstance(DurableAIAgentContext, DurableOrchestrationContext)`` is **False** at
43+
runtime (expected).
44+
* Delegation via ``__getattr__`` works for every member of the real context.
45+
* No reliance on internal initialization side-effects of the durable SDK.
46+
"""
1647

1748
def __init__(
1849
self,
@@ -38,14 +69,6 @@ def call_activity_with_retry(
3869
self._task_tracker.record_activity_call()
3970
return task
4071

41-
def set_custom_status(self, status: str):
42-
"""Set custom status for the orchestration."""
43-
self._context.set_custom_status(status)
44-
45-
def wait_for_external_event(self, event_name: str):
46-
"""Wait for an external event in the orchestration."""
47-
return self._context.wait_for_external_event(event_name)
48-
4972
def create_activity_tool(
5073
self,
5174
activity_func: Callable,
@@ -101,3 +124,14 @@ async def run_activity(ctx: RunContextWrapper[Any], input: str) -> Any:
101124
on_invoke_tool=run_activity,
102125
strict_json_schema=True,
103126
)
127+
128+
def __getattr__(self, name):
129+
"""Delegate missing attributes to the underlying DurableOrchestrationContext."""
130+
try:
131+
return getattr(self._context, name)
132+
except AttributeError:
133+
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")
134+
135+
def __dir__(self):
136+
"""Improve introspection and tab-completion by including delegated attributes."""
137+
return sorted(set(dir(type(self)) + list(self.__dict__) + dir(self._context)))

0 commit comments

Comments
 (0)