agentex-sdk 0.4.11__py3-none-any.whl → 0.4.12__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.
agentex/_version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
2
 
3
3
  __title__ = "agentex"
4
- __version__ = "0.4.11" # x-release-please-version
4
+ __version__ = "0.4.12" # x-release-please-version
@@ -205,6 +205,8 @@ class ACPModule:
205
205
  self,
206
206
  task_id: str | None = None,
207
207
  task_name: str | None = None,
208
+ agent_id: str | None = None,
209
+ agent_name: str | None = None,
208
210
  trace_id: str | None = None,
209
211
  parent_span_id: str | None = None,
210
212
  start_to_close_timeout: timedelta = timedelta(seconds=5),
@@ -212,11 +214,13 @@ class ACPModule:
212
214
  retry_policy: RetryPolicy = DEFAULT_RETRY_POLICY,
213
215
  ) -> Task:
214
216
  """
215
- Cancel a task.
217
+ Cancel a task by sending cancel request to the agent that owns the task.
216
218
 
217
219
  Args:
218
- task_id: The ID of the task to cancel.
219
- task_name: The name of the task to cancel.
220
+ task_id: ID of the task to cancel.
221
+ task_name: Name of the task to cancel.
222
+ agent_id: ID of the agent that owns the task.
223
+ agent_name: Name of the agent that owns the task.
220
224
  trace_id: The trace ID for the task.
221
225
  parent_span_id: The parent span ID for the task.
222
226
  start_to_close_timeout: The start to close timeout for the task.
@@ -225,6 +229,10 @@ class ACPModule:
225
229
 
226
230
  Returns:
227
231
  The task entry.
232
+
233
+ Raises:
234
+ ValueError: If neither agent_name nor agent_id is provided,
235
+ or if neither task_name nor task_id is provided
228
236
  """
229
237
  if in_temporal_workflow():
230
238
  return await ActivityHelpers.execute_activity(
@@ -232,6 +240,8 @@ class ACPModule:
232
240
  request=TaskCancelParams(
233
241
  task_id=task_id,
234
242
  task_name=task_name,
243
+ agent_id=agent_id,
244
+ agent_name=agent_name,
235
245
  trace_id=trace_id,
236
246
  parent_span_id=parent_span_id,
237
247
  ),
@@ -244,6 +254,8 @@ class ACPModule:
244
254
  return await self._acp_service.task_cancel(
245
255
  task_id=task_id,
246
256
  task_name=task_name,
257
+ agent_id=agent_id,
258
+ agent_name=agent_name,
247
259
  trace_id=trace_id,
248
260
  parent_span_id=parent_span_id,
249
261
  )
@@ -88,6 +88,7 @@ class OpenAIModule:
88
88
  mcp_timeout_seconds: int | None = None,
89
89
  input_guardrails: list[InputGuardrail] | None = None,
90
90
  output_guardrails: list[OutputGuardrail] | None = None,
91
+ max_turns: int | None = None,
91
92
  ) -> SerializableRunResult | RunResult:
92
93
  """
93
94
  Run an agent without streaming or TaskMessage creation.
@@ -114,6 +115,7 @@ class OpenAIModule:
114
115
  mcp_timeout_seconds: Optional param to set the timeout threshold for the MCP servers. Defaults to 5 seconds.
115
116
  input_guardrails: Optional list of input guardrails to run on initial user input.
116
117
  output_guardrails: Optional list of output guardrails to run on final agent output.
118
+ max_turns: Maximum number of turns the agent can take. Uses Runner's default if None.
117
119
 
118
120
  Returns:
119
121
  Union[SerializableRunResult, RunResult]: SerializableRunResult when in Temporal, RunResult otherwise.
@@ -136,6 +138,7 @@ class OpenAIModule:
136
138
  mcp_timeout_seconds=mcp_timeout_seconds,
137
139
  input_guardrails=input_guardrails,
138
140
  output_guardrails=output_guardrails,
141
+ max_turns=max_turns,
139
142
  )
140
143
  return await ActivityHelpers.execute_activity(
141
144
  activity_name=OpenAIActivityName.RUN_AGENT,
@@ -163,6 +166,7 @@ class OpenAIModule:
163
166
  mcp_timeout_seconds=mcp_timeout_seconds,
164
167
  input_guardrails=input_guardrails,
165
168
  output_guardrails=output_guardrails,
169
+ max_turns=max_turns,
166
170
  )
167
171
 
168
172
  async def run_agent_auto_send(
@@ -191,6 +195,7 @@ class OpenAIModule:
191
195
  mcp_timeout_seconds: int | None = None,
192
196
  input_guardrails: list[InputGuardrail] | None = None,
193
197
  output_guardrails: list[OutputGuardrail] | None = None,
198
+ max_turns: int | None = None,
194
199
  ) -> SerializableRunResult | RunResult:
195
200
  """
