azure-functions-durable 1.3.2__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.
- azure/durable_functions/__init__.py +8 -0
- azure/durable_functions/decorators/durable_app.py +64 -1
- azure/durable_functions/models/DurableOrchestrationContext.py +24 -0
- azure/durable_functions/openai_agents/__init__.py +13 -0
- azure/durable_functions/openai_agents/context.py +194 -0
- azure/durable_functions/openai_agents/event_loop.py +17 -0
- azure/durable_functions/openai_agents/exceptions.py +11 -0
- azure/durable_functions/openai_agents/handoffs.py +67 -0
- azure/durable_functions/openai_agents/model_invocation_activity.py +268 -0
- azure/durable_functions/openai_agents/orchestrator_generator.py +67 -0
- azure/durable_functions/openai_agents/runner.py +103 -0
- azure/durable_functions/openai_agents/task_tracker.py +171 -0
- azure/durable_functions/openai_agents/tools.py +148 -0
- azure/durable_functions/openai_agents/usage_telemetry.py +69 -0
- {azure_functions_durable-1.3.2.dist-info → azure_functions_durable-1.4.0rc2.dist-info}/METADATA +7 -2
- {azure_functions_durable-1.3.2.dist-info → azure_functions_durable-1.4.0rc2.dist-info}/RECORD +26 -9
- tests/models/test_DurableOrchestrationContext.py +8 -0
- tests/openai_agents/__init__.py +0 -0
- tests/openai_agents/test_context.py +466 -0
- tests/openai_agents/test_task_tracker.py +290 -0
- tests/openai_agents/test_usage_telemetry.py +99 -0
- tests/orchestrator/openai_agents/__init__.py +0 -0
- tests/orchestrator/openai_agents/test_openai_agents.py +316 -0
- {azure_functions_durable-1.3.2.dist-info → azure_functions_durable-1.4.0rc2.dist-info}/LICENSE +0 -0
- {azure_functions_durable-1.3.2.dist-info → azure_functions_durable-1.4.0rc2.dist-info}/WHEEL +0 -0
- {azure_functions_durable-1.3.2.dist-info → azure_functions_durable-1.4.0rc2.dist-info}/top_level.txt +0 -0
|
@@ -79,3 +79,11 @@ try:
|
|
|
79
79
|
__all__.append('Blueprint')
|
|
80
80
|
except ModuleNotFoundError:
|
|
81
81
|
pass
|
|
82
|
+
|
|
83
|
+
# Import OpenAI Agents integration (optional dependency)
|
|
84
|
+
try:
|
|
85
|
+
from . import openai_agents # noqa
|
|
86
|
+
__all__.append('openai_agents')
|
|
87
|
+
except ImportError:
|
|
88
|
+
# OpenAI agents integration requires additional dependencies
|
|
89
|
+
pass
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
2
2
|
# Licensed under the MIT License.
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
from azure.durable_functions.models.RetryOptions import RetryOptions
|
|
5
|
+
from .metadata import OrchestrationTrigger, ActivityTrigger, EntityTrigger, \
|
|
4
6
|
DurableClient
|
|
5
7
|
from typing import Callable, Optional
|
|
6
8
|
from azure.durable_functions.entity import Entity
|
|
@@ -45,6 +47,7 @@ class Blueprint(TriggerApi, BindingApi, SettingsApi):
|
|
|
45
47
|
New instance of a Durable Functions app
|
|
46
48
|
"""
|
|
47
49
|
super().__init__(auth_level=http_auth_level)
|
|
50
|
+
self._is_durable_openai_agent_setup = False
|
|
48
51
|
|
|
49
52
|
def _configure_entity_callable(self, wrap) -> Callable:
|
|
50
53
|
"""Obtain decorator to construct an Entity class from a user-defined Function.
|
|
@@ -250,6 +253,66 @@ class Blueprint(TriggerApi, BindingApi, SettingsApi):
|
|
|
250
253
|
|
|
251
254
|
return wrap
|
|
252
255
|
|
|
256
|
+
def _create_invoke_model_activity(self, model_provider, activity_name):
|
|
257
|
+
"""Create and register the invoke_model_activity function with the provided FunctionApp."""
|
|
258
|
+
|
|
259
|
+
@self.activity_trigger(input_name="input", activity=activity_name)
|
|
260
|
+
async def run_model_activity(input: str):
|
|
261
|
+
from azure.durable_functions.openai_agents.orchestrator_generator\
|
|
262
|
+
import durable_openai_agent_activity
|
|
263
|
+
|
|
264
|
+
return await durable_openai_agent_activity(input, model_provider)
|
|
265
|
+
|
|
266
|
+
return run_model_activity
|
|
267
|
+
|
|
268
|
+
def _setup_durable_openai_agent(self, model_provider, activity_name):
|
|
269
|
+
if not self._is_durable_openai_agent_setup:
|
|
270
|
+
self._create_invoke_model_activity(model_provider, activity_name)
|
|
271
|
+
self._is_durable_openai_agent_setup = True
|
|
272
|
+
|
|
273
|
+
def durable_openai_agent_orchestrator(
|
|
274
|
+
self,
|
|
275
|
+
_func=None,
|
|
276
|
+
*,
|
|
277
|
+
model_provider=None,
|
|
278
|
+
model_retry_options: Optional[RetryOptions] = RetryOptions(
|
|
279
|
+
first_retry_interval_in_milliseconds=2000, max_number_of_attempts=5
|
|
280
|
+
),
|
|
281
|
+
):
|
|
282
|
+
"""Decorate Azure Durable Functions orchestrators that use OpenAI Agents.
|
|
283
|
+
|
|
284
|
+
Parameters
|
|
285
|
+
----------
|
|
286
|
+
model_provider: Optional[ModelProvider]
|
|
287
|
+
Use a non-default ModelProvider instead of the default OpenAIProvider,
|
|
288
|
+
such as when testing.
|
|
289
|
+
"""
|
|
290
|
+
from agents import ModelProvider
|
|
291
|
+
from azure.durable_functions.openai_agents.orchestrator_generator\
|
|
292
|
+
import durable_openai_agent_orchestrator_generator
|
|
293
|
+
|
|
294
|
+
if model_provider is not None and type(model_provider) is not ModelProvider:
|
|
295
|
+
raise TypeError("Provided model provider must be of type ModelProvider")
|
|
296
|
+
|
|
297
|
+
activity_name = "run_model"
|
|
298
|
+
|
|
299
|
+
self._setup_durable_openai_agent(model_provider, activity_name)
|
|
300
|
+
|
|
301
|
+
def generator_wrapper_wrapper(func):
|
|
302
|
+
|
|
303
|
+
@wraps(func)
|
|
304
|
+
def generator_wrapper(context):
|
|
305
|
+
return durable_openai_agent_orchestrator_generator(
|
|
306
|
+
func, context, model_retry_options, activity_name
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
return generator_wrapper
|
|
310
|
+
|
|
311
|
+
if _func is None:
|
|
312
|
+
return generator_wrapper_wrapper
|
|
313
|
+
else:
|
|
314
|
+
return generator_wrapper_wrapper(_func)
|
|
315
|
+
|
|
253
316
|
|
|
254
317
|
class DFApp(Blueprint, FunctionRegister):
|
|
255
318
|
"""Durable Functions (DF) app.
|
|
@@ -100,6 +100,8 @@ class DurableOrchestrationContext:
|
|
|
100
100
|
self.open_tasks = defaultdict(list)
|
|
101
101
|
self.deferred_tasks: Dict[Union[int, str], Tuple[HistoryEvent, bool, str]] = {}
|
|
102
102
|
|
|
103
|
+
self._version: str = self._extract_version_from_history(self._histories)
|
|
104
|
+
|
|
103
105
|
@classmethod
|
|
104
106
|
def from_json(cls, json_string: str):
|
|
105
107
|
"""Convert the value passed into a new instance of the class.
|
|
@@ -752,3 +754,25 @@ class DurableOrchestrationContext:
|
|
|
752
754
|
"https://github.com/Azure/azure-functions-durable-python.\n"\
|
|
753
755
|
"Error trace: " + e.message
|
|
754
756
|
raise e
|
|
757
|
+
|
|
758
|
+
@property
|
|
759
|
+
def version(self) -> Optional[str]:
|
|
760
|
+
"""Get the version assigned to the orchestration instance on creation.
|
|
761
|
+
|
|
762
|
+
Returns
|
|
763
|
+
-------
|
|
764
|
+
Optional[str]
|
|
765
|
+
The version assigned to the orchestration instance on creation, or None if not found.
|
|
766
|
+
"""
|
|
767
|
+
return self._version
|
|
768
|
+
|
|
769
|
+
@staticmethod
|
|
770
|
+
def _extract_version_from_history(history_events: List[HistoryEvent]) -> Optional[str]:
|
|
771
|
+
"""Extract the version from the execution started event in history.
|
|
772
|
+
|
|
773
|
+
Returns None if not found.
|
|
774
|
+
"""
|
|
775
|
+
for event in history_events:
|
|
776
|
+
if event.event_type == HistoryEventType.EXECUTION_STARTED:
|
|
777
|
+
return event.Version
|
|
778
|
+
return None
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
"""OpenAI Agents integration for Durable Functions.
|
|
4
|
+
|
|
5
|
+
This module provides decorators and utilities to integrate OpenAI Agents
|
|
6
|
+
with Durable Functions orchestration patterns.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from .context import DurableAIAgentContext
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
'DurableAIAgentContext',
|
|
13
|
+
]
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
import json
|
|
4
|
+
from typing import Any, Callable, Optional, TYPE_CHECKING, Union
|
|
5
|
+
|
|
6
|
+
from azure.durable_functions.models.DurableOrchestrationContext import (
|
|
7
|
+
DurableOrchestrationContext,
|
|
8
|
+
)
|
|
9
|
+
from azure.durable_functions.models.RetryOptions import RetryOptions
|
|
10
|
+
|
|
11
|
+
from agents import RunContextWrapper, Tool
|
|
12
|
+
from agents.function_schema import function_schema
|
|
13
|
+
from agents.tool import FunctionTool
|
|
14
|
+
|
|
15
|
+
from azure.durable_functions.models.Task import TaskBase
|
|
16
|
+
from .task_tracker import TaskTracker
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
# At type-check time we want all members / signatures for IDE & linters.
|
|
21
|
+
_BaseDurableContext = DurableOrchestrationContext
|
|
22
|
+
else:
|
|
23
|
+
class _BaseDurableContext: # lightweight runtime stub
|
|
24
|
+
"""Runtime stub base class for delegation; real context is wrapped.
|
|
25
|
+
|
|
26
|
+
At runtime we avoid inheriting from DurableOrchestrationContext so that
|
|
27
|
+
attribute lookups for its members are delegated via __getattr__ to the
|
|
28
|
+
wrapped ``_context`` instance.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
__slots__ = ()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class DurableAIAgentContext(_BaseDurableContext):
|
|
35
|
+
"""Context for AI agents running in Azure Durable Functions orchestration.
|
|
36
|
+
|
|
37
|
+
Design
|
|
38
|
+
------
|
|
39
|
+
* Static analysis / IDEs: Appears to subclass ``DurableOrchestrationContext`` so
|
|
40
|
+
you get autocompletion and type hints (under TYPE_CHECKING branch).
|
|
41
|
+
* Runtime: Inherits from a trivial stub. All durable orchestration operations
|
|
42
|
+
are delegated to the real ``DurableOrchestrationContext`` instance provided
|
|
43
|
+
as ``context`` and stored in ``_context``.
|
|
44
|
+
|
|
45
|
+
Consequences
|
|
46
|
+
------------
|
|
47
|
+
* ``isinstance(DurableAIAgentContext, DurableOrchestrationContext)`` is **False** at
|
|
48
|
+
runtime (expected).
|
|
49
|
+
* Delegation via ``__getattr__`` works for every member of the real context.
|
|
50
|
+
* No reliance on internal initialization side-effects of the durable SDK.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def __init__(
|
|
54
|
+
self,
|
|
55
|
+
context: DurableOrchestrationContext,
|
|
56
|
+
task_tracker: TaskTracker,
|
|
57
|
+
model_retry_options: Optional[RetryOptions],
|
|
58
|
+
):
|
|
59
|
+
self._context = context
|
|
60
|
+
self._task_tracker = task_tracker
|
|
61
|
+
self._model_retry_options = model_retry_options
|
|
62
|
+
|
|
63
|
+
def call_activity(
|
|
64
|
+
self, name: Union[str, Callable], input_: Optional[Any] = None
|
|
65
|
+
) -> TaskBase:
|
|
66
|
+
"""Schedule an activity for execution.
|
|
67
|
+
|
|
68
|
+
Parameters
|
|
69
|
+
----------
|
|
70
|
+
name: str | Callable
|
|
71
|
+
Either the name of the activity function to call, as a string or,
|
|
72
|
+
in the Python V2 programming model, the activity function itself.
|
|
73
|
+
input_: Optional[Any]
|
|
74
|
+
The JSON-serializable input to pass to the activity function.
|
|
75
|
+
|
|
76
|
+
Returns
|
|
77
|
+
-------
|
|
78
|
+
Task
|
|
79
|
+
A Durable Task that completes when the called activity function completes or fails.
|
|
80
|
+
"""
|
|
81
|
+
task = self._context.call_activity(name, input_)
|
|
82
|
+
self._task_tracker.record_activity_call()
|
|
83
|
+
return task
|
|
84
|
+
|
|
85
|
+
def call_activity_with_retry(
|
|
86
|
+
self,
|
|
87
|
+
name: Union[str, Callable],
|
|
88
|
+
retry_options: RetryOptions,
|
|
89
|
+
input_: Optional[Any] = None,
|
|
90
|
+
) -> TaskBase:
|
|
91
|
+
"""Schedule an activity for execution with retry options.
|
|
92
|
+
|
|
93
|
+
Parameters
|
|
94
|
+
----------
|
|
95
|
+
name: str | Callable
|
|
96
|
+
Either the name of the activity function to call, as a string or,
|
|
97
|
+
in the Python V2 programming model, the activity function itself.
|
|
98
|
+
retry_options: RetryOptions
|
|
99
|
+
The retry options for the activity function.
|
|
100
|
+
input_: Optional[Any]
|
|
101
|
+
The JSON-serializable input to pass to the activity function.
|
|
102
|
+
|
|
103
|
+
Returns
|
|
104
|
+
-------
|
|
105
|
+
Task
|
|
106
|
+
A Durable Task that completes when the called activity function completes or
|
|
107
|
+
fails completely.
|
|
108
|
+
"""
|
|
109
|
+
task = self._context.call_activity_with_retry(name, retry_options, input_)
|
|
110
|
+
self._task_tracker.record_activity_call()
|
|
111
|
+
return task
|
|
112
|
+
|
|
113
|
+
def create_activity_tool(
|
|
114
|
+
self,
|
|
115
|
+
activity_func: Callable,
|
|
116
|
+
*,
|
|
117
|
+
description: Optional[str] = None,
|
|
118
|
+
retry_options: Optional[RetryOptions] = RetryOptions(
|
|
119
|
+
first_retry_interval_in_milliseconds=2000, max_number_of_attempts=5
|
|
120
|
+
),
|
|
121
|
+
) -> Tool:
|
|
122
|
+
"""Convert an Azure Durable Functions activity to an OpenAI Agents SDK Tool.
|
|
123
|
+
|
|
124
|
+
Args
|
|
125
|
+
----
|
|
126
|
+
activity_func: The Azure Functions activity function to convert
|
|
127
|
+
description: Optional description override for the tool
|
|
128
|
+
retry_options: The retry options for the activity function
|
|
129
|
+
|
|
130
|
+
Returns
|
|
131
|
+
-------
|
|
132
|
+
Tool: An OpenAI Agents SDK Tool object
|
|
133
|
+
|
|
134
|
+
"""
|
|
135
|
+
if activity_func._function is None:
|
|
136
|
+
raise ValueError("The provided function is not a valid Azure Function.")
|
|
137
|
+
|
|
138
|
+
if (activity_func._function._trigger is not None
|
|
139
|
+
and activity_func._function._trigger.activity is not None):
|
|
140
|
+
activity_name = activity_func._function._trigger.activity
|
|
141
|
+
else:
|
|
142
|
+
activity_name = activity_func._function._name
|
|
143
|
+
|
|
144
|
+
input_name = None
|
|
145
|
+
if (activity_func._function._trigger is not None
|
|
146
|
+
and hasattr(activity_func._function._trigger, 'name')):
|
|
147
|
+
input_name = activity_func._function._trigger.name
|
|
148
|
+
|
|
149
|
+
async def run_activity(ctx: RunContextWrapper[Any], input: str) -> Any:
|
|
150
|
+
# Parse JSON input and extract the named value if input_name is specified
|
|
151
|
+
activity_input = input
|
|
152
|
+
if input_name:
|
|
153
|
+
try:
|
|
154
|
+
parsed_input = json.loads(input)
|
|
155
|
+
if isinstance(parsed_input, dict) and input_name in parsed_input:
|
|
156
|
+
activity_input = parsed_input[input_name]
|
|
157
|
+
# If parsing fails or the named parameter is not found, pass the original input
|
|
158
|
+
except (json.JSONDecodeError, TypeError):
|
|
159
|
+
pass
|
|
160
|
+
|
|
161
|
+
if retry_options:
|
|
162
|
+
result = self._task_tracker.get_activity_call_result_with_retry(
|
|
163
|
+
activity_name, retry_options, activity_input
|
|
164
|
+
)
|
|
165
|
+
else:
|
|
166
|
+
result = self._task_tracker.get_activity_call_result(activity_name, activity_input)
|
|
167
|
+
return result
|
|
168
|
+
|
|
169
|
+
schema = function_schema(
|
|
170
|
+
func=activity_func._function._func,
|
|
171
|
+
docstring_style=None,
|
|
172
|
+
description_override=description,
|
|
173
|
+
use_docstring_info=True,
|
|
174
|
+
strict_json_schema=True,
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
return FunctionTool(
|
|
178
|
+
name=schema.name,
|
|
179
|
+
description=schema.description or "",
|
|
180
|
+
params_json_schema=schema.params_json_schema,
|
|
181
|
+
on_invoke_tool=run_activity,
|
|
182
|
+
strict_json_schema=True,
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
def __getattr__(self, name):
|
|
186
|
+
"""Delegate missing attributes to the underlying DurableOrchestrationContext."""
|
|
187
|
+
try:
|
|
188
|
+
return getattr(self._context, name)
|
|
189
|
+
except AttributeError:
|
|
190
|
+
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")
|
|
191
|
+
|
|
192
|
+
def __dir__(self):
|
|
193
|
+
"""Improve introspection and tab-completion by including delegated attributes."""
|
|
194
|
+
return sorted(set(dir(type(self)) + list(self.__dict__) + dir(self._context)))
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
import asyncio
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def ensure_event_loop():
|
|
7
|
+
"""Ensure an event loop is available for sync execution context.
|
|
8
|
+
|
|
9
|
+
This is necessary when calling Runner.run_sync from Azure Functions
|
|
10
|
+
Durable orchestrators, which run in a synchronous context but need
|
|
11
|
+
an event loop for internal async operations.
|
|
12
|
+
"""
|
|
13
|
+
try:
|
|
14
|
+
asyncio.get_running_loop()
|
|
15
|
+
except RuntimeError:
|
|
16
|
+
loop = asyncio.new_event_loop()
|
|
17
|
+
asyncio.set_event_loop(loop)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
from azure.durable_functions.models.Task import TaskBase
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class YieldException(BaseException):
|
|
7
|
+
"""Exception raised when an orchestrator should yield control."""
|
|
8
|
+
|
|
9
|
+
def __init__(self, task: TaskBase):
|
|
10
|
+
super().__init__("Orchestrator should yield.")
|
|
11
|
+
self.task = task
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
"""Handoff conversion utilities for Azure Durable Functions OpenAI agent operations."""
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from agents import Handoff
|
|
8
|
+
from pydantic import BaseModel
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DurableHandoff(BaseModel):
|
|
12
|
+
"""Serializable representation of a Handoff.
|
|
13
|
+
|
|
14
|
+
Contains only the data needed by the model execution to
|
|
15
|
+
determine what to handoff to, not the actual handoff invocation.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
tool_name: str
|
|
19
|
+
tool_description: str
|
|
20
|
+
input_json_schema: dict[str, Any]
|
|
21
|
+
agent_name: str
|
|
22
|
+
strict_json_schema: bool = True
|
|
23
|
+
|
|
24
|
+
@classmethod
|
|
25
|
+
def from_handoff(cls, handoff: Handoff) -> "DurableHandoff":
|
|
26
|
+
"""Create a DurableHandoff from an OpenAI agent Handoff.
|
|
27
|
+
|
|
28
|
+
This method converts OpenAI agent Handoff instances into serializable
|
|
29
|
+
DurableHandoff objects for use within Azure Durable Functions.
|
|
30
|
+
|
|
31
|
+
Parameters
|
|
32
|
+
----------
|
|
33
|
+
handoff : Handoff
|
|
34
|
+
The OpenAI agent Handoff to convert
|
|
35
|
+
|
|
36
|
+
Returns
|
|
37
|
+
-------
|
|
38
|
+
DurableHandoff
|
|
39
|
+
A serializable handoff representation
|
|
40
|
+
"""
|
|
41
|
+
return cls(
|
|
42
|
+
tool_name=handoff.tool_name,
|
|
43
|
+
tool_description=handoff.tool_description,
|
|
44
|
+
input_json_schema=handoff.input_json_schema,
|
|
45
|
+
agent_name=handoff.agent_name,
|
|
46
|
+
strict_json_schema=handoff.strict_json_schema,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
def to_handoff(self) -> Handoff[Any, Any]:
|
|
50
|
+
"""Create an OpenAI agent Handoff instance from this DurableHandoff.
|
|
51
|
+
|
|
52
|
+
This method converts the serializable DurableHandoff back into an
|
|
53
|
+
OpenAI agent Handoff instance for execution.
|
|
54
|
+
|
|
55
|
+
Returns
|
|
56
|
+
-------
|
|
57
|
+
Handoff
|
|
58
|
+
OpenAI agent Handoff instance
|
|
59
|
+
"""
|
|
60
|
+
return Handoff(
|
|
61
|
+
tool_name=self.tool_name,
|
|
62
|
+
tool_description=self.tool_description,
|
|
63
|
+
input_json_schema=self.input_json_schema,
|
|
64
|
+
agent_name=self.agent_name,
|
|
65
|
+
strict_json_schema=self.strict_json_schema,
|
|
66
|
+
on_invoke_handoff=lambda ctx, input: None,
|
|
67
|
+
)
|