174 lines
4.6 KiB
Python
174 lines
4.6 KiB
Python
"""
|
|
User-Facing Error Messages.
|
|
|
|
This module provides functions for formatting user-friendly error messages,
|
|
particularly for rate limit errors.
|
|
|
|
Functions:
|
|
format_rate_limit_error: Format a rate limit error for user display
|
|
format_error_with_partial_save: Format error with partial save location
|
|
format_retry_time: Format retry time in human-readable format
|
|
print_user_error: Print error to console in user-friendly format
|
|
"""
|
|
|
|
from typing import Optional
|
|
|
|
from tradingagents.utils.exceptions import LLMRateLimitError
|
|
|
|
try:
|
|
from rich.console import Console
|
|
from rich.panel import Panel
|
|
RICH_AVAILABLE = True
|
|
except ImportError:
|
|
RICH_AVAILABLE = False
|
|
|
|
|
|
def format_rate_limit_error(error: LLMRateLimitError) -> str:
|
|
"""
|
|
Format a rate limit error for user display.
|
|
|
|
Creates a user-friendly message that includes:
|
|
- Provider name
|
|
- Retry guidance
|
|
- Retry time if available
|
|
|
|
Args:
|
|
error: LLMRateLimitError instance
|
|
|
|
Returns:
|
|
str: Formatted error message
|
|
|
|
Example:
|
|
>>> error = OpenAIRateLimitError("Rate limit exceeded", retry_after=60)
|
|
>>> format_rate_limit_error(error)
|
|
'Rate limit exceeded for OpenAI. Please retry in 60 seconds (1 minute).'
|
|
"""
|
|
provider_name = _format_provider_name(error.provider)
|
|
|
|
if error.retry_after is not None:
|
|
retry_time = format_retry_time(error.retry_after)
|
|
return (
|
|
f"Rate limit exceeded for {provider_name}. "
|
|
f"Please retry in {retry_time}."
|
|
)
|
|
else:
|
|
return (
|
|
f"Rate limit exceeded for {provider_name}. "
|
|
f"Please wait a moment and try again later."
|
|
)
|
|
|
|
|
|
def format_error_with_partial_save(error_message: str, partial_file: str) -> str:
|
|
"""
|
|
Format error message with information about saved partial analysis.
|
|
|
|
Args:
|
|
error_message: The error message
|
|
partial_file: Path to saved partial analysis file
|
|
|
|
Returns:
|
|
str: Formatted message
|
|
|
|
Example:
|
|
>>> format_error_with_partial_save(
|
|
... "Rate limit exceeded",
|
|
... "./results/partial_AAPL_20241226.json"
|
|
... )
|
|
'Rate limit exceeded\\n\\nPartial analysis saved to: ./results/partial_AAPL_20241226.json'
|
|
"""
|
|
return (
|
|
f"{error_message}\n\n"
|
|
f"Partial analysis saved to: {partial_file}\n"
|
|
f"You can inspect the partial results and retry when the rate limit resets."
|
|
)
|
|
|
|
|
|
def format_retry_time(seconds: int) -> str:
|
|
"""
|
|
Format retry time in human-readable format.
|
|
|
|
Converts seconds to appropriate units:
|
|
- < 60s: "X seconds"
|
|
- < 3600s: "X minutes (Y seconds)"
|
|
- >= 3600s: "X hours (Y minutes)"
|
|
|
|
Args:
|
|
seconds: Number of seconds
|
|
|
|
Returns:
|
|
str: Human-readable time format
|
|
|
|
Example:
|
|
>>> format_retry_time(60)
|
|
'1 minute (60 seconds)'
|
|
>>> format_retry_time(300)
|
|
'5 minutes (300 seconds)'
|
|
>>> format_retry_time(3600)
|
|
'1 hour (60 minutes)'
|
|
"""
|
|
if seconds < 60:
|
|
return f"{seconds} seconds"
|
|
|
|
minutes = seconds // 60
|
|
if minutes < 60:
|
|
return f"{minutes} minute{'s' if minutes != 1 else ''} ({seconds} seconds)"
|
|
|
|
hours = minutes // 60
|
|
remaining_minutes = minutes % 60
|
|
return f"{hours} hour{'s' if hours != 1 else ''} ({remaining_minutes} minutes)"
|
|
|
|
|
|
def print_user_error(error: LLMRateLimitError) -> None:
|
|
"""
|
|
Print error to console in user-friendly format.
|
|
|
|
Uses Rich Panel if available, otherwise falls back to simple print.
|
|
|
|
Args:
|
|
error: LLMRateLimitError instance
|
|
|
|
Example:
|
|
>>> error = OpenAIRateLimitError("Rate limit exceeded", retry_after=60)
|
|
>>> print_user_error(error)
|
|
# Displays formatted error panel in terminal
|
|
"""
|
|
message = format_rate_limit_error(error)
|
|
|
|
if RICH_AVAILABLE:
|
|
console = Console()
|
|
panel = Panel(
|
|
message,
|
|
title="[bold red]Rate Limit Error[/bold red]",
|
|
border_style="red",
|
|
)
|
|
console.print(panel)
|
|
else:
|
|
print(f"\n{'='*60}")
|
|
print(f"RATE LIMIT ERROR")
|
|
print(f"{'='*60}")
|
|
print(message)
|
|
print(f"{'='*60}\n")
|
|
|
|
|
|
def _format_provider_name(provider: Optional[str]) -> str:
|
|
"""
|
|
Format provider name for display.
|
|
|
|
Args:
|
|
provider: Provider identifier
|
|
|
|
Returns:
|
|
str: Formatted provider name
|
|
"""
|
|
if provider is None:
|
|
return "LLM provider"
|
|
|
|
# Capitalize provider names
|
|
provider_names = {
|
|
"openai": "OpenAI",
|
|
"anthropic": "Anthropic",
|
|
"openrouter": "OpenRouter",
|
|
}
|
|
|
|
return provider_names.get(provider.lower(), provider.title())
|