Skip to content

Commit 86db2e1

Browse files
committed
Merge commit 'b9711b357f10e1288f516613986faa6d68e58112' into anatolib/context
2 parents b0d1ea3 + b9711b3 commit 86db2e1

File tree

6 files changed

+65
-17
lines changed

6 files changed

+65
-17
lines changed

azure/durable_functions/decorators/durable_app.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -253,21 +253,21 @@ def decorator():
253253

254254
return wrap
255255

256-
def _create_invoke_model_activity(self, model_provider):
256+
def _create_invoke_model_activity(self, model_provider, activity_name):
257257
"""Create and register the invoke_model_activity function with the provided FunctionApp."""
258258

259-
@self.activity_trigger(input_name="input")
260-
async def invoke_model_activity(input: str):
259+
@self.activity_trigger(input_name="input", activity=activity_name)
260+
async def run_model_activity(input: str):
261261
from azure.durable_functions.openai_agents.orchestrator_generator\
262262
import durable_openai_agent_activity
263263

264264
return await durable_openai_agent_activity(input, model_provider)
265265

266-
return invoke_model_activity
266+
return run_model_activity
267267

268-
def _setup_durable_openai_agent(self, model_provider):
268+
def _setup_durable_openai_agent(self, model_provider, activity_name):
269269
if not self._is_durable_openai_agent_setup:
270-
self._create_invoke_model_activity(model_provider)
270+
self._create_invoke_model_activity(model_provider, activity_name)
271271
self._is_durable_openai_agent_setup = True
272272

