Skip to content

Commit 7d26af7

Browse files
committed
Improve error handling with better messages and logging
Enhanced error handling across multiple modules to provide more user-friendly error messages and better debugging information: - tools.py: Added context-aware error messages for timeouts, network issues, and permission errors. Includes exception type information in logs while keeping user messages clear and actionable. - agent_creator.py: Replaced print statements with proper logging module usage. Added better error context and debugging information for JSON parsing failures. - utils.py: Improved WebSocket error handling with specific checks for connection and timeout issues, providing more informative warning messages. All changes maintain backward compatibility while significantly improving the developer and user experience when errors occur.
1 parent e96df07 commit 7d26af7

File tree

3 files changed

+76
-12
lines changed

3 files changed

+76
-12
lines changed

gpt_researcher/actions/agent_creator.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import json
22
import re
33
import json_repair
4+
import logging
45
from ..utils.llm import create_chat_completion
56
from ..prompts import PromptFamily
67

8+
logger = logging.getLogger(__name__)
9+
710
async def choose_agent(
811
query,
912
cfg,
@@ -57,18 +60,27 @@ async def handle_json_error(response):
5760
if agent_dict.get("server") and agent_dict.get("agent_role_prompt"):
5861
return agent_dict["server"], agent_dict["agent_role_prompt"]
5962
except Exception as e:
60-
print(f"⚠️ Error in reading JSON and failed to repair with json_repair: {e}")
61-
print(f"⚠️ LLM Response: `{response}`")
63+
error_type = type(e).__name__
64+
error_msg = str(e)
65+
logger.warning(
66+
f"Failed to parse agent JSON with json_repair: {error_type}: {error_msg}",
67+
exc_info=True
68+
)
69+
if response:
70+
logger.debug(f"LLM response that failed to parse: {response[:500]}...")
6271

6372
json_string = extract_json_with_regex(response)
6473
if json_string:
6574
try:
6675
json_data = json.loads(json_string)
6776
return json_data["server"], json_data["agent_role_prompt"]
6877
except json.JSONDecodeError as e:
69-
print(f"Error decoding JSON: {e}")
78+
logger.warning(
79+
f"Failed to decode JSON from regex extraction: {str(e)}",
80+
exc_info=True
81+
)
7082

71-
print("No JSON found in the string. Falling back to Default Agent.")
83+
logger.info("No valid JSON found in LLM response. Falling back to default agent.")
7284
return "Default Agent", (
7385
"You are an AI critical thinker research assistant. Your sole purpose is to write well written, "
7486
"critically acclaimed, objective and structured reports on given text."

gpt_researcher/actions/utils.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,17 @@ async def safe_send_json(websocket: Any, data: Dict[str, Any]) -> None:
4646
try:
4747
await websocket.send_json(data)
4848
except Exception as e:
49-
logger.error(f"Error sending JSON through WebSocket: {e}")
49+
error_type = type(e).__name__
50+
error_msg = str(e)
51+
logger.error(
52+
f"Error sending JSON through WebSocket: {error_type}: {error_msg}",
53+
exc_info=True
54+
)
55+
# Check for common WebSocket errors and provide helpful context
56+
if "closed" in error_msg.lower() or "connection" in error_msg.lower():
57+
logger.warning("WebSocket connection appears to be closed. Client may have disconnected.")
58+
elif "timeout" in error_msg.lower():
59+
logger.warning("WebSocket send operation timed out. The client may be unresponsive.")
5060

5161

5262
def calculate_cost(

gpt_researcher/utils/tools.py

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,21 @@ async def create_chat_completion_with_tools(
121121
tool_result = await tool(**tool_args) if asyncio.iscoroutinefunction(tool) else tool(**tool_args)
122122
break
123123
except Exception as e:
124-
logger.error(f"Error executing tool {tool_name}: {e}")
125-
tool_result = f"Tool error: {str(e)}"
124+
error_type = type(e).__name__
125+
error_msg = str(e)
126+
logger.error(
127+
f"Error executing tool '{tool_name}': {error_type}: {error_msg}",
128+
exc_info=True
129+
)
130+
# Provide user-friendly error message
131+
if "timeout" in error_msg.lower() or "timed out" in error_msg.lower():
132+
tool_result = f"Tool '{tool_name}' timed out. The operation took too long to complete. Please try again or check your network connection."
133+
elif "connection" in error_msg.lower() or "network" in error_msg.lower():
134+
tool_result = f"Tool '{tool_name}' failed due to a network issue. Please check your internet connection and try again."
135+
elif "permission" in error_msg.lower() or "access" in error_msg.lower():
136+
tool_result = f"Tool '{tool_name}' failed due to insufficient permissions. Please check your API keys or access credentials."
137+
else:
138+
tool_result = f"Tool '{tool_name}' encountered an error: {error_msg}. Please check the logs for more details."
126139

127140
# Add tool result to conversation
128141
tool_message = ToolMessage(content=str(tool_result), tool_call_id=tool_id)
@@ -159,7 +172,12 @@ async def create_chat_completion_with_tools(
159172
return response.content, []
160173

161174
except Exception as e:
162-
logger.error(f"Error in tool-enabled chat completion: {str(e)}")
175+
error_type = type(e).__name__
176+
error_msg = str(e)
177+
logger.error(
178+
f"Error in tool-enabled chat completion: {error_type}: {error_msg}",
179+
exc_info=True
180+
)
163181
logger.info("Falling back to simple chat completion without tools")
164182

165183
# Fallback to simple chat completion without tools
@@ -202,8 +220,21 @@ def search_tool(query: str) -> str:
202220
else:
203221
return f"No search results found for: {query}"
204222
except Exception as e:
205-
logger.error(f"Search tool error: {str(e)}")
206-
return f"Search error: {str(e)}"
223+
error_type = type(e).__name__
224+
error_msg = str(e)
225+
logger.error(
226+
f"Search tool error: {error_type}: {error_msg}",
227+
exc_info=True
228+
)
229+
# Provide context-aware error messages
230+
if "api" in error_msg.lower() or "key" in error_msg.lower():
231+
return f"Search failed: API key issue. Please verify your search API credentials are configured correctly."
232+
elif "timeout" in error_msg.lower() or "timed out" in error_msg.lower():
233+
return f"Search timed out. The search request took too long. Please try again with a different query."
234+
elif "rate limit" in error_msg.lower() or "quota" in error_msg.lower():
235+
return f"Search rate limit exceeded. Please wait a moment before trying again."
236+
else:
237+
return f"Search encountered an error: {error_msg}. Please check your search provider configuration."
207238

208239
return search_tool
209240

@@ -232,8 +263,19 @@ def custom_tool(*args, **kwargs) -> str:
232263
result = function(*args, **kwargs)
233264
return str(result) if result is not None else "Tool executed successfully"
234265
except Exception as e:
235-
logger.error(f"Custom tool '{name}' error: {str(e)}")
236-
return f"Tool error: {str(e)}"
266+
error_type = type(e).__name__
267+
error_msg = str(e)
268+
logger.error(
269+
f"Custom tool '{name}' error: {error_type}: {error_msg}",
270+
exc_info=True
271+
)
272+
# Provide informative error message without exposing internal details
273+
if "validation" in error_msg.lower() or "invalid" in error_msg.lower():
274+
return f"Tool '{name}' received invalid input. Please check the parameters and try again."
275+
elif "not found" in error_msg.lower() or "missing" in error_msg.lower():
276+
return f"Tool '{name}' could not find required resources. Please verify the input data is correct."
277+
else:
278+
return f"Tool '{name}' encountered an error: {error_msg}. Please check the tool configuration."
237279

238280
# Set tool metadata
239281
custom_tool.name = name

0 commit comments

Comments
 (0)