chore: openai reasoning model cleanup (#7529)
This commit is contained in:
+107
-11
@@ -57,6 +57,8 @@ pub struct ModelConfig {
|
||||
/// Provider-specific request parameters (e.g., anthropic_beta headers)
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub request_params: Option<HashMap<String, Value>>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub reasoning: Option<bool>,
|
||||
}
|
||||
|
||||
impl ModelConfig {
|
||||
@@ -104,21 +106,22 @@ impl ModelConfig {
|
||||
toolshim_model,
|
||||
fast_model_config: None,
|
||||
request_params,
|
||||
reasoning: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn with_canonical_limits(mut self, provider_name: &str) -> Self {
|
||||
if self.context_limit.is_none() || self.max_tokens.is_none() {
|
||||
if let Some(canonical) = crate::providers::canonical::maybe_get_canonical_model(
|
||||
provider_name,
|
||||
&self.model_name,
|
||||
) {
|
||||
if self.context_limit.is_none() {
|
||||
self.context_limit = Some(canonical.limit.context);
|
||||
}
|
||||
if self.max_tokens.is_none() {
|
||||
self.max_tokens = canonical.limit.output.map(|o| o as i32);
|
||||
}
|
||||
if let Some(canonical) =
|
||||
crate::providers::canonical::maybe_get_canonical_model(provider_name, &self.model_name)
|
||||
{
|
||||
if self.context_limit.is_none() {
|
||||
self.context_limit = Some(canonical.limit.context);
|
||||
}
|
||||
if self.max_tokens.is_none() {
|
||||
self.max_tokens = canonical.limit.output.map(|o| o as i32);
|
||||
}
|
||||
if self.reasoning.is_none() {
|
||||
self.reasoning = canonical.reasoning;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,6 +278,18 @@ impl ModelConfig {
|
||||
self.context_limit.unwrap_or(DEFAULT_CONTEXT_LIMIT)
|
||||
}
|
||||
|
||||
pub fn is_openai_reasoning_model(&self) -> bool {
|
||||
const DATABRICKS_MODEL_NAME_PREFIXES: &[&str] = &["goose-", "databricks-"];
|
||||
const REASONING_PREFIXES: &[&str] = &["o1", "o3", "o4", "gpt-5"];
|
||||
|
||||
let base = DATABRICKS_MODEL_NAME_PREFIXES
|
||||
.iter()
|
||||
.find_map(|p| self.model_name.strip_prefix(p))
|
||||
.unwrap_or(&self.model_name);
|
||||
|
||||
REASONING_PREFIXES.iter().any(|p| base.starts_with(p))
|
||||
}
|
||||
|
||||
pub fn max_output_tokens(&self) -> i32 {
|
||||
if let Some(tokens) = self.max_tokens {
|
||||
return tokens;
|
||||
@@ -408,4 +423,85 @@ mod tests {
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
mod with_canonical_limits {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn sets_limits_from_canonical_model() {
|
||||
let config = ModelConfig::new_or_fail("gpt-4o").with_canonical_limits("openai");
|
||||
|
||||
assert_eq!(config.context_limit, Some(128_000));
|
||||
assert_eq!(config.max_tokens, Some(16_384));
|
||||
assert_eq!(config.reasoning, Some(false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn does_not_override_existing_context_limit() {
|
||||
let mut config = ModelConfig::new_or_fail("gpt-4o");
|
||||
config.context_limit = Some(64_000);
|
||||
let config = config.with_canonical_limits("openai");
|
||||
|
||||
assert_eq!(config.context_limit, Some(64_000));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn does_not_override_existing_max_tokens() {
|
||||
let mut config = ModelConfig::new_or_fail("gpt-4o");
|
||||
config.max_tokens = Some(1_000);
|
||||
let config = config.with_canonical_limits("openai");
|
||||
|
||||
assert_eq!(config.max_tokens, Some(1_000));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_model_leaves_fields_none() {
|
||||
let config =
|
||||
ModelConfig::new_or_fail("totally-unknown-model").with_canonical_limits("openai");
|
||||
|
||||
assert_eq!(config.context_limit, None);
|
||||
assert_eq!(config.max_tokens, None);
|
||||
assert_eq!(config.reasoning, None);
|
||||
}
|
||||
}
|
||||
|
||||
mod is_openai_reasoning_model {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn bare_reasoning_models() {
|
||||
assert!(ModelConfig::new_or_fail("o1").is_openai_reasoning_model());
|
||||
assert!(ModelConfig::new_or_fail("o1-preview").is_openai_reasoning_model());
|
||||
assert!(ModelConfig::new_or_fail("o3").is_openai_reasoning_model());
|
||||
assert!(ModelConfig::new_or_fail("o3-mini").is_openai_reasoning_model());
|
||||
assert!(ModelConfig::new_or_fail("o4-mini").is_openai_reasoning_model());
|
||||
assert!(ModelConfig::new_or_fail("gpt-5").is_openai_reasoning_model());
|
||||
assert!(ModelConfig::new_or_fail("gpt-5-3-codex").is_openai_reasoning_model());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goose_prefixed_reasoning_models() {
|
||||
assert!(ModelConfig::new_or_fail("goose-o3-mini").is_openai_reasoning_model());
|
||||
assert!(ModelConfig::new_or_fail("goose-o4-mini").is_openai_reasoning_model());
|
||||
assert!(ModelConfig::new_or_fail("goose-gpt-5").is_openai_reasoning_model());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn databricks_prefixed_reasoning_models() {
|
||||
assert!(ModelConfig::new_or_fail("databricks-o3-mini").is_openai_reasoning_model());
|
||||
assert!(ModelConfig::new_or_fail("databricks-o4-mini").is_openai_reasoning_model());
|
||||
assert!(ModelConfig::new_or_fail("databricks-gpt-5").is_openai_reasoning_model());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_reasoning_models() {
|
||||
assert!(!ModelConfig::new_or_fail("claude-sonnet-4").is_openai_reasoning_model());
|
||||
assert!(!ModelConfig::new_or_fail("gpt-4o").is_openai_reasoning_model());
|
||||
assert!(
|
||||
!ModelConfig::new_or_fail("databricks-claude-sonnet-4").is_openai_reasoning_model()
|
||||
);
|
||||
assert!(!ModelConfig::new_or_fail("goose-claude-sonnet-4").is_openai_reasoning_model());
|
||||
assert!(!ModelConfig::new_or_fail("llama-3-70b").is_openai_reasoning_model());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user