fix: honor timeout_seconds on the Anthropic declarative provider (#10440)

This commit is contained in:
Jared Manfredi
2026-07-31 07:21:56 -04:00
committed by GitHub
parent 950575bcd3
commit d982860222
3 changed files with 50 additions and 1 deletions
+15 -1
View File
@@ -52,6 +52,12 @@ const ANTHROPIC_KNOWN_MODELS: &[&str] = &[
const ANTHROPIC_DOC_URL: &str = "https://docs.anthropic.com/en/docs/about-claude/models";
pub const ANTHROPIC_API_VERSION: &str = "2023-06-01";
// Total-request timeout applied when a declarative provider does not set
// `timeout_seconds`. Matches the OpenAI engine's default (`openai.rs`) and the
// shared `ApiClient` default so behavior is unchanged for providers that leave
// the field unset.
const DEFAULT_ANTHROPIC_TIMEOUT_SECONDS: u64 = 600;
#[derive(serde::Serialize)]
pub struct AnthropicProvider {
#[serde(skip)]
@@ -390,7 +396,15 @@ pub fn from_declarative_config(
let format_options = format_options_for_provider(config.preserves_thinking);
let mut api_client = ApiClient::new_with_tls(config.base_url, auth, tls_config)?;
let timeout_secs = config
.timeout_seconds
.unwrap_or(DEFAULT_ANTHROPIC_TIMEOUT_SECONDS);
let mut api_client = ApiClient::with_timeout_and_tls(
config.base_url,
auth,
std::time::Duration::from_secs(timeout_secs),
tls_config,
)?;
if let Some(headers) = &config.headers {
let mut header_map = reqwest::header::HeaderMap::new();
+4
View File
@@ -279,6 +279,10 @@ impl ApiClient {
&self.host
}
pub fn timeout(&self) -> Duration {
self.timeout
}
fn rebuild_client(&mut self) -> Result<()> {
let mut client_builder = Client::builder()
.timeout(self.timeout)
@@ -152,4 +152,35 @@ mod tests {
"error message should mention dynamic_models: false; got: {msg}"
);
}
// Capture the built client's request timeout without exposing a builder
// getter: map_api_client sees the ApiClient produced by
// from_declarative_config before it is replaced/decorated.
fn built_timeout(config: DeclarativeProviderConfig) -> std::time::Duration {
let captured = std::cell::Cell::new(None);
anthropic::from_declarative_config(config, None, ConfigKeyResolver::new(Config::global()))
.expect("provider construction should succeed")
.map_api_client(|api_client| {
captured.set(Some(api_client.timeout()));
api_client
});
captured.get().expect("map_api_client should have run")
}
#[test]
fn from_custom_config_honors_explicit_timeout_seconds() {
let mut config =
base_declarative_config(vec![ModelInfo::new("m1".to_string(), 200000)], Some(false));
config.timeout_seconds = Some(120);
assert_eq!(built_timeout(config), std::time::Duration::from_secs(120));
}
#[test]
fn from_custom_config_defaults_timeout_when_unset() {
// timeout_seconds: None in base config → 600s default, unchanged
// behavior for providers that don't set the field.
let config =
base_declarative_config(vec![ModelInfo::new("m1".to_string(), 200000)], Some(false));
assert_eq!(built_timeout(config), std::time::Duration::from_secs(600));
}
}