agentscope-runtime 1.0.4__py3-none-any.whl → 1.0.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. agentscope_runtime/adapters/agentscope/stream.py +1 -1
  2. agentscope_runtime/adapters/langgraph/stream.py +120 -70
  3. agentscope_runtime/cli/commands/deploy.py +465 -1
  4. agentscope_runtime/cli/commands/stop.py +16 -0
  5. agentscope_runtime/common/container_clients/__init__.py +52 -0
  6. agentscope_runtime/common/container_clients/agentrun_client.py +6 -4
  7. agentscope_runtime/common/container_clients/boxlite_client.py +442 -0
  8. agentscope_runtime/common/container_clients/docker_client.py +0 -20
  9. agentscope_runtime/common/container_clients/fc_client.py +6 -4
  10. agentscope_runtime/common/container_clients/gvisor_client.py +38 -0
  11. agentscope_runtime/common/container_clients/knative_client.py +1 -0
  12. agentscope_runtime/common/utils/deprecation.py +164 -0
  13. agentscope_runtime/engine/app/agent_app.py +16 -4
  14. agentscope_runtime/engine/deployers/__init__.py +31 -20
  15. agentscope_runtime/engine/deployers/adapter/__init__.py +8 -0
  16. agentscope_runtime/engine/deployers/adapter/a2a/a2a_protocol_adapter.py +9 -8
  17. agentscope_runtime/engine/deployers/adapter/a2a/nacos_a2a_registry.py +19 -1
  18. agentscope_runtime/engine/deployers/adapter/agui/__init__.py +8 -0
  19. agentscope_runtime/engine/deployers/adapter/agui/agui_adapter_utils.py +652 -0
  20. agentscope_runtime/engine/deployers/adapter/agui/agui_protocol_adapter.py +225 -0
  21. agentscope_runtime/engine/deployers/pai_deployer.py +2335 -0
  22. agentscope_runtime/engine/deployers/utils/net_utils.py +37 -0
  23. agentscope_runtime/engine/deployers/utils/oss_utils.py +38 -0
  24. agentscope_runtime/engine/deployers/utils/package.py +46 -42
  25. agentscope_runtime/engine/helpers/agent_api_client.py +372 -0
  26. agentscope_runtime/engine/runner.py +1 -0
  27. agentscope_runtime/engine/schemas/agent_schemas.py +9 -3
  28. agentscope_runtime/engine/services/agent_state/__init__.py +7 -0
  29. agentscope_runtime/engine/services/memory/__init__.py +7 -0
  30. agentscope_runtime/engine/services/memory/redis_memory_service.py +15 -16
  31. agentscope_runtime/engine/services/session_history/__init__.py +7 -0
  32. agentscope_runtime/engine/tracing/local_logging_handler.py +2 -3
  33. agentscope_runtime/sandbox/box/sandbox.py +4 -0
  34. agentscope_runtime/sandbox/manager/sandbox_manager.py +11 -25
  35. agentscope_runtime/sandbox/manager/server/config.py +3 -1
  36. agentscope_runtime/sandbox/model/manager_config.py +11 -9
  37. agentscope_runtime/tools/modelstudio_memory/__init__.py +106 -0
  38. agentscope_runtime/tools/modelstudio_memory/base.py +220 -0
  39. agentscope_runtime/tools/modelstudio_memory/config.py +86 -0
  40. agentscope_runtime/tools/modelstudio_memory/core.py +594 -0
  41. agentscope_runtime/tools/modelstudio_memory/exceptions.py +60 -0
  42. agentscope_runtime/tools/modelstudio_memory/schemas.py +253 -0
  43. agentscope_runtime/version.py +1 -1
  44. {agentscope_runtime-1.0.4.dist-info → agentscope_runtime-1.0.5.dist-info}/METADATA +101 -62
  45. {agentscope_runtime-1.0.4.dist-info → agentscope_runtime-1.0.5.dist-info}/RECORD +49 -34
  46. {agentscope_runtime-1.0.4.dist-info → agentscope_runtime-1.0.5.dist-info}/WHEEL +0 -0
  47. {agentscope_runtime-1.0.4.dist-info → agentscope_runtime-1.0.5.dist-info}/entry_points.txt +0 -0
  48. {agentscope_runtime-1.0.4.dist-info → agentscope_runtime-1.0.5.dist-info}/licenses/LICENSE +0 -0
  49. {agentscope_runtime-1.0.4.dist-info → agentscope_runtime-1.0.5.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,652 @@
1
+ # -*- coding: utf-8 -*-
2
+ from collections import defaultdict
3
+ from enum import Enum
4
+ import logging
5
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union, cast
6
+ from uuid import uuid4
7
+
8
+ from ag_ui.core import RunAgentInput
9
+ from ag_ui.core.events import (
10
+ Event as AGUIEvent,
11
+ EventType as AGUIEventType,
12
+ RunErrorEvent,
13
+ RunFinishedEvent,
14
+ RunStartedEvent,
15
+ TextMessageContentEvent,
16
+ TextMessageEndEvent,
17
+ TextMessageStartEvent,
18
+ ToolCallArgsEvent,
19
+ ToolCallEndEvent,
20
+ ToolCallStartEvent,
21
+ ToolCallResultEvent,
22
+ )
23
+ from ag_ui.core.types import (
24
+ AssistantMessage,
25
+ BinaryInputContent,
26
+ DeveloperMessage,
27
+ SystemMessage,
28
+ TextInputContent,
29
+ ToolMessage,
30
+ UserMessage,
31
+ ActivityMessage,
32
+ Message as AGUIMessage,
33
+ Tool as AGUITool,
34
+ )
35
+ from pydantic import BaseModel, TypeAdapter
36
+
37
+ from ....schemas.agent_schemas import (
38
+ AgentRequest,
39
+ AgentResponse,
40
+ BaseResponse,
41
+ Content,
42
+ ContentType,
43
+ DataContent,
44
+ FunctionCall,
45
+ FunctionCallOutput,
46
+ FunctionTool,
47
+ FunctionParameters,
48
+ ImageContent,
49
+ Message,
50
+ MessageType,
51
+ Role,
52
+ RunStatus,
53
+ TextContent,
54
+ Tool,
55
+ )
56
+
57
+ if TYPE_CHECKING:
58
+ from .agui_protocol_adapter import FlexibleRunAgentInput
59
+
60
+ logger = logging.getLogger(__name__)
61
+
62
+
63
+ # pylint: disable=too-many-branches,too-many-statements,too-many-nested-blocks
64
+ def convert_ag_ui_messages_to_agent_api_messages(
65
+ ag_ui_messages: List[AGUIMessage],
66
+ ) -> List[Message]:
67
+ """
68
+ Convert AG-UI messages to AgentRequest messages.
69
+
70
+ Args:
71
+ ag_ui_messages: List of AG-UI Message objects.
72
+
73
+ Returns:
74
+ List of Message objects compatible with AgentRequest.input
75
+ """
76
+ converted_messages = []
77
+
78
+ for ag_ui_msg in ag_ui_messages:
79
+ message_id = ag_ui_msg.id or f"msg_{uuid4()}"
80
+
81
+ # Handle different AG-UI message types based on class
82
+ if isinstance(ag_ui_msg, (DeveloperMessage, SystemMessage)):
83
+ # Developer/System messages -> MESSAGE type with system role
84
+ content_text = ag_ui_msg.content or ""
85
+ user_msg = Message(
86
+ id=message_id,
87
+ type=MessageType.MESSAGE,
88
+ role=Role.SYSTEM,
89
+ content=[TextContent(text=content_text)],
90
+ )
91
+ converted_messages.append(user_msg)
92
+
93
+ elif isinstance(ag_ui_msg, UserMessage):
94
+ # User messages -> MESSAGE type with user role
95
+ content = ag_ui_msg.content
96
+ user_content = []
97
+
98
+ if isinstance(content, str):
99
+ # Simple text content
100
+ user_content.append(TextContent(text=content))
101
+ elif isinstance(content, list):
102
+ # Multimodal content (text, binary/image, etc.)
103
+ for item in content:
104
+ if isinstance(item, TextInputContent):
105
+ user_content.append(TextContent(text=item.text or ""))
106
+ elif isinstance(item, BinaryInputContent):
107
+ # Handle binary content (e.g., images)
108
+ mime_type = item.mime_type or ""
109
+ if mime_type.startswith("image/"):
110
+ # Convert binary to image content
111
+ image_url = item.url or item.data
112
+ if image_url:
113
+ user_content.append(
114
+ ImageContent(image_url=image_url),
115
+ )
116
+ else:
117
+ # For other binary types, store as data content
118
+ user_content.append(
119
+ DataContent(
120
+ data=item.model_dump(exclude_none=True),
121
+ ),
122
+ )
123
+ else:
124
+ raise ValueError(
125
+ f"Unsupported user message content: {type(content)}",
126
+ )
127
+
128
+ user_msg = Message(
129
+ id=message_id,
130
+ type=MessageType.MESSAGE,
131
+ role=Role.USER,
132
+ content=(
133
+ user_content if user_content else [TextContent(text="")]
134
+ ),
135
+ )
136
+ converted_messages.append(user_msg)
137
+
138
+ elif isinstance(ag_ui_msg, AssistantMessage):
139
+ # Assistant messages can have text content and/or tool_calls
140
+ content_text = ag_ui_msg.content
141
+ tool_calls = ag_ui_msg.tool_calls
142
+
143
+ if tool_calls:
144
+ # Assistant message with tool calls -> FUNCTION_CALL type
145
+ function_call_contents = []
146
+ for tool_call in tool_calls:
147
+ function_data = tool_call.function
148
+ function_call_contents.append(
149
+ DataContent(
150
+ data=FunctionCall(
151
+ call_id=tool_call.id or f"call_{uuid4()}",
152
+ name=function_data.name or "",
153
+ arguments=function_data.arguments or "{}",
154
+ ).model_dump(),
155
+ ),
156
+ )
157
+
158
+ user_msg = Message(
159
+ id=message_id,
160
+ type=MessageType.FUNCTION_CALL,
161
+ role=Role.ASSISTANT,
162
+ content=function_call_contents,
163
+ )
164
+ converted_messages.append(user_msg)
165
+ elif isinstance(content_text, str) and content_text:
166
+ # Assistant message with text only -> MESSAGE type
167
+ user_msg = Message(
168
+ id=message_id,
169
+ type=MessageType.MESSAGE,
170
+ role=Role.ASSISTANT,
171
+ content=[TextContent(text=content_text)],
172
+ )
173
+ converted_messages.append(user_msg)
174
+
175
+ elif isinstance(ag_ui_msg, ToolMessage):
176
+ # Tool messages -> FUNCTION_CALL_OUTPUT type
177
+ tool_call_id = ag_ui_msg.tool_call_id or ""
178
+ if ag_ui_msg.content:
179
+ content_text = ag_ui_msg.content
180
+ elif ag_ui_msg.error:
181
+ content_text = f"error: {ag_ui_msg.error}"
182
+ else:
183
+ content_text = ""
184
+
185
+ user_msg = Message(
186
+ id=message_id,
187
+ type=MessageType.FUNCTION_CALL_OUTPUT,
188
+ role=Role.TOOL,
189
+ content=[
190
+ DataContent(
191
+ data=FunctionCallOutput(
192
+ call_id=tool_call_id,
193
+ output=content_text,
194
+ ).model_dump(),
195
+ ),
196
+ ],
197
+ )
198
+ converted_messages.append(user_msg)
199
+
200
+ elif isinstance(ag_ui_msg, ActivityMessage):
201
+ logger.warning(
202
+ "Activity messages are not supported yet: %s",
203
+ ag_ui_msg,
204
+ )
205
+ else:
206
+ raise ValueError(
207
+ f"Unsupported AG-UI message type: {type(ag_ui_msg)}",
208
+ )
209
+
210
+ return converted_messages
211
+
212
+
213
+ class AGUI_MESSAGE_STATUS(Enum):
214
+ CREATED = "CREATED"
215
+ IN_PROGRESS = "IN_PROGRESS"
216
+ COMPLETED = "COMPLETED"
217
+
218
+
219
+ class AGUIAdapter:
220
+ """
221
+ Utility adapter that converts between Agent API events and AG-UI events.
222
+ """
223
+
224
+ def __init__(
225
+ self,
226
+ thread_id: Optional[str] = None,
227
+ run_id: Optional[str] = None,
228
+ threadId: Optional[str] = None,
229
+ runId: Optional[str] = None,
230
+ ) -> None:
231
+ thread_id = thread_id or threadId
232
+ run_id = run_id or runId
233
+ self.thread_id = thread_id or f"thread_{uuid4()}"
234
+ self.run_id = run_id or f"run_{uuid4()}"
235
+ self._run_started_emitted = False
236
+ self._run_finished_emitted = False
237
+ self._agui_message_status: dict[
238
+ str,
239
+ AGUI_MESSAGE_STATUS,
240
+ ] = defaultdict(lambda: AGUI_MESSAGE_STATUS.CREATED)
241
+ self._message_id_to_agui_message_id_mapping = defaultdict(set[str])
242
+
243
+ @property
244
+ def run_finished_emitted(self) -> bool:
245
+ return self._run_finished_emitted
246
+
247
+ def convert_agui_request_to_agent_request(
248
+ self,
249
+ agui_request: Union[RunAgentInput, "FlexibleRunAgentInput"],
250
+ ) -> AgentRequest:
251
+ """
252
+ Convert an AG-UI request payload to an AgentRequest.
253
+
254
+ Accepts both RunAgentInput and FlexibleRunAgentInput.
255
+ """
256
+ converted_messages = convert_ag_ui_messages_to_agent_api_messages(
257
+ agui_request.messages,
258
+ )
259
+
260
+ user_id_fields = ["user_id", "userId"]
261
+
262
+ user_id = "default_user_id"
263
+ forward_props = agui_request.forwarded_props or {}
264
+ for user_id_field in user_id_fields:
265
+ if user_id_field in forward_props:
266
+ user_id = forward_props[user_id_field]
267
+ break
268
+
269
+ if agui_request.tools:
270
+ tools = [
271
+ self.convert_ag_ui_tool(tool).model_dump()
272
+ for tool in agui_request.tools
273
+ ]
274
+ else:
275
+ tools = []
276
+
277
+ agent_request = AgentRequest.model_validate(
278
+ {
279
+ "input": [
280
+ msg.model_dump(exclude_none=True)
281
+ for msg in converted_messages
282
+ ],
283
+ "stream": True, # AG-UI request is always in stream mode
284
+ "id": self.run_id,
285
+ "session_id": self.thread_id,
286
+ "user_id": user_id,
287
+ "tools": tools,
288
+ },
289
+ )
290
+ return agent_request
291
+
292
+ def convert_ag_ui_tool(self, ag_tool: AGUITool) -> Tool:
293
+ """
294
+ Convert an AG-UI Tool(name/description/parameters) into the Agent API
295
+ Tool.
296
+ """
297
+ params = ag_tool.parameters
298
+
299
+ if isinstance(params, BaseModel):
300
+ params = params.model_dump(
301
+ mode="json",
302
+ exclude_none=True,
303
+ )
304
+
305
+ if params is None:
306
+ params = {
307
+ "type": "object",
308
+ "properties": {},
309
+ "required": [],
310
+ }
311
+
312
+ # If it's not a dict, we can't interpret it as JSON Schema; just wrap
313
+ # as-is
314
+ if not isinstance(params, dict):
315
+ return Tool(
316
+ type="function",
317
+ function=FunctionTool(
318
+ name=ag_tool.name,
319
+ description=ag_tool.description,
320
+ parameters=params, # preserve without crashing
321
+ ),
322
+ )
323
+
324
+ # Heuristic: try to construct FunctionParameters if it matches the
325
+ # expected shape
326
+ schema_type = params.get("type")
327
+ properties = params.get("properties")
328
+ required = params.get("required", None)
329
+
330
+ if schema_type == "object" and isinstance(properties, dict):
331
+ if required is not None and not (
332
+ isinstance(required, list)
333
+ and all(isinstance(x, str) for x in required)
334
+ ):
335
+ required = None
336
+
337
+ fp = FunctionParameters(
338
+ type="object",
339
+ properties=properties,
340
+ required=required,
341
+ )
342
+ converted_params: Union[FunctionParameters, Dict[str, Any]] = fp
343
+ else:
344
+ converted_params = params
345
+
346
+ return Tool(
347
+ type="function",
348
+ function=FunctionTool(
349
+ name=ag_tool.name,
350
+ description=ag_tool.description,
351
+ parameters=converted_params,
352
+ ),
353
+ )
354
+
355
+ def convert_agent_event_to_agui_events(
356
+ self,
357
+ agent_event: Content | Message | AgentResponse,
358
+ ) -> List[AGUIEvent]:
359
+ """
360
+ Convert an Agent API event to one or more AG-UI events.
361
+ """
362
+ if isinstance(agent_event, AgentResponse):
363
+ return self._convert_response_event(agent_event)
364
+ elif isinstance(agent_event, Message):
365
+ return self._convert_message_event(agent_event)
366
+ elif isinstance(agent_event, Content):
367
+ return self._convert_content_event(agent_event)
368
+ else:
369
+ logger.warning(
370
+ f"Ignore not support agent api events: {agent_event}",
371
+ )
372
+ return []
373
+
374
+ def _convert_message_event(
375
+ self,
376
+ message_event: Message,
377
+ ) -> List[AGUIEvent]:
378
+ events: List[AGUIEvent] = []
379
+ events.extend(self._ensure_run_started_event())
380
+ # Process message completion status
381
+ if message_event.status in {RunStatus.Completed}:
382
+ agui_message_ids = self._message_id_to_agui_message_id_mapping[
383
+ message_event.id
384
+ ]
385
+ for agui_message_id in agui_message_ids:
386
+ agui_message_status = self._agui_message_status.get(
387
+ agui_message_id,
388
+ None,
389
+ )
390
+ if not agui_message_status:
391
+ logger.warning(
392
+ "AG UI message not started before Agent API message"
393
+ " completed: %s",
394
+ agui_message_id,
395
+ )
396
+ continue
397
+
398
+ if agui_message_status != AGUI_MESSAGE_STATUS.COMPLETED:
399
+ events.append(
400
+ TextMessageEndEvent(
401
+ message_id=agui_message_id,
402
+ ),
403
+ )
404
+ self._agui_message_status[
405
+ agui_message_id
406
+ ] = AGUI_MESSAGE_STATUS.COMPLETED
407
+
408
+ return events
409
+
410
+ def _convert_response_event(
411
+ self,
412
+ response_event: BaseResponse,
413
+ ) -> List[AGUIEvent]:
414
+ events: List[AGUIEvent] = []
415
+
416
+ if response_event.status == RunStatus.Created:
417
+ events.extend(self._ensure_run_started_event())
418
+ elif response_event.status in {RunStatus.Failed, RunStatus.Rejected}:
419
+ if getattr(response_event, "error", None):
420
+ error_dict = response_event.error.model_dump()
421
+ message = error_dict.get("message", "agent run failed")
422
+ code = error_dict.get("code", "unknown_error")
423
+ else:
424
+ message = "agent run failed"
425
+ code = "unknown_error"
426
+
427
+ events.append(
428
+ self.build_run_event(
429
+ AGUIEventType.RUN_ERROR,
430
+ message=message,
431
+ code=code,
432
+ ),
433
+ )
434
+ self._run_finished_emitted = True
435
+ elif response_event.status in {RunStatus.Completed}:
436
+ self._run_finished_emitted = True
437
+ events.append(
438
+ self.build_run_event(event_type=AGUIEventType.RUN_FINISHED),
439
+ )
440
+ elif response_event.status in {RunStatus.Canceled}:
441
+ self._run_finished_emitted = True
442
+ events.append(
443
+ self.build_run_event(
444
+ event_type=AGUIEventType.RUN_FINISHED,
445
+ result="agent run canceled",
446
+ ),
447
+ )
448
+ else:
449
+ logger.info(f"Not support AgentResponse event: {response_event}")
450
+ return events
451
+
452
+ def _get_msg_content_index(self, content: Content) -> int:
453
+ values = sorted(
454
+ [
455
+ str(v)
456
+ for k, v in vars(ContentType).items()
457
+ if not k.startswith("_") and isinstance(v, str)
458
+ ],
459
+ )
460
+ return values.index(content.type)
461
+
462
+ def _convert_content_event(self, content: Content) -> List[AGUIEvent]:
463
+ events: List[AGUIEvent] = []
464
+ events.extend(self._ensure_run_started_event())
465
+
466
+ def _ensure_agui_text_message_started(
467
+ agui_msg_id: str,
468
+ ) -> List[AGUIEvent]:
469
+ if agui_msg_id in self._agui_message_status:
470
+ return []
471
+ self._agui_message_status[
472
+ agui_msg_id
473
+ ] = AGUI_MESSAGE_STATUS.CREATED
474
+ return [
475
+ TextMessageStartEvent(
476
+ message_id=agui_msg_id,
477
+ ),
478
+ ]
479
+
480
+ def _ensure_agui_tool_call_message_started(
481
+ agui_msg_id: str,
482
+ tool_call: FunctionCall,
483
+ ) -> List[AGUIEvent]:
484
+ if agui_msg_id in self._agui_message_status:
485
+ return []
486
+ self._agui_message_status[
487
+ agui_msg_id
488
+ ] = AGUI_MESSAGE_STATUS.CREATED
489
+ return [
490
+ ToolCallStartEvent(
491
+ tool_call_id=tool_call.call_id,
492
+ tool_call_name=tool_call.name,
493
+ ),
494
+ ]
495
+
496
+ if content.index is None:
497
+ logger.warning("Content Index is Null")
498
+ logger.warning(
499
+ "Content: %s",
500
+ content.model_dump(exclude_none=True),
501
+ )
502
+
503
+ agui_msg_id = content.msg_id
504
+ self._message_id_to_agui_message_id_mapping[content.msg_id].add(
505
+ agui_msg_id,
506
+ )
507
+
508
+ if isinstance(content, TextContent):
509
+ events.extend(_ensure_agui_text_message_started(agui_msg_id))
510
+ if content.delta:
511
+ if (
512
+ self._agui_message_status[agui_msg_id]
513
+ == AGUI_MESSAGE_STATUS.COMPLETED
514
+ ):
515
+ logger.warning(
516
+ "Message already completed: %s",
517
+ agui_msg_id,
518
+ )
519
+ elif content.text:
520
+ self._agui_message_status[
521
+ agui_msg_id
522
+ ] = AGUI_MESSAGE_STATUS.IN_PROGRESS
523
+ events.append(
524
+ TextMessageContentEvent(
525
+ message_id=agui_msg_id,
526
+ delta=content.text,
527
+ ),
528
+ )
529
+ else:
530
+ if (
531
+ self._agui_message_status[agui_msg_id]
532
+ == AGUI_MESSAGE_STATUS.IN_PROGRESS
533
+ ):
534
+ events.append(
535
+ TextMessageEndEvent(
536
+ message_id=agui_msg_id,
537
+ ),
538
+ )
539
+
540
+ self._agui_message_status[
541
+ agui_msg_id
542
+ ] = AGUI_MESSAGE_STATUS.COMPLETED
543
+ elif (
544
+ self._agui_message_status[agui_msg_id]
545
+ == AGUI_MESSAGE_STATUS.CREATED
546
+ ):
547
+ events.append(
548
+ TextMessageContentEvent(
549
+ message_id=agui_msg_id,
550
+ delta=content.text,
551
+ ),
552
+ )
553
+ self._agui_message_status[
554
+ agui_msg_id
555
+ ] = AGUI_MESSAGE_STATUS.COMPLETED
556
+ else:
557
+ logger.warning(
558
+ "AG UI message stream is completed for the "
559
+ "text content: %s",
560
+ content.text,
561
+ )
562
+ elif isinstance(content, DataContent):
563
+ # currently, Agent API Protocol does not support streaming tool
564
+ # calls events
565
+ if agui_msg_id in self._agui_message_status:
566
+ logger.warning(
567
+ "AG UI message stream is completed for the "
568
+ "tool call content: %s",
569
+ content.data,
570
+ )
571
+ elif content.status == RunStatus.Completed:
572
+ X = Union[FunctionCall, FunctionCallOutput]
573
+ ta = TypeAdapter(X)
574
+ val = ta.validate_python(content.data)
575
+
576
+ if isinstance(val, FunctionCall):
577
+ events.extend(
578
+ _ensure_agui_tool_call_message_started(
579
+ agui_msg_id=agui_msg_id,
580
+ tool_call=val,
581
+ ),
582
+ )
583
+ events.append(
584
+ ToolCallArgsEvent(
585
+ tool_call_id=val.call_id,
586
+ delta=val.arguments,
587
+ ),
588
+ )
589
+
590
+ events.append(
591
+ ToolCallEndEvent(
592
+ tool_call_id=val.call_id,
593
+ ),
594
+ )
595
+
596
+ self._agui_message_status[
597
+ agui_msg_id
598
+ ] = AGUI_MESSAGE_STATUS.COMPLETED
599
+ else:
600
+ val = cast(FunctionCallOutput, val)
601
+ events.append(
602
+ ToolCallResultEvent(
603
+ message_id=agui_msg_id,
604
+ tool_call_id=val.call_id,
605
+ content=val.output,
606
+ role=Role.TOOL,
607
+ ),
608
+ )
609
+
610
+ else:
611
+ logger.warning(
612
+ "Not support Agent API Content type: %s, content: %s",
613
+ type(content),
614
+ content.model_dump(exclude_none=True),
615
+ )
616
+
617
+ return events
618
+
619
+ def _ensure_run_started_event(self) -> List[AGUIEvent]:
620
+ if self._run_started_emitted:
621
+ return []
622
+ self._run_started_emitted = True
623
+ return [
624
+ RunStartedEvent(
625
+ thread_id=self.thread_id,
626
+ run_id=self.run_id,
627
+ ),
628
+ ]
629
+
630
+ def build_run_event(
631
+ self,
632
+ event_type: AGUIEventType,
633
+ **kwargs: Any,
634
+ ) -> AGUIEvent:
635
+ if event_type == AGUIEventType.RUN_STARTED:
636
+ return RunStartedEvent(
637
+ thread_id=self.thread_id,
638
+ run_id=self.run_id,
639
+ **kwargs,
640
+ )
641
+ if event_type == AGUIEventType.RUN_FINISHED:
642
+ return RunFinishedEvent(
643
+ thread_id=self.thread_id,
644
+ run_id=self.run_id,
645
+ **kwargs,
646
+ )
647
+ if event_type == AGUIEventType.RUN_ERROR:
648
+ return RunErrorEvent(
649
+ run_id=self.run_id,
650
+ **kwargs,
651
+ )
652
+ raise ValueError(f"Unsupported run event type: {event_type}")