agent-runtime-sdk 0.1.0__tar.gz

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 (57) hide show
  1. agent_runtime_sdk-0.1.0/MANIFEST.in +12 -0
  2. agent_runtime_sdk-0.1.0/PKG-INFO +125 -0
  3. agent_runtime_sdk-0.1.0/README_PACKAGE.md +99 -0
  4. agent_runtime_sdk-0.1.0/agent_runtime/__init__.py +84 -0
  5. agent_runtime_sdk-0.1.0/agent_runtime/builder.py +317 -0
  6. agent_runtime_sdk-0.1.0/agent_runtime/config/__init__.py +29 -0
  7. agent_runtime_sdk-0.1.0/agent_runtime/config/definitions.py +144 -0
  8. agent_runtime_sdk-0.1.0/agent_runtime/config/policies.py +63 -0
  9. agent_runtime_sdk-0.1.0/agent_runtime/config/storage.py +117 -0
  10. agent_runtime_sdk-0.1.0/agent_runtime/context.py +10 -0
  11. agent_runtime_sdk-0.1.0/agent_runtime/definitions.py +33 -0
  12. agent_runtime_sdk-0.1.0/agent_runtime/discovery.py +16 -0
  13. agent_runtime_sdk-0.1.0/agent_runtime/exceptions.py +74 -0
  14. agent_runtime_sdk-0.1.0/agent_runtime/mcp/__init__.py +28 -0
  15. agent_runtime_sdk-0.1.0/agent_runtime/mcp/discovery.py +146 -0
  16. agent_runtime_sdk-0.1.0/agent_runtime/mcp/metadata.py +68 -0
  17. agent_runtime_sdk-0.1.0/agent_runtime/mcp/utils.py +52 -0
  18. agent_runtime_sdk-0.1.0/agent_runtime/model_registry.py +40 -0
  19. agent_runtime_sdk-0.1.0/agent_runtime/plugins/__init__.py +4 -0
  20. agent_runtime_sdk-0.1.0/agent_runtime/plugins/base.py +90 -0
  21. agent_runtime_sdk-0.1.0/agent_runtime/plugins/default.py +19 -0
  22. agent_runtime_sdk-0.1.0/agent_runtime/plugins/instructions.py +38 -0
  23. agent_runtime_sdk-0.1.0/agent_runtime/plugins/loader.py +59 -0
  24. agent_runtime_sdk-0.1.0/agent_runtime/policies.py +15 -0
  25. agent_runtime_sdk-0.1.0/agent_runtime/runtime.py +110 -0
  26. agent_runtime_sdk-0.1.0/agent_runtime/runtime_engine/__init__.py +22 -0
  27. agent_runtime_sdk-0.1.0/agent_runtime/runtime_engine/a2a_bridge.py +190 -0
  28. agent_runtime_sdk-0.1.0/agent_runtime/runtime_engine/a2a_task_io.py +165 -0
  29. agent_runtime_sdk-0.1.0/agent_runtime/runtime_engine/agent_build.py +315 -0
  30. agent_runtime_sdk-0.1.0/agent_runtime/runtime_engine/context.py +469 -0
  31. agent_runtime_sdk-0.1.0/agent_runtime/runtime_engine/loading.py +170 -0
  32. agent_runtime_sdk-0.1.0/agent_runtime/runtime_engine/observability.py +154 -0
  33. agent_runtime_sdk-0.1.0/agent_runtime/runtime_engine/policy_registry.py +98 -0
  34. agent_runtime_sdk-0.1.0/agent_runtime/runtime_engine/protocol_tools.py +94 -0
  35. agent_runtime_sdk-0.1.0/agent_runtime/runtime_engine/task_flow.py +897 -0
  36. agent_runtime_sdk-0.1.0/agent_runtime/runtime_engine/tool_flow.py +332 -0
  37. agent_runtime_sdk-0.1.0/agent_runtime/sdk_agent.py +548 -0
  38. agent_runtime_sdk-0.1.0/agent_runtime/server/__init__.py +15 -0
  39. agent_runtime_sdk-0.1.0/agent_runtime/server/app_factory.py +37 -0
  40. agent_runtime_sdk-0.1.0/agent_runtime/server/bootstrap.py +48 -0
  41. agent_runtime_sdk-0.1.0/agent_runtime/server/endpoint_utils.py +37 -0
  42. agent_runtime_sdk-0.1.0/agent_runtime/server/management.py +107 -0
  43. agent_runtime_sdk-0.1.0/agent_runtime/smol/__init__.py +4 -0
  44. agent_runtime_sdk-0.1.0/agent_runtime/smol/agents.py +431 -0
  45. agent_runtime_sdk-0.1.0/agent_runtime/smol/llm_models.py +212 -0
  46. agent_runtime_sdk-0.1.0/agent_runtime/smol/memory.py +111 -0
  47. agent_runtime_sdk-0.1.0/agent_runtime/smol/models.py +69 -0
  48. agent_runtime_sdk-0.1.0/agent_runtime/standalone.py +57 -0
  49. agent_runtime_sdk-0.1.0/agent_runtime/storage.py +5 -0
  50. agent_runtime_sdk-0.1.0/agent_runtime/tools.py +5 -0
  51. agent_runtime_sdk-0.1.0/agent_runtime_sdk.egg-info/PKG-INFO +125 -0
  52. agent_runtime_sdk-0.1.0/agent_runtime_sdk.egg-info/SOURCES.txt +55 -0
  53. agent_runtime_sdk-0.1.0/agent_runtime_sdk.egg-info/dependency_links.txt +1 -0
  54. agent_runtime_sdk-0.1.0/agent_runtime_sdk.egg-info/requires.txt +20 -0
  55. agent_runtime_sdk-0.1.0/agent_runtime_sdk.egg-info/top_level.txt +1 -0
  56. agent_runtime_sdk-0.1.0/pyproject.toml +44 -0
  57. agent_runtime_sdk-0.1.0/setup.cfg +4 -0
