Coverage for projects/04-llm-adapter-shadow/src/llm_adapter/providers/mock.py: 94%
33 statements
« prev ^ index » next coverage.py v7.10.7, created at 2025-09-24 01:32 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2025-09-24 01:32 +0000
1"""Mock provider that can deterministically trigger failure modes."""
3from __future__ import annotations
5import random
6import time
7from collections.abc import Iterable
9from ..errors import AdapterError, RateLimitError, RetriableError, TimeoutError
10from ..provider_spi import ProviderRequest, ProviderResponse, ProviderSPI, TokenUsage
12ErrorSpec = tuple[type[AdapterError], str]
13_ERROR_BY_MARKER: dict[str, ErrorSpec] = {
14 "[TIMEOUT]": (TimeoutError, "simulated timeout"),
15 "[RATELIMIT]": (RateLimitError, "simulated rate limit"),
16 "[INVALID_JSON]": (RetriableError, "simulated invalid JSON"),
17}
20class MockProvider(ProviderSPI):
21 """Very small provider implementation for exercising the adapter."""
23 def __init__(
24 self,
25 name: str,
26 base_latency_ms: int = 50,
27 error_markers: Iterable[str] | None = None,
28 ) -> None:
29 self._name = name
30 self.base_latency_ms = base_latency_ms
31 if error_markers is None:
32 self._error_markers: set[str] = set(_ERROR_BY_MARKER)
33 else:
34 self._error_markers = {
35 marker for marker in error_markers if marker in _ERROR_BY_MARKER
36 }
38 def name(self) -> str:
39 return self._name
41 def capabilities(self) -> set[str]:
42 return {"chat"}
44 def _maybe_raise_error(self, text: str) -> None:
45 for marker in self._error_markers:
46 if marker in text:
47 exc_cls, message = _ERROR_BY_MARKER[marker]
48 raise exc_cls(message)
50 def invoke(self, request: ProviderRequest) -> ProviderResponse:
51 text = request.prompt
52 self._maybe_raise_error(text)
54 latency = self.base_latency_ms + int(random.random() * 20)
55 time.sleep(latency / 1000.0)
57 prompt_tokens = max(1, len(text) // 4)
58 completion_tokens = 16
60 return ProviderResponse(
61 text=f"echo({self._name}): {text}",
62 token_usage=TokenUsage(prompt=prompt_tokens, completion=completion_tokens),
63 latency_ms=latency,
64 )
67__all__ = ["MockProvider"]