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:
@@ -48,7 +48,7 @@ describe("activity dock pure layer", () => {
|
||||
it("folds cost with jobCost; absent usage is null, never zero", () => {
|
||||
const priced = job({
|
||||
usage: {
|
||||
model: "z-ai/glm-5.3-flash",
|
||||
model: "openrouter:z-ai/glm-5.3-flash",
|
||||
inputTokens: 10_000,
|
||||
outputTokens: 1000,
|
||||
cacheReadTokens: 5000,
|
||||
@@ -67,7 +67,7 @@ describe("activity dock pure layer", () => {
|
||||
|
||||
it("context line = LAST step's tokens vs the model limit, never a cumulative sum", () => {
|
||||
const base = {
|
||||
model: "z-ai/glm-5.3-flash",
|
||||
model: "openrouter:z-ai/glm-5.3-flash",
|
||||
inputTokens: 400_000, // running total (much bigger — must NOT feed the line)
|
||||
outputTokens: 8_000,
|
||||
cacheReadTokens: 0,
|
||||
@@ -166,7 +166,7 @@ describe("activity section render", () => {
|
||||
|
||||
it("shows total running cost when jobs are priced", () => {
|
||||
const usage = {
|
||||
model: "z-ai/glm-5.3-flash",
|
||||
model: "openrouter:z-ai/glm-5.3-flash",
|
||||
inputTokens: 10_000,
|
||||
outputTokens: 1000,
|
||||
cacheReadTokens: 0,
|
||||
|
||||
Reference in New Issue
Block a user