54 lines
1.5 KiB
Markdown
54 lines
1.5 KiB
Markdown
|
|
# Contract: RoutingPolicy
|
||
|
|
|
||
|
|
**layer:** Functional
|
||
|
|
**maturity:** Beta
|
||
|
|
**module:** `llm_connect.routing`
|
||
|
|
**since:** WP-0003
|
||
|
|
|
||
|
|
## Purpose
|
||
|
|
|
||
|
|
Route logical task types to concrete `LLMAdapter` instances based on a
|
||
|
|
prioritised rule list, with optional per-rule cost-cap fallback.
|
||
|
|
|
||
|
|
## Public surface
|
||
|
|
|
||
|
|
```python
|
||
|
|
@dataclass
|
||
|
|
class RoutingRule:
|
||
|
|
task_type: str
|
||
|
|
prefer: LLMAdapter
|
||
|
|
max_cost_per_1k: Optional[float] = None # USD per 1 000 tokens
|
||
|
|
fallback: Optional[LLMAdapter] = None
|
||
|
|
|
||
|
|
@dataclass
|
||
|
|
class RoutingPolicy:
|
||
|
|
rules: List[RoutingRule] = field(default_factory=list)
|
||
|
|
default: Optional[LLMAdapter] = None
|
||
|
|
|
||
|
|
def resolve(
|
||
|
|
self,
|
||
|
|
task_type: str,
|
||
|
|
estimated_cost_per_1k: Optional[float] = None,
|
||
|
|
) -> LLMAdapter: ...
|
||
|
|
```
|
||
|
|
|
||
|
|
## Invariants
|
||
|
|
|
||
|
|
1. Rules are evaluated in list order; the first rule whose `task_type` matches wins.
|
||
|
|
2. When `estimated_cost_per_1k` is supplied and a matching rule has `max_cost_per_1k` set:
|
||
|
|
- If `estimated_cost_per_1k > max_cost_per_1k` **and** `fallback is not None` → return `fallback`.
|
||
|
|
- Otherwise → return `prefer` (no fallback configured or cost within cap).
|
||
|
|
3. When no rule matches and `default is not None` → return `default`.
|
||
|
|
4. When no rule matches and `default is None` → raise `LookupError`.
|
||
|
|
5. `resolve()` never mutates policy state.
|
||
|
|
|
||
|
|
## Error contract
|
||
|
|
|
||
|
|
| Condition | Exception |
|
||
|
|
|-----------|-----------|
|
||
|
|
| No matching rule, no default | `LookupError` |
|
||
|
|
|
||
|
|
## Known consumers
|
||
|
|
|
||
|
|
- `inter-hub` (IHUB-WP-0012 Phase 11): uses `RoutingPolicy` to select federation adapters per task class.
|