package llm // effortLevels records which output_config.effort levels each model accepts. // It is a per-model table for the same reason prices and adaptiveThinking are: // the answer differs by model and a wrong one is not a rounding error. // claude-haiku-4-5 answers a request carrying any effort level with // // 400 invalid_request_error: This model does not support the effort parameter // // which kills the agent before its first turn — which is what a Haiku subagent // spawned from a session set to high effort did on every spawn. // // xhigh is not universal either: it arrived with Opus 4.7, so the 4.6 family // takes the other four and Opus 4.5 takes only the first three. // // Only ids confirmed to accept a level are listed, and an unlisted id — a // gateway serving something this build has never heard of, a family released // after it — is treated as taking none. The two mistakes are not symmetric: // sending effort to a model that lacks it is a hard 400 that ends the run // before any work happens, where omitting it costs the level and says so in // the status line. The recoverable failure is the one to default to, which is // the same reading SupportsAdaptiveThinking makes. var effortLevels = map[string][]string{ "claude-fable-5-1": EffortLevels, "claude-fable-5": EffortLevels, "claude-mythos-5-1": EffortLevels, "claude-mythos-5": EffortLevels, "claude-opus-5": EffortLevels, "claude-opus-4-8": EffortLevels, "claude-opus-4-7": EffortLevels, "claude-sonnet-5": EffortLevels, "claude-opus-4-6": {"low", "medium", "high", "max"}, "claude-sonnet-4-6": {"low", "medium", "high", "max"}, "claude-opus-4-5": {"low", "medium", "high"}, } // SupportsEffort reports whether model accepts this effort level. An empty // level is the API's own default and is always safe: it means the request // carries no output_config at all. func SupportsEffort(model, level string) bool { if level == "" { return true } for _, l := range effortLevels[basePrice(model)] { if l == level { return true } } return false }