196
201
  Run an agent with automatic TaskMessage creation.
@@ -216,6 +221,7 @@ class OpenAIModule:
216
221
  mcp_timeout_seconds: Optional param to set the timeout threshold for the MCP servers. Defaults to 5 seconds.
217
222
  input_guardrails: Optional list of input guardrails to run on initial user input.
218
223
  output_guardrails: Optional list of output guardrails to run on final agent output.
224
+ max_turns: Maximum number of turns the agent can take. Uses Runner's default if None.
219
225
 
220
226
  Returns:
221
227
  Union[SerializableRunResult, RunResult]: SerializableRunResult when in Temporal, RunResult otherwise.
@@ -239,6 +245,7 @@ class OpenAIModule:
239
245
  mcp_timeout_seconds=mcp_timeout_seconds,
240
246
  input_guardrails=input_guardrails,
241
247
  output_guardrails=output_guardrails,
248
+ max_turns=max_turns,
242
249
  )
243
250
  return await ActivityHelpers.execute_activity(
244
251
  activity_name=OpenAIActivityName.RUN_AGENT_AUTO_SEND,
@@ -267,6 +274,7 @@ class OpenAIModule:
267
274
  mcp_timeout_seconds=mcp_timeout_seconds,
268
275
  input_guardrails=input_guardrails,
269
276
  output_guardrails=output_guardrails,
277
+ max_turns=max_turns,
270
278
  )
271
279
 
272
280
  async def run_agent_streamed(
@@ -291,6 +299,7 @@ class OpenAIModule:
291
299
  mcp_timeout_seconds: int | None = None,
292
300
  input_guardrails: list[InputGuardrail] | None = None,
293
301
  output_guardrails: list[OutputGuardrail] | None = None,
302
+ max_turns: int | None = None,
294
303
  ) -> RunResultStreaming:
295
304
  """
296
305
  Run an agent with streaming enabled but no TaskMessage creation.
@@ -320,6 +329,7 @@ class OpenAIModule:
320
329
  mcp_timeout_seconds: Optional param to set the timeout threshold for the MCP servers. Defaults to 5 seconds.
321
330
  input_guardrails: Optional list of input guardrails to run on initial user input.
322
331
  output_guardrails: Optional list of output guardrails to run on final agent output.
332
+ max_turns: Maximum number of turns the agent can take. Uses Runner's default if None.
323
333
 
324
334
  Returns:
325
335
  RunResultStreaming: The result of the agent run with streaming.
@@ -352,6 +362,7 @@ class OpenAIModule:
352
362
  mcp_timeout_seconds=mcp_timeout_seconds,
353
363
  input_guardrails=input_guardrails,
354
364
  output_guardrails=output_guardrails,
365
+ max_turns=max_turns,
355
366
  )
356
367
 
357
368
  async def run_agent_streamed_auto_send(
@@ -380,6 +391,7 @@ class OpenAIModule:
380
391
  mcp_timeout_seconds: int | None = None,
381
392
  input_guardrails: list[InputGuardrail] | None = None,
382
393
  output_guardrails: list[OutputGuardrail] | None = None,
394
+ max_turns: int | None = None,
383
395
  ) -> SerializableRunResultStreaming | RunResultStreaming:
384
396
  """
385
397
  Run an agent with streaming enabled and automatic TaskMessage creation.
@@ -405,6 +417,7 @@ class OpenAIModule:
405
417
  output_type: Optional output type.
406
418
  tool_use_behavior: Optional tool use behavior.
407
419
  mcp_timeout_seconds: Optional param to set the timeout threshold for the MCP servers. Defaults to 5 seconds.
420
+ max_turns: Maximum number of turns the agent can take. Uses Runner's default if None.
408
421
 
409
422
  Returns:
410
423
  Union[SerializableRunResultStreaming, RunResultStreaming]: SerializableRunResultStreaming when in Temporal, RunResultStreaming otherwise.
