Y Phung Nguyen commited on
Commit
7a7ea02
Β·
1 Parent(s): e6bba1f

Upd MCP Gemini timeout bufer

Browse files
Files changed (2) hide show
  1. agent.py +20 -9
  2. client.py +10 -5
agent.py CHANGED
@@ -288,19 +288,29 @@ async def call_tool(name: str, arguments: dict) -> Sequence[TextContent | ImageC
288
  "max_output_tokens": GEMINI_MAX_OUTPUT_TOKENS
289
  }
290
 
291
- # Convert timeout from milliseconds to seconds, cap at 20s to stay under 120s function limit
292
- timeout_seconds = min(GEMINI_TIMEOUT / 1000.0, 20.0)
 
 
293
  logger.info(f"πŸ”΅ Calling Gemini API with model={model}, timeout={timeout_seconds}s...")
294
 
295
  # Use asyncio.to_thread to make the blocking call async
296
  # The API accepts contents as a list and config as a separate parameter
297
  def generate_sync():
298
- return gemini_client.models.generate_content(
299
- model=model,
300
- contents=gemini_contents,
301
- config=generation_config,
302
- )
 
 
 
 
 
 
 
303
 
 
304
  response = await asyncio.wait_for(
305
  asyncio.to_thread(generate_sync),
306
  timeout=timeout_seconds
@@ -329,10 +339,11 @@ async def call_tool(name: str, arguments: dict) -> Sequence[TextContent | ImageC
329
  return [TextContent(type="text", text="Error: No response from Gemini")]
330
 
331
  except asyncio.TimeoutError:
332
- timeout_seconds = min(GEMINI_TIMEOUT / 1000.0, 100.0)
333
  error_msg = f"Gemini API call timed out after {timeout_seconds}s"
334
  logger.error(f"❌ {error_msg}")
335
- return [TextContent(type="text", text=f"Error: {error_msg}")]
 
 
336
  except Exception as e:
337
  logger.error(f"❌ Error generating content: {type(e).__name__}: {e}")
338
  import traceback
 
288
  "max_output_tokens": GEMINI_MAX_OUTPUT_TOKENS
289
  }
290
 
291
+ # Convert timeout from milliseconds to seconds
292
+ # Cap at 18s to leave buffer for client timeout (25s) and communication overhead
293
+ # This ensures server completes before client times out
294
+ timeout_seconds = min(GEMINI_TIMEOUT / 1000.0, 18.0)
295
  logger.info(f"πŸ”΅ Calling Gemini API with model={model}, timeout={timeout_seconds}s...")
296
 
297
  # Use asyncio.to_thread to make the blocking call async
298
  # The API accepts contents as a list and config as a separate parameter
299
  def generate_sync():
300
+ try:
301
+ logger.debug(f"Calling Gemini API synchronously (model={model})...")
302
+ result = gemini_client.models.generate_content(
303
+ model=model,
304
+ contents=gemini_contents,
305
+ config=generation_config,
306
+ )
307
+ logger.debug("Gemini API synchronous call completed")
308
+ return result
309
+ except Exception as sync_error:
310
+ logger.error(f"Error in synchronous Gemini API call: {type(sync_error).__name__}: {sync_error}")
311
+ raise
312
 
313
+ logger.debug(f"Starting async wrapper for Gemini API call (timeout={timeout_seconds}s)...")
314
  response = await asyncio.wait_for(
315
  asyncio.to_thread(generate_sync),
316
  timeout=timeout_seconds
 
339
  return [TextContent(type="text", text="Error: No response from Gemini")]
340
 
341
  except asyncio.TimeoutError:
 
342
  error_msg = f"Gemini API call timed out after {timeout_seconds}s"
343
  logger.error(f"❌ {error_msg}")
344
+ logger.error(f" Model: {model}, Prompt length: {len(user_prompt)} chars")
345
+ logger.error(f" This may indicate network issues, API rate limiting, or the request is too complex")
346
+ return [TextContent(type="text", text=f"Error: {error_msg}. The request may be too complex or there may be network issues.")]
347
  except Exception as e:
348
  logger.error(f"❌ Error generating content: {type(e).__name__}: {e}")
349
  import traceback
client.py CHANGED
@@ -375,18 +375,23 @@ async def call_agent(user_prompt: str, system_prompt: str = None, files: list =
375
  logger.debug(f"MCP tool arguments keys: {list(arguments.keys())}")
376
  logger.debug(f"User prompt length: {len(user_prompt)} chars")
377
 
378
- # Add timeout to prevent hanging (max 20s to stay under 120s function limit)
 
 
 
379
  try:
380
- logger.debug("Starting MCP tool call with 20s timeout...")
381
  result = await asyncio.wait_for(
382
  session.call_tool(generate_tool.name, arguments=arguments),
383
- timeout=20.0
384
  )
385
  logger.info(f"βœ… MCP tool call completed successfully")
386
  except asyncio.TimeoutError:
387
- logger.error(f"❌ MCP tool call timed out after 20s - this exceeds the function time limit")
388
- logger.error(f" This suggests the MCP server (agent.py) is not responding or the Gemini API call is hanging")
 
389
  logger.error(f" Check if agent.py process is still running and responsive")
 
390
  # Invalidate session on timeout to force retry
391
  config.global_mcp_session = None
392
  # Properly cleanup stdio context
 
375
  logger.debug(f"MCP tool arguments keys: {list(arguments.keys())}")
376
  logger.debug(f"User prompt length: {len(user_prompt)} chars")
377
 
378
+ # Add timeout to prevent hanging
379
+ # Client timeout should be longer than server timeout to account for communication overhead
380
+ # Server timeout is ~18s, so client should wait ~25s to allow for processing + communication
381
+ client_timeout = 25.0
382
  try:
383
+ logger.debug(f"Starting MCP tool call with {client_timeout}s timeout...")
384
  result = await asyncio.wait_for(
385
  session.call_tool(generate_tool.name, arguments=arguments),
386
+ timeout=client_timeout
387
  )
388
  logger.info(f"βœ… MCP tool call completed successfully")
389
  except asyncio.TimeoutError:
390
+ logger.error(f"❌ MCP tool call timed out after {client_timeout}s")
391
+ logger.error(f" Tool: {generate_tool.name}, Model: {model or 'default'}")
392
+ logger.error(f" This suggests the MCP server (agent.py) is not responding or the Gemini API call is taking too long")
393
  logger.error(f" Check if agent.py process is still running and responsive")
394
+ logger.error(f" Consider increasing GEMINI_TIMEOUT or checking network connectivity")
395
  # Invalidate session on timeout to force retry
396
  config.global_mcp_session = None
397
  # Properly cleanup stdio context