azure-functions-durable 1.3.3__py3-none-any.whl → 1.4.0rc2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. azure/durable_functions/__init__.py +8 -0
  2. azure/durable_functions/decorators/durable_app.py +64 -1
  3. azure/durable_functions/openai_agents/__init__.py +13 -0
  4. azure/durable_functions/openai_agents/context.py +194 -0
  5. azure/durable_functions/openai_agents/event_loop.py +17 -0
  6. azure/durable_functions/openai_agents/exceptions.py +11 -0
  7. azure/durable_functions/openai_agents/handoffs.py +67 -0
  8. azure/durable_functions/openai_agents/model_invocation_activity.py +268 -0
  9. azure/durable_functions/openai_agents/orchestrator_generator.py +67 -0
  10. azure/durable_functions/openai_agents/runner.py +103 -0
  11. azure/durable_functions/openai_agents/task_tracker.py +171 -0
  12. azure/durable_functions/openai_agents/tools.py +148 -0
  13. azure/durable_functions/openai_agents/usage_telemetry.py +69 -0
  14. {azure_functions_durable-1.3.3.dist-info → azure_functions_durable-1.4.0rc2.dist-info}/METADATA +6 -1
  15. {azure_functions_durable-1.3.3.dist-info → azure_functions_durable-1.4.0rc2.dist-info}/RECORD +24 -7
  16. tests/openai_agents/__init__.py +0 -0
  17. tests/openai_agents/test_context.py +466 -0
  18. tests/openai_agents/test_task_tracker.py +290 -0
  19. tests/openai_agents/test_usage_telemetry.py +99 -0
  20. tests/orchestrator/openai_agents/__init__.py +0 -0
  21. tests/orchestrator/openai_agents/test_openai_agents.py +316 -0
  22. {azure_functions_durable-1.3.3.dist-info → azure_functions_durable-1.4.0rc2.dist-info}/LICENSE +0 -0
  23. {azure_functions_durable-1.3.3.dist-info → azure_functions_durable-1.4.0rc2.dist-info}/WHEEL +0 -0
  24. {azure_functions_durable-1.3.3.dist-info → azure_functions_durable-1.4.0rc2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,67 @@
1
+ # Copyright (c) Microsoft Corporation. All rights reserved.
2
+ # Licensed under the MIT License.
3
+ from functools import partial
4
+ from typing import Optional
5
+ from agents import ModelProvider, ModelResponse
6
+ from agents.run import set_default_agent_runner
7
+ from azure.durable_functions.models.DurableOrchestrationContext import DurableOrchestrationContext
8
+ from azure.durable_functions.models.RetryOptions import RetryOptions
9
+ from .model_invocation_activity import DurableModelActivityInput, ModelInvoker
10
+ from .task_tracker import TaskTracker
11
+ from .runner import DurableOpenAIRunner
12
+ from .context import DurableAIAgentContext
13
+ from .event_loop import ensure_event_loop
14
+ from .usage_telemetry import UsageTelemetry
15
+
16
+
17
+ async def durable_openai_agent_activity(input: str, model_provider: ModelProvider) -> str:
18
+ """Activity logic that handles OpenAI model invocations."""
19
+ activity_input = DurableModelActivityInput.from_json(input)
20
+
21
+ model_invoker = ModelInvoker(model_provider=model_provider)
22
+ result = await model_invoker.invoke_model_activity(activity_input)
23
+
24
+ # Use safe/public Pydantic API when possible. Prefer model_dump_json if result is a BaseModel
25
+ # Otherwise handle common types (str/bytes/dict/list) and fall back to json.dumps.
26
+ import json as _json
27
+
28
+ if hasattr(result, "model_dump_json"):
29
+ # Pydantic v2 BaseModel
30
+ json_str = result.model_dump_json()
31
+ else:
32
+ if isinstance(result, bytes):
33
+ json_str = result.decode()
34
+ elif isinstance(result, str):
35
+ json_str = result
36
+ else:
37
+ # Try the internal serializer as a last resort, but fall back to json.dumps
38
+ try:
39
+ json_bytes = ModelResponse.__pydantic_serializer__.to_json(result)
40
+ json_str = json_bytes.decode()
41
+ except Exception:
42
+ json_str = _json.dumps(result)
43
+
44
+ return json_str
45
+
46
+
47
+ def durable_openai_agent_orchestrator_generator(
48
+ func,
49
+ durable_orchestration_context: DurableOrchestrationContext,
50
+ model_retry_options: Optional[RetryOptions],
51
+ activity_name: str,
52
+ ):
53
+ """Adapts the synchronous OpenAI Agents function to an Durable orchestrator generator."""
54
+ # Log versions the first time this generator is invoked
55
+ UsageTelemetry.log_usage_once()
56
+
57
+ ensure_event_loop()
58
+ task_tracker = TaskTracker(durable_orchestration_context)
59
+ durable_ai_agent_context = DurableAIAgentContext(
60
+ durable_orchestration_context, task_tracker, model_retry_options
61
+ )
62
+ durable_openai_runner = DurableOpenAIRunner(
63
+ context=durable_ai_agent_context, activity_name=activity_name)
64
+ set_default_agent_runner(durable_openai_runner)
65
+
66
+ func_with_context = partial(func, durable_ai_agent_context)
67
+ return task_tracker.execute_orchestrator_function(func_with_context)
@@ -0,0 +1,103 @@
1
+ # Copyright (c) Microsoft Corporation. All rights reserved.
2
+ # Licensed under the MIT License.
3
+ import json
4
+ from dataclasses import replace
5
+ from typing import Any, Union
6
+
7
+ from agents import (
8
+ Agent,
9
+ RunConfig,
10
+ RunResult,
11
+ RunResultStreaming,
12
+ TContext,
13
+ TResponseInputItem,
14
+ )
15
+ from agents.run import DEFAULT_AGENT_RUNNER, DEFAULT_MAX_TURNS, AgentRunner
16
+ from pydantic_core import to_json
17
+
18
+ from .context import DurableAIAgentContext
19
+ from .model_invocation_activity import DurableActivityModel
20
+
21
+
22
+ class DurableOpenAIRunner:
23
+ """Runner for OpenAI agents using Durable Functions orchestration."""
24
+
25
+ def __init__(self, context: DurableAIAgentContext, activity_name: str) -> None:
26
+ self._runner = DEFAULT_AGENT_RUNNER or AgentRunner()
27
+ self._context = context
28
+ self._activity_name = activity_name
29
+
30
+ def _prepare_run_config(
31
+ self,
32
+ starting_agent: Agent[TContext],
33
+ input: Union[str, list[TResponseInputItem]],
34
+ **kwargs: Any,
35
+ ) -> tuple[Union[str, list[TResponseInputItem]], RunConfig, dict[str, Any]]:
36
+ """Prepare and validate the run configuration and parameters for agent execution."""
37
+ # Avoid https://github.com/pydantic/pydantic/issues/9541
38
+ normalized_input = json.loads(to_json(input))
39
+
40
+ run_config = kwargs.get("run_config") or RunConfig()
41
+
42
+ model_name = run_config.model or starting_agent.model
43
+ if model_name and not isinstance(model_name, str):
44
+ raise ValueError(
45
+ "For agent execution in Durable Functions, model name in run_config or "
46
+ "starting_agent must be a string."
47
+ )
48
+
49
+ updated_run_config = replace(
50
+ run_config,
51
+ model=DurableActivityModel(
52
+ model_name=model_name,
53
+ task_tracker=self._context._task_tracker,
54
+ retry_options=self._context._model_retry_options,
55
+ activity_name=self._activity_name,
56
+ ),
57
+ )
58
+
59
+ run_params = {
60
+ "context": kwargs.get("context"),
61
+ "max_turns": kwargs.get("max_turns", DEFAULT_MAX_TURNS),
62
+ "hooks": kwargs.get("hooks"),
63
+ "previous_response_id": kwargs.get("previous_response_id"),
64
+ "session": kwargs.get("session"),
65
+ }
66
+
67
+ return normalized_input, updated_run_config, run_params
68
+
69
+ def run_sync(
70
+ self,
71
+ starting_agent: Agent[TContext],
72
+ input: Union[str, list[TResponseInputItem]],
73
+ **kwargs: Any,
74
+ ) -> RunResult:
75
+ """Run an agent synchronously with the given input and configuration."""
76
+ normalized_input, updated_run_config, run_params = self._prepare_run_config(
77
+ starting_agent, input, **kwargs
78
+ )
79
+
80
+ return self._runner.run_sync(
81
+ starting_agent=starting_agent,
82
+ input=normalized_input,
83
+ run_config=updated_run_config,
84
+ **run_params,
85
+ )
86
+
87
+ def run(
88
+ self,
89
+ starting_agent: Agent[TContext],
90
+ input: Union[str, list[TResponseInputItem]],
91
+ **kwargs: Any,
92
+ ) -> RunResult:
93
+ """Run an agent asynchronously. Not supported in Durable Functions."""
94
+ raise RuntimeError("Durable Functions do not support asynchronous runs.")
95
+
96
+ def run_streamed(
97
+ self,
98
+ starting_agent: Agent[TContext],
99
+ input: Union[str, list[TResponseInputItem]],
100
+ **kwargs: Any,
101
+ ) -> RunResultStreaming:
102
+ """Run an agent with streaming. Not supported in Durable Functions."""
103
+ raise RuntimeError("Durable Functions do not support streaming.")
@@ -0,0 +1,171 @@
1
+ # Copyright (c) Microsoft Corporation. All rights reserved.
2
+ # Licensed under the MIT License.
3
+ import json
4
+ import inspect
5
+ from typing import Any
6
+
7
+ from azure.durable_functions.models.DurableOrchestrationContext import (
8
+ DurableOrchestrationContext,
9
+ )
10
+ from azure.durable_functions.models.history.HistoryEventType import HistoryEventType
11
+ from azure.durable_functions.models.RetryOptions import RetryOptions
12
+
13
+ from .exceptions import YieldException
14
+
15
+
16
+ class TaskTracker:
17
+ """Tracks activity calls and handles task result processing for durable AI agents."""
18
+
19
+ def __init__(self, context: DurableOrchestrationContext):
20
+ self._context = context
21
+ self._activities_called = 0
22
+ self._tasks_to_yield = []
23
+
24
+ def _get_activity_result_or_raise(self, task):
25
+ """Return the activity result if available; otherwise raise ``YieldException`` to defer.
26
+
27
+ The first time an activity is scheduled its result won't yet exist in the
28
+ orchestration history, so we raise ``YieldException`` with the task so the
29
+ orchestrator can yield it. On replay, once the corresponding TASK_COMPLETED
30
+ history event is present, we capture the result and queue the task for a
31
+ later yield (to preserve ordering) while returning the deserialized value.
32
+ """
33
+ self.record_activity_call()
34
+
35
+ histories = self._context.histories
36
+ completed_tasks = [
37
+ entry for entry in histories
38
+ if entry.event_type == HistoryEventType.TASK_COMPLETED
39
+ ]
40
+ if len(completed_tasks) < self._activities_called:
41
+ # Result not yet available in history -> raise to signal a yield now
42
+ raise YieldException(task)
43
+ # Result exists (replay). Queue task to be yielded after returning value.
44
+ #
45
+ # We cannot just yield it now because this method can be called from
46
+ # deeply nested code paths that we don't control (such as the
47
+ # OpenAI Agents SDK internals), and yielding here would lead to
48
+ # unintended behavior. Instead, we queue the task to be yielded
49
+ # later and return the result recorded in the history, so the
50
+ # code invoking this method can continue executing normally.
51
+ self._tasks_to_yield.append(task)
52
+
53
+ result_json = completed_tasks[self._activities_called - 1].Result
54
+ result = json.loads(result_json)
55
+ return result
56
+
57
+ def get_activity_call_result(self, activity_name, input: Any):
58
+ """Call an activity and return its result or raise ``YieldException`` if pending."""
59
+ task = self._context.call_activity(activity_name, input)
60
+ return self._get_activity_result_or_raise(task)
61
+
62
+ def get_activity_call_result_with_retry(
63
+ self, activity_name, retry_options: RetryOptions, input: Any
64
+ ):
65
+ """Call an activity with retry and return its result or raise YieldException if pending."""
66
+ task = self._context.call_activity_with_retry(activity_name, retry_options, input)
67
+ return self._get_activity_result_or_raise(task)
68
+
69
+ def record_activity_call(self):
70
+ """Record that an activity was called."""
71
+ self._activities_called += 1
72
+
73
+ def _yield_and_clear_tasks(self):
74
+ """Yield all accumulated tasks and clear the tasks list."""
75
+ for task in self._tasks_to_yield:
76
+ yield task
77
+ self._tasks_to_yield.clear()
78
+
79
+ def execute_orchestrator_function(self, func):
80
+ """Execute the orchestrator function with comprehensive task and exception handling.
81
+
82
+ The orchestrator function can exhibit any combination of the following behaviors:
83
+ - Execute regular code and return a value or raise an exception
84
+ - Invoke get_activity_call_result or get_activity_call_result_with_retry, which leads to
85
+ either interrupting the orchestrator function immediately (because of YieldException),
86
+ or queueing the task for later yielding while continuing execution
87
+ - Invoke DurableAIAgentContext.call_activity or call_activity_with_retry (which must lead
88
+ to corresponding record_activity_call invocations)
89
+ - Yield tasks (typically produced by DurableAIAgentContext methods like call_activity,
90
+ wait_for_external_event, etc.), which may or may not interrupt orchestrator function
91
+ execution
92
+ - Mix all of the above in any combination
93
+
94
+ This method converts both YieldException and regular yields into a sequence of yields
95
+ preserving the order, while also capturing return values through the generator protocol.
96
+ For example, if the orchestrator function yields task A, then queues task B for yielding,
97
+ then raises YieldException wrapping task C, this method makes sure that the resulting
98
+ sequence of yields is: (A, B, C).
99
+
100
+ Args
101
+ ----
102
+ func: The orchestrator function to execute (generator or regular function)
103
+
104
+ Yields
105
+ ------
106
+ Tasks yielded by the orchestrator function and tasks wrapped in YieldException
107
+
108
+ Returns
109
+ -------
110
+ The return value from the orchestrator function
111
+ """
112
+ if inspect.isgeneratorfunction(func):
113
+ gen = iter(func())
114
+ try:
115
+ # prime the subiterator
116
+ value = next(gen)
117
+ yield from self._yield_and_clear_tasks()
118
+ while True:
119
+ try:
120
+ # send whatever was sent into us down to the subgenerator
121
+ yield from self._yield_and_clear_tasks()
122
+ sent = yield value
123
+ except GeneratorExit:
124
+ # ensure the subgenerator is closed
125
+ if hasattr(gen, "close"):
126
+ gen.close()
127
+ raise
128
+ except BaseException as exc:
129
+ # forward thrown exceptions if possible
130
+ if hasattr(gen, "throw"):
131
+ value = gen.throw(type(exc), exc, exc.__traceback__)
132
+ else:
133
+ raise
134
+ else:
135
+ # normal path: forward .send (or .__next__)
136
+ if hasattr(gen, "send"):
137
+ value = gen.send(sent)
138
+ else:
139
+ value = next(gen)
140
+ except StopIteration as e:
141
+ yield from self._yield_and_clear_tasks()
142
+ return TaskTracker._durable_serializer(e.value)
143
+ except YieldException as e:
144
+ yield from self._yield_and_clear_tasks()
145
+ yield e.task
146
+ else:
147
+ try:
148
+ result = func()
149
+ return TaskTracker._durable_serializer(result)
150
+ except YieldException as e:
151
+ yield from self._yield_and_clear_tasks()
152
+ yield e.task
153
+ finally:
154
+ yield from self._yield_and_clear_tasks()
155
+
156
+ @staticmethod
157
+ def _durable_serializer(obj: Any) -> str:
158
+ # Strings are already "serialized"
159
+ if type(obj) is str:
160
+ return obj
161
+
162
+ # Serialize "Durable" and OpenAI models, and typed dictionaries
163
+ if callable(getattr(obj, "to_json", None)):
164
+ return obj.to_json()
165
+
166
+ # Serialize Pydantic models
167
+ if callable(getattr(obj, "model_dump_json", None)):
168
+ return obj.model_dump_json()
169
+
170
+ # Fallback to default JSON serialization
171
+ return json.dumps(obj)
@@ -0,0 +1,148 @@
1
+ # Copyright (c) Microsoft Corporation. All rights reserved.
2
+ # Licensed under the MIT License.
3
+ """Tool conversion utilities for Azure Durable Functions OpenAI agent operations."""
4
+
5
+ from typing import Any, Union
6
+
7
+ from agents import (
8
+ CodeInterpreterTool,
9
+ FileSearchTool,
10
+ FunctionTool,
11
+ HostedMCPTool,
12
+ ImageGenerationTool,
13
+ Tool,
14
+ UserError,
15
+ WebSearchTool,
16
+ )
17
+ from openai.types.responses.tool_param import Mcp
18
+ from pydantic import BaseModel
19
+
20
+
21
+ # Built-in tool types that can be serialized directly without conversion
22
+ BUILT_IN_TOOL_TYPES = (
23
+ FileSearchTool,
24
+ WebSearchTool,
25
+ ImageGenerationTool,
26
+ CodeInterpreterTool,
27
+ )
28
+
29
+
30
+ class DurableFunctionTool(BaseModel):
31
+ """Serializable representation of a FunctionTool.
32
+
33
+ Contains only the data needed by the model execution to
34
+ determine what tool to call, not the actual tool invocation.
35
+ """
36
+
37
+ name: str
38
+ description: str
39
+ params_json_schema: dict[str, Any]
40
+ strict_json_schema: bool = True
41
+
42
+
43
+ class DurableMCPToolConfig(BaseModel):
44
+ """Serializable representation of a HostedMCPTool.
45
+
46
+ Contains only the data needed by the model execution to
47
+ determine what tool to call, not the actual tool invocation.
48
+ """
49
+
50
+ tool_config: Mcp
51
+
52
+
53
+ DurableTool = Union[
54
+ DurableFunctionTool,
55
+ FileSearchTool,
56
+ WebSearchTool,
57
+ ImageGenerationTool,
58
+ CodeInterpreterTool,
59
+ DurableMCPToolConfig,
60
+ ]
61
+
62
+
63
+ def create_tool_from_durable_tool(
64
+ durable_tool: DurableTool,
65
+ ) -> Tool:
66
+ """Convert a DurableTool to an OpenAI agent Tool for execution.
67
+
68
+ This function transforms Durable Functions tool definitions into actual
69
+ OpenAI agent Tool instances that can be used during model execution.
70
+
71
+ Parameters
72
+ ----------
73
+ durable_tool : DurableTool
74
+ The Durable tool definition to convert
75
+
76
+ Returns
77
+ -------
78
+ Tool
79
+ An OpenAI agent Tool instance ready for execution
80
+
81
+ Raises
82
+ ------
83
+ UserError
84
+ If the tool type is not supported
85
+ """
86
+ # Built-in tools that don't need conversion
87
+ if isinstance(durable_tool, BUILT_IN_TOOL_TYPES):
88
+ return durable_tool
89
+
90
+ # Convert Durable MCP tool configuration to HostedMCPTool
91
+ if isinstance(durable_tool, DurableMCPToolConfig):
92
+ return HostedMCPTool(
93
+ tool_config=durable_tool.tool_config,
94
+ )
95
+
96
+ # Convert Durable function tool to FunctionTool
97
+ if isinstance(durable_tool, DurableFunctionTool):
98
+ return FunctionTool(
99
+ name=durable_tool.name,
100
+ description=durable_tool.description,
101
+ params_json_schema=durable_tool.params_json_schema,
102
+ on_invoke_tool=lambda ctx, input: "",
103
+ strict_json_schema=durable_tool.strict_json_schema,
104
+ )
105
+
106
+ raise UserError(f"Unsupported tool type: {durable_tool}")
107
+
108
+
109
+ def convert_tool_to_durable_tool(tool: Tool) -> DurableTool:
110
+ """Convert an OpenAI agent Tool to a DurableTool for serialization.
111
+
112
+ This function transforms OpenAI agent Tool instances into Durable Functions
113
+ tool definitions that can be serialized and passed to activities.
114
+
115
+ Parameters
116
+ ----------
117
+ tool : Tool
118
+ The OpenAI agent Tool to convert
119
+
120
+ Returns
121
+ -------
122
+ DurableTool
123
+ A serializable tool definition
124
+
125
+ Raises
126
+ ------
127
+ ValueError
128
+ If the tool type is not supported for conversion
129
+ """
130
+ # Built-in tools that can be serialized directly
131
+ if isinstance(tool, BUILT_IN_TOOL_TYPES):
132
+ return tool
133
+
134
+ # Convert HostedMCPTool to Durable MCP configuration
135
+ elif isinstance(tool, HostedMCPTool):
136
+ return DurableMCPToolConfig(tool_config=tool.tool_config)
137
+
138
+ # Convert FunctionTool to Durable function tool
139
+ elif isinstance(tool, FunctionTool):
140
+ return DurableFunctionTool(
141
+ name=tool.name,
142
+ description=tool.description,
143
+ params_json_schema=tool.params_json_schema,
144
+ strict_json_schema=tool.strict_json_schema,
145
+ )
146
+
147
+ else:
148
+ raise ValueError(f"Unsupported tool type for Durable Functions: {type(tool).__name__}")
@@ -0,0 +1,69 @@
1
+ # Copyright (c) Microsoft Corporation. All rights reserved.
2
+ # Licensed under the MIT License.
3
+
4
+
5
+ class UsageTelemetry:
6
+ """Handles telemetry logging for OpenAI Agents SDK integration usage."""
7
+
8
+ # Class-level flag to ensure logging happens only once across all instances
9
+ _usage_logged = False
10
+
11
+ @classmethod
12
+ def log_usage_once(cls):
13
+ """Log OpenAI Agents SDK integration usage exactly once.
14
+
15
+ Fails gracefully if metadata cannot be retrieved.
16
+ """
17
+ if cls._usage_logged:
18
+ return
19
+
20
+ # NOTE: Any log line beginning with the special prefix defined below will be
21
+ # captured by the Azure Functions host as a Language Worker console log and
22
+ # forwarded to internal telemetry pipelines.
23
+ # Do not change this constant value without coordinating with the Functions
24
+ # host team.
25
+ LANGUAGE_WORKER_CONSOLE_LOG_PREFIX = "LanguageWorkerConsoleLog"
26
+
27
+ package_versions = cls._collect_openai_agent_package_versions()
28
+ msg = (
29
+ f"{LANGUAGE_WORKER_CONSOLE_LOG_PREFIX}" # Prefix captured by Azure Functions host
30
+ "Detected OpenAI Agents SDK integration with Durable Functions. "
31
+ f"Package versions: {package_versions}"
32
+ )
33
+ print(msg)
34
+
35
+ cls._usage_logged = True
36
+
37
+ @classmethod
38
+ def _collect_openai_agent_package_versions(cls) -> str:
39
+ """Collect versions of relevant packages for telemetry logging.
40
+
41
+ Returns
42
+ -------
43
+ str
44
+ Comma-separated list of name=version entries or "(unavailable)" if
45
+ versions could not be determined.
46
+ """
47
+ try:
48
+ try:
49
+ from importlib import metadata # Python 3.8+
50
+ except ImportError: # pragma: no cover - legacy fallback
51
+ import importlib_metadata as metadata # type: ignore
52
+
53
+ package_names = [
54
+ "azure-functions-durable",
55
+ "openai",
56
+ "openai-agents",
57
+ ]
58
+
59
+ versions = []
60
+ for package_name in package_names:
61
+ try:
62
+ ver = metadata.version(package_name)
63
+ versions.append(f"{package_name}={ver}")
64
+ except Exception: # noqa: BLE001 - swallow and continue
65
+ versions.append(f"{package_name}=(not installed)")
66
+
67
+ return ", ".join(versions) if versions else "(unavailable)"
68
+ except Exception: # noqa: BLE001 - never let version gathering break user code
69
+ return "(unavailable)"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: azure-functions-durable
3
- Version: 1.3.3
3
+ Version: 1.4.0rc2
4
4
  Summary: Durable Functions For Python
5
5
  Home-page: https://github.com/Azure/azure-functions-durable-python
6
6
  Author: Azure Functions team at Microsoft Corp.
@@ -58,4 +58,9 @@ Follow these instructions to get started with Durable Functions in Python:
58
58
 
59
59
  * Python Durable Functions requires [Azure Functions Core Tools](https://docs.microsoft.com/en-us/azure/azure-functions/functions-run-local) version 3.0.2630 or higher.
60
60
 
61
+ ## Durable OpenAI Agents (Preview)
62
+
63
+ Build resilient, stateful AI agents backed by Durable Functions orchestration—see the full documentation at [docs/openai_agents/README.md](docs/openai_agents/README.md).
64
+
65
+
61
66
 
@@ -1,9 +1,9 @@
1
- azure/durable_functions/__init__.py,sha256=Syz9yrT8sHREVyxqvwjg8_c_h8yMTyuBHcDMsuQs1YM,2910
1
+ azure/durable_functions/__init__.py,sha256=aTQRMnMb3zT3w28yEbYsEl-_f3x2Rf-kKp91E9nE8nk,3150
2
2
  azure/durable_functions/constants.py,sha256=JtknDhaVihMeo-ygY9QNofiO2KEqnQvopdfZ6Qatnik,414
3
3
  azure/durable_functions/entity.py,sha256=mUUzb1BZiDrUJjvxOTlnVURnKPyDGPJ3mXXMN0DKT7M,4649
4
4
  azure/durable_functions/orchestrator.py,sha256=SZni90Aweq0OZykHyMblfJpUndJ2woJmySarcsDiIK4,2554
5
5
  azure/durable_functions/decorators/__init__.py,sha256=wEubgP2rUUISwidZWgKx6mmzEeGKsSnpGmetjIUi1nw,150
6
- azure/durable_functions/decorators/durable_app.py,sha256=8K4gevZVYcDvifxXVuvTt8YzF8jx63c1PwdBBk1Vt0g,10114
6
+ azure/durable_functions/decorators/durable_app.py,sha256=5ajbhMPKvZKZhYLCxsEn8J4sZO6Pql9j0nOTCxDaMuY,12599
7
7
  azure/durable_functions/decorators/metadata.py,sha256=p91rdCe6OSRYJaKAXnrfR0QCV3PoHK7aGy1m6WAnPIE,2828
8
8
  azure/durable_functions/models/DurableEntityContext.py,sha256=cyZmjjZu18oV9S4A2NpnXfjd1JQxPxp9EMmAR424UK0,5830
9
9
  azure/durable_functions/models/DurableHttpRequest.py,sha256=a5kgRdg4eA0sgyDcpmQWc0dbwP-o3BwWW2Ive0BYO_Q,2021
@@ -53,6 +53,17 @@ azure/durable_functions/models/utils/__init__.py,sha256=dQ6-HRUPsCtDIqGjRJ3TA6NX
53
53
  azure/durable_functions/models/utils/entity_utils.py,sha256=TqNTtRC8VuKFtqWLq9oEAloioV-FyinjgRYVKkCldHo,2881
54
54
  azure/durable_functions/models/utils/http_utils.py,sha256=AoCWjCapd_984J_4296iJ8cNJWEG8GIdhRttBPt0HnA,2551
55
55
  azure/durable_functions/models/utils/json_utils.py,sha256=zUn62pm3dQw054ZlK7F4uRP-UELjQC8EmZBU1WncHMg,3811
56
+ azure/durable_functions/openai_agents/__init__.py,sha256=pAUkXR5ctS0leHiR0IwBC1aHurzOn70wasL-LDRcRnQ,374
57
+ azure/durable_functions/openai_agents/context.py,sha256=tShhQmlMxvOGHvYu55e10xPp2mVHmeIUf37yUzzjE50,7551
58
+ azure/durable_functions/openai_agents/event_loop.py,sha256=JxezIdw1TL-R77ITlarZZvk3F1IMXHqGUAuCgxKX5Wk,571
59
+ azure/durable_functions/openai_agents/exceptions.py,sha256=AmbFAxpkrQ-LiU_LaU7-aWpb6dR-ETMs_XWkoCfZIoQ,394
60
+ azure/durable_functions/openai_agents/handoffs.py,sha256=R8fuQ-FlZ3DQw1jI0WwU8gWwTlWdRLWW2EVrwsfDEuU,2195
61
+ azure/durable_functions/openai_agents/model_invocation_activity.py,sha256=P94iMKVq9qfZyC_fUQWXb-tuOhvANaGhGvqVwUGfFVk,9783
62
+ azure/durable_functions/openai_agents/orchestrator_generator.py,sha256=dvm5e2pk-wtpTog0XZ4iMy1eskLOgwHcMwswiZ3zCA8,2898
63
+ azure/durable_functions/openai_agents/runner.py,sha256=qfsX52zaTDJpuU3_hxgkrEUtoHQ3GTMm5VYSgUXXsjA,3726
64
+ azure/durable_functions/openai_agents/task_tracker.py,sha256=dBGIGlkIdbwxH2OJ3XJZBFazzC32JZ9hWpN4isxvYLQ,7749
65
+ azure/durable_functions/openai_agents/tools.py,sha256=YahQOV1Duv0217tEwG6SX8gLLTvkQNLFi9E_6KUOjvI,4229
66
+ azure/durable_functions/openai_agents/usage_telemetry.py,sha256=U4nqLJIv_vzxT2Y5FIUi9NkvCyHex1OY4eWY8flydNs,2646
56
67
  azure/durable_functions/testing/OrchestratorGeneratorWrapper.py,sha256=cjh-HAq5rVNCoR0pIbfGrqy6cKSf4S1KMQxrBMWU1-s,1728
57
68
  azure/durable_functions/testing/__init__.py,sha256=NLbltPtoPXK-0iMTwcKTKPjQlAWrEq55oDYmrhYz6vg,189
58
69
  tests/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -65,6 +76,10 @@ tests/models/test_DurableOrchestrationStatus.py,sha256=fnUZxrHGy771OoaD5TInELhaG
65
76
  tests/models/test_OrchestrationState.py,sha256=L-k8ScrqoDIZEqIUORbxXA7yCuMbVAUPr-7VmyuQkUc,1272
66
77
  tests/models/test_RpcManagementOptions.py,sha256=hvDzlJED8egJloju5nFvKYusgwLgy-o_avJAY6uzfdg,3190
67
78
  tests/models/test_TokenSource.py,sha256=wlRn-RPM72U6Ose4sa3Yvu2ng1VbAopDIbea90CYDjk,589
79
+ tests/openai_agents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
80
+ tests/openai_agents/test_context.py,sha256=r5nGikUU2j23TmyknruR1ntP4UvxyvYrWil9Ap11qs4,21861
81
+ tests/openai_agents/test_task_tracker.py,sha256=6vjalKmCb-CjpJysx7D0s_3CZaOPycE76TQpcX0ludU,13263
82
+ tests/openai_agents/test_usage_telemetry.py,sha256=gB8efDTZhO9UvCwRTqmCELcgmtwbhSoA6_1Hy7kPCDQ,4825
68
83
  tests/orchestrator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
69
84
  tests/orchestrator/orchestrator_test_utils.py,sha256=ldgQGGuOVALcI98enRU08nF_VJd5ypJl-hVG-q0kH1o,5359
70
85
  tests/orchestrator/test_call_http.py,sha256=CvemeCayrQLjmjl3lpB1fk_CpV-DRPgfxLgD4iNgStQ,9103
@@ -83,6 +98,8 @@ tests/orchestrator/test_sub_orchestrator.py,sha256=QUb5Q3nLcCdhFwGaEQlYdoNQLUvI8
83
98
  tests/orchestrator/test_sub_orchestrator_with_retry.py,sha256=bfufQnteEZ9i3q7wzCvd8EwFmaOh89KPOpTPSJvlP1I,6040
84
99
  tests/orchestrator/test_task_any.py,sha256=9PQalbQW-Qx7_iO4Yjl1MR2hhsBjst6k00_4veIt97g,2695
85
100
  tests/orchestrator/models/OrchestrationInstance.py,sha256=CQ3qyNumjksuFNMujbESsvjadntP3b9VtTpEuI4hzaE,491
101
+ tests/orchestrator/openai_agents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
102
+ tests/orchestrator/openai_agents/test_openai_agents.py,sha256=qa8DLkJ1IZV9C5kP2mnFfchriv4ppl1nIu9J_3rNlhA,24221
86
103
  tests/orchestrator/schemas/OrchetrationStateSchema.py,sha256=EyTsDUZ3K-9mVljLIlg8f_h2zsK228E0PMvvtyEWw24,2718
87
104
  tests/tasks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
88
105
  tests/tasks/tasks_test_utils.py,sha256=Ymc5GESJpzybBq3n2mT4IABDRNTVCerAZrlMilv0Pdk,737
@@ -96,8 +113,8 @@ tests/test_utils/json_utils.py,sha256=B0q3COMya7TGxbH-7sD_0ypWDSuaF4fpD4QV_oJPgG
96
113
  tests/test_utils/testClasses.py,sha256=U_u5qKxC9U81SzjLo7ejjPjEn_cE5qjaqoq8edGD6l8,1521
97
114
  tests/utils/__init__.py,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
98
115
  tests/utils/test_entity_utils.py,sha256=kdk5_DV_-bFu_5q2mw9o1yjyzh8Lcxv1jo1Q7is_ukA,748
99
- azure_functions_durable-1.3.3.dist-info/LICENSE,sha256=-VS-Izmxdykuae1Xc4vHtVUx02rNQi6SSQlONvvuYeQ,1090
100
- azure_functions_durable-1.3.3.dist-info/METADATA,sha256=3chTZDt7a-uj_TSYe2X48a03t5K7A7Ieyp1coS3H86c,3524
101
- azure_functions_durable-1.3.3.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
102
- azure_functions_durable-1.3.3.dist-info/top_level.txt,sha256=h-L8XDVPJ9YzBbHlPvM7FVo1cqNGToNK9ix99ySGOUY,12
103
- azure_functions_durable-1.3.3.dist-info/RECORD,,
116
+ azure_functions_durable-1.4.0rc2.dist-info/LICENSE,sha256=-VS-Izmxdykuae1Xc4vHtVUx02rNQi6SSQlONvvuYeQ,1090
117
+ azure_functions_durable-1.4.0rc2.dist-info/METADATA,sha256=HOJS4Ub2kDvHZtLcJ1XLNp6P7-yqLO1tDBqGgKpJrHM,3742
118
+ azure_functions_durable-1.4.0rc2.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
119
+ azure_functions_durable-1.4.0rc2.dist-info/top_level.txt,sha256=h-L8XDVPJ9YzBbHlPvM7FVo1cqNGToNK9ix99ySGOUY,12
120
+ azure_functions_durable-1.4.0rc2.dist-info/RECORD,,
File without changes