@@ -428,6 +441,7 @@ class OpenAIModule:
428
441
  mcp_timeout_seconds=mcp_timeout_seconds,
429
442
  input_guardrails=input_guardrails,
430
443
  output_guardrails=output_guardrails,
444
+ max_turns=max_turns
431
445
  )
432
446
  return await ActivityHelpers.execute_activity(
433
447
  activity_name=OpenAIActivityName.RUN_AGENT_STREAMED_AUTO_SEND,
@@ -456,4 +470,5 @@ class OpenAIModule:
456
470
  mcp_timeout_seconds=mcp_timeout_seconds,
457
471
  input_guardrails=input_guardrails,
458
472
  output_guardrails=output_guardrails,
473
+ max_turns=max_turns,
459
474
  )
@@ -229,9 +229,12 @@ def merge_deployment_configs(
229
229
  all_env_vars[EnvVarKeys.AUTH_PRINCIPAL_B64.value] = encoded_principal
230
230
  else:
231
231
  raise DeploymentError(f"Auth principal unable to be encoded for agent_env_config: {agent_env_config}")
232
-
232
+
233
+ logger.info(f"Defined agent helm overrides: {agent_env_config.helm_overrides}")
234
+ logger.info(f"Before-merge helm values: {helm_values}")
233
235
  if agent_env_config.helm_overrides:
234
236
  _deep_merge(helm_values, agent_env_config.helm_overrides)
237
+ logger.info(f"After-merge helm values: {helm_values}")
235
238
 
236
239
  # Set final environment variables
237
240
  # Environment variable precedence: manifest -> environments.yaml -> secrets (highest)
@@ -52,7 +52,7 @@ environments:
52
52
  limits:
53
53
  cpu: "1000m"
54
54
  memory: "2Gi"
55
- temporal:
55
+ temporal-worker:
56
56
  enabled: true
57
57
  replicaCount: 2
58
58
  resources:
@@ -180,9 +180,19 @@ class ACPService:
180
180
  self,
181
181
  task_id: str | None = None,
182
182
  task_name: str | None = None,
183
+ agent_id: str | None = None,
184
+ agent_name: str | None = None,
183
185
  trace_id: str | None = None,
184
186
  parent_span_id: str | None = None,
185
- ) -> Task:
187
+ ) -> Task:
188
+ # Require agent identification
189
+ if not agent_name and not agent_id:
190
+ raise ValueError("Either agent_name or agent_id must be provided to identify the agent that owns the task")
191
+
192
+ # Require task identification
193
+ if not task_name and not task_id:
194
+ raise ValueError("Either task_name or task_id must be provided to identify the task to cancel")
195
+
186
196
  trace = self._tracer.trace(trace_id=trace_id)
187
197
  async with trace.span(
188
198
  parent_id=parent_span_id,
@@ -190,27 +200,32 @@ class ACPService:
190
200
  input={
191
201
  "task_id": task_id,
192
202
  "task_name": task_name,
203
+ "agent_id": agent_id,
204
+ "agent_name": agent_name,
193
205
  },
194
206
  ) as span:
195
207
  heartbeat_if_in_workflow("task cancel")
208
+
209
+ # Build params for the agent (task identification)
210
+ params = {}
211
+ if task_id:
212
+ params["task_id"] = task_id
196
213
  if task_name:
214
+ params["task_name"] = task_name
215
+
216
+ # Send cancel request to the correct agent
217
+ if agent_name:
197
218
  json_rpc_response = await self._agentex_client.agents.rpc_by_name(
198
- agent_name=task_name,
219
+ agent_name=agent_name,
199
220
  method="task/cancel",
200
- params={
201
- "task_name": task_name,
202
- },
221
+ params=params,
203
222
  )
204
- elif task_id:
223
+ else: # agent_id is provided (validated above)
205
224
  json_rpc_response = await self._agentex_client.agents.rpc(
206
- agent_id=task_id,
225
+ agent_id=agent_id,
207
226
  method="task/cancel",
208
- params={
209
- "task_id": task_id,
210
- },
227
+ params=params,
211
228
  )
212
- else:
213
- raise ValueError("Either task_name or task_id must be provided")
214
229
 
215
230
  task_entry = Task.model_validate(json_rpc_response.result)
216
231
  if span:
@@ -1,5 +1,4 @@
1
1
  # Standard library imports
