dragon-py-a2a 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.
- dragon_py_a2a-0.1.0/PKG-INFO +14 -0
- dragon_py_a2a-0.1.0/README.md +4 -0
- dragon_py_a2a-0.1.0/pyproject.toml +15 -0
- dragon_py_a2a-0.1.0/setup.cfg +4 -0
- dragon_py_a2a-0.1.0/src/dragon_py_a2a/__init__.py +9 -0
- dragon_py_a2a-0.1.0/src/dragon_py_a2a/client.py +79 -0
- dragon_py_a2a-0.1.0/src/dragon_py_a2a.egg-info/PKG-INFO +14 -0
- dragon_py_a2a-0.1.0/src/dragon_py_a2a.egg-info/SOURCES.txt +10 -0
- dragon_py_a2a-0.1.0/src/dragon_py_a2a.egg-info/dependency_links.txt +1 -0
- dragon_py_a2a-0.1.0/src/dragon_py_a2a.egg-info/requires.txt +2 -0
- dragon_py_a2a-0.1.0/src/dragon_py_a2a.egg-info/top_level.txt +1 -0
- dragon_py_a2a-0.1.0/tests/test_card.py +57 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dragon-py-a2a
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: DRAGON A2A 协议客户端(Python):官方 v1.0 卡面 + ARA 投影 + JSON-RPC 透传
|
|
5
|
+
License: Apache-2.0
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: pydantic>=1.10
|
|
9
|
+
Requires-Dist: dragon-py-core
|
|
10
|
+
|
|
11
|
+
# dragon-py-a2a
|
|
12
|
+
|
|
13
|
+
DRAGON 领域框架 Python SDK 组件(a2a 面)。用法与多语言一致性门禁见
|
|
14
|
+
[python/README.md](../README.md);框架主仓:DRAGON 领域框架(Apache-2.0)。
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "dragon-py-a2a"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "DRAGON A2A 协议客户端(Python):官方 v1.0 卡面 + ARA 投影 + JSON-RPC 透传"
|
|
9
|
+
requires-python = ">=3.10"
|
|
10
|
+
license = {text = "Apache-2.0"}
|
|
11
|
+
readme = "README.md"
|
|
12
|
+
dependencies = ["pydantic>=1.10", "dragon-py-core"]
|
|
13
|
+
|
|
14
|
+
[tool.setuptools.packages.find]
|
|
15
|
+
where = ["src"]
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""dragon_py_a2a — A2A 协议客户端(官方 v1.0 卡面 + JSON-RPC 透传)。
|
|
2
|
+
|
|
3
|
+
S-5 卡面发现(`/.well-known/agent-card.json`)的 Python 消费面;`skills[].tags`
|
|
4
|
+
承载 ARA 投影(GADR-019 模式)。JSON-RPC 方法名以对端实现为准——本客户端提供透传,
|
|
5
|
+
不发明方法语义(诚实边界)。
|
|
6
|
+
"""
|
|
7
|
+
from dragon_py_a2a.client import A2AClient, AgentCard, AgentSkill, fetch_card
|
|
8
|
+
|
|
9
|
+
__all__ = ["A2AClient", "AgentCard", "AgentSkill", "fetch_card"]
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""A2A 客户端(stdlib urllib——零第三方传输依赖)。"""
|
|
2
|
+
import json
|
|
3
|
+
import urllib.request
|
|
4
|
+
from typing import Any, Dict, List, Optional
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel, Field
|
|
7
|
+
|
|
8
|
+
AGENT_CARD_PATH = "/.well-known/agent-card.json"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AgentSkill(BaseModel):
|
|
12
|
+
id: str
|
|
13
|
+
name: Optional[str] = None
|
|
14
|
+
description: Optional[str] = None
|
|
15
|
+
tags: List[str] = Field(default_factory=list)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class AgentCard(BaseModel):
|
|
19
|
+
name: str
|
|
20
|
+
description: Optional[str] = None
|
|
21
|
+
version: Optional[str] = None
|
|
22
|
+
skills: List[AgentSkill] = Field(default_factory=list)
|
|
23
|
+
|
|
24
|
+
def ara_projection(self) -> Dict[str, Any]:
|
|
25
|
+
"""S-5/S-10 投影:tags 语法 → 结构化视图(缺失= None,不造默认;confidence 非法→None 不落 0.0)。"""
|
|
26
|
+
role = scope = autonomy = contract_version = None
|
|
27
|
+
confidence = None
|
|
28
|
+
categories: List[str] = []
|
|
29
|
+
for skill in self.skills:
|
|
30
|
+
for tag in skill.tags:
|
|
31
|
+
if tag in {"L1", "L2", "L3", "L4", "L5"}:
|
|
32
|
+
role = tag
|
|
33
|
+
elif tag in {"IS", "IIS"}:
|
|
34
|
+
scope = tag
|
|
35
|
+
elif tag.startswith("autonomy:"):
|
|
36
|
+
autonomy = tag.split(":", 1)[1]
|
|
37
|
+
elif tag.startswith("confidence:"):
|
|
38
|
+
raw = tag.split(":", 1)[1]
|
|
39
|
+
try:
|
|
40
|
+
confidence = float(raw)
|
|
41
|
+
except ValueError:
|
|
42
|
+
confidence = None # 非法数值 → None(不落 0.0)
|
|
43
|
+
elif tag.startswith("x-dragon.contractVersion:"):
|
|
44
|
+
contract_version = tag.split(":", 1)[1]
|
|
45
|
+
else:
|
|
46
|
+
categories.append(tag)
|
|
47
|
+
return {
|
|
48
|
+
"role": role,
|
|
49
|
+
"scope": scope,
|
|
50
|
+
"categories": categories,
|
|
51
|
+
"autonomy": autonomy,
|
|
52
|
+
"confidence": confidence,
|
|
53
|
+
"contractVersion": contract_version,
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def fetch_card(base_url: str, timeout: float = 5.0) -> AgentCard:
|
|
58
|
+
"""拉取并解析官方 v1.0 卡面。"""
|
|
59
|
+
url = base_url.rstrip("/") + AGENT_CARD_PATH
|
|
60
|
+
with urllib.request.urlopen(url, timeout=timeout) as resp: # noqa: S310 (调用方传入可信端点)
|
|
61
|
+
return AgentCard(**json.loads(resp.read().decode("utf-8")))
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class A2AClient:
|
|
65
|
+
"""A2A JSON-RPC 透传客户端(方法名以对端实现为准)。"""
|
|
66
|
+
|
|
67
|
+
def __init__(self, endpoint: str, timeout: float = 30.0):
|
|
68
|
+
self.endpoint = endpoint.rstrip("/")
|
|
69
|
+
self.timeout = timeout
|
|
70
|
+
|
|
71
|
+
def call(self, method: str, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
72
|
+
payload = json.dumps(
|
|
73
|
+
{"jsonrpc": "2.0", "id": 1, "method": method, "params": params}
|
|
74
|
+
).encode("utf-8")
|
|
75
|
+
req = urllib.request.Request( # noqa: S310
|
|
76
|
+
self.endpoint, data=payload, headers={"content-type": "application/json"}
|
|
77
|
+
)
|
|
78
|
+
with urllib.request.urlopen(req, timeout=self.timeout) as resp: # noqa: S310
|
|
79
|
+
return json.loads(resp.read().decode("utf-8"))
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dragon-py-a2a
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: DRAGON A2A 协议客户端(Python):官方 v1.0 卡面 + ARA 投影 + JSON-RPC 透传
|
|
5
|
+
License: Apache-2.0
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: pydantic>=1.10
|
|
9
|
+
Requires-Dist: dragon-py-core
|
|
10
|
+
|
|
11
|
+
# dragon-py-a2a
|
|
12
|
+
|
|
13
|
+
DRAGON 领域框架 Python SDK 组件(a2a 面)。用法与多语言一致性门禁见
|
|
14
|
+
[python/README.md](../README.md);框架主仓:DRAGON 领域框架(Apache-2.0)。
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/dragon_py_a2a/__init__.py
|
|
4
|
+
src/dragon_py_a2a/client.py
|
|
5
|
+
src/dragon_py_a2a.egg-info/PKG-INFO
|
|
6
|
+
src/dragon_py_a2a.egg-info/SOURCES.txt
|
|
7
|
+
src/dragon_py_a2a.egg-info/dependency_links.txt
|
|
8
|
+
src/dragon_py_a2a.egg-info/requires.txt
|
|
9
|
+
src/dragon_py_a2a.egg-info/top_level.txt
|
|
10
|
+
tests/test_card.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
dragon_py_a2a
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""A2A 客户端测试:真卡面形状(取自 2026-09-22 hello-domain 实测记录)+ ARA 投影语义。"""
|
|
2
|
+
from dragon_py_a2a import AgentCard, AgentSkill
|
|
3
|
+
|
|
4
|
+
# 真实卡面(hello 域实测输出节选——shapes 与官方 v1.0 一致)
|
|
5
|
+
RAW_CARD = {
|
|
6
|
+
"name": "hello 示例服务",
|
|
7
|
+
"description": "hello 域示例 IS(脚手架生成——契约正本)",
|
|
8
|
+
"version": "1.0.0",
|
|
9
|
+
"supportedInterfaces": [
|
|
10
|
+
{
|
|
11
|
+
"protocolBinding": "JSONRPC",
|
|
12
|
+
"url": "http://127.0.0.1:8907",
|
|
13
|
+
"tenant": "",
|
|
14
|
+
"protocolVersion": "1.0",
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"capabilities": {
|
|
18
|
+
"streaming": True,
|
|
19
|
+
"pushNotifications": True,
|
|
20
|
+
"extendedAgentCard": False,
|
|
21
|
+
"extensions": [],
|
|
22
|
+
},
|
|
23
|
+
"skills": [
|
|
24
|
+
{
|
|
25
|
+
"id": "hello.hello",
|
|
26
|
+
"name": "hello 示例服务",
|
|
27
|
+
"tags": ["L4", "IS", "autonomy:L1", "confidence:0.8", "x-dragon.contractVersion:1.0.0"],
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_card_parses_official_v1_shape():
|
|
34
|
+
card = AgentCard(**RAW_CARD)
|
|
35
|
+
assert card.name == "hello 示例服务"
|
|
36
|
+
assert card.skills[0].id == "hello.hello"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_ara_projection_maps_tags_never_defaults():
|
|
40
|
+
proj = AgentCard(**RAW_CARD).ara_projection()
|
|
41
|
+
assert proj["role"] == "L4"
|
|
42
|
+
assert proj["scope"] == "IS"
|
|
43
|
+
assert proj["autonomy"] == "L1"
|
|
44
|
+
assert proj["confidence"] == 0.8
|
|
45
|
+
assert proj["contractVersion"] == "1.0.0"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_missing_tags_project_to_none_not_default():
|
|
49
|
+
card = AgentCard(name="bare", skills=[AgentSkill(id="x")])
|
|
50
|
+
proj = card.ara_projection()
|
|
51
|
+
assert proj["role"] is None and proj["scope"] is None
|
|
52
|
+
assert proj["autonomy"] is None and proj["contractVersion"] is None
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_invalid_confidence_degrades_to_none_never_zero():
|
|
56
|
+
card = AgentCard(name="bad", skills=[AgentSkill(id="x", tags=["confidence:NaN?"])])
|
|
57
|
+
assert card.ara_projection()["confidence"] is None
|