agentrun-inner-test 0.0.46__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 (135) hide show
  1. agentrun/__init__.py +325 -0
  2. agentrun/agent_runtime/__client_async_template.py +466 -0
  3. agentrun/agent_runtime/__endpoint_async_template.py +345 -0
  4. agentrun/agent_runtime/__init__.py +53 -0
  5. agentrun/agent_runtime/__runtime_async_template.py +477 -0
  6. agentrun/agent_runtime/api/__data_async_template.py +58 -0
  7. agentrun/agent_runtime/api/__init__.py +6 -0
  8. agentrun/agent_runtime/api/control.py +1362 -0
  9. agentrun/agent_runtime/api/data.py +98 -0
  10. agentrun/agent_runtime/client.py +868 -0
  11. agentrun/agent_runtime/endpoint.py +649 -0
  12. agentrun/agent_runtime/model.py +362 -0
  13. agentrun/agent_runtime/runtime.py +904 -0
  14. agentrun/credential/__client_async_template.py +177 -0
  15. agentrun/credential/__credential_async_template.py +216 -0
  16. agentrun/credential/__init__.py +28 -0
  17. agentrun/credential/api/__init__.py +5 -0
  18. agentrun/credential/api/control.py +606 -0
  19. agentrun/credential/client.py +319 -0
  20. agentrun/credential/credential.py +381 -0
  21. agentrun/credential/model.py +248 -0
  22. agentrun/integration/__init__.py +21 -0
  23. agentrun/integration/agentscope/__init__.py +12 -0
  24. agentrun/integration/agentscope/adapter.py +17 -0
  25. agentrun/integration/agentscope/builtin.py +65 -0
  26. agentrun/integration/agentscope/message_adapter.py +185 -0
  27. agentrun/integration/agentscope/model_adapter.py +60 -0
  28. agentrun/integration/agentscope/tool_adapter.py +59 -0
  29. agentrun/integration/builtin/__init__.py +16 -0
  30. agentrun/integration/builtin/model.py +93 -0
  31. agentrun/integration/builtin/sandbox.py +1234 -0
  32. agentrun/integration/builtin/toolset.py +47 -0
  33. agentrun/integration/crewai/__init__.py +12 -0
  34. agentrun/integration/crewai/adapter.py +9 -0
  35. agentrun/integration/crewai/builtin.py +65 -0
  36. agentrun/integration/crewai/model_adapter.py +31 -0
  37. agentrun/integration/crewai/tool_adapter.py +26 -0
  38. agentrun/integration/google_adk/__init__.py +12 -0
  39. agentrun/integration/google_adk/adapter.py +15 -0
  40. agentrun/integration/google_adk/builtin.py +65 -0
  41. agentrun/integration/google_adk/message_adapter.py +144 -0
  42. agentrun/integration/google_adk/model_adapter.py +46 -0
  43. agentrun/integration/google_adk/tool_adapter.py +235 -0
  44. agentrun/integration/langchain/__init__.py +30 -0
  45. agentrun/integration/langchain/adapter.py +15 -0
  46. agentrun/integration/langchain/builtin.py +71 -0
  47. agentrun/integration/langchain/message_adapter.py +141 -0
  48. agentrun/integration/langchain/model_adapter.py +37 -0
  49. agentrun/integration/langchain/tool_adapter.py +50 -0
  50. agentrun/integration/langgraph/__init__.py +35 -0
  51. agentrun/integration/langgraph/adapter.py +20 -0
  52. agentrun/integration/langgraph/agent_converter.py +1073 -0
  53. agentrun/integration/langgraph/builtin.py +65 -0
  54. agentrun/integration/pydantic_ai/__init__.py +12 -0
  55. agentrun/integration/pydantic_ai/adapter.py +13 -0
  56. agentrun/integration/pydantic_ai/builtin.py +65 -0
  57. agentrun/integration/pydantic_ai/model_adapter.py +44 -0
  58. agentrun/integration/pydantic_ai/tool_adapter.py +19 -0
  59. agentrun/integration/utils/__init__.py +112 -0
  60. agentrun/integration/utils/adapter.py +560 -0
  61. agentrun/integration/utils/canonical.py +164 -0
  62. agentrun/integration/utils/converter.py +134 -0
  63. agentrun/integration/utils/model.py +110 -0
  64. agentrun/integration/utils/tool.py +1759 -0
  65. agentrun/model/__client_async_template.py +357 -0
  66. agentrun/model/__init__.py +57 -0
  67. agentrun/model/__model_proxy_async_template.py +270 -0
  68. agentrun/model/__model_service_async_template.py +267 -0
  69. agentrun/model/api/__init__.py +6 -0
  70. agentrun/model/api/control.py +1173 -0
  71. agentrun/model/api/data.py +196 -0
  72. agentrun/model/client.py +674 -0
  73. agentrun/model/model.py +235 -0
  74. agentrun/model/model_proxy.py +439 -0
  75. agentrun/model/model_service.py +438 -0
  76. agentrun/sandbox/__aio_sandbox_async_template.py +523 -0
  77. agentrun/sandbox/__browser_sandbox_async_template.py +110 -0
  78. agentrun/sandbox/__client_async_template.py +491 -0
  79. agentrun/sandbox/__code_interpreter_sandbox_async_template.py +463 -0
  80. agentrun/sandbox/__init__.py +69 -0
  81. agentrun/sandbox/__sandbox_async_template.py +463 -0
  82. agentrun/sandbox/__template_async_template.py +152 -0
  83. agentrun/sandbox/aio_sandbox.py +905 -0
  84. agentrun/sandbox/api/__aio_data_async_template.py +335 -0
  85. agentrun/sandbox/api/__browser_data_async_template.py +140 -0
  86. agentrun/sandbox/api/__code_interpreter_data_async_template.py +206 -0
  87. agentrun/sandbox/api/__init__.py +19 -0
  88. agentrun/sandbox/api/__sandbox_data_async_template.py +107 -0
  89. agentrun/sandbox/api/aio_data.py +551 -0
  90. agentrun/sandbox/api/browser_data.py +172 -0
  91. agentrun/sandbox/api/code_interpreter_data.py +396 -0
  92. agentrun/sandbox/api/control.py +1051 -0
  93. agentrun/sandbox/api/playwright_async.py +492 -0
  94. agentrun/sandbox/api/playwright_sync.py +492 -0
  95. agentrun/sandbox/api/sandbox_data.py +154 -0
  96. agentrun/sandbox/browser_sandbox.py +185 -0
  97. agentrun/sandbox/client.py +925 -0
  98. agentrun/sandbox/code_interpreter_sandbox.py +823 -0
  99. agentrun/sandbox/model.py +397 -0
  100. agentrun/sandbox/sandbox.py +848 -0
  101. agentrun/sandbox/template.py +217 -0
  102. agentrun/server/__init__.py +191 -0
  103. agentrun/server/agui_normalizer.py +180 -0
  104. agentrun/server/agui_protocol.py +797 -0
  105. agentrun/server/invoker.py +309 -0
  106. agentrun/server/model.py +427 -0
  107. agentrun/server/openai_protocol.py +535 -0
  108. agentrun/server/protocol.py +140 -0
  109. agentrun/server/server.py +208 -0
  110. agentrun/toolset/__client_async_template.py +62 -0
  111. agentrun/toolset/__init__.py +51 -0
  112. agentrun/toolset/__toolset_async_template.py +204 -0
  113. agentrun/toolset/api/__init__.py +17 -0
  114. agentrun/toolset/api/control.py +262 -0
  115. agentrun/toolset/api/mcp.py +100 -0
  116. agentrun/toolset/api/openapi.py +1251 -0
  117. agentrun/toolset/client.py +102 -0
  118. agentrun/toolset/model.py +321 -0
  119. agentrun/toolset/toolset.py +270 -0
  120. agentrun/utils/__data_api_async_template.py +720 -0
  121. agentrun/utils/__init__.py +5 -0
  122. agentrun/utils/__resource_async_template.py +158 -0
  123. agentrun/utils/config.py +258 -0
  124. agentrun/utils/control_api.py +78 -0
  125. agentrun/utils/data_api.py +1120 -0
  126. agentrun/utils/exception.py +151 -0
  127. agentrun/utils/helper.py +108 -0
  128. agentrun/utils/log.py +77 -0
  129. agentrun/utils/model.py +168 -0
  130. agentrun/utils/resource.py +291 -0
  131. agentrun_inner_test-0.0.46.dist-info/METADATA +263 -0
  132. agentrun_inner_test-0.0.46.dist-info/RECORD +135 -0
  133. agentrun_inner_test-0.0.46.dist-info/WHEEL +5 -0
  134. agentrun_inner_test-0.0.46.dist-info/licenses/LICENSE +201 -0
  135. agentrun_inner_test-0.0.46.dist-info/top_level.txt +1 -0