2
- import json
3
2
  from contextlib import AsyncExitStack, asynccontextmanager
4
3
  from typing import Any, Literal
5
4
 
@@ -11,7 +10,8 @@ from agents.mcp import MCPServerStdio
11
10
  from mcp import StdioServerParameters
12
11
  from openai.types.responses import (
13
12
  ResponseCompletedEvent,
14
- ResponseFunctionToolCall,
13
+ ResponseFunctionWebSearch,
14
+ ResponseCodeInterpreterToolCall,
15
15
  ResponseOutputItemDoneEvent,
16
16
  ResponseTextDeltaEvent,
17
17
  ResponseReasoningSummaryTextDeltaEvent,
@@ -85,6 +85,86 @@ class OpenAIService:
85
85
  self.streaming_service = streaming_service
86
86
  self.tracer = tracer
87
87
 
88
+ def _extract_tool_call_info(
89
+ self, tool_call_item: Any
90
+ ) -> tuple[str, str, dict[str, Any]]:
91
+ """
92
+ Extract call_id, tool_name, and tool_arguments from a tool call item.
93
+
94
+ Args:
95
+ tool_call_item: The tool call item to process
96
+
97
+ Returns:
98
+ A tuple of (call_id, tool_name, tool_arguments)
99
+ """
100
+ # Generic handling for different tool call types
101
+ # Try 'call_id' first, then 'id', then generate placeholder
102
+ if hasattr(tool_call_item, 'call_id'):
103
+ call_id = tool_call_item.call_id
104
+ elif hasattr(tool_call_item, 'id'):
105
+ call_id = tool_call_item.id
106
+ else:
107
+ call_id = f"unknown_call_{id(tool_call_item)}"
108
+ logger.warning(
109
+ f"Warning: Tool call item {type(tool_call_item)} has "
110
+ f"neither 'call_id' nor 'id' attribute, using placeholder: "
111
+ f"{call_id}"
112
+ )
113
+
114
+ if isinstance(tool_call_item, ResponseFunctionWebSearch):
115
+ tool_name = "web_search"
116
+ tool_arguments = {
117
+ "action": tool_call_item.action.model_dump(),
118
+ "status": tool_call_item.status
119
+ }
120
+ elif isinstance(tool_call_item, ResponseCodeInterpreterToolCall):
121
+ tool_name = "code_interpreter"
122
+ tool_arguments = {
123
+ "code": tool_call_item.code,
124
+ "status": tool_call_item.status
125
+ }
126
+ else:
127
+ # Generic handling for any tool call type
128
+ tool_name = getattr(tool_call_item, 'name', type(tool_call_item).__name__)
129
+ tool_arguments = tool_call_item.model_dump()
130
+
131
+ return call_id, tool_name, tool_arguments
132
+
133
+ def _extract_tool_response_info(
134
+ self, tool_call_map: dict[str, Any], tool_output_item: Any
135
+ ) -> tuple[str, str, str]:
136
+ """
137
+ Extract call_id, tool_name, and content from a tool output item.
138
+
139
+ Args:
140
+ tool_call_map: Map of call_ids to tool_call items
141
+ tool_output_item: The tool output item to process
142
+
143
+ Returns:
144
+ A tuple of (call_id, tool_name, content)
145
+ """
146
+ # Extract call_id and content from the tool_output_item
147
+ # Handle both dictionary access and attribute access
148
+ if hasattr(tool_output_item, 'get') and callable(tool_output_item.get):
149
+ # Dictionary-like access
150
+ call_id = tool_output_item["call_id"]
151
+ content = tool_output_item["output"]
152
+ else:
153
+ # Attribute access for structured objects
154
+ call_id = getattr(tool_output_item, 'call_id', None)
155
+ content = getattr(tool_output_item, 'output', None)
156
+
157
+ # Get the name from the tool call map using generic approach
158
+ tool_call = tool_call_map[call_id]
159
+ if hasattr(tool_call, "name"):
160
+ tool_name = getattr(tool_call, "name")
161
+ elif hasattr(tool_call, "type"):
162
+ tool_name = getattr(tool_call, "type")
163
+ else:
164
+ tool_name = type(tool_call).__name__
165
+
166
+ return call_id, tool_name, content
167
+
88
168
  async def run_agent(
89
169
  self,
90
170
  input_list: list[dict[str, Any]],
@@ -107,6 +187,7 @@ class OpenAIService:
107
187
  mcp_timeout_seconds: int | None = None,
108
188
  input_guardrails: list[InputGuardrail] | None = None,
109
189
  output_guardrails: list[OutputGuardrail] | None = None,
190
+ max_turns: int | None = None,
110
191
  ) -> RunResult:
111
192
  """
112
193
  Run an agent without streaming or TaskMessage creation.
@@ -131,6 +212,8 @@ class OpenAIService:
131
212
  initial user input.
132
213
  output_guardrails: Optional list of output guardrails to run on
133
214
  final agent output.
215
+ mcp_timeout_seconds: Optional param to set the timeout threshold for the MCP servers. Defaults to 5 seconds.
216
+ max_turns: Maximum number of turns the agent can take. Uses Runner's default if None.
134
217
  Returns:
135
218
  SerializableRunResult: The result of the agent run.
136
219
  """
@@ -152,6 +235,7 @@ class OpenAIService:
152
235
  "tools": tools,
153
236
  "output_type": output_type,
154
237
  "tool_use_behavior": tool_use_behavior,
238
+ "max_turns": max_turns,
155
239
  },
