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
@@ -24,11 +24,32 @@ async fn main() -> ExitCode {
|
||||
|
||||
/// Instantiates the configured LLM provider. The Anthropic key comes from
|
||||
/// the environment until the secret broker lands in P2.
|
||||
///
|
||||
/// The **subscription wins** when both credentials are present. This is the
|
||||
/// structural half of the fix that `cm_api::subscription` does per-call: a bare
|
||||
/// model name resolves to whatever this function returns, so making that the
|
||||
/// subscription means no server-side call can reach the metered key by
|
||||
/// accident — by construction, rather than by a source-grep test that has
|
||||
/// already missed four call sites once. The metered key stays usable as a
|
||||
/// fallback for deployments that have credit; ours does not, which is what
|
||||
/// made the ordering matter.
|
||||
fn build_provider(config: &AppConfig) -> Result<Arc<dyn LlmProvider>, String> {
|
||||
match config.llm.provider {
|
||||
LlmProviderKind::Anthropic => {
|
||||
let key = std::env::var("ANTHROPIC_API_KEY")
|
||||
.map_err(|_| "llm.provider = \"anthropic\" requires ANTHROPIC_API_KEY")?;
|
||||
if let Some(provider) = cm_api::subscription::provider() {
|
||||
println!(
|
||||
"clawmates-server: default LLM provider = Claude Code subscription \
|
||||
(bare model names bill no metered key)"
|
||||
);
|
||||
return Ok(Arc::new(provider));
|
||||
}
|
||||
let key = std::env::var("ANTHROPIC_API_KEY").map_err(|_| {
|
||||
"llm.provider = \"anthropic\" needs a credential: either \
|
||||
ANTHROPIC_OAUTH_TOKEN / CLAUDE_CODE_OAUTH_TOKEN (sk-ant-oat…, \
|
||||
the Claude Code subscription, preferred) or ANTHROPIC_API_KEY \
|
||||
(sk-ant-api…, metered)"
|
||||
.to_string()
|
||||
})?;
|
||||
// A subscription OAuth token pasted where an API key belongs
|
||||
// authenticates nothing here and fails on the first model call,
|
||||
// far from the mistake. Both start `sk-ant-`, so the confusion is
|
||||
@@ -40,6 +61,11 @@ fn build_provider(config: &AppConfig) -> Result<Arc<dyn LlmProvider>, String> {
|
||||
bearer auth and is what the phase evaluator reads."
|
||||
.to_string());
|
||||
}
|
||||
eprintln!(
|
||||
"clawmates-server: WARNING — no subscription token; the default LLM \
|
||||
provider is the METERED ANTHROPIC_API_KEY and every bare model name \
|
||||
bills it"
|
||||
);
|
||||
Ok(Arc::new(AnthropicProvider::new(key)))
|
||||
}
|
||||
LlmProviderKind::OpenAiCompat => {
|
||||
|
||||
Reference in New Issue
Block a user