ClearCast-AI / agent /errors.py
abhinavvathadi's picture
Upgrade ClearCast: Docker Space with Node.js gateway, FastAPI orchestrator, validated plans (GitHub 84b0cf9)
79c4c46 verified
Raw History Blame Contribute Delete
3.48 kB
"""Error taxonomy shared by the agent service, the internal API, and the UI.
Every error carries a stable ``code``, an HTTP status for the internal API, and
a message that is safe to show to users (no keys, URLs, or prompt content).
"""
from __future__ import annotations
import asyncio
import httpx
class ClearCastError(Exception):
"""Base error with a stable code and a client-safe message."""
code = "internal_error"
http_status = 500
retryable = False
def __init__(
self,
message: str,
*,
code: str | None = None,
http_status: int | None = None,
retryable: bool | None = None,
) -> None:
super().__init__(message)
self.message = message
if code is not None:
self.code = code
if http_status is not None:
self.http_status = http_status
if retryable is not None:
self.retryable = retryable
class ServiceUnavailableError(ClearCastError):
code = "service_unavailable"
http_status = 503
retryable = True
class ConfigurationError(ServiceUnavailableError):
code = "configuration_missing"
retryable = False
class ToolServiceUnavailableError(ServiceUnavailableError):
"""The MCP weather tool subprocess could not be started or reached."""
code = "tool_service_unavailable"
class ModelProviderError(ClearCastError):
code = "model_provider_error"
http_status = 502
class SessionBusyError(ClearCastError):
code = "session_busy"
http_status = 429
retryable = True
class PlanNotFoundError(ClearCastError):
code = "plan_not_found"
http_status = 404
class ReviewConflictError(ClearCastError):
code = "review_conflict"
http_status = 409
def classify_model_exception(exc: BaseException) -> ModelProviderError | None:
"""Map OpenAI/httpx/timeout failures to a categorised, client-safe error."""
import openai
if isinstance(exc, openai.APITimeoutError | httpx.TimeoutException | asyncio.TimeoutError):
return ModelProviderError(
"The language model did not respond in time.",
code="model_timeout",
http_status=504,
retryable=True,
)
if isinstance(exc, openai.RateLimitError):
return ModelProviderError(
"The language model provider is rate limiting requests.",
code="model_rate_limited",
http_status=503,
retryable=True,
)
if isinstance(exc, openai.AuthenticationError | openai.PermissionDeniedError):
return ModelProviderError(
"The language model provider rejected the configured credentials.",
code="model_auth_error",
)
if isinstance(exc, openai.APIConnectionError):
return ModelProviderError(
"The language model provider could not be reached.",
code="model_unavailable",
retryable=True,
)
if isinstance(exc, openai.BadRequestError):
return ModelProviderError("The language model provider rejected the request.", code="model_bad_request")
if isinstance(exc, openai.APIStatusError):
return ModelProviderError(
f"The language model provider returned an error (HTTP {exc.status_code}).",
retryable=exc.status_code >= 500,
)
return None