156
240
  ) as span:
157
241
  heartbeat_if_in_workflow("run agent")
@@ -159,7 +243,9 @@ class OpenAIService:
159
243
  async with mcp_server_context(
160
244
  mcp_server_params, mcp_timeout_seconds
161
245
  ) as servers:
162
- tools = [tool.to_oai_function_tool() for tool in tools] if tools else []
246
+ tools = [
247
+ tool.to_oai_function_tool()for tool in tools
248
+ ] if tools else []
163
249
  handoffs = (
164
250
  [Agent(**handoff.model_dump()) for handoff in handoffs]
165
251
  if handoffs
@@ -189,7 +275,10 @@ class OpenAIService:
189
275
  agent = Agent(**agent_kwargs)
190
276
 
191
277
  # Run without streaming
192
- result = await Runner.run(starting_agent=agent, input=input_list)
278
+ if max_turns is not None:
279
+ result = await Runner.run(starting_agent=agent, input=input_list, max_turns=max_turns)
280
+ else:
281
+ result = await Runner.run(starting_agent=agent, input=input_list)
193
282
 
194
283
  if span:
195
284
  span.output = {
@@ -227,6 +316,7 @@ class OpenAIService:
227
316
  mcp_timeout_seconds: int | None = None,
228
317
  input_guardrails: list[InputGuardrail] | None = None,
229
318
  output_guardrails: list[OutputGuardrail] | None = None,
319
+ max_turns: int | None = None,
230
320
  ) -> RunResult:
231
321
  """
232
322
  Run an agent with automatic TaskMessage creation.
@@ -249,6 +339,7 @@ class OpenAIService:
249
339
  mcp_timeout_seconds: Optional param to set the timeout threshold for the MCP servers. Defaults to 5 seconds.
250
340
  input_guardrails: Optional list of input guardrails to run on initial user input.
251
341
  output_guardrails: Optional list of output guardrails to run on final agent output.
342
+ max_turns: Maximum number of turns the agent can take. Uses Runner's default if None.
252
343
  Returns:
253
344
  SerializableRunResult: The result of the agent run.
254
345
  """
@@ -276,6 +367,7 @@ class OpenAIService:
276
367
  "tools": tools,
277
368
  "output_type": output_type,
278
369
  "tool_use_behavior": tool_use_behavior,
370
+ "max_turns": max_turns,
279
371
  },
280
372
  ) as span:
281
373
  heartbeat_if_in_workflow("run agent auto send")
@@ -312,7 +404,10 @@ class OpenAIService:
312
404
  agent = Agent(**agent_kwargs)
313
405
 
314
406
  # Run without streaming
315
- result = await Runner.run(starting_agent=agent, input=input_list)
407
+ if max_turns is not None:
408
+ result = await Runner.run(starting_agent=agent, input=input_list, max_turns=max_turns)
409
+ else:
410
+ result = await Runner.run(starting_agent=agent, input=input_list)
316
411
 
317
412
  if span:
318
413
  span.output = {
@@ -325,7 +420,7 @@ class OpenAIService:
325
420
  "final_output": result.final_output,
326
421
  }
327
422
 
328
- tool_call_map: dict[str, ResponseFunctionToolCall] = {}
423
+ tool_call_map: dict[str, Any] = {}
329
424
 
