AMRYB's picture
Upload 91 files
287f3d3 verified
Raw
History Blame Contribute Delete
2.43 kB
"""LLM provider abstraction.
The entire agentic system depends on this interface instead of a concrete
provider, so the underlying model/service can be replaced without touching
any agent code.
"""
from __future__ import annotations
import inspect
from abc import ABC, abstractmethod
class LLMProviderError(Exception):
"""Raised when the underlying LLM provider fails (network, auth, etc.)."""
class LLMGenerationError(Exception):
"""Raised when the provider returns unusable output."""
class StructuredOutputError(Exception):
"""Raised when structured output cannot be parsed/validated."""
class LLMProvider(ABC):
"""Stateless interface to an LLM: given prompts, return assistant text.
An optional ``stats`` dict may be passed for telemetry; providers update it
in place (call_id, model, time-to-first-token, total duration, …).
"""
@abstractmethod
async def generate(
self, system_prompt: str, user_prompt: str, stats: dict | None = None
) -> str:
"""Generate a single assistant response as plain text."""
async def aclose(self) -> None:
"""Release any underlying resources."""
class FakeLLMProvider(LLMProvider):
"""In-memory provider for tests and offline demos."""
def __init__(self, responses: list[str] | None = None, handler=None):
self._responses = list(responses or [])
self._handler = handler
self.calls: list[tuple[str, str]] = []
def set_handler(self, handler) -> None:
self._handler = handler
def set_responses(self, responses: list[str]) -> None:
self._responses = list(responses)
async def generate(
self, system_prompt: str, user_prompt: str, stats: dict | None = None
) -> str:
self.calls.append((system_prompt, user_prompt))
if stats is not None:
stats.setdefault("call_id", "fake")
stats.setdefault("model", "fake")
stats["ttft_s"] = 0.0
stats["total_s"] = 0.0
if self._handler is not None:
result = self._handler(system_prompt, user_prompt)
if inspect.isawaitable(result):
result = await result
return result
if self._responses:
return self._responses.pop(0)
raise LLMProviderError("FakeLLMProvider has no response configured")