@@ -0,0 +1,12 @@
1
+ include README_PACKAGE.md
2
+ include pyproject.toml
3
+ recursive-include agent_runtime *.py
4
+ recursive-exclude agent_app *
5
+ recursive-exclude tests *
6
+ global-exclude __pycache__
7
+ global-exclude *.py[cod]
8
+ exclude README.md
9
+ exclude Dockerfile
10
+ exclude CSK_AGENT_API_FLOW.md
11
+ exclude main.py
12
+ exclude requirements.txt
@@ -0,0 +1,125 @@
1
+ Metadata-Version: 2.4
2
+ Name: agent-runtime-sdk
3
+ Version: 0.1.0
4
+ Summary: Single-agent runtime SDK for A2A agents backed by MCP tools.
5
+ Project-URL: Homepage, https://pypi.org/project/agent-runtime-sdk/
6
+ Requires-Python: >=3.11
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: a2a-sdk==0.3.22
9
+ Requires-Dist: smolagents[mcp]
10
+ Requires-Dist: openai
11
+ Requires-Dist: fastapi
12
+ Requires-Dist: uvicorn
13
+ Requires-Dist: httpx
14
+ Requires-Dist: jinja2
15
+ Requires-Dist: pyyaml
16
+ Requires-Dist: loguru
17
+ Requires-Dist: pydantic
18
+ Requires-Dist: rich
19
+ Provides-Extra: observability
20
+ Requires-Dist: langfuse; extra == "observability"
21
+ Requires-Dist: openinference-instrumentation-smolagents; extra == "observability"
22
+ Provides-Extra: dev
23
+ Requires-Dist: build; extra == "dev"
24
+ Requires-Dist: pytest; extra == "dev"
25
+ Requires-Dist: twine; extra == "dev"
26
+
27
+ # agent-runtime-sdk
28
+
29
+ Single-agent runtime SDK for exposing MCP tools as an A2A-compatible agent service.
30
+
31
+ ## Install
32
+
33
+ ```bash
34
+ pip install agent-runtime-sdk
35
+ ```
36
+
37
+ Install optional Langfuse/smolagents observability support:
38
+
39
+ ```bash
40
+ pip install "agent-runtime-sdk[observability]"
41
+ ```
42
+
43
+ ## Basic Usage
44
+
45
+ Create an agent project:
46
+
47
+ ```text
48
+ my-agent/
49
+ ├── agent_app/
50
+ │ ├── agent.yaml
51
+ │ └── plugin.py
52
+ └── main.py
53
+ ```
54
+
55
+ Example `main.py`:
56
+
57
+ ```python
58
+ from pathlib import Path
59
+
60
+ from agent_runtime.standalone import build_single_agent_app, run_single_agent_app
61
+
62
+
63
+ app = build_single_agent_app(
64
+ Path(__file__).resolve().parent / "agent_app" / "agent.yaml"
65
+ )
66
+
67
+
68
+ if __name__ == "__main__":
69
+ run_single_agent_app(app)
70
+ ```
71
+
72
+ Example SDK import:
73
+
74
+ ```python
75
+ from agent_runtime import AgentBuilder
76
+ ```
77
+
78
+ ## Minimal Configuration
79
+
80
+ Example `agent_app/agent.yaml`:
81
+
82
+ ```yaml
83
+ agent:
84
+ agent_id: example-agent
85
+ name: Example Agent
86
+ description: Example A2A agent backed by MCP tools.
87
+
88
+ runtime:
89
+ public_base_url: ${AGENT_PUBLIC_BASE_URL:-http://127.0.0.1:10020}
90
+ model:
91
+ provider: openai_compatible
92
+ api_base: ${MODEL_SOURCE_API_BASE}
93
+ model_id: ${MODEL_SOURCE_MODEL_ID}
94
+ api_key_env: MODEL_SOURCE_API_KEY
95
+
96
+ mcps:
97
+ - name: example
98
+ url: ${MCP_EXAMPLE_URL}
99
+ transport: streamable-http
100
+ ```
101
+
102
+ Run:
103
+
104
+ ```bash
105
+ export MODEL_SOURCE_API_BASE="https://your-model-api.example.com"
106
+ export MODEL_SOURCE_MODEL_ID="your-model-id"
107
+ export MODEL_SOURCE_API_KEY="your-api-key"
108
+ export MCP_EXAMPLE_URL="http://127.0.0.1:8000/mcp"
109
+ export AGENT_PUBLIC_BASE_URL="http://127.0.0.1:10020"
110
+
111
+ python main.py
112
+ ```
113
+
114
+ ## Langfuse
115
+
116
+ After installing `agent-runtime-sdk[observability]`, configure:
117
+
118
+ ```bash
119
+ export MCP_AGENT_LANGFUSE_ENABLED=true
120
+ export LANGFUSE_BASE_URL="http://your-langfuse-host:3000"
121
+ export LANGFUSE_PUBLIC_KEY="pk-lf-..."
122
+ export LANGFUSE_SECRET_KEY="sk-lf-..."
123
+ ```
124
+
125
+ Set `MCP_AGENT_LANGFUSE_ENABLED=false` to disable instrumentation.
@@ -0,0 +1,99 @@
1
+ # agent-runtime-sdk
2
+
3
+ Single-agent runtime SDK for exposing MCP tools as an A2A-compatible agent service.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install agent-runtime-sdk
9
+ ```
10
+
11
+ Install optional Langfuse/smolagents observability support:
12
+
13
+ ```bash
14
+ pip install "agent-runtime-sdk[observability]"
15
+ ```
16
+
17
+ ## Basic Usage
18
+
19
+ Create an agent project:
20
+
21
+ ```text
22
+ my-agent/
23
+ ├── agent_app/
24
+ │ ├── agent.yaml
25
+ │ └── plugin.py
26
+ └── main.py
27
+ ```
28
+
29
+ Example `main.py`:
30
+
31
+ ```python
32
+ from pathlib import Path
33
+
34
+ from agent_runtime.standalone import build_single_agent_app, run_single_agent_app
35
+
36
+
37
+ app = build_single_agent_app(
38
+ Path(__file__).resolve().parent / "agent_app" / "agent.yaml"
39
+ )
40
+
41
+
42
+ if __name__ == "__main__":
43
+ run_single_agent_app(app)
44
+ ```
45
+
46
+ Example SDK import:
47
+
48
+ ```python
49
+ from agent_runtime import AgentBuilder
50
+ ```
51
+
52
+ ## Minimal Configuration
53
+
54
+ Example `agent_app/agent.yaml`:
55
+
56
+ ```yaml
57
+ agent:
58
+ agent_id: example-agent
59
+ name: Example Agent
60
+ description: Example A2A agent backed by MCP tools.
61
+
62
+ runtime:
63
+ public_base_url: ${AGENT_PUBLIC_BASE_URL:-http://127.0.0.1:10020}
64
+ model:
65
+ provider: openai_compatible
66
+ api_base: ${MODEL_SOURCE_API_BASE}
67
+ model_id: ${MODEL_SOURCE_MODEL_ID}
68
+ api_key_env: MODEL_SOURCE_API_KEY
69
+
70
+ mcps:
71
+ - name: example
72
+ url: ${MCP_EXAMPLE_URL}
73
+ transport: streamable-http
74
+ ```
75
+
76
+ Run:
77
+
78
+ ```bash
79
+ export MODEL_SOURCE_API_BASE="https://your-model-api.example.com"
80
+ export MODEL_SOURCE_MODEL_ID="your-model-id"
81
+ export MODEL_SOURCE_API_KEY="your-api-key"
82
+ export MCP_EXAMPLE_URL="http://127.0.0.1:8000/mcp"
83
+ export AGENT_PUBLIC_BASE_URL="http://127.0.0.1:10020"
84
+
85
+ python main.py
86
+ ```
87
+
88
+ ## Langfuse
89
+
90
+ After installing `agent-runtime-sdk[observability]`, configure:
91
+
92
+ ```bash
93
+ export MCP_AGENT_LANGFUSE_ENABLED=true
94
+ export LANGFUSE_BASE_URL="http://your-langfuse-host:3000"
95
+ export LANGFUSE_PUBLIC_KEY="pk-lf-..."
96
+ export LANGFUSE_SECRET_KEY="sk-lf-..."
97
+ ```
98
+
99
+ Set `MCP_AGENT_LANGFUSE_ENABLED=false` to disable instrumentation.
@@ -0,0 +1,84 @@
1
+ from importlib.metadata import PackageNotFoundError, version
2
+
3
+ from .builder import AgentBuilder
4
+ from .definitions import (
5
+ A2ASettings,
6
+ AgentConfig,
7
+ AgentDefinition,
8
+ InputFieldDefinition,
9
+ MCPSettings,
10
+ ModelSettings,
11
+ PluginSettings,
12
+ RuntimeConfig,
13
+ SkillDefinition,
14
+ ToolPolicy,
15
+ )
16
+ from .discovery import DiscoveredTool
17
+ from .exceptions import (
18
+ AgentBuildError,
19
+ AgentRuntimeError,
20
+ DefinitionLoadError,
21
+ MCPToolLoadError,
22
+ PluginLoadError,
23
+ TaskCancelledError,
24
+ TaskWaitTimeoutError,
25
+ UserCancelledError,
26
+ )
27
+ from .model_registry import ModelRegistry
28
+ from .plugins.base import BaseAgentPlugin, BeforeToolDecision, ToolExecutionContext
29
+ from .plugins.default import DefaultPlugin
30
+ from .runtime import ManagedAgentRuntime
31
+ from .sdk_agent import (
32
+ BaseSDKAgent,
33
+ ConfiguredSDKAgent,
34
+ DeclarativeSDKAgent,
35
+ OpenAICompatibleSingleMCPAgent,
36
+ SingleMCPAgent,
37
+ )
38
+ from .server.bootstrap import load_single_agent_runtime
39
+ from .standalone import build_single_agent_app, run_single_agent_app
40
+ from .storage import AgentDefinitionStore
41
+
42
+ try:
43
+ __version__ = version("agent-runtime-sdk")
44
+ except PackageNotFoundError: # pragma: no cover - source tree without installed metadata
45
+ __version__ = "0.1.0"
46
+
47
+ __all__ = [
48
+ "__version__",
49
+ "A2ASettings",
50
+ "AgentConfig",
51
+ "AgentBuilder",
52
+ "AgentBuildError",
53
+ "AgentDefinition",
54
+ "AgentDefinitionStore",
55
+ "AgentRuntimeError",
56
+ "BaseAgentPlugin",
57
+ "BaseSDKAgent",
58
+ "ConfiguredSDKAgent",
59
+ "DeclarativeSDKAgent",
60
+ "BeforeToolDecision",
61
+ "DefinitionLoadError",
62
+ "DefaultPlugin",
63
+ "InputFieldDefinition",
64
+ "MCPToolLoadError",
65
+ "MCPSettings",
66
+ "ManagedAgentRuntime",
67
+ "ModelSettings",
68
+ "ModelRegistry",
69
+ "OpenAICompatibleSingleMCPAgent",
70
+ "PluginSettings",
71
+ "PluginLoadError",
72
+ "RuntimeConfig",
73
+ "SkillDefinition",
74
+ "SingleMCPAgent",
75
+ "TaskCancelledError",
76
+ "TaskWaitTimeoutError",
77
+ "DiscoveredTool",
78
+ "ToolExecutionContext",
79
+ "ToolPolicy",
80
+ "UserCancelledError",
81
+ "build_single_agent_app",
82
+ "load_single_agent_runtime",
83
+ "run_single_agent_app",
84
+ ]
@@ -0,0 +1,317 @@
1
+ from __future__ import annotations
2
+
3
+ """面向使用者的代码式构建入口。"""
4
+
5
+ import os
6
+ import re
7
+ from pathlib import Path
8
+ from typing import Any, Callable
9
+ from urllib.parse import urlparse
10
+
11
+ from .config.definitions import (
12
+ DEFAULT_PASS_THROUGH_HEADERS,
13
+ AgentConfig,
14
+ AgentDefinition,
15
+ InputFieldDefinition,
16
+ MCPSettings,
17
+ ModelSettings,
18
+ PluginSettings,
19
+ RuntimeConfig,
20
+ SkillDefinition,
21
+ ToolPolicy,
22
+ )
23
+ from .mcp.metadata import DiscoveredTool
24
+ from .plugins.base import BaseAgentPlugin
25
+ from .runtime import ManagedAgentRuntime
26
+
27
+
28
+ class AgentBuilder:
29
+ """代码式配置入口。"""
30
+
31
+ def __init__(self):
32
+ self._agent = AgentConfig(
33
+ agent_id="mcp_agent",
34
+ name="",
35
+ description="",
36
+ version="0.1.0",
37
+ )
38
+ self._runtime = RuntimeConfig(
39
+ base_path=".",
40
+ public_base_url=None,
41
+ model=ModelSettings(),
42
+ )
43
+ self._mcps: list[MCPSettings] = []
44
+
45
+ self._plugin_instance: BaseAgentPlugin | None = None
46
+ self._discoverer: Callable[..., list[DiscoveredTool]] | None = None
47
+ self._explicit_model_api_key: str | None = None
48
+ self._auto_agent_id = True
49
+
50
+ def model(
51
+ self,
52
+ *,
53
+ provider: str | None = None,
54
+ api_base: str | None = None,
55
+ model_id: str | None = None,
56
+ api_key: str | None = None,
57
+ api_key_env: str | None = None,
58
+ max_steps: int | None = None,
59
+ flatten_messages_as_text: bool | None = None,
60
+ ) -> AgentBuilder:
61
+ model = self._runtime.model
62
+ if provider is not None:
63
+ model.provider = provider
64
+ if api_base is not None:
65
+ model.api_base = api_base
66
+ if model_id is not None:
67
+ model.model_id = model_id
68
+ if api_key is not None:
69
+ self._explicit_model_api_key = api_key
70
+ if api_key_env is not None:
71
+ model.api_key_env = api_key_env
72
+ if max_steps is not None:
73
+ model.max_steps = max_steps
74
+ if flatten_messages_as_text is not None:
75
+ model.flatten_messages_as_text = flatten_messages_as_text
76
+ return self
77
+
78
+ def tool_policy(
79
+ self,
80
+ tool_name: str,
81
+ *,
82
+ mcp_name: str | None = None,
83
+ requires_confirmation: bool = False,
84
+ prompt: str | None = None,
85
+ enabled: bool = True,
86
+ allow_arg_override: bool = True,
87
+ input_fields: list[InputFieldDefinition | dict[str, Any]] | None = None,
88
+ ) -> AgentBuilder:
89
+ fields: list[InputFieldDefinition] = []
90
+ if input_fields:
91
+ for field in input_fields:
92
+ if isinstance(field, InputFieldDefinition):
93
+ fields.append(field)
94
+ else:
95
+ fields.append(InputFieldDefinition.model_validate(field))
96
+ target_mcp = self._resolve_policy_target_mcp(mcp_name)
97
+ target_mcp.tool_policies[tool_name] = ToolPolicy(
98
+ enabled=enabled,
99
+ requires_confirmation=requires_confirmation,
100
+ input_fields=fields,
101
+ prompt=prompt,
102
+ allow_arg_override=allow_arg_override,
103
+ )
104
+ return self
105
+
106
+ def plugin(self, plugin: BaseAgentPlugin) -> AgentBuilder:
107
+ self._plugin_instance = plugin
108
+ return self
109
+
110
+ def instructions(self, text: str) -> AgentBuilder:
111
+ self._agent.extra_instructions = text
112
+ return self
113
+
114
+ def max_steps(self, n: int) -> AgentBuilder:
115
+ self._runtime.model.max_steps = n
116
+ return self
117
+
118
+ def agent_info(
119
+ self,
120
+ *,
121
+ agent_id: str | None = None,
122
+ name: str | None = None,
123
+ description: str | None = None,
124
+ version: str | None = None,
125
+ ) -> AgentBuilder:
126
+ if agent_id is not None:
127
+ self._agent.agent_id = agent_id
128
+ self._auto_agent_id = False
129
+ if name is not None:
130
+ self._agent.name = name
131
+ if description is not None:
132
+ self._agent.description = description
133
+ if version is not None:
134
+ self._agent.version = version
135
+ return self
136
+
137
+ def add_mcp(
138
+ self,
139
+ *,
140
+ name: str,
141
+ url: str,
142
+ transport: str = "streamable-http",
143
+ enabled: bool = True,
144
+ retry_count: int = 3,
145
+ retry_delay_seconds: float = 1.0,
146
+ static_headers: dict[str, str] | None = None,
147
+ pass_through_headers: list[str] | None = None,
148
+ tool_policies: dict[str, ToolPolicy | dict[str, Any]] | None = None,
149
+ ) -> AgentBuilder:
150
+ policies: dict[str, ToolPolicy] = {}
151
+ if tool_policies:
152
+ for tool_name, policy in tool_policies.items():
153
+ policies[tool_name] = (
154
+ policy
155
+ if isinstance(policy, ToolPolicy)
156
+ else ToolPolicy.model_validate(policy)
157
+ )
158
+ self._mcps.append(
159
+ MCPSettings(
160
+ name=name,
161
+ url=url,
162
+ transport=transport,
163
+ enabled=enabled,
164
+ retry_count=retry_count,
165
+ retry_delay_seconds=retry_delay_seconds,
166
+ static_headers=static_headers or {},
167
+ pass_through_headers=pass_through_headers
168
+ or list(DEFAULT_PASS_THROUGH_HEADERS),
169
+ tool_policies=policies,
170
+ )
171
+ )
172
+ return self
173
+
174
+ def a2a_skill(
175
+ self,
176
+ *,
177
+ id: str,
178
+ name: str,
179
+ description: str,
180
+ tags: list[str] | None = None,
181
+ examples: list[str] | None = None,
182
+ ) -> AgentBuilder:
183
+ self._agent.a2a.skills.append(
184
+ SkillDefinition(
185
+ id=id,
186
+ name=name,
187
+ description=description,
188
+ tags=tags or [],
189
+ examples=examples or [],
190
+ )
191
+ )
192
+ return self
193
+
194
+ def public_base_url(self, url: str) -> AgentBuilder:
195
+ self._runtime.public_base_url = url
196
+ return self
197
+
198
+ def discoverer(self, func: Callable[..., list[DiscoveredTool]]) -> AgentBuilder:
199
+ self._discoverer = func
200
+ return self
201
+
202
+ def build(self) -> ManagedAgentRuntime:
203
+ definition = self.build_definition()
204
+ if self._explicit_model_api_key is not None:
205
+ os.environ[definition.runtime.model.api_key_env] = (
206
+ self._explicit_model_api_key
207
+ )
208
+
209
+ runtime = ManagedAgentRuntime(
210
+ definition=definition,
211
+ public_base_url=definition.runtime.public_base_url or "",
212
+ discoverer=self._discoverer,
213
+ )
214
+ if self._plugin_instance is not None:
215
+ runtime.plugin = self._plugin_instance
216
+ runtime.reload(discover=True, skip_plugin_load=True)
217
+ else:
218
+ runtime.reload(discover=True)
219
+ if runtime.load_error:
220
+ raise RuntimeError(runtime.load_error)
221
+ return runtime
222
+
223
+ def build_a2a_app(
224
+ self, *, public_url: str | None = None, enable_management: bool = True
225
+ ):
226
+ from .server.app_factory import build_app_from_runtime
227
+
228
+ if public_url is not None:
229
+ self.public_base_url(public_url)
230
+ runtime = self.build()
231
+ return build_app_from_runtime(runtime, enable_management=enable_management)
232
+
233
+ @classmethod
234
+ def from_yaml(cls, path: str | Path) -> AgentBuilder:
235
+ from .config.storage import AgentDefinitionStore
236
+
237
+ resolved = Path(path).resolve()
238
+ definition = AgentDefinitionStore(resolved.parent).load_path(resolved)
239
+ return cls.from_definition(definition)
240
+
241
+ @classmethod
242
+ def from_definition(cls, definition: AgentDefinition) -> AgentBuilder:
243
+ builder = cls()
244
+ builder._agent = definition.agent.model_copy(deep=True)
245
+ builder._runtime = definition.runtime.model_copy(deep=True)
246
+ builder._mcps = [mcp.model_copy(deep=True) for mcp in definition.mcps]
247
+ builder._auto_agent_id = False
248
+ return builder
249
+
250
+ def build_definition(self) -> AgentDefinition:
251
+ """返回当前 builder 收敛后的 `AgentDefinition`。
252
+
253
+ 这个方法适合:
254
+
255
+ - 需要在真正 `build()` 前检查最终配置
256
+ - Python SDK 层希望把 YAML / 类配置统一收敛到同一个 definition 规格
257
+ """
258
+
259
+ if not self._mcps:
260
+ raise ValueError("at least one MCP server must be added before build()")
261
+
262
+ agent = self._agent.model_copy(deep=True)
263
+ runtime = self._runtime.model_copy(deep=True)
264
+ mcps = [mcp.model_copy(deep=True) for mcp in self._mcps]
265
+
266
+ if self._auto_agent_id:
267
+ agent.agent_id = self._auto_agent_id_from_mcps(mcps)
268
+ if not agent.name:
269
+ agent.name = agent.agent_id
270
+ if not agent.description:
271
+ agent.description = agent.name
272
+
273
+ if runtime.model.api_base is None:
274
+ runtime.model.api_base = os.getenv("MODEL_SOURCE_API_BASE")
275
+ if runtime.model.model_id is None:
276
+ runtime.model.model_id = os.getenv("MODEL_SOURCE_MODEL_ID")
277
+
278
+ if self._plugin_instance is not None:
279
+ runtime.plugin = None
280
+
281
+ return AgentDefinition(
282
+ agent=agent,
283
+ runtime=runtime,
284
+ mcps=mcps,
285
+ )
286
+
287
+ def _build_definition(self) -> AgentDefinition:
288
+ """兼容旧内部调用;新代码请优先使用 `build_definition()`。"""
289
+
290
+ return self.build_definition()
291
+
292
+ @staticmethod
293
+ def _auto_agent_id_from_mcps(mcps: list[MCPSettings]) -> str:
294
+ parsed = urlparse(mcps[0].url)
295
+ path_part = (
296
+ parsed.path.strip("/").split("/")[-1] if parsed.path.strip("/") else ""
297
+ )
298
+ if path_part:
299
+ normalized = re.sub(r"[^0-9a-zA-Z_]", "_", path_part)
300
+ return f"mcp_{normalized}" if normalized else "mcp_agent"
301
+ return "mcp_agent"
302
+
303
+ def _resolve_policy_target_mcp(self, mcp_name: str | None) -> MCPSettings:
304
+ if mcp_name is not None:
305
+ for mcp in self._mcps:
306
+ if mcp.name == mcp_name:
307
+ return mcp
308
+ raise ValueError(f"mcp '{mcp_name}' not found")
309
+ if len(self._mcps) == 1:
310
+ return self._mcps[0]
311
+ if not self._mcps:
312
+ raise ValueError(
313
+ "add an MCP before tool_policy(), or pass mcp_name after configuring MCPs"
314
+ )
315
+ raise ValueError(
316
+ "multiple MCPs configured; please specify mcp_name when setting tool_policy()"
317
+ )
@@ -0,0 +1,29 @@
1
+ """配置层内部实现。"""
2
+
3
+ from .definitions import (
4
+ A2ASettings,
5
+ AgentConfig,
6
+ AgentDefinition,
7
+ InputFieldDefinition,
8
+ MCPSettings,
9
+ ModelSettings,
10
+ PluginSettings,
11
+ RuntimeConfig,
12
+ SkillDefinition,
13
+ ToolPolicy,
14
+ )
15
+ from .storage import AgentDefinitionStore
16
+
17
+ __all__ = [
18
+ "A2ASettings",
19
+ "AgentConfig",
20
+ "AgentDefinition",
21
+ "AgentDefinitionStore",
22
+ "InputFieldDefinition",
23
+ "MCPSettings",
24
+ "ModelSettings",
25
+ "PluginSettings",
26
+ "RuntimeConfig",
27
+ "SkillDefinition",
28
+ "ToolPolicy",
29
+ ]