@@ -0,0 +1,217 @@
1
+ """
2
+ This file is auto generated by the code generation script.
3
+ Do not modify this file manually.
4
+ Use the `make codegen` command to regenerate.
5
+
6
+ 当前文件为自动生成的控制 API 客户端代码。请勿手动修改此文件。
7
+ 使用 `make codegen` 命令重新生成。
8
+
9
+ source: agentrun/sandbox/__template_async_template.py
10
+
11
+ Template 高层 API / Template High-Level API
12
+
13
+ 此模块定义沙箱模板资源的高级API。
14
+ This module defines the high-level API for sandbox template resources.
15
+ """
16
+
17
+ from typing import Dict, List, Optional
18
+
19
+ from agentrun.sandbox.model import (
20
+ PageableInput,
21
+ TemplateContainerConfiguration,
22
+ TemplateCredentialConfiguration,
23
+ TemplateInput,
24
+ TemplateLogConfiguration,
25
+ TemplateMcpOptions,
26
+ TemplateMcpState,
27
+ TemplateNetworkConfiguration,
28
+ TemplateOssConfiguration,
29
+ TemplateType,
30
+ )
31
+ from agentrun.utils.config import Config
32
+ from agentrun.utils.model import BaseModel
33
+
34
+
35
+ class Template(BaseModel):
36
+ """Template 实例
37
+
38
+ 封装了 Template 的基本信息和操作方法
39
+ """
40
+
41
+ template_id: Optional[str] = None
42
+ """模板 ID / Template ID"""
43
+ template_name: Optional[str] = None
44
+ """模板名称 / Template Name"""
45
+ template_version: Optional[str] = None
46
+ """模板版本 / Template Version"""
47
+ template_arn: Optional[str] = None
48
+ """模板 ARN / Template ARN"""
49
+ resource_name: Optional[str] = None
50
+ """资源名称 / Resource Name"""
51
+ template_type: Optional[TemplateType] = None
52
+ """模板类型 / Template Type"""
53
+ cpu: Optional[float] = None
54
+ """CPU 核数 / CPU Cores"""
55
+ memory: Optional[int] = None
56
+ """内存大小(MB) / Memory Size (MB)"""
57
+ disk_size: Optional[int] = None
58
+ """磁盘大小(GB) / Disk Size (GB)"""
59
+ description: Optional[str] = None
60
+ """描述 / Description"""
61
+ execution_role_arn: Optional[str] = None
62
+ """执行角色 ARN / Execution Role ARN"""
63
+ sandbox_idle_timeout_in_seconds: Optional[int] = None
64
+ """沙箱空闲超时时间(秒) / Sandbox Idle Timeout (seconds)"""
65
+ sandbox_ttlin_seconds: Optional[int] = None
66
+ """沙箱存活时间(秒) / Sandbox TTL (seconds)"""
67
+ share_concurrency_limit_per_sandbox: Optional[int] = None
68
+ """每个沙箱的最大并发会话数 / Max Concurrency Limit Per Sandbox"""
69
+ template_configuration: Optional[Dict] = None
70
+ """模板配置 / Template Configuration"""
71
+ environment_variables: Optional[Dict] = None
72
+ """环境变量 / Environment Variables"""
73
+ network_configuration: Optional[TemplateNetworkConfiguration] = None
74
+ """网络配置 / Network Configuration"""
75
+ oss_configuration: Optional[List[TemplateOssConfiguration]] = None
76
+ """OSS 配置列表 / OSS Configuration List"""
77
+ log_configuration: Optional[TemplateLogConfiguration] = None
78
+ """日志配置 / Log Configuration"""
79
+ credential_configuration: Optional[TemplateCredentialConfiguration] = None
80
+ """凭证配置 / Credential Configuration"""
81
+ container_configuration: Optional[TemplateContainerConfiguration] = None
82
+ """容器配置 / Container Configuration"""
83
+ mcp_options: Optional[TemplateMcpOptions] = None
84
+ """MCP 选项 / MCP Options"""
85
+ mcp_state: Optional[TemplateMcpState] = None
86
+ """MCP 状态 / MCP State"""
87
+ allow_anonymous_manage: Optional[bool] = None
88
+ """是否允许匿名管理 / Whether to allow anonymous management"""
89
+ created_at: Optional[str] = None
90
+ """创建时间 / Creation Time"""
91
+ last_updated_at: Optional[str] = None
92
+ """最后更新时间 / Last Updated Time"""
93
+ status: Optional[str] = None
94
+ """状态 / Status"""
95
+ status_reason: Optional[str] = None
96
+ """状态原因 / Status Reason"""
97
+
98
+ @classmethod
99
+ def __get_client(cls, config: Optional[Config] = None):
100
+ """获取 Sandbox 客户端"""
101
+ from .client import SandboxClient
102
+
103
+ return SandboxClient(config)
104
+
105
+ @classmethod
106
+ async def create_async(
107
+ cls, input: TemplateInput, config: Optional[Config] = None
108
+ ):
109
+ return await cls.__get_client(config=config).create_template_async(
110
+ input, config=config
111
+ )
112
+
113
+ @classmethod
114
+ def create(cls, input: TemplateInput, config: Optional[Config] = None):
115
+ return cls.__get_client(config=config).create_template(
116
+ input, config=config
117
+ )
118
+
119
+ @classmethod
120
+ async def delete_by_name_async(
121
+ cls, template_name: str, config: Optional[Config] = None
122
+ ):
123
+ return await cls.__get_client(config=config).delete_template_async(
124
+ template_name=template_name, config=config
125
+ )
126
+
127
+ @classmethod
128
+ def delete_by_name(
129
+ cls, template_name: str, config: Optional[Config] = None
130
+ ):
131
+ return cls.__get_client(config=config).delete_template(
132
+ template_name=template_name, config=config
133
+ )
134
+
135
+ @classmethod
136
+ async def update_by_name_async(
137
+ cls,
138
+ template_name: str,
139
+ input: TemplateInput,
140
+ config: Optional[Config] = None,
141
+ ):
142
+ return await cls.__get_client(config=config).update_template_async(
143
+ template_name=template_name, input=input, config=config
144
+ )
145
+
146
+ @classmethod
147
+ def update_by_name(
148
+ cls,
149
+ template_name: str,
150
+ input: TemplateInput,
151
+ config: Optional[Config] = None,
152
+ ):
153
+ return cls.__get_client(config=config).update_template(
154
+ template_name=template_name, input=input, config=config
155
+ )
156
+
157
+ @classmethod
158
+ async def get_by_name_async(
159
+ cls, template_name: str, config: Optional[Config] = None
160
+ ):
161
+ return await cls.__get_client(config=config).get_template_async(
162
+ template_name=template_name, config=config
163
+ )
164
+
165
+ @classmethod
166
+ def get_by_name(cls, template_name: str, config: Optional[Config] = None):
167
+ return cls.__get_client(config=config).get_template(
168
+ template_name=template_name, config=config
169
+ )
170
+
171
+ @classmethod
172
+ async def list_templates_async(
173
+ cls,
174
+ input: Optional[PageableInput] = None,
175
+ config: Optional[Config] = None,
176
+ ):
177
+ return await cls.__get_client(config=config).list_templates_async(
178
+ input, config=config
179
+ )
180
+
181
+ @classmethod
182
+ def list_templates(
183
+ cls,
184
+ input: Optional[PageableInput] = None,
185
+ config: Optional[Config] = None,
186
+ ):
187
+ return cls.__get_client(config=config).list_templates(
188
+ input, config=config
189
+ )
190
+
191
+ async def create_sandbox_async(
192
+ self,
193
+ sandbox_idle_timeout_seconds: Optional[int] = None,
194
+ config: Optional[Config] = None,
195
+ ):
196
+ if self.template_name is None:
197
+ raise ValueError("Template name is required")
198
+
199
+ return await self.__get_client(config=config).create_sandbox_async(
200
+ self.template_name,
201
+ sandbox_idle_timeout_seconds=sandbox_idle_timeout_seconds,
202
+ config=config,
203
+ )
204
+
205
+ def create_sandbox(
206
+ self,
207
+ sandbox_idle_timeout_seconds: Optional[int] = None,
208
+ config: Optional[Config] = None,
209
+ ):
210
+ if self.template_name is None:
211
+ raise ValueError("Template name is required")
212
+
213
+ return self.__get_client(config=config).create_sandbox(
214
+ self.template_name,
215
+ sandbox_idle_timeout_seconds=sandbox_idle_timeout_seconds,
216
+ config=config,
217
+ )
@@ -0,0 +1,191 @@
1
+ """AgentRun Server 模块 / AgentRun Server Module
2
+
3
+ 提供 HTTP Server 集成能力,支持符合 AgentRun 规范的 Agent 调用接口。
4
+ 支持 OpenAI Chat Completions 和 AG-UI 两种协议。
5
+
6
+ Example (基本使用 - 返回字符串):
7
+ >>> from agentrun.server import AgentRunServer, AgentRequest
8
+ >>>
9
+ >>> def invoke_agent(request: AgentRequest):
10
+ ... return "Hello, world!"
11
+ >>>
12
+ >>> server = AgentRunServer(invoke_agent=invoke_agent)
13
+ >>> server.start(port=9000)
14
+
15
+ Example (流式输出):
16
+ >>> def invoke_agent(request: AgentRequest):
17
+ ... for word in ["Hello", ", ", "world", "!"]:
18
+ ... yield word
19
+ >>>
20
+ >>> AgentRunServer(invoke_agent=invoke_agent).start()
21
+
22
+ Example (使用事件):
23
+ >>> from agentrun.server import AgentEvent, EventType
24
+ >>>
25
+ >>> async def invoke_agent(request: AgentRequest):
26
+ ... # 发送自定义事件(如步骤开始)
27
+ ... yield AgentEvent(
28
+ ... event=EventType.CUSTOM,
29
+ ... data={"name": "step_started", "value": {"step": "processing"}}
30
+ ... )
31
+ ...
32
+ ... # 流式输出内容
33
+ ... yield "Hello, "
34
+ ... yield "world!"
35
+ ...
36
+ ... # 发送步骤结束事件
37
+ ... yield AgentEvent(
38
+ ... event=EventType.CUSTOM,
39
+ ... data={"name": "step_finished", "value": {"step": "processing"}}
40
+ ... )
41
+
42
+ Example (工具调用事件):
43
+ >>> async def invoke_agent(request: AgentRequest):
44
+ ... # 完整工具调用
45
+ ... yield AgentEvent(
46
+ ... event=EventType.TOOL_CALL,
47
+ ... data={"id": "call_1", "name": "get_time", "args": '{"timezone": "UTC"}'}
48
+ ... )
49
+ ...
50
+ ... # 执行工具
51
+ ... result = "2024-01-01 12:00:00"
52
+ ...
53
+ ... # 工具调用结果
54
+ ... yield AgentEvent(
55
+ ... event=EventType.TOOL_RESULT,
56
+ ... data={"id": "call_1", "result": result}
57
+ ... )
58
+ ...
59
+ ... yield f"当前时间: {result}"
60
+
61
+ Example (流式工具输出):
62
+ >>> async def invoke_agent(request: AgentRequest):
63
+ ... # 发起工具调用
64
+ ... yield AgentEvent(
65
+ ... event=EventType.TOOL_CALL,
66
+ ... data={"id": "call_1", "name": "run_code", "args": '{"code": "..."}'}
67
+ ... )
68
+ ...
69
+ ... # 流式输出执行过程
70
+ ... yield AgentEvent(
71
+ ... event=EventType.TOOL_RESULT_CHUNK,
72
+ ... data={"id": "call_1", "delta": "Step 1: Compiling...\\n"}
73
+ ... )
74
+ ... yield AgentEvent(
75
+ ... event=EventType.TOOL_RESULT_CHUNK,
76
+ ... data={"id": "call_1", "delta": "Step 2: Running...\\n"}
77
+ ... )
78
+ ...
79
+ ... # 最终结果(标识流式输出结束)
80
+ ... yield AgentEvent(
81
+ ... event=EventType.TOOL_RESULT,
82
+ ... data={"id": "call_1", "result": "Execution completed."}
83
+ ... )
84
+
85
+ Example (HITL - 请求人类介入):
86
+ >>> async def invoke_agent(request: AgentRequest):
87
+ ... # 请求用户确认
88
+ ... yield AgentEvent(
89
+ ... event=EventType.HITL,
90
+ ... data={
91
+ ... "id": "hitl_1",
92
+ ... "tool_call_id": "call_delete", # 可选
93
+ ... "type": "confirmation",
94
+ ... "prompt": "确认删除文件?",
95
+ ... "options": ["确认", "取消"]
96
+ ... }
97
+ ... )
98
+ ... # 用户响应将通过下一轮对话的 messages 传回
99
+
100
+ Example (访问原始请求):
101
+ >>> async def invoke_agent(request: AgentRequest):
102
+ ... # 访问当前协议
103
+ ... protocol = request.protocol # "openai" 或 "agui"
104
+ ...
105
+ ... # 访问原始请求头
106
+ ... auth = request.raw_request.headers.get("Authorization")
107
+ ...
108
+ ... # 访问查询参数
109
+ ... params = request.raw_request.query_params
110
+ ...
111
+ ... # 访问客户端 IP
112
+ ... client_ip = request.raw_request.client.host if request.raw_request.client else None
113
+ ...
114
+ ... return "Hello, world!"
115
+ """
116
+
117
+ from ..utils.helper import MergeOptions
118
+ from .agui_normalizer import AguiEventNormalizer
119
+ from .agui_protocol import AGUIProtocolHandler
120
+ from .model import (
121
+ AgentEvent,
122
+ AgentEventItem,
123
+ AgentRequest,
124
+ AgentResult,
125
+ AgentResultItem,
126
+ AgentReturnType,
127
+ AGUIProtocolConfig,
128
+ AsyncAgentEventGenerator,
129
+ AsyncAgentResultGenerator,
130
+ EventType,
131
+ Message,
132
+ MessageRole,
133
+ OpenAIProtocolConfig,
134
+ ProtocolConfig,
135
+ ServerConfig,
136
+ SyncAgentEventGenerator,
137
+ SyncAgentResultGenerator,
138
+ Tool,
139
+ ToolCall,
140
+ )
141
+ from .openai_protocol import OpenAIProtocolHandler
142
+ from .protocol import (
143
+ AsyncInvokeAgentHandler,
144
+ BaseProtocolHandler,
145
+ InvokeAgentHandler,
146
+ ProtocolHandler,
147
+ SyncInvokeAgentHandler,
148
+ )
149
+ from .server import AgentRunServer
150
+
151
+ __all__ = [
152
+ # Server
153
+ "AgentRunServer",
154
+ # Config
155
+ "ServerConfig",
156
+ "ProtocolConfig",
157
+ "OpenAIProtocolConfig",
158
+ "AGUIProtocolConfig",
159
+ # Request/Response Models
160
+ "AgentRequest",
161
+ "AgentEvent",
162
+ "AgentResult", # 兼容别名
163
+ "Message",
164
+ "MessageRole",
165
+ "Tool",
166
+ "ToolCall",
167
+ # Event Types
168
+ "EventType",
169
+ # Type Aliases
170
+ "AgentEventItem",
171
+ "AgentResultItem", # 兼容别名
172
+ "AgentReturnType",
173
+ "SyncAgentEventGenerator",
174
+ "SyncAgentResultGenerator", # 兼容别名
175
+ "AsyncAgentEventGenerator",
176
+ "AsyncAgentResultGenerator", # 兼容别名
177
+ "InvokeAgentHandler",
178
+ "AsyncInvokeAgentHandler",
179
+ "SyncInvokeAgentHandler",
180
+ # Protocol Base
181
+ "ProtocolHandler",
182
+ "BaseProtocolHandler",
183
+ # Protocol - OpenAI
184
+ "OpenAIProtocolHandler",
185
+ # Protocol - AG-UI
186
+ "AGUIProtocolHandler",
187
+ # Event Normalizer
188
+ "AguiEventNormalizer",
189
+ # Helpers
190
+ "MergeOptions",
191
+ ]
@@ -0,0 +1,180 @@
1
+ """AG-UI 事件规范化器
2
+
3
+ 提供事件流规范化功能,确保事件符合 AG-UI 协议的顺序要求。
4
+
5
+ 主要功能:
6
+ - 追踪工具调用状态
7
+ - 在 TOOL_RESULT 前确保工具调用已开始
8
+ - 自动补充缺失的状态
9
+
10
+ 注意:边界事件(如 TEXT_MESSAGE_START/END、TOOL_CALL_START/END)
11
+ 由协议层(agui_protocol.py)自动生成,不需要用户关心。
12
+
13
+ 使用示例:
14
+
15
+ >>> from agentrun.server.agui_normalizer import AguiEventNormalizer
16
+ >>>
17
+ >>> normalizer = AguiEventNormalizer()
18
+ >>> for event in raw_events:
19
+ ... for normalized_event in normalizer.normalize(event):
20
+ ... yield normalized_event
21
+ """
22
+
23
+ from typing import Any, Dict, Iterator, List, Optional, Set, Union
24
+
25
+ from .model import AgentEvent, EventType
26
+
27
+
28
+ class AguiEventNormalizer:
29
+ """AG-UI 事件规范化器
30
+
31
+ 追踪工具调用状态,确保事件顺序正确:
32
+ 1. 追踪已开始的工具调用
33
+ 2. 确保 TOOL_RESULT 前工具调用存在
34
+
35
+ 协议层会自动处理边界事件(START/END),这个类主要用于
36
+ 高级用户需要手动控制事件流时。
37
+
38
+ Example:
39
+ >>> normalizer = AguiEventNormalizer()
40
+ >>> for event in agent_events:
41
+ ... for normalized in normalizer.normalize(event):
42
+ ... yield normalized
43
+ """
44
+
45
+ def __init__(self):
46
+ # 已看到的工具调用 ID 集合
47
+ self._seen_tool_calls: Set[str] = set()
48
+ # 活跃的工具调用信息(tool_call_id -> tool_call_name)
49
+ self._active_tool_calls: Dict[str, str] = {}
50
+
51
+ def normalize(
52
+ self,
53
+ event: Union[AgentEvent, str, Dict[str, Any]],
54
+ ) -> Iterator[AgentEvent]:
55
+ """规范化单个事件
56
+
57
+ 将事件标准化为 AgentEvent,并追踪工具调用状态。
58
+
59
+ Args:
60
+ event: 原始事件(AgentEvent、str 或 dict)
61
+
62
+ Yields:
63
+ 规范化后的事件
64
+ """
65
+ # 将事件标准化为 AgentEvent
66
+ normalized_event = self._to_agent_event(event)
67
+ if normalized_event is None:
68
+ return
69
+
70
+ # 根据事件类型进行处理
71
+ event_type = normalized_event.event
72
+
73
+ if event_type == EventType.TOOL_CALL_CHUNK:
74
+ yield from self._handle_tool_call_chunk(normalized_event)
75
+
76
+ elif event_type == EventType.TOOL_CALL:
77
+ yield from self._handle_tool_call(normalized_event)
78
+
79
+ elif event_type == EventType.TOOL_RESULT:
80
+ yield from self._handle_tool_result(normalized_event)
81
+
82
+ else:
83
+ # 其他事件类型直接传递
84
+ yield normalized_event
85
+
86
+ def _to_agent_event(
87
+ self, event: Union[AgentEvent, str, Dict[str, Any]]
88
+ ) -> Optional[AgentEvent]:
89
+ """将事件转换为 AgentEvent"""
90
+ if isinstance(event, AgentEvent):
91
+ return event
92
+
93
+ if isinstance(event, str):
94
+ # 字符串转为 TEXT
95
+ return AgentEvent(
96
+ event=EventType.TEXT,
97
+ data={"delta": event},
98
+ )
99
+
100
+ if isinstance(event, dict):
101
+ event_type = event.get("event")
102
+ if event_type is None:
103
+ return None
104
+
105
+ # 尝试解析 event_type
106
+ if isinstance(event_type, str):
107
+ try:
108
+ event_type = EventType(event_type)
109
+ except ValueError:
110
+ try:
111
+ event_type = EventType[event_type]
112
+ except KeyError:
113
+ return None
114
+
115
+ return AgentEvent(
116
+ event=event_type,
117
+ data=event.get("data", {}),
118
+ )
119
+
120
+ return None
121
+
122
+ def _handle_tool_call(self, event: AgentEvent) -> Iterator[AgentEvent]:
123
+ """处理 TOOL_CALL 事件
124
+
125
+ 记录工具调用并直接传递
126
+ """
127
+ tool_call_id = event.data.get("id", "")
128
+ tool_call_name = event.data.get("name", "")
129
+
130
+ if tool_call_id:
131
+ self._seen_tool_calls.add(tool_call_id)
132
+ self._active_tool_calls[tool_call_id] = tool_call_name
133
+
134
+ yield event
135
+
136
+ def _handle_tool_call_chunk(
137
+ self, event: AgentEvent
138
+ ) -> Iterator[AgentEvent]:
139
+ """处理 TOOL_CALL_CHUNK 事件
140
+
141
+ 记录工具调用并直接传递
142
+ """
143
+ tool_call_id = event.data.get("id", "")
144
+ tool_call_name = event.data.get("name", "")
145
+
146
+ if tool_call_id:
147
+ self._seen_tool_calls.add(tool_call_id)
148
+ if tool_call_name:
149
+ self._active_tool_calls[tool_call_id] = tool_call_name
150
+
151
+ yield event
152
+
153
+ def _handle_tool_result(self, event: AgentEvent) -> Iterator[AgentEvent]:
154
+ """处理 TOOL_RESULT 事件
155
+
156
+ 标记工具调用完成
157
+ """
158
+ tool_call_id = event.data.get("id", "")
159
+
160
+ if tool_call_id:
161
+ # 标记工具调用已完成(从活跃列表移除)
162
+ self._active_tool_calls.pop(tool_call_id, None)
163
+
164
+ yield event
165
+
166
+ def get_active_tool_calls(self) -> List[str]:
167
+ """获取当前活跃(未结束)的工具调用 ID 列表"""
168
+ return list(self._active_tool_calls.keys())
169
+
170
+ def get_seen_tool_calls(self) -> List[str]:
171
+ """获取所有已见过的工具调用 ID 列表"""
172
+ return list(self._seen_tool_calls)
173
+
174
+ def reset(self):
175
+ """重置状态
176
+
177
+ 在处理新的请求时,建议创建新的实例而不是复用。
178
+ """
179
+ self._seen_tool_calls.clear()
180
+ self._active_tool_calls.clear()