Spaces:
Runtime error
Runtime error
File size: 4,599 Bytes
fa77b99 2b13db0 7dcad13 2b13db0 7dcad13 2b13db0 fa77b99 2b13db0 fa77b99 7dcad13 2b13db0 7dcad13 2b13db0 7dcad13 2b13db0 fa77b99 2b13db0 7dcad13 fa77b99 2b13db0 7dcad13 2b13db0 fa77b99 2b13db0 7dcad13 2b13db0 7dcad13 2b13db0 7dcad13 2b13db0 fa77b99 7dcad13 2b13db0 fa77b99 7dcad13 2b13db0 fa77b99 7dcad13 2b13db0 7dcad13 2b13db0 7dcad13 2b13db0 fa77b99 7dcad13 fa77b99 7dcad13 fa77b99 2b13db0 7dcad13 2b13db0 7dcad13 2b13db0 fa77b99 7dcad13 2b13db0 7dcad13 2b13db0 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | """
FinSight β Domain Exception Hierarchy
========================================
Every layer raises a typed subclass so callers can handle specific
failure modes without fragile message string-matching.
Hierarchy:
FinSightError
βββ DataFetchError
β βββ TickerNotFoundError
β βββ RateLimitError
β βββ NetworkTimeoutError
βββ LLMError
β βββ LLMRateLimitError
β βββ ContextWindowExceededError
βββ RAGError
βββ DocumentLoadError
βββ DocumentParseError
βββ IndexNotFoundError
"""
from __future__ import annotations
class FinSightError(Exception):
"""Root exception for all FinSight domain errors."""
# ββ Data Layer ββββββββββββββββββββββββββββββββββββββββββββββββ
class DataFetchError(FinSightError):
"""A remote data source returned an unexpected or unrecoverable response."""
def __init__(self, message: str, ticker: str | None = None) -> None:
self.ticker = ticker
super().__init__(message)
class TickerNotFoundError(DataFetchError):
"""yfinance returned empty/invalid data for the requested ticker."""
def __init__(self, ticker: str) -> None:
self.ticker = ticker
super().__init__(
f"No market data found for '{ticker}'. "
"Verify the symbol and try again.",
ticker=ticker,
)
class RateLimitError(DataFetchError):
"""Yahoo Finance throttled the request (HTTP 429)."""
def __init__(self, ticker: str, retry_after: int = 60) -> None:
self.retry_after = retry_after
super().__init__(
f"Yahoo Finance rate limit hit for '{ticker}'. "
f"Wait ~{retry_after}s then retry.",
ticker=ticker,
)
class NetworkTimeoutError(DataFetchError):
"""A market-data request exceeded the configured timeout."""
def __init__(self, ticker: str, timeout_seconds: float) -> None:
self.timeout_seconds = timeout_seconds
super().__init__(
f"Request timed out after {timeout_seconds}s for '{ticker}'.",
ticker=ticker,
)
# ββ LLM Layer βββββββββββββββββββββββββββββββββββββββββββββββββ
class LLMError(FinSightError):
"""Claude API call failed after all retry attempts."""
def __init__(self, message: str, status_code: int | None = None) -> None:
self.status_code = status_code
super().__init__(message)
class LLMRateLimitError(LLMError):
"""Anthropic API returned HTTP 429 after exhausting retries."""
def __init__(self, retry_after: int = 60) -> None:
self.retry_after = retry_after
super().__init__(
f"Anthropic API rate limit exceeded. Retry after {retry_after}s.",
status_code=429,
)
class ContextWindowExceededError(LLMError):
"""Constructed prompt exceeds the model's context window."""
def __init__(self, estimated_tokens: int | None = None) -> None:
self.estimated_tokens = estimated_tokens
msg = "Prompt exceeds model context window."
if estimated_tokens:
msg += f" Estimated tokens: {estimated_tokens:,}."
super().__init__(msg)
# ββ RAG Layer βββββββββββββββββββββββββββββββββββββββββββββββββ
class RAGError(FinSightError):
"""Base exception for RAG pipeline failures."""
class DocumentLoadError(RAGError):
"""PDF cannot be opened or read from disk."""
def __init__(self, path: str, reason: str) -> None:
self.path = path
self.reason = reason
super().__init__(f"Cannot load '{path}': {reason}")
class DocumentParseError(RAGError):
"""pypdf opened the file but extracted zero usable text (image-only PDF)."""
def __init__(self, path: str, page_count: int) -> None:
self.path = path
self.page_count = page_count
super().__init__(
f"No text extracted from '{path}' ({page_count} pages). "
"The PDF may be image-only (requires OCR)."
)
class IndexNotFoundError(RAGError):
"""Retrieval attempted before any documents have been ingested."""
def __init__(self) -> None:
super().__init__(
"No documents indexed yet. Upload a PDF on the Document Q&A page."
)
|