Skip to content

Commit 43f5191

Browse files
committed
feat: add ping support for a2a app
(cherry picked from commit dec0614b8faa9f3381070defb172af30f9176c89)
1 parent 31bddaf commit 43f5191

File tree

3 files changed

+89
-10
lines changed

3 files changed

+89
-10
lines changed

agentkit/apps/a2a_app/a2a_app.py

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@
1414

1515
import logging
1616
import os
17-
from typing import Callable, override
18-
1917
import uvicorn
18+
import inspect
19+
20+
from typing import Callable, override
2021
from a2a.server.agent_execution import AgentExecutor
2122
from a2a.server.agent_execution.context import RequestContext
2223
from a2a.server.apps import A2AStarletteApplication
@@ -25,8 +26,9 @@
2526
from a2a.server.tasks import InMemoryTaskStore
2627
from a2a.server.tasks.task_store import TaskStore
2728
from a2a.types import AgentCard
29+
from starlette.requests import Request
2830
from starlette.applications import Starlette
29-
from starlette.responses import JSONResponse
31+
from starlette.responses import JSONResponse, Response
3032
from starlette.routing import Route
3133

3234
from agentkit.apps.a2a_app.telemetry import telemetry
@@ -75,6 +77,7 @@ def __init__(self) -> None:
7577

7678
self._agent_executor: AgentExecutor | None = None
7779
self._task_store: TaskStore | None = None
80+
self._ping_func: Callable | None = None
7881

7982
def agent_executor(self, **kwargs) -> Callable:
8083
"""Wrap an AgentExecutor class, init it, then bind it to the app instance."""
@@ -136,6 +139,30 @@ def is_agentkit_runtime() -> bool:
136139
)
137140
app.routes.append(route)
138141

142+
def ping(self, func: Callable) -> Callable:
143+
"""Register a zero-argument health check function and expose it via GET /ping.
144+
145+
The function must accept no arguments and should return either a string or a dict.
146+
The response shape mirrors SimpleApp: {"status": <str|dict>}.
147+
"""
148+
# Ensure zero-argument function similar to SimpleApp
149+
if len(list(inspect.signature(func).parameters.keys())) != 0:
150+
raise AssertionError(
151+
f"Health check function `{func.__name__}` should not receive any arguments."
152+
)
153+
154+
self._ping_func = func
155+
return func
156+
157+
def _format_ping_status(self, result: str | dict) -> dict:
158+
# Align behavior with SimpleApp: always wrap into {"status": result}
159+
if isinstance(result, (str, dict)):
160+
return {"status": result}
161+
logger.error(
162+
f"Health check function {getattr(self._ping_func, '__name__', 'unknown')} must return `dict` or `str` type."
163+
)
164+
return {"status": "error", "message": "Invalid response type."}
165+
139166
@override
140167
def run(self, agent_card: AgentCard, host: str, port: int = 8000):
141168
if not self._agent_executor:
@@ -155,6 +182,30 @@ def run(self, agent_card: AgentCard, host: str, port: int = 8000):
155182
),
156183
).build()
157184

185+
# Register /ping route consistent with SimpleApp behavior
186+
async def _ping_handler(request: Request):
187+
if not self._ping_func:
188+
logger.error("Ping handler function is not set")
189+
return Response(status_code=404)
190+
191+
try:
192+
result = (
193+
await self._ping_func()
194+
if inspect.iscoroutinefunction(self._ping_func)
195+
else self._ping_func()
196+
)
197+
payload = self._format_ping_status(result)
198+
return JSONResponse(content=payload, media_type="application/json")
199+
except Exception as e:
200+
logger.exception("Ping handler function failed: %s", e)
201+
return JSONResponse(
202+
content={"status": "error", "message": str(e)},
203+
media_type="application/json",
204+
status_code=500,
205+
)
206+
207+
a2a_app.add_route("/ping", _ping_handler, methods=["GET"])
208+
158209
self.add_env_detect_route(a2a_app)
159210

160211
uvicorn.run(a2a_app, host=host, port=port)

agentkit/toolkit/resources/samples/a2a.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,15 @@
6363
class MyAgentExecutor(A2aAgentExecutor):
6464
pass
6565

66+
@a2a_app.ping
67+
def ping() -> str:
68+
return "pong!"
6669

6770
if __name__ == "__main__":
6871
from a2a.types import AgentCard, AgentProvider, AgentSkill, AgentCapabilities
6972

7073
agent_card = AgentCard(
71-
capabilities=AgentCapabilities(streaming=True), # 启用流式
74+
capabilities=AgentCapabilities(streaming=True),
7275
description=agent.description,
7376
name=agent.name,
7477
defaultInputModes=["text"],

agentkit/toolkit/runners/ve_agentkit.py

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,28 @@ def status(self, config: VeAgentkitRunnerConfig) -> StatusResult:
248248
},
249249
timeout=10,
250250
)
251-
ping_status = ping_response.status_code == 200
251+
if ping_response.status_code == 200:
252+
ping_status = True
253+
elif ping_response.status_code in (404, 405):
254+
# Fallback: try /health for SimpleApp compatibility
255+
try:
256+
health_response = requests.get(
257+
urljoin(public_endpoint, "health"),
258+
headers={
259+
"Authorization": f"Bearer {runner_config.runtime_apikey}"
260+
},
261+
timeout=10,
262+
)
263+
if health_response.status_code == 200:
264+
ping_status = True
265+
else:
266+
ping_status = None # Endpoint reachable but health route not available
267+
except Exception:
268+
# Endpoint reachable (ping returned 404/405), but health check failed
269+
ping_status = None
270+
else:
271+
# Non-200 status indicates server responded but not healthy
272+
ping_status = False
252273
except Exception as e:
253274
logger.error(f"Failed to check endpoint connectivity: {str(e)}")
254275
ping_status = False
@@ -265,11 +286,15 @@ def status(self, config: VeAgentkitRunnerConfig) -> StatusResult:
265286
status=status,
266287
endpoint_url=public_endpoint,
267288
service_id=runner_config.runtime_id,
268-
health="healthy"
269-
if ping_status
270-
else "unhealthy"
271-
if ping_status is False
272-
else None,
289+
health=(
290+
"healthy"
291+
if ping_status is True
292+
else "unhealthy"
293+
if ping_status is False
294+
else "unknown"
295+
if ping_status is None
296+
else None
297+
),
273298
metadata={
274299
"runtime_id": runner_config.runtime_id,
275300
"runtime_name": runtime.name

0 commit comments

Comments
 (0)