File size: 2,378 Bytes
6bcf4a2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""Typed error hierarchy.

Every failure mode in the platform maps to one of these. The API layer
translates them into HTTP responses in a single place, so handlers never
construct status codes by hand.
"""

from __future__ import annotations


class SecRagError(Exception):
    """Base class for every error raised by this package."""

    status_code: int = 500
    code: str = "internal_error"

    def __init__(self, message: str, *, detail: object | None = None) -> None:
        super().__init__(message)
        self.message = message
        self.detail = detail

    def to_dict(self) -> dict[str, object]:
        payload: dict[str, object] = {"code": self.code, "message": self.message}
        if self.detail is not None:
            payload["detail"] = self.detail
        return payload


class ConfigurationError(SecRagError):
    """Settings are missing or mutually inconsistent."""

    status_code = 500
    code = "configuration_error"


class ProviderError(SecRagError):
    """An upstream LLM provider failed."""

    status_code = 502
    code = "provider_error"


class AllProvidersFailedError(ProviderError):
    """Every provider in the fallback chain failed."""

    code = "all_providers_failed"


class RateLimitError(ProviderError):
    """Provider rejected the request because of rate limiting."""

    status_code = 429
    code = "rate_limited"


class IngestionError(SecRagError):
    """A document could not be fetched or parsed."""

    status_code = 422
    code = "ingestion_error"


class EdgarError(IngestionError):
    """EDGAR rejected or could not satisfy the request."""

    code = "edgar_error"


class RetrievalError(SecRagError):
    """The retrieval stage failed."""

    status_code = 500
    code = "retrieval_error"


class IndexNotReadyError(RetrievalError):
    """A query arrived before any documents were indexed."""

    status_code = 409
    code = "index_not_ready"


class GuardrailTrippedError(SecRagError):
    """A guardrail blocked the request or the response.

    This is not a bug. It is the system correctly refusing to answer, and the
    API surfaces it as a normal response rather than an error.
    """

    status_code = 200
    code = "guardrail_tripped"


class EvaluationError(SecRagError):
    """The evaluation harness could not complete a run."""

    status_code = 500
    code = "evaluation_error"