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