Skip to content

Commit d749d2d

Browse files
authored
Merge pull request #1562 from Bolajiadesina/improve/error-handling
feat: improve error handling with better messages and logging
2 parents e96df07 + 7d26af7 commit d749d2d

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)