273273
def durable_openai_agent_orchestrator(
@@ -294,14 +294,16 @@ def durable_openai_agent_orchestrator(
294294
if model_provider is not None and type(model_provider) is not ModelProvider:
295295
raise TypeError("Provided model provider must be of type ModelProvider")
296296

297-
self._setup_durable_openai_agent(model_provider)
297+
activity_name = "run_model"
298+
299+
self._setup_durable_openai_agent(model_provider, activity_name)
298300

299301
def generator_wrapper_wrapper(func):
300302

301303
@wraps(func)
302304
def generator_wrapper(context):
303305
return durable_openai_agent_orchestrator_generator(
304-
func, context, model_retry_options
306+
func, context, model_retry_options, activity_name
305307
)
306308

307309
return generator_wrapper

azure/durable_functions/openai_agents/context.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def call_activity_with_retry(
6969
self._task_tracker.record_activity_call()
7070
return task
7171

72-
def activity_as_tool(
72+
def create_activity_tool(
7373
self,
7474
activity_func: Callable,
7575
*,
@@ -91,7 +91,14 @@ def activity_as_tool(
9191
Tool: An OpenAI Agents SDK Tool object
9292
9393
"""
94-
activity_name = activity_func._function._name
94+
if activity_func._function is None:
95+
raise ValueError("The provided function is not a valid Azure Function.")
96+
97+
if (activity_func._function._trigger is not None
98+
and activity_func._function._trigger.activity is not None):
99+
activity_name = activity_func._function._trigger.activity
100+
else:
101+
activity_name = activity_func._function._name
95102

96103
async def run_activity(ctx: RunContextWrapper[Any], input: str) -> Any:
97104
if retry_options:
@@ -104,7 +111,6 @@ async def run_activity(ctx: RunContextWrapper[Any], input: str) -> Any:
104111

105112
schema = function_schema(
106113
func=activity_func._function._func,
107-
name_override=activity_name,
108114
docstring_style=None,
109115
description_override=description,
110116
use_docstring_info=True,

azure/durable_functions/openai_agents/model_invocation_activity.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -291,10 +291,12 @@ def __init__(
291291
model_name: Optional[str],
292292
task_tracker: TaskTracker,
293293
retry_options: Optional[RetryOptions],
294+
activity_name: str,
294295
) -> None:
295296
self.model_name = model_name
296297
self.task_tracker = task_tracker
297298
self.retry_options = retry_options
299+
self.activity_name = activity_name
298300

299301
async def get_response(
300302
self,
@@ -382,13 +384,13 @@ def make_tool_info(tool: Tool) -> ToolInput:
382384

383385
if self.retry_options:
384386
response = self.task_tracker.get_activity_call_result_with_retry(
385-
"invoke_model_activity",
387+
self.activity_name,
386388
self.retry_options,
387389
activity_input_json,
388390
)
389391
else:
390392
response = self.task_tracker.get_activity_call_result(
391-
"invoke_model_activity", activity_input_json
393+
self.activity_name, activity_input_json
392394
)
393395

394396
json_response = json.loads(response)

azure/durable_functions/openai_agents/orchestrator_generator.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,16 @@ def durable_openai_agent_orchestrator_generator(
2626
func,
2727
durable_orchestration_context: DurableOrchestrationContext,
2828
model_retry_options: Optional[RetryOptions],
29+
activity_name: str,
2930
):
3031
"""Adapts the synchronous OpenAI Agents function to an Durable orchestrator generator."""
3132
ensure_event_loop()
3233
task_tracker = TaskTracker(durable_orchestration_context)
3334
durable_ai_agent_context = DurableAIAgentContext(
3435
durable_orchestration_context, task_tracker, model_retry_options
3536
)
36-
durable_openai_runner = DurableOpenAIRunner(context=durable_ai_agent_context)
37+
durable_openai_runner = DurableOpenAIRunner(
38+
context=durable_ai_agent_context, activity_name=activity_name)
3739
set_default_agent_runner(durable_openai_runner)
3840

3941
func_with_context = partial(func, durable_ai_agent_context)

azure/durable_functions/openai_agents/runner.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@
2323
class DurableOpenAIRunner:
2424
"""Runner for OpenAI agents using Durable Functions orchestration."""
2525

26-
def __init__(self, context: DurableAIAgentContext) -> None:
26+
def __init__(self, context: DurableAIAgentContext, activity_name: str) -> None:
2727
self._runner = DEFAULT_AGENT_RUNNER or AgentRunner()
2828
self.context = context
29+
self.activity_name = activity_name
2930

3031
def run_sync(
3132
self,
@@ -62,6 +63,7 @@ def run_sync(
6263
model_name=model_name,
6364
task_tracker=self.context._task_tracker,
6465
retry_options=self.context._model_retry_options,
66+
activity_name=self.activity_name,
6567
),
6668
)
6769

tests/orchestrator/openai_agents/test_openai_agents.py

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,26 @@ def openai_agent_use_tool(context):
5151
agent = Agent(
5252
name="Assistant",
5353
instructions="You only respond in haikus.",
54-
tools=[context.activity_as_tool(get_weather, retry_options=None)]
54+
tools=[context.create_activity_tool(get_weather, retry_options=None)]
55+
)
56+
57+
result = Runner.run_sync(agent, "Tell me the weather in Seattle.", )
58+
59+
return result.final_output;
60+
61+
@app.activity_trigger(input_name="city", activity="get_weather_with_explicit_name")
62+
def get_named_weather(city: str) -> Weather:
63+
print("[debug] get_weather called")
64+
return Weather(city=city, temperature_range="14-20C", conditions="Sunny with wind.")
65+
66+
@app.function_name("openai_agent_use_tool_with_explicit_name")
67+
@app.orchestration_trigger(context_name="context")
68+
@app.durable_openai_agent_orchestrator(model_retry_options=None)
69+
def openai_agent_use_tool_with_explicit_name(context):
70+
agent = Agent(
71+
name="Assistant",
72+
instructions="You only respond in haikus.",
73+
tools=[context.create_activity_tool(get_named_weather, retry_options=None)]
5574
)
5675

5776
result = Runner.run_sync(agent, "Tell me the weather in Seattle.", )
@@ -112,7 +131,7 @@ def openai_agent_return_pydantic_model_type(context):
112131

113132
return model
114133

115-
model_activity_name = "invoke_model_activity"
134+
model_activity_name = "run_model"
116135

117136
def base_expected_state(output=None, replay_schema: ReplaySchema = ReplaySchema.V1) -> OrchestratorState:
118137
return OrchestratorState(is_done=False, actions=[], output=output, replay_schema=replay_schema)
@@ -172,6 +191,21 @@ def test_openai_agent_use_tool_activity_start():
172191
assert_valid_schema(result)
173192
assert_orchestration_state_equals(expected, result)
174193

194+
def test_openai_agent_use_explicitly_named_tool_activity_start():
195+
context_builder = ContextBuilder('test_openai_agent_use_tool_start')
196+
add_activity_completed_events(context_builder, 0, '{"output":[{"arguments":"{\\"args\\":\\"Seattle, WA\\"}","call_id":"call_mEdywElQTNpxAdivuEFjO0cT","name":"get_named_weather","type":"function_call","id":"fc_68b9ecc0ff9c819f863d6cf9e0a1b4e101011fd6f5f8c0a6","status":"completed"}],"usage":{"requests":1,"input_tokens":57,"input_tokens_details":{"cached_tokens":0},"output_tokens":17,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":74},"response_id":"resp_68b9ecc092e0819fb79b97c11aacef2001011fd6f5f8c0a6"}')
197+
198+
result = get_orchestration_state_result(
199+
context_builder, openai_agent_use_tool_with_explicit_name, uses_pystein=True)
200+
201+
expected_state = base_expected_state()
202+
add_activity_action(expected_state, "{\"input\":[{\"content\":\"Tell me the weather in Seattle.\",\"role\":\"user\"}],\"model_settings\":{\"temperature\":null,\"top_p\":null,\"frequency_penalty\":null,\"presence_penalty\":null,\"tool_choice\":null,\"parallel_tool_calls\":null,\"truncation\":null,\"max_tokens\":null,\"reasoning\":null,\"metadata\":null,\"store\":null,\"include_usage\":null,\"response_include\":null,\"extra_query\":null,\"extra_body\":null,\"extra_headers\":null,\"extra_args\":null},\"tracing\":0,\"model_name\":null,\"system_instructions\":\"You only respond in haikus.\",\"tools\":[{\"name\":\"get_named_weather\",\"description\":\"\",\"params_json_schema\":{\"properties\":{\"city\":{\"title\":\"City\",\"type\":\"string\"}},\"required\":[\"city\"],\"title\":\"get_named_weather_args\",\"type\":\"object\",\"additionalProperties\":false},\"strict_json_schema\":true}],\"output_schema\":null,\"handoffs\":[],\"previous_response_id\":null,\"prompt\":null}")
203+
add_activity_action(expected_state, "{\"args\":\"Seattle, WA\"}", activity_name="get_weather_with_explicit_name")
204+
expected = expected_state.to_json()
205+
206+
assert_valid_schema(result)
207+
assert_orchestration_state_equals(expected, result)
208+
175209
def test_openai_agent_use_tool_activity_completed():
176210
context_builder = ContextBuilder('test_openai_agent_use_tool_start')
177211
add_activity_completed_events(context_builder, 0, '{"output":[{"arguments":"{\\"args\\":\\"Seattle, WA\\"}","call_id":"call_mEdywElQTNpxAdivuEFjO0cT","name":"get_weather","type":"function_call","id":"fc_68b9ecc0ff9c819f863d6cf9e0a1b4e101011fd6f5f8c0a6","status":"completed"}],"usage":{"requests":1,"input_tokens":57,"input_tokens_details":{"cached_tokens":0},"output_tokens":17,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":74},"response_id":"resp_68b9ecc092e0819fb79b97c11aacef2001011fd6f5f8c0a6"}')

0 commit comments

Comments
 (0)