Ok, well, that got out of hand (#3718)
Co-authored-by: Douwe Osinga <douwe@squareup.com> Co-authored-by: Michael Neale <michael.neale@gmail.com>
This commit is contained in:
+229
-206
@@ -1,64 +1,78 @@
|
||||
use once_cell::sync::Lazy;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use thiserror::Error;
|
||||
|
||||
const DEFAULT_CONTEXT_LIMIT: usize = 128_000;
|
||||
|
||||
// Define the model limits as a static HashMap for reuse
|
||||
static MODEL_SPECIFIC_LIMITS: Lazy<HashMap<&'static str, usize>> = Lazy::new(|| {
|
||||
let mut map = HashMap::new();
|
||||
// OpenAI models, https://platform.openai.com/docs/models#models-overview
|
||||
map.insert("gpt-4o", 128_000);
|
||||
map.insert("gpt-4-turbo", 128_000);
|
||||
map.insert("o3", 200_000);
|
||||
map.insert("o3-mini", 200_000);
|
||||
map.insert("o4-mini", 200_000);
|
||||
map.insert("gpt-4.1", 1_000_000);
|
||||
map.insert("gpt-4-1", 1_000_000);
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ConfigError {
|
||||
#[error("Environment variable '{0}' not found")]
|
||||
EnvVarMissing(String),
|
||||
#[error("Invalid value for '{0}': '{1}' - {2}")]
|
||||
InvalidValue(String, String, String),
|
||||
#[error("Value for '{0}' is out of valid range: {1}")]
|
||||
InvalidRange(String, String),
|
||||
}
|
||||
|
||||
// Anthropic models, https://docs.anthropic.com/en/docs/about-claude/models
|
||||
map.insert("claude", 200_000);
|
||||
|
||||
// Google models, https://ai.google/get-started/our-models/
|
||||
map.insert("gemini-2.5", 1_000_000);
|
||||
map.insert("gemini-2-5", 1_000_000);
|
||||
|
||||
// Meta Llama models, https://github.com/meta-llama/llama-models/tree/main?tab=readme-ov-file#llama-models-1
|
||||
map.insert("llama3.2", 128_000);
|
||||
map.insert("llama3.3", 128_000);
|
||||
|
||||
// x.ai Grok models, https://docs.x.ai/docs/overview
|
||||
map.insert("grok", 131_072);
|
||||
|
||||
// Groq models, https://console.groq.com/docs/models
|
||||
map.insert("gemma2-9b", 8_192);
|
||||
map.insert("kimi-k2", 131_072);
|
||||
map.insert("qwen3-32b", 131_072);
|
||||
map.insert("grok-3", 131_072);
|
||||
map.insert("grok-4", 256_000); // 256K
|
||||
map.insert("qwen3-coder", 262_144); // 262K
|
||||
|
||||
map
|
||||
static MODEL_SPECIFIC_LIMITS: Lazy<Vec<(&'static str, usize)>> = Lazy::new(|| {
|
||||
vec![
|
||||
// openai
|
||||
("gpt-4-turbo", 128_000),
|
||||
("gpt-4.1", 1_000_000),
|
||||
("gpt-4-1", 1_000_000),
|
||||
("gpt-4o", 128_000),
|
||||
("o4-mini", 200_000),
|
||||
("o3-mini", 200_000),
|
||||
("o3", 200_000),
|
||||
// anthropic - all 200k
|
||||
("claude", 200_000),
|
||||
// google
|
||||
("gemini-1", 128_000),
|
||||
("gemini-2", 1_000_000),
|
||||
("gemma-3-27b", 128_000),
|
||||
("gemma-3-12b", 128_000),
|
||||
("gemma-3-4b", 128_000),
|
||||
("gemma-3-1b", 32_000),
|
||||
("gemma3-27b", 128_000),
|
||||
("gemma3-12b", 128_000),
|
||||
("gemma3-4b", 128_000),
|
||||
("gemma3-1b", 32_000),
|
||||
("gemma-2-27b", 8_192),
|
||||
("gemma-2-9b", 8_192),
|
||||
("gemma-2-2b", 8_192),
|
||||
("gemma2-", 8_192),
|
||||
("gemma-7b", 8_192),
|
||||
("gemma-2b", 8_192),
|
||||
("gemma1", 8_192),
|
||||
("gemma", 8_192),
|
||||
// facebook
|
||||
("llama-2-1b", 32_000),
|
||||
("llama", 128_000),
|
||||
// qwen
|
||||
("qwen3-coder", 262_144),
|
||||
("qwen2-7b", 128_000),
|
||||
("qwen2-14b", 128_000),
|
||||
("qwen2-32b", 131_072),
|
||||
("qwen2-70b", 262_144),
|
||||
("qwen2", 128_000),
|
||||
("qwen3-32b", 131_072),
|
||||
// other
|
||||
("kimi-k2", 131_072),
|
||||
("grok-4", 256_000),
|
||||
("grok", 131_072),
|
||||
]
|
||||
});
|
||||
|
||||
/// Configuration for model-specific settings and limits
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ModelConfig {
|
||||
/// The name of the model to use
|
||||
pub model_name: String,
|
||||
/// Optional explicit context limit that overrides any defaults
|
||||
pub context_limit: Option<usize>,
|
||||
/// Optional temperature setting (0.0 - 1.0)
|
||||
pub temperature: Option<f32>,
|
||||
/// Optional maximum tokens to generate
|
||||
pub max_tokens: Option<i32>,
|
||||
/// Whether to interpret tool calls with toolshim
|
||||
pub toolshim: bool,
|
||||
/// Model to use for toolshim (optional as a default exists)
|
||||
pub toolshim_model: Option<String>,
|
||||
}
|
||||
|
||||
/// Struct to represent model pattern matches and their limits
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ModelLimitConfig {
|
||||
pub pattern: String,
|
||||
@@ -66,258 +80,267 @@ pub struct ModelLimitConfig {
|
||||
}
|
||||
|
||||
impl ModelConfig {
|
||||
/// Create a new ModelConfig with the specified model name
|
||||
///
|
||||
/// The context limit is set with the following precedence:
|
||||
/// 1. Explicit context_limit if provided in config
|
||||
/// 2. Environment variable override (GOOSE_CONTEXT_LIMIT)
|
||||
/// 3. Model-specific default based on model name
|
||||
/// 4. Global default (128_000) (in get_context_limit)
|
||||
pub fn new(model_name: String) -> Self {
|
||||
Self::new_with_context_env(model_name, None)
|
||||
pub fn new(model_name: &str) -> Result<Self, ConfigError> {
|
||||
Self::new_with_context_env(model_name.to_string(), None)
|
||||
}
|
||||
|
||||
/// Create a new ModelConfig with the specified model name and custom context limit env var
|
||||
///
|
||||
/// This is useful for specific model purposes like lead, worker, planner models
|
||||
/// that may have their own context limit environment variables.
|
||||
pub fn new_with_context_env(model_name: String, context_env_var: Option<&str>) -> Self {
|
||||
let context_limit = Self::get_context_limit_with_env_override(&model_name, context_env_var);
|
||||
pub fn new_with_context_env(
|
||||
model_name: String,
|
||||
context_env_var: Option<&str>,
|
||||
) -> Result<Self, ConfigError> {
|
||||
let context_limit = Self::parse_context_limit(&model_name, context_env_var)?;
|
||||
let temperature = Self::parse_temperature()?;
|
||||
let toolshim = Self::parse_toolshim()?;
|
||||
let toolshim_model = Self::parse_toolshim_model()?;
|
||||
|
||||
let toolshim = std::env::var("GOOSE_TOOLSHIM")
|
||||
.map(|val| val == "1" || val.to_lowercase() == "true")
|
||||
.unwrap_or(false);
|
||||
|
||||
let toolshim_model = std::env::var("GOOSE_TOOLSHIM_OLLAMA_MODEL").ok();
|
||||
|
||||
let temperature = std::env::var("GOOSE_TEMPERATURE")
|
||||
.ok()
|
||||
.and_then(|val| val.parse::<f32>().ok());
|
||||
|
||||
Self {
|
||||
Ok(Self {
|
||||
model_name,
|
||||
context_limit,
|
||||
temperature,
|
||||
max_tokens: None,
|
||||
toolshim,
|
||||
toolshim_model,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Get model-specific context limit based on model name
|
||||
fn get_model_specific_limit(model_name: &str) -> Option<usize> {
|
||||
for (pattern, &limit) in MODEL_SPECIFIC_LIMITS.iter() {
|
||||
if model_name.contains(pattern) {
|
||||
return Some(limit);
|
||||
fn parse_context_limit(
|
||||
model_name: &str,
|
||||
custom_env_var: Option<&str>,
|
||||
) -> Result<Option<usize>, ConfigError> {
|
||||
if let Some(env_var) = custom_env_var {
|
||||
if let Ok(val) = std::env::var(env_var) {
|
||||
return Self::validate_context_limit(&val, env_var).map(Some);
|
||||
}
|
||||
}
|
||||
None
|
||||
if let Ok(val) = std::env::var("GOOSE_CONTEXT_LIMIT") {
|
||||
return Self::validate_context_limit(&val, "GOOSE_CONTEXT_LIMIT").map(Some);
|
||||
}
|
||||
Ok(Self::get_model_specific_limit(model_name))
|
||||
}
|
||||
|
||||
fn validate_context_limit(val: &str, env_var: &str) -> Result<usize, ConfigError> {
|
||||
let limit = val.parse::<usize>().map_err(|_| {
|
||||
ConfigError::InvalidValue(
|
||||
env_var.to_string(),
|
||||
val.to_string(),
|
||||
"must be a positive integer".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
if limit < 4 * 1024 {
|
||||
return Err(ConfigError::InvalidRange(
|
||||
env_var.to_string(),
|
||||
"must be greater than 4K".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(limit)
|
||||
}
|
||||
|
||||
fn parse_temperature() -> Result<Option<f32>, ConfigError> {
|
||||
if let Ok(val) = std::env::var("GOOSE_TEMPERATURE") {
|
||||
let temp = val.parse::<f32>().map_err(|_| {
|
||||
ConfigError::InvalidValue(
|
||||
"GOOSE_TEMPERATURE".to_string(),
|
||||
val.clone(),
|
||||
"must be a valid number".to_string(),
|
||||
)
|
||||
})?;
|
||||
if temp < 0.0 {
|
||||
return Err(ConfigError::InvalidRange(
|
||||
"GOOSE_TEMPERATURE".to_string(),
|
||||
val,
|
||||
));
|
||||
}
|
||||
Ok(Some(temp))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_toolshim() -> Result<bool, ConfigError> {
|
||||
if let Ok(val) = std::env::var("GOOSE_TOOLSHIM") {
|
||||
match val.to_lowercase().as_str() {
|
||||
"1" | "true" | "yes" | "on" => Ok(true),
|
||||
"0" | "false" | "no" | "off" => Ok(false),
|
||||
_ => Err(ConfigError::InvalidValue(
|
||||
"GOOSE_TOOLSHIM".to_string(),
|
||||
val,
|
||||
"must be one of: 1, true, yes, on, 0, false, no, off".to_string(),
|
||||
)),
|
||||
}
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_toolshim_model() -> Result<Option<String>, ConfigError> {
|
||||
match std::env::var("GOOSE_TOOLSHIM_OLLAMA_MODEL") {
|
||||
Ok(val) if val.trim().is_empty() => Err(ConfigError::InvalidValue(
|
||||
"GOOSE_TOOLSHIM_OLLAMA_MODEL".to_string(),
|
||||
val,
|
||||
"cannot be empty if set".to_string(),
|
||||
)),
|
||||
Ok(val) => Ok(Some(val)),
|
||||
Err(_) => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_model_specific_limit(model_name: &str) -> Option<usize> {
|
||||
MODEL_SPECIFIC_LIMITS
|
||||
.iter()
|
||||
.find(|(pattern, _)| model_name.contains(pattern))
|
||||
.map(|(_, limit)| *limit)
|
||||
}
|
||||
|
||||
/// Get all model pattern matches and their limits
|
||||
pub fn get_all_model_limits() -> Vec<ModelLimitConfig> {
|
||||
MODEL_SPECIFIC_LIMITS
|
||||
.iter()
|
||||
.map(|(&pattern, &context_limit)| ModelLimitConfig {
|
||||
.map(|(pattern, context_limit)| ModelLimitConfig {
|
||||
pattern: pattern.to_string(),
|
||||
context_limit,
|
||||
context_limit: *context_limit,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Set an explicit context limit
|
||||
pub fn with_context_limit(mut self, limit: Option<usize>) -> Self {
|
||||
// Default is None and therefore DEFAULT_CONTEXT_LIMIT, only set
|
||||
// if input is Some to allow passing through with_context_limit in
|
||||
// configuration cases
|
||||
if limit.is_some() {
|
||||
self.context_limit = limit;
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the temperature
|
||||
pub fn with_temperature(mut self, temp: Option<f32>) -> Self {
|
||||
self.temperature = temp;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the max tokens
|
||||
pub fn with_max_tokens(mut self, tokens: Option<i32>) -> Self {
|
||||
self.max_tokens = tokens;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set whether to interpret tool calls
|
||||
pub fn with_toolshim(mut self, toolshim: bool) -> Self {
|
||||
self.toolshim = toolshim;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the tool call interpreter model
|
||||
pub fn with_toolshim_model(mut self, model: Option<String>) -> Self {
|
||||
self.toolshim_model = model;
|
||||
self
|
||||
}
|
||||
|
||||
/// Get the context_limit for the current model
|
||||
/// If none are defined, use the DEFAULT_CONTEXT_LIMIT
|
||||
pub fn context_limit(&self) -> usize {
|
||||
self.context_limit.unwrap_or(DEFAULT_CONTEXT_LIMIT)
|
||||
}
|
||||
|
||||
/// Get context limit with environment variable override support
|
||||
///
|
||||
/// The context limit is resolved with the following precedence:
|
||||
/// 1. Custom environment variable (if specified)
|
||||
/// 2. GOOSE_CONTEXT_LIMIT (default environment variable)
|
||||
/// 3. Model-specific default based on model name
|
||||
/// 4. Global default (128_000)
|
||||
fn get_context_limit_with_env_override(
|
||||
model_name: &str,
|
||||
custom_env_var: Option<&str>,
|
||||
) -> Option<usize> {
|
||||
// 1. Check custom environment variable first (e.g., GOOSE_LEAD_CONTEXT_LIMIT)
|
||||
if let Some(env_var) = custom_env_var {
|
||||
if let Ok(limit_str) = std::env::var(env_var) {
|
||||
if let Ok(limit) = limit_str.parse::<usize>() {
|
||||
return Some(limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Check default context limit environment variable
|
||||
if let Ok(limit_str) = std::env::var("GOOSE_CONTEXT_LIMIT") {
|
||||
if let Ok(limit) = limit_str.parse::<usize>() {
|
||||
return Some(limit);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Fall back to model-specific defaults
|
||||
Self::get_model_specific_limit(model_name)
|
||||
pub fn new_or_fail(model_name: &str) -> ModelConfig {
|
||||
ModelConfig::new(model_name)
|
||||
.unwrap_or_else(|_| panic!("Failed to create model config for {}", model_name))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use temp_env::with_var;
|
||||
|
||||
#[test]
|
||||
fn test_model_config_context_limits() {
|
||||
// Test explicit limit
|
||||
let config =
|
||||
ModelConfig::new("claude-3-opus".to_string()).with_context_limit(Some(150_000));
|
||||
let config = ModelConfig::new("claude-3-opus")
|
||||
.unwrap()
|
||||
.with_context_limit(Some(150_000));
|
||||
assert_eq!(config.context_limit(), 150_000);
|
||||
|
||||
// Test model-specific defaults
|
||||
let config = ModelConfig::new("claude-3-opus".to_string());
|
||||
let config = ModelConfig::new("claude-3-opus").unwrap();
|
||||
assert_eq!(config.context_limit(), 200_000);
|
||||
|
||||
let config = ModelConfig::new("gpt-4-turbo".to_string());
|
||||
let config = ModelConfig::new("gpt-4-turbo").unwrap();
|
||||
assert_eq!(config.context_limit(), 128_000);
|
||||
|
||||
// Test fallback to default
|
||||
let config = ModelConfig::new("unknown-model".to_string());
|
||||
let config = ModelConfig::new("unknown-model").unwrap();
|
||||
assert_eq!(config.context_limit(), DEFAULT_CONTEXT_LIMIT);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_model_config_settings() {
|
||||
let config = ModelConfig::new("test-model".to_string())
|
||||
.with_temperature(Some(0.7))
|
||||
.with_max_tokens(Some(1000))
|
||||
.with_context_limit(Some(50_000));
|
||||
fn test_invalid_context_limit() {
|
||||
with_var("GOOSE_CONTEXT_LIMIT", Some("abc"), || {
|
||||
let result = ModelConfig::new("test-model");
|
||||
assert!(result.is_err());
|
||||
if let Err(ConfigError::InvalidValue(var, val, msg)) = result {
|
||||
assert_eq!(var, "GOOSE_CONTEXT_LIMIT");
|
||||
assert_eq!(val, "abc");
|
||||
assert!(msg.contains("positive integer"));
|
||||
}
|
||||
});
|
||||
|
||||
assert_eq!(config.temperature, Some(0.7));
|
||||
assert_eq!(config.max_tokens, Some(1000));
|
||||
assert_eq!(config.context_limit, Some(50_000));
|
||||
with_var("GOOSE_CONTEXT_LIMIT", Some("0"), || {
|
||||
let result = ModelConfig::new("test-model");
|
||||
assert!(result.is_err());
|
||||
assert!(matches!(
|
||||
result.unwrap_err(),
|
||||
ConfigError::InvalidRange(_, _)
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_model_config_tool_interpretation() {
|
||||
// Test without env vars - should be false
|
||||
let config = ModelConfig::new("test-model".to_string());
|
||||
assert!(!config.toolshim);
|
||||
fn test_invalid_temperature() {
|
||||
with_var("GOOSE_TEMPERATURE", Some("hot"), || {
|
||||
let result = ModelConfig::new("test-model");
|
||||
assert!(result.is_err());
|
||||
});
|
||||
|
||||
// Test with tool interpretation setting
|
||||
let config = ModelConfig::new("test-model".to_string()).with_toolshim(true);
|
||||
assert!(config.toolshim);
|
||||
|
||||
// Test tool interpreter model
|
||||
let config = ModelConfig::new("test-model".to_string())
|
||||
.with_toolshim_model(Some("mistral-nemo".to_string()));
|
||||
assert_eq!(config.toolshim_model, Some("mistral-nemo".to_string()));
|
||||
with_var("GOOSE_TEMPERATURE", Some("-1.0"), || {
|
||||
let result = ModelConfig::new("test-model");
|
||||
assert!(result.is_err());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_model_config_temp_env_var() {
|
||||
use temp_env::with_var;
|
||||
|
||||
with_var("GOOSE_TEMPERATURE", Some("0.128"), || {
|
||||
let config = ModelConfig::new("test-model".to_string());
|
||||
assert_eq!(config.temperature, Some(0.128));
|
||||
fn test_invalid_toolshim() {
|
||||
with_var("GOOSE_TOOLSHIM", Some("maybe"), || {
|
||||
let result = ModelConfig::new("test-model");
|
||||
assert!(result.is_err());
|
||||
if let Err(ConfigError::InvalidValue(var, val, msg)) = result {
|
||||
assert_eq!(var, "GOOSE_TOOLSHIM");
|
||||
assert_eq!(val, "maybe");
|
||||
assert!(msg.contains("must be one of"));
|
||||
}
|
||||
});
|
||||
|
||||
with_var("GOOSE_TEMPERATURE", Some("notanum"), || {
|
||||
let config = ModelConfig::new("test-model".to_string());
|
||||
assert_eq!(config.temperature, None);
|
||||
});
|
||||
|
||||
with_var("GOOSE_TEMPERATURE", Some(""), || {
|
||||
let config = ModelConfig::new("test-model".to_string());
|
||||
assert_eq!(config.temperature, None);
|
||||
});
|
||||
|
||||
let config = ModelConfig::new("test-model".to_string());
|
||||
assert_eq!(config.temperature, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_all_model_limits() {
|
||||
let limits = ModelConfig::get_all_model_limits();
|
||||
assert!(!limits.is_empty());
|
||||
fn test_empty_toolshim_model() {
|
||||
with_var("GOOSE_TOOLSHIM_OLLAMA_MODEL", Some(""), || {
|
||||
let result = ModelConfig::new("test-model");
|
||||
assert!(result.is_err());
|
||||
assert!(matches!(
|
||||
result.unwrap_err(),
|
||||
ConfigError::InvalidValue(_, _, _)
|
||||
));
|
||||
});
|
||||
|
||||
// Test that we can find specific patterns
|
||||
let gpt4_limit = limits.iter().find(|l| l.pattern == "gpt-4o");
|
||||
assert!(gpt4_limit.is_some());
|
||||
assert_eq!(gpt4_limit.unwrap().context_limit, 128_000);
|
||||
with_var("GOOSE_TOOLSHIM_OLLAMA_MODEL", Some(" "), || {
|
||||
let result = ModelConfig::new("test-model");
|
||||
assert!(result.is_err());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial_test::serial]
|
||||
fn test_model_config_context_limit_env_vars() {
|
||||
use temp_env::with_vars;
|
||||
|
||||
// Test default context limit environment variable
|
||||
with_vars([("GOOSE_CONTEXT_LIMIT", Some("250000"))], || {
|
||||
let config = ModelConfig::new("unknown-model".to_string());
|
||||
assert_eq!(config.context_limit(), 250_000);
|
||||
fn test_valid_configurations() {
|
||||
with_var("GOOSE_CONTEXT_LIMIT", Some("50000"), || {
|
||||
with_var("GOOSE_TEMPERATURE", Some("0.7"), || {
|
||||
with_var("GOOSE_TOOLSHIM", Some("true"), || {
|
||||
with_var("GOOSE_TOOLSHIM_OLLAMA_MODEL", Some("llama3"), || {
|
||||
let config = ModelConfig::new("test-model").unwrap();
|
||||
assert_eq!(config.context_limit(), 50_000);
|
||||
assert_eq!(config.temperature, Some(0.7));
|
||||
assert!(config.toolshim);
|
||||
assert_eq!(config.toolshim_model, Some("llama3".to_string()));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Test custom context limit environment variable
|
||||
with_vars(
|
||||
[
|
||||
("GOOSE_LEAD_CONTEXT_LIMIT", Some("300000")),
|
||||
("GOOSE_CONTEXT_LIMIT", Some("250000")),
|
||||
],
|
||||
|| {
|
||||
let config = ModelConfig::new_with_context_env(
|
||||
"unknown-model".to_string(),
|
||||
Some("GOOSE_LEAD_CONTEXT_LIMIT"),
|
||||
);
|
||||
// Should use the custom env var, not the default one
|
||||
assert_eq!(config.context_limit(), 300_000);
|
||||
},
|
||||
);
|
||||
|
||||
// Test fallback to model-specific when env var is invalid
|
||||
with_vars([("GOOSE_CONTEXT_LIMIT", Some("invalid"))], || {
|
||||
let config = ModelConfig::new("gpt-4o".to_string());
|
||||
assert_eq!(config.context_limit(), 128_000); // Should use model-specific default
|
||||
});
|
||||
|
||||
// Test fallback to default when no env vars and unknown model
|
||||
let config = ModelConfig::new("unknown-model".to_string());
|
||||
assert_eq!(config.context_limit(), DEFAULT_CONTEXT_LIMIT);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user