fix: honor timeout_seconds on the Anthropic declarative provider (#10440)
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user