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.
Files changed (107) hide show
  1. polaris_python_ai-0.0.1.dev2/LICENSE +27 -0
  2. polaris_python_ai-0.0.1.dev2/MANIFEST.in +4 -0
  3. polaris_python_ai-0.0.1.dev2/PKG-INFO +351 -0
  4. polaris_python_ai-0.0.1.dev2/README-zh.md +286 -0
  5. polaris_python_ai-0.0.1.dev2/README.md +320 -0
  6. polaris_python_ai-0.0.1.dev2/docs/README.md +14 -0
  7. polaris_python_ai-0.0.1.dev2/docs/configuration.md +644 -0
  8. polaris_python_ai-0.0.1.dev2/docs/version-release.md +309 -0
  9. polaris_python_ai-0.0.1.dev2/examples/README-zh.md +44 -0
  10. polaris_python_ai-0.0.1.dev2/examples/README.md +48 -0
  11. polaris_python_ai-0.0.1.dev2/examples/_internal/_manage.py +580 -0
  12. polaris_python_ai-0.0.1.dev2/examples/_internal/_run.py +32 -0
  13. polaris_python_ai-0.0.1.dev2/examples/_internal/_runtime.py +203 -0
  14. polaris_python_ai-0.0.1.dev2/examples/_internal/_stub.py +222 -0
  15. polaris_python_ai-0.0.1.dev2/examples/_internal/_verify.py +493 -0
  16. polaris_python_ai-0.0.1.dev2/examples/agentscope/README-zh.md +34 -0
  17. polaris_python_ai-0.0.1.dev2/examples/agentscope/README.md +78 -0
  18. polaris_python_ai-0.0.1.dev2/examples/agentscope/_common.py +96 -0
  19. polaris_python_ai-0.0.1.dev2/examples/agentscope/a2a/README-zh.md +14 -0
  20. polaris_python_ai-0.0.1.dev2/examples/agentscope/a2a/README.md +15 -0
  21. polaris_python_ai-0.0.1.dev2/examples/agentscope/a2a/consumer/README-zh.md +15 -0
  22. polaris_python_ai-0.0.1.dev2/examples/agentscope/a2a/consumer/README.md +14 -0
  23. polaris_python_ai-0.0.1.dev2/examples/agentscope/a2a/consumer/main.py +49 -0
  24. polaris_python_ai-0.0.1.dev2/examples/agentscope/a2a/consumer/test.md +99 -0
  25. polaris_python_ai-0.0.1.dev2/examples/agentscope/a2a/provider/README-zh.md +17 -0
  26. polaris_python_ai-0.0.1.dev2/examples/agentscope/a2a/provider/README.md +17 -0
  27. polaris_python_ai-0.0.1.dev2/examples/agentscope/a2a/provider/main.py +137 -0
  28. polaris_python_ai-0.0.1.dev2/examples/agentscope/a2a/provider/server.py +80 -0
  29. polaris_python_ai-0.0.1.dev2/examples/agentscope/a2a/provider/skill_agent.py +79 -0
  30. polaris_python_ai-0.0.1.dev2/examples/agentscope/a2a/provider/test.md +99 -0
  31. polaris_python_ai-0.0.1.dev2/examples/agentscope/a2a/skill/README-zh.md +21 -0
  32. polaris_python_ai-0.0.1.dev2/examples/agentscope/a2a/skill/README.md +23 -0
  33. polaris_python_ai-0.0.1.dev2/examples/agentscope/a2a/skill/main.py +32 -0
  34. polaris_python_ai-0.0.1.dev2/examples/agentscope/a2a/skill/test.md +24 -0
  35. polaris_python_ai-0.0.1.dev2/examples/agentscope/a2a/test.md +99 -0
  36. polaris_python_ai-0.0.1.dev2/examples/agentscope/mcp/README-zh.md +12 -0
  37. polaris_python_ai-0.0.1.dev2/examples/agentscope/mcp/README.md +12 -0
  38. polaris_python_ai-0.0.1.dev2/examples/agentscope/mcp/main.py +78 -0
  39. polaris_python_ai-0.0.1.dev2/examples/agentscope/mcp/test.md +24 -0
  40. polaris_python_ai-0.0.1.dev2/examples/agentscope/polaris.yaml +9 -0
  41. polaris_python_ai-0.0.1.dev2/examples/agentscope/runtime/README-zh.md +21 -0
  42. polaris_python_ai-0.0.1.dev2/examples/agentscope/runtime/README.md +17 -0
  43. polaris_python_ai-0.0.1.dev2/examples/agentscope/runtime/main.py +99 -0
  44. polaris_python_ai-0.0.1.dev2/examples/agentscope/runtime/test.md +27 -0
  45. polaris_python_ai-0.0.1.dev2/examples/agentscope/skill/README-zh.md +12 -0
  46. polaris_python_ai-0.0.1.dev2/examples/agentscope/skill/README.md +12 -0
  47. polaris_python_ai-0.0.1.dev2/examples/agentscope/skill/mounted/README-zh.md +14 -0
  48. polaris_python_ai-0.0.1.dev2/examples/agentscope/skill/mounted/README.md +13 -0
  49. polaris_python_ai-0.0.1.dev2/examples/agentscope/skill/mounted/main.py +47 -0
  50. polaris_python_ai-0.0.1.dev2/examples/agentscope/skill/mounted/test.md +99 -0
  51. polaris_python_ai-0.0.1.dev2/examples/agentscope/skill/published/README-zh.md +14 -0
  52. polaris_python_ai-0.0.1.dev2/examples/agentscope/skill/published/README.md +13 -0
  53. polaris_python_ai-0.0.1.dev2/examples/agentscope/skill/published/main.py +49 -0
  54. polaris_python_ai-0.0.1.dev2/examples/agentscope/skill/published/test.md +99 -0
  55. polaris_python_ai-0.0.1.dev2/examples/agentscope/skill/test.md +99 -0
  56. polaris_python_ai-0.0.1.dev2/examples/agentscope/test.md +100 -0
  57. polaris_python_ai-0.0.1.dev2/examples/test.md +99 -0
  58. polaris_python_ai-0.0.1.dev2/pyproject.toml +65 -0
  59. polaris_python_ai-0.0.1.dev2/setup.cfg +4 -0
  60. polaris_python_ai-0.0.1.dev2/src/polaris_ai/__init__.py +34 -0
  61. polaris_python_ai-0.0.1.dev2/src/polaris_ai/_async.py +39 -0
  62. polaris_python_ai-0.0.1.dev2/src/polaris_ai/_version.py +18 -0
  63. polaris_python_ai-0.0.1.dev2/src/polaris_ai/a2a/__init__.py +31 -0
  64. polaris_python_ai-0.0.1.dev2/src/polaris_ai/a2a/_card.py +44 -0
  65. polaris_python_ai-0.0.1.dev2/src/polaris_ai/a2a/registry.py +240 -0
  66. polaris_python_ai-0.0.1.dev2/src/polaris_ai/a2a/resolver.py +198 -0
  67. polaris_python_ai-0.0.1.dev2/src/polaris_ai/a2a/server.py +92 -0
  68. polaris_python_ai-0.0.1.dev2/src/polaris_ai/agentscope/__init__.py +76 -0
  69. polaris_python_ai-0.0.1.dev2/src/polaris_ai/agentscope/a2a.py +276 -0
  70. polaris_python_ai-0.0.1.dev2/src/polaris_ai/agentscope/cards.py +106 -0
  71. polaris_python_ai-0.0.1.dev2/src/polaris_ai/agentscope/config.py +221 -0
  72. polaris_python_ai-0.0.1.dev2/src/polaris_ai/agentscope/context.py +195 -0
  73. polaris_python_ai-0.0.1.dev2/src/polaris_ai/agentscope/mcp.py +598 -0
  74. polaris_python_ai-0.0.1.dev2/src/polaris_ai/agentscope/middleware.py +103 -0
  75. polaris_python_ai-0.0.1.dev2/src/polaris_ai/agentscope/runtime.py +291 -0
  76. polaris_python_ai-0.0.1.dev2/src/polaris_ai/agentscope/skill.py +464 -0
  77. polaris_python_ai-0.0.1.dev2/src/polaris_ai/langgraph/__init__.py +50 -0
  78. polaris_python_ai-0.0.1.dev2/src/polaris_ai/langgraph/config.py +160 -0
  79. polaris_python_ai-0.0.1.dev2/src/polaris_ai/langgraph/context.py +152 -0
  80. polaris_python_ai-0.0.1.dev2/src/polaris_ai/langgraph/registry.py +258 -0
  81. polaris_python_ai-0.0.1.dev2/src/polaris_ai/langgraph/remote.py +315 -0
  82. polaris_python_ai-0.0.1.dev2/src/polaris_ai/langgraph/resolver.py +226 -0
  83. polaris_python_ai-0.0.1.dev2/src/polaris_ai/log.py +408 -0
  84. polaris_python_ai-0.0.1.dev2/src/polaris_ai/metrics.py +154 -0
  85. polaris_python_ai-0.0.1.dev2/src/polaris_python_ai.egg-info/PKG-INFO +351 -0
  86. polaris_python_ai-0.0.1.dev2/src/polaris_python_ai.egg-info/SOURCES.txt +105 -0
  87. polaris_python_ai-0.0.1.dev2/src/polaris_python_ai.egg-info/dependency_links.txt +1 -0
  88. polaris_python_ai-0.0.1.dev2/src/polaris_python_ai.egg-info/requires.txt +22 -0
  89. polaris_python_ai-0.0.1.dev2/src/polaris_python_ai.egg-info/top_level.txt +1 -0
  90. polaris_python_ai-0.0.1.dev2/tests/__init__.py +16 -0
  91. polaris_python_ai-0.0.1.dev2/tests/_support.py +121 -0
  92. polaris_python_ai-0.0.1.dev2/tests/test_a2a.py +362 -0
  93. polaris_python_ai-0.0.1.dev2/tests/test_a2a_agent.py +257 -0
  94. polaris_python_ai-0.0.1.dev2/tests/test_a2a_transport.py +56 -0
  95. polaris_python_ai-0.0.1.dev2/tests/test_agent_cards.py +65 -0
  96. polaris_python_ai-0.0.1.dev2/tests/test_agent_config.py +115 -0
  97. polaris_python_ai-0.0.1.dev2/tests/test_agent_context.py +113 -0
  98. polaris_python_ai-0.0.1.dev2/tests/test_agent_runtime.py +281 -0
  99. polaris_python_ai-0.0.1.dev2/tests/test_dynamic_skill_middleware.py +148 -0
  100. polaris_python_ai-0.0.1.dev2/tests/test_example_scripts.py +271 -0
  101. polaris_python_ai-0.0.1.dev2/tests/test_langgraph.py +342 -0
  102. polaris_python_ai-0.0.1.dev2/tests/test_log.py +266 -0
  103. polaris_python_ai-0.0.1.dev2/tests/test_mcp.py +550 -0
  104. polaris_python_ai-0.0.1.dev2/tests/test_metrics.py +80 -0
  105. polaris_python_ai-0.0.1.dev2/tests/test_skill.py +345 -0
  106. polaris_python_ai-0.0.1.dev2/tests/test_skill_agent.py +53 -0
  107. 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,4 @@
1
+ include LICENSE README.md README-zh.md
2
+ recursive-include docs *.md
3
+ recursive-include examples *.py *.yaml *.md
4
+ recursive-include tests *.py
@@ -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 写操作暂不在当前范围。