polaris-python-ai 0.0.1.dev2__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.
- polaris_python_ai-0.0.1.dev2/LICENSE +27 -0
- polaris_python_ai-0.0.1.dev2/MANIFEST.in +4 -0
- polaris_python_ai-0.0.1.dev2/PKG-INFO +351 -0
- polaris_python_ai-0.0.1.dev2/README-zh.md +286 -0
- polaris_python_ai-0.0.1.dev2/README.md +320 -0
- polaris_python_ai-0.0.1.dev2/docs/README.md +14 -0
- polaris_python_ai-0.0.1.dev2/docs/configuration.md +644 -0
- polaris_python_ai-0.0.1.dev2/docs/version-release.md +309 -0
- polaris_python_ai-0.0.1.dev2/examples/README-zh.md +44 -0
- polaris_python_ai-0.0.1.dev2/examples/README.md +48 -0
- polaris_python_ai-0.0.1.dev2/examples/_internal/_manage.py +580 -0
- polaris_python_ai-0.0.1.dev2/examples/_internal/_run.py +32 -0
- polaris_python_ai-0.0.1.dev2/examples/_internal/_runtime.py +203 -0
- polaris_python_ai-0.0.1.dev2/examples/_internal/_stub.py +222 -0
- polaris_python_ai-0.0.1.dev2/examples/_internal/_verify.py +493 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/README-zh.md +34 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/README.md +78 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/_common.py +96 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/a2a/README-zh.md +14 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/a2a/README.md +15 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/a2a/consumer/README-zh.md +15 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/a2a/consumer/README.md +14 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/a2a/consumer/main.py +49 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/a2a/consumer/test.md +99 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/a2a/provider/README-zh.md +17 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/a2a/provider/README.md +17 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/a2a/provider/main.py +137 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/a2a/provider/server.py +80 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/a2a/provider/skill_agent.py +79 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/a2a/provider/test.md +99 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/a2a/skill/README-zh.md +21 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/a2a/skill/README.md +23 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/a2a/skill/main.py +32 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/a2a/skill/test.md +24 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/a2a/test.md +99 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/mcp/README-zh.md +12 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/mcp/README.md +12 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/mcp/main.py +78 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/mcp/test.md +24 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/polaris.yaml +9 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/runtime/README-zh.md +21 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/runtime/README.md +17 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/runtime/main.py +99 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/runtime/test.md +27 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/skill/README-zh.md +12 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/skill/README.md +12 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/skill/mounted/README-zh.md +14 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/skill/mounted/README.md +13 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/skill/mounted/main.py +47 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/skill/mounted/test.md +99 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/skill/published/README-zh.md +14 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/skill/published/README.md +13 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/skill/published/main.py +49 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/skill/published/test.md +99 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/skill/test.md +99 -0
- polaris_python_ai-0.0.1.dev2/examples/agentscope/test.md +100 -0
- polaris_python_ai-0.0.1.dev2/examples/test.md +99 -0
- polaris_python_ai-0.0.1.dev2/pyproject.toml +65 -0
- polaris_python_ai-0.0.1.dev2/setup.cfg +4 -0
- polaris_python_ai-0.0.1.dev2/src/polaris_ai/__init__.py +34 -0
- polaris_python_ai-0.0.1.dev2/src/polaris_ai/_async.py +39 -0
- polaris_python_ai-0.0.1.dev2/src/polaris_ai/_version.py +18 -0
- polaris_python_ai-0.0.1.dev2/src/polaris_ai/a2a/__init__.py +31 -0
- polaris_python_ai-0.0.1.dev2/src/polaris_ai/a2a/_card.py +44 -0
- polaris_python_ai-0.0.1.dev2/src/polaris_ai/a2a/registry.py +240 -0
- polaris_python_ai-0.0.1.dev2/src/polaris_ai/a2a/resolver.py +198 -0
- polaris_python_ai-0.0.1.dev2/src/polaris_ai/a2a/server.py +92 -0
- polaris_python_ai-0.0.1.dev2/src/polaris_ai/agentscope/__init__.py +76 -0
- polaris_python_ai-0.0.1.dev2/src/polaris_ai/agentscope/a2a.py +276 -0
- polaris_python_ai-0.0.1.dev2/src/polaris_ai/agentscope/cards.py +106 -0
- polaris_python_ai-0.0.1.dev2/src/polaris_ai/agentscope/config.py +221 -0
- polaris_python_ai-0.0.1.dev2/src/polaris_ai/agentscope/context.py +195 -0
- polaris_python_ai-0.0.1.dev2/src/polaris_ai/agentscope/mcp.py +598 -0
- polaris_python_ai-0.0.1.dev2/src/polaris_ai/agentscope/middleware.py +103 -0
- polaris_python_ai-0.0.1.dev2/src/polaris_ai/agentscope/runtime.py +291 -0
- polaris_python_ai-0.0.1.dev2/src/polaris_ai/agentscope/skill.py +464 -0
- polaris_python_ai-0.0.1.dev2/src/polaris_ai/langgraph/__init__.py +50 -0
- polaris_python_ai-0.0.1.dev2/src/polaris_ai/langgraph/config.py +160 -0
- polaris_python_ai-0.0.1.dev2/src/polaris_ai/langgraph/context.py +152 -0
- polaris_python_ai-0.0.1.dev2/src/polaris_ai/langgraph/registry.py +258 -0
- polaris_python_ai-0.0.1.dev2/src/polaris_ai/langgraph/remote.py +315 -0
- polaris_python_ai-0.0.1.dev2/src/polaris_ai/langgraph/resolver.py +226 -0
- polaris_python_ai-0.0.1.dev2/src/polaris_ai/log.py +408 -0
- polaris_python_ai-0.0.1.dev2/src/polaris_ai/metrics.py +154 -0
- polaris_python_ai-0.0.1.dev2/src/polaris_python_ai.egg-info/PKG-INFO +351 -0
- polaris_python_ai-0.0.1.dev2/src/polaris_python_ai.egg-info/SOURCES.txt +105 -0
- polaris_python_ai-0.0.1.dev2/src/polaris_python_ai.egg-info/dependency_links.txt +1 -0
- polaris_python_ai-0.0.1.dev2/src/polaris_python_ai.egg-info/requires.txt +22 -0
- polaris_python_ai-0.0.1.dev2/src/polaris_python_ai.egg-info/top_level.txt +1 -0
- polaris_python_ai-0.0.1.dev2/tests/__init__.py +16 -0
- polaris_python_ai-0.0.1.dev2/tests/_support.py +121 -0
- polaris_python_ai-0.0.1.dev2/tests/test_a2a.py +362 -0
- polaris_python_ai-0.0.1.dev2/tests/test_a2a_agent.py +257 -0
- polaris_python_ai-0.0.1.dev2/tests/test_a2a_transport.py +56 -0
- polaris_python_ai-0.0.1.dev2/tests/test_agent_cards.py +65 -0
- polaris_python_ai-0.0.1.dev2/tests/test_agent_config.py +115 -0
- polaris_python_ai-0.0.1.dev2/tests/test_agent_context.py +113 -0
- polaris_python_ai-0.0.1.dev2/tests/test_agent_runtime.py +281 -0
- polaris_python_ai-0.0.1.dev2/tests/test_dynamic_skill_middleware.py +148 -0
- polaris_python_ai-0.0.1.dev2/tests/test_example_scripts.py +271 -0
- polaris_python_ai-0.0.1.dev2/tests/test_langgraph.py +342 -0
- polaris_python_ai-0.0.1.dev2/tests/test_log.py +266 -0
- polaris_python_ai-0.0.1.dev2/tests/test_mcp.py +550 -0
- polaris_python_ai-0.0.1.dev2/tests/test_metrics.py +80 -0
- polaris_python_ai-0.0.1.dev2/tests/test_skill.py +345 -0
- polaris_python_ai-0.0.1.dev2/tests/test_skill_agent.py +53 -0
- polaris_python_ai-0.0.1.dev2/tests/test_snapshot_workflow.py +270 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Tencent
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
3. Neither the name of the copyright holder nor the names of its contributors
|
|
15
|
+
may be used to endorse or promote products derived from this software without
|
|
16
|
+
specific prior written permission.
|
|
17
|
+
|
|
18
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
19
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
20
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
21
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
22
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
23
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
24
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
25
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
26
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
27
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: polaris-python-ai
|
|
3
|
+
Version: 0.0.1.dev2
|
|
4
|
+
Summary: Polaris integrations for Python AI frameworks.
|
|
5
|
+
License-Expression: BSD-3-Clause
|
|
6
|
+
Project-URL: Homepage, https://github.com/polaris-contrib/polaris-python-ai
|
|
7
|
+
Project-URL: Repository, https://github.com/polaris-contrib/polaris-python-ai
|
|
8
|
+
Project-URL: Issues, https://github.com/polaris-contrib/polaris-python-ai/issues
|
|
9
|
+
Project-URL: Documentation, https://github.com/polaris-contrib/polaris-python-ai#readme
|
|
10
|
+
Requires-Python: >=3.12
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Requires-Dist: polaris-python<0.1,>=0.0.1.dev0
|
|
14
|
+
Provides-Extra: a2a
|
|
15
|
+
Requires-Dist: a2a-sdk<2,>=1.2.3; extra == "a2a"
|
|
16
|
+
Provides-Extra: agentscope
|
|
17
|
+
Requires-Dist: a2a-sdk<2,>=1.2.3; extra == "agentscope"
|
|
18
|
+
Requires-Dist: agentscope<3,>=2.0.10; extra == "agentscope"
|
|
19
|
+
Provides-Extra: langgraph
|
|
20
|
+
Requires-Dist: langgraph<2,>=1.0; extra == "langgraph"
|
|
21
|
+
Provides-Extra: mcp
|
|
22
|
+
Requires-Dist: a2a-sdk<2,>=1.2.3; extra == "mcp"
|
|
23
|
+
Requires-Dist: agentscope<3,>=2.0.10; extra == "mcp"
|
|
24
|
+
Requires-Dist: mcp<3,>=2; extra == "mcp"
|
|
25
|
+
Provides-Extra: all
|
|
26
|
+
Requires-Dist: a2a-sdk<2,>=1.2.3; extra == "all"
|
|
27
|
+
Requires-Dist: agentscope<3,>=2.0.10; extra == "all"
|
|
28
|
+
Requires-Dist: langgraph<2,>=1.0; extra == "all"
|
|
29
|
+
Requires-Dist: mcp<3,>=2; extra == "all"
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
# polaris-python-ai
|
|
33
|
+
|
|
34
|
+
Polaris integrations for Python AI frameworks. The repository publishes one
|
|
35
|
+
distribution, `polaris-python-ai`, with optional A2A, AgentScope, MCP, and
|
|
36
|
+
LangGraph extras under the `polaris_ai` import namespace.
|
|
37
|
+
|
|
38
|
+
## Development Installation
|
|
39
|
+
|
|
40
|
+
Use Python 3.12 or newer. Run from this project's root directory:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
uv sync --all-extras
|
|
44
|
+
uv run python -m pytest
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The sibling `polaris-python` checkout is selected through `[tool.uv.sources]`;
|
|
48
|
+
published wheel metadata contains only version ranges.
|
|
49
|
+
|
|
50
|
+
Install framework adapters through package extras:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install "polaris-python-ai[a2a]"
|
|
54
|
+
pip install "polaris-python-ai[agentscope]"
|
|
55
|
+
pip install "polaris-python-ai[langgraph]"
|
|
56
|
+
pip install "polaris-python-ai[mcp]"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
See [docs/version-release.md](docs/version-release.md) for the single version file,
|
|
60
|
+
Snapshot format, and release tag.
|
|
61
|
+
|
|
62
|
+
See [docs/configuration.md](docs/configuration.md) for the complete feature and
|
|
63
|
+
configuration guide, including Core SDK, Skill, A2A, MCP and AI logging settings.
|
|
64
|
+
|
|
65
|
+
AgentScope, A2A, and LangGraph are optional. Base-package imports do not load
|
|
66
|
+
their SDKs. The integration targets AgentScope 2.0.10, A2A SDK 1.2.3, and
|
|
67
|
+
LangGraph 1.x. Legacy Java AgentCard JSON is decoded through the official A2A
|
|
68
|
+
compatibility parser.
|
|
69
|
+
|
|
70
|
+
## Shared AgentScope Context
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from polaris_ai.agentscope import PolarisAgentContext
|
|
74
|
+
|
|
75
|
+
with PolarisAgentContext.from_address("127.0.0.1:8091", "default") as polaris:
|
|
76
|
+
provider = polaris.provider
|
|
77
|
+
consumer = polaris.consumer
|
|
78
|
+
skill = polaris.skill
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The factory owns and closes only the context it creates. It disables stat
|
|
82
|
+
reporting and the provider lossless lifecycle, writes the connector token, and
|
|
83
|
+
maps every discovery host to the Skill endpoint on port 8094 unless
|
|
84
|
+
`skill_address` is explicit. A context passed through `sdk_context` is borrowed
|
|
85
|
+
unchanged and is never destroyed by the factory.
|
|
86
|
+
|
|
87
|
+
Applications that prefer environment-driven assembly can use:
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from polaris_ai.agentscope import PolarisAgentConfig, PolarisAgentRuntime
|
|
91
|
+
|
|
92
|
+
config = PolarisAgentConfig.from_env(agent_name="weather-agent")
|
|
93
|
+
async with PolarisAgentRuntime(config) as runtime:
|
|
94
|
+
loader = runtime.skill_loader
|
|
95
|
+
resolver = runtime.resolver
|
|
96
|
+
registrar = runtime.registrar
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`POLARIS_AI_*` variables take precedence. `POLARIS_SERVER_ADDRESS`,
|
|
100
|
+
`POLARIS_TOKEN`, `POLARIS_SERVICE_TOKEN`, and
|
|
101
|
+
`POLARIS_SKILL_SERVER_ADDRESS` are accepted as fallbacks. Connector and service
|
|
102
|
+
tokens remain separate. Passing an explicit `sdk_context` borrows it and never
|
|
103
|
+
changes its configuration or ownership.
|
|
104
|
+
When `skill.attach_to_agent` is true, `runtime.skill_toolkit` builds a native
|
|
105
|
+
Toolkit containing the Polaris loader, and `runtime.create_agent()` attaches that
|
|
106
|
+
Toolkit plus the dynamic refresh middleware.
|
|
107
|
+
|
|
108
|
+
## AI Logging
|
|
109
|
+
|
|
110
|
+
AI extension logging is optional and disabled for managed files by default. Set
|
|
111
|
+
`ai_log_options` on the runtime to enable Polaris-managed category logs:
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
from polaris_ai import PolarisAILogOptions
|
|
115
|
+
|
|
116
|
+
runtime = PolarisAgentRuntime(
|
|
117
|
+
config,
|
|
118
|
+
log_options=core_sdk_log_options,
|
|
119
|
+
ai_log_options=PolarisAILogOptions(
|
|
120
|
+
level="info",
|
|
121
|
+
log_dir="./polaris/log/ai",
|
|
122
|
+
max_bytes=50 * 1024 * 1024,
|
|
123
|
+
backup_count=5,
|
|
124
|
+
max_age_days=30,
|
|
125
|
+
warning_throttle_seconds=30,
|
|
126
|
+
),
|
|
127
|
+
)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Managed logs are separated from Core SDK logs under `./polaris/log/ai` by
|
|
131
|
+
default. Categories are `base`, `agentscope`, `skill`, `a2a`, `mcp`,
|
|
132
|
+
`langgraph`, and `metrics`. Each active runtime writes files such as
|
|
133
|
+
`base/polaris-ai-base-<clientId>.log`; files are created on first use. Category
|
|
134
|
+
files rotate at 50MB, keep five backups, retain backups for 30 days, and use
|
|
135
|
+
exclusive file locks.
|
|
136
|
+
|
|
137
|
+
`POLARIS_AI_LOG_LEVEL` and `POLARIS_AI_LOG_PATH` are used when the corresponding
|
|
138
|
+
options are not assigned. `POLARIS_AI_LOG_CAPTURE_AGENTSCOPE=true` optionally
|
|
139
|
+
adds an AgentScope `"as"` logger handler for the runtime lifetime. The extension
|
|
140
|
+
does not call `agentscope.setup_logger()` or replace existing AgentScope handlers.
|
|
141
|
+
Because `"as"` is process-global, only one active runtime can capture it at a time.
|
|
142
|
+
|
|
143
|
+
The detail policy avoids flooding high-frequency paths:
|
|
144
|
+
|
|
145
|
+
- `INFO`: runtime lifecycle, Skill reference changes, A2A instance failover and MCP pool changes.
|
|
146
|
+
- `DEBUG`: per-page listing, cache hits, candidate filtering, selected endpoints and retries.
|
|
147
|
+
- `WARNING`: recoverable failures, deduplicated by key. The default interval is 30 seconds;
|
|
148
|
+
the next emitted warning includes the number of suppressed occurrences.
|
|
149
|
+
|
|
150
|
+
## AgentScope Skill Loader
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
import asyncio
|
|
154
|
+
from agentscope.tool import Toolkit
|
|
155
|
+
from polaris.api.config import init_context_by_file
|
|
156
|
+
from polaris_ai.agentscope import PolarisSkillLoader
|
|
157
|
+
|
|
158
|
+
async def main():
|
|
159
|
+
context = init_context_by_file("polaris.yaml")
|
|
160
|
+
try:
|
|
161
|
+
async with PolarisSkillLoader(context, namespace="default") as loader:
|
|
162
|
+
toolkit = Toolkit(skills_or_loaders=[loader])
|
|
163
|
+
print(await toolkit.get_skill_instructions())
|
|
164
|
+
finally:
|
|
165
|
+
await asyncio.to_thread(context.destroy)
|
|
166
|
+
|
|
167
|
+
asyncio.run(main())
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Published loading uses 50-item pages, at most 100 skills, and a 30-second list
|
|
171
|
+
cache. `names=["weather"]` skips listing. `service_name="weather-agent"` loads
|
|
172
|
+
only that service's mounted skills. Mount identities are `namespace:skillName`,
|
|
173
|
+
split at the first colon; other namespaces are skipped. Version precedence is
|
|
174
|
+
mounted version, configured `version`, then the server's active version.
|
|
175
|
+
|
|
176
|
+
ZIPs include `SKILL.md` and assets. Both flat archives and a single skill folder
|
|
177
|
+
are accepted; traversal, absolute paths, links, conflicting entries and multiple
|
|
178
|
+
skill roots are rejected. Maximum archive/expanded size is 64 MiB and the entry
|
|
179
|
+
limit is 2048. Skills live in temporary directories until the loader closes.
|
|
180
|
+
Successful loads with an explicit version are cached by name/version. A blank
|
|
181
|
+
version is an active-version alias: it is revalidated and materialized under
|
|
182
|
+
the server-returned version. Middleware instances sharing one loader also
|
|
183
|
+
share one Toolkit snapshot, so agents created from the same runtime see the
|
|
184
|
+
latest successful refresh.
|
|
185
|
+
Persistent offline fallback is provided by the SDK, not a second adapter cache.
|
|
186
|
+
Published list errors propagate; an individual failed download is logged and
|
|
187
|
+
skipped. Empty mount tables are re-read on the next call.
|
|
188
|
+
`async_list_skill_names()` refreshes references without downloading ZIPs, and
|
|
189
|
+
mounted `async_skill_exists()` checks only the mount table. A `400202` response
|
|
190
|
+
raises `PolarisSkillNotFoundError`. The loader exposes `source`, `location`,
|
|
191
|
+
`get_repository_info()`, and its read-only identity.
|
|
192
|
+
|
|
193
|
+
`list_skills()` follows AgentScope's asynchronous interface. Explicit single-skill
|
|
194
|
+
loading is `async_get_skill()`. `close()` and `async_close()` remove temporary assets.
|
|
195
|
+
|
|
196
|
+
## A2A
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
from polaris_ai.agentscope import A2ATransport, PolarisA2ARegistrar, PolarisA2AResolver
|
|
200
|
+
from agentscope.agent import A2AAgent
|
|
201
|
+
|
|
202
|
+
# card is the native a2a.types.AgentCard for an already listening server.
|
|
203
|
+
async with PolarisA2ARegistrar(context, service_token="service-token") as registrar:
|
|
204
|
+
await registrar.async_register(card, [A2ATransport("10.0.0.1", 8080)])
|
|
205
|
+
async with PolarisA2AResolver(context) as resolver:
|
|
206
|
+
remote_card = await resolver.async_resolve(card.name)
|
|
207
|
+
async with A2AAgent(remote_card) as agent:
|
|
208
|
+
reply = await agent.reply(message)
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Each transport becomes a Polaris instance. Metadata includes `ai-agent-name`,
|
|
212
|
+
`a2a.agent.card`, `a2a.transport` and `a2a.path`; TLS selects `http` or `https`.
|
|
213
|
+
Automatic heartbeat and re-registration use the SDK's `register_instance` with a
|
|
214
|
+
5-second TTL. `service_token` is used for registration, heartbeats and
|
|
215
|
+
deregistration, is distinct from the connector token, and may be empty when the
|
|
216
|
+
server permits unauthenticated registration. The SDK's `bindIP` is not a business
|
|
217
|
+
host.
|
|
218
|
+
|
|
219
|
+
Partial registration failure rolls back only that call's created instances.
|
|
220
|
+
Failed deregistrations stay in `registrar.registered`, so another `close()` can
|
|
221
|
+
retry. Cancelled registration waits for in-flight SDK work and rolls back its
|
|
222
|
+
successes. A closed registrar accepts further cleanup, but no new registrations.
|
|
223
|
+
`PolarisA2AServerRegistration` waits for `server_ready` before registration and
|
|
224
|
+
deregisters on normal or exceptional exit, replacing the Java Server / Spring
|
|
225
|
+
lifecycle assembly.
|
|
226
|
+
|
|
227
|
+
The resolver refreshes discovery on every lookup and stays on the selected
|
|
228
|
+
healthy `host:port`. It sees card updates in place and switches when that instance
|
|
229
|
+
is unhealthy, isolated, absent or has invalid metadata. Successful empty results
|
|
230
|
+
raise `AgentCardNotFoundError`; SDK discovery errors reuse a previously decoded
|
|
231
|
+
card. The card's advertised URLs are preserved. Configure each server's card
|
|
232
|
+
with its externally reachable endpoints.
|
|
233
|
+
|
|
234
|
+
`PolarisA2AAgent` wraps the native AgentScope `A2AAgent`. It resolves once,
|
|
235
|
+
reuses the client for later turns, and rebuilds it after an explicit
|
|
236
|
+
`invalidate()` or a network failure that occurs before any response event is
|
|
237
|
+
delivered. Business errors are propagated without switching instances. Streaming
|
|
238
|
+
does not retry after an event has already been emitted.
|
|
239
|
+
|
|
240
|
+
Long-lived Skill-backed Agents can add `PolarisDynamicSkillMiddleware(loader)`.
|
|
241
|
+
It refreshes the loader before each reply, honors the loader's refresh interval,
|
|
242
|
+
and continues with the last successful state when Polaris is unavailable.
|
|
243
|
+
|
|
244
|
+
## MCP
|
|
245
|
+
|
|
246
|
+
```python
|
|
247
|
+
from polaris_ai.agentscope import PolarisMCPClientPool, PolarisMCPResolver
|
|
248
|
+
|
|
249
|
+
resolver = PolarisMCPResolver(context, "default")
|
|
250
|
+
client = await resolver.async_create_client("search-mcp")
|
|
251
|
+
|
|
252
|
+
async with PolarisMCPClientPool(resolver, "search-mcp") as pool:
|
|
253
|
+
selected = pool.choose_client()
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
The resolver reads standard MCP instance metadata, validates the protocol, builds
|
|
257
|
+
an AgentScope `HttpMCPConfig`, and maps disabled contract Tools to
|
|
258
|
+
`MCPClient.disable_tools`. Streamable HTTP is stateless; SSE is connected as a
|
|
259
|
+
stateful client. The pool watches Polaris changes and reconciles clients.
|
|
260
|
+
MCP Tool calls automatically report the original Tool name as method and
|
|
261
|
+
`instance_type="mcp"`. A2A replies report `message/send` with
|
|
262
|
+
`instance_type="a2a"`; native A2A timeout errors are reported as timeouts.
|
|
263
|
+
|
|
264
|
+
`build_agent_card()` and `PolarisAgentCardTemplate` construct native A2A cards
|
|
265
|
+
with protocol, Skills, streaming capability, and input/output modes.
|
|
266
|
+
|
|
267
|
+
MCP-aware applications can enable the resolver with `POLARIS_AI_MCP_ENABLED=true`
|
|
268
|
+
and use `POLARIS_AI_MCP_PROTOCOL_VERSION`, `POLARIS_AI_MCP_SCHEME`,
|
|
269
|
+
`POLARIS_AI_MCP_TIMEOUT`, `POLARIS_AI_MCP_HEADERS`,
|
|
270
|
+
`POLARIS_AI_MCP_FILTER_CONTRACT`, `POLARIS_AI_MCP_CALLER_SERVICE`, and
|
|
271
|
+
`POLARIS_AI_MCP_LABELS`.
|
|
272
|
+
|
|
273
|
+
Ordinary A2A method names are synchronous; `async_` names are asynchronous.
|
|
274
|
+
Skill and A2A adapters borrow `SDKContext`; `PolarisAgentContext` is the only
|
|
275
|
+
component that can own one. Close adapters before destroying a shared context.
|
|
276
|
+
Configuration remains in `polaris.yaml` or SDK `Configuration` objects; framework
|
|
277
|
+
settings are code options.
|
|
278
|
+
|
|
279
|
+
## LangGraph
|
|
280
|
+
|
|
281
|
+
```python
|
|
282
|
+
from polaris_ai.langgraph import (
|
|
283
|
+
LangGraphTransport,
|
|
284
|
+
PolarisLangGraphContext,
|
|
285
|
+
PolarisLangGraphRegistrar,
|
|
286
|
+
PolarisLangGraphResolver,
|
|
287
|
+
PolarisRemoteGraph,
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
with PolarisLangGraphContext.from_address(
|
|
291
|
+
"127.0.0.1:8091",
|
|
292
|
+
"production",
|
|
293
|
+
) as polaris, PolarisLangGraphRegistrar(
|
|
294
|
+
polaris.context,
|
|
295
|
+
"production",
|
|
296
|
+
) as registrar, PolarisLangGraphResolver(
|
|
297
|
+
polaris.context,
|
|
298
|
+
"production",
|
|
299
|
+
) as resolver:
|
|
300
|
+
registrar.register(
|
|
301
|
+
"review-service",
|
|
302
|
+
[LangGraphTransport("10.0.0.8", 8123, "/api", graph_id="review-graph")],
|
|
303
|
+
)
|
|
304
|
+
with PolarisRemoteGraph(
|
|
305
|
+
resolver,
|
|
306
|
+
"review-graph",
|
|
307
|
+
service="review-service",
|
|
308
|
+
) as graph:
|
|
309
|
+
result = graph.invoke(
|
|
310
|
+
{"messages": []},
|
|
311
|
+
{"configurable": {"thread_id": "thread-1"}},
|
|
312
|
+
)
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
The registrar writes `langgraph.graph-id`, `langgraph.path`, and
|
|
316
|
+
`langgraph.protocol` metadata to an ordinary Polaris instance. The resolver
|
|
317
|
+
selects a healthy, non-isolated instance for the requested graph, prefers the
|
|
318
|
+
previous `host:port`, and reuses the last endpoint when discovery temporarily
|
|
319
|
+
fails.
|
|
320
|
+
|
|
321
|
+
`PolarisRemoteGraph` exposes `invoke`, `ainvoke`, `stream`, and `astream`.
|
|
322
|
+
Each call refreshes discovery and reports `instance_type="langgraph"` to Polaris
|
|
323
|
+
without changing LangGraph's result or exception. The native `RemoteGraph`
|
|
324
|
+
client is reused until the selected URL changes.
|
|
325
|
+
|
|
326
|
+
`PolarisLangGraphConfig.from_env()` reads `POLARIS_AI_LANGGRAPH_*` first and
|
|
327
|
+
accepts `POLARIS_SERVER_ADDRESS`, `POLARIS_TOKEN`,
|
|
328
|
+
`POLARIS_SERVICE_TOKEN`, and `POLARIS_NAMESPACE` as fallbacks.
|
|
329
|
+
|
|
330
|
+
## Validation
|
|
331
|
+
|
|
332
|
+
```bash
|
|
333
|
+
uv run python -m pytest
|
|
334
|
+
uv build
|
|
335
|
+
./examples/verify.sh
|
|
336
|
+
./examples/verify.sh --only agentscope/a2a/skill
|
|
337
|
+
./examples/verify.sh --only agentscope/mcp
|
|
338
|
+
./examples/verify_e2e.sh --only agentscope/a2a
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
Tests use real AgentScope Toolkit and A2A types, public Polaris API wrappers and
|
|
342
|
+
a deterministic SDK engine. The A2A transport test exercises the official server
|
|
343
|
+
routes, JSON-RPC/SSE and `A2AAgent` through an in-process HTTP transport. These
|
|
344
|
+
tests do not constitute a commercial Polaris server integration test.
|
|
345
|
+
|
|
346
|
+
Offline example verification uses real SDK contexts and AgentScope, replacing
|
|
347
|
+
only Polaris RPC calls. Real-registry verification is explicitly enabled through
|
|
348
|
+
`verify_e2e.sh`. See [examples/test.md](examples/test.md) for prerequisites,
|
|
349
|
+
per-run logs, assertions and cleanup, and [AgentScope examples](examples/agentscope/README.md)
|
|
350
|
+
for manual commands. Real MCP Server E2E, remote controller governance, and
|
|
351
|
+
Skill write operations remain outside the current example coverage.
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
# polaris-python-ai
|
|
2
|
+
|
|
3
|
+
面向 Python AI 框架的北极星扩展。本仓库只发布一个 distribution
|
|
4
|
+
`polaris-python-ai`,通过可选 extras 提供 A2A、AgentScope、MCP 和 LangGraph
|
|
5
|
+
适配,公开导入名统一为 `polaris_ai`,底层调用 `polaris-python` 公开 API。
|
|
6
|
+
|
|
7
|
+
## 安装
|
|
8
|
+
|
|
9
|
+
使用 Python 3.12 或更高版本,在本项目根目录执行:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
uv sync --all-extras
|
|
13
|
+
uv run python -m pytest
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
相邻 `polaris-python` 通过 `[tool.uv.sources]` 参与本地解析;发布 wheel
|
|
17
|
+
只声明版本范围。
|
|
18
|
+
|
|
19
|
+
通过 extras 安装对应适配器:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install "polaris-python-ai[a2a]"
|
|
23
|
+
pip install "polaris-python-ai[agentscope]"
|
|
24
|
+
pip install "polaris-python-ai[langgraph]"
|
|
25
|
+
pip install "polaris-python-ai[mcp]"
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
单版本文件、Snapshot 和 Release tag 见 [docs/version-release.md](docs/version-release.md)。
|
|
29
|
+
|
|
30
|
+
完整功能与配置见 [docs/configuration.md](docs/configuration.md),包含 Core SDK、
|
|
31
|
+
Skill、A2A、MCP 和 AI 日志配置。
|
|
32
|
+
|
|
33
|
+
AgentScope、A2A 和 LangGraph 为可选依赖,基础包导入不会加载它们。当前验证组合为
|
|
34
|
+
AgentScope 2.0.10、A2A SDK 1.2.3 和 LangGraph 1.x,A2A 使用官方兼容解析器读取
|
|
35
|
+
Java 卡片 JSON。
|
|
36
|
+
|
|
37
|
+
## 共享 AgentScope Context
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from polaris_ai.agentscope import PolarisAgentContext
|
|
41
|
+
|
|
42
|
+
with PolarisAgentContext.from_address("127.0.0.1:8091", "default") as polaris:
|
|
43
|
+
provider = polaris.provider
|
|
44
|
+
consumer = polaris.consumer
|
|
45
|
+
skill = polaris.skill
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
工厂只拥有并关闭自己创建的 context。它会关闭 stat 上报、写入 connector token,
|
|
49
|
+
并关闭 provider 无损上下线,然后把每个发现地址的 host 映射到 8094 端口的 Skill
|
|
50
|
+
地址;显式 `skill_address` 优先。通过 `sdk_context` 传入的 context 保持原配置,
|
|
51
|
+
且不会被工厂销毁。
|
|
52
|
+
|
|
53
|
+
应用侧也可以使用环境变量装配:
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from polaris_ai.agentscope import PolarisAgentConfig, PolarisAgentRuntime
|
|
57
|
+
|
|
58
|
+
config = PolarisAgentConfig.from_env(agent_name="weather-agent")
|
|
59
|
+
async with PolarisAgentRuntime(config) as runtime:
|
|
60
|
+
loader = runtime.skill_loader
|
|
61
|
+
resolver = runtime.resolver
|
|
62
|
+
registrar = runtime.registrar
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`POLARIS_AI_*` 变量优先;`POLARIS_SERVER_ADDRESS`、`POLARIS_TOKEN`、
|
|
66
|
+
`POLARIS_SERVICE_TOKEN` 和 `POLARIS_SKILL_SERVER_ADDRESS` 作为回退。connector
|
|
67
|
+
token 与 service token 保持分离。显式传入 `sdk_context` 时只借用,不改配置、
|
|
68
|
+
不接管生命周期。
|
|
69
|
+
`skill.attach_to_agent` 为 true 时,`runtime.skill_toolkit` 会创建包含 Polaris
|
|
70
|
+
loader 的原生 Toolkit;`runtime.create_agent()` 会同时接入 Toolkit 和动态刷新
|
|
71
|
+
Middleware。
|
|
72
|
+
|
|
73
|
+
## AI 日志
|
|
74
|
+
|
|
75
|
+
AI 扩展默认不创建 Polaris 管理的日志文件。需要由 Runtime 管理分类日志时,
|
|
76
|
+
传入独立的 `ai_log_options`:
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from polaris_ai import PolarisAILogOptions
|
|
80
|
+
|
|
81
|
+
runtime = PolarisAgentRuntime(
|
|
82
|
+
config,
|
|
83
|
+
log_options=core_sdk_log_options,
|
|
84
|
+
ai_log_options=PolarisAILogOptions(
|
|
85
|
+
level="info",
|
|
86
|
+
log_dir="./polaris/log/ai",
|
|
87
|
+
max_bytes=50 * 1024 * 1024,
|
|
88
|
+
backup_count=5,
|
|
89
|
+
max_age_days=30,
|
|
90
|
+
warning_throttle_seconds=30,
|
|
91
|
+
),
|
|
92
|
+
)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
默认与 Core SDK 日志分开存放在 `./polaris/log/ai`。分类为 `base`、
|
|
96
|
+
`agentscope`、`skill`、`a2a`、`mcp`、`langgraph` 和 `metrics`,文件按首次使用创建,例如
|
|
97
|
+
`base/polaris-ai-base-<clientId>.log`。每类默认 50MB 滚动、保留 5 份和 30 天,
|
|
98
|
+
并使用跨进程独占文件锁。
|
|
99
|
+
|
|
100
|
+
代码未显式赋值的 level 和 path 会读取 `POLARIS_AI_LOG_LEVEL`、
|
|
101
|
+
`POLARIS_AI_LOG_PATH`。`POLARIS_AI_LOG_CAPTURE_AGENTSCOPE=true` 可以在 Runtime
|
|
102
|
+
生命周期内桥接 AgentScope `"as"` logger,但不会调用 `agentscope.setup_logger()`
|
|
103
|
+
或替换 AgentScope 原有 handler。由于 `"as"` 是进程级 logger,同一进程同一时刻只能
|
|
104
|
+
有一个活动 Runtime 捕获它。
|
|
105
|
+
|
|
106
|
+
详细程度策略:
|
|
107
|
+
|
|
108
|
+
- `INFO`:Runtime 生命周期、Skill 引用变化、A2A 实例切换和 MCP 池成员变化。
|
|
109
|
+
- `DEBUG`:分页、缓存命中、候选过滤、选中地址和重试等高频细节。
|
|
110
|
+
- `WARNING`:可恢复错误按 key 聚合,默认 30 秒内只输出一次;下一次输出会带上被抑制次数。
|
|
111
|
+
|
|
112
|
+
## Skill 加载
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
import asyncio
|
|
116
|
+
from agentscope.tool import Toolkit
|
|
117
|
+
from polaris.api.config import init_context_by_file
|
|
118
|
+
from polaris_ai.agentscope import PolarisSkillLoader
|
|
119
|
+
|
|
120
|
+
async def main():
|
|
121
|
+
context = init_context_by_file("polaris.yaml")
|
|
122
|
+
try:
|
|
123
|
+
async with PolarisSkillLoader(context, namespace="default") as loader:
|
|
124
|
+
toolkit = Toolkit(skills_or_loaders=[loader])
|
|
125
|
+
print(await toolkit.get_skill_instructions())
|
|
126
|
+
finally:
|
|
127
|
+
await asyncio.to_thread(context.destroy)
|
|
128
|
+
|
|
129
|
+
asyncio.run(main())
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
默认每页 50 条,最多加载 100 个,列表缓存 30 秒。指定 `names=["weather"]`
|
|
133
|
+
直接下载,跳过列表;指定 `service_name="weather-agent"` 只加载该服务挂载的 Skill。
|
|
134
|
+
挂载名称 `namespace:skillName` 按第一个冒号切分,其他命名空间跳过,重复名称保留第一条。
|
|
135
|
+
版本优先级为挂载版本、构造时指定的 `version`、服务端 active 版本。
|
|
136
|
+
|
|
137
|
+
显式版本下载成功后按名称和版本缓存;空版本表示服务端 active 版本,会重新验证,并按服务端
|
|
138
|
+
返回的实际版本物化缓存。共享同一个 loader 的多个动态刷新中间件共享一份 Toolkit 快照,
|
|
139
|
+
同一 Runtime 创建的多个 Agent 都会看到最新成功刷新。
|
|
140
|
+
失败不会缓存。列表失败直接抛错,单个下载失败会记录日志并跳过,下次重试。空的已发布列表缓存
|
|
141
|
+
30 秒,空挂载表下次重新查询。
|
|
142
|
+
`async_list_skill_names()` 只刷新引用列表,不下载 ZIP;挂载模式的
|
|
143
|
+
`async_skill_exists()` 只检查挂载表。服务端返回 `400202` 时抛
|
|
144
|
+
`PolarisSkillNotFoundError`。loader 暴露 `source`、`location`、
|
|
145
|
+
`get_repository_info()` 和只读标识。
|
|
146
|
+
|
|
147
|
+
下载 ZIP 中的 `SKILL.md` 和资源解压到临时目录,支持平铺或单一顶层 Skill 目录。
|
|
148
|
+
拒绝越界 / 绝对路径、链接、冲突文件和多 Skill 根目录,压缩与解压大小上限 64 MiB,
|
|
149
|
+
最多 2048 项。`close()` / `async_close()` 清理临时资源;持久化和网络故障降级由 SDK 负责。
|
|
150
|
+
`list_skills()` 遵循 AgentScope 原生异步接口,单个 Skill 使用 `async_get_skill()`。
|
|
151
|
+
|
|
152
|
+
## A2A 注册与发现
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
from agentscope.agent import A2AAgent
|
|
156
|
+
from polaris_ai.agentscope import A2ATransport, PolarisA2ARegistrar, PolarisA2AResolver
|
|
157
|
+
|
|
158
|
+
# card 为已启动服务的 a2a.types.AgentCard;message 为 AgentScope 消息。
|
|
159
|
+
async with PolarisA2ARegistrar(context, service_token="service-token") as registrar:
|
|
160
|
+
await registrar.async_register(card, [A2ATransport("10.0.0.1", 8080)])
|
|
161
|
+
async with PolarisA2AResolver(context) as resolver:
|
|
162
|
+
remote_card = await resolver.async_resolve(card.name)
|
|
163
|
+
async with A2AAgent(remote_card) as agent:
|
|
164
|
+
reply = await agent.reply(message)
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
每个 transport 注册一个普通实例,写入 `ai-agent-name`、`a2a.agent.card`、
|
|
168
|
+
`a2a.transport` 和 `a2a.path`。自动心跳和实例不存在重注册复用 SDK,TTL 默认 5 秒。
|
|
169
|
+
`service_token` 用于注册、心跳和反注册,它与 connector token 是两个配置;可为空,
|
|
170
|
+
由服务端决定是否接受无鉴权注册。
|
|
171
|
+
业务实例地址由 `A2ATransport.host` 指定,SDK `bindIP` 不作为业务实例 host。
|
|
172
|
+
|
|
173
|
+
部分注册失败回滚本次成功实例;反注册失败记录保留在 `registered`,可再次 `close()` 重试。
|
|
174
|
+
取消注册会等待在途请求完成并回滚,关闭后禁止新注册。
|
|
175
|
+
`PolarisA2AServerRegistration` 可等待 `server_ready` 事件后再注册,退出或异常时
|
|
176
|
+
统一反注册,用于替代 Java Server / Spring 的生命周期装配。
|
|
177
|
+
|
|
178
|
+
每次解析重新发现,按健康 `host:port` 保持粘性并读取最新卡片;实例移除、不健康、隔离或
|
|
179
|
+
卡片失效后切换。SDK 查询异常时复用此前成功卡片;成功空结果抛 `AgentCardNotFoundError`。
|
|
180
|
+
保留卡片声明的 URL,服务端需提供真实可访问的地址。
|
|
181
|
+
|
|
182
|
+
A2A 普通名称为同步接口,`async_` 前缀为异步接口;通用 A2A 入口也可从 `polaris_ai.a2a`
|
|
183
|
+
导入。Skill 与 A2A 适配器借用 `SDKContext`,只有 `PolarisAgentContext` 可以拥有
|
|
184
|
+
context。关闭时只释放自身资源,应用统一销毁共享 context。
|
|
185
|
+
SDK 配置继续使用 `polaris.yaml` 或 `Configuration`,框架参数通过代码指定。
|
|
186
|
+
|
|
187
|
+
`PolarisA2AAgent` 包装原生 AgentScope `A2AAgent`:首次调用解析卡片,后续轮次复用
|
|
188
|
+
客户端;显式 `invalidate()` 或尚未输出任何事件的网络错误会重新解析并重建客户端。
|
|
189
|
+
业务错误直接抛出,不切换实例;流式调用已经输出事件后不会重试。
|
|
190
|
+
|
|
191
|
+
长生命周期 Skill Agent 可加入 `PolarisDynamicSkillMiddleware(loader)`。它会在每轮
|
|
192
|
+
回复前刷新 loader,遵循刷新间隔;Polaris 不可用时记录警告并继续使用最近一次成功状态。
|
|
193
|
+
|
|
194
|
+
## MCP
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
from polaris_ai.agentscope import PolarisMCPClientPool, PolarisMCPResolver
|
|
198
|
+
|
|
199
|
+
resolver = PolarisMCPResolver(context, "default")
|
|
200
|
+
client = await resolver.async_create_client("search-mcp")
|
|
201
|
+
|
|
202
|
+
async with PolarisMCPClientPool(resolver, "search-mcp") as pool:
|
|
203
|
+
selected = pool.choose_client()
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Resolver 读取标准 MCP 实例 metadata、校验协议、构造 AgentScope `HttpMCPConfig`,
|
|
207
|
+
并把契约中禁用的 Tool 写入 `MCPClient.disable_tools`。Streamable HTTP 使用无状态
|
|
208
|
+
客户端,SSE 使用有状态连接。连接池通过 watch 感知实例增删并同步重建客户端。
|
|
209
|
+
MCP Tool 调用自动以原 Tool 名作为 method、`instance_type="mcp"` 上报;A2A 回复自动以
|
|
210
|
+
`message/send` 和 `instance_type="a2a"` 上报,官方 A2A 超时异常会按 timeout 上报。
|
|
211
|
+
|
|
212
|
+
`build_agent_card()` 与 `PolarisAgentCardTemplate` 可构造原生 A2A 卡片,支持协议、
|
|
213
|
+
Skill、流式能力和输入输出模式。
|
|
214
|
+
|
|
215
|
+
MCP 应用可通过 `POLARIS_AI_MCP_ENABLED=true` 启用 resolver,并配置
|
|
216
|
+
`POLARIS_AI_MCP_PROTOCOL_VERSION`、`POLARIS_AI_MCP_SCHEME`、
|
|
217
|
+
`POLARIS_AI_MCP_TIMEOUT`、`POLARIS_AI_MCP_HEADERS`、
|
|
218
|
+
`POLARIS_AI_MCP_FILTER_CONTRACT`、`POLARIS_AI_MCP_CALLER_SERVICE` 和
|
|
219
|
+
`POLARIS_AI_MCP_LABELS`。
|
|
220
|
+
|
|
221
|
+
## LangGraph
|
|
222
|
+
|
|
223
|
+
```python
|
|
224
|
+
from polaris_ai.langgraph import (
|
|
225
|
+
LangGraphTransport,
|
|
226
|
+
PolarisLangGraphContext,
|
|
227
|
+
PolarisLangGraphRegistrar,
|
|
228
|
+
PolarisLangGraphResolver,
|
|
229
|
+
PolarisRemoteGraph,
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
with PolarisLangGraphContext.from_address(
|
|
233
|
+
"127.0.0.1:8091",
|
|
234
|
+
"production",
|
|
235
|
+
) as polaris, PolarisLangGraphRegistrar(
|
|
236
|
+
polaris.context,
|
|
237
|
+
"production",
|
|
238
|
+
) as registrar, PolarisLangGraphResolver(
|
|
239
|
+
polaris.context,
|
|
240
|
+
"production",
|
|
241
|
+
) as resolver:
|
|
242
|
+
registrar.register(
|
|
243
|
+
"review-service",
|
|
244
|
+
[LangGraphTransport("10.0.0.8", 8123, "/api", graph_id="review-graph")],
|
|
245
|
+
)
|
|
246
|
+
with PolarisRemoteGraph(
|
|
247
|
+
resolver,
|
|
248
|
+
"review-graph",
|
|
249
|
+
service="review-service",
|
|
250
|
+
) as graph:
|
|
251
|
+
result = graph.invoke(
|
|
252
|
+
{"messages": []},
|
|
253
|
+
{"configurable": {"thread_id": "thread-1"}},
|
|
254
|
+
)
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
注册器把 `langgraph.graph-id`、`langgraph.path` 和 `langgraph.protocol` 写入普通
|
|
258
|
+
Polaris 实例。Resolver 只选择对应 graph 的健康、未隔离实例,优先复用上次
|
|
259
|
+
`host:port`;发现临时失败时复用最近一次成功端点。
|
|
260
|
+
|
|
261
|
+
`PolarisRemoteGraph` 提供 `invoke`、`ainvoke`、`stream` 和 `astream`。每次调用都会
|
|
262
|
+
刷新发现,并以 `instance_type="langgraph"` 上报结果,不改变 LangGraph 的返回值或
|
|
263
|
+
异常。原生 `RemoteGraph` 客户端在 URL 不变时复用,切换实例后重建。
|
|
264
|
+
|
|
265
|
+
`PolarisLangGraphConfig.from_env()` 优先读取 `POLARIS_AI_LANGGRAPH_*`,并接受
|
|
266
|
+
`POLARIS_SERVER_ADDRESS`、`POLARIS_TOKEN`、`POLARIS_SERVICE_TOKEN` 和
|
|
267
|
+
`POLARIS_NAMESPACE` 作为回退。
|
|
268
|
+
|
|
269
|
+
## 验证
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
uv run python -m pytest
|
|
273
|
+
uv build
|
|
274
|
+
./examples/verify.sh
|
|
275
|
+
./examples/verify.sh --only agentscope/a2a/skill
|
|
276
|
+
./examples/verify.sh --only agentscope/mcp
|
|
277
|
+
./examples/verify_e2e.sh --only agentscope/a2a
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
已验证原生 Toolkit、官方 A2A 类型、公开 SDK 包装器,以及本地 JSON-RPC/SSE 调用。
|
|
281
|
+
单元测试使用确定性 SDK 替身。examples 离线集成测试使用真实 SDK 和 AgentScope,
|
|
282
|
+
仅替换 Polaris gRPC 边界;真实注册中心验证由 verify_e2e.sh 显式运行。
|
|
283
|
+
运行参数、前置资源、日志和清理见 [examples/test.md](examples/test.md)。
|
|
284
|
+
可运行的已发布 Skill、挂载 Skill、A2A provider / consumer 和 Skill 驱动 A2A 示例见
|
|
285
|
+
[examples/agentscope](examples/agentscope/README.md),A2A Echo 示例无需模型密钥。
|
|
286
|
+
真实 MCP Server E2E、远端 Controller 治理和 Skill 写操作暂不在当前范围。
|