blob: 4ca198873ad1e783e237959994b686a7d40eda50 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
"""Backend-agnostic model interface.
Concrete backends (Claude Code, Ollama, Anthropic API direct, OpenCode, etc.)
implement `ModelBackend.call`. The orchestration code only ever talks to this
interface, never to a specific backend.
Persona is NOT carried in ModelRequest; that's a backend-level configuration
concern (some backends autodiscover persona from a workspace, others need to
be told). What flows through here is per-turn DYNAMIC context only.
"""
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import Literal
Role = Literal["system", "user", "assistant", "tool"]
@dataclass
class Message:
role: Role
content: str
@dataclass
class ModelRequest:
"""Everything a backend needs to produce one response."""
messages: list[Message]
turn_context: str | None = None
extras: dict = field(default_factory=dict)
@dataclass
class ModelResponse:
"""What a backend returns. Kept narrow now, extended later."""
content: str
extras: dict = field(default_factory=dict)
class ModelBackend(ABC):
@abstractmethod
def call(self, request: ModelRequest) -> ModelResponse:
...
|