supervaizer 0.9.6__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.
Files changed (50) hide show
  1. supervaizer/__init__.py +88 -0
  2. supervaizer/__version__.py +10 -0
  3. supervaizer/account.py +304 -0
  4. supervaizer/account_service.py +87 -0
  5. supervaizer/admin/routes.py +1254 -0
  6. supervaizer/admin/templates/agent_detail.html +145 -0
  7. supervaizer/admin/templates/agents.html +175 -0
  8. supervaizer/admin/templates/agents_grid.html +80 -0
  9. supervaizer/admin/templates/base.html +233 -0
  10. supervaizer/admin/templates/case_detail.html +230 -0
  11. supervaizer/admin/templates/cases_list.html +182 -0
  12. supervaizer/admin/templates/cases_table.html +134 -0
  13. supervaizer/admin/templates/console.html +389 -0
  14. supervaizer/admin/templates/dashboard.html +153 -0
  15. supervaizer/admin/templates/job_detail.html +192 -0
  16. supervaizer/admin/templates/jobs_list.html +180 -0
  17. supervaizer/admin/templates/jobs_table.html +122 -0
  18. supervaizer/admin/templates/navigation.html +153 -0
  19. supervaizer/admin/templates/recent_activity.html +81 -0
  20. supervaizer/admin/templates/server.html +105 -0
  21. supervaizer/admin/templates/server_status_cards.html +121 -0
  22. supervaizer/agent.py +816 -0
  23. supervaizer/case.py +400 -0
  24. supervaizer/cli.py +135 -0
  25. supervaizer/common.py +283 -0
  26. supervaizer/event.py +181 -0
  27. supervaizer/examples/controller-template.py +195 -0
  28. supervaizer/instructions.py +145 -0
  29. supervaizer/job.py +379 -0
  30. supervaizer/job_service.py +155 -0
  31. supervaizer/lifecycle.py +417 -0
  32. supervaizer/parameter.py +173 -0
  33. supervaizer/protocol/__init__.py +11 -0
  34. supervaizer/protocol/a2a/__init__.py +21 -0
  35. supervaizer/protocol/a2a/model.py +227 -0
  36. supervaizer/protocol/a2a/routes.py +99 -0
  37. supervaizer/protocol/acp/__init__.py +21 -0
  38. supervaizer/protocol/acp/model.py +198 -0
  39. supervaizer/protocol/acp/routes.py +74 -0
  40. supervaizer/py.typed +1 -0
  41. supervaizer/routes.py +667 -0
  42. supervaizer/server.py +554 -0
  43. supervaizer/server_utils.py +54 -0
  44. supervaizer/storage.py +436 -0
  45. supervaizer/telemetry.py +81 -0
  46. supervaizer-0.9.6.dist-info/METADATA +245 -0
  47. supervaizer-0.9.6.dist-info/RECORD +50 -0
  48. supervaizer-0.9.6.dist-info/WHEEL +4 -0
  49. supervaizer-0.9.6.dist-info/entry_points.txt +2 -0
  50. supervaizer-0.9.6.dist-info/licenses/LICENSE.md +346 -0
