Ctrl K
ComunidadGuías…

soc_ai/errors.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
"""soc-ai error hierarchy.

The API layer in :mod:`soc_ai.api` maps these to HTTP responses; tools and the
agent loop raise them at the boundaries of the trust model.
"""

from __future__ import annotations


class SocAiError(Exception):
    """Root of the soc-ai error hierarchy."""


class SoApiError(SocAiError):
    """An error returned by the Security Onion HTTP API."""

    def __init__(
        self,
        message: str,
        *,
        status_code: int | None = None,
        url: str | None = None,
    ) -> None:
        super().__init__(message)
        self.status_code = status_code
        self.url = url


class SoAuthError(SoApiError):
    """Authentication to the SO grid failed (bad credentials, expired session)."""


class SoNotFoundError(SoApiError):
    """A requested SO resource (alert, case, detection) does not exist."""


class OqlValidationError(SocAiError):
    """The OQL parser/validator rejected a query before it reached Elasticsearch.

    Carries the offending fragment and a human-readable reason so the agent can
    self-correct in the next turn.
    """

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


class TimeBoundError(OqlValidationError):
    """An absolute time bound (``from`` or ``to``) is not an ISO 8601 timestamp.

    A subclass so every caller that already turns an OQL refusal into a 400
    keeps doing so; the API answers it with reason ``bad_time`` because the
    query itself was fine.
    """


class ModelError(SocAiError):
    """The LiteLLM gateway / underlying model returned an error or malformed output."""


class SyntheticAnchorError(SocAiError):
    """The alert a run is anchored to is a plant the run's own scope hides.

    Raised at the anchor fetch, before any pivot runs, because everything the
    run would gather about that alert is already excluded by the same guard.
    Continuing does not produce a weakly-evidenced answer; it produces a
    confident one built out of zeros the guard created. Measured on the range:
    fifteen tool calls, no results from any of them, an explicit statement that
    the account's purpose could not be independently verified, and a verdict of
    false positive at 0.60 on a critical detection whose group also held three
    genuine events.

    Deliberately not a :class:`SoNotFoundError`. The document exists and the
    grid can read it; this run cannot, and those are different sentences to
    whoever reads the run afterwards.
    """

    def __init__(self, message: str, *, alert_id: str, scenario_id: str) -> None:
        super().__init__(message)
        self.alert_id = alert_id
        self.scenario_id = scenario_id