From 641cf5a4c1d167956d4bcafbfcac98e1b9d4ac5f Mon Sep 17 00:00:00 2001 From: Siddhant Rai Date: Thu, 11 Sep 2025 19:04:10 +0530 Subject: [PATCH] feat: skip empty fields in mcp tool call + improve error handling and response --- application/agents/base.py | 12 +++---- application/agents/tools/mcp_tool.py | 9 ++++- application/core/settings.py | 2 +- application/llm/google_ai.py | 9 +---- application/llm/handlers/base.py | 36 +++++++++++++------ application/llm/handlers/google.py | 14 ++++---- .../src/conversation/ConversationBubble.tsx | 21 +++++++++-- frontend/src/conversation/types/index.ts | 3 +- 8 files changed, 69 insertions(+), 37 deletions(-) diff --git a/application/agents/base.py b/application/agents/base.py index dff191a3..068b2a3c 100644 --- a/application/agents/base.py +++ b/application/agents/base.py @@ -142,28 +142,28 @@ class BaseAgent(ABC): tool_id, action_name, call_args = parser.parse_args(call) call_id = getattr(call, "id", None) or str(uuid.uuid4()) - + # Check if parsing failed if tool_id is None or action_name is None: error_message = f"Error: Failed to parse LLM tool call. Tool name: {getattr(call, 'name', 'unknown')}" logger.error(error_message) - + tool_call_data = { "tool_name": "unknown", "call_id": call_id, - "action_name": getattr(call, 'name', 'unknown'), + "action_name": getattr(call, "name", "unknown"), "arguments": call_args or {}, "result": f"Failed to parse tool call. Invalid tool name format: {getattr(call, 'name', 'unknown')}", } yield {"type": "tool_call", "data": {**tool_call_data, "status": "error"}} self.tool_calls.append(tool_call_data) return f"Failed to parse tool call.", call_id - + # Check if tool_id exists in available tools if tool_id not in tools_dict: error_message = f"Error: Tool ID '{tool_id}' extracted from LLM call not found in available tools_dict. Available IDs: {list(tools_dict.keys())}" logger.error(error_message) - + # Return error result tool_call_data = { "tool_name": "unknown", @@ -175,7 +175,7 @@ class BaseAgent(ABC): yield {"type": "tool_call", "data": {**tool_call_data, "status": "error"}} self.tool_calls.append(tool_call_data) return f"Tool with ID {tool_id} not found.", call_id - + tool_call_data = { "tool_name": tools_dict[tool_id]["name"], "call_id": call_id, diff --git a/application/agents/tools/mcp_tool.py b/application/agents/tools/mcp_tool.py index 72a26482..dc689367 100644 --- a/application/agents/tools/mcp_tool.py +++ b/application/agents/tools/mcp_tool.py @@ -283,7 +283,14 @@ class MCPTool(Tool): """ self._ensure_valid_session() - call_params = {"name": action_name, "arguments": kwargs} + # Skipping empty/None values - letting the server use defaults + + cleaned_kwargs = {} + for key, value in kwargs.items(): + if value == "" or value is None: + continue + cleaned_kwargs[key] = value + call_params = {"name": action_name, "arguments": cleaned_kwargs} try: result = self._make_mcp_request("tools/call", call_params) return result diff --git a/application/core/settings.py b/application/core/settings.py index a8c6bfa3..f1563569 100644 --- a/application/core/settings.py +++ b/application/core/settings.py @@ -26,7 +26,7 @@ class Settings(BaseSettings): "gpt-4o-mini": 128000, "gpt-3.5-turbo": 4096, "claude-2": 1e5, - "gemini-2.0-flash-exp": 1e6, + "gemini-2.5-flash": 1e6, } UPLOAD_FOLDER: str = "inputs" PARSE_PDF_AS_IMAGE: bool = False diff --git a/application/llm/google_ai.py b/application/llm/google_ai.py index 54567f6f..b88e1d9f 100644 --- a/application/llm/google_ai.py +++ b/application/llm/google_ai.py @@ -151,15 +151,8 @@ class GoogleLLM(BaseLLM): if role == "assistant": role = "model" - elif role == "system": - continue elif role == "tool": - continue - elif role not in ["user", "model"]: - logging.warning( - f"GoogleLLM: Converting unsupported role '{role}' to 'user'" - ) - role = "user" + role = "model" parts = [] if role and content is not None: diff --git a/application/llm/handlers/base.py b/application/llm/handlers/base.py index 43205472..96ed4c00 100644 --- a/application/llm/handlers/base.py +++ b/application/llm/handlers/base.py @@ -205,7 +205,6 @@ class LLMHandler(ABC): except StopIteration as e: tool_response, call_id = e.value break - updated_messages.append( { "role": "assistant", @@ -222,17 +221,36 @@ class LLMHandler(ABC): ) updated_messages.append(self.create_tool_message(call, tool_response)) - except Exception as e: logger.error(f"Error executing tool: {str(e)}", exc_info=True) - updated_messages.append( - { - "role": "tool", - "content": f"Error executing tool: {str(e)}", - "tool_call_id": call.id, - } + error_call = ToolCall( + id=call.id, name=call.name, arguments=call.arguments ) + error_response = f"Error executing tool: {str(e)}" + error_message = self.create_tool_message(error_call, error_response) + updated_messages.append(error_message) + call_parts = call.name.split("_") + if len(call_parts) >= 2: + tool_id = call_parts[-1] # Last part is tool ID (e.g., "1") + action_name = "_".join(call_parts[:-1]) + tool_name = tools_dict.get(tool_id, {}).get("name", "unknown_tool") + full_action_name = f"{action_name}_{tool_id}" + else: + tool_name = "unknown_tool" + action_name = call.name + full_action_name = call.name + yield { + "type": "tool_call", + "data": { + "tool_name": tool_name, + "call_id": call.id, + "action_name": full_action_name, + "arguments": call.arguments, + "error": error_response, + "status": "error", + }, + } return updated_messages def handle_non_streaming( @@ -263,13 +281,11 @@ class LLMHandler(ABC): except StopIteration as e: messages = e.value break - response = agent.llm.gen( model=agent.gpt_model, messages=messages, tools=agent.tools ) parsed = self.parse_response(response) self.llm_calls.append(build_stack_data(agent.llm)) - return parsed.content def handle_streaming( diff --git a/application/llm/handlers/google.py b/application/llm/handlers/google.py index b43f2a16..7fa44cb6 100644 --- a/application/llm/handlers/google.py +++ b/application/llm/handlers/google.py @@ -17,7 +17,6 @@ class GoogleLLMHandler(LLMHandler): finish_reason="stop", raw_response=response, ) - if hasattr(response, "candidates"): parts = response.candidates[0].content.parts if response.candidates else [] tool_calls = [ @@ -41,7 +40,6 @@ class GoogleLLMHandler(LLMHandler): finish_reason="tool_calls" if tool_calls else "stop", raw_response=response, ) - else: tool_calls = [] if hasattr(response, "function_call"): @@ -61,14 +59,16 @@ class GoogleLLMHandler(LLMHandler): def create_tool_message(self, tool_call: ToolCall, result: Any) -> Dict: """Create Google-style tool message.""" - from google.genai import types return { - "role": "tool", + "role": "model", "content": [ - types.Part.from_function_response( - name=tool_call.name, response={"result": result} - ).to_json_dict() + { + "function_response": { + "name": tool_call.name, + "response": {"result": result}, + } + } ], } diff --git a/frontend/src/conversation/ConversationBubble.tsx b/frontend/src/conversation/ConversationBubble.tsx index 3be40df7..bbdf5e00 100644 --- a/frontend/src/conversation/ConversationBubble.tsx +++ b/frontend/src/conversation/ConversationBubble.tsx @@ -1,6 +1,6 @@ import 'katex/dist/katex.min.css'; -import { forwardRef, Fragment, useRef, useState, useEffect } from 'react'; +import { forwardRef, Fragment, useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import ReactMarkdown from 'react-markdown'; import { useSelector } from 'react-redux'; @@ -12,12 +12,13 @@ import { import rehypeKatex from 'rehype-katex'; import remarkGfm from 'remark-gfm'; import remarkMath from 'remark-math'; -import DocumentationDark from '../assets/documentation-dark.svg'; + import ChevronDown from '../assets/chevron-down.svg'; import Cloud from '../assets/cloud.svg'; import DocsGPT3 from '../assets/cute_docsgpt3.svg'; import Dislike from '../assets/dislike.svg?react'; import Document from '../assets/document.svg'; +import DocumentationDark from '../assets/documentation-dark.svg'; import Edit from '../assets/edit.svg'; import Like from '../assets/like.svg?react'; import Link from '../assets/link.svg'; @@ -761,7 +762,11 @@ function ToolCalls({ toolCalls }: { toolCalls: ToolCallsType[] }) { Response {' '}

{toolCall.status === 'pending' && ( @@ -779,6 +784,16 @@ function ToolCalls({ toolCalls }: { toolCalls: ToolCallsType[] }) {

)} + {toolCall.status === 'error' && ( +

+ + {toolCall.error} + +

+ )} diff --git a/frontend/src/conversation/types/index.ts b/frontend/src/conversation/types/index.ts index 4ccb04a1..d962e4bc 100644 --- a/frontend/src/conversation/types/index.ts +++ b/frontend/src/conversation/types/index.ts @@ -4,5 +4,6 @@ export type ToolCallsType = { call_id: string; arguments: Record; result?: Record; - status?: 'pending' | 'completed'; + error?: string; + status?: 'pending' | 'completed' | 'error'; };