LiamKhoaLe commited on
Commit
c816ffa
·
1 Parent(s): b843268

Upd gemini mcp server fallback

Browse files
Files changed (1) hide show
  1. app.py +116 -108
app.py CHANGED
@@ -277,6 +277,17 @@ async def get_mcp_session():
277
  session = ClientSession(read, write)
278
  await session.__aenter__()
279
 
 
 
 
 
 
 
 
 
 
 
 
280
  # Store both the session and stdio context to keep them alive
281
  global_mcp_session = session
282
  global_mcp_stdio_ctx = stdio_ctx
@@ -293,24 +304,44 @@ async def get_mcp_session():
293
  async def call_gemini_mcp(user_prompt: str, system_prompt: str = None, files: list = None, model: str = None, temperature: float = 0.2) -> str:
294
  """Call Gemini MCP generate_content tool"""
295
  if not MCP_AVAILABLE:
 
296
  return ""
297
 
298
  try:
299
  session = await get_mcp_session()
300
  if session is None:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
301
  return ""
302
 
303
  # Find generate_content tool
304
- tools = await session.list_tools()
305
  generate_tool = None
306
  for tool in tools.tools:
307
- if "generate_content" in tool.name.lower() or "generate" in tool.name.lower():
308
  generate_tool = tool
309
  logger.info(f"Found Gemini MCP tool: {tool.name}")
310
  break
311
 
312
  if not generate_tool:
313
- logger.warning("Gemini MCP generate_content tool not found")
314
  return ""
315
 
316
  # Prepare arguments
