1919import logging
2020import os
2121import threading
22+ import time
2223from abc import ABC , abstractmethod
2324from typing import Any , Callable , Dict , Optional , Tuple
2425
2526import uvicorn
26- from mcp .server .fastmcp import Context , FastMCP
27+
28+ # from mcp.server.fastmcp import Context, FastMCP
29+ from fastmcp import Context , FastMCP
2730from starlette .requests import Request
2831from starlette .responses import JSONResponse
2932from uvicorn .middleware .proxy_headers import ProxyHeadersMiddleware
@@ -87,11 +90,11 @@ def __init__(self, server_name: str, adapter: EnvironmentAdapter, seed: Optional
8790 self .adapter = adapter
8891
8992 # Create FastMCP server
90- self .mcp = FastMCP (
91- server_name ,
92- host = "0.0.0.0" ,
93- port = int ( os . environ . get ( "PORT" , 8000 )),
94- )
93+ self .mcp = FastMCP (name = server_name )
94+
95+ # Store host and port for later use in run() method
96+ self . host = "0.0.0.0"
97+ self . port = int ( os . environ . get ( "PORT" , 8000 ) )
9598
9699 # Multi-session support
97100 self .sessions = {} # session_id -> {"env": env, "obs": obs, "session_data": data}
@@ -117,6 +120,7 @@ def __init__(self, server_name: str, adapter: EnvironmentAdapter, seed: Optional
117120 self ._register_tools ()
118121 self ._discover_and_register_control_plane_endpoints ()
119122 self ._register_session_reset_endpoint ()
123+ # self._register_health_check_endpoint()
120124
121125 def _get_session_id (self , ctx : Context ) -> str :
122126 """
@@ -184,6 +188,7 @@ def _get_or_create_session(self, ctx: Context) -> Dict[str, Any]:
184188 """
185189 session_id = self ._get_session_id (ctx )
186190 print (f"🔍 _get_or_create_session: session_id: { session_id } " )
191+ return self .sessions [session_id ]
187192
188193 with self .session_lock :
189194 if session_id not in self .sessions :
@@ -238,18 +243,56 @@ async def reset_session_endpoint(request: Request) -> JSONResponse:
238243 print (f"🔍 _register_session_reset_endpoint: Resetting session, session_id: { session_id } , seed: { seed } " )
239244 if not session_id :
240245 return JSONResponse ({"error" : "Missing mcp-session-id header" }, status_code = 400 )
241- with self .session_lock :
242- if session_id in self .sessions :
243- env , obs , _ = self ._new_env (seed = seed )
244- self .sessions [session_id ] = {
245- "env" : env ,
246- "obs" : obs ,
247- "session_data" : {},
248- "session_id" : session_id ,
249- }
250- print (f"🔍 _register_session_reset_endpoint: Finished reset session, session_id: { session_id } " )
246+ # with self.session_lock:
247+ # if session_id in self.sessions:
248+ # env, obs, _ = self._new_env(seed=seed)
249+ # self.sessions[session_id] = {
250+ # "env": env,
251+ # "obs": obs,
252+ # "session_data": {},
253+ # "session_id": session_id,
254+ # }
255+ # print(f"🔍 _register_session_reset_endpoint: Finished reset session, session_id: {session_id}")
251256 return JSONResponse ({"message" : "Session reset successfully" })
252257
258+ # def _register_health_check_endpoint(self):
259+ # """Register a simple health check endpoint for diagnostics."""
260+
261+ # @self.mcp.custom_route("/health", methods=["GET"])
262+ # async def health_check_endpoint(request: Request) -> JSONResponse:
263+ # """Simple health check that returns immediately."""
264+ # return JSONResponse({"ok": True, "timestamp": time.time()})
265+
266+ # def _add_timing_middleware(self, starlette_app):
267+ # """Add ASGI middleware to log request arrival times."""
268+
269+ # class TimingMiddleware:
270+ # def __init__(self, app):
271+ # self.app = app
272+
273+ # async def __call__(self, scope, receive, send):
274+ # if scope["type"] != "http":
275+ # await self.app(scope, receive, send)
276+ # return
277+
278+ # # Log immediately when request arrives at server
279+ # start_time = time.time()
280+ # path = scope.get("path", "")
281+ # method = scope.get("method", "")
282+
283+ # print(f"🚀 REQUEST ARRIVED: {method} {path} at {start_time}")
284+
285+ # async def send_wrapper(message):
286+ # if message["type"] == "http.response.start":
287+ # # Log completion time for comparison
288+ # end_time = time.time()
289+ # if path in ["/health", "/control/initial_state"]:
290+ # print(f"✅ REQUEST took: {end_time - start_time:.3f}s")
291+ # await send(message)
292+
293+ # await self.app(scope, receive, send_wrapper)
294+ # starlette_app.add_middleware(TimingMiddleware)
295+
253296 def _discover_and_register_control_plane_endpoints (self ):
254297 """
255298 Discover and register control plane endpoints on the subclass instance.
@@ -271,6 +314,9 @@ def _discover_and_register_control_plane_endpoints(self):
271314 # Create session-aware handler for this endpoint
272315 def create_endpoint_handler (func : Callable ):
273316 async def endpoint_handler (request : Request ) -> JSONResponse :
317+
318+ if func .__name__ == "get_initial_state_endpoint" :
319+ logger .info (f"===== starting to handle endpoint: { func .__name__ } , time: { time .time ()} " )
274320 try :
275321 # Extract session ID from request headers (similar to StreamableHTTP pattern)
276322 session_id = request .headers .get ("mcp-session-id" )
@@ -284,31 +330,43 @@ async def endpoint_handler(request: Request) -> JSONResponse:
284330 with self .session_lock :
285331 session_data = self .sessions .get (session_id )
286332 if not session_data :
287- # For initial state endpoint, we need to create the session
288- # based on the session ID and available information
289- if func .__name__ == "get_initial_state_endpoint" :
290- env , obs , info = self ._new_env (seed = None )
291- # Initialize session state with extracted seed from session ID
292- session_data = {
293- "env" : env ,
294- "obs" : obs ,
295- "session_data" : {}, # Subclasses can store additional data here
296- "session_id" : session_id ,
297- }
298- # Store the session
299- self .sessions [session_id ] = session_data
300- else :
301- return JSONResponse (
302- {"error" : f"Session { session_id } not found" },
303- status_code = 404 ,
304- )
333+ # create a placeholder session data
334+ self .sessions [session_id ] = {"placeholder" : True }
335+ # For initial state endpoint, we need to create the session
336+ # based on the session ID and available information
337+ if func .__name__ == "get_initial_state_endpoint" :
338+ env , obs , info = self ._new_env (seed = None )
339+ # Initialize session state with extracted seed from session ID
340+ session_data = {
341+ "env" : env ,
342+ "obs" : obs ,
343+ "session_data" : {}, # Subclasses can store additional data here
344+ "session_id" : session_id ,
345+ }
346+ # Store the session
347+ with self .session_lock :
348+ self .sessions [session_id ] = session_data
349+
350+ else :
351+ return JSONResponse (
352+ {"error" : f"Session { session_id } not found" },
353+ status_code = 404 ,
354+ )
305355
306356 # Call the endpoint function with session data
357+ method_start = time .time ()
358+ if func .__name__ == "get_initial_state_endpoint" :
359+ print (f"🎯 METHOD START: { func .__name__ } at { method_start } " )
360+
307361 if inspect .iscoroutinefunction (func ):
308362 result = await func (session_data = session_data )
309363 else :
310364 result = func (session_data = session_data )
311365
366+ # method_end = time.time()
367+ # if func.__name__ == "get_initial_state_endpoint":
368+ # print(f"🎯 METHOD END: {func.__name__} at {method_end} (took {method_end - method_start:.3f}s)")
369+
312370 return JSONResponse (result )
313371
314372 except Exception as e :
@@ -351,22 +409,25 @@ def _update_control_plane(self, reward: float, terminated: bool, truncated: bool
351409
352410 def _get_or_create_session_control_plane (self , session_id : str ) -> Dict [str , Any ]:
353411 """Get or create control plane state for a specific session."""
354- with self .session_lock :
355- if session_id not in self .sessions :
356- return {}
357-
358- session_data = self .sessions [session_id ]
359- if "control_plane" not in session_data ["session_data" ]:
360- session_data ["session_data" ]["control_plane" ] = {
361- "reward" : 0.0 ,
362- "terminated" : False ,
363- "truncated" : False ,
364- "info" : {},
365- "step_count" : 0 ,
366- "total_reward" : 0.0 ,
367- }
412+ if session_id not in self .sessions :
413+ raise Exception (f"Session { session_id } not found" )
414+
415+ # with self.session_lock:
416+ # if session_id not in self.sessions:
417+ # return {}
418+
419+ session_data = self .sessions [session_id ]
420+ if "control_plane" not in session_data ["session_data" ]:
421+ session_data ["session_data" ]["control_plane" ] = {
422+ "reward" : 0.0 ,
423+ "terminated" : False ,
424+ "truncated" : False ,
425+ "info" : {},
426+ "step_count" : 0 ,
427+ "total_reward" : 0.0 ,
428+ }
368429
369- return session_data ["session_data" ]["control_plane" ]
430+ return session_data ["session_data" ]["control_plane" ]
370431
371432 def _update_session_control_plane (
372433 self ,
@@ -391,12 +452,12 @@ def _update_session_control_plane(
391452 f"🎛️ Session { session_id [:16 ]} ... control plane: reward={ reward } , terminated={ terminated } , step={ control_plane ['step_count' ]} , total_reward={ control_plane ['total_reward' ]} "
392453 )
393454
394- def get_control_plane_state (self , session_id : str ) -> Optional [Dict [str , Any ]]:
395- """Get control plane state for a specific session (for rollout system)."""
396- with self .session_lock :
397- if session_id in self .sessions :
398- return self ._get_or_create_session_control_plane (session_id ).copy ()
399- return None
455+ # def get_control_plane_state(self, session_id: str) -> Optional[Dict[str, Any]]:
456+ # """Get control plane state for a specific session (for rollout system)."""
457+ # with self.session_lock:
458+ # if session_id in self.sessions:
459+ # return self._get_or_create_session_control_plane(session_id).copy()
460+ # return None
400461
401462 def _execute_environment_step (self , action_int : int ) -> Dict [str , Any ]:
402463 """
@@ -507,6 +568,7 @@ def get_info_endpoint(self, session_data: Dict[str, Any]) -> Dict[str, Any]:
507568 @control_plane_endpoint ("/control/initial_state" )
508569 def get_initial_state_endpoint (self , session_data : Dict [str , Any ]) -> Dict [str , Any ]:
509570 """Get initial state for this session."""
571+ print (f"🔍 STARTING get_initial_state_endpoint: { time .time ()} " )
510572 env = session_data .get ("env" )
511573 obs = session_data .get ("obs" )
512574
@@ -593,14 +655,14 @@ async def run_with_high_concurrency():
593655
594656 config = uvicorn .Config (
595657 starlette_app ,
596- host = self .mcp . settings . host ,
597- port = self .mcp . settings . port ,
598- log_level = self . mcp . settings . log_level . lower (),
658+ host = self .host ,
659+ port = self .port ,
660+ log_level = "info" , # Use default log level instead of accessing settings
599661 proxy_headers = True ,
600662 forwarded_allow_ips = "*" ,
601663 # HIGH CONCURRENCY SETTINGS
602- limit_concurrency = 200 , # Increase for HTTP endpoints + MCP
603- limit_max_requests = 100000 , # Higher request limit
664+ limit_concurrency = None , # Increase for HTTP endpoints + MCP
665+ limit_max_requests = None , # Higher request limit
604666 timeout_keep_alive = 120 , # Longer keep-alive for control plane
605667 timeout_notify = 180 ,
606668 h11_max_incomplete_event_size = 4 * 1024 * 1024 , # Handle larger events
0 commit comments