dcd004289f
Phase 1 of the majordomo build: - llm/ canonical contract (messages, parts, tools, capabilities, streaming, Model/Provider, error classification) - health/ clock-injected tracker (threshold bench, exponential capped cooldown, reset-on-success) - root Registry + Parse (verbatim model ids, inline recursive alias expansion with cycle detection, chain dedup), LLM_* env-DSN providers (go-llm parity: lazy fallback + eager LoadEnv), health-aware chain executor behind the Model interface - provider/fake scriptable test provider; hermetic test suite incl. the trailing-thinking chain and foreman:// env loading - ADRs 0001-0008, CLAUDE.md, README (honest matrix), CI workflow, docs/phase-1-design.md Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
58 lines
2.6 KiB
Markdown
58 lines
2.6 KiB
Markdown
# ADR-0003: Parse grammar — verbatim model ids, inline alias expansion, chains
|
|
|
|
**Status:** Accepted — 2026-06-10
|
|
|
|
## Context
|
|
|
|
Callers (mort first) address models by string: single targets, tier aliases,
|
|
and comma-separated failover chains, with custom and env-defined providers as
|
|
first-class elements. go-llm's grammar is close but nests alias-chains as
|
|
composite Models and strips `:low/:medium/:high` reasoning suffixes, which
|
|
collides with Ollama-style tags (`minimax-m3:cloud`) and Google-style ids.
|
|
|
|
## Decision
|
|
|
|
Grammar (binding, from the kickoff):
|
|
|
|
```
|
|
spec := element ("," element)*
|
|
element := target | alias
|
|
target := provider "/" model # model = everything after the FIRST "/",
|
|
# up to the next comma, passed VERBATIM
|
|
alias := bare token, no slash
|
|
```
|
|
|
|
- Provider resolution order per target: registered providers (built-ins,
|
|
RegisterProvider, eagerly env-loaded) → lazy `LLM_{UPPER(name)}` env DSN
|
|
(ADR-0004) → error naming both places checked.
|
|
- Aliases expand **inline** wherever they appear (head/middle/tail),
|
|
recursively, into the flat element list. Cycles are detected via the
|
|
expansion stack and return `ErrAliasCycle` — never a hang. Inline (not
|
|
nested-Model, as in go-llm) expansion keeps one flat chain so health
|
|
skipping and error reporting see every element uniformly.
|
|
- Duplicate elements after expansion are dropped (first occurrence wins):
|
|
retrying an already-failed target in the same pass is never useful.
|
|
- A single element and a multi-element chain return the same `Model`
|
|
(a chain of one) — identical retry/health semantics, callers never branch.
|
|
- **No reasoning-suffix stripping.** mort's `:high` dialect is handled by
|
|
mort's spec layer during migration; majordomo will expose reasoning effort
|
|
as an explicit request option instead.
|
|
- The package-level `Default()` registry (lazy, loads process env) backs
|
|
`majordomo.Parse` for go-llm-style one-call ergonomics; `New()` builds
|
|
isolated registries for tests/multi-tenant use.
|
|
|
|
## Consequences
|
|
|
|
- `m1/richardyoung/qwen3-14b-abliterated:q4_K_M` (a real mort tier value)
|
|
parses as provider `m1`, model `richardyoung/qwen3-14b-abliterated:q4_K_M`.
|
|
- A bare token that is a provider name yields a targeted error
|
|
("use openai/<model-id>").
|
|
- Alias updates after Parse don't affect already-built Models (expansion is
|
|
at Parse time). mort re-parses per request, so DB-tier edits still apply.
|
|
|
|
## Alternatives considered
|
|
|
|
- Nested alias expansion (go-llm): opaque chains inside chains; health
|
|
skipping can't see the elements. Rejected.
|
|
- Reasoning suffixes in the grammar: breaks verbatim ids. Rejected.
|