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.
- agentrun/__init__.py +325 -0
- agentrun/agent_runtime/__client_async_template.py +466 -0
- agentrun/agent_runtime/__endpoint_async_template.py +345 -0
- agentrun/agent_runtime/__init__.py +53 -0
- agentrun/agent_runtime/__runtime_async_template.py +477 -0
- agentrun/agent_runtime/api/__data_async_template.py +58 -0
- agentrun/agent_runtime/api/__init__.py +6 -0
- agentrun/agent_runtime/api/control.py +1362 -0
- agentrun/agent_runtime/api/data.py +98 -0
- agentrun/agent_runtime/client.py +868 -0
- agentrun/agent_runtime/endpoint.py +649 -0
- agentrun/agent_runtime/model.py +362 -0
- agentrun/agent_runtime/runtime.py +904 -0
- agentrun/credential/__client_async_template.py +177 -0
- agentrun/credential/__credential_async_template.py +216 -0
- agentrun/credential/__init__.py +28 -0
- agentrun/credential/api/__init__.py +5 -0
- agentrun/credential/api/control.py +606 -0
- agentrun/credential/client.py +319 -0
- agentrun/credential/credential.py +381 -0
- agentrun/credential/model.py +248 -0
- agentrun/integration/__init__.py +21 -0
- agentrun/integration/agentscope/__init__.py +12 -0
- agentrun/integration/agentscope/adapter.py +17 -0
- agentrun/integration/agentscope/builtin.py +65 -0
- agentrun/integration/agentscope/message_adapter.py +185 -0
- agentrun/integration/agentscope/model_adapter.py +60 -0
- agentrun/integration/agentscope/tool_adapter.py +59 -0
- agentrun/integration/builtin/__init__.py +16 -0
- agentrun/integration/builtin/model.py +93 -0
- agentrun/integration/builtin/sandbox.py +1234 -0
- agentrun/integration/builtin/toolset.py +47 -0
- agentrun/integration/crewai/__init__.py +12 -0
- agentrun/integration/crewai/adapter.py +9 -0
- agentrun/integration/crewai/builtin.py +65 -0
- agentrun/integration/crewai/model_adapter.py +31 -0
- agentrun/integration/crewai/tool_adapter.py +26 -0
- agentrun/integration/google_adk/__init__.py +12 -0
- agentrun/integration/google_adk/adapter.py +15 -0
- agentrun/integration/google_adk/builtin.py +65 -0
- agentrun/integration/google_adk/message_adapter.py +144 -0
- agentrun/integration/google_adk/model_adapter.py +46 -0
- agentrun/integration/google_adk/tool_adapter.py +235 -0
- agentrun/integration/langchain/__init__.py +30 -0
- agentrun/integration/langchain/adapter.py +15 -0
- agentrun/integration/langchain/builtin.py +71 -0
- agentrun/integration/langchain/message_adapter.py +141 -0
- agentrun/integration/langchain/model_adapter.py +37 -0
- agentrun/integration/langchain/tool_adapter.py +50 -0
- agentrun/integration/langgraph/__init__.py +35 -0
- agentrun/integration/langgraph/adapter.py +20 -0
- agentrun/integration/langgraph/agent_converter.py +1073 -0
- agentrun/integration/langgraph/builtin.py +65 -0
- agentrun/integration/pydantic_ai/__init__.py +12 -0
- agentrun/integration/pydantic_ai/adapter.py +13 -0
- agentrun/integration/pydantic_ai/builtin.py +65 -0
- agentrun/integration/pydantic_ai/model_adapter.py +44 -0
- agentrun/integration/pydantic_ai/tool_adapter.py +19 -0
- agentrun/integration/utils/__init__.py +112 -0
- agentrun/integration/utils/adapter.py +560 -0
- agentrun/integration/utils/canonical.py +164 -0
- agentrun/integration/utils/converter.py +134 -0
- agentrun/integration/utils/model.py +110 -0
- agentrun/integration/utils/tool.py +1759 -0
- agentrun/model/__client_async_template.py +357 -0
- agentrun/model/__init__.py +57 -0
- agentrun/model/__model_proxy_async_template.py +270 -0
- agentrun/model/__model_service_async_template.py +267 -0
- agentrun/model/api/__init__.py +6 -0
- agentrun/model/api/control.py +1173 -0
- agentrun/model/api/data.py +196 -0
- agentrun/model/client.py +674 -0
- agentrun/model/model.py +235 -0
- agentrun/model/model_proxy.py +439 -0
- agentrun/model/model_service.py +438 -0
- agentrun/sandbox/__aio_sandbox_async_template.py +523 -0
- agentrun/sandbox/__browser_sandbox_async_template.py +110 -0
- agentrun/sandbox/__client_async_template.py +491 -0
- agentrun/sandbox/__code_interpreter_sandbox_async_template.py +463 -0
- agentrun/sandbox/__init__.py +69 -0
- agentrun/sandbox/__sandbox_async_template.py +463 -0
- agentrun/sandbox/__template_async_template.py +152 -0
- agentrun/sandbox/aio_sandbox.py +905 -0
- agentrun/sandbox/api/__aio_data_async_template.py +335 -0
- agentrun/sandbox/api/__browser_data_async_template.py +140 -0
- agentrun/sandbox/api/__code_interpreter_data_async_template.py +206 -0
- agentrun/sandbox/api/__init__.py +19 -0
- agentrun/sandbox/api/__sandbox_data_async_template.py +107 -0
- agentrun/sandbox/api/aio_data.py +551 -0
- agentrun/sandbox/api/browser_data.py +172 -0
- agentrun/sandbox/api/code_interpreter_data.py +396 -0
- agentrun/sandbox/api/control.py +1051 -0
- agentrun/sandbox/api/playwright_async.py +492 -0
- agentrun/sandbox/api/playwright_sync.py +492 -0
- agentrun/sandbox/api/sandbox_data.py +154 -0
- agentrun/sandbox/browser_sandbox.py +185 -0
- agentrun/sandbox/client.py +925 -0
- agentrun/sandbox/code_interpreter_sandbox.py +823 -0
- agentrun/sandbox/model.py +397 -0
- agentrun/sandbox/sandbox.py +848 -0
- agentrun/sandbox/template.py +217 -0
- agentrun/server/__init__.py +191 -0
- agentrun/server/agui_normalizer.py +180 -0
- agentrun/server/agui_protocol.py +797 -0
- agentrun/server/invoker.py +309 -0
- agentrun/server/model.py +427 -0
- agentrun/server/openai_protocol.py +535 -0
- agentrun/server/protocol.py +140 -0
- agentrun/server/server.py +208 -0
- agentrun/toolset/__client_async_template.py +62 -0
- agentrun/toolset/__init__.py +51 -0
- agentrun/toolset/__toolset_async_template.py +204 -0
- agentrun/toolset/api/__init__.py +17 -0
- agentrun/toolset/api/control.py +262 -0
- agentrun/toolset/api/mcp.py +100 -0
- agentrun/toolset/api/openapi.py +1251 -0
- agentrun/toolset/client.py +102 -0
- agentrun/toolset/model.py +321 -0
- agentrun/toolset/toolset.py +270 -0
- agentrun/utils/__data_api_async_template.py +720 -0
- agentrun/utils/__init__.py +5 -0
- agentrun/utils/__resource_async_template.py +158 -0
- agentrun/utils/config.py +258 -0
- agentrun/utils/control_api.py +78 -0
- agentrun/utils/data_api.py +1120 -0
- agentrun/utils/exception.py +151 -0
- agentrun/utils/helper.py +108 -0
- agentrun/utils/log.py +77 -0
- agentrun/utils/model.py +168 -0
- agentrun/utils/resource.py +291 -0
- agentrun_inner_test-0.0.46.dist-info/METADATA +263 -0
- agentrun_inner_test-0.0.46.dist-info/RECORD +135 -0
- agentrun_inner_test-0.0.46.dist-info/WHEEL +5 -0
- agentrun_inner_test-0.0.46.dist-info/licenses/LICENSE +201 -0
- agentrun_inner_test-0.0.46.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,560 @@
|
|
|
1
|
+
"""适配器接口定义 / Adapter Interface Definition
|
|
2
|
+
|
|
3
|
+
定义统一的适配器接口,所有框架适配器都实现这些接口。
|
|
4
|
+
Defines unified adapter interfaces that all framework adapters implement.
|
|
5
|
+
|
|
6
|
+
这样可以确保一致的转换行为,并最大化代码复用。
|
|
7
|
+
This ensures consistent conversion behavior and maximizes code reuse.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from abc import ABC, abstractmethod
|
|
11
|
+
import inspect
|
|
12
|
+
import re
|
|
13
|
+
from typing import Any, Callable, Dict, List, Optional, Union
|
|
14
|
+
|
|
15
|
+
from pydantic import create_model, Field
|
|
16
|
+
|
|
17
|
+
from agentrun.integration.utils.canonical import CanonicalMessage, CanonicalTool
|
|
18
|
+
from agentrun.integration.utils.model import CommonModel
|
|
19
|
+
from agentrun.integration.utils.tool import normalize_tool_name
|
|
20
|
+
|
|
21
|
+
# 用于缓存动态创建的 Pydantic 模型,避免重复创建
|
|
22
|
+
_dynamic_models_cache: Dict[str, type] = {}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _resolve_schema_ref(
|
|
26
|
+
ref: str, root_schema: Dict[str, Any]
|
|
27
|
+
) -> Optional[Dict[str, Any]]:
|
|
28
|
+
"""解析 JSON Schema $ref 引用
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
ref: $ref 字符串,如 "#/$defs/MyType" 或 "#/definitions/MyType"
|
|
32
|
+
root_schema: 根 schema,包含 $defs 或 definitions
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
解析后的 schema,如果无法解析则返回 None
|
|
36
|
+
"""
|
|
37
|
+
if not ref or not ref.startswith("#/"):
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
# 解析路径,如 "#/$defs/MyType" -> ["$defs", "MyType"]
|
|
41
|
+
path_parts = ref[2:].split("/")
|
|
42
|
+
current = root_schema
|
|
43
|
+
|
|
44
|
+
for part in path_parts:
|
|
45
|
+
if not isinstance(current, dict) or part not in current:
|
|
46
|
+
return None
|
|
47
|
+
current = current[part]
|
|
48
|
+
|
|
49
|
+
return current if isinstance(current, dict) else None
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _generate_model_name(
|
|
53
|
+
schema: Dict[str, Any], prefix: str = "Dynamic"
|
|
54
|
+
) -> str:
|
|
55
|
+
"""为 schema 生成唯一的模型名称"""
|
|
56
|
+
# 优先使用 schema 中的 title
|
|
57
|
+
title = schema.get("title")
|
|
58
|
+
if title:
|
|
59
|
+
# 清理名称,只保留字母数字
|
|
60
|
+
clean_name = re.sub(r"[^a-zA-Z0-9]", "", title)
|
|
61
|
+
if clean_name:
|
|
62
|
+
return clean_name
|
|
63
|
+
|
|
64
|
+
# 使用属性名生成名称
|
|
65
|
+
properties = schema.get("properties", {})
|
|
66
|
+
if properties:
|
|
67
|
+
prop_names = sorted(properties.keys())[:3] # 最多取3个属性名
|
|
68
|
+
name = "".join(n.title() for n in prop_names)
|
|
69
|
+
return f"{prefix}{name}Model"
|
|
70
|
+
|
|
71
|
+
return f"{prefix}Model"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _schema_to_type_description(
|
|
75
|
+
schema: Dict[str, Any],
|
|
76
|
+
root_schema: Optional[Dict[str, Any]] = None,
|
|
77
|
+
indent: int = 0,
|
|
78
|
+
) -> str:
|
|
79
|
+
"""将 JSON Schema 转换为人类可读的类型描述
|
|
80
|
+
|
|
81
|
+
用于在函数文档字符串中描述复杂类型结构,
|
|
82
|
+
帮助不支持精确类型的框架(如 Google ADK)理解参数类型。
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
schema: JSON Schema 定义
|
|
86
|
+
root_schema: 根 schema,用于解析 $ref 引用
|
|
87
|
+
indent: 缩进级别
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
类型描述字符串
|
|
91
|
+
"""
|
|
92
|
+
if not schema or not isinstance(schema, dict):
|
|
93
|
+
return "any"
|
|
94
|
+
|
|
95
|
+
if root_schema is None:
|
|
96
|
+
root_schema = schema
|
|
97
|
+
|
|
98
|
+
indent_prefix = " " * indent
|
|
99
|
+
|
|
100
|
+
# 处理 $ref 引用
|
|
101
|
+
if "$ref" in schema:
|
|
102
|
+
resolved = _resolve_schema_ref(schema["$ref"], root_schema)
|
|
103
|
+
if resolved:
|
|
104
|
+
return _schema_to_type_description(resolved, root_schema, indent)
|
|
105
|
+
return "object"
|
|
106
|
+
|
|
107
|
+
schema_type = schema.get("type")
|
|
108
|
+
|
|
109
|
+
# 处理 anyOf / oneOf
|
|
110
|
+
if "anyOf" in schema:
|
|
111
|
+
types = [
|
|
112
|
+
_schema_to_type_description(s, root_schema, indent)
|
|
113
|
+
for s in schema["anyOf"]
|
|
114
|
+
]
|
|
115
|
+
return " | ".join(types)
|
|
116
|
+
|
|
117
|
+
if "oneOf" in schema:
|
|
118
|
+
types = [
|
|
119
|
+
_schema_to_type_description(s, root_schema, indent)
|
|
120
|
+
for s in schema["oneOf"]
|
|
121
|
+
]
|
|
122
|
+
return " | ".join(types)
|
|
123
|
+
|
|
124
|
+
# null 类型
|
|
125
|
+
if schema_type == "null":
|
|
126
|
+
return "null"
|
|
127
|
+
|
|
128
|
+
# 数组类型
|
|
129
|
+
if schema_type == "array":
|
|
130
|
+
items_schema = schema.get("items")
|
|
131
|
+
if items_schema:
|
|
132
|
+
item_desc = _schema_to_type_description(
|
|
133
|
+
items_schema, root_schema, indent
|
|
134
|
+
)
|
|
135
|
+
return f"array[{item_desc}]"
|
|
136
|
+
return "array"
|
|
137
|
+
|
|
138
|
+
# 对象类型 - 详细描述属性
|
|
139
|
+
if schema_type == "object":
|
|
140
|
+
properties = schema.get("properties", {})
|
|
141
|
+
required = set(schema.get("required", []))
|
|
142
|
+
|
|
143
|
+
if not properties:
|
|
144
|
+
return "object"
|
|
145
|
+
|
|
146
|
+
# 获取标题
|
|
147
|
+
title = schema.get("title", "object")
|
|
148
|
+
lines = [f"{title} {{"]
|
|
149
|
+
|
|
150
|
+
for prop_name, prop_schema in properties.items():
|
|
151
|
+
prop_desc = _schema_to_type_description(
|
|
152
|
+
prop_schema, root_schema, indent + 1
|
|
153
|
+
)
|
|
154
|
+
description = prop_schema.get("description", "")
|
|
155
|
+
optional_marker = "" if prop_name in required else "?"
|
|
156
|
+
|
|
157
|
+
prop_line = (
|
|
158
|
+
f"{indent_prefix} {prop_name}{optional_marker}: {prop_desc}"
|
|
159
|
+
)
|
|
160
|
+
if description:
|
|
161
|
+
prop_line += f" # {description}"
|
|
162
|
+
lines.append(prop_line)
|
|
163
|
+
|
|
164
|
+
lines.append(f"{indent_prefix}}}")
|
|
165
|
+
return "\n".join(lines)
|
|
166
|
+
|
|
167
|
+
# 基本类型
|
|
168
|
+
type_names = {
|
|
169
|
+
"string": "string",
|
|
170
|
+
"integer": "integer",
|
|
171
|
+
"number": "number",
|
|
172
|
+
"boolean": "boolean",
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if schema_type in type_names:
|
|
176
|
+
# 添加枚举值信息
|
|
177
|
+
enum_values = schema.get("enum")
|
|
178
|
+
if enum_values:
|
|
179
|
+
enum_str = ", ".join(repr(v) for v in enum_values)
|
|
180
|
+
return f"{type_names[schema_type]}({enum_str})"
|
|
181
|
+
return type_names[schema_type]
|
|
182
|
+
|
|
183
|
+
return "any"
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _generate_params_docstring(
|
|
187
|
+
parameters_schema: Dict[str, Any],
|
|
188
|
+
) -> str:
|
|
189
|
+
"""生成参数的详细文档字符串
|
|
190
|
+
|
|
191
|
+
为复杂类型生成详细的类型描述,用于不支持精确类型的框架。
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
parameters_schema: 参数的 JSON Schema
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
参数文档字符串
|
|
198
|
+
"""
|
|
199
|
+
properties = parameters_schema.get("properties", {})
|
|
200
|
+
required = set(parameters_schema.get("required", []))
|
|
201
|
+
|
|
202
|
+
if not properties:
|
|
203
|
+
return ""
|
|
204
|
+
|
|
205
|
+
lines = ["\n\nArgs:"]
|
|
206
|
+
|
|
207
|
+
for param_name, param_schema in properties.items():
|
|
208
|
+
# 基础描述
|
|
209
|
+
description = param_schema.get("description", "")
|
|
210
|
+
optional_marker = " (optional)" if param_name not in required else ""
|
|
211
|
+
|
|
212
|
+
# 获取类型描述
|
|
213
|
+
type_desc = _schema_to_type_description(param_schema, parameters_schema)
|
|
214
|
+
|
|
215
|
+
# 检查是否是复杂类型(包含换行的描述)
|
|
216
|
+
if "\n" in type_desc:
|
|
217
|
+
# 复杂类型,使用多行格式
|
|
218
|
+
lines.append(f" {param_name}{optional_marker}:")
|
|
219
|
+
if description:
|
|
220
|
+
lines.append(f" {description}")
|
|
221
|
+
lines.append(" Type:")
|
|
222
|
+
# 缩进复杂类型描述
|
|
223
|
+
for type_line in type_desc.split("\n"):
|
|
224
|
+
lines.append(f" {type_line}")
|
|
225
|
+
else:
|
|
226
|
+
# 简单类型
|
|
227
|
+
param_line = f" {param_name} ({type_desc}){optional_marker}"
|
|
228
|
+
if description:
|
|
229
|
+
param_line += f": {description}"
|
|
230
|
+
lines.append(param_line)
|
|
231
|
+
|
|
232
|
+
return "\n".join(lines)
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def _schema_to_pydantic_model(
|
|
236
|
+
schema: Dict[str, Any],
|
|
237
|
+
root_schema: Optional[Dict[str, Any]] = None,
|
|
238
|
+
model_name: Optional[str] = None,
|
|
239
|
+
) -> type:
|
|
240
|
+
"""将 JSON Schema 转换为 Pydantic 模型
|
|
241
|
+
|
|
242
|
+
Args:
|
|
243
|
+
schema: 对象类型的 JSON Schema
|
|
244
|
+
root_schema: 根 schema,用于解析 $ref
|
|
245
|
+
model_name: 可选的模型名称
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
动态创建的 Pydantic 模型类
|
|
249
|
+
"""
|
|
250
|
+
if root_schema is None:
|
|
251
|
+
root_schema = schema
|
|
252
|
+
|
|
253
|
+
# 生成模型名称
|
|
254
|
+
if not model_name:
|
|
255
|
+
model_name = _generate_model_name(schema)
|
|
256
|
+
|
|
257
|
+
# 检查缓存
|
|
258
|
+
cache_key = f"{model_name}_{id(schema)}"
|
|
259
|
+
if cache_key in _dynamic_models_cache:
|
|
260
|
+
return _dynamic_models_cache[cache_key]
|
|
261
|
+
|
|
262
|
+
properties = schema.get("properties", {})
|
|
263
|
+
required_fields = set(schema.get("required", []))
|
|
264
|
+
|
|
265
|
+
if not properties:
|
|
266
|
+
# 无属性的对象,返回基础 dict
|
|
267
|
+
return dict
|
|
268
|
+
|
|
269
|
+
fields: Dict[str, Any] = {}
|
|
270
|
+
|
|
271
|
+
for field_name, field_schema in properties.items():
|
|
272
|
+
# 递归获取字段类型
|
|
273
|
+
field_type = schema_to_python_type(field_schema, root_schema)
|
|
274
|
+
|
|
275
|
+
# 获取字段描述
|
|
276
|
+
description = field_schema.get("description", "")
|
|
277
|
+
default = field_schema.get("default")
|
|
278
|
+
|
|
279
|
+
# 构建字段定义
|
|
280
|
+
if field_name in required_fields:
|
|
281
|
+
fields[field_name] = (field_type, Field(description=description))
|
|
282
|
+
else:
|
|
283
|
+
# 可选字段
|
|
284
|
+
fields[field_name] = (
|
|
285
|
+
Optional[field_type],
|
|
286
|
+
Field(default=default, description=description),
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
# 动态创建 Pydantic 模型
|
|
290
|
+
try:
|
|
291
|
+
model = create_model(model_name, **fields)
|
|
292
|
+
_dynamic_models_cache[cache_key] = model
|
|
293
|
+
return model
|
|
294
|
+
except Exception:
|
|
295
|
+
# 如果创建失败,回退到 dict
|
|
296
|
+
return dict
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def schema_to_python_type(
|
|
300
|
+
schema: Dict[str, Any],
|
|
301
|
+
root_schema: Optional[Dict[str, Any]] = None,
|
|
302
|
+
) -> Any:
|
|
303
|
+
"""将 JSON Schema 转换为 Python 类型注解
|
|
304
|
+
|
|
305
|
+
支持复杂类型:嵌套对象、数组元素类型、联合类型、$ref 引用等。
|
|
306
|
+
对于嵌套对象,会动态创建 Pydantic 模型以保留完整的类型信息。
|
|
307
|
+
|
|
308
|
+
Args:
|
|
309
|
+
schema: JSON Schema 定义
|
|
310
|
+
root_schema: 根 schema,用于解析 $ref 引用。
|
|
311
|
+
如果未提供,则使用 schema 本身作为根。
|
|
312
|
+
|
|
313
|
+
Returns:
|
|
314
|
+
Python 类型注解(可能是基本类型、List、Optional、Union 或 Pydantic 模型)
|
|
315
|
+
"""
|
|
316
|
+
if not schema or not isinstance(schema, dict):
|
|
317
|
+
return Any
|
|
318
|
+
|
|
319
|
+
# 如果没有提供 root_schema,使用当前 schema 作为根
|
|
320
|
+
if root_schema is None:
|
|
321
|
+
root_schema = schema
|
|
322
|
+
|
|
323
|
+
# 处理 $ref 引用
|
|
324
|
+
if "$ref" in schema:
|
|
325
|
+
resolved = _resolve_schema_ref(schema["$ref"], root_schema)
|
|
326
|
+
if resolved:
|
|
327
|
+
return schema_to_python_type(resolved, root_schema)
|
|
328
|
+
# 无法解析 $ref,返回 dict(对象类型的默认)
|
|
329
|
+
return dict
|
|
330
|
+
|
|
331
|
+
schema_type = schema.get("type")
|
|
332
|
+
|
|
333
|
+
# 处理 anyOf / oneOf(联合类型)
|
|
334
|
+
if "anyOf" in schema:
|
|
335
|
+
types = [schema_to_python_type(s, root_schema) for s in schema["anyOf"]]
|
|
336
|
+
# 过滤掉 None 类型,用于构造 Optional
|
|
337
|
+
non_none_types = [t for t in types if t is not type(None)]
|
|
338
|
+
has_none = any(t is type(None) for t in types)
|
|
339
|
+
|
|
340
|
+
if len(non_none_types) == 1 and has_none:
|
|
341
|
+
return Optional[non_none_types[0]]
|
|
342
|
+
elif len(non_none_types) > 1:
|
|
343
|
+
return Union[tuple(non_none_types)]
|
|
344
|
+
elif non_none_types:
|
|
345
|
+
return non_none_types[0]
|
|
346
|
+
return Any
|
|
347
|
+
|
|
348
|
+
if "oneOf" in schema:
|
|
349
|
+
types = [schema_to_python_type(s, root_schema) for s in schema["oneOf"]]
|
|
350
|
+
non_none_types = [t for t in types if t is not type(None)]
|
|
351
|
+
if len(non_none_types) > 1:
|
|
352
|
+
return Union[tuple(non_none_types)]
|
|
353
|
+
elif non_none_types:
|
|
354
|
+
return non_none_types[0]
|
|
355
|
+
return Any
|
|
356
|
+
|
|
357
|
+
# 处理 null 类型
|
|
358
|
+
if schema_type == "null":
|
|
359
|
+
return type(None)
|
|
360
|
+
|
|
361
|
+
# 处理数组类型
|
|
362
|
+
if schema_type == "array":
|
|
363
|
+
items_schema = schema.get("items")
|
|
364
|
+
if items_schema:
|
|
365
|
+
item_type = schema_to_python_type(items_schema, root_schema)
|
|
366
|
+
return List[item_type]
|
|
367
|
+
return List[Any]
|
|
368
|
+
|
|
369
|
+
# 处理对象类型 - 动态创建 Pydantic 模型
|
|
370
|
+
if schema_type == "object":
|
|
371
|
+
properties = schema.get("properties")
|
|
372
|
+
if properties:
|
|
373
|
+
# 有属性定义,创建 Pydantic 模型
|
|
374
|
+
return _schema_to_pydantic_model(schema, root_schema)
|
|
375
|
+
# 无属性的通用对象
|
|
376
|
+
return dict
|
|
377
|
+
|
|
378
|
+
# 基本类型映射
|
|
379
|
+
type_mapping: Dict[str, type] = {
|
|
380
|
+
"string": str,
|
|
381
|
+
"integer": int,
|
|
382
|
+
"number": float,
|
|
383
|
+
"boolean": bool,
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if schema_type and schema_type in type_mapping:
|
|
387
|
+
return type_mapping[schema_type]
|
|
388
|
+
|
|
389
|
+
return Any
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
class MessageAdapter(ABC):
|
|
393
|
+
"""消息格式适配器接口
|
|
394
|
+
|
|
395
|
+
用于在 ModelAdapter 内部进行消息格式转换。
|
|
396
|
+
只需要将框架消息转换为标准 OpenAI 格式。
|
|
397
|
+
|
|
398
|
+
转换流程:
|
|
399
|
+
- 框架消息 → to_canonical() → CanonicalMessage(OpenAI 格式)
|
|
400
|
+
"""
|
|
401
|
+
|
|
402
|
+
@abstractmethod
|
|
403
|
+
def to_canonical(self, messages: Any) -> List[CanonicalMessage]:
|
|
404
|
+
"""将框架消息转换为标准格式(供 ModelAdapter 内部使用)
|
|
405
|
+
|
|
406
|
+
Args:
|
|
407
|
+
messages: 框架特定的消息格式
|
|
408
|
+
|
|
409
|
+
Returns:
|
|
410
|
+
标准格式消息列表
|
|
411
|
+
"""
|
|
412
|
+
pass
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
class ToolAdapter(ABC):
|
|
416
|
+
"""工具格式适配器接口 / Utils Adapters
|
|
417
|
+
|
|
418
|
+
用于将标准工具定义转换为框架特定格式。
|
|
419
|
+
单向转换:CanonicalTool → 框架工具"""
|
|
420
|
+
|
|
421
|
+
def __init__(self) -> None:
|
|
422
|
+
super().__init__()
|
|
423
|
+
|
|
424
|
+
# 记录工具定义,便于模型适配器回溯参数 schema
|
|
425
|
+
self._registered_tools: Dict[str, CanonicalTool] = {}
|
|
426
|
+
|
|
427
|
+
@abstractmethod
|
|
428
|
+
def from_canonical(self, tools: List[CanonicalTool]) -> Any:
|
|
429
|
+
"""将标准工具转换为框架特定格式 / 将标准工具Converts为框架特定格式
|
|
430
|
+
|
|
431
|
+
Args:
|
|
432
|
+
tools: 标准格式工具列表
|
|
433
|
+
|
|
434
|
+
Returns:
|
|
435
|
+
框架特定的工具格式"""
|
|
436
|
+
pass
|
|
437
|
+
|
|
438
|
+
def function_tools(
|
|
439
|
+
self,
|
|
440
|
+
tools: List[CanonicalTool],
|
|
441
|
+
modify_func: Optional[Callable[..., Any]] = None,
|
|
442
|
+
include_type_docstring: bool = False,
|
|
443
|
+
):
|
|
444
|
+
"""将标准格式转换为可调用函数工具
|
|
445
|
+
|
|
446
|
+
通过函数的类型注解推断参数,动态创建带注解的函数。
|
|
447
|
+
|
|
448
|
+
Args:
|
|
449
|
+
tools: 标准格式工具列表
|
|
450
|
+
modify_func: 可选的函数调用修改器
|
|
451
|
+
include_type_docstring: 是否在文档字符串中包含详细的类型描述
|
|
452
|
+
对于不支持精确类型的框架(如 Google ADK),可以设为 True
|
|
453
|
+
以便在函数文档中包含复杂类型的结构描述"""
|
|
454
|
+
result = []
|
|
455
|
+
|
|
456
|
+
for tool in tools:
|
|
457
|
+
# 记录工具定义
|
|
458
|
+
self._registered_tools[tool.name] = tool
|
|
459
|
+
|
|
460
|
+
# 从 parameters schema 构建函数签名
|
|
461
|
+
parameters_schema = tool.parameters or {
|
|
462
|
+
"type": "object",
|
|
463
|
+
"properties": {},
|
|
464
|
+
}
|
|
465
|
+
properties = parameters_schema.get("properties", {})
|
|
466
|
+
required = set(parameters_schema.get("required", []))
|
|
467
|
+
|
|
468
|
+
# 构建函数参数
|
|
469
|
+
params = []
|
|
470
|
+
annotations = {}
|
|
471
|
+
|
|
472
|
+
for param_name, param_schema in properties.items():
|
|
473
|
+
# 使用递归类型转换处理复杂类型
|
|
474
|
+
# 传入 parameters_schema 作为根 schema 以解析 $ref 引用
|
|
475
|
+
param_type = schema_to_python_type(
|
|
476
|
+
param_schema, parameters_schema
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
# 设置默认值
|
|
480
|
+
default = (
|
|
481
|
+
inspect.Parameter.empty if param_name in required else None
|
|
482
|
+
)
|
|
483
|
+
|
|
484
|
+
params.append(
|
|
485
|
+
inspect.Parameter(
|
|
486
|
+
param_name,
|
|
487
|
+
inspect.Parameter.KEYWORD_ONLY,
|
|
488
|
+
default=default,
|
|
489
|
+
annotation=param_type,
|
|
490
|
+
)
|
|
491
|
+
)
|
|
492
|
+
annotations[param_name] = param_type
|
|
493
|
+
|
|
494
|
+
# 创建带正确签名的函数
|
|
495
|
+
def make_tool_function(
|
|
496
|
+
canonical_tool: CanonicalTool,
|
|
497
|
+
sig: inspect.Signature,
|
|
498
|
+
annots: dict,
|
|
499
|
+
params_schema: dict,
|
|
500
|
+
add_type_doc: bool,
|
|
501
|
+
):
|
|
502
|
+
def tool_func(**kwargs):
|
|
503
|
+
if canonical_tool.func is None:
|
|
504
|
+
raise NotImplementedError(
|
|
505
|
+
f"Tool function for '{canonical_tool.name}' "
|
|
506
|
+
"is not implemented."
|
|
507
|
+
)
|
|
508
|
+
|
|
509
|
+
if modify_func:
|
|
510
|
+
return modify_func(canonical_tool, **kwargs)
|
|
511
|
+
|
|
512
|
+
return canonical_tool.func(**kwargs)
|
|
513
|
+
|
|
514
|
+
# 设置函数元数据(函数名也需要标准化以防第三方工具名限制)
|
|
515
|
+
tool_func.__name__ = normalize_tool_name(canonical_tool.name)
|
|
516
|
+
|
|
517
|
+
# 生成文档字符串
|
|
518
|
+
base_doc = canonical_tool.description or ""
|
|
519
|
+
if add_type_doc:
|
|
520
|
+
# 对于不支持精确类型的框架,在文档中包含详细的类型描述
|
|
521
|
+
params_doc = _generate_params_docstring(params_schema)
|
|
522
|
+
tool_func.__doc__ = base_doc + params_doc
|
|
523
|
+
else:
|
|
524
|
+
tool_func.__doc__ = base_doc
|
|
525
|
+
|
|
526
|
+
tool_func.__annotations__ = annots
|
|
527
|
+
object.__setattr__(tool_func, "__signature__", sig)
|
|
528
|
+
|
|
529
|
+
return tool_func
|
|
530
|
+
|
|
531
|
+
# 创建签名
|
|
532
|
+
signature = inspect.Signature(params)
|
|
533
|
+
wrapped_func = make_tool_function(
|
|
534
|
+
tool,
|
|
535
|
+
signature,
|
|
536
|
+
annotations,
|
|
537
|
+
parameters_schema,
|
|
538
|
+
include_type_docstring,
|
|
539
|
+
)
|
|
540
|
+
|
|
541
|
+
result.append(wrapped_func)
|
|
542
|
+
|
|
543
|
+
return result
|
|
544
|
+
|
|
545
|
+
|
|
546
|
+
class ModelAdapter(ABC):
|
|
547
|
+
"""模型适配器接口 / Utils Model Adapter
|
|
548
|
+
|
|
549
|
+
用于包装框架模型,使其能够与 CommonModel 协同工作。"""
|
|
550
|
+
|
|
551
|
+
@abstractmethod
|
|
552
|
+
def wrap_model(self, common_model: CommonModel) -> Any:
|
|
553
|
+
"""包装 CommonModel 为框架特定的模型格式 / 包装 CommonModel 为framework特定的模型格式
|
|
554
|
+
|
|
555
|
+
Args:
|
|
556
|
+
common_model: CommonModel 实例
|
|
557
|
+
|
|
558
|
+
Returns:
|
|
559
|
+
框架特定的模型对象"""
|
|
560
|
+
pass
|