forked from themanojdesai/python-a2a
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathagent.py
More file actions
158 lines (132 loc) · 5.88 KB
/
Copy pathagent.py
File metadata and controls
158 lines (132 loc) · 5.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
"""
Agent-related models for the A2A protocol.
"""
import uuid
from dataclasses import dataclass, field
from typing import Dict, List, Optional, Any
from .base import BaseModel
@dataclass
class AgentSkill(BaseModel):
"""Represents a skill in an A2A agent card"""
name: str
description: str
id: str = field(default_factory=lambda: str(uuid.uuid4()))
tags: List[str] = field(default_factory=list)
examples: List[str] = field(default_factory=list)
input_modes: List[str] = field(default_factory=lambda: ["text/plain"])
output_modes: List[str] = field(default_factory=lambda: ["text/plain"])
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary for serialization"""
result = {
"id": self.id,
"name": self.name,
"description": self.description,
"tags": self.tags
}
if self.examples:
result["examples"] = self.examples
if self.input_modes:
result["inputModes"] = self.input_modes
if self.output_modes:
result["outputModes"] = self.output_modes
return result
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> 'AgentSkill':
"""Create an AgentSkill from a dictionary"""
return cls(
id=data.get("id", str(uuid.uuid4())),
name=data.get("name", ""),
description=data.get("description", ""),
tags=data.get("tags", []),
examples=data.get("examples", []),
input_modes=data.get("inputModes", ["text/plain"]),
output_modes=data.get("outputModes", ["text/plain"])
)
@dataclass
class AgentCard(BaseModel):
"""Represents an A2A agent card for discovery"""
name: str
description: str
url: str
version: str = "1.0.0"
protocol_version: str = "0.3.0" # A2A protocol version
preferred_transport: str = "JSONRPC" # A2A specification default
authentication: Optional[str] = None
capabilities: Dict[str, Any] = field(default_factory=lambda: {
"streaming": False,
"pushNotifications": False,
"stateTransitionHistory": False
})
default_input_modes: List[str] = field(default_factory=lambda: ["text/plain"])
default_output_modes: List[str] = field(default_factory=lambda: ["text/plain"])
skills: List[AgentSkill] = field(default_factory=list)
provider: Optional[str] = None
documentation_url: Optional[str] = None
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary for serialization"""
result = {
"name": self.name,
"description": self.description,
"url": self.url,
"version": self.version,
"protocolVersion": self.protocol_version,
"preferredTransport": self.preferred_transport,
"capabilities": self.capabilities,
"defaultInputModes": self.default_input_modes,
"defaultOutputModes": self.default_output_modes,
"skills": [skill.to_dict() for skill in self.skills]
}
if self.authentication:
result["authentication"] = self.authentication
if self.provider:
result["provider"] = self.provider
if self.documentation_url:
result["documentationUrl"] = self.documentation_url
return result
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> 'AgentCard':
"""Create an AgentCard from a dictionary"""
skills = [
AgentSkill.from_dict(skill)
for skill in data.get("skills", [])
]
return cls(
name=data.get("name", ""),
description=data.get("description", ""),
url=data.get("url", ""),
version=data.get("version", "1.0.0"),
protocol_version=data.get("protocolVersion", "0.3.0"),
preferred_transport=data.get("preferredTransport", "JSONRPC"),
authentication=data.get("authentication"),
capabilities=data.get("capabilities", {
"streaming": False,
"pushNotifications": False,
"stateTransitionHistory": False
}),
default_input_modes=data.get("defaultInputModes", ["text/plain"]),
default_output_modes=data.get("defaultOutputModes", ["text/plain"]),
skills=skills,
provider=data.get("provider"),
documentation_url=data.get("documentationUrl")
)
def validate_a2a_protocol(self) -> List[str]:
"""
Validate agent card against A2A protocol specification.
Returns:
List of protocol violations (empty if compliant)
"""
violations = []
# A2A specification required fields
required_fields = ['name', 'description', 'url', 'version', 'protocolVersion', 'skills', 'capabilities', 'defaultInputModes', 'defaultOutputModes']
for field in required_fields:
field_name = field.replace('protocolVersion', 'protocol_version').replace('defaultInputModes', 'default_input_modes').replace('defaultOutputModes', 'default_output_modes')
field_value = getattr(self, field_name, None)
if field_value is None:
violations.append(f"A2A protocol requires field: {field}")
elif field in ['name', 'description', 'url', 'version', 'protocolVersion'] and not field_value:
violations.append(f"A2A protocol requires non-empty field: {field}")
# For lists (skills, defaultInputModes, defaultOutputModes), empty lists are valid
# Check capabilities is a dictionary
if not isinstance(self.capabilities, dict):
violations.append("A2A protocol requires capabilities to be an object")
return violations