agentscope-runtime 0.1.6__py3-none-any.whl → 0.2.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.
Files changed (87) hide show
  1. agentscope_runtime/common/container_clients/__init__.py +0 -0
  2. agentscope_runtime/{sandbox/manager → common}/container_clients/kubernetes_client.py +546 -6
  3. agentscope_runtime/engine/__init__.py +12 -0
  4. agentscope_runtime/engine/agents/agentscope_agent.py +130 -10
  5. agentscope_runtime/engine/agents/agno_agent.py +8 -10
  6. agentscope_runtime/engine/agents/langgraph_agent.py +52 -9
  7. agentscope_runtime/engine/app/__init__.py +6 -0
  8. agentscope_runtime/engine/app/agent_app.py +239 -0
  9. agentscope_runtime/engine/app/base_app.py +181 -0
  10. agentscope_runtime/engine/app/celery_mixin.py +92 -0
  11. agentscope_runtime/engine/deployers/__init__.py +13 -0
  12. agentscope_runtime/engine/deployers/adapter/responses/__init__.py +0 -0
  13. agentscope_runtime/engine/deployers/adapter/responses/response_api_adapter_utils.py +2890 -0
  14. agentscope_runtime/engine/deployers/adapter/responses/response_api_agent_adapter.py +51 -0
  15. agentscope_runtime/engine/deployers/adapter/responses/response_api_protocol_adapter.py +314 -0
  16. agentscope_runtime/engine/deployers/base.py +1 -0
  17. agentscope_runtime/engine/deployers/cli_fc_deploy.py +203 -0
  18. agentscope_runtime/engine/deployers/kubernetes_deployer.py +272 -0
  19. agentscope_runtime/engine/deployers/local_deployer.py +414 -501
  20. agentscope_runtime/engine/deployers/modelstudio_deployer.py +838 -0
  21. agentscope_runtime/engine/deployers/utils/__init__.py +0 -0
  22. agentscope_runtime/engine/deployers/utils/deployment_modes.py +14 -0
  23. agentscope_runtime/engine/deployers/utils/docker_image_utils/__init__.py +8 -0
  24. agentscope_runtime/engine/deployers/utils/docker_image_utils/docker_image_builder.py +429 -0
  25. agentscope_runtime/engine/deployers/utils/docker_image_utils/dockerfile_generator.py +240 -0
  26. agentscope_runtime/engine/deployers/utils/docker_image_utils/runner_image_factory.py +306 -0
  27. agentscope_runtime/engine/deployers/utils/package_project_utils.py +1163 -0
  28. agentscope_runtime/engine/deployers/utils/service_utils/__init__.py +9 -0
  29. agentscope_runtime/engine/deployers/utils/service_utils/fastapi_factory.py +1064 -0
  30. agentscope_runtime/engine/deployers/utils/service_utils/fastapi_templates.py +157 -0
  31. agentscope_runtime/engine/deployers/utils/service_utils/process_manager.py +268 -0
  32. agentscope_runtime/engine/deployers/utils/service_utils/service_config.py +75 -0
  33. agentscope_runtime/engine/deployers/utils/service_utils/service_factory.py +220 -0
  34. agentscope_runtime/engine/deployers/utils/service_utils/standalone_main.py.j2 +211 -0
  35. agentscope_runtime/engine/deployers/utils/wheel_packager.py +389 -0
  36. agentscope_runtime/engine/helpers/agent_api_builder.py +651 -0
  37. agentscope_runtime/engine/runner.py +76 -35
  38. agentscope_runtime/engine/schemas/agent_schemas.py +112 -2
  39. agentscope_runtime/engine/schemas/embedding.py +37 -0
  40. agentscope_runtime/engine/schemas/modelstudio_llm.py +310 -0
  41. agentscope_runtime/engine/schemas/oai_llm.py +538 -0
  42. agentscope_runtime/engine/schemas/realtime.py +254 -0
  43. agentscope_runtime/engine/services/tablestore_memory_service.py +4 -1
  44. agentscope_runtime/engine/tracing/__init__.py +9 -3
  45. agentscope_runtime/engine/tracing/asyncio_util.py +24 -0
  46. agentscope_runtime/engine/tracing/base.py +66 -34
  47. agentscope_runtime/engine/tracing/local_logging_handler.py +45 -31
  48. agentscope_runtime/engine/tracing/message_util.py +528 -0
  49. agentscope_runtime/engine/tracing/tracing_metric.py +20 -8
  50. agentscope_runtime/engine/tracing/tracing_util.py +130 -0
  51. agentscope_runtime/engine/tracing/wrapper.py +794 -169
  52. agentscope_runtime/sandbox/box/base/base_sandbox.py +2 -1
  53. agentscope_runtime/sandbox/box/browser/browser_sandbox.py +2 -1
  54. agentscope_runtime/sandbox/box/dummy/dummy_sandbox.py +2 -1
  55. agentscope_runtime/sandbox/box/filesystem/filesystem_sandbox.py +2 -1
  56. agentscope_runtime/sandbox/box/gui/gui_sandbox.py +2 -1
  57. agentscope_runtime/sandbox/box/training_box/training_box.py +0 -42
  58. agentscope_runtime/sandbox/client/http_client.py +52 -18
  59. agentscope_runtime/sandbox/constant.py +3 -0
  60. agentscope_runtime/sandbox/custom/custom_sandbox.py +2 -1
  61. agentscope_runtime/sandbox/custom/example.py +2 -1
  62. agentscope_runtime/sandbox/enums.py +0 -1
  63. agentscope_runtime/sandbox/manager/sandbox_manager.py +29 -22
  64. agentscope_runtime/sandbox/model/container.py +6 -0
  65. agentscope_runtime/sandbox/registry.py +1 -1
  66. agentscope_runtime/sandbox/tools/tool.py +4 -0
  67. agentscope_runtime/version.py +1 -1
  68. {agentscope_runtime-0.1.6.dist-info → agentscope_runtime-0.2.0.dist-info}/METADATA +103 -59
  69. {agentscope_runtime-0.1.6.dist-info → agentscope_runtime-0.2.0.dist-info}/RECORD +87 -52
  70. {agentscope_runtime-0.1.6.dist-info → agentscope_runtime-0.2.0.dist-info}/entry_points.txt +1 -0
  71. /agentscope_runtime/{sandbox/manager/container_clients → common}/__init__.py +0 -0
  72. /agentscope_runtime/{sandbox/manager → common}/collections/__init__.py +0 -0
  73. /agentscope_runtime/{sandbox/manager → common}/collections/base_mapping.py +0 -0
  74. /agentscope_runtime/{sandbox/manager → common}/collections/base_queue.py +0 -0
  75. /agentscope_runtime/{sandbox/manager → common}/collections/base_set.py +0 -0
  76. /agentscope_runtime/{sandbox/manager → common}/collections/in_memory_mapping.py +0 -0
  77. /agentscope_runtime/{sandbox/manager → common}/collections/in_memory_queue.py +0 -0
  78. /agentscope_runtime/{sandbox/manager → common}/collections/in_memory_set.py +0 -0
  79. /agentscope_runtime/{sandbox/manager → common}/collections/redis_mapping.py +0 -0
  80. /agentscope_runtime/{sandbox/manager → common}/collections/redis_queue.py +0 -0
  81. /agentscope_runtime/{sandbox/manager → common}/collections/redis_set.py +0 -0
  82. /agentscope_runtime/{sandbox/manager → common}/container_clients/agentrun_client.py +0 -0
  83. /agentscope_runtime/{sandbox/manager → common}/container_clients/base_client.py +0 -0
  84. /agentscope_runtime/{sandbox/manager → common}/container_clients/docker_client.py +0 -0
  85. {agentscope_runtime-0.1.6.dist-info → agentscope_runtime-0.2.0.dist-info}/WHEEL +0 -0
  86. {agentscope_runtime-0.1.6.dist-info → agentscope_runtime-0.2.0.dist-info}/licenses/LICENSE +0 -0
  87. {agentscope_runtime-0.1.6.dist-info → agentscope_runtime-0.2.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,2890 @@
1
+ # -*- coding: utf-8 -*-
2
+ # pylint: disable=too-many-branches,too-many-return-statements,line-too-long
3
+
4
+ """
5
+ Responses Adapter
6
+
7
+ Bidirectional protocol converter: Responses API ↔ Agent API
8
+
9
+ Conversion functions:
10
+ 1. Responses API request → Agent API request
11
+ 2. Agent API event → Responses API event
12
+ 3. Support for streaming and non-streaming conversion
13
+ """
14
+
15
+ import time
16
+ import uuid
17
+ from typing import Any, Dict, List, Optional, Union
18
+
19
+ # OpenAI Responses API Types
20
+ from openai.types.responses import (
21
+ Response,
22
+ ResponseCompletedEvent,
23
+ ResponseContentPartAddedEvent,
24
+ ResponseContentPartDoneEvent,
25
+ ResponseCreatedEvent,
26
+ ResponseErrorEvent,
27
+ ResponseFailedEvent,
28
+ ResponseFunctionCallArgumentsDeltaEvent,
29
+ ResponseFunctionCallArgumentsDoneEvent,
30
+ ResponseInProgressEvent,
31
+ ResponseOutputItemAddedEvent,
32
+ ResponseOutputItemDoneEvent,
33
+ ResponseReasoningTextDeltaEvent,
34
+ ResponseReasoningTextDoneEvent,
35
+ ResponseRefusalDeltaEvent,
36
+ ResponseRefusalDoneEvent,
37
+ ResponseStatus,
38
+ ResponseStreamEvent,
39
+ ResponseTextDeltaEvent,
40
+ ResponseTextDoneEvent,
41
+ )
42
+ from openai.types.responses.response_function_tool_call import (
43
+ ResponseFunctionToolCall,
44
+ )
45
+ from openai.types.responses.response_mcp_call_completed_event import (
46
+ ResponseMcpCallCompletedEvent,
47
+ )
48
+ from openai.types.responses.response_mcp_call_in_progress_event import (
49
+ ResponseMcpCallInProgressEvent,
50
+ )
51
+ from openai.types.responses.response_mcp_list_tools_completed_event import (
52
+ ResponseMcpListToolsCompletedEvent,
53
+ )
54
+ from openai.types.responses.response_mcp_list_tools_in_progress_event import (
55
+ ResponseMcpListToolsInProgressEvent,
56
+ )
57
+ from openai.types.responses.response_output_item import (
58
+ McpCall,
59
+ McpListTools,
60
+ McpListToolsTool,
61
+ ResponseOutputItem,
62
+ )
63
+ from openai.types.responses.response_output_message import (
64
+ ResponseOutputMessage,
65
+ )
66
+ from openai.types.responses.response_output_refusal import (
67
+ ResponseOutputRefusal,
68
+ )
69
+ from openai.types.responses.response_output_text import ResponseOutputText
70
+ from openai.types.responses.response_reasoning_item import (
71
+ ResponseReasoningItem,
72
+ )
73
+
74
+ from openai.types.responses.response_reasoning_item import (
75
+ Content as ReasoningContent,
76
+ )
77
+
78
+ from agentscope_runtime.engine.schemas.agent_schemas import (
79
+ AgentRequest,
80
+ BaseResponse,
81
+ Content,
82
+ ContentType,
83
+ DataContent,
84
+ Event,
85
+ Message,
86
+ MessageType,
87
+ RefusalContent,
88
+ Role,
89
+ RunStatus,
90
+ TextContent,
91
+ ToolCall,
92
+ ToolCallOutput,
93
+ FunctionTool,
94
+ Tool,
95
+ ImageContent,
96
+ AudioContent,
97
+ FileContent,
98
+ )
99
+
100
+
101
+ # Agent API Types
102
+
103
+
104
+ class ResponsesAdapter:
105
+ """
106
+ Bidirectional protocol converter: Responses API ↔ Agent API
107
+
108
+ Main functions:
109
+ 1. Convert Responses API request → Agent API request
110
+ 2. Convert Agent API event → Responses API event
111
+ 3. Convert Responses API event stream → Agent API event stream
112
+ 4. Handle various message types (text, tool calls, reasoning, etc.)
113
+ """
114
+
115
+ def __init__(self):
116
+ self.sequence_counter = 0
117
+ # Temporary storage structure: key is message id, value is dict
118
+ # containing message_type and content_index_list
119
+ self._message_content_index_map: Dict = {}
120
+ # Additional adaptation work for adapting Agent API RAG plugin calls
121
+ # to Responses API FileSearch calls
122
+ self._file_search_call_map: Optional[Dict] = None
123
+ self._output_index: int = 0
124
+ self._output: List[ResponseOutputItem] = []
125
+
126
+ def convert_agent_response_to_responses(
127
+ self,
128
+ agent_response: BaseResponse,
129
+ ):
130
+ # First convert Response
131
+ response = self._convert_agent_response_responses_api(
132
+ agent_response=agent_response,
133
+ )
134
+
135
+ # Convert Message
136
+ messages = self._convert_agent_message_to_responses(
137
+ agent_message_list=agent_response.output,
138
+ )
139
+ response.output = messages
140
+
141
+ # Convert Content
142
+ return response
143
+
144
+ def convert_status_to_responses(self, agent_status: str) -> ResponseStatus:
145
+ if agent_status in (RunStatus.Created, RunStatus.Queued):
146
+ return "queued"
147
+ elif agent_status == RunStatus.InProgress:
148
+ return "in_progress"
149
+ elif agent_status == RunStatus.Completed:
150
+ return "completed"
151
+ elif agent_status == RunStatus.Failed:
152
+ return "failed"
153
+ elif agent_status == RunStatus.Cancelled:
154
+ return "cancelled"
155
+ elif agent_status == RunStatus.Incomplete:
156
+ return "incomplete"
157
+ else:
158
+ return "in_progress"
159
+
160
+ def _convert_agent_message_to_responses(
161
+ self,
162
+ agent_message_list: List[Message],
163
+ ):
164
+ messages = []
165
+ if agent_message_list:
166
+ for message in agent_message_list:
167
+ if message.type == MessageType.MESSAGE:
168
+ output_message = (
169
+ self._convert_message_type_to_output_message(
170
+ message,
171
+ )
172
+ )
173
+ messages.append(output_message)
174
+ if message.type == MessageType.FUNCTION_CALL:
175
+ function_call_message = (
176
+ self._convert_function_call_to_output_message(
177
+ message,
178
+ )
179
+ )
180
+ messages.append(function_call_message)
181
+ if message.type == MessageType.MCP_LIST_TOOLS:
182
+ mcp_list_tools_message = (
183
+ self._convert_mcp_list_tools_to_output_message(
184
+ message,
185
+ )
186
+ )
187
+ messages.append(mcp_list_tools_message)
188
+ if message.type == MessageType.MCP_TOOL_CALL:
189
+ tool_call_message = (
190
+ self._convert_mcp_tool_call_to_output_message(
191
+ message,
192
+ )
193
+ )
194
+ messages.append(tool_call_message)
195
+ if message.type == MessageType.REASONING:
196
+ reasoning_message = (
197
+ self._convert_reasoning_to_output_message(
198
+ message,
199
+ )
200
+ )
201
+ messages.append(reasoning_message)
202
+ return messages
203
+
204
+ def _convert_agent_response_responses_api(
205
+ self,
206
+ agent_response: BaseResponse,
207
+ ):
208
+ status = agent_response.status
209
+ response_status = self.convert_status_to_responses(status)
210
+
211
+ # Extract real data from agent_event
212
+ response_id = (
213
+ getattr(
214
+ agent_response,
215
+ "id",
216
+ f"resp_{uuid.uuid4().hex[:8]}",
217
+ )
218
+ or f"resp_{uuid.uuid4().hex[:8]}"
219
+ )
220
+ created_at = (
221
+ getattr(
222
+ agent_response,
223
+ "created_at",
224
+ time.time(),
225
+ )
226
+ or time.time()
227
+ )
228
+ # Modified: ensure model value returns default empty string when None
229
+ model = getattr(agent_response, "model", "") or ""
230
+ parallel_tool_calls = (
231
+ getattr(
232
+ agent_response,
233
+ "parallel_tool_calls",
234
+ False,
235
+ )
236
+ or False
237
+ )
238
+ tool_choice = getattr(agent_response, "tool_choice", "auto") or "auto"
239
+ tools = getattr(agent_response, "tools", []) or []
240
+ error = getattr(agent_response, "error", None)
241
+
242
+ # Convert Agent API error to Responses API error
243
+ responses_error = None
244
+ if error:
245
+ responses_error = self._convert_agent_error_to_responses_error(
246
+ error,
247
+ )
248
+
249
+ # Create real Response object using data from agent_event
250
+ response = Response(
251
+ id=response_id,
252
+ status=response_status,
253
+ created_at=created_at,
254
+ model=model,
255
+ object="response",
256
+ output=[],
257
+ parallel_tool_calls=parallel_tool_calls,
258
+ tool_choice=tool_choice,
259
+ tools=tools,
260
+ error=responses_error, # Set converted error
261
+ )
262
+
263
+ return response
264
+
265
+ # ===== Request conversion: Responses API → Agent API =====
266
+
267
+ def convert_responses_request_to_agent_request(
268
+ self,
269
+ responses_request: Dict[str, Any],
270
+ ) -> AgentRequest:
271
+ """
272
+ Convert Responses API request to Agent API request
273
+
274
+ Implement automatic assignment of fields with the same name,
275
+ then explicitly handle different field names
276
+
277
+ Args:
278
+ responses_request: OpenAI ResponseCreateParams
279
+
280
+ Returns:
281
+ AgentRequest: Agent API request format
282
+ """
283
+ # 1. Extract input messages
284
+ input_messages = self._extract_input_messages(responses_request)
285
+
286
+ # 2. Automatic assignment of fields with the same name
287
+ common_fields = self._extract_common_fields(
288
+ responses_request=responses_request,
289
+ request_type="agent",
290
+ )
291
+
292
+ # 3. Explicit mapping of different field names
293
+ special_mappings = self._extract_special_mappings(responses_request)
294
+
295
+ # 4. Merge all fields to create AgentRequest
296
+ agent_request_data = {
297
+ "input": input_messages,
298
+ **common_fields,
299
+ **special_mappings,
300
+ }
301
+
302
+ return AgentRequest(**agent_request_data)
303
+
304
+ def _extract_input_messages(
305
+ self,
306
+ responses_request: Dict[str, Any],
307
+ ) -> List[Message]:
308
+ """Extract and convert input messages"""
309
+ input_messages = []
310
+
311
+ # Extract input from responses_request
312
+ if "input" in responses_request and responses_request["input"]:
313
+ input_data = responses_request["input"]
314
+
315
+ # Handle Text input (string) type
316
+ if isinstance(input_data, str):
317
+ message = self._convert_text_input_to_agent_message(input_data)
318
+ input_messages.append(message)
319
+
320
+ # Handle Input item list (array) type
321
+ elif isinstance(input_data, list):
322
+ for input_item in input_data:
323
+ # Filter out developer role (not supported yet)
324
+ if "developer" == input_item.get("role", "user"):
325
+ continue
326
+
327
+ # Handle dictionary format input
328
+ if isinstance(input_item, dict):
329
+ item_type = input_item.get("type")
330
+
331
+ # If there's no type field but has role and content,
332
+ # consider it as message type
333
+ if (
334
+ not item_type
335
+ and "role" in input_item
336
+ and "content" in input_item
337
+ ):
338
+ item_type = "message"
339
+ else:
340
+ item_type = getattr(input_item, "type", None)
341
+
342
+ if item_type == "message":
343
+ # Convert to Agent API Message
344
+ message = self._convert_responses_input_message_to_agent_message( # noqa: E501
345
+ input_item,
346
+ )
347
+ input_messages.append(message)
348
+ elif item_type == "reasoning":
349
+ # Convert to Agent API Message (type=REASONING)
350
+ message = self._convert_reasoning_to_message(
351
+ input_item,
352
+ )
353
+ input_messages.append(message)
354
+ elif item_type == "custom_tool_call":
355
+ # Convert to Agent API Message (type=PLUGIN_CALL)
356
+ message = self._convert_custom_tool_call_to_message(
357
+ input_item,
358
+ )
359
+ input_messages.append(message)
360
+ elif item_type == "custom_tool_call_output":
361
+ # Convert to Agent API Message
362
+ # (type=PLUGIN_CALL_OUTPUT)
363
+ message = (
364
+ self._convert_custom_tool_call_output_to_message(
365
+ input_item,
366
+ )
367
+ )
368
+ input_messages.append(message)
369
+ elif item_type == "function_call":
370
+ # Convert to Agent API Message (type=FUNCTION_CALL)
371
+ message = self._convert_function_call_to_message(
372
+ input_item,
373
+ )
374
+ input_messages.append(message)
375
+ elif item_type == "function_call_output":
376
+ # Convert to Agent API Message
377
+ # (type=FUNCTION_CALL_OUTPUT)
378
+ message = (
379
+ self._convert_function_call_output_to_message(
380
+ input_item,
381
+ )
382
+ )
383
+ input_messages.append(message)
384
+
385
+ return input_messages
386
+
387
+ def _convert_text_input_to_agent_message(self, text_input: str) -> Message:
388
+ """Convert Text input (string) to Agent API Message"""
389
+
390
+ # Create text content
391
+ text_content = TextContent(type="text", text=text_input, delta=False)
392
+
393
+ # Create message
394
+ message = Message(
395
+ role=Role.USER,
396
+ type="message",
397
+ content=[text_content],
398
+ )
399
+
400
+ return message
401
+
402
+ def _extract_common_fields(
403
+ self,
404
+ responses_request: Dict[str, Any],
405
+ request_type: Optional[str] = "agent",
406
+ ) -> Dict[str, Any]:
407
+ """
408
+ Intelligently extract fields with the same name, automatically detect
409
+ and map fields with the same name and type
410
+
411
+ Automatically discover fields with the same name and type in
412
+ ResponseCreateParams and AgentRequest through reflection mechanism
413
+ """
414
+ common_fields = {}
415
+
416
+ # Get AgentRequest field information
417
+ request_fields = None
418
+ if request_type == "workflow":
419
+ request_fields = self._get_workflow_request_field_info()
420
+ else:
421
+ request_fields = self._get_agent_request_field_info()
422
+
423
+ # Iterate through all keys in ResponseCreateParams
424
+ for attr_name in responses_request.keys():
425
+ # Skip private attributes and methods
426
+ if attr_name.startswith("_"):
427
+ continue
428
+
429
+ # Get value
430
+ try:
431
+ value = responses_request[attr_name]
432
+ except KeyError:
433
+ continue
434
+
435
+ # Skip None values
436
+ if value is None:
437
+ continue
438
+
439
+ # Check if AgentRequest has field with same name
440
+ if attr_name not in request_fields:
441
+ continue
442
+
443
+ # Skip fields that need special handling
444
+ if attr_name == "input":
445
+ # input field needs special conversion, not handled here
446
+ continue
447
+ if attr_name == "tools":
448
+ # tools field needs special conversion, convert Responses API
449
+ # format to Agent API format
450
+ converted_tools = self._convert_responses_tools_to_agent_tools(
451
+ value,
452
+ )
453
+ if converted_tools is not None:
454
+ common_fields[attr_name] = converted_tools
455
+ continue
456
+
457
+ # Check if types are compatible
458
+ agent_field_type = request_fields[attr_name]
459
+ if self._is_type_compatible(value, agent_field_type):
460
+ common_fields[attr_name] = value
461
+
462
+ return common_fields
463
+
464
+ def _convert_responses_tools_to_agent_tools(
465
+ self,
466
+ responses_tools: List[
467
+ Dict[
468
+ str,
469
+ Any,
470
+ ]
471
+ ],
472
+ ) -> Optional[List[Any]]:
473
+ """
474
+ Convert Responses API tools format to Agent API tools format
475
+
476
+ Responses API format:
477
+ [{
478
+ "name": "get_weather",
479
+ "description": "Get the current weather in a given location",
480
+ "strict": true,
481
+ "type": "function",
482
+ "parameters": {
483
+ "type": "object",
484
+ "properties": {...},
485
+ "required": [...]
486
+ }
487
+ }]
488
+
489
+ Agent API format:
490
+ [{
491
+ "type": "function",
492
+ "function": {
493
+ "name": "get_weather",
494
+ "description": "Get the current weather in a given location",
495
+ "parameters": {
496
+ "type": "object",
497
+ "properties": {...},
498
+ "required": [...]
499
+ }
500
+ }
501
+ }]
502
+ """
503
+ if not responses_tools or not isinstance(responses_tools, list):
504
+ return None
505
+
506
+ converted_tools = []
507
+ for tool_data in responses_tools:
508
+ if not isinstance(tool_data, dict):
509
+ continue
510
+
511
+ # Extract basic information
512
+ name = tool_data.get("name", "")
513
+ description = tool_data.get("description", "")
514
+ tool_type = tool_data.get("type", "function")
515
+ parameters = tool_data.get("parameters", {})
516
+
517
+ # Skip invalid tools
518
+ if not name:
519
+ continue
520
+
521
+ # Create FunctionTool
522
+ function_tool = FunctionTool(
523
+ name=name,
524
+ description=description,
525
+ parameters=parameters,
526
+ )
527
+
528
+ # Create Agent API Tool
529
+ agent_tool = Tool(type=tool_type, function=function_tool)
530
+
531
+ converted_tools.append(agent_tool)
532
+
533
+ return converted_tools if converted_tools else None
534
+
535
+ def _get_agent_request_field_info(self) -> Dict[str, type]:
536
+ """Get AgentRequest field type information"""
537
+ # Cache field information to avoid repeated calculations
538
+ if not hasattr(self, "_agent_request_fields_cache"):
539
+ from typing import get_type_hints
540
+
541
+ # Get AgentRequest type annotations
542
+ type_hints = get_type_hints(AgentRequest)
543
+ self._agent_request_fields_cache = type_hints
544
+
545
+ return self._agent_request_fields_cache
546
+
547
+ def _is_type_compatible(self, value: Any, target_type: type) -> bool:
548
+ """Check if value type is compatible with target type"""
549
+ if target_type is None:
550
+ return True
551
+
552
+ # Handle Union types (e.g. Optional[str] = Union[str, None])
553
+ if (
554
+ hasattr(
555
+ target_type,
556
+ "__origin__",
557
+ )
558
+ and target_type.__origin__ is Union
559
+ ):
560
+ # Check if compatible with any type in Union
561
+ for union_type in target_type.__args__:
562
+ if union_type is type(None): # Skip None type
563
+ continue
564
+ if self._is_type_compatible(value, union_type):
565
+ return True
566
+ return False
567
+
568
+ # Handle List types
569
+ if (
570
+ hasattr(
571
+ target_type,
572
+ "__origin__",
573
+ )
574
+ and target_type.__origin__ is list
575
+ ):
576
+ if isinstance(value, list):
577
+ return True
578
+ return False
579
+
580
+ # Handle basic types
581
+ try:
582
+ # Direct type check
583
+ if isinstance(value, target_type):
584
+ return True
585
+
586
+ # Special type conversion check - more strict check
587
+ if target_type == str and isinstance(value, (str, int, float)):
588
+ return True
589
+ if target_type == int and isinstance(value, int):
590
+ return True # Only allow integers
591
+ if target_type == float and isinstance(value, (float, int)):
592
+ return True # Allow integer to float conversion
593
+ if target_type == bool and isinstance(value, bool):
594
+ return True # Only allow boolean values
595
+
596
+ except Exception:
597
+ pass
598
+
599
+ return False
600
+
601
+ def _extract_special_mappings(
602
+ self,
603
+ responses_request: Dict[str, Any],
604
+ ) -> Dict[str, Any]:
605
+ """
606
+ Extract different field names, explicit mapping
607
+
608
+ Different field name mappings:
609
+ - max_output_tokens -> max_tokens
610
+ - conversation -> session_id
611
+ - previous_response_id -> previous_response_id
612
+ """
613
+ special_mappings = {}
614
+
615
+ # conversation -> session_id
616
+ if "conversation" in responses_request:
617
+ conversation = responses_request["conversation"]
618
+ if conversation is not None:
619
+ # If conversation is an object, extract ID
620
+ if hasattr(conversation, "id"):
621
+ special_mappings["session_id"] = conversation.id
622
+ elif isinstance(conversation, dict) and "id" in conversation:
623
+ special_mappings["session_id"] = conversation["id"]
624
+ else:
625
+ # If conversation is itself an ID string
626
+ special_mappings["session_id"] = str(conversation)
627
+
628
+ return special_mappings
629
+
630
+ def _convert_responses_input_message_to_agent_message(
631
+ self,
632
+ input_message,
633
+ ) -> Message:
634
+ """Convert Responses API Input message to Agent API Message"""
635
+
636
+ # Extract message attributes
637
+ if isinstance(input_message, dict):
638
+ content = input_message.get("content", [])
639
+ role = input_message.get("role", "user")
640
+ msg_type = input_message.get("type", "message")
641
+ else:
642
+ content = getattr(input_message, "content", [])
643
+ role = getattr(input_message, "role", "user")
644
+ msg_type = getattr(input_message, "type", "message")
645
+
646
+ # Convert content items
647
+ content_list = []
648
+
649
+ # If content is string, directly convert to TextContent
650
+ if isinstance(content, str):
651
+ text_content = TextContent(
652
+ type=ContentType.TEXT,
653
+ text=content,
654
+ delta=False,
655
+ )
656
+ content_list.append(text_content)
657
+ # If content is list, iterate and convert each item
658
+ elif isinstance(content, list):
659
+ for content_item in content:
660
+ agent_content = self._convert_content_item_to_agent_content(
661
+ content_item,
662
+ )
663
+ if agent_content:
664
+ content_list.append(agent_content)
665
+
666
+ # Create Agent API Message
667
+ message = Message(role=role, type=msg_type, content=content_list)
668
+
669
+ return message
670
+
671
+ def _convert_reasoning_to_message(self, input_reasoning) -> Message:
672
+ """Convert Responses API Input reasoning to Agent API Message"""
673
+ # Extract reasoning attributes
674
+ if isinstance(input_reasoning, dict):
675
+ content = input_reasoning.get("content", [])
676
+ else:
677
+ content = getattr(input_reasoning, "content", [])
678
+
679
+ # Convert content items to text content
680
+ content_list = []
681
+
682
+ # Process content
683
+ for content_item in content:
684
+ if isinstance(content_item, dict):
685
+ content_text = content_item.get("text", "")
686
+ else:
687
+ content_text = getattr(content_item, "text", "")
688
+
689
+ if content_text:
690
+ text_content = TextContent(
691
+ type=ContentType.TEXT,
692
+ text=content_text,
693
+ )
694
+ content_list.append(text_content)
695
+
696
+ # Create Agent API Message (type=REASONING)
697
+ message = Message(
698
+ role="assistant",
699
+ # reasoning is usually assistant's reasoning process
700
+ type=MessageType.REASONING,
701
+ content=content_list,
702
+ )
703
+
704
+ return message
705
+
706
+ def _convert_content_item_to_agent_content(self, content_item):
707
+ """Convert content item to Agent API Content"""
708
+
709
+ # Handle dictionary or object format content items
710
+ if isinstance(content_item, dict):
711
+ content_type = content_item.get("type")
712
+ content_text = content_item.get("text")
713
+ content_refusal = content_item.get("refusal")
714
+ image_url = content_item.get("image_url")
715
+ # Audio data is in input_audio object
716
+ input_audio = content_item.get("input_audio", {})
717
+ audio_data = (
718
+ input_audio.get("data")
719
+ if isinstance(
720
+ input_audio,
721
+ dict,
722
+ )
723
+ else None
724
+ )
725
+ audio_format = (
726
+ input_audio.get("format")
727
+ if isinstance(
728
+ input_audio,
729
+ dict,
730
+ )
731
+ else None
732
+ )
733
+ # File data is directly at root level
734
+ file_data = content_item.get("file_data")
735
+ file_id = content_item.get("file_id")
736
+ file_url = content_item.get("file_url")
737
+ filename = content_item.get("filename")
738
+ else:
739
+ content_type = getattr(content_item, "type", None)
740
+ content_text = getattr(content_item, "text", None)
741
+ content_refusal = getattr(content_item, "refusal", None)
742
+ image_url = getattr(content_item, "image_url", None)
743
+ # Audio data is in input_audio object
744
+ input_audio = getattr(content_item, "input_audio", None)
745
+ audio_data = (
746
+ getattr(
747
+ input_audio,
748
+ "data",
749
+ None,
750
+ )
751
+ if input_audio
752
+ else None
753
+ )
754
+ audio_format = (
755
+ getattr(
756
+ input_audio,
757
+ "format",
758
+ None,
759
+ )
760
+ if input_audio
761
+ else None
762
+ )
763
+ # File data is directly at root level
764
+ file_data = getattr(content_item, "file_data", None)
765
+ file_id = getattr(content_item, "file_id", None)
766
+ file_url = getattr(content_item, "file_url", None)
767
+ filename = getattr(content_item, "filename", None)
768
+
769
+ # Convert different types of input content
770
+ if content_type == "input_text" and content_text:
771
+ return TextContent(type=ContentType.TEXT, text=content_text)
772
+ elif content_type == "output_text" and content_text:
773
+ return TextContent(type=ContentType.TEXT, text=content_text)
774
+ elif content_type == "refusal" and content_refusal:
775
+ return RefusalContent(
776
+ type=ContentType.REFUSAL,
777
+ refusal=content_refusal,
778
+ )
779
+ elif content_type == "input_image" and image_url:
780
+ return ImageContent(type=ContentType.IMAGE, image_url=image_url)
781
+ elif content_type == "input_audio" and audio_data:
782
+ return AudioContent(
783
+ type=ContentType.AUDIO,
784
+ data=audio_data,
785
+ format=audio_format,
786
+ )
787
+ elif content_type == "input_file" and (
788
+ file_url or file_id or file_data
789
+ ):
790
+ return FileContent(
791
+ type=ContentType.FILE,
792
+ file_url=file_url,
793
+ file_id=file_id,
794
+ filename=filename,
795
+ )
796
+
797
+ return None
798
+
799
+ # ===== Response conversion: Agent API → Responses API =====
800
+
801
+ def convert_agent_event_to_responses_event(
802
+ self,
803
+ agent_event: Event,
804
+ ) -> Optional[List[ResponseStreamEvent]]:
805
+ """
806
+ Convert Agent API event to Responses API stream event
807
+
808
+ Args:
809
+ agent_event: Agent API Event
810
+
811
+ Returns:
812
+ ResponseStreamEvent or None
813
+ """
814
+ # 1. If it's a response message type, convert to response stream
815
+ # message in responses api
816
+ if isinstance(agent_event, BaseResponse):
817
+ return self._convert_response_to_responses_event(agent_event)
818
+
819
+ # 2. If it's a message message type, convert to corresponding
820
+ # message type
821
+ elif isinstance(agent_event, Message):
822
+ return self._convert_message_to_responses_event(agent_event)
823
+
824
+ # 3. If it's a content message, perform corresponding
825
+ # content conversion
826
+ elif isinstance(agent_event, Content):
827
+ return self._convert_content_to_responses_event(agent_event)
828
+
829
+ # Other types return None for now
830
+ return None
831
+
832
+ def _convert_response_to_responses_event(
833
+ self,
834
+ response_event: BaseResponse,
835
+ ) -> Optional[List[ResponseStreamEvent]]:
836
+ """
837
+ Convert response message type to Responses API stream event
838
+
839
+ Args:
840
+ response_event: Agent API BaseResponse
841
+
842
+ Returns:
843
+ ResponseStreamEvent or None
844
+ """
845
+ status = response_event.status
846
+ responses = []
847
+
848
+ response = self._convert_agent_response_responses_api(response_event)
849
+ response.output = self._output
850
+
851
+ # Create corresponding events based on status
852
+ if status == "created":
853
+ created = ResponseCreatedEvent(
854
+ type="response.created",
855
+ response=response,
856
+ sequence_number=0,
857
+ ) # Will be set uniformly in responses_service
858
+ responses.append(created)
859
+ elif status == "in_progress":
860
+ in_progress = ResponseInProgressEvent(
861
+ type="response.in_progress",
862
+ response=response,
863
+ sequence_number=0,
864
+ ) # Will be set uniformly in responses_service
865
+ responses.append(in_progress)
866
+ elif status == "completed":
867
+ completed = ResponseCompletedEvent(
868
+ type="response.completed",
869
+ response=response,
870
+ sequence_number=0,
871
+ ) # Will be set uniformly in responses_service
872
+ responses.append(completed)
873
+ elif status == "failed":
874
+ failed = ResponseFailedEvent(
875
+ type="response.failed",
876
+ response=response,
877
+ sequence_number=0,
878
+ ) # Will be set uniformly in responses_service
879
+ responses.append(failed)
880
+
881
+ return responses
882
+
883
+ def _convert_message_to_responses_event(
884
+ self,
885
+ message_event: Message,
886
+ ) -> Optional[List[ResponseStreamEvent]]:
887
+ """
888
+ Convert message message type to Responses API stream event
889
+
890
+ Args:
891
+ message_event: Agent API Message
892
+
893
+ Returns:
894
+ ResponseStreamEvent or None
895
+ """
896
+ message_id = message_event.id
897
+
898
+ # Check if message id already exists in temporary storage structure
899
+ if message_id not in self._message_content_index_map:
900
+ # If not, record it in the structure
901
+ self._message_content_index_map[message_id] = {
902
+ "message_type": message_event.type,
903
+ "content_index_list": [],
904
+ }
905
+
906
+ # If message_id doesn't exist, handle new message
907
+ return self._handle_new_message(message_event)
908
+ else:
909
+ # If message_id already exists, handle differently based on
910
+ # message status
911
+ return self._handle_existing_message(message_event)
912
+
913
+ def _get_add_output_index(self, message_id: str):
914
+ output_index = self._output_index
915
+ if (
916
+ self._message_content_index_map
917
+ and self._message_content_index_map[message_id]
918
+ ):
919
+ self._message_content_index_map[message_id][
920
+ "output_index"
921
+ ] = output_index
922
+ self._output_index += 1
923
+ return output_index
924
+
925
+ def _get_output_index(self, message_id: str):
926
+ if (
927
+ self._message_content_index_map
928
+ and self._message_content_index_map[message_id]
929
+ ):
930
+ return self._message_content_index_map[message_id]["output_index"]
931
+ return self._output_index
932
+
933
+ def _handle_new_message(
934
+ self,
935
+ message_event: Message,
936
+ ) -> Optional[List[ResponseStreamEvent]]:
937
+ messages = []
938
+
939
+ # Handle different message types
940
+ if message_event.type == MessageType.FUNCTION_CALL:
941
+ self._get_add_output_index(message_event.id)
942
+
943
+ elif message_event.type == MessageType.REASONING:
944
+ # reasoning directly returns ResponseReasoningItem
945
+ reasoning_item = self._convert_reasoning_to_output_message(
946
+ message_event,
947
+ )
948
+ item_added_event = ResponseOutputItemAddedEvent(
949
+ type="response.output_item.added",
950
+ item=reasoning_item,
951
+ output_index=self._output_index,
952
+ sequence_number=0,
953
+ ) # Will be set uniformly in responses_service
954
+
955
+ # sequence_number will be set uniformly in responses_service
956
+ self._get_add_output_index(message_event.id)
957
+ messages.append(item_added_event)
958
+
959
+ elif message_event.type == MessageType.MCP_LIST_TOOLS:
960
+ # Convert MCP tool list to ResponseOutputMessage
961
+ output_message = self._convert_mcp_list_tools_to_output_message(
962
+ message_event,
963
+ )
964
+ output_item_added_event = ResponseOutputItemAddedEvent(
965
+ type="response.output_item.added",
966
+ item=output_message,
967
+ output_index=self._output_index,
968
+ sequence_number=0,
969
+ ) # Will be set uniformly in responses_service
970
+
971
+ # sequence_number will be set uniformly in responses_service
972
+ self._get_add_output_index(message_event.id)
973
+ messages.append(output_item_added_event)
974
+
975
+ elif message_event.type == MessageType.MCP_TOOL_CALL:
976
+ # Convert MCP tool call to ResponseFunctionToolCall
977
+ function_tool_call = self._convert_mcp_tool_call_to_output_message(
978
+ message_event,
979
+ )
980
+ output_item_added_event = ResponseOutputItemAddedEvent(
981
+ type="response.output_item.added",
982
+ item=function_tool_call,
983
+ output_index=self._output_index,
984
+ sequence_number=0,
985
+ ) # Will be set uniformly in responses_service
986
+
987
+ # sequence_number will be set uniformly in responses_service
988
+ self._get_add_output_index(message_event.id)
989
+ messages.append(output_item_added_event)
990
+
991
+ elif message_event.type == MessageType.MESSAGE:
992
+ # Convert other types to ResponseOutputMessage
993
+ add_output_message = self._convert_message_type_to_output_message(
994
+ message_event,
995
+ )
996
+ add_output_message.content = []
997
+ add_output_message.status = RunStatus.InProgress
998
+ output_item_added_event = ResponseOutputItemAddedEvent(
999
+ type="response.output_item.added",
1000
+ item=add_output_message,
1001
+ output_index=self._output_index,
1002
+ sequence_number=0,
1003
+ ) # Will be set uniformly in responses_service
1004
+
1005
+ # sequence_number will be set uniformly in responses_service
1006
+ self._get_add_output_index(message_event.id)
1007
+ messages.append(output_item_added_event)
1008
+
1009
+ if message_event.status == "completed":
1010
+ output_message = self._convert_message_type_to_output_message(
1011
+ message_event,
1012
+ )
1013
+
1014
+ if not output_message:
1015
+ return messages
1016
+
1017
+ # Generate response.output_item.done
1018
+ # corresponding responses api object
1019
+ # sequence_number will be set uniformly in responses_service
1020
+ event = ResponseOutputItemDoneEvent(
1021
+ type="response.output_item.done",
1022
+ item=output_message,
1023
+ output_index=self._output_index,
1024
+ sequence_number=0,
1025
+ ) # Will be set uniformly in responses_service
1026
+ self._output.append(output_message)
1027
+ messages.append(event)
1028
+
1029
+ return messages
1030
+
1031
+ def _handle_existing_message(
1032
+ self,
1033
+ message_event: Message,
1034
+ ) -> Optional[List[ResponseStreamEvent]]:
1035
+ """
1036
+ # Handle existing message, generate corresponding events
1037
+ # based on message type and status
1038
+
1039
+ Args:
1040
+ message_event: Agent API Message
1041
+
1042
+ Returns:
1043
+ ResponseStreamEvent or None
1044
+ """
1045
+ # Dispatch to corresponding handler functions based on message type
1046
+ if message_event.type == MessageType.MESSAGE:
1047
+ return self._handle_message_status_change(message_event)
1048
+ elif message_event.type == MessageType.FUNCTION_CALL:
1049
+ return self._handle_function_call_status_change(message_event)
1050
+ elif message_event.type == MessageType.MCP_LIST_TOOLS:
1051
+ return self._handle_mcp_list_tools_status_change(message_event)
1052
+ elif message_event.type == MessageType.MCP_TOOL_CALL:
1053
+ return self._handle_mcp_tool_call_status_change(message_event)
1054
+ elif message_event.type == MessageType.REASONING:
1055
+ return self._handle_reasoning_status_change(message_event)
1056
+ elif message_event.type == MessageType.ERROR:
1057
+ return self._handle_error_status_change(message_event)
1058
+
1059
+ return None
1060
+
1061
+ def _handle_message_status_change(
1062
+ self,
1063
+ message_event: Message,
1064
+ ) -> Optional[List[ResponseStreamEvent]]:
1065
+ """
1066
+ Handle MESSAGE type message status changes
1067
+
1068
+ Args:
1069
+ message_event: Agent API Message
1070
+
1071
+ Returns:
1072
+ ResponseStreamEvent list or None
1073
+ """
1074
+ status = getattr(message_event, "status", "completed")
1075
+
1076
+ messages = []
1077
+
1078
+ if status == "completed":
1079
+ output_message = self._convert_message_type_to_output_message(
1080
+ message_event,
1081
+ )
1082
+
1083
+ if not output_message:
1084
+ return messages
1085
+
1086
+ output_index = self._get_output_index(message_id=message_event.id)
1087
+
1088
+ # Generate response.output_item.done
1089
+ # corresponding responses api object
1090
+ # sequence_number will be set uniformly in responses_service
1091
+ event = ResponseOutputItemDoneEvent(
1092
+ type="response.output_item.done",
1093
+ item=output_message,
1094
+ output_index=output_index,
1095
+ sequence_number=0,
1096
+ ) # Will be set uniformly in responses_service
1097
+ self._output.append(output_message)
1098
+ messages.append(event)
1099
+
1100
+ return messages
1101
+
1102
+ def _handle_function_call_status_change(
1103
+ self,
1104
+ message_event: Message,
1105
+ ) -> Optional[List[ResponseStreamEvent]]:
1106
+ """
1107
+ Handle FUNCTION_CALL type message status changes
1108
+
1109
+ Args:
1110
+ message_event: Agent API Message
1111
+
1112
+ Returns:
1113
+ ResponseStreamEvent list or None
1114
+ """
1115
+ status = getattr(message_event, "status", "completed")
1116
+
1117
+ if status == "completed":
1118
+ messages = []
1119
+ output_message = None
1120
+
1121
+ output_message = self._convert_function_call_to_output_message(
1122
+ message_event,
1123
+ )
1124
+
1125
+ if not output_message:
1126
+ return messages
1127
+
1128
+ output_index = self._get_output_index(message_id=message_event.id)
1129
+
1130
+ # Generate response.output_item.done
1131
+ # corresponding responses api object
1132
+ # sequence_number will be set uniformly in responses_service
1133
+ event = ResponseOutputItemDoneEvent(
1134
+ type="response.output_item.done",
1135
+ item=output_message,
1136
+ output_index=output_index,
1137
+ sequence_number=0,
1138
+ ) # Will be set uniformly in responses_service
1139
+ messages.append(event)
1140
+ self._output.append(output_message)
1141
+ return messages
1142
+
1143
+ return None
1144
+
1145
+ def _handle_mcp_list_tools_status_change(
1146
+ self,
1147
+ message_event: Message,
1148
+ ) -> Optional[List[ResponseStreamEvent]]:
1149
+ """
1150
+ Handle MCP_LIST_TOOLS type message status changes
1151
+
1152
+ Args:
1153
+ message_event: Agent API Message
1154
+
1155
+ Returns:
1156
+ ResponseStreamEvent list or None
1157
+ """
1158
+ status = getattr(message_event, "status", "completed")
1159
+ events = []
1160
+
1161
+ # Get output_index
1162
+ output_index = self._get_output_index(message_event.id)
1163
+
1164
+ if status == "in_progress":
1165
+ event = self._create_mcp_list_tools_in_progress_event(
1166
+ message_event,
1167
+ output_index,
1168
+ )
1169
+ if event:
1170
+ events.append(event)
1171
+ elif status == "completed":
1172
+ completed_events = self._create_mcp_list_tools_completed_event(
1173
+ message_event,
1174
+ output_index,
1175
+ )
1176
+ if completed_events:
1177
+ events.extend(completed_events)
1178
+ elif status == "failed":
1179
+ error_message = "MCP list tools operation failed"
1180
+ error_event = self._create_error_event(
1181
+ error_message=error_message,
1182
+ )
1183
+ if error_event:
1184
+ events.append(error_event)
1185
+
1186
+ return events if events else None
1187
+
1188
+ def _handle_mcp_tool_call_status_change(
1189
+ self,
1190
+ message_event: Message,
1191
+ ) -> Optional[List[ResponseStreamEvent]]:
1192
+ """
1193
+ Handle MCP_TOOL_CALL type message status changes
1194
+
1195
+ Args:
1196
+ message_event: Agent API Message
1197
+
1198
+ Returns:
1199
+ ResponseStreamEvent list or None
1200
+ """
1201
+ status = getattr(message_event, "status", "completed")
1202
+ events = []
1203
+
1204
+ # Get output_index
1205
+ output_index = self._get_output_index(message_event.id)
1206
+
1207
+ if status == "in_progress":
1208
+ event = self._create_mcp_tool_call_in_progress_event(
1209
+ message_event,
1210
+ output_index,
1211
+ )
1212
+ if event:
1213
+ events.append(event)
1214
+ elif status == "completed":
1215
+ completed_events = self._create_mcp_tool_call_completed_event(
1216
+ message_event,
1217
+ output_index,
1218
+ )
1219
+ if completed_events:
1220
+ events.extend(completed_events)
1221
+ elif status == "failed":
1222
+ error_message = "MCP tool call operation failed"
1223
+ error_event = self._create_error_event(
1224
+ error_message=error_message,
1225
+ )
1226
+ if error_event:
1227
+ events.append(error_event)
1228
+
1229
+ return events if events else None
1230
+
1231
+ def _handle_reasoning_status_change(
1232
+ self,
1233
+ message_event: Message,
1234
+ ) -> Optional[List[ResponseStreamEvent]]:
1235
+ """
1236
+ Handle REASONING type message status changes
1237
+
1238
+ Args:
1239
+ message_event: Agent API Message
1240
+
1241
+ Returns:
1242
+ ResponseStreamEvent list or None
1243
+ """
1244
+ status = getattr(message_event, "status", "completed")
1245
+
1246
+ if status == "completed":
1247
+ messages = []
1248
+
1249
+ output_message = self._convert_reasoning_to_output_message(
1250
+ message_event,
1251
+ )
1252
+
1253
+ if not output_message:
1254
+ return messages
1255
+
1256
+ # Generate response.output_item.done
1257
+ # corresponding responses api object
1258
+ # sequence_number will be set uniformly in responses_service
1259
+ event = ResponseOutputItemDoneEvent(
1260
+ type="response.output_item.done",
1261
+ item=output_message,
1262
+ output_index=self._output_index,
1263
+ sequence_number=0,
1264
+ ) # Will be set uniformly in responses_service
1265
+ messages.append(event)
1266
+ self._output.append(output_message)
1267
+ return messages
1268
+
1269
+ return None
1270
+
1271
+ def _handle_error_status_change(
1272
+ self,
1273
+ message_event: Message,
1274
+ ) -> Optional[List[ResponseStreamEvent]]:
1275
+ """
1276
+ Handle ERROR type message status changes
1277
+
1278
+ Args:
1279
+ message_event: Agent API Message
1280
+
1281
+ Returns:
1282
+ ResponseStreamEvent list or None
1283
+ """
1284
+ status = getattr(message_event, "status", "completed")
1285
+
1286
+ if status == "completed":
1287
+ messages = []
1288
+
1289
+ output_message = self._convert_error_to_output_message(
1290
+ message_event,
1291
+ )
1292
+
1293
+ if not output_message:
1294
+ return messages
1295
+
1296
+ # Generate response.output_item.done
1297
+ # corresponding responses api object
1298
+ # sequence_number will be set uniformly in responses_service
1299
+ event = ResponseOutputItemDoneEvent(
1300
+ type="response.output_item.done",
1301
+ item=output_message,
1302
+ output_index=self._output_index,
1303
+ sequence_number=0,
1304
+ ) # Will be set uniformly in responses_service
1305
+ messages.append(event)
1306
+ return messages
1307
+
1308
+ return None
1309
+
1310
+ def _convert_message_type_to_output_message(
1311
+ self,
1312
+ message: Message,
1313
+ ) -> ResponseOutputMessage:
1314
+ """
1315
+ Convert normal message type to ResponseOutputMessage
1316
+
1317
+ Args:
1318
+ message: Agent API Message (type='message')
1319
+
1320
+ Returns:
1321
+ ResponseOutputMessage: Responses API output message
1322
+ """
1323
+ # Convert content
1324
+ output_content = []
1325
+ if message.content:
1326
+ for content_item in message.content:
1327
+ if content_item.type == ContentType.TEXT:
1328
+ output_text = ResponseOutputText(
1329
+ type="output_text",
1330
+ text=content_item.text,
1331
+ annotations=[],
1332
+ )
1333
+ output_content.append(output_text)
1334
+ elif content_item.type == ContentType.REFUSAL:
1335
+ # Handle REFUSAL type
1336
+ refusal_text = getattr(content_item, "refusal", "")
1337
+ output_refusal = ResponseOutputRefusal(
1338
+ type="refusal",
1339
+ refusal=refusal_text,
1340
+ )
1341
+ output_content.append(output_refusal)
1342
+
1343
+ return self._create_base_output_message(message, output_content)
1344
+
1345
+ def _convert_function_call_to_output_message(
1346
+ self,
1347
+ message: Message,
1348
+ ) -> ResponseFunctionToolCall:
1349
+ """
1350
+ Convert function_call type to ResponseFunctionToolCall
1351
+
1352
+ Args:
1353
+ message: Agent API Message (type='function_call')
1354
+
1355
+ Returns:
1356
+ ResponseFunctionToolCall: Responses API function tool call
1357
+ """
1358
+ # Convert function call data
1359
+ function_call_data = {}
1360
+ if message.content:
1361
+ for content_item in message.content:
1362
+ if content_item.type == ContentType.DATA:
1363
+ function_call_data = content_item.data
1364
+ break
1365
+
1366
+ if not isinstance(function_call_data, dict):
1367
+ function_call_data = {}
1368
+
1369
+ # Create ResponseFunctionToolCall
1370
+ return ResponseFunctionToolCall(
1371
+ id=message.id,
1372
+ type="function_call",
1373
+ name=function_call_data.get("name", ""),
1374
+ arguments=function_call_data.get("arguments", ""),
1375
+ call_id=function_call_data.get("call_id", ""),
1376
+ status=message.status,
1377
+ )
1378
+
1379
+ def _convert_reasoning_to_output_message(
1380
+ self,
1381
+ message: Message,
1382
+ ) -> ResponseReasoningItem:
1383
+ """
1384
+ Convert reasoning type to ResponseReasoningItem
1385
+
1386
+ Args:
1387
+ message: Agent API Message (type='reasoning')
1388
+
1389
+ Returns:
1390
+ ResponseReasoningItem: Responses API reasoning item
1391
+ """
1392
+ # Extract reasoning text content from message content
1393
+ reasoning_text = ""
1394
+ if message.content:
1395
+ for content_item in message.content:
1396
+ if content_item.type == ContentType.TEXT:
1397
+ reasoning_text = content_item.text
1398
+ break
1399
+
1400
+ # Create ResponseReasoningItem
1401
+ return ResponseReasoningItem(
1402
+ type="reasoning",
1403
+ id=message.id,
1404
+ summary=[], # Empty summary
1405
+ content=(
1406
+ [ReasoningContent(type="reasoning_text", text=reasoning_text)]
1407
+ if reasoning_text
1408
+ else None
1409
+ ),
1410
+ encrypted_content=None,
1411
+ status=None,
1412
+ )
1413
+
1414
+ def _convert_error_to_output_message(
1415
+ self,
1416
+ message: Message,
1417
+ ) -> ResponseOutputMessage:
1418
+ """
1419
+ Convert error type to ResponseOutputMessage
1420
+
1421
+ Args:
1422
+ message: Agent API Message (type='error')
1423
+
1424
+ Returns:
1425
+ ResponseOutputMessage: Responses API output message
1426
+ """
1427
+ # Convert error data to text content
1428
+ output_content = []
1429
+ if message.content:
1430
+ for content_item in message.content:
1431
+ if content_item.type == ContentType.TEXT:
1432
+ # Convert error text content to ResponseOutputText
1433
+ error_text = content_item.text
1434
+ if error_text:
1435
+ output_text_obj = ResponseOutputText(
1436
+ type="output_text",
1437
+ text=error_text,
1438
+ annotations=[],
1439
+ )
1440
+ output_content.append(output_text_obj)
1441
+ elif content_item.type == ContentType.DATA:
1442
+ # Handle error data content
1443
+ error_data = content_item.data
1444
+ if isinstance(error_data, dict):
1445
+ error_message = error_data.get(
1446
+ "message",
1447
+ str(error_data),
1448
+ )
1449
+ if error_message:
1450
+ output_text_obj = ResponseOutputText(
1451
+ type="output_text",
1452
+ text=error_message,
1453
+ annotations=[],
1454
+ )
1455
+ output_content.append(output_text_obj)
1456
+
1457
+ return self._create_base_output_message(message, output_content)
1458
+
1459
+ def _create_base_output_message(
1460
+ self,
1461
+ message: Message,
1462
+ content: List,
1463
+ ) -> ResponseOutputMessage:
1464
+ """
1465
+ Create base ResponseOutputMessage object
1466
+
1467
+ Args:
1468
+ message: Agent API Message
1469
+ content: Converted content list
1470
+
1471
+ Returns:
1472
+ ResponseOutputMessage: Responses API output message
1473
+ """
1474
+ # Determine status
1475
+ status = "completed" # Default status
1476
+ if hasattr(message, "status") and message.status:
1477
+ # Map Agent API status to Responses API status
1478
+ if message.status in ["in_progress", "completed", "incomplete"]:
1479
+ status = message.status
1480
+ else:
1481
+ status = "completed" # Other statuses default to completed
1482
+
1483
+ return ResponseOutputMessage(
1484
+ id=message.id,
1485
+ type="message",
1486
+ role="assistant",
1487
+ content=content,
1488
+ status=status,
1489
+ )
1490
+
1491
+ def _convert_content_to_responses_event(
1492
+ self,
1493
+ content_event,
1494
+ ) -> Optional[ResponseStreamEvent]:
1495
+ """
1496
+ Convert content message type to Responses API stream event
1497
+
1498
+ Args:
1499
+ content_event: Agent API Content
1500
+
1501
+ Returns:
1502
+ ResponseStreamEvent or None
1503
+ """
1504
+ message_id = getattr(content_event, "msg_id", None)
1505
+ if not message_id:
1506
+ return None
1507
+
1508
+ # Query corresponding message id from temporary storage structure
1509
+ # If message id doesn't exist, indicates abnormal situation,
1510
+ # should not process this content
1511
+ if message_id not in self._message_content_index_map:
1512
+ # Abnormal situation: message corresponding to
1513
+ # content event doesn't exist
1514
+ # This usually indicates message processing order issue,
1515
+ # directly return None
1516
+ return None
1517
+
1518
+ # Get message information
1519
+ message_info = self._message_content_index_map[message_id]
1520
+ message_type = message_info["message_type"]
1521
+
1522
+ # plugin calls need special adaptation, streaming not supported
1523
+ if message_type in [
1524
+ MessageType.PLUGIN_CALL,
1525
+ MessageType.PLUGIN_CALL_OUTPUT,
1526
+ ]:
1527
+ return None
1528
+
1529
+ content_indexes = message_info["content_index_list"]
1530
+ output_index = message_info["output_index"]
1531
+
1532
+ # Check if corresponding index already exists
1533
+ # (need to determine index based on content)
1534
+ content_index = content_event.index
1535
+
1536
+ new_content = content_index not in content_indexes
1537
+
1538
+ # If not, insert
1539
+ if new_content:
1540
+ content_indexes.append(content_index)
1541
+
1542
+ # Perform different content adaptation based on message type
1543
+ message_type = message_info["message_type"]
1544
+ if message_type == MessageType.MESSAGE:
1545
+ events = self._convert_message_content_to_responses_event(
1546
+ content_event,
1547
+ new_content,
1548
+ output_index,
1549
+ )
1550
+ return events if events else None
1551
+ elif message_type == MessageType.FUNCTION_CALL:
1552
+ events = self._convert_function_call_content_to_responses_event(
1553
+ content_event,
1554
+ new_content,
1555
+ output_index,
1556
+ )
1557
+ return events if events else None
1558
+ elif message_type == MessageType.REASONING:
1559
+ events = self._convert_reasoning_content_to_responses_event(
1560
+ content_event=content_event,
1561
+ output_index=output_index,
1562
+ )
1563
+ return events if events else None
1564
+ elif message_type == MessageType.ERROR:
1565
+ events = self._convert_error_content_to_responses_event(
1566
+ content_event=content_event,
1567
+ new_content=new_content,
1568
+ )
1569
+ return events if events else None
1570
+
1571
+ return None
1572
+
1573
+ def convert_response_to_agent_events(
1574
+ self,
1575
+ response: Response,
1576
+ ) -> List[Event]:
1577
+ """
1578
+ Convert OpenAI Response object to Agent API Event list
1579
+
1580
+ Args:
1581
+ response: OpenAI Response object
1582
+
1583
+ Returns:
1584
+ Agent API Event list
1585
+ """
1586
+ events = []
1587
+
1588
+ # Reset sequence counter
1589
+ self.sequence_counter = 0
1590
+
1591
+ # Create response created event
1592
+ response_created = BaseResponse(
1593
+ sequence_number=0, # Will be set uniformly in responses_service
1594
+ object="response",
1595
+ status="created",
1596
+ error=None,
1597
+ )
1598
+ events.append(response_created)
1599
+
1600
+ # Process output items
1601
+ for output_item in response.output:
1602
+ if output_item.type == "message":
1603
+ # Convert to Agent API Message and Content events
1604
+ message_events = self._convert_output_message_to_events(
1605
+ output_item,
1606
+ )
1607
+ events.extend(message_events)
1608
+
1609
+ # Create response completed event
1610
+ response_completed = BaseResponse(
1611
+ sequence_number=0, # Will be set uniformly in responses_service
1612
+ object="response",
1613
+ status="completed",
1614
+ error=None,
1615
+ )
1616
+ events.append(response_completed)
1617
+
1618
+ return events
1619
+
1620
+ def _convert_output_message_to_events(self, output_message) -> List[Event]:
1621
+ """Convert OutputMessage to Agent API events"""
1622
+ events = []
1623
+
1624
+ # Create message in progress event
1625
+ message_id = f"msg_{uuid.uuid4().hex[:8]}"
1626
+ message_event = Message(
1627
+ sequence_number=0, # Will be set uniformly in responses_service
1628
+ object="message",
1629
+ status="in_progress",
1630
+ error=None,
1631
+ id=message_id,
1632
+ type=MessageType.MESSAGE,
1633
+ role=Role.ASSISTANT,
1634
+ )
1635
+ events.append(message_event)
1636
+
1637
+ # Process content items
1638
+ for content_item in output_message.content:
1639
+ if content_item.type == "output_text":
1640
+ # Create text content events
1641
+ text_content = TextContent(
1642
+ sequence_number=0,
1643
+ # Will be set uniformly in responses_service
1644
+ object="content",
1645
+ status="completed",
1646
+ error=None,
1647
+ type=ContentType.TEXT,
1648
+ msg_id=message_id,
1649
+ delta=False,
1650
+ text=content_item.text,
1651
+ )
1652
+ events.append(text_content)
1653
+
1654
+ # Create message completed event
1655
+ message_completed = Message(
1656
+ sequence_number=0, # Will be set uniformly in responses_service
1657
+ object="message",
1658
+ status="completed",
1659
+ error=None,
1660
+ id=message_id,
1661
+ type=MessageType.MESSAGE,
1662
+ role=Role.ASSISTANT,
1663
+ )
1664
+ events.append(message_completed)
1665
+
1666
+ return events
1667
+
1668
+ def _convert_custom_tool_call_to_message(self, tool_call_item) -> Message:
1669
+ """Convert Custom tool call to Agent API Message"""
1670
+
1671
+ # Extract tool call attributes
1672
+ if isinstance(tool_call_item, dict):
1673
+ call_id = tool_call_item.get("call_id", "")
1674
+ name = tool_call_item.get("name", "")
1675
+ input_data = tool_call_item.get("input", "")
1676
+ tool_id = tool_call_item.get("id", "")
1677
+ else:
1678
+ call_id = getattr(tool_call_item, "call_id", "")
1679
+ name = getattr(tool_call_item, "name", "")
1680
+ input_data = getattr(tool_call_item, "input", "")
1681
+ tool_id = getattr(tool_call_item, "id", "")
1682
+
1683
+ # Create DataContent containing tool call data
1684
+ tool_call_data = {
1685
+ "call_id": call_id,
1686
+ "name": name,
1687
+ "input": input_data,
1688
+ "id": tool_id,
1689
+ }
1690
+
1691
+ data_content = DataContent(
1692
+ type=ContentType.DATA,
1693
+ data=tool_call_data,
1694
+ delta=False,
1695
+ )
1696
+
1697
+ # Create Message
1698
+ message = Message(type=MessageType.PLUGIN_CALL, content=[data_content])
1699
+
1700
+ return message
1701
+
1702
+ def _convert_custom_tool_call_output_to_message(
1703
+ self,
1704
+ tool_output_item,
1705
+ ) -> Message:
1706
+ """Convert Custom tool call output to Agent API Message"""
1707
+
1708
+ # Extract tool call output attributes
1709
+ if isinstance(tool_output_item, dict):
1710
+ call_id = tool_output_item.get("call_id", "")
1711
+ output = tool_output_item.get("output", "")
1712
+ output_id = tool_output_item.get("id", "")
1713
+ else:
1714
+ call_id = getattr(tool_output_item, "call_id", "")
1715
+ output = getattr(tool_output_item, "output", "")
1716
+ output_id = getattr(tool_output_item, "id", "")
1717
+
1718
+ # Create DataContent containing tool call output data
1719
+ tool_output_data = {
1720
+ "call_id": call_id,
1721
+ "output": output,
1722
+ "id": output_id,
1723
+ }
1724
+
1725
+ data_content = DataContent(
1726
+ type=ContentType.DATA,
1727
+ data=tool_output_data,
1728
+ delta=False,
1729
+ )
1730
+
1731
+ # Create Message
1732
+ message = Message(
1733
+ type=MessageType.PLUGIN_CALL_OUTPUT,
1734
+ content=[data_content],
1735
+ )
1736
+
1737
+ return message
1738
+
1739
+ def _convert_function_call_to_message(self, function_call_item) -> Message:
1740
+ """Convert Function tool call to Agent API Message"""
1741
+
1742
+ # Extract function call attributes
1743
+ if isinstance(function_call_item, dict):
1744
+ name = function_call_item.get("name", "")
1745
+ arguments = function_call_item.get("arguments", "")
1746
+ call_id = function_call_item.get("call_id", "")
1747
+ else:
1748
+ name = getattr(function_call_item, "name", "")
1749
+ arguments = getattr(function_call_item, "arguments", "")
1750
+ call_id = getattr(function_call_item, "call_id", "")
1751
+
1752
+ # Create DataContent containing function call data
1753
+ function_call_data = ToolCall.model_validate(
1754
+ {
1755
+ "name": name,
1756
+ "arguments": arguments,
1757
+ "call_id": call_id,
1758
+ },
1759
+ ).model_dump()
1760
+
1761
+ data_content = DataContent(
1762
+ type=ContentType.DATA,
1763
+ data=function_call_data,
1764
+ )
1765
+
1766
+ # Create Message
1767
+ message = Message(
1768
+ type=MessageType.FUNCTION_CALL,
1769
+ content=[data_content],
1770
+ )
1771
+
1772
+ return message
1773
+
1774
+ def _convert_function_call_output_to_message(
1775
+ self,
1776
+ function_output_item,
1777
+ ) -> Message:
1778
+ """Convert Function tool call output to Agent API Message"""
1779
+
1780
+ # Extract function call output attributes
1781
+ if isinstance(function_output_item, dict):
1782
+ call_id = function_output_item.get("call_id", "")
1783
+ output = function_output_item.get("output", "")
1784
+ else:
1785
+ call_id = getattr(function_output_item, "call_id", "")
1786
+ output = getattr(function_output_item, "output", "")
1787
+
1788
+ # Create DataContent containing function call output data
1789
+ function_output_data = ToolCallOutput.model_validate(
1790
+ {
1791
+ "call_id": call_id,
1792
+ "output": output,
1793
+ },
1794
+ ).model_dump()
1795
+
1796
+ data_content = DataContent(
1797
+ type=ContentType.DATA,
1798
+ data=function_output_data,
1799
+ )
1800
+
1801
+ # Create Message
1802
+ message = Message(
1803
+ type=MessageType.FUNCTION_CALL_OUTPUT,
1804
+ content=[data_content],
1805
+ )
1806
+
1807
+ return message
1808
+
1809
+ # ===== Content adaptation methods =====
1810
+
1811
+ def _convert_message_content_to_responses_event(
1812
+ self,
1813
+ content_event,
1814
+ new_content: bool,
1815
+ output_index: int = 0,
1816
+ ) -> Optional[ResponseStreamEvent]:
1817
+ """
1818
+ Convert MESSAGE type content to Responses API events
1819
+
1820
+ Args:
1821
+ content_event: Agent API Content event
1822
+ new_content: whether it is new content
1823
+
1824
+ Returns:
1825
+ ResponseStreamEvent or None
1826
+ """
1827
+ events = []
1828
+
1829
+ # Determine event type based on content type
1830
+ content_type = getattr(content_event, "type", None)
1831
+ content_status = getattr(content_event, "status", None)
1832
+
1833
+ if content_type == ContentType.TEXT:
1834
+ # If content is new, generate a response.content_part.added event
1835
+ if new_content:
1836
+ content_add_event = self._create_content_part_added_event(
1837
+ content_event,
1838
+ output_index,
1839
+ )
1840
+ events.append(content_add_event)
1841
+
1842
+ # If content is completed, generate a
1843
+ # response.content_part.done event
1844
+ if content_status == "completed":
1845
+ output_text_done_event = self._create_output_text_done_event(
1846
+ content_event,
1847
+ output_index,
1848
+ )
1849
+ events.append(output_text_done_event)
1850
+ content_done_event = self._create_content_part_done_event(
1851
+ content_event,
1852
+ output_index,
1853
+ )
1854
+ events.append(content_done_event)
1855
+
1856
+ if content_status == "in_progress":
1857
+ content_in_progress_event = self._create_text_delta_event(
1858
+ content_event,
1859
+ output_index,
1860
+ )
1861
+ events.append(content_in_progress_event)
1862
+
1863
+ if content_type == ContentType.REFUSAL:
1864
+ if new_content:
1865
+ content_add_event = self._create_content_part_added_event(
1866
+ content_event,
1867
+ output_index,
1868
+ )
1869
+ events.append(content_add_event)
1870
+
1871
+ if content_status == "completed":
1872
+ output_text_done_event = self._create_refusal_text_done_event(
1873
+ content_event,
1874
+ output_index,
1875
+ )
1876
+ events.append(output_text_done_event)
1877
+ content_done_event = self._create_content_part_done_event(
1878
+ content_event,
1879
+ output_index,
1880
+ )
1881
+ events.append(content_done_event)
1882
+
1883
+ if content_status == "in_progress":
1884
+ content_in_progress_event = (
1885
+ self._create_refusal_text_delta_event(
1886
+ content_event,
1887
+ output_index,
1888
+ )
1889
+ )
1890
+ events.append(content_in_progress_event)
1891
+
1892
+ return events
1893
+
1894
+ def _convert_function_call_content_to_responses_event(
1895
+ self,
1896
+ content_event,
1897
+ new_content: bool,
1898
+ output_index: int = 0,
1899
+ ) -> Optional[ResponseStreamEvent]:
1900
+ """
1901
+ Convert FUNCTION_CALL type content to Responses API events
1902
+
1903
+ Args:
1904
+ content_event: Agent API Content event
1905
+ new_content: whether it is new content
1906
+
1907
+ Returns:
1908
+ ResponseStreamEvent or None
1909
+ """
1910
+ events = []
1911
+
1912
+ # Get content type and status
1913
+ content_type = getattr(content_event, "type", None)
1914
+ content_status = getattr(content_event, "status", None)
1915
+
1916
+ if content_type == ContentType.DATA:
1917
+ if new_content:
1918
+ add_event = (
1919
+ self._create_function_call_arguments_add_output_item_event(
1920
+ content_event=content_event,
1921
+ output_index=output_index,
1922
+ )
1923
+ )
1924
+ events.append(add_event)
1925
+
1926
+ # Extract function call information from data
1927
+ function = getattr(content_event, "data", {})
1928
+ if isinstance(function, dict):
1929
+ arguments = function.get("arguments", "")
1930
+
1931
+ # Generate function_call_arguments.delta event
1932
+ if content_status == "in_progress":
1933
+ delta_event = (
1934
+ self._create_function_call_arguments_delta_event(
1935
+ content_event,
1936
+ arguments,
1937
+ output_index,
1938
+ )
1939
+ )
1940
+ events.append(delta_event)
1941
+
1942
+ if content_status == "completed":
1943
+ done_event = (
1944
+ self._create_function_call_arguments_done_event(
1945
+ content_event,
1946
+ arguments,
1947
+ output_index,
1948
+ )
1949
+ )
1950
+ events.append(done_event)
1951
+
1952
+ return events if events else None
1953
+
1954
+ def _convert_reasoning_content_to_responses_event(
1955
+ self,
1956
+ content_event,
1957
+ output_index: int = 0,
1958
+ ) -> Optional[ResponseStreamEvent]:
1959
+ """
1960
+ Convert REASONING type content to Responses API events
1961
+
1962
+ Args:
1963
+ content_event: Agent API Content event
1964
+ new_content: whether it is new content
1965
+ output_index: output index
1966
+
1967
+ Returns:
1968
+ ResponseStreamEvent or None
1969
+ """
1970
+ events = []
1971
+
1972
+ # Get content type and status
1973
+ content_type = getattr(content_event, "type", None)
1974
+ content_status = getattr(content_event, "status", None)
1975
+
1976
+ if content_type == ContentType.TEXT:
1977
+ # Extract reasoning content from text
1978
+ reasoning_text = getattr(content_event, "text", "")
1979
+
1980
+ # Generate reasoning_text.delta event
1981
+ if content_status == "in_progress":
1982
+ delta_event = self._create_reasoning_text_delta_event(
1983
+ content_event,
1984
+ reasoning_text,
1985
+ output_index,
1986
+ )
1987
+ events.append(delta_event)
1988
+
1989
+ if content_status == "completed":
1990
+ # First generate delta event, then generate done event
1991
+ delta_event = self._create_reasoning_text_delta_event(
1992
+ content_event,
1993
+ reasoning_text,
1994
+ output_index,
1995
+ )
1996
+ events.append(delta_event)
1997
+ done_event = self._create_reasoning_text_done_event(
1998
+ content_event,
1999
+ reasoning_text,
2000
+ output_index,
2001
+ )
2002
+ events.append(done_event)
2003
+
2004
+ return events if events else None
2005
+
2006
+ def _convert_error_content_to_responses_event(
2007
+ self,
2008
+ content_event,
2009
+ new_content: bool,
2010
+ ) -> Optional[ResponseStreamEvent]:
2011
+ """
2012
+ Convert ERROR type content to Responses API events
2013
+
2014
+ Args:
2015
+ content_event: Agent API Content event
2016
+ new_content: whether it is new content
2017
+
2018
+ Returns:
2019
+ ResponseStreamEvent or None
2020
+ """
2021
+ events = []
2022
+
2023
+ # Get content type and status
2024
+ content_type = getattr(content_event, "type", None)
2025
+ content_status = getattr(content_event, "status", None)
2026
+
2027
+ if content_type == ContentType.TEXT:
2028
+ # Extract error message from text
2029
+ error_text = getattr(content_event, "text", "")
2030
+
2031
+ if new_content and content_status == "completed":
2032
+ # Generate error event
2033
+ error_event = self._create_error_event(
2034
+ error_message=error_text,
2035
+ )
2036
+ events.append(error_event)
2037
+ elif content_type == ContentType.DATA:
2038
+ # Extract error information from data
2039
+ data = getattr(content_event, "data", {})
2040
+ if isinstance(data, dict):
2041
+ error_message = data.get("message", str(data))
2042
+
2043
+ if new_content and content_status == "completed":
2044
+ # Generate error event
2045
+ error_event = self._create_error_event(
2046
+ error_message=error_message,
2047
+ )
2048
+ events.append(error_event)
2049
+
2050
+ return events if events else None
2051
+
2052
+ def _create_content_part_added_event(
2053
+ self,
2054
+ content_event: Content,
2055
+ output_index: int = 0,
2056
+ ) -> ResponseStreamEvent:
2057
+ """
2058
+ Create response.content_part.added event
2059
+
2060
+ Args:
2061
+ content_event: Agent API Content event
2062
+
2063
+ Returns:
2064
+ ResponseStreamEvent: Responses API event
2065
+ """
2066
+ # Create corresponding part based on content type
2067
+ content_type = getattr(content_event, "type", None)
2068
+
2069
+ if content_type == ContentType.TEXT:
2070
+ part = ResponseOutputText(
2071
+ type="output_text",
2072
+ text="",
2073
+ annotations=[],
2074
+ )
2075
+ elif content_type == ContentType.REFUSAL:
2076
+ part = ResponseOutputRefusal(type="refusal", refusal="")
2077
+ else:
2078
+ # Default to text type
2079
+ part = ResponseOutputText(
2080
+ type="output_text",
2081
+ text=getattr(content_event, "text", ""),
2082
+ annotations=[],
2083
+ )
2084
+
2085
+ # Generate response.content_part.added structure
2086
+ # sequence_number will be set uniformly in responses_service
2087
+ return ResponseContentPartAddedEvent(
2088
+ type="response.content_part.added",
2089
+ content_index=content_event.index,
2090
+ item_id=content_event.msg_id,
2091
+ output_index=output_index,
2092
+ part=part,
2093
+ sequence_number=0,
2094
+ ) # Will be set uniformly in responses_service
2095
+
2096
+ def _create_content_part_done_event(
2097
+ self,
2098
+ content_event: Content,
2099
+ output_index: int = 0,
2100
+ ) -> ResponseStreamEvent:
2101
+ """
2102
+ Create response.content_part.done event
2103
+
2104
+ Args:
2105
+ content_event: Agent API Content event
2106
+
2107
+ Returns:
2108
+ ResponseStreamEvent: Responses API event
2109
+ """
2110
+ # Create corresponding part based on content type
2111
+ content_type = getattr(content_event, "type", None)
2112
+
2113
+ if content_type == ContentType.TEXT:
2114
+ part = ResponseOutputText(
2115
+ type="output_text",
2116
+ text=getattr(content_event, "text", ""),
2117
+ annotations=[],
2118
+ )
2119
+ elif content_type == ContentType.REFUSAL:
2120
+ part = ResponseOutputRefusal(
2121
+ type="refusal",
2122
+ refusal=getattr(
2123
+ content_event,
2124
+ "refusal",
2125
+ "",
2126
+ ),
2127
+ )
2128
+ else:
2129
+ # Default to text type
2130
+ part = ResponseOutputText(
2131
+ type="output_text",
2132
+ text=getattr(content_event, "text", ""),
2133
+ annotations=[],
2134
+ )
2135
+
2136
+ # Generate response.content_part.done structure
2137
+ # sequence_number will be set uniformly in responses_service
2138
+ return ResponseContentPartDoneEvent(
2139
+ type="response.content_part.done",
2140
+ content_index=content_event.index,
2141
+ item_id=content_event.msg_id,
2142
+ output_index=output_index,
2143
+ part=part,
2144
+ sequence_number=0,
2145
+ ) # Will be set uniformly in responses_service
2146
+
2147
+ def _create_output_text_done_event(
2148
+ self,
2149
+ content_event: Content,
2150
+ output_index: int = 0,
2151
+ ) -> ResponseStreamEvent:
2152
+ """
2153
+ Create response.output_text.done event
2154
+
2155
+ Args:
2156
+ content_event: Agent API Content event
2157
+
2158
+ Returns:
2159
+ ResponseStreamEvent: Responses API event
2160
+ """
2161
+ # Generate response.output_text.done structure
2162
+ # sequence_number will be set uniformly in responses_service
2163
+ return ResponseTextDoneEvent(
2164
+ type="response.output_text.done",
2165
+ content_index=content_event.index,
2166
+ item_id=content_event.msg_id,
2167
+ output_index=output_index,
2168
+ text=getattr(content_event, "text", ""),
2169
+ logprobs=[],
2170
+ # Temporarily use empty list, can add logprobs support later
2171
+ sequence_number=0,
2172
+ ) # Will be set uniformly in responses_service
2173
+
2174
+ def _create_text_delta_event(
2175
+ self,
2176
+ content_event: Content,
2177
+ output_index: int = 0,
2178
+ ) -> ResponseStreamEvent:
2179
+ """
2180
+ Create response.output_text.delta event
2181
+
2182
+ Args:
2183
+ content_event: Agent API Content event
2184
+
2185
+ Returns:
2186
+ ResponseStreamEvent: Responses API event
2187
+ """
2188
+ # Generate response.output_text.delta structure
2189
+ # sequence_number will be set uniformly in responses_service
2190
+ return ResponseTextDeltaEvent(
2191
+ type="response.output_text.delta",
2192
+ content_index=content_event.index,
2193
+ item_id=content_event.msg_id,
2194
+ output_index=output_index,
2195
+ delta=getattr(content_event, "text", ""),
2196
+ logprobs=[],
2197
+ # Temporarily use empty list, can add logprobs support later
2198
+ sequence_number=0,
2199
+ ) # Will be set uniformly in responses_service
2200
+
2201
+ def _create_refusal_text_done_event(
2202
+ self,
2203
+ content_event: Content,
2204
+ output_index: int = 0,
2205
+ ) -> ResponseStreamEvent:
2206
+ """
2207
+ Create response.refusal.done event
2208
+
2209
+ Args:
2210
+ content_event: Agent API Content event
2211
+
2212
+ Returns:
2213
+ ResponseStreamEvent: Responses API event
2214
+ """
2215
+ # Generate response.refusal.done structure
2216
+ # sequence_number will be set uniformly in responses_service
2217
+ return ResponseRefusalDoneEvent(
2218
+ type="response.refusal.done",
2219
+ content_index=content_event.index,
2220
+ item_id=content_event.msg_id,
2221
+ output_index=output_index,
2222
+ refusal=getattr(content_event, "refusal", ""),
2223
+ sequence_number=0,
2224
+ ) # Will be set uniformly in responses_service
2225
+
2226
+ def _create_refusal_text_delta_event(
2227
+ self,
2228
+ content_event: Content,
2229
+ output_index: int = 0,
2230
+ ) -> ResponseStreamEvent:
2231
+ """
2232
+ Create response.refusal.delta event
2233
+
2234
+ Args:
2235
+ content_event: Agent API Content event
2236
+
2237
+ Returns:
2238
+ ResponseStreamEvent: Responses API event
2239
+ """
2240
+ # Generate response.refusal.delta structure
2241
+ # sequence_number will be set uniformly in responses_service
2242
+ return ResponseRefusalDeltaEvent(
2243
+ type="response.refusal.delta",
2244
+ content_index=content_event.index,
2245
+ item_id=content_event.msg_id,
2246
+ output_index=output_index,
2247
+ delta=getattr(content_event, "refusal", ""),
2248
+ sequence_number=0,
2249
+ ) # Will be set uniformly in responses_service
2250
+
2251
+ def _next_sequence(self) -> int:
2252
+ """Get next sequence number"""
2253
+ current = self.sequence_counter
2254
+ # sequence_number will be set uniformly in responses_service
2255
+ return current
2256
+
2257
+ # ===== New event creation methods =====
2258
+
2259
+ def _create_function_call_arguments_add_output_item_event(
2260
+ self,
2261
+ content_event,
2262
+ output_index: int = 0,
2263
+ ) -> ResponseStreamEvent:
2264
+ """
2265
+ Create function call corresponding response.output_item.added event
2266
+
2267
+ Args:
2268
+ content_event: Agent API Content event
2269
+ arguments: function call parameters
2270
+ output_index: output index
2271
+
2272
+ Returns:
2273
+ ResponseStreamEvent: Responses API event
2274
+ """
2275
+
2276
+ # Convert function call data
2277
+ function_call_data = {}
2278
+ if content_event:
2279
+ if content_event.type == ContentType.DATA:
2280
+ function_call_data = content_event.data
2281
+
2282
+ if not isinstance(function_call_data, dict):
2283
+ function_call_data = {}
2284
+
2285
+ # Create ResponseFunctionToolCall
2286
+ function_tool_call = ResponseFunctionToolCall(
2287
+ type="function_call",
2288
+ name=function_call_data.get("name", ""),
2289
+ arguments="",
2290
+ call_id=function_call_data.get("call_id", ""),
2291
+ status=content_event.status,
2292
+ )
2293
+
2294
+ return ResponseOutputItemAddedEvent(
2295
+ type="response.output_item.added",
2296
+ item=function_tool_call,
2297
+ output_index=output_index,
2298
+ sequence_number=0,
2299
+ ) # Will be set uniformly in responses_service
2300
+
2301
+ def _create_function_call_arguments_delta_event(
2302
+ self,
2303
+ content_event,
2304
+ arguments: str,
2305
+ output_index: int = 0,
2306
+ ) -> ResponseStreamEvent:
2307
+ """
2308
+ Create response.function_call_arguments.delta event
2309
+
2310
+ Args:
2311
+ content_event: Agent API Content event
2312
+ arguments: function call parameters
2313
+ output_index: output index
2314
+
2315
+ Returns:
2316
+ ResponseStreamEvent: Responses API event
2317
+ """
2318
+ # sequence_number will be set uniformly in responses_service
2319
+ return ResponseFunctionCallArgumentsDeltaEvent(
2320
+ type="response.function_call_arguments.delta",
2321
+ delta=arguments,
2322
+ item_id=content_event.msg_id,
2323
+ output_index=output_index,
2324
+ sequence_number=0,
2325
+ ) # Will be set uniformly in responses_service
2326
+
2327
+ def _create_function_call_arguments_done_event(
2328
+ self,
2329
+ content_event,
2330
+ arguments: str,
2331
+ output_index: int = 0,
2332
+ ) -> ResponseStreamEvent:
2333
+ """
2334
+ Create response.function_call_arguments.done event
2335
+
2336
+ Args:
2337
+ content_event: Agent API Content event
2338
+ arguments: function call parameters
2339
+ output_index: output index
2340
+
2341
+ Returns:
2342
+ ResponseStreamEvent: Responses API event
2343
+ """
2344
+ # sequence_number will be set uniformly in responses_service
2345
+ return ResponseFunctionCallArgumentsDoneEvent(
2346
+ type="response.function_call_arguments.done",
2347
+ arguments=arguments,
2348
+ item_id=content_event.msg_id,
2349
+ output_index=output_index,
2350
+ sequence_number=0,
2351
+ ) # Will be set uniformly in responses_service
2352
+
2353
+ def _create_reasoning_text_delta_event(
2354
+ self,
2355
+ content_event,
2356
+ text: str,
2357
+ output_index: int = 0,
2358
+ ) -> ResponseStreamEvent:
2359
+ """
2360
+ Create response.reasoning_text.delta event
2361
+
2362
+ Args:
2363
+ content_event: Agent API Content event
2364
+ text: reasoning text content
2365
+ output_index: output index
2366
+
2367
+ Returns:
2368
+ ResponseStreamEvent: Responses API event
2369
+ """
2370
+ # sequence_number will be set uniformly in responses_service
2371
+ return ResponseReasoningTextDeltaEvent(
2372
+ type="response.reasoning_text.delta",
2373
+ content_index=content_event.index,
2374
+ delta=text,
2375
+ item_id=content_event.msg_id,
2376
+ output_index=output_index,
2377
+ sequence_number=0,
2378
+ ) # Will be set uniformly in responses_service
2379
+
2380
+ def _create_reasoning_text_done_event(
2381
+ self,
2382
+ content_event,
2383
+ text: str,
2384
+ output_index: int = 0,
2385
+ ) -> ResponseStreamEvent:
2386
+ """
2387
+ Create response.reasoning_text.done event
2388
+
2389
+ Args:
2390
+ content_event: Agent API Content event
2391
+ text: reasoning text content
2392
+ output_index: output index
2393
+
2394
+ Returns:
2395
+ ResponseStreamEvent: Responses API event
2396
+ """
2397
+ # sequence_number will be set uniformly in responses_service
2398
+ return ResponseReasoningTextDoneEvent(
2399
+ type="response.reasoning_text.done",
2400
+ content_index=content_event.index,
2401
+ text=text,
2402
+ item_id=content_event.msg_id,
2403
+ output_index=output_index,
2404
+ sequence_number=0,
2405
+ ) # Will be set uniformly in responses_service
2406
+
2407
+ def _convert_agent_error_to_responses_error(
2408
+ self,
2409
+ agent_error,
2410
+ ) -> Optional[Any]:
2411
+ """
2412
+ Convert Agent API Error to Responses API ResponseError
2413
+
2414
+ Args:
2415
+ agent_error: Agent API Error object
2416
+
2417
+ Returns:
2418
+ ResponseError: Responses API ResponseError or None
2419
+ """
2420
+ if not agent_error:
2421
+ return None
2422
+
2423
+ try:
2424
+ # Extract error information from Agent API Error object
2425
+ error_code = getattr(agent_error, "code", "server_error")
2426
+ error_message = getattr(
2427
+ agent_error,
2428
+ "message",
2429
+ "Unknown error occurred",
2430
+ )
2431
+
2432
+ # Map Agent API error code to Responses API error code
2433
+ # If Agent API code is not in Responses API allowed range,
2434
+ # use server_error as default
2435
+ valid_codes = [
2436
+ "server_error",
2437
+ "rate_limit_exceeded",
2438
+ "invalid_prompt",
2439
+ "vector_store_timeout",
2440
+ "invalid_image",
2441
+ "invalid_image_format",
2442
+ "invalid_base64_image",
2443
+ "invalid_image_url",
2444
+ "image_too_large",
2445
+ "image_too_small",
2446
+ "image_parse_error",
2447
+ "image_content_policy_violation",
2448
+ "invalid_image_mode",
2449
+ "image_file_too_large",
2450
+ "unsupported_image_media_type",
2451
+ "empty_image_file",
2452
+ "failed_to_download_image",
2453
+ "image_file_not_found",
2454
+ ]
2455
+
2456
+ # If Agent API code is in valid range, use directly;
2457
+ # otherwise use server_error
2458
+ mapped_code = (
2459
+ error_code if error_code in valid_codes else "server_error"
2460
+ )
2461
+
2462
+ # Create Responses API ResponseError
2463
+ from openai.types.responses import ResponseError
2464
+
2465
+ return ResponseError(code=mapped_code, message=error_message)
2466
+ except Exception as e:
2467
+ # If conversion fails, log error and return None
2468
+ print(f"Error converting agent error to responses error: {e}")
2469
+ return None
2470
+
2471
+ def _create_error_event(
2472
+ self,
2473
+ error_message: str,
2474
+ ) -> ResponseStreamEvent:
2475
+ """
2476
+ Create error event
2477
+
2478
+ Args:
2479
+ content_event: Agent API Content event
2480
+ error_message: error message
2481
+ output_index: output index
2482
+
2483
+ Returns:
2484
+ ResponseStreamEvent: Responses API event
2485
+ """
2486
+ # sequence_number will be set uniformly in responses_service
2487
+ return ResponseErrorEvent(
2488
+ type="error",
2489
+ message=error_message,
2490
+ sequence_number=0,
2491
+ ) # Will be set uniformly in responses_service
2492
+
2493
+ def _create_function_call_item_added_event(
2494
+ self,
2495
+ content_event,
2496
+ output_index: int = 0,
2497
+ ) -> ResponseStreamEvent:
2498
+ """
2499
+ Create function_call type response.output_item.added event
2500
+
2501
+ Args:
2502
+ content_event: Agent API Content event
2503
+ output_index: output index
2504
+
2505
+ Returns:
2506
+ ResponseStreamEvent: Responses API event
2507
+ """
2508
+ # Extract function call information from data
2509
+ data = getattr(content_event, "data", {})
2510
+ name = data.get("name", "")
2511
+ arguments = data.get("arguments", "")
2512
+ call_id = data.get("call_id", "")
2513
+
2514
+ # Create ResponseFunctionToolCall as item
2515
+ item = ResponseFunctionToolCall(
2516
+ type="function_call",
2517
+ name=name,
2518
+ arguments=arguments,
2519
+ call_id=call_id,
2520
+ id=data.get("id"),
2521
+ status=data.get("status"),
2522
+ )
2523
+
2524
+ # Generate response.output_item.added structure
2525
+ # sequence_number will be set uniformly in responses_service
2526
+ return ResponseOutputItemAddedEvent(
2527
+ type="response.output_item.added",
2528
+ item=item,
2529
+ output_index=output_index,
2530
+ sequence_number=0,
2531
+ ) # Will be set uniformly in responses_service
2532
+
2533
+ def _create_reasoning_item_added_event(
2534
+ self,
2535
+ content_event,
2536
+ output_index: int = 0,
2537
+ ) -> ResponseStreamEvent:
2538
+ """
2539
+ Create reasoning type response.output_item.added event
2540
+
2541
+ Args:
2542
+ content_event: Agent API Content event
2543
+ output_index: output index
2544
+
2545
+ Returns:
2546
+ ResponseStreamEvent: Responses API event
2547
+ """
2548
+ # Extract reasoning content from text
2549
+ reasoning_text = getattr(content_event, "text", "")
2550
+
2551
+ # Create ResponseReasoningItem as item
2552
+ item = ResponseReasoningItem(
2553
+ type="reasoning",
2554
+ id=content_event.msg_id,
2555
+ summary=[], # Empty summary
2556
+ content=(
2557
+ [Content(type="reasoning_text", text=reasoning_text)]
2558
+ if reasoning_text
2559
+ else None
2560
+ ),
2561
+ encrypted_content=None,
2562
+ status=None,
2563
+ )
2564
+
2565
+ # Generate response.output_item.added structure
2566
+ # sequence_number will be set uniformly in responses_service
2567
+ return ResponseOutputItemAddedEvent(
2568
+ type="response.output_item.added",
2569
+ item=item,
2570
+ output_index=output_index,
2571
+ sequence_number=0,
2572
+ ) # Will be set uniformly in responses_service
2573
+
2574
+ def _convert_mcp_list_tools_to_output_message(
2575
+ self,
2576
+ message: Message,
2577
+ ) -> McpListTools:
2578
+ """
2579
+ Convert MCP tool list message to McpListTools
2580
+
2581
+ Args:
2582
+ message: Agent API Message (type='mcp_list_tools')
2583
+
2584
+ Returns:
2585
+ McpListTools: Responses API MCP tool list
2586
+ """
2587
+ # Convert MCP tool list data
2588
+ mcp_data = {}
2589
+ if message.content:
2590
+ for content_item in message.content:
2591
+ if content_item.type == ContentType.DATA:
2592
+ mcp_data = content_item.data
2593
+ break
2594
+
2595
+ if not isinstance(mcp_data, dict):
2596
+ mcp_data = {}
2597
+
2598
+ # Extract tool list information
2599
+ tools_info = mcp_data.get("tools", [])
2600
+ tools = []
2601
+ for tool_info in tools_info:
2602
+ if isinstance(tool_info, dict):
2603
+ tool = McpListToolsTool(
2604
+ name=tool_info.get("name", "unknown"),
2605
+ input_schema=tool_info.get("input_schema", {}),
2606
+ description=tool_info.get("description", ""),
2607
+ annotations=tool_info.get("annotations"),
2608
+ )
2609
+ tools.append(tool)
2610
+
2611
+ # Create McpListTools
2612
+ return McpListTools(
2613
+ id=message.id,
2614
+ server_label=mcp_data.get("server_label", "MCP Server"),
2615
+ tools=tools,
2616
+ type="mcp_list_tools",
2617
+ )
2618
+
2619
+ def _convert_mcp_tool_call_to_output_message(
2620
+ self,
2621
+ message: Message,
2622
+ ) -> McpCall:
2623
+ """
2624
+ Convert MCP tool call message to McpCall
2625
+
2626
+ Args:
2627
+ message: Agent API Message (type='mcp_call')
2628
+
2629
+ Returns:
2630
+ McpCall: Responses API MCP tool call
2631
+ """
2632
+ # Convert MCP tool call data
2633
+ mcp_call_data = {}
2634
+ if message.content:
2635
+ for content_item in message.content:
2636
+ if content_item.type == ContentType.DATA:
2637
+ mcp_call_data = content_item.data
2638
+ break
2639
+
2640
+ if not isinstance(mcp_call_data, dict):
2641
+ mcp_call_data = {}
2642
+
2643
+ # Extract MCP tool call information
2644
+ tool_name = mcp_call_data.get("name", "mcp_tool")
2645
+ tool_arguments = mcp_call_data.get("arguments", "{}")
2646
+ server_label = mcp_call_data.get("server_label", "MCP Server")
2647
+
2648
+ # Create McpCall
2649
+ return McpCall(
2650
+ id=message.id,
2651
+ name=tool_name,
2652
+ arguments=tool_arguments,
2653
+ server_label=server_label,
2654
+ type="mcp_call",
2655
+ error=mcp_call_data.get("error"),
2656
+ output=mcp_call_data.get("output"),
2657
+ )
2658
+
2659
+ def _create_mcp_list_tools_item_added_event(
2660
+ self,
2661
+ content_event,
2662
+ output_index: int = 0,
2663
+ ) -> ResponseStreamEvent:
2664
+ """
2665
+ Create MCP tool list item added event
2666
+
2667
+ Args:
2668
+ content_event: Agent API Content event
2669
+ output_index: output index
2670
+
2671
+ Returns:
2672
+ ResponseStreamEvent: Responses API event
2673
+ """
2674
+ # Extract MCP tool list information from data
2675
+ data = getattr(content_event, "data", {})
2676
+ if not isinstance(data, dict):
2677
+ data = {}
2678
+
2679
+ # Extract tool list information
2680
+ tools_info = data.get("tools", [])
2681
+ tools = []
2682
+ for tool_info in tools_info:
2683
+ if isinstance(tool_info, dict):
2684
+ tool = McpListToolsTool(
2685
+ name=tool_info.get("name", "unknown"),
2686
+ input_schema=tool_info.get("input_schema", {}),
2687
+ description=tool_info.get("description", ""),
2688
+ annotations=tool_info.get("annotations"),
2689
+ )
2690
+ tools.append(tool)
2691
+
2692
+ # Create McpListTools as item
2693
+ item = McpListTools(
2694
+ id=content_event.msg_id,
2695
+ server_label=data.get("server_label", "MCP Server"),
2696
+ tools=tools,
2697
+ type="mcp_list_tools",
2698
+ )
2699
+
2700
+ # Generate response.output_item.added structure
2701
+ # sequence_number will be set uniformly in responses_service
2702
+ return ResponseOutputItemAddedEvent(
2703
+ type="response.output_item.added",
2704
+ item=item,
2705
+ output_index=output_index,
2706
+ sequence_number=0,
2707
+ ) # Will be set uniformly in responses_service
2708
+
2709
+ def _create_mcp_tool_call_item_added_event(
2710
+ self,
2711
+ content_event,
2712
+ output_index: int = 0,
2713
+ ) -> ResponseStreamEvent:
2714
+ """
2715
+ Create MCP tool call item added event
2716
+
2717
+ Args:
2718
+ content_event: Agent API Content event
2719
+ output_index: output index
2720
+
2721
+ Returns:
2722
+ ResponseStreamEvent: Responses API event
2723
+ """
2724
+ # Extract MCP tool call information from data
2725
+ data = getattr(content_event, "data", {})
2726
+ if not isinstance(data, dict):
2727
+ data = {}
2728
+
2729
+ # Extract MCP tool call information
2730
+ tool_name = data.get("name", "mcp_tool")
2731
+ tool_arguments = data.get("arguments", "{}")
2732
+ server_label = data.get("server_label", "MCP Server")
2733
+
2734
+ # Create McpCall as item
2735
+ item = McpCall(
2736
+ id=content_event.msg_id,
2737
+ name=tool_name,
2738
+ arguments=tool_arguments,
2739
+ server_label=server_label,
2740
+ type="mcp_call",
2741
+ error=data.get("error"),
2742
+ output=data.get("output"),
2743
+ )
2744
+
2745
+ # Generate response.output_item.added structure
2746
+ # sequence_number will be set uniformly in responses_service
2747
+ return ResponseOutputItemAddedEvent(
2748
+ type="response.output_item.added",
2749
+ item=item,
2750
+ output_index=output_index,
2751
+ sequence_number=0,
2752
+ ) # Will be set uniformly in responses_service
2753
+
2754
+ def _create_mcp_list_tools_in_progress_event(
2755
+ self,
2756
+ message_event: Message,
2757
+ output_index: int = 0,
2758
+ ) -> ResponseStreamEvent:
2759
+ """
2760
+ Create MCP tool list in_progress event
2761
+
2762
+ Args:
2763
+ message_event: Agent API Message event
2764
+ output_index: output index
2765
+
2766
+ Returns:
2767
+ ResponseStreamEvent: Responses API event
2768
+ """
2769
+ # Generate response.mcp_list_tools.in_progress event
2770
+ # sequence_number will be set uniformly in responses_service
2771
+ return ResponseMcpListToolsInProgressEvent(
2772
+ type="response.mcp_list_tools.in_progress",
2773
+ item_id=message_event.id,
2774
+ output_index=output_index,
2775
+ sequence_number=0,
2776
+ ) # Will be set uniformly in responses_service
2777
+
2778
+ def _create_mcp_list_tools_completed_event(
2779
+ self,
2780
+ message_event: Message,
2781
+ output_index: int = 0,
2782
+ ) -> List[ResponseStreamEvent]:
2783
+ """
2784
+ Create MCP tool list completed event
2785
+
2786
+ Args:
2787
+ message_event: Agent API Message event
2788
+ output_index: output index
2789
+
2790
+ Returns:
2791
+ List[ResponseStreamEvent]: Responses API event list
2792
+ """
2793
+ events = []
2794
+
2795
+ # 1. Generate response.mcp_list_tools.completed event
2796
+ mcp_completed_event = ResponseMcpListToolsCompletedEvent(
2797
+ type="response.mcp_list_tools.completed",
2798
+ item_id=message_event.id,
2799
+ output_index=output_index,
2800
+ sequence_number=0,
2801
+ ) # Will be set uniformly in responses_service
2802
+ events.append(mcp_completed_event)
2803
+
2804
+ # 2. Generate response.output_item.done event
2805
+ output_message = self._convert_mcp_list_tools_to_output_message(
2806
+ message_event,
2807
+ )
2808
+ if output_message:
2809
+ output_item_done_event = ResponseOutputItemDoneEvent(
2810
+ type="response.output_item.done",
2811
+ item=output_message,
2812
+ output_index=output_index,
2813
+ sequence_number=0,
2814
+ ) # Will be set uniformly in responses_service
2815
+ events.append(output_item_done_event)
2816
+ # Add to _output list
2817
+ self._output.append(output_message)
2818
+
2819
+ return events
2820
+
2821
+ def _create_mcp_tool_call_in_progress_event(
2822
+ self,
2823
+ message_event: Message,
2824
+ output_index: int = 0,
2825
+ ) -> ResponseStreamEvent:
2826
+ """
2827
+ Create MCP tool call in_progress event
2828
+
2829
+ Args:
2830
+ message_event: Agent API Message event
2831
+ output_index: output index
2832
+
2833
+ Returns:
2834
+ ResponseStreamEvent: Responses API event
2835
+ """
2836
+ # Generate response.mcp_call.in_progress event
2837
+ # sequence_number will be set uniformly in responses_service
2838
+ return ResponseMcpCallInProgressEvent(
2839
+ type="response.mcp_call.in_progress",
2840
+ item_id=message_event.id,
2841
+ output_index=output_index,
2842
+ sequence_number=0,
2843
+ ) # Will be set uniformly in responses_service
2844
+
2845
+ def _create_mcp_tool_call_completed_event(
2846
+ self,
2847
+ message_event: Message,
2848
+ output_index: int = 0,
2849
+ ) -> List[ResponseStreamEvent]:
2850
+ """
2851
+ Create MCP tool call completed event
2852
+
2853
+ Args:
2854
+ message_event: Agent API Message event
2855
+ output_index: output index
2856
+
2857
+ Returns:
2858
+ List[ResponseStreamEvent]: Responses API event list
2859
+ """
2860
+ events = []
2861
+
2862
+ # 1. Generate response.mcp_call.completed event
2863
+ mcp_completed_event = ResponseMcpCallCompletedEvent(
2864
+ type="response.mcp_call.completed",
2865
+ item_id=message_event.id,
2866
+ output_index=output_index,
2867
+ sequence_number=0,
2868
+ ) # Will be set uniformly in responses_service
2869
+ events.append(mcp_completed_event)
2870
+
2871
+ # 2. Generate response.output_item.done event
2872
+ output_message = self._convert_mcp_tool_call_to_output_message(
2873
+ message_event,
2874
+ )
2875
+ if output_message:
2876
+ output_item_done_event = ResponseOutputItemDoneEvent(
2877
+ type="response.output_item.done",
2878
+ item=output_message,
2879
+ output_index=output_index,
2880
+ sequence_number=0,
2881
+ ) # Will be set uniformly in responses_service
2882
+ events.append(output_item_done_event)
2883
+ # Add to _output list
2884
+ self._output.append(output_message)
2885
+
2886
+ return events
2887
+
2888
+
2889
+ # Export main adapter class
2890
+ __all__ = ["ResponsesAdapter"]