@@ -0,0 +1,227 @@
1
+ # Copyright (c) 2024-2025 Alain Prasquier - Supervaize.com. All rights reserved.
2
+ #
3
+ # This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
4
+ # If a copy of the MPL was not distributed with this file, you can obtain one at
5
+ # https://mozilla.org/MPL/2.0/.
6
+
7
+ from datetime import datetime
8
+ from typing import Any, Dict, List
9
+
10
+ from supervaizer.agent import Agent
11
+ from supervaizer.job import EntityStatus, Jobs
12
+
13
+
14
+ def create_agent_card(agent: Agent, base_url: str) -> Dict[str, Any]:
15
+ """
16
+ Create an A2A agent card for the given agent.
17
+
18
+ This follows the A2A protocol as defined in:
19
+ https://github.com/google/A2A/blob/main/specification/json/a2a.json
20
+
21
+ Args:
22
+ agent: The Agent instance
23
+ base_url: The base URL of the server
24
+
25
+ Returns:
26
+ A dictionary representing the agent card in A2A format
27
+ """
28
+ # Construct the agent URL
29
+ agent_url = f"{base_url}{agent.path}"
30
+
31
+ # Build API endpoints object with OpenAPI integration
32
+ api_endpoints = [
33
+ {
34
+ "type": "json",
35
+ "url": agent_url,
36
+ "name": "Supervaize API - A2A protocol support",
37
+ "description": f"RESTful API for {agent.name} agent",
38
+ "openapi_url": f"{base_url}/openapi.json",
39
+ "docs_url": f"{base_url}/docs",
40
+ "examples": [
41
+ {
42
+ "name": "Get agent info",
43
+ "description": "Retrieve information about the agent",
44
+ "request": {"method": "GET", "url": agent_url},
45
+ },
46
+ {
47
+ "name": "Start a job",
48
+ "description": "Start a new job with this agent",
49
+ "request": {"method": "POST", "url": f"{agent_url}/jobs"},
50
+ },
51
+ ],
52
+ }
53
+ ]
54
+
55
+ # Build the tools object based on agent methods
56
+ tools = []
57
+
58
+ # Add basic job tools
59
+ tools.append(
60
+ {
61
+ "name": "job_start",
62
+ "description": (
63
+ agent.methods.job_start.description if agent.methods else None
64
+ )
65
+ or f"Start a job with {agent.name}",
66
+ "input_schema": {
67
+ "type": "object",
68
+ "properties": {
69
+ "job_fields": {"type": "object"},
70
+ "job_context": {"type": "object"},
71
+ },
72
+ },
73
+ }
74
+ )
75
+
76
+ tools.append(
77
+ {
78
+ "name": "job_status",
79
+ "description": "Check the status of a job",
80
+ "input_schema": {
81
+ "type": "object",
82
+ "properties": {"job_id": {"type": "string"}},
83
+ },
84
+ }
85
+ )
86
+
87
+ # Add custom tools if available
88
+ if agent.methods and agent.methods.custom:
89
+ for name, method in agent.methods.custom.items():
90
+ tools.append(
91
+ {
92
+ "name": name,
93
+ "description": method.description
94
+ or f"Execute {name} custom method",
95
+ "input_schema": {
96
+ "type": "object",
97
+ "properties": {
98
+ "method_name": {"type": "string", "const": name},
99
+ "params": {"type": "object"},
100
+ },
101
+ },
102
+ }
103
+ )
104
+
105
+ # Build authentication object
106
+ authentication = {
107
+ "type": "none",
108
+ "description": "Authentication is handled at the Supervaize server level",
109
+ }
110
+
111
+ # Version information
112
+ version_info = {
113
+ "current": agent.version,
114
+ "latest": agent.version,
115
+ "changelog_url": f"{base_url}/changelog/{agent.slug}",
116
+ }
117
+
118
+ # Create the main agent card
119
+ agent_card = {
120
+ "name": agent.name,
121
+ "description": agent.description,
122
+ "developer": {
123
+ "name": agent.developer or agent.author or "Supervaize",
124
+ "url": "https://supervaize.com/",
125
+ "email": "info@supervaize.com",
126
+ },
127
+ "version": agent.version,
128
+ "version_info": version_info,
129
+ "logo_url": f"{base_url}/static/agents/{agent.slug}_logo.png",
130
+ "human_url": f"{base_url}/agents/{agent.slug}",
131
+ "contact_information": {"general": {"email": "support@supervaize.com"}},
132
+ "api_endpoints": api_endpoints,
133
+ "tools": tools,
134
+ "authentication": authentication,
135
+ }
136
+
137
+ return agent_card
138
+
139
+
140
+ def create_agents_list(agents: List[Agent], base_url: str) -> Dict[str, Any]:
141
+ """
142
+ Create an A2A agents list for all available agents.
143
+
144
+ Args:
145
+ agents: List of Agent instances
146
+ base_url: The base URL of the server
147
+
148
+ Returns:
149
+ A dictionary representing the list of agent cards in A2A format
150
+ """
151
+ return {
152
+ "schema_version": "a2a_2023_v1",
153
+ "agents": [
154
+ {
155
+ "name": agent.name,
156
+ "description": agent.description,
157
+ "developer": agent.developer or agent.author or "Supervaize",
158
+ "version": agent.version,
159
+ "agent_card_url": f"{base_url}/.well-known/agents/v{agent.version}/{agent.slug}_agent.json",
160
+ }
161
+ for agent in agents
162
+ ],
163
+ }
164
+
165
+
166
+ def create_health_data(agents: List[Agent]) -> Dict[str, Any]:
167
+ """
168
+ Create health data for all agents according to A2A protocol.
169
+
170
+ Args:
171
+ agents: List of Agent instances
172
+
173
+ Returns:
174
+ A dictionary with health information for all agents
175
+ """
176
+ jobs_registry = Jobs()
177
+
178
+ agents_health = {}
179
+ for agent in agents:
180
+ # Get agent jobs
181
+ agent_jobs = jobs_registry.get_agent_jobs(agent.name)
182
+
183
+ # Calculate job statistics
184
+ total_jobs = len(agent_jobs)
185
+ completed_jobs = sum(
186
+ 1 for j in agent_jobs.values() if j.status == EntityStatus.COMPLETED
187
+ )
188
+ failed_jobs = sum(
189
+ 1 for j in agent_jobs.values() if j.status == EntityStatus.FAILED
190
+ )
191
+ in_progress_jobs = sum(
192
+ 1 for j in agent_jobs.values() if j.status == EntityStatus.IN_PROGRESS
193
+ )
194
+
195
+ # Set agent status based on health indicators
196
+ if total_jobs == 0:
197
+ status = "available"
198
+ elif failed_jobs > total_jobs / 2: # If more than half are failing
199
+ status = "degraded"
200
+ elif in_progress_jobs > 0:
201
+ status = "busy"
202
+ else:
203
+ status = "available"
204
+
205
+ agents_health[agent.id] = {
206
+ "agent_id": agent.id,
207
+ "agent_server_id": agent.server_agent_id,
208
+ "name": agent.name,
209
+ "status": status,
210
+ "version": agent.version,
211
+ "statistics": {
212
+ "total_jobs": total_jobs,
213
+ "completed_jobs": completed_jobs,
214
+ "failed_jobs": failed_jobs,
215
+ "in_progress_jobs": in_progress_jobs,
216
+ "success_rate": (completed_jobs / total_jobs * 100)
217
+ if total_jobs > 0
218
+ else 100,
219
+ },
220
+ }
221
+
222
+ return {
223
+ "schema_version": "a2a_2023_v1",
224
+ "status": "operational",
225
+ "timestamp": str(datetime.now()),
226
+ "agents": agents_health,
227
+ }
@@ -0,0 +1,99 @@
1
+ # Copyright (c) 2024-2025 Alain Prasquier - Supervaize.com. All rights reserved.
2
+ #
3
+ # This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
4
+ # If a copy of the MPL was not distributed with this file, you can obtain one at
5
+ # https://mozilla.org/MPL/2.0/.
6
+
7
+ from typing import TYPE_CHECKING, Any, Dict
8
+
9
+ from fastapi import APIRouter
10
+
11
+ from supervaizer.common import log
12
+ from supervaizer.protocol.a2a.model import (
13
+ create_agent_card,
14
+ create_agents_list,
15
+ create_health_data,
16
+ )
17
+ from supervaizer.routes import handle_route_errors
18
+
19
+ if TYPE_CHECKING:
20
+ from supervaizer.agent import Agent
21
+ from supervaizer.server import Server
22
+
23
+
24
+ def create_routes(server: "Server") -> APIRouter:
25
+ """Create A2A protocol routes for the server."""
26
+ router = APIRouter(prefix="/.well-known", tags=["Protocol A2A"])
27
+ base_url = server.public_url or ""
28
+
29
+ @router.get(
30
+ "/agents.json",
31
+ summary="A2A Agents Discovery",
32
+ description="Returns a list of all agents according to A2A protocol specification",
33
+ response_model=Dict[str, Any],
34
+ )
35
+ @handle_route_errors()
36
+ async def get_a2a_agents() -> Dict[str, Any]:
37
+ """Get a list of all available agents in A2A format."""
38
+ log.info("[A2A] GET /.well-known/agents.json [Agent discovery]")
39
+ return create_agents_list(server.agents, base_url)
40
+
41
+ # Health endpoint
42
+ @router.get(
43
+ "/health",
44
+ summary="A2A Health Status",
45
+ description="Returns health information about the server and agents",
46
+ response_model=Dict[str, Any],
47
+ )
48
+ @handle_route_errors()
49
+ async def get_health() -> Dict[str, Any]:
50
+ """Get health information for the server and all agents."""
51
+ log.debug("[A2A] GET /.well-known/health [Health status]")
52
+ return create_health_data(server.agents)
53
+
54
+ # Create explicit routes for each agent in the versioned format
55
+ for agent in server.agents:
56
+ # V1 endpoints (current version)
57
+ def create_agent_route_versioned(current_agent: "Agent") -> None:
58
+ route_path = (
59
+ f"/agents/v{current_agent.version}/{current_agent.slug}_agent.json"
60
+ )
61
+
62
+ @router.get(
63
+ route_path,
64
+ summary=f"A2A Agent Card for {current_agent.name} (v1)",
65
+ description=f"Returns agent card for {current_agent.name} according to A2A protocol specification",
66
+ response_model=Dict[str, Any],
67
+ )
68
+ @handle_route_errors()
69
+ async def get_agent_card() -> Dict[str, Any]:
70
+ """Get an agent card in A2A format."""
71
+ log.info(
72
+ f"[A2A] GET /.well-known/agents/v{current_agent.version}/"
73
+ f"{current_agent.slug}_agent.json [Agent card]"
74
+ )
75
+ return create_agent_card(current_agent, base_url)
76
+
77
+ # Create routes for backward compatibility to current versions
78
+ def create_agent_route_legacy(current_agent: "Agent") -> None:
79
+ route_path = f"/agents/{current_agent.slug}_agent.json"
80
+
81
+ @router.get(
82
+ route_path,
83
+ summary=f"A2A Agent Card for {current_agent.name} (Legacy)",
84
+ description=f"Legacy endpoint for {current_agent.name} agent card",
85
+ response_model=Dict[str, Any],
86
+ )
87
+ @handle_route_errors()
88
+ async def get_agent_card_legacy() -> Dict[str, Any]:
89
+ """Get an agent card in A2A format (legacy endpoint)."""
90
+ log.info(
91
+ f"[A2A] GET /.well-known/agents/{current_agent.slug}_agent.json [Legacy Agent card]"
92
+ )
93
+ return create_agent_card(current_agent, base_url)
94
+
95
+ # Call the closure function with the current agent
96
+ create_agent_route_versioned(agent)
97
+ create_agent_route_legacy(agent)
98
+
99
+ return router
@@ -0,0 +1,21 @@
1
+ # Copyright (c) 2024-2025 Alain Prasquier - Supervaize.com. All rights reserved.
2
+ #
3
+ # This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
4
+ # If a copy of the MPL was not distributed with this file, you can obtain one at
5
+ # https://mozilla.org/MPL/2.0/.
6
+
7
+ """ACP protocol implementation for SUPERVAIZER."""
8
+
9
+ from supervaizer.protocol.acp.model import (
10
+ create_agent_detail,
11
+ create_health_data,
12
+ list_agents,
13
+ )
14
+ from supervaizer.protocol.acp.routes import create_routes
15
+
16
+ __all__ = [
17
+ "create_agent_detail",
18
+ "list_agents",
19
+ "create_health_data",
20
+ "create_routes",
21
+ ]
@@ -0,0 +1,198 @@
1
+ # Copyright (c) 2024-2025 Alain Prasquier - Supervaize.com. All rights reserved.
2
+ #
3
+ # This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
4
+ # If a copy of the MPL was not distributed with this file, you can obtain one at
5
+ # https://mozilla.org/MPL/2.0/.
6
+
7
+ from datetime import datetime
8
+ from typing import Any, Dict, List
9
+
10
+ from supervaizer.agent import Agent
11
+ from supervaizer.job import EntityStatus, Jobs
12
+
13
+
14
+ def create_agent_detail(agent: Agent, base_url: str) -> Dict[str, Any]:
15
+ """
16
+ Create an ACP agent detail for the given agent.
17
+
18
+ This follows the ACP protocol as defined in:
19
+ https://docs.beeai.dev/acp/spec/concepts/discovery
20
+
21
+ Args:
22
+ agent: The Agent instance
23
+ base_url: The base URL of the server
24
+
25
+ Returns:
26
+ A dictionary representing the agent detail in ACP format
27
+ """
28
+ # Build the interfaces object
29
+ interfaces = {
30
+ "input": "chat",
31
+ "output": "chat",
32
+ "awaits": [{"name": "user_response", "request": {}, "response": {}}],
33
+ }
34
+
35
+ # Build the metadata object
36
+ metadata = {
37
+ "documentation": agent.description or f"Documentation for {agent.name} agent",
38
+ "license": "MPL-2.0",
39
+ "programmingLanguage": "Python",
40
+ "naturalLanguages": ["en"],
41
+ "framework": "SUPERVAIZER",
42
+ "useCases": [f"Agent services provided by {agent.name}"],
43
+ "examples": [
44
+ {
45
+ "prompt": f"Example interaction with {agent.name}",
46
+ "response": "Example response",
47
+ }
48
+ ],
49
+ "tags": [agent.slug] + (agent.tags or []),
50
+ "createdAt": datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ"),
51
+ "updatedAt": datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ"),
52
+ "author": {
53
+ "name": agent.author or agent.developer or "SUPERVAIZER",
54
+ "email": "info@supervaize.com",
55
+ "url": "https://supervaize.com/",
56
+ },
57
+ "contributors": [
58
+ {
59
+ "name": agent.maintainer
60
+ or agent.author
61
+ or agent.developer
62
+ or "SUPERVAIZER",
63
+ "email": "info@supervaize.com",
64
+ "url": "https://supervaize.com/",
65
+ }
66
+ ],
67
+ "links": [
68
+ {"type": "homepage", "url": f"{base_url}/agents/{agent.slug}"},
69
+ {"type": "documentation", "url": f"{base_url}/docs"},
70
+ {"type": "source-code", "url": "https://github.com/supervaize/supervaizer"},
71
+ ],
72
+ "dependencies": [{"type": "tool", "name": "supervaizer-core"}],
73
+ "recommendedModels": ["gpt-4", "claude-3-opus"],
74
+ }
75
+
76
+ # Get job statistics for status data
77
+ jobs_registry = Jobs()
78
+ agent_jobs = jobs_registry.get_agent_jobs(agent.name)
79
+
80
+ total_jobs = len(agent_jobs)
81
+ completed_jobs = sum(
82
+ 1 for j in agent_jobs.values() if j.status == EntityStatus.COMPLETED
83
+ )
84
+
85
+ # Calculate average runtime and success rate for status
86
+ avg_run_time = 0.0
87
+ if total_jobs > 0:
88
+ success_rate = (completed_jobs / total_jobs) * 100
89
+
90
+ # Calculate average runtime in seconds
91
+ runtimes = []
92
+ for job in agent_jobs.values():
93
+ if (
94
+ job.created_at
95
+ and job.finished_at
96
+ and job.status == EntityStatus.COMPLETED
97
+ ):
98
+ runtime = (job.finished_at - job.created_at).total_seconds()
99
+ runtimes.append(runtime)
100
+
101
+ if runtimes:
102
+ avg_run_time = sum(runtimes) / len(runtimes)
103
+ else:
104
+ success_rate = 100 # If no jobs, assume 100% success rate
105
+
106
+ # Build the status object
107
+ status = {
108
+ "avgRunTokens": 0, # Placeholder, actual token count if available
109
+ "avgRunTimeSeconds": avg_run_time,
110
+ "successRate": success_rate,
111
+ }
112
+
113
+ # Create the main agent detail
114
+ agent_detail = {
115
+ "name": agent.name,
116
+ "description": agent.description,
117
+ "interfaces": interfaces,
118
+ "metadata": metadata,
119
+ "status": status,
120
+ }
121
+
122
+ return agent_detail
123
+
124
+
125
+ def list_agents(agents: List[Agent], base_url: str) -> List[Dict[str, Any]]:
126
+ """
127
+ Create a list of ACP agent details for all available agents.
128
+
129
+ Args:
130
+ agents: List of Agent instances
131
+ base_url: The base URL of the server
132
+
133
+ Returns:
134
+ A list of dictionaries representing agent details in ACP format
135
+ """
136
+ return [create_agent_detail(agent, base_url) for agent in agents]
137
+
138
+
139
+ def create_health_data(agents: List[Agent]) -> Dict[str, Any]:
140
+ """
141
+ Create health data for all agents according to ACP protocol.
142
+
143
+ Args:
144
+ agents: List of Agent instances
145
+
146
+ Returns:
147
+ A dictionary with health information for all agents
148
+ """
149
+ jobs_registry = Jobs()
150
+
151
+ agents_health = {}
152
+ for agent in agents:
153
+ # Get agent jobs
154
+ agent_jobs = jobs_registry.get_agent_jobs(agent.name)
155
+
156
+ # Calculate job statistics
157
+ total_jobs = len(agent_jobs)
158
+ completed_jobs = sum(
159
+ 1 for j in agent_jobs.values() if j.status == EntityStatus.COMPLETED
160
+ )
161
+ failed_jobs = sum(
162
+ 1 for j in agent_jobs.values() if j.status == EntityStatus.FAILED
163
+ )
164
+ in_progress_jobs = sum(
165
+ 1 for j in agent_jobs.values() if j.status == EntityStatus.IN_PROGRESS
166
+ )
167
+
168
+ # Set agent status based on health indicators
169
+ if total_jobs == 0:
170
+ status = "available"
171
+ elif failed_jobs > total_jobs / 2: # If more than half are failing
172
+ status = "degraded"
173
+ elif in_progress_jobs > 0:
174
+ status = "busy"
175
+ else:
176
+ status = "available"
177
+
178
+ agents_health[agent.id] = {
179
+ "agent_id": agent.id,
180
+ "name": agent.name,
181
+ "status": status,
182
+ "version": agent.version,
183
+ "statistics": {
184
+ "total_jobs": total_jobs,
185
+ "completed_jobs": completed_jobs,
186
+ "failed_jobs": failed_jobs,
187
+ "in_progress_jobs": in_progress_jobs,
188
+ "success_rate": (completed_jobs / total_jobs * 100)
189
+ if total_jobs > 0
190
+ else 100,
191
+ },
192
+ }
193
+
194
+ return {
195
+ "status": "operational",
196
+ "timestamp": str(datetime.now()),
197
+ "agents": agents_health,
198
+ }
@@ -0,0 +1,74 @@
1
+ # Copyright (c) 2024-2025 Alain Prasquier - Supervaize.com. All rights reserved.
2
+ #
3
+ # This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
4
+ # If a copy of the MPL was not distributed with this file, you can obtain one at
5
+ # https://mozilla.org/MPL/2.0/.
6
+
7
+ from typing import TYPE_CHECKING, Any, Dict, List
8
+
9
+ from fastapi import APIRouter
10
+
11
+ from supervaizer.common import log
12
+ from supervaizer.protocol.acp.model import (
13
+ create_agent_detail,
14
+ create_health_data,
15
+ list_agents,
16
+ )
17
+ from supervaizer.routes import handle_route_errors
18
+
19
+ if TYPE_CHECKING:
20
+ from supervaizer.agent import Agent
21
+ from supervaizer.server import Server
22
+
23
+
24
+ def create_routes(server: "Server") -> APIRouter:
25
+ """Create ACP protocol routes for the server."""
26
+ router = APIRouter(prefix="/agents", tags=["Protocol ACP"])
27
+ base_url = server.public_url or ""
28
+
29
+ @router.get(
30
+ "",
31
+ summary="ACP Agents Discovery",
32
+ description="Returns a list of all agents according to ACP protocol specification",
33
+ response_model=List[Dict[str, Any]],
34
+ )
35
+ @handle_route_errors()
36
+ async def get_acp_agents() -> List[Dict[str, Any]]:
37
+ """Get a list of all available agents in ACP format."""
38
+ log.info("[ACP] GET /agents [Agent discovery]")
39
+ return list_agents(server.agents, base_url)
40
+
41
+ # Create explicit routes for each agent
42
+ for agent in server.agents:
43
+
44
+ def create_agent_route(current_agent: "Agent") -> None:
45
+ route_path = f"/{current_agent.slug}"
46
+
47
+ @router.get(
48
+ route_path,
49
+ summary=f"ACP Agent Detail for {current_agent.name}",
50
+ description=f"Returns details for agent {current_agent.name} according to ACP protocol specification",
51
+ response_model=Dict[str, Any],
52
+ )
53
+ @handle_route_errors()
54
+ async def get_agent_detail() -> Dict[str, Any]:
55
+ """Get details for a specific agent in ACP format."""
56
+ log.info(f"[ACP] GET /agents/{current_agent.slug} [Agent detail]")
57
+ return create_agent_detail(current_agent, base_url)
58
+
59
+ # Call the closure function with the current agent
60
+ create_agent_route(agent)
61
+
62
+ @router.get(
63
+ "/health",
64
+ summary="ACP Health Status",
65
+ description="Returns health information about the server and agents",
66
+ response_model=Dict[str, Any],
67
+ )
68
+ @handle_route_errors()
69
+ async def get_acp_health() -> Dict[str, Any]:
70
+ """Get health information for the server and all agents."""
71
+ log.info("[ACP] GET /agents/health [Health status]")
72
+ return create_health_data(server.agents)
73
+
74
+ return router
supervaizer/py.typed ADDED
@@ -0,0 +1 @@
1
+ # This file indicates that the supervaizer package supports type checking