mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-27 06:15:24 +00:00
88 lines
5.5 KiB
Markdown
88 lines
5.5 KiB
Markdown
# Model selection at Fabro boundaries
|
|
|
|
Fabro uses Lithos `ModelSelection`, `SelectionPolicy`, `ResolvedRoute`, and
|
|
`ModelSelectionError`. Lithos owns parsing, provider defaults, aliases,
|
|
priority, wire IDs, and passthrough permissions. `fabro-llm::selection::policy()`
|
|
chooses exact provider matching and unknown-bare-model routing through the
|
|
default provider. Both still require enabled, available providers and their
|
|
`allow_passthrough` permission. Normal and offline/gateway clients install this
|
|
policy. The Petri client uses `workflow_policy()` during both admission and
|
|
execution: workflows retain their existing requirement that unknown models
|
|
have explicit provider scope (`UnknownModelPolicy::ExplicitProvider`).
|
|
Catalog-only consumers use `UnknownModelPolicy::CatalogOnly`.
|
|
|
|
Completions and `fabro exec` inspect the client's selection before building a
|
|
request, then use the selected route for dispatch (the CLI bridges a validated handle into
|
|
Pebble's string-only interface). No dummy conversation is needed
|
|
for selection. A separate provider field scopes the entire model value; never
|
|
join user input into `provider/model`. Gateway requests send separate provider
|
|
and model fields. CLI gateway clients register the transport for all locally
|
|
enabled providers; the server enforces its own readiness before dispatch. CLI
|
|
settings and explicit provider flags still scope selection; without them, the
|
|
CLI no longer invents an Anthropic pin. Model probes use the canonical handle's string representation
|
|
only because Lithos's probe API currently accepts a string selector.
|
|
|
|
## HTTP compatibility
|
|
|
|
Completions and Ask Fabro session creation share these rules:
|
|
|
|
- `provider: "gemini"` (or its alias `google`) with model omitted chooses the
|
|
Gemini default, including operator overlays. An unavailable provider errors.
|
|
Putting a provider-only name in `model` returns HTTP 400 with instructions to
|
|
use the provider field; it never selects a default or passes through.
|
|
- Both `openai/gpt-5.6-sol` and `openai:gpt-5.6-sol` are qualified selectors.
|
|
- With `provider: "openrouter"`, `model: "openai/gpt-5.6-sol"` is an opaque
|
|
OpenRouter identifier. `vendor/model:version` and `future-model:latest` also
|
|
remain whole. Supplying `model: "default"` with a provider means a literal
|
|
model named `default`; omit model to select the provider default.
|
|
- Genuine model names and aliases resolve as models even if a provider uses
|
|
the same name. Canonical model IDs beat aliases; Lithos owns priority and tie
|
|
breaking. Provider defaults are never inferred from model text.
|
|
- Unknown bare models may pass through on the default provider only when it
|
|
permits them. Disabled providers, unavailable pins, and known models with no
|
|
eligible offering never turn into wire IDs on an unrelated provider.
|
|
|
|
Sessions keep rejecting provider-only selectors through the shared resolver.
|
|
The separate provider field is authoritative rather than checking for conflicts
|
|
with a parsed prefix inside the model field. Sessions no longer
|
|
reinterpret an unavailable raw prefix as an aggregator's wire ID, or accept an
|
|
unknown colon prefix as an unqualified model. Supply the aggregator in the
|
|
`provider` field for those opaque IDs. Empty session model values remain errors.
|
|
|
|
## Session persistence and resume
|
|
|
|
New `run.session.created` events keep an optional `selection` alongside their
|
|
existing display provider/model fields. Catalog routes persist a `ModelHandle`
|
|
as a resolved selection; passthrough routes persist explicit provider scope and
|
|
the opaque wire ID. Projection retains the full selection. Before building or
|
|
resuming Pebble, Fabro resolves that stored intent through the client. Aliases
|
|
and changed defaults cannot redirect a catalog handle, and removing its row
|
|
fails rather than turning its slug into a passthrough model. The current catalog
|
|
still owns the wire ID; handles are not configuration snapshots.
|
|
|
|
Legacy creation events have no selection discriminator. A legacy provider/model
|
|
pair is interpreted conservatively as exact catalog identity. If the catalog
|
|
row is absent, including an old passthrough session, create a new session to
|
|
record explicit passthrough intent. No old event is rewritten. Stored Pebble
|
|
conversation identity must agree with the validated session selection.
|
|
|
|
Pebble still accepts a string model selector; Fabro bridges the validated
|
|
canonical handle into that interface. Petri owns workflow admission, fallback
|
|
plans, and workflow resume, including their serialized selector formats. Those
|
|
upstream formats are unchanged here; the client Fabro supplies now enforces the
|
|
same exact-provider and unknown-model policies as its preflight. The exact
|
|
persisted-handle guarantee above applies to Ask Fabro sessions: Petri still has selector-based
|
|
workflow resume and needs its own upstream migration for equivalent missing-row
|
|
protection. The old unused Fabro fallback resolver and fallback target type have been removed. No alternate catalog-wide
|
|
retry is performed on an unavailable pin.
|
|
|
|
## Temporary dependency
|
|
|
|
This adoption depends on unmerged
|
|
[lithos-llm PR #49](https://github.com/lithoscomputer/lithos-llm/pull/49), pinned at
|
|
`d8edab8d89315be363073dd0de0fe47089f6b557`. Cargo rejects a git patch to the same
|
|
source, so a temporary patch uses GitHub's public `www.github.com` HTTPS redirect
|
|
to unify Pebble and Petri with that revision. It requires no SSH credentials.
|
|
After verifying the upstream merge, remove the patch, restore `branch = "main"`,
|
|
and update the lockfile to a main revision containing the API. Do not ship two
|
|
Lithos package identities or silently update unrelated dependencies.
|