@@ -326,6 +357,7 @@ async def call_gemini_mcp(user_prompt: str, system_prompt: str = None, files: li
326
  if temperature is not None:
327
  arguments["temperature"] = temperature
328
 
 
329
  result = await session.call_tool(generate_tool.name, arguments=arguments)
330
 
331
  # Parse result
@@ -333,6 +365,7 @@ async def call_gemini_mcp(user_prompt: str, system_prompt: str = None, files: li
333
  for item in result.content:
334
  if hasattr(item, 'text'):
335
  return item.text.strip()
 
336
  return ""
337
  except Exception as e:
338
  logger.error(f"Gemini MCP call error: {e}")
@@ -655,120 +688,95 @@ def translate_text(text: str, target_lang: str = "en", source_lang: str = None)
655
  # Return original text if translation fails
656
  return text
657
 
658
- async def search_web_mcp(query: str, max_results: int = 5) -> list:
659
- """Search web using MCP tools"""
660
  if not MCP_AVAILABLE:
661
- logger.warning("MCP not available, falling back to direct search")
662
- return search_web_fallback(query, max_results)
663
 
664
  try:
665
- # Get MCP session
666
- session = await get_mcp_session()
667
- if session is None:
668
- logger.warning("Failed to get MCP session, falling back to direct search")
669
- return search_web_fallback(query, max_results)
 
 
 
 
 
 
 
 
 
 
670
 
671
- # Call MCP search tool
672
- try:
673
- tools = await session.list_tools()
674
- except Exception as e:
675
- logger.error(f"Failed to list MCP tools: {e}")
676
- return search_web_fallback(query, max_results)
677
 
678
- search_tool = None
679
- for tool in tools.tools:
680
- if "search" in tool.name.lower() or "duckduckgo" in tool.name.lower():
681
- search_tool = tool
682
- logger.info(f"Found MCP search tool: {tool.name}")
683
- break
684
 
685
- if search_tool:
686
- # Execute search via MCP
687
- try:
688
- result = await session.call_tool(
689
- search_tool.name,
690
- arguments={"query": query, "max_results": max_results}
691
- )
692
- except Exception as e:
693
- logger.error(f"Error calling MCP search tool: {e}")
694
- return search_web_fallback(query, max_results)
695
-
696
- # Parse MCP result
697
- web_content = []
698
- if hasattr(result, 'content') and result.content:
699
- for item in result.content:
700
- if hasattr(item, 'text'):
701
- # Parse JSON response from MCP
702
- import json
703
- try:
704
- data = json.loads(item.text)
705
- if isinstance(data, list):
706
- for entry in data[:max_results]:
707
- web_content.append({
708
- 'title': entry.get('title', ''),
709
- 'url': entry.get('url', entry.get('href', '')),
710
- 'content': entry.get('body', entry.get('snippet', entry.get('content', '')))
711
- })
712
- elif isinstance(data, dict):
713
- web_content.append({
714
- 'title': data.get('title', ''),
715
- 'url': data.get('url', data.get('href', '')),
716
- 'content': data.get('body', data.get('snippet', data.get('content', '')))
717
- })
718
- except json.JSONDecodeError:
719
- # If not JSON, treat as plain text
720
- web_content.append({
721
- 'title': '',
722
- 'url': '',
723
- 'content': item.text
724
- })
725
-
726
- # If MCP search didn't return results, try fetching content via MCP fetch tool
727
- if web_content:
728
- # Try to fetch full content for each result using MCP fetch tool
729
- fetch_tool = None
730
- for tool in tools.tools:
731
- if "fetch" in tool.name.lower() or "scrape" in tool.name.lower() or "get" in tool.name.lower():
732
- fetch_tool = tool
733
- logger.info(f"Found MCP fetch tool: {tool.name}")
734
- break
735
-
736
- if fetch_tool:
737
- for item in web_content[:3]: # Fetch content for top 3 results
738
- if not item.get('url'):
739
- continue
740
- try:
741
- fetch_result = await session.call_tool(
742
- fetch_tool.name,
743
- arguments={"url": item['url']}
744
- )
745
- if hasattr(fetch_result, 'content') and fetch_result.content:
746
- for content_item in fetch_result.content:
747
- if hasattr(content_item, 'text'):
748
- # Extract text content
749
- full_text = content_item.text
750
- if len(full_text) > 1000:
751
- full_text = full_text[:1000] + "..."
752
- item['content'] = item.get('content', '') + "\n" + full_text[:500]
753
- except Exception as e:
754
- logger.debug(f"Could not fetch content for {item['url']}: {e}")
755
- continue
756
-
757
- if web_content:
758
- logger.info(f"MCP search returned {len(web_content)} results")
759
- return web_content
760
- else:
761
- logger.warning("MCP search returned no results, falling back to direct search")
762
- return search_web_fallback(query, max_results)
763
- else:
764
- logger.warning("MCP search tool not found, falling back to direct search")
765
- return search_web_fallback(query, max_results)
766
-
767
  except Exception as e:
768
- logger.error(f"MCP web search error: {e}, falling back to direct search")
769
  import traceback
770
  logger.debug(traceback.format_exc())
771
- return search_web_fallback(query, max_results)
 
 
 
 
 
 
 
 
 
772
 
773
  def search_web_fallback(query: str, max_results: int = 5) -> list:
774
  """Fallback web search using DuckDuckGo directly (when MCP is not available)"""
 
277
  session = ClientSession(read, write)
278
  await session.__aenter__()
279
 
280
+ # Wait a bit for the server to fully initialize
281
+ await asyncio.sleep(0.5)
282
+
283
+ # Verify the session works by listing tools
284
+ try:
285
+ tools = await session.list_tools()
286
+ logger.info(f"MCP server initialized with {len(tools.tools)} tools")
287
+ except Exception as e:
288
+ logger.warning(f"Could not list tools immediately after session creation: {e}")
289
+ # Continue anyway, might work on first actual call
290
+
291
  # Store both the session and stdio context to keep them alive
292
  global_mcp_session = session
293
  global_mcp_stdio_ctx = stdio_ctx
 
304
  async def call_gemini_mcp(user_prompt: str, system_prompt: str = None, files: list = None, model: str = None, temperature: float = 0.2) -> str:
305
  """Call Gemini MCP generate_content tool"""
306
  if not MCP_AVAILABLE:
307
+ logger.warning("MCP not available for Gemini call")
308
  return ""
309
 
310
  try:
311
  session = await get_mcp_session()
312
  if session is None:
313
+ logger.warning("Failed to get MCP session for Gemini call")
314
+ return ""
315
+
316
+ # Retry listing tools if it fails the first time
317
+ max_retries = 3
318
+ tools = None
319
+ for attempt in range(max_retries):
320
+ try:
321
+ tools = await session.list_tools()
322
+ break
323
+ except Exception as e:
324
+ if attempt < max_retries - 1:
325
+ logger.debug(f"Failed to list tools (attempt {attempt + 1}/{max_retries}), retrying...")
326
+ await asyncio.sleep(0.5 * (attempt + 1)) # Exponential backoff
327
+ else:
328
+ logger.error(f"Failed to list MCP tools after {max_retries} attempts: {e}")
329
+ return ""
330
+
331
+ if not tools or not hasattr(tools, 'tools'):
332
+ logger.error("Invalid tools response from MCP server")
333
  return ""
334
 
335
  # Find generate_content tool
 
336
  generate_tool = None
337
  for tool in tools.tools:
338
+ if tool.name == "generate_content" or "generate_content" in tool.name.lower():
339
  generate_tool = tool
340
  logger.info(f"Found Gemini MCP tool: {tool.name}")
341
  break
342
 
343
  if not generate_tool:
344
+ logger.warning(f"Gemini MCP generate_content tool not found. Available tools: {[t.name for t in tools.tools]}")
345
  return ""
346
 
347
  # Prepare arguments
 
357
  if temperature is not None:
358
  arguments["temperature"] = temperature
359
 
360
+ logger.debug(f"Calling Gemini MCP tool '{generate_tool.name}' with arguments: {list(arguments.keys())}")
361
  result = await session.call_tool(generate_tool.name, arguments=arguments)
362
 
363
  # Parse result
 
365
  for item in result.content:
366
  if hasattr(item, 'text'):
367
  return item.text.strip()
368
+ logger.warning("Gemini MCP returned empty or invalid result")
369
  return ""
370
  except Exception as e:
371
  logger.error(f"Gemini MCP call error: {e}")
 
688
  # Return original text if translation fails
689
  return text
690
 
691
+ async def search_web_gemini(query: str, max_results: int = 5) -> list:
692
+ """Search web using Gemini MCP generate_content tool"""
693
  if not MCP_AVAILABLE:
694
+ logger.warning("Gemini MCP not available for web search")
695
+ return []
696
 
697
  try:
698
+ # Use Gemini MCP to search the web and get structured results
699
+ user_prompt = f"""Search the web for: "{query}"
700
+
701
+ Return the search results in JSON format with the following structure:
702
+ {{
703
+ "results": [
704
+ {{
705
+ "title": "Result title",
706
+ "url": "Result URL",
707
+ "content": "Brief summary or snippet of the content"
708
+ }}
709
+ ]
710
+ }}
711
+
712
+ Return up to {max_results} most relevant results. Focus on medical/health information if applicable."""
713
 
714
+ # Use concise system prompt
715
+ system_prompt = "You are a web search assistant. Search the web and return structured JSON results with titles, URLs, and content summaries."
 
 
 
 
716
 
717
+ result = await call_gemini_mcp(
718
+ user_prompt=user_prompt,
719
+ system_prompt=system_prompt,
720
+ model=GEMINI_MODEL, # Use full model for web search
721
+ temperature=0.3
722
+ )
723
 
724
+ if not result:
725
+ logger.warning("Gemini MCP returned empty search results")
726
+ return []
727
+
728
+ # Parse JSON response
729
+ try:
730
+ # Extract JSON from response
731
+ json_start = result.find('{')
732
+ json_end = result.rfind('}') + 1
733
+ if json_start >= 0 and json_end > json_start:
734
+ data = json.loads(result[json_start:json_end])
735
+ if isinstance(data, dict) and "results" in data:
736
+ web_content = []
737
+ for entry in data["results"][:max_results]:
738
+ web_content.append({
739
+ 'title': entry.get('title', ''),
740
+ 'url': entry.get('url', ''),
741
+ 'content': entry.get('content', '')
742
+ })
743
+ logger.info(f"Gemini MCP search returned {len(web_content)} results")
744
+ return web_content
745
+ elif isinstance(data, list):
746
+ # Handle case where results are directly in a list
747
+ web_content = []
748
+ for entry in data[:max_results]:
749
+ web_content.append({
750
+ 'title': entry.get('title', ''),
751
+ 'url': entry.get('url', entry.get('href', '')),
752
+ 'content': entry.get('content', entry.get('body', entry.get('snippet', '')))
753
+ })
754
+ logger.info(f"Gemini MCP search returned {len(web_content)} results")
755
+ return web_content
756
+ except json.JSONDecodeError as e:
757
+ logger.warning(f"Failed to parse Gemini search results as JSON: {e}")
758
+ # Fallback: treat as plain text result
759
+ return [{
760
+ 'title': 'Web Search Result',
761
+ 'url': '',
762
+ 'content': result[:1000] # Limit content length
763
+ }]
764
+
765
+ return []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
766
  except Exception as e:
767
+ logger.error(f"Gemini MCP web search error: {e}")
768
  import traceback
769
  logger.debug(traceback.format_exc())
770
+ return []
771
+
772
+ async def search_web_mcp(query: str, max_results: int = 5) -> list:
773
+ """Search web using Gemini MCP (wrapper for compatibility)"""
774
+ results = await search_web_gemini(query, max_results)
775
+ if results:
776
+ return results
777
+ # Fallback to direct search only if Gemini MCP fails
778
+ logger.warning("Gemini MCP web search failed, falling back to direct search")
779
+ return search_web_fallback(query, max_results)
780
 
781
  def search_web_fallback(query: str, max_results: int = 5) -> list:
782
  """Fallback web search using DuckDuckGo directly (when MCP is not available)"""