feat(llm): the subscription is the default provider, with a recorded fallback chain
Two changes so an empty metered account stops being a platform outage. 1. `build_provider` prefers the subscription token over ANTHROPIC_API_KEY. A bare model name resolves to whatever this returns, so making it the subscription means no server-side call can reach the metered key by construction — rather than by a source-grep test that already missed four call sites once. The metered key remains a fallback and now warns loudly when it is the one in use; boot no longer requires it at all. 2. `complete_with_fallback` walks a declared chain when a model has no capacity: opus -> haiku -> glm:glm-4.7 by default, overridable via CLAWMATES_MODEL_FALLBACK, empty to disable. Measured on gw-04 today: opus and sonnet return 429 on the subscription while haiku, GLM and Kimi all return 200, so a capped window no longer means "the planner is gone". The chain returns the model that ANSWERED, and every caller persists it — mission_plan_proposals.author_model, mission_team_proposals.author_model, and the swarm's step role. A plan drafted by the third link and filed as an opus plan is a silent quality change, which is the failure shape this project keeps paying for. Two negative controls hold the design: the chain never retries the model that just failed as its own fallback, and it steps down ONLY for a capacity failure — walking it on a malformed prompt would ask three models the same bad question and report the third one's confusion. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5
parent
ee5a939ce6
commit
9c9439a271
@@ -174,7 +174,9 @@ pub async fn suggest(
|
||||
PLANNABLE_KINDS.join(", "),
|
||||
);
|
||||
|
||||
let raw = crate::subscription::complete_or(
|
||||
// The stored `author_model` is whichever link of the fallback chain
|
||||
// actually answered — see `subscription::complete_with_fallback`.
|
||||
let (raw, author_model) = crate::subscription::complete_with_fallback(
|
||||
&state.runtime,
|
||||
PLAN_SYSTEM,
|
||||
&prompt,
|
||||
@@ -212,7 +214,7 @@ pub async fn suggest(
|
||||
id,
|
||||
ws.as_uuid().to_owned(),
|
||||
&stored,
|
||||
PLANNER_MODEL,
|
||||
&author_model,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
@@ -220,7 +222,7 @@ pub async fn suggest(
|
||||
ApiError::Internal
|
||||
})?;
|
||||
eprintln!(
|
||||
"mission_plan: mission {id} — {PLANNER_MODEL} proposed {} phase(s): {}",
|
||||
"mission_plan: mission {id} — {author_model} proposed {} phase(s): {}",
|
||||
plan.phases.len(),
|
||||
plan.phases
|
||||
.iter()
|
||||
@@ -232,7 +234,7 @@ pub async fn suggest(
|
||||
Ok(Json(PlanProposalResponse {
|
||||
id: pid,
|
||||
plan: stored,
|
||||
author_model: PLANNER_MODEL.to_string(),
|
||||
author_model,
|
||||
status: "proposed".into(),
|
||||
}))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user