330
425
  for item in result.new_items:
331
426
  if item.type == "message_output_item":
@@ -349,13 +444,17 @@ class OpenAIService:
349
444
  )
350
445
 
351
446
  elif item.type == "tool_call_item":
352
- tool_call_map[item.raw_item.call_id] = item.raw_item
447
+ tool_call_item = item.raw_item
448
+
449
+ # Extract tool call information using the helper method
450
+ call_id, tool_name, tool_arguments = self._extract_tool_call_info(tool_call_item)
451
+ tool_call_map[call_id] = tool_call_item
353
452
 
354
453
  tool_request_content = ToolRequestContent(
355
454
  author="agent",
356
- tool_call_id=item.raw_item.call_id,
357
- name=item.raw_item.name,
358
- arguments=json.loads(item.raw_item.arguments),
455
+ tool_call_id=call_id,
456
+ name=tool_name,
457
+ arguments=tool_arguments,
359
458
  )
360
459
 
361
460
  # Create tool request using streaming context
@@ -376,11 +475,16 @@ class OpenAIService:
376
475
  elif item.type == "tool_call_output_item":
377
476
  tool_output_item = item.raw_item
378
477
 
478
+ # Extract tool response information using the helper method
479
+ call_id, tool_name, content = self._extract_tool_response_info(
480
+ tool_call_map, tool_output_item
481
+ )
482
+
379
483
  tool_response_content = ToolResponseContent(
380
484
  author="agent",
381
- tool_call_id=tool_output_item["call_id"],
382
- name=tool_call_map[tool_output_item["call_id"]].name,
383
- content=tool_output_item["output"],
485
+ tool_call_id=call_id,
486
+ name=tool_name,
487
+ content=content,
384
488
  )
385
489
  # Create tool response using streaming context
386
490
  async with (
@@ -422,6 +526,7 @@ class OpenAIService:
422
526
  mcp_timeout_seconds: int | None = None,
423
527
  input_guardrails: list[InputGuardrail] | None = None,
424
528
  output_guardrails: list[OutputGuardrail] | None = None,
529
+ max_turns: int | None = None,
425
530
  ) -> RunResultStreaming:
426
531
  """
427
532
  Run an agent with streaming enabled but no TaskMessage creation.
@@ -446,6 +551,8 @@ class OpenAIService:
446
551
  initial user input.
447
552
  output_guardrails: Optional list of output guardrails to run on
448
553
  final agent output.
554
+ mcp_timeout_seconds: Optional param to set the timeout threshold for the MCP servers. Defaults to 5 seconds.
555
+ max_turns: Maximum number of turns the agent can take. Uses Runner's default if None.
449
556
  Returns:
450
557
  RunResultStreaming: The result of the agent run with streaming.
451
558
  """
@@ -467,6 +574,7 @@ class OpenAIService:
467
574
  "tools": tools,
468
575
  "output_type": output_type,
469
576
  "tool_use_behavior": tool_use_behavior,
577
+ "max_turns": max_turns,
470
578
  },
471
579
  ) as span:
472
580
  heartbeat_if_in_workflow("run agent streamed")
@@ -503,7 +611,10 @@ class OpenAIService:
503
611
  agent = Agent(**agent_kwargs)
504
612
 
505
613
  # Run with streaming (but no TaskMessage creation)
506
- result = Runner.run_streamed(starting_agent=agent, input=input_list)
614
+ if max_turns is not None:
615
+ result = Runner.run_streamed(starting_agent=agent, input=input_list, max_turns=max_turns)
616
+ else:
617
+ result = Runner.run_streamed(starting_agent=agent, input=input_list)
507
618
 
508
619
  if span:
509
620
  span.output = {
@@ -541,6 +652,7 @@ class OpenAIService:
541
652
  mcp_timeout_seconds: int | None = None,
542
653
  input_guardrails: list[InputGuardrail] | None = None,
543
654
  output_guardrails: list[OutputGuardrail] | None = None,
655
+ max_turns: int | None = None,
544
656
  ) -> RunResultStreaming:
