Spaces:
Running
Running
| """Exception hierarchy for FrontierAgent.""" | |
| from __future__ import annotations | |
| from typing import Any | |
| class FrontierAgentError(Exception): | |
| """Base exception for all FrontierAgent errors.""" | |
| # ββ Kernel errors βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| class KernelError(FrontierAgentError): | |
| """Errors originating from the OS kernel layer.""" | |
| class TaskNotFoundError(KernelError): | |
| def __init__(self, task_id: str) -> None: | |
| super().__init__(f"Task not found: {task_id}") | |
| self.task_id = task_id | |
| class InvalidStateTransition(KernelError): | |
| def __init__(self, task_id: str, current: str, target: str) -> None: | |
| super().__init__(f"Invalid transition for {task_id}: {current} β {target}") | |
| class ServiceNotRegistered(KernelError): | |
| def __init__(self, service_type: type) -> None: | |
| super().__init__(f"Service not registered: {service_type.__name__}") | |
| class PermissionDenied(KernelError): | |
| def __init__(self, role: str, tool: str) -> None: | |
| super().__init__(f"Role '{role}' has no permission for tool '{tool}'") | |
| # LLM request errors | |
| class LLMError(FrontierAgentError): | |
| """Errors from the LLM/provider layer.""" | |
| class LLMReasoningRunaway(LLMError): | |
| """A live stream spent its semantic budget on reasoning-only output. | |
| Unlike :class:`LLMStreamStalled`, the provider is healthy and actively | |
| emitting chunks. The failure is semantic: no non-whitespace visible text | |
| or tool-call delta appeared before the configured time/token guard fired. | |
| ``partial_response`` is intentionally carried separately from provider | |
| usage. Early stream cancellation often happens before the terminal usage | |
| chunk arrives, so its estimated reasoning tokens must never be presented | |
| as authoritative billing data. | |
| """ | |
| def __init__( | |
| self, | |
| *, | |
| elapsed_s: float, | |
| estimated_tokens: int, | |
| trigger: str, | |
| partial_response: Any, | |
| ) -> None: | |
| self.elapsed_s = float(elapsed_s) | |
| self.estimated_tokens = int(estimated_tokens) | |
| self.trigger = trigger | |
| self.partial_response = partial_response | |
| super().__init__( | |
| "reasoning-only stream exceeded " | |
| f"{trigger} guard (elapsed={self.elapsed_s:.1f}s, " | |
| f"estimated_tokens={self.estimated_tokens})", | |
| ) | |
| class LLMStreamStalled(LLMError, TimeoutError): | |
| """A streaming LLM call went silent mid-flight. | |
| Subclasses ``asyncio.TimeoutError`` so every existing transient- | |
| timeout handler (retry/backoff in ``call_llm``, chain wrappers, | |
| classification) treats it identically without changes; carried | |
| fields make the distinct failure mode visible in logs and traces. | |
| """ | |
| def __init__( | |
| self, stall_s: float, chunks_seen: int, elapsed_s: float, | |
| ) -> None: | |
| self.stall_s = stall_s | |
| self.chunks_seen = chunks_seen | |
| self.elapsed_s = elapsed_s | |
| super().__init__( | |
| f"stream stalled: no chunks for {stall_s:.0f}s " | |
| f"(chunks_seen={chunks_seen}, elapsed={elapsed_s:.0f}s)", | |
| ) | |
| class LLMCallExhausted(LLMError, RuntimeError): | |
| """Raised by ``call_llm`` when retries are exhausted or the error is | |
| structurally unrecoverable (4xx without proxy-wrap, or a chain-aware | |
| fallback signal like ``model_not_found``). | |
| Wraps the last exception encountered so the caller (typically | |
| ``run_agent_loop``) can surface it to a chain wrapper for leg | |
| rotation. Carries ``last_exc`` separately because ``raise from`` is | |
| too opaque for chain-aware classification β ``provider_chain`` calls | |
| ``classify_error(last_exc)`` directly. | |
| """ | |
| def __init__(self, last_exc: BaseException, reason: str) -> None: | |
| self.last_exc = last_exc | |
| self.reason = reason | |
| super().__init__(f"call_llm {reason}: {last_exc!r}") | |