silvia-1-1 in chat-turn.ts: integration checklist
Goal: a chat turn whoseeffectiveModel is silvia-1-1 runs on our vLLM
endpoint through the adapter in lib/inference/silvia-chat/, and the browser
sees the same SSE stream it sees today. Every branch below keys on
isSilviaChatModel(model) from lib/inference/silvia-chat/model.ts.
Line numbers are for lib/claude/chat-turn.ts on branch
silvia-orch/plumbing at commit c15186711d (6,849 lines). Re-check them
after rebasing.
Already done outside chat-turn.ts (plumbing lane)
Checklist for chat-turn.ts
Each item: lines, what is there, what to do.-
Imports (top of file, near 71-97). Add
isSilviaChatModel,SILVIA_CAPABILITIESfrom@/lib/inference/silvia-chat/model, the stream factory from@/lib/inference/silvia-chat/create-stream(stream lane), andmodelContextTokenLimitsfrom@/lib/router/model-router. -
Stream creation,
createAndConsumeStreamWithFallback1791-1797. Today:usedBedrock ? createBedrockStream(...) : createStream(streamParams, betaFlags). Add a first branch:!usedBedrock && isSilviaChatModel(currentModel)-> the silvia stream factory withstreamParams(it drops betas, cache markers, thinking, context_management, speed, container itself). The returnedSilviaMessageStreammust be accepted byconsumeStream(1431): widen itsstreamparam type toReturnType<typeof anthropic.messages.stream> | SilviaMessageStream, or cast at the call site. consumeStream itself needs no other change. -
Fallback model,
createAndConsumeStreamWithFallback1740-1766 and the call site 4964-4971. TodayfallbackModelisDEFAULT_LLM_CONFIG.fallbackModel(claude-sonnet-5). For a silvia turn passfallbackModel: DEFAULT_LLM_CONFIG.model(Opus 4.8), so themodel_fallbackpath lands on Opus.streamParamswere built for silvia (no thinking, no betas), which is a valid Opus request, so the fallback runs Opus with thinking off. That is acceptable for v1. If you want thinking on the fallback: addfallbackThinkingSupportedtoStreamWithFallbackConfig(290-307), use it in the re-clamp at 1752-1756, and when it is true setstreamParams.thinking/output_configfromgetThinkingConfig(...)(the existing code at 1759 only rewritesoutput_configif it already exists, so it does nothing for a silvia turn). -
Retry count for silvia, 1729-1737 and 4964. Default
maxRetries: 2means three silvia attempts (1s, 2s backoff) before Opus, and this repeats on every loop becausestreamParams.modelis rebuilt fromeffectiveModelat 4784 each iteration. PassmaxRetries: 0(or 1) for silvia, and after a loop returnsusedFallback === truewith a silviaeffectiveModel, seteffectiveModel = DEFAULT_LLM_CONFIG.modelfor the rest of the turn (after 5030). Persist that through the checkpoint (6171) so resumed slices stay on Opus. -
Error classification,
isRetryableError386-443 andisTransientConnectionError331-371. Only 429/500/529 (or the matchingtype) are retryable, and a connection error must have nostatus.SilviaStreamErrorwithtype: 'timeout_error'and a numeric status is neither, so the turn fails with no fallback. Either the stream lane emits timeouts withname: 'APIConnectionTimeoutError'and nostatus(then 1857 fails over to Bedrock Opus once), or addisSilviaChatModel(currentModel) && error?.type === 'timeout_error'as retryable here. Same question forinvalid_request_error400 (context overflow): today it throws immediately at 2000-2003. For a silvia turn, treat it as fallback-worthy, since Opus has a far bigger window. -
Token counting and truncation, 4432-4539. Replace the two constants at
4433 and 4435 with
modelContextTokenLimits(effectiveModel). ThecountTokenscalls at 4440-4453 and 4492-4494 sendmodel: effectiveModel; Anthropic rejectssilvia-1-1, the catch at 4530 logs and moves on, so truncation never runs for silvia. Usemodel: isSilviaChatModel(effectiveModel) ? DEFAULT_LLM_CONFIG.model : effectiveModelintokenCountParams(Claude’s tokenizer is a proxy; the 10% margin inmodelContextTokenLimitscovers the gap, andcontext-budget.tsstill does the final fit at the wire). -
Static turn config,
deriveTurnStaticConfig2404-2850.- 2810
cacheableSystem, 277-290getCacheableTools, and the message markers at 4789-4791: leave as is, the adapter dropscache_control. Optional: skipapplyMessageCacheControlwhen silvia to keep params clean. - 2813
isFastMode: forcefalsewhenisSilviaChatModel(effectiveModel). The router never picks silvia for a fast-mode request (suppressed), but a resumed slice or a future pin could; also stops thefast_modeSSE at 4307-4318. - 2819-2827 thinking: nothing to do (item in the table above).
- 2831-2834 skills: set
skillsEnabled = falsefor silvia. Thecode_execution_20250825server tool (app/api/claude/skills.ts) and thecontainer(4889-4891) are Anthropic-hosted; silvia cannot run them. Check whether the system prompt then still describes skills.
- 2810
-
Per-loop request build, 4782-4896.
- 4797-4846
context_management: wrap the whole block inif (!isSilviaChatModel(effectiveModel)). Compaction is already gated off; client-sentclear_tool_usesedits (4826-4841) are not, and 4869 still addscontext-management-2025-06-27. The adapter drops them, so this is log hygiene, not correctness. - 4849-4896 betas: with items 7 and 8 done,
betaFlagsis empty for silvia. - 4874-4877 fast mode: covered by item 7.
- 4797-4846
-
Fallback notice, 5040-5070. The
model_fallbackSSE and PostHogMODEL_FALLBACKalready fire when silvia falls back; no change. The client ignores the event today. -
Routing, 3846-3899. No code change needed (see Router below).
effectiveModelcomes fromdecideChatModel, which now applies the internal-only gate. -
Usage and cost, 5473-5530 and 6456-6497. The adapter’s
finalMessage().usagemust fillinput_tokens/output_tokensand zero the cache fields; noiterations. Cost is priced byfinalModelUsed, already correct.cost_usdis nulled on any fallback turn (6476), as today. -
max_tokens, 4785 and 5584.
DEFAULT_LLM_CONFIG.maxTokensis 32,768; silvia reserves 8,192 (SILVIA_MAX_OUTPUT_TOKENS). The adapter clamps. A clamped reply that ends withstop_reason: 'max_tokens'and no tool call throwsmax_tokens_cutoffat 5584, as for Claude. -
Bedrock, 547-615 and 1768-1783, 1857-1876, 1883-1912. No change.
Bedrock is only reached after the Anthropic fallback, or on a connection
error, and
createBedrockStreamoverwritesmodelwith the Bedrock Opus id. -
Images, 497-515 and 3809. No change. Downscaling to 2,000 px is fine
for silvia (
SILVIA_CAPABILITIES.images: true).
Router: is the env var enough?
Yes, with conditions.CHAT_ROUTER_MODEL_SHORT=silvia-1-1 routes the short
class with no code change, but only when all of these hold
(lib/router/model-router.ts::decideChatModel):
CHAT_ROUTER_MODE=live. Inshadow(the code default) the model never changes. Confirm the prod value in Vercel before relying on it.- Confidence >=
CHAT_ROUTER_MIN_CONFIDENCE(default 0.65). - Not suppressed: research mode, fast mode (
body.speed === 'fast'), attachments or inlined files, video intent, or an explicitly requested model. Each keeps the baseline (Opus 4.8). - Demo mode never calls the router (chat-turn 3846).
silviaChatUsers() (lib/router/model-router.ts):
Jacob’s account by default, replaced by SILVIA_CHAT_USERS (comma list; empty
string turns silvia off). Those users run every turn on silvia-1-1 with no router:
no classifier and no suppressions (research, fast mode, attachments, video and
explicit models included). A silvia failure still falls back to Opus. A
CHAT_ROUTER_MODEL_<CLASS>=silvia-1-1 override never reaches anyone outside
the list, internal or not. No env change is needed for Jacob to use it.
Other readers of the model string (checked, no change needed)
lib/token-limit.ts/lib/token-limit-client.ts: the USD cap callsget_user_cost_in_window, which prices chat rows byCOALESCE(router_effective_model, model)throughnormalize_model_key. The new migration covers it. Note: a silvia turn that fell back to Opus is still priced as silvia (router_effective_model wins). Same rate today, so no billing difference.ai_token_usage_dailyrollup (compute_ai_token_usage_daily): same pricing path, covered.- PostHog
RESPONSE_COMPLETED(6453-6512):model_used,cost_usd,thinking_*,genui_*all correct with no change.normalize-genui-fencesdoes not branch on the model; it will report silvia’s GenUI slips, which is the signal we want. ai_chat_logs.thinking_supported/thinking(6420-6428):false/off.lib/router/log-decision.ts: logsrouter_effective_model = silvia-1-1.lib/fine-tuning-collector.ts: recordsmodelverbatim, no branching.lib/judge: judges with its own model (quant-analyst 1.0); does not read the chat model.lib/suggested-prompts.ts: no model branching.app/api/claude/internal/route.ts383/392: only checks Opus and Sonnet.
Risks
- Silent no-fallback on timeouts (item 5). Highest risk: a Modal cold start or a stalled vLLM request kills the turn instead of falling back.
- Retry cost per loop (item 4). With an unhealthy endpoint every loop pays three failed attempts plus backoff before Opus.
- Opus fallback runs without thinking (item 3) and without the 1M beta. Quality on fallback turns is below a normal Opus turn.
- Training on our own outputs. Fine-tuning traces from silvia turns land
in
fine_tuning_traceswithmodel = 'silvia-1-1'. Exclude them (or tag them) in any training cut, or the next model trains on the current one. - Judge self-preference. The chat judge is Silvia Analyst 1.0 (a Qwen fine-tune). Scores comparing silvia-1-1 against Opus may favor silvia. Compare within judge_model and spot-check with a Claude judge.
- Mixed history in one turn. If loop 1 falls back to Sonnet/Opus and
loop 2 returns to silvia, the history contains thinking blocks and possibly
server_tool_use. The request converter must drop those block types. - Pricing row only in prod.
model_pricing.quant-analystwas added by hand; the new migration inserts it only when missing. Without it,model_cost_usdreturns NULL for analyst and silvia rows. - Migration not applied. Until
20261013_02runs, SQL meters silvia-1-1 asopus. Same rate, so the cap is unaffected, but the model key in any per-model report is wrong.