azure-functions-durable 1.3.3__py3-none-any.whl → 1.4.0__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/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.3.dist-info → azure_functions_durable-1.4.0.dist-info}/METADATA +6 -1
- {azure_functions_durable-1.3.3.dist-info → azure_functions_durable-1.4.0.dist-info}/RECORD +24 -7
- 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.3.dist-info → azure_functions_durable-1.4.0.dist-info}/LICENSE +0 -0
- {azure_functions_durable-1.3.3.dist-info → azure_functions_durable-1.4.0.dist-info}/WHEEL +0 -0
- {azure_functions_durable-1.3.3.dist-info → azure_functions_durable-1.4.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
import azure.durable_functions as df
|
|
4
|
+
import azure.functions as func
|
|
5
|
+
import json
|
|
6
|
+
import pydantic
|
|
7
|
+
from typing import TypedDict
|
|
8
|
+
from agents import Agent, Runner
|
|
9
|
+
from azure.durable_functions.models import OrchestratorState
|
|
10
|
+
from azure.durable_functions.models.actions import CallActivityAction
|
|
11
|
+
from azure.durable_functions.models.ReplaySchema import ReplaySchema
|
|
12
|
+
from openai import BaseModel
|
|
13
|
+
from tests.orchestrator.orchestrator_test_utils import get_orchestration_state_result, assert_valid_schema, \
|
|
14
|
+
assert_orchestration_state_equals
|
|
15
|
+
from tests.test_utils.ContextBuilder import ContextBuilder
|
|
16
|
+
|
|
17
|
+
app = df.DFApp(http_auth_level=func.AuthLevel.ANONYMOUS)
|
|
18
|
+
|
|
19
|
+
@app.function_name("openai_agent_hello_world")
|
|
20
|
+
@app.orchestration_trigger(context_name="context")
|
|
21
|
+
@app.durable_openai_agent_orchestrator(model_retry_options=None)
|
|
22
|
+
def openai_agent_hello_world(context):
|
|
23
|
+
agent = Agent(
|
|
24
|
+
name="Assistant",
|
|
25
|
+
instructions="You only respond in haikus."
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
result = Runner.run_sync(agent, "Tell me about recursion in programming.")
|
|
29
|
+
|
|
30
|
+
return result.final_output;
|
|
31
|
+
|
|
32
|
+
#
|
|
33
|
+
# Run an agent that uses various tools.
|
|
34
|
+
#
|
|
35
|
+
class Weather(BaseModel):
|
|
36
|
+
city: str
|
|
37
|
+
temperature_range: str
|
|
38
|
+
conditions: str
|
|
39
|
+
|
|
40
|
+
@staticmethod
|
|
41
|
+
def from_json(data: str) -> "Weather":
|
|
42
|
+
return Weather(**json.loads(data))
|
|
43
|
+
|
|
44
|
+
@app.activity_trigger(input_name="city")
|
|
45
|
+
def get_weather(city: str) -> Weather:
|
|
46
|
+
print("[debug] get_weather called")
|
|
47
|
+
return Weather(city=city, temperature_range="14-20C", conditions="Sunny with wind.")
|
|
48
|
+
|
|
49
|
+
@app.function_name("openai_agent_use_tool")
|
|
50
|
+
@app.orchestration_trigger(context_name="context")
|
|
51
|
+
@app.durable_openai_agent_orchestrator(model_retry_options=None)
|
|
52
|
+
def openai_agent_use_tool(context):
|
|
53
|
+
agent = Agent(
|
|
54
|
+
name="Assistant",
|
|
55
|
+
instructions="You only respond in haikus.",
|
|
56
|
+
tools=[context.create_activity_tool(get_weather, retry_options=None)]
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
result = Runner.run_sync(agent, "Tell me the weather in Seattle.", )
|
|
60
|
+
|
|
61
|
+
return result.final_output;
|
|
62
|
+
|
|
63
|
+
@app.activity_trigger(input_name="city", activity="get_weather_with_explicit_name")
|
|
64
|
+
def get_named_weather(city: str) -> Weather:
|
|
65
|
+
print("[debug] get_weather called")
|
|
66
|
+
return Weather(city=city, temperature_range="14-20C", conditions="Sunny with wind.")
|
|
67
|
+
|
|
68
|
+
@app.function_name("openai_agent_use_tool_with_explicit_name")
|
|
69
|
+
@app.orchestration_trigger(context_name="context")
|
|
70
|
+
@app.durable_openai_agent_orchestrator(model_retry_options=None)
|
|
71
|
+
def openai_agent_use_tool_with_explicit_name(context):
|
|
72
|
+
agent = Agent(
|
|
73
|
+
name="Assistant",
|
|
74
|
+
instructions="You only respond in haikus.",
|
|
75
|
+
tools=[context.create_activity_tool(get_named_weather, retry_options=None)]
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
result = Runner.run_sync(agent, "Tell me the weather in Seattle.", )
|
|
79
|
+
|
|
80
|
+
return result.final_output;
|
|
81
|
+
|
|
82
|
+
@app.function_name("openai_agent_return_string_type")
|
|
83
|
+
@app.orchestration_trigger(context_name="context")
|
|
84
|
+
@app.durable_openai_agent_orchestrator(model_retry_options=None)
|
|
85
|
+
def openai_agent_return_string_type(context):
|
|
86
|
+
return "Hello World"
|
|
87
|
+
|
|
88
|
+
class DurableModel:
|
|
89
|
+
def __init__(self, property: str) -> None:
|
|
90
|
+
self._property = property
|
|
91
|
+
|
|
92
|
+
def to_json(self) -> str:
|
|
93
|
+
return json.dumps({"property": self._property})
|
|
94
|
+
|
|
95
|
+
@app.function_name("openai_agent_return_durable_model_type")
|
|
96
|
+
@app.orchestration_trigger(context_name="context")
|
|
97
|
+
@app.durable_openai_agent_orchestrator(model_retry_options=None)
|
|
98
|
+
def openai_agent_return_durable_model_type(context):
|
|
99
|
+
model = DurableModel(property="value")
|
|
100
|
+
|
|
101
|
+
return model
|
|
102
|
+
|
|
103
|
+
class TypedDictionaryModel(TypedDict):
|
|
104
|
+
property: str
|
|
105
|
+
|
|
106
|
+
@app.function_name("openai_agent_return_typed_dictionary_model_type")
|
|
107
|
+
@app.orchestration_trigger(context_name="context")
|
|
108
|
+
@app.durable_openai_agent_orchestrator(model_retry_options=None)
|
|
109
|
+
def openai_agent_return_typed_dictionary_model_type(context):
|
|
110
|
+
model = TypedDictionaryModel(property="value")
|
|
111
|
+
|
|
112
|
+
return model
|
|
113
|
+
|
|
114
|
+
class OpenAIPydanticModel(BaseModel):
|
|
115
|
+
property: str
|
|
116
|
+
|
|
117
|
+
@app.function_name("openai_agent_return_openai_pydantic_model_type")
|
|
118
|
+
@app.orchestration_trigger(context_name="context")
|
|
119
|
+
@app.durable_openai_agent_orchestrator(model_retry_options=None)
|
|
120
|
+
def openai_agent_return_openai_pydantic_model_type(context):
|
|
121
|
+
model = OpenAIPydanticModel(property="value")
|
|
122
|
+
|
|
123
|
+
return model
|
|
124
|
+
|
|
125
|
+
class PydanticModel(pydantic.BaseModel):
|
|
126
|
+
property: str
|
|
127
|
+
|
|
128
|
+
@app.function_name("openai_agent_return_pydantic_model_type")
|
|
129
|
+
@app.orchestration_trigger(context_name="context")
|
|
130
|
+
@app.durable_openai_agent_orchestrator(model_retry_options=None)
|
|
131
|
+
def openai_agent_return_pydantic_model_type(context):
|
|
132
|
+
model = PydanticModel(property="value")
|
|
133
|
+
|
|
134
|
+
return model
|
|
135
|
+
|
|
136
|
+
model_activity_name = "run_model"
|
|
137
|
+
|
|
138
|
+
def base_expected_state(output=None, replay_schema: ReplaySchema = ReplaySchema.V1) -> OrchestratorState:
|
|
139
|
+
return OrchestratorState(is_done=False, actions=[], output=output, replay_schema=replay_schema)
|
|
140
|
+
|
|
141
|
+
def add_activity_action(state: OrchestratorState, input_: str, activity_name=model_activity_name):
|
|
142
|
+
action = CallActivityAction(function_name=activity_name, input_=input_)
|
|
143
|
+
state.actions.append([action])
|
|
144
|
+
|
|
145
|
+
def add_activity_completed_events(
|
|
146
|
+
context_builder: ContextBuilder, id_: int, result: str, is_played=False, activity_name=model_activity_name):
|
|
147
|
+
context_builder.add_task_scheduled_event(name=activity_name, id_=id_)
|
|
148
|
+
context_builder.add_orchestrator_completed_event()
|
|
149
|
+
context_builder.add_orchestrator_started_event()
|
|
150
|
+
context_builder.add_task_completed_event(id_=id_, result=json.dumps(result), is_played=is_played)
|
|
151
|
+
|
|
152
|
+
def test_openai_agent_hello_world_start():
|
|
153
|
+
context_builder = ContextBuilder('test_openai_agent_hello_world_start')
|
|
154
|
+
|
|
155
|
+
result = get_orchestration_state_result(
|
|
156
|
+
context_builder, openai_agent_hello_world, uses_pystein=True)
|
|
157
|
+
|
|
158
|
+
expected_state = base_expected_state()
|
|
159
|
+
add_activity_action(expected_state, "{\"input\":[{\"content\":\"Tell me about recursion in programming.\",\"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\":[],\"output_schema\":null,\"handoffs\":[],\"previous_response_id\":null,\"prompt\":null}")
|
|
160
|
+
expected = expected_state.to_json()
|
|
161
|
+
|
|
162
|
+
assert_valid_schema(result)
|
|
163
|
+
assert_orchestration_state_equals(expected, result)
|
|
164
|
+
|
|
165
|
+
def test_openai_agent_hello_world_completed():
|
|
166
|
+
context_builder = ContextBuilder('test_openai_agent_hello_world_completed')
|
|
167
|
+
add_activity_completed_events(context_builder, 0, '{"output":[{"id":"msg_68b9b2a9c67c81a38559c20c18fe86040a86c28ba39b53e8","content":[{"annotations":[],"text":"Skyscrapers whisper— \\nTaxis hum beneath the lights, \\nCity dreams don’t sleep.","type":"output_text","logprobs":null}],"role":"assistant","status":"completed","type":"message"}],"usage":{"requests":1,"input_tokens":27,"input_tokens_details":{"cached_tokens":0},"output_tokens":21,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":48},"response_id":"resp_68b9b2a9461481a3984d0f790dd33f7b0a86c28ba39b53e8"}')
|
|
168
|
+
|
|
169
|
+
result = get_orchestration_state_result(
|
|
170
|
+
context_builder, openai_agent_hello_world, uses_pystein=True)
|
|
171
|
+
|
|
172
|
+
expected_state = base_expected_state()
|
|
173
|
+
add_activity_action(expected_state, "{\"input\":[{\"content\":\"Tell me about recursion in programming.\",\"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\":[],\"output_schema\":null,\"handoffs\":[],\"previous_response_id\":null,\"prompt\":null}")
|
|
174
|
+
expected_state._is_done = True
|
|
175
|
+
expected_state._output = 'Skyscrapers whisper— \nTaxis hum beneath the lights, \nCity dreams don’t sleep.'
|
|
176
|
+
expected = expected_state.to_json()
|
|
177
|
+
|
|
178
|
+
assert_valid_schema(result)
|
|
179
|
+
assert_orchestration_state_equals(expected, result)
|
|
180
|
+
|
|
181
|
+
def test_openai_agent_use_tool_activity_start():
|
|
182
|
+
context_builder = ContextBuilder('test_openai_agent_use_tool_start')
|
|
183
|
+
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"}')
|
|
184
|
+
|
|
185
|
+
result = get_orchestration_state_result(
|
|
186
|
+
context_builder, openai_agent_use_tool, uses_pystein=True)
|
|
187
|
+
|
|
188
|
+
expected_state = base_expected_state()
|
|
189
|
+
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_weather\",\"description\":\"\",\"params_json_schema\":{\"properties\":{\"city\":{\"title\":\"City\",\"type\":\"string\"}},\"required\":[\"city\"],\"title\":\"get_weather_args\",\"type\":\"object\",\"additionalProperties\":false},\"strict_json_schema\":true}],\"output_schema\":null,\"handoffs\":[],\"previous_response_id\":null,\"prompt\":null}")
|
|
190
|
+
add_activity_action(expected_state, "{\"args\":\"Seattle, WA\"}", activity_name="get_weather")
|
|
191
|
+
expected = expected_state.to_json()
|
|
192
|
+
|
|
193
|
+
assert_valid_schema(result)
|
|
194
|
+
assert_orchestration_state_equals(expected, result)
|
|
195
|
+
|
|
196
|
+
def test_openai_agent_use_explicitly_named_tool_activity_start():
|
|
197
|
+
context_builder = ContextBuilder('test_openai_agent_use_tool_start')
|
|
198
|
+
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"}')
|
|
199
|
+
|
|
200
|
+
result = get_orchestration_state_result(
|
|
201
|
+
context_builder, openai_agent_use_tool_with_explicit_name, uses_pystein=True)
|
|
202
|
+
|
|
203
|
+
expected_state = base_expected_state()
|
|
204
|
+
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}")
|
|
205
|
+
add_activity_action(expected_state, "{\"args\":\"Seattle, WA\"}", activity_name="get_weather_with_explicit_name")
|
|
206
|
+
expected = expected_state.to_json()
|
|
207
|
+
|
|
208
|
+
assert_valid_schema(result)
|
|
209
|
+
assert_orchestration_state_equals(expected, result)
|
|
210
|
+
|
|
211
|
+
def test_openai_agent_use_tool_activity_completed():
|
|
212
|
+
context_builder = ContextBuilder('test_openai_agent_use_tool_start')
|
|
213
|
+
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"}')
|
|
214
|
+
add_activity_completed_events(context_builder, 1, '{"__class__":"Weather","__module__":"function_app","__data__":"{\n \"city\": \"{\\\"args\\\":\\\"Seattle, WA\\\"}\",\n \"temperature_range\": \"14-20C\",\n \"conditions\": \"Sunny with wind.\"\n}"}')
|
|
215
|
+
|
|
216
|
+
result = get_orchestration_state_result(
|
|
217
|
+
context_builder, openai_agent_use_tool, uses_pystein=True)
|
|
218
|
+
|
|
219
|
+
expected_state = base_expected_state()
|
|
220
|
+
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_weather\",\"description\":\"\",\"params_json_schema\":{\"properties\":{\"city\":{\"title\":\"City\",\"type\":\"string\"}},\"required\":[\"city\"],\"title\":\"get_weather_args\",\"type\":\"object\",\"additionalProperties\":false},\"strict_json_schema\":true}],\"output_schema\":null,\"handoffs\":[],\"previous_response_id\":null,\"prompt\":null}")
|
|
221
|
+
add_activity_action(expected_state, "{\"args\":\"Seattle, WA\"}", activity_name="get_weather")
|
|
222
|
+
add_activity_action(expected_state, "{\"input\":[{\"content\":\"Tell me the weather in Seattle.\",\"role\":\"user\"},{\"arguments\":\"{\\\"args\\\":\\\"Seattle, WA\\\"}\",\"call_id\":\"call_mEdywElQTNpxAdivuEFjO0cT\",\"name\":\"get_weather\",\"type\":\"function_call\",\"id\":\"fc_68b9ecc0ff9c819f863d6cf9e0a1b4e101011fd6f5f8c0a6\",\"status\":\"completed\"},{\"call_id\":\"call_mEdywElQTNpxAdivuEFjO0cT\",\"output\":\"{\\\"__class__\\\":\\\"Weather\\\",\\\"__module__\\\":\\\"function_app\\\",\\\"__data__\\\":\\\"{\\n \\\"city\\\": \\\"{\\\\\\\"args\\\\\\\":\\\\\\\"Seattle, WA\\\\\\\"}\\\",\\n \\\"temperature_range\\\": \\\"14-20C\\\",\\n \\\"conditions\\\": \\\"Sunny with wind.\\\"\\n}\\\"}\",\"type\":\"function_call_output\"}],\"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_weather\",\"description\":\"\",\"params_json_schema\":{\"properties\":{\"city\":{\"title\":\"City\",\"type\":\"string\"}},\"required\":[\"city\"],\"title\":\"get_weather_args\",\"type\":\"object\",\"additionalProperties\":false},\"strict_json_schema\":true}],\"output_schema\":null,\"handoffs\":[],\"previous_response_id\":null,\"prompt\":null}")
|
|
223
|
+
expected = expected_state.to_json()
|
|
224
|
+
|
|
225
|
+
assert_valid_schema(result)
|
|
226
|
+
assert_orchestration_state_equals(expected, result)
|
|
227
|
+
|
|
228
|
+
def test_openai_agent_use_tool_analysis_completed():
|
|
229
|
+
context_builder = ContextBuilder('test_openai_agent_use_tool_start')
|
|
230
|
+
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"}')
|
|
231
|
+
add_activity_completed_events(context_builder, 1, '{"__class__":"Weather","__module__":"function_app","__data__":"{\n \"city\": \"{\\\"args\\\":\\\"Seattle, WA\\\"}\",\n \"temperature_range\": \"14-20C\",\n \"conditions\": \"Sunny with wind.\"\n}"}')
|
|
232
|
+
add_activity_completed_events(context_builder, 2, '{"output":[{"id":"msg_68b9f4b09c14819faa62abfd69cb53e501011fd6f5f8c0a6","content":[{"annotations":[],"text":"The weather in Seattle, WA is currently sunny with some wind. Temperatures are ranging from 14°C to 20°C.","type":"output_text","logprobs":null}],"role":"assistant","status":"completed","type":"message"}],"usage":{"requests":1,"input_tokens":107,"input_tokens_details":{"cached_tokens":0},"output_tokens":28,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":135},"response_id":"resp_68b9f4b00804819f9fe99eac95bd198e01011fd6f5f8c0a6"}')
|
|
233
|
+
|
|
234
|
+
result = get_orchestration_state_result(
|
|
235
|
+
context_builder, openai_agent_use_tool, uses_pystein=True)
|
|
236
|
+
|
|
237
|
+
expected_state = base_expected_state()
|
|
238
|
+
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_weather\",\"description\":\"\",\"params_json_schema\":{\"properties\":{\"city\":{\"title\":\"City\",\"type\":\"string\"}},\"required\":[\"city\"],\"title\":\"get_weather_args\",\"type\":\"object\",\"additionalProperties\":false},\"strict_json_schema\":true}],\"output_schema\":null,\"handoffs\":[],\"previous_response_id\":null,\"prompt\":null}")
|
|
239
|
+
add_activity_action(expected_state, "{\"args\":\"Seattle, WA\"}", activity_name="get_weather")
|
|
240
|
+
add_activity_action(expected_state, "{\"input\":[{\"content\":\"Tell me the weather in Seattle.\",\"role\":\"user\"},{\"arguments\":\"{\\\"args\\\":\\\"Seattle, WA\\\"}\",\"call_id\":\"call_mEdywElQTNpxAdivuEFjO0cT\",\"name\":\"get_weather\",\"type\":\"function_call\",\"id\":\"fc_68b9ecc0ff9c819f863d6cf9e0a1b4e101011fd6f5f8c0a6\",\"status\":\"completed\"},{\"call_id\":\"call_mEdywElQTNpxAdivuEFjO0cT\",\"output\":\"{\\\"__class__\\\":\\\"Weather\\\",\\\"__module__\\\":\\\"function_app\\\",\\\"__data__\\\":\\\"{\\n \\\"city\\\": \\\"{\\\\\\\"args\\\\\\\":\\\\\\\"Seattle, WA\\\\\\\"}\\\",\\n \\\"temperature_range\\\": \\\"14-20C\\\",\\n \\\"conditions\\\": \\\"Sunny with wind.\\\"\\n}\\\"}\",\"type\":\"function_call_output\"}],\"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_weather\",\"description\":\"\",\"params_json_schema\":{\"properties\":{\"city\":{\"title\":\"City\",\"type\":\"string\"}},\"required\":[\"city\"],\"title\":\"get_weather_args\",\"type\":\"object\",\"additionalProperties\":false},\"strict_json_schema\":true}],\"output_schema\":null,\"handoffs\":[],\"previous_response_id\":null,\"prompt\":null}")
|
|
241
|
+
expected_state._is_done = True
|
|
242
|
+
expected_state._output = 'The weather in Seattle, WA is currently sunny with some wind. Temperatures are ranging from 14°C to 20°C.'
|
|
243
|
+
expected = expected_state.to_json()
|
|
244
|
+
|
|
245
|
+
assert_valid_schema(result)
|
|
246
|
+
assert_orchestration_state_equals(expected, result)
|
|
247
|
+
|
|
248
|
+
def test_openai_agent_string_serialization():
|
|
249
|
+
context_builder = ContextBuilder('test_openai_agent_string_serialization')
|
|
250
|
+
|
|
251
|
+
result = get_orchestration_state_result(
|
|
252
|
+
context_builder, openai_agent_return_string_type, uses_pystein=True)
|
|
253
|
+
|
|
254
|
+
expected_state = base_expected_state()
|
|
255
|
+
expected_state._is_done = True
|
|
256
|
+
expected_state._output = "Hello World"
|
|
257
|
+
expected = expected_state.to_json()
|
|
258
|
+
|
|
259
|
+
assert_valid_schema(result)
|
|
260
|
+
assert_orchestration_state_equals(expected, result)
|
|
261
|
+
|
|
262
|
+
def test_openai_agent_durable_model_serialization():
|
|
263
|
+
context_builder = ContextBuilder('test_openai_agent_durable_model_serialization')
|
|
264
|
+
|
|
265
|
+
result = get_orchestration_state_result(
|
|
266
|
+
context_builder, openai_agent_return_durable_model_type, uses_pystein=True)
|
|
267
|
+
|
|
268
|
+
expected_state = base_expected_state()
|
|
269
|
+
expected_state._is_done = True
|
|
270
|
+
expected_state._output = DurableModel(property="value").to_json()
|
|
271
|
+
expected = expected_state.to_json()
|
|
272
|
+
|
|
273
|
+
assert_valid_schema(result)
|
|
274
|
+
assert_orchestration_state_equals(expected, result)
|
|
275
|
+
|
|
276
|
+
def test_openai_agent_typed_dictionary_model_serialization():
|
|
277
|
+
context_builder = ContextBuilder('test_openai_agent_typed_dictionary_model_serialization')
|
|
278
|
+
|
|
279
|
+
result = get_orchestration_state_result(
|
|
280
|
+
context_builder, openai_agent_return_typed_dictionary_model_type, uses_pystein=True)
|
|
281
|
+
|
|
282
|
+
expected_state = base_expected_state()
|
|
283
|
+
expected_state._is_done = True
|
|
284
|
+
expected_state._output = json.dumps(TypedDictionaryModel(property="value"))
|
|
285
|
+
expected = expected_state.to_json()
|
|
286
|
+
|
|
287
|
+
assert_valid_schema(result)
|
|
288
|
+
assert_orchestration_state_equals(expected, result)
|
|
289
|
+
|
|
290
|
+
def test_openai_agent_openai_pydantic_model_serialization():
|
|
291
|
+
context_builder = ContextBuilder('test_openai_agent_openai_pydantic_model_serialization')
|
|
292
|
+
|
|
293
|
+
result = get_orchestration_state_result(
|
|
294
|
+
context_builder, openai_agent_return_openai_pydantic_model_type, uses_pystein=True)
|
|
295
|
+
|
|
296
|
+
expected_state = base_expected_state()
|
|
297
|
+
expected_state._is_done = True
|
|
298
|
+
expected_state._output = OpenAIPydanticModel(property="value").to_json()
|
|
299
|
+
expected = expected_state.to_json()
|
|
300
|
+
|
|
301
|
+
assert_valid_schema(result)
|
|
302
|
+
assert_orchestration_state_equals(expected, result)
|
|
303
|
+
|
|
304
|
+
def test_openai_agent_pydantic_model_serialization():
|
|
305
|
+
context_builder = ContextBuilder('test_openai_agent_pydantic_model_serialization')
|
|
306
|
+
|
|
307
|
+
result = get_orchestration_state_result(
|
|
308
|
+
context_builder, openai_agent_return_pydantic_model_type, uses_pystein=True)
|
|
309
|
+
|
|
310
|
+
expected_state = base_expected_state()
|
|
311
|
+
expected_state._is_done = True
|
|
312
|
+
expected_state._output = PydanticModel(property="value").model_dump_json()
|
|
313
|
+
expected = expected_state.to_json()
|
|
314
|
+
|
|
315
|
+
assert_valid_schema(result)
|
|
316
|
+
assert_orchestration_state_equals(expected, result)
|
|
File without changes
|
|
File without changes
|
{azure_functions_durable-1.3.3.dist-info → azure_functions_durable-1.4.0.dist-info}/top_level.txt
RENAMED
|
File without changes
|