diff --git a/src/agent/context.rs b/src/agent/context.rs index 8dcc6a3..eeca48f 100644 --- a/src/agent/context.rs +++ b/src/agent/context.rs @@ -6,15 +6,8 @@ use crate::agent::types::*; use tiktoken_rs::CoreBPE; /// Look up a model's context window size in tokens. -pub fn model_context_window(model: &str) -> usize { - let m = model.to_lowercase(); - if m.contains("opus") || m.contains("sonnet") { - 200_000 - } else if m.contains("qwen") { - 131_072 - } else { - 128_000 - } +pub fn model_context_window(_model: &str) -> usize { + crate::config::get().api_context_window } /// Context budget in tokens: 60% of the model's context window. diff --git a/src/config.rs b/src/config.rs index 903e9a2..93e0393 100644 --- a/src/config.rs +++ b/src/config.rs @@ -53,6 +53,7 @@ pub struct ContextGroup { } fn default_true() -> bool { true } +fn default_context_window() -> usize { 128_000 } fn default_identity_dir() -> PathBuf { PathBuf::from(std::env::var("HOME").expect("HOME not set")).join(".consciousness/identity") } @@ -85,6 +86,8 @@ pub struct Config { pub api_key: Option, #[serde(skip)] pub api_model: Option, + #[serde(skip, default = "default_context_window")] + pub api_context_window: usize, /// Used to resolve API settings, not stored on Config #[serde(default)] agent_model: Option, @@ -134,6 +137,7 @@ impl Default for Config { api_base_url: None, api_key: None, api_model: None, + api_context_window: default_context_window(), agent_model: None, api_reasoning: "high".to_string(), agent_types: vec![ @@ -178,6 +182,9 @@ impl Config { .and_then(|v| v.as_str()).map(String::from); } config.api_model = Some(model_id.to_string()); + if let Some(cw) = model_cfg.get("context_window").and_then(|v| v.as_u64()) { + config.api_context_window = cw as usize; + } } Some(config) @@ -479,7 +486,7 @@ impl AppConfig { api_base = base; api_key = key; model = mdl; - prompt_file = if is_anthropic_model(&model) { + prompt_file = if self.backend == "anthropic" { self.prompts.anthropic.clone() } else { self.prompts.other.clone() @@ -546,7 +553,7 @@ impl AppConfig { let prompt_file = model.prompt_file.clone() .unwrap_or_else(|| { - if is_anthropic_model(&model.model_id) { + if model.backend == "anthropic" { self.prompts.anthropic.clone() } else { self.prompts.other.clone() @@ -651,11 +658,6 @@ pub fn reload_for_model(app: &AppConfig, prompt_file: &str) -> Result<(String, V Ok((system_prompt, context_parts)) } -fn is_anthropic_model(model: &str) -> bool { - let m = model.to_lowercase(); - m.contains("claude") || m.contains("opus") || m.contains("sonnet") -} - pub fn show_config(app: &AppConfig, figment: &Figment) { fn mask(key: &str) -> String { if key.is_empty() { "(not set)".into() } diff --git a/src/subconscious/defs.rs b/src/subconscious/defs.rs index e05b870..7f49c9f 100644 --- a/src/subconscious/defs.rs +++ b/src/subconscious/defs.rs @@ -39,7 +39,6 @@ pub struct AgentDef { /// Steps — single-step agents have one entry, multi-step have several. /// Steps are separated by `=== PROMPT ===` in the .agent file. pub steps: Vec, - pub model: String, pub schedule: String, pub tools: Vec, pub count: Option, @@ -60,8 +59,6 @@ struct AgentHeader { agent: String, #[serde(default)] query: String, - #[serde(default = "default_model")] - model: String, #[serde(default)] schedule: String, #[serde(default)] @@ -87,7 +84,6 @@ struct AgentHeader { bail: Option, } -fn default_model() -> String { "sonnet".into() } fn default_priority() -> i32 { 10 } /// Parse an agent file: first line is JSON config, rest is the prompt(s). @@ -149,7 +145,6 @@ fn parse_agent_file(content: &str) -> Option { agent: header.agent, query: header.query, steps, - model: header.model, schedule: header.schedule, tools: header.tools, count: header.count, diff --git a/src/subconscious/knowledge.rs b/src/subconscious/knowledge.rs index 0ba6b3d..4414ac9 100644 --- a/src/subconscious/knowledge.rs +++ b/src/subconscious/knowledge.rs @@ -340,8 +340,8 @@ fn run_one_agent_inner( }; let phases: Vec<&str> = agent_batch.steps.iter().map(|s| s.phase.as_str()).collect(); - log(&format!("{} step(s) {:?}, {}KB initial, model={}, {}, {} nodes, output={}", - n_steps, phases, first_len / 1024, def.model, tools_desc, + log(&format!("{} step(s) {:?}, {}KB initial, {}, {} nodes, output={}", + n_steps, phases, first_len / 1024, tools_desc, agent_batch.node_keys.len(), state_dir.display())); let prompts: Vec = agent_batch.steps.iter() diff --git a/src/thought/context.rs b/src/thought/context.rs index 1d2d44c..e5746b5 100644 --- a/src/thought/context.rs +++ b/src/thought/context.rs @@ -11,15 +11,8 @@ use chrono::{DateTime, Utc}; use tiktoken_rs::CoreBPE; /// Look up a model's context window size in tokens. -pub fn model_context_window(model: &str) -> usize { - let m = model.to_lowercase(); - if m.contains("opus") || m.contains("sonnet") { - 200_000 - } else if m.contains("qwen") { - 131_072 - } else { - 128_000 - } +pub fn model_context_window(_model: &str) -> usize { + crate::config::get().api_context_window } /// Context budget in tokens: 60% of the model's context window.