1414
1515import logging
1616import os
17- from typing import Callable , override
18-
1917import uvicorn
18+ import inspect
19+
20+ from typing import Callable , override
2021from a2a .server .agent_execution import AgentExecutor
2122from a2a .server .agent_execution .context import RequestContext
2223from a2a .server .apps import A2AStarletteApplication
2526from a2a .server .tasks import InMemoryTaskStore
2627from a2a .server .tasks .task_store import TaskStore
2728from a2a .types import AgentCard
29+ from starlette .requests import Request
2830from starlette .applications import Starlette
29- from starlette .responses import JSONResponse
31+ from starlette .responses import JSONResponse , Response
3032from starlette .routing import Route
3133
3234from 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 )
0 commit comments