Skip to content

Commit b0666cc

Browse files
committed
improve debug mode exception catching
1 parent 29a1eb7 commit b0666cc

File tree

1 file changed

+20
-50
lines changed

1 file changed

+20
-50
lines changed

n0mail/cli/chat_commands.py

Lines changed: 20 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from typing import Optional, List, Dict, Union, Iterator
66
import os # Import os for path handling
77
import json # For parsing tool arguments
8+
import traceback # Import traceback module
89
import re # Import re for email validation
910
from email.utils import parseaddr # Need this for parsing addresses
1011
from datetime import timezone # Ensure timezone is imported
@@ -46,30 +47,27 @@
4647
MAX_HISTORY_TURNS = 10 # Max user/assistant turns to keep in context (20 messages total)
4748
MAX_CONSECUTIVE_TOOL_CALLS = 5 # Limit for consecutive tool calls
4849

49-
# --- Tool Definitions for OpenAI Function Calling ---
5050

51-
# Removed tools definition list - moved to tools/definitions.py
52-
53-
# --- System Prompt (Adjusted for Tool Use) ---
51+
# --- System Prompt (Adjusted for Tool Use and Keyword Search) ---
5452
CHAT_SYSTEM_PROMPT = (
5553
"You are an AI assistant for the N0Mail application. Your primary goal is to help users find information within their emails.\n"
5654
"You have access to several tools:\n"
57-
"1. `retrieve_rag_context`: Use this for general questions about email content (e.g., 'What was discussed about project X?', 'Summarize recent important emails'). It performs a semantic search.\n"
58-
"2. `search_email_by_sender`: Use this *only* when you have a specific, complete email address to search for (e.g., '[email protected]').\n"
59-
"3. `find_email_address_based_on_name`: Use this *first* if the user asks about emails involving a person but only provides their name. This tool tries to find potential email addresses associated with that name.\n\n"
55+
"1. `retrieve_rag_context`: Performs a semantic search based on the *meaning* of the query. Use this for general questions, topic exploration, or finding conceptually related emails even if exact keywords don't match (e.g., 'What was discussed about project X?').\n"
56+
"2. `search_email_by_sender`: Searches for emails sent *from* a specific, complete email address. Use this ONLY when the user provides a full email address (e.g., '[email protected]') and asks for emails FROM that specific sender.\n"
57+
"3. `search_email_by_keyword`: Searches for emails containing *exact* keywords or phrases within the subject or body. Use this for finding specific terms, IDs, error codes, or quoted text. This tool does NOT understand semantics, only exact matches.\n"
58+
"4. `find_email_address_based_on_name`: Looks up potential email addresses associated with a person's name. Use this *before* calling `search_email_by_sender` if the user only provides a name.\n\n"
6059
"Tool Usage Workflow:\n"
61-
"- Analyze the user's request and decide if a tool is needed.\n"
62-
"- **CRITICAL: If the user asks about emails from/to a person by name ONLY (e.g., 'find emails from John Doe'), you MUST first call `find_email_address_based_on_name` with the provided name.**\n"
63-
"- **Consecutive Tool Calls:** You can now make multiple tool calls in sequence within a single turn to fulfill the user's request efficiently. For example:\n"
64-
" - After calling `find_email_address_based_on_name`:\n"
65-
" - If it returns a *single* email address, immediately proceed to call `search_email_by_sender` with that address in the *same turn*.\n"
66-
" - If it returns *multiple* potential addresses, STOP the tool sequence, present these options to the user, and ask them to confirm the correct one before potentially calling `search_email_by_sender` in the *next* turn.\n"
67-
" - If it returns 'No matching addresses found', STOP the tool sequence and inform the user.\n"
68-
"- Only call `search_email_by_sender` if you have a confirmed, complete email address.\n"
69-
"- **Limit:** You can make a maximum of 5 consecutive tool calls per user turn. Plan your tool usage accordingly.\n"
70-
"- **Goal:** Your aim after any sequence of tool calls is to provide a comprehensive final answer to the user based on the collected information. Only stop the sequence mid-way if user input is genuinely required (like choosing between multiple emails) or if you hit the call limit.\n"
71-
"- **Errors:** If a tool returns an error message (e.g., starts with 'Error:'), relay the error to the user politely and stop the current tool sequence.\n"
72-
"- **Handling No/Insufficient Results:** If `retrieve_rag_context` returns no relevant emails or the found emails don't contain the specific information requested, inform the user clearly (e.g., 'I searched your recent emails but couldn't find specific details about X. Right now I can only find the information that xxxxxx. Could you provide more context or rephrase your question?'). Don't invent information.\n"
60+
"- Analyze the user's request carefully to determine the most appropriate tool.\n"
61+
"- **Choosing the Right Search Tool:**\n"
62+
" - For general questions about topics, summaries, or concepts where exact wording isn't known or important, use `retrieve_rag_context`.\n"
63+
" - If the user provides specific keywords or phrases they want to find *exactly* within the email subject or body, use `search_email_by_keyword`.\n"
64+
" - If the user asks for emails specifically *from* a person AND provides their *full email address*, use `search_email_by_sender`.\n"
65+
"- **Handling Names vs. Emails:**\n"
66+
" - **CRITICAL:** If the user asks about emails involving a person but only provides their name (e.g., 'find emails from John Doe'), you MUST first call `find_email_address_based_on_name` to get potential email addresses.\n"
67+
"- **Consecutive Tool Calls:** You can make multiple tool calls in sequence (up to 5 max). For example, after `find_email_address_based_on_name` returns a single address, immediately call `search_email_by_sender` in the same turn. If multiple addresses are found, present them to the user first.\n"
68+
"- **Goal:** Aim to provide a comprehensive final answer after tool calls. Only stop a sequence mid-way if user input is required or the call limit is reached.\n"
69+
"- **Errors:** If a tool returns an error, inform the user politely and stop the sequence.\n"
70+
"- **Handling No/Insufficient Results:** If search tools return no relevant emails or insufficient information, clearly state that (e.g., 'I searched your emails but couldn't find specific details about X...'). Do not invent information.\n"
7371
"- If no tool seems necessary, answer directly based on the conversation history.\n"
7472
"- Keep your responses concise and helpful. Format using Markdown.\n"
7573
"- The initial message in the history is an email briefing for context."
@@ -115,37 +113,6 @@ def _prepare_initial_brief_data(brief_days: int, max_emails_brief: int) -> Optio
115113
console.print(f"[bold red]Error preparing initial brief data:[/bold red] {e}")
116114
return None
117115

118-
def _find_full_emails_by_ids(email_ids: List[str]) -> List[Dict]:
119-
"""Fetches full email details from SQLite database based on a list of IDs."""
120-
if not email_ids:
121-
return []
122-
123-
unique_ids = list(set(email_ids)) # Ensure IDs are unique
124-
console.print(f"[grey]Fetching full content for {len(unique_ids)} unique email IDs from database...[/grey]")
125-
126-
full_emails = []
127-
try:
128-
with database.get_db_connection() as conn:
129-
query = (
130-
select(
131-
models.emails_table.c.id, models.emails_table.c.subject,
132-
models.emails_table.c.sender, models.emails_table.c.date,
133-
models.emails_table.c.body_md # Get the full body
134-
).where(models.emails_table.c.id.in_(unique_ids))
135-
)
136-
results = conn.execute(query).mappings().fetchall()
137-
full_emails = [dict(row) for row in results]
138-
139-
if len(full_emails) < len(unique_ids):
140-
found_ids = {e['id'] for e in full_emails}
141-
missing_ids = [eid for eid in unique_ids if eid not in found_ids]
142-
console.print(f"[yellow]Warning: Could not find full details for {len(missing_ids)} email IDs in SQLite: {missing_ids}[/yellow]")
143-
144-
except Exception as e:
145-
console.print(f"[bold red]Error fetching full emails from SQLite:[/bold red] {e}")
146-
147-
return full_emails
148-
149116
def _format_emails_for_llm(email_list: List[Dict]) -> str:
150117
"""Formats a list of email dictionaries into a string for LLM context."""
151118
if not email_list:
@@ -640,6 +607,9 @@ def run_chat(
640607
break
641608
except Exception as api_e:
642609
console.print(f"\n[bold red]Error calling LLM API (iteration {consecutive_tool_calls + 1}):[/bold red] {api_e}")
610+
console.print("[bold red]--- Traceback Start ---[/bold red]")
611+
traceback.print_exc() # Explicitly print the full traceback
612+
console.print("[bold red]--- Traceback End ---[/bold red]")
643613
break
644614

645615
if response_message is None:

0 commit comments

Comments
 (0)