feat(models): strong/cheap/vision model slots + multi-provider engine federation
Model slots wave (2026-09-20), chunk B:
- settings: new `models` section {strong, cheap, vision} slots (each
field independent, same shape as a role override); ROLE_SLOT maps all
nine roles to tiers (strong=prober+teacher, cheap=researcher/
demoBuilder/extractor/auditor/labeler, vision=visualMaker+pdfReader);
resolution chain per role: override → slot → factory pin.
- config: ModelSpec gains provider (all pins openrouter, the BYOK
default); MODEL_PINS in stores.ts now spans all nine roles (the strong
slot's factory rep IS the prober pin).
- engine: piModels() registers EVERY builtin provider catalog;
piEngineForSpec(resolved spec) is the new engine factory (pi-ai
resolves per-provider keys from env itself); piEngineFor stays as the
config-pin fallback. chat-handler resolves the conversation chain
(chat override → strong slot → pin) and injects engineFor into the job
runner, closing the old gap where per-role overrides never reached
job engines.
- /api/models: models federate ONLY keyed providers (providers surface
with keyPresent, env-wins, fresh per call); slots (resolved tier pins)
join rolePins; catalogDefaultsFor inherits the strong slot; the models
settings section gets the same catalog gate as chat.
- pricing: PRICES/CONTEXT_LIMITS uniformly keyed provider:id
(regenerated from ALL pi-ai catalogs, 1354 keys); usage stamps
(TurnUsage, JobUsage, extraction stages) switch to provider:id —
owner decision: no legacy compat, old stamps render unpriced.
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
* components never import from the server band).
|
||||
*/
|
||||
import type { ThinkingLevel } from "@earendil-works/pi-agent-core";
|
||||
import type { BackgroundModelRole } from "../settings.ts";
|
||||
import type { BackgroundModelRole, ModelSlotName } from "../settings.ts";
|
||||
|
||||
/** One picker row: everything the UI shows about a model, projected from
|
||||
* pi-ai's catalog. Costs are USD per 1M tokens (the same catalog, the same
|
||||
@@ -42,6 +42,12 @@ export type RolePins = Record<
|
||||
{ model: { provider: string; id: string }; thinkingLevel?: ThinkingLevel }
|
||||
>;
|
||||
|
||||
/** One slot tier's RESOLVED pin (model slots wave, 2026-09-20): the
|
||||
* strong/cheap/vision tier's concrete model — slot override falling back
|
||||
* to the factory rep pin. The settings UI captions each slot row with it
|
||||
* and the picker pre-fills from it. */
|
||||
export type SlotPins = Record<ModelSlotName, { model: { provider: string; id: string }; thinkingLevel?: ThinkingLevel }>;
|
||||
|
||||
/** GET /api/models response. */
|
||||
export interface ModelCatalogResponse {
|
||||
models: ModelCatalogEntry[];
|
||||
@@ -49,4 +55,23 @@ export interface ModelCatalogResponse {
|
||||
/** The six background roles' resolved pins (same resolution the agent
|
||||
* factories use — src/server/stores.ts effectiveSettings). */
|
||||
rolePins: RolePins;
|
||||
/** The three slot tiers' resolved pins (model slots wave, 2026-09-20):
|
||||
* same resolution chain — role override → slot → factory pin — reported
|
||||
* at tier granularity; effectiveSettings().models verbatim. */
|
||||
slots: SlotPins;
|
||||
/** Providers with a PRESENT key, and which env var gates each — the
|
||||
* model slots wave's federation surface (2026-09-20): the picker lists
|
||||
* models from these providers' pi-ai catalogs only. Keyless providers
|
||||
* are simply absent (the Keys page is the discovery surface, same
|
||||
* discipline as the search engines). */
|
||||
providers: CatalogProvider[];
|
||||
}
|
||||
|
||||
/** One row of the providers surface. */
|
||||
export interface CatalogProvider {
|
||||
id: string;
|
||||
/** The env var whose presence federates this provider in. */
|
||||
envVar: string;
|
||||
/** Whether that var is currently present (env or auth file). */
|
||||
keyPresent: boolean;
|
||||
}
|
||||
Reference in New Issue
Block a user