agenteng 0.0.1__py3-none-any.whl
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.
- agenteng/__init__.py +3 -0
- agenteng/__main__.py +3 -0
- agenteng/a2a.py +191 -0
- agenteng/auth.py +7 -0
- agenteng/calendar.py +55 -0
- agenteng/catalogue.py +15 -0
- agenteng/cli.py +566 -0
- agenteng/config.py +54 -0
- agenteng/contracts.py +7 -0
- agenteng/data/catalogue.json +1273 -0
- agenteng/data/install.sh +33 -0
- agenteng/data/tools.json +10737 -0
- agenteng/discovery.py +249 -0
- agenteng/engines.py +291 -0
- agenteng/mcp.py +77 -0
- agenteng/models.py +266 -0
- agenteng/participation.py +511 -0
- agenteng/remote.py +25 -0
- agenteng/server.py +247 -0
- agenteng/service.py +537 -0
- agenteng/tool_directory.py +292 -0
- agenteng/tool_import.py +185 -0
- agenteng/tool_pages.py +73 -0
- agenteng-0.0.1.dist-info/METADATA +209 -0
- agenteng-0.0.1.dist-info/RECORD +29 -0
- agenteng-0.0.1.dist-info/WHEEL +4 -0
- agenteng-0.0.1.dist-info/entry_points.txt +2 -0
- agenteng-0.0.1.dist-info/licenses/LICENSE +199 -0
- agenteng-0.0.1.dist-info/licenses/NOTICE +18 -0
agenteng/__init__.py
ADDED
agenteng/__main__.py
ADDED
agenteng/a2a.py
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"""Official A2A 1.0 SDK adapter; immediate responses with no retained conversations."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from uuid import uuid4
|
|
5
|
+
|
|
6
|
+
from a2a.server.agent_execution import AgentExecutor
|
|
7
|
+
from a2a.server.request_handlers import DefaultRequestHandler
|
|
8
|
+
from a2a.server.routes import (
|
|
9
|
+
add_a2a_routes_to_fastapi,
|
|
10
|
+
create_agent_card_routes,
|
|
11
|
+
create_jsonrpc_routes,
|
|
12
|
+
)
|
|
13
|
+
from a2a.server.tasks import InMemoryTaskStore
|
|
14
|
+
from a2a.types import (
|
|
15
|
+
AgentCapabilities,
|
|
16
|
+
AgentCard,
|
|
17
|
+
AgentInterface,
|
|
18
|
+
AgentProvider,
|
|
19
|
+
AgentSkill,
|
|
20
|
+
Message,
|
|
21
|
+
Part,
|
|
22
|
+
HTTPAuthSecurityScheme,
|
|
23
|
+
SecurityRequirement,
|
|
24
|
+
SecurityScheme,
|
|
25
|
+
StringList,
|
|
26
|
+
)
|
|
27
|
+
from a2a.utils.errors import InvalidParamsError, UnsupportedOperationError
|
|
28
|
+
from google.protobuf.json_format import MessageToDict, ParseDict
|
|
29
|
+
from google.protobuf.struct_pb2 import Value
|
|
30
|
+
from pydantic import ValidationError
|
|
31
|
+
|
|
32
|
+
from .auth import bearer
|
|
33
|
+
from .models import Request
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def agent_card(service):
|
|
37
|
+
private_skills = []
|
|
38
|
+
security_schemes = {}
|
|
39
|
+
if service.settings.enable_intake:
|
|
40
|
+
security_schemes["participant"] = SecurityScheme(
|
|
41
|
+
http_auth_security_scheme=HTTPAuthSecurityScheme(
|
|
42
|
+
scheme="bearer",
|
|
43
|
+
description="Organizer-issued, per-participant pilot credential. "
|
|
44
|
+
"Not the optional inference operator credential.",
|
|
45
|
+
)
|
|
46
|
+
)
|
|
47
|
+
private_skills.append(
|
|
48
|
+
AgentSkill(
|
|
49
|
+
id="agenteng-participation",
|
|
50
|
+
name="Private Agent Engineering HQ participation",
|
|
51
|
+
description="Prepare an exact private preview, explicitly confirm submission, read your receipt or withdraw. "
|
|
52
|
+
"Requires participant access. No review, response, acceptance or event is guaranteed. "
|
|
53
|
+
"Submission content is never published automatically.",
|
|
54
|
+
tags=["proposals", "London", "San Francisco", "private-intake"],
|
|
55
|
+
security_requirements=[
|
|
56
|
+
SecurityRequirement(schemes={"participant": StringList(list=[])})
|
|
57
|
+
],
|
|
58
|
+
)
|
|
59
|
+
)
|
|
60
|
+
return AgentCard(
|
|
61
|
+
name="Agent Engineering HQ — AgentEng Conference",
|
|
62
|
+
description="AgentEng, the Agent Engineering Conference, and Agent Engineering HQ technical events in London and San Francisco. Discover published dates, speakers, agendas, registration links, proposal drafting and a tool directory across twelve agent-engineering disciplines. Anonymous requests use catalogue lookup with no model calls. London 2026 has an invited programme and no public CFP.",
|
|
63
|
+
version="1.0",
|
|
64
|
+
provider=AgentProvider(
|
|
65
|
+
organization="Agent Engineering HQ", url="https://agentengineering.world"
|
|
66
|
+
),
|
|
67
|
+
documentation_url=service.settings.public_url + "/docs",
|
|
68
|
+
supported_interfaces=[
|
|
69
|
+
AgentInterface(
|
|
70
|
+
url=service.settings.public_url + "/",
|
|
71
|
+
protocol_binding="JSONRPC",
|
|
72
|
+
protocol_version="1.0",
|
|
73
|
+
)
|
|
74
|
+
],
|
|
75
|
+
capabilities=AgentCapabilities(
|
|
76
|
+
streaming=False, push_notifications=False, extended_agent_card=False
|
|
77
|
+
),
|
|
78
|
+
default_input_modes=["application/json", "text/plain"],
|
|
79
|
+
default_output_modes=["application/json", "text/plain"],
|
|
80
|
+
security_schemes=security_schemes,
|
|
81
|
+
skills=[
|
|
82
|
+
AgentSkill(
|
|
83
|
+
id="agenteng",
|
|
84
|
+
name="AgentEng London and San Francisco events",
|
|
85
|
+
description="Use a typed AgentEng request or a public-event search question. Returns evidence, date precision and snapshot freshness.",
|
|
86
|
+
tags=[
|
|
87
|
+
"events",
|
|
88
|
+
"conferences",
|
|
89
|
+
"agent-engineering",
|
|
90
|
+
"AgentEng",
|
|
91
|
+
"Agent Engineering HQ",
|
|
92
|
+
"Agent Engineering Conference",
|
|
93
|
+
"London",
|
|
94
|
+
"San Francisco",
|
|
95
|
+
"coding-agents",
|
|
96
|
+
"mcp",
|
|
97
|
+
"a2a",
|
|
98
|
+
],
|
|
99
|
+
examples=[
|
|
100
|
+
'{"operation":"events","upcoming":true}',
|
|
101
|
+
'{"operation":"discover"}',
|
|
102
|
+
'{"operation":"tickets","event_id":"agenteng-london-2026"}',
|
|
103
|
+
'{"operation":"proposal_draft","draft":{"city":"San Francisco","kind":"event_idea","title":"Agent evaluation workshop"}}',
|
|
104
|
+
],
|
|
105
|
+
),
|
|
106
|
+
AgentSkill(
|
|
107
|
+
id="agenteng-tools",
|
|
108
|
+
name="Agent-engineering tool directory",
|
|
109
|
+
description="Browse public tool names and links across twelve disciplines using "
|
|
110
|
+
"disciplines, tools and tool. Filter by discipline, kind, category or query; paginate "
|
|
111
|
+
"with limit/offset and next_offset. Offline, model-free listings with source provenance; "
|
|
112
|
+
"no popularity rankings, installations or vendor integrations are implied.",
|
|
113
|
+
tags=[
|
|
114
|
+
"tools",
|
|
115
|
+
"agent-engineering",
|
|
116
|
+
"disciplines",
|
|
117
|
+
"memory",
|
|
118
|
+
"inference",
|
|
119
|
+
"protocol",
|
|
120
|
+
],
|
|
121
|
+
examples=[
|
|
122
|
+
'{"operation":"disciplines"}',
|
|
123
|
+
'{"operation":"tools","discipline":"memory"}',
|
|
124
|
+
'{"operation":"tool","tool_id":"langgraph"}',
|
|
125
|
+
],
|
|
126
|
+
),
|
|
127
|
+
]
|
|
128
|
+
+ private_skills,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class Executor(AgentExecutor):
|
|
133
|
+
def __init__(self, service):
|
|
134
|
+
self.service = service
|
|
135
|
+
|
|
136
|
+
async def execute(self, context, event_queue):
|
|
137
|
+
message = context.message
|
|
138
|
+
if message is None:
|
|
139
|
+
raise InvalidParamsError("A user message is required.")
|
|
140
|
+
if any(p.HasField("raw") or p.HasField("url") for p in message.parts):
|
|
141
|
+
raise InvalidParamsError("Only JSON request data or text input is supported.")
|
|
142
|
+
data_parts = [p for p in message.parts if p.HasField("data")]
|
|
143
|
+
try:
|
|
144
|
+
if data_parts:
|
|
145
|
+
if len(data_parts) != 1 or any(p.HasField("text") for p in message.parts):
|
|
146
|
+
raise ValueError("Send one JSON request part.")
|
|
147
|
+
request = Request.model_validate(MessageToDict(data_parts[0].data))
|
|
148
|
+
else:
|
|
149
|
+
text = context.get_user_input().strip()
|
|
150
|
+
request = (
|
|
151
|
+
Request.model_validate_json(text)
|
|
152
|
+
if text.startswith("{")
|
|
153
|
+
else Request(operation="ask", query=text)
|
|
154
|
+
)
|
|
155
|
+
except (ValueError, ValidationError) as exc:
|
|
156
|
+
raise InvalidParamsError(
|
|
157
|
+
"Invalid AgentEng request; use the documented request schema."
|
|
158
|
+
) from exc
|
|
159
|
+
call_context = context.call_context
|
|
160
|
+
headers = call_context.state.get("headers", {}) if call_context else {}
|
|
161
|
+
result = await self.service.execute(request, token=bearer(headers))
|
|
162
|
+
await event_queue.enqueue_event(
|
|
163
|
+
Message(
|
|
164
|
+
message_id=str(uuid4()),
|
|
165
|
+
context_id=context.context_id,
|
|
166
|
+
role=2,
|
|
167
|
+
parts=[
|
|
168
|
+
Part(text=result.answer),
|
|
169
|
+
Part(
|
|
170
|
+
data=ParseDict(json.loads(result.model_dump_json()), Value()),
|
|
171
|
+
media_type="application/json",
|
|
172
|
+
),
|
|
173
|
+
],
|
|
174
|
+
)
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
async def cancel(self, context, event_queue):
|
|
178
|
+
raise UnsupportedOperationError(
|
|
179
|
+
"This agent returns immediate messages; persistent tasks are not supported."
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def add_a2a(app, service):
|
|
184
|
+
card = agent_card(service)
|
|
185
|
+
handler = DefaultRequestHandler(Executor(service), InMemoryTaskStore(), card)
|
|
186
|
+
add_a2a_routes_to_fastapi(
|
|
187
|
+
app,
|
|
188
|
+
agent_card_routes=create_agent_card_routes(card),
|
|
189
|
+
jsonrpc_routes=create_jsonrpc_routes(handler, rpc_url="/"),
|
|
190
|
+
)
|
|
191
|
+
return card
|
agenteng/auth.py
ADDED
agenteng/calendar.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Standards-compliant UTC calendar export; never invent missing session times."""
|
|
2
|
+
|
|
3
|
+
from datetime import UTC, datetime
|
|
4
|
+
|
|
5
|
+
from .models import Event, Session
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def escape(value: str) -> str:
|
|
9
|
+
return value.replace("\\", "\\\\").replace("\n", "\\n").replace(";", "\\;").replace(",", "\\,")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def fold(line: str) -> str:
|
|
13
|
+
chunks, current = [], ""
|
|
14
|
+
for char in line:
|
|
15
|
+
if len((current + char).encode()) > 75:
|
|
16
|
+
chunks.append(current)
|
|
17
|
+
current = " "
|
|
18
|
+
current += char
|
|
19
|
+
return "\r\n".join([*chunks, current])
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def calendar(event: Event, sessions: list[Session], now: datetime) -> str:
|
|
23
|
+
def stamp(value):
|
|
24
|
+
return value.astimezone(UTC).strftime("%Y%m%dT%H%M%SZ")
|
|
25
|
+
|
|
26
|
+
lines = [
|
|
27
|
+
"BEGIN:VCALENDAR",
|
|
28
|
+
"VERSION:2.0",
|
|
29
|
+
"PRODID:-//AgentEng HQ//Events//EN",
|
|
30
|
+
"CALSCALE:GREGORIAN",
|
|
31
|
+
"METHOD:PUBLISH",
|
|
32
|
+
]
|
|
33
|
+
# An event without an exact day/time cannot be exported as a timed event.
|
|
34
|
+
rows = (
|
|
35
|
+
[(event.id, event.title, event.start, event.end)]
|
|
36
|
+
if not sessions
|
|
37
|
+
else [(s.id, s.title, s.start, s.end) for s in sessions]
|
|
38
|
+
)
|
|
39
|
+
for uid, title, start, end in rows:
|
|
40
|
+
if start is None or end is None:
|
|
41
|
+
continue
|
|
42
|
+
lines.extend(
|
|
43
|
+
[
|
|
44
|
+
"BEGIN:VEVENT",
|
|
45
|
+
f"UID:{uid}@agentengineering.world",
|
|
46
|
+
"DTSTAMP:" + stamp(now),
|
|
47
|
+
"DTSTART:" + stamp(start),
|
|
48
|
+
"DTEND:" + stamp(end),
|
|
49
|
+
"SUMMARY:" + escape(title),
|
|
50
|
+
"LOCATION:" + escape(event.venue),
|
|
51
|
+
"END:VEVENT",
|
|
52
|
+
]
|
|
53
|
+
)
|
|
54
|
+
lines.append("END:VCALENDAR")
|
|
55
|
+
return "\r\n".join(map(fold, lines)) + "\r\n"
|
agenteng/catalogue.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Load and validate the local immutable public snapshot."""
|
|
2
|
+
|
|
3
|
+
from importlib.resources import files
|
|
4
|
+
|
|
5
|
+
from .models import Catalogue
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def load_catalogue(path: str | None = None) -> Catalogue:
|
|
9
|
+
if path:
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
payload = Path(path).read_text()
|
|
13
|
+
else:
|
|
14
|
+
payload = files("agenteng").joinpath("data/catalogue.json").read_text()
|
|
15
|
+
return Catalogue.model_validate_json(payload)
|