545
657
  """
546
658
  Run an agent with streaming enabled and automatic TaskMessage creation.
@@ -566,6 +678,8 @@ class OpenAIService:
566
678
  initial user input.
567
679
  output_guardrails: Optional list of output guardrails to run on
568
680
  final agent output.
681
+ mcp_timeout_seconds: Optional param to set the timeout threshold for the MCP servers. Defaults to 5 seconds.
682
+ max_turns: Maximum number of turns the agent can take. Uses Runner's default if None.
569
683
 
570
684
  Returns:
571
685
  RunResultStreaming: The result of the agent run with streaming.
@@ -575,7 +689,7 @@ class OpenAIService:
575
689
  if self.agentex_client is None:
576
690
  raise ValueError("Agentex client must be provided for auto_send methods")
577
691
 
578
- tool_call_map: dict[str, ResponseFunctionToolCall] = {}
692
+ tool_call_map: dict[str, Any] = {}
579
693
 
580
694
  trace = self.tracer.trace(trace_id)
581
695
  redacted_params = redact_mcp_server_params(mcp_server_params)
@@ -596,6 +710,7 @@ class OpenAIService:
596
710
  "tools": tools,
597
711
  "output_type": output_type,
598
712
  "tool_use_behavior": tool_use_behavior,
713
+ "max_turns": max_turns,
599
714
  },
600
715
  ) as span:
601
716
  heartbeat_if_in_workflow("run agent streamed auto send")
@@ -632,7 +747,10 @@ class OpenAIService:
632
747
  agent = Agent(**agent_kwargs)
633
748
 
634
749
  # Run with streaming
635
- result = Runner.run_streamed(starting_agent=agent, input=input_list)
750
+ if max_turns is not None:
751
+ result = Runner.run_streamed(starting_agent=agent, input=input_list, max_turns=max_turns)
752
+ else:
753
+ result = Runner.run_streamed(starting_agent=agent, input=input_list)
636
754
 
637
755
  item_id_to_streaming_context: dict[
638
756
  str, StreamingTaskMessageContext
@@ -649,13 +767,16 @@ class OpenAIService:
649
767
  if event.type == "run_item_stream_event":
650
768
  if event.item.type == "tool_call_item":
651
769
  tool_call_item = event.item.raw_item
652
- tool_call_map[tool_call_item.call_id] = tool_call_item
770
+
771
+ # Extract tool call information using the helper method
772
+ call_id, tool_name, tool_arguments = self._extract_tool_call_info(tool_call_item)
773
+ tool_call_map[call_id] = tool_call_item
653
774
 
654
775
  tool_request_content = ToolRequestContent(
655
776
  author="agent",
656
- tool_call_id=tool_call_item.call_id,
657
- name=tool_call_item.name,
658
- arguments=json.loads(tool_call_item.arguments),
777
+ tool_call_id=call_id,
778
+ name=tool_name,
779
+ arguments=tool_arguments,
659
780
  )
660
781
 
661
782
  # Create tool request using streaming context (immediate completion)
@@ -677,13 +798,16 @@ class OpenAIService:
677
798
  elif event.item.type == "tool_call_output_item":
678
799
  tool_output_item = event.item.raw_item
679
800
 
801
+ # Extract tool response information using the helper method
802
+ call_id, tool_name, content = self._extract_tool_response_info(
803
+ tool_call_map, tool_output_item
804
+ )
805
+
680
806
  tool_response_content = ToolResponseContent(
681
807
  author="agent",
682
- tool_call_id=tool_output_item["call_id"],
683
- name=tool_call_map[
684
- tool_output_item["call_id"]
685
- ].name,
686
- content=tool_output_item["output"],
808
+ tool_call_id=call_id,
809
+ name=tool_name,
810
+ content=content,
687
811
  )
688
812
 
689
813
  # Create tool response using streaming context (immediate completion)
@@ -45,6 +45,8 @@ class EventSendParams(BaseModelWithTraceParams):
45
45
  class TaskCancelParams(BaseModelWithTraceParams):
46
46
  task_id: str | None = None
47
47
  task_name: str | None = None
48
+ agent_id: str | None = None
49
+ agent_name: str | None = None
48
50
 
49
51
 
50
52
  class ACPActivities:
@@ -83,4 +85,8 @@ class ACPActivities:
83
85
  return await self._acp_service.task_cancel(
84
86
  task_id=params.task_id,
85
87
  task_name=params.task_name,
88
+ agent_id=params.agent_id,
89
+ agent_name=params.agent_name,
90
+ trace_id=params.trace_id,
91
+ parent_span_id=params.parent_span_id,
86
92
  )