From 204fbe9b097802b47ab061b5568b64fe74364683 Mon Sep 17 00:00:00 2001 From: Greg Hale Date: Sat, 1 Nov 2025 11:53:12 -0700 Subject: [PATCH 1/2] compound client timeouts --- .../http_config_composite_wrong_field.baml | 19 +- .../llm-client/src/clients/helpers.rs | 44 ++- .../internal/llm_client/orchestrator/call.rs | 91 +++++ .../internal/llm_client/orchestrator/mod.rs | 6 +- .../llm_client/orchestrator/stream.rs | 91 +++++ .../internal/llm_client/strategy/fallback.rs | 18 +- .../src/internal/llm_client/strategy/mod.rs | 16 +- .../llm_client/strategy/roundrobin.rs | 12 +- fern/01-guide/04-baml-basics/timeouts.mdx | 63 +++- .../test-files/client-timeout-config.baml | 57 +++ integ-tests/go/baml_client/baml_source_map.go | 2 +- integ-tests/go/baml_client/functions.go | 198 ++++++++++ integ-tests/go/baml_client/functions_parse.go | 141 ++++++++ .../go/baml_client/functions_parse_stream.go | 141 ++++++++ .../go/baml_client/functions_stream.go | 222 ++++++++++++ integ-tests/openapi/baml_client/openapi.yaml | 87 +++++ .../python-v1/baml_client/async_client.py | 123 +++++++ .../python-v1/baml_client/inlinedbaml.py | 2 +- integ-tests/python-v1/baml_client/parser.py | 36 ++ .../python-v1/baml_client/sync_client.py | 120 ++++++ .../python/baml_client/async_client.py | 123 +++++++ integ-tests/python/baml_client/inlinedbaml.py | 2 +- integ-tests/python/baml_client/parser.py | 36 ++ integ-tests/python/baml_client/sync_client.py | 120 ++++++ integ-tests/python/tests/test_timeouts.py | 44 ++- integ-tests/react/baml_client/async_client.ts | 342 ++++++++++++++++++ .../react/baml_client/async_request.ts | 150 ++++++++ integ-tests/react/baml_client/inlinedbaml.ts | 2 +- integ-tests/react/baml_client/parser.ts | 138 +++++++ integ-tests/react/baml_client/react/hooks.tsx | 150 ++++++++ integ-tests/react/baml_client/react/server.ts | 54 +++ .../baml_client/react/server_streaming.ts | 57 +++ .../react/server_streaming_types.ts | 3 + integ-tests/react/baml_client/sync_client.ts | 126 +++++++ integ-tests/react/baml_client/sync_request.ts | 150 ++++++++ integ-tests/ruby/baml_client/client.rb | 150 ++++++++ .../baml_client/async_client.ts | 342 ++++++++++++++++++ .../baml_client/async_request.ts | 150 ++++++++ .../typescript-esm/baml_client/inlinedbaml.ts | 2 +- .../typescript-esm/baml_client/parser.ts | 138 +++++++ .../typescript-esm/baml_client/sync_client.ts | 126 +++++++ .../baml_client/sync_request.ts | 150 ++++++++ .../typescript/baml_client/async_client.ts | 342 ++++++++++++++++++ .../typescript/baml_client/async_request.ts | 150 ++++++++ .../typescript/baml_client/inlinedbaml.ts | 2 +- integ-tests/typescript/baml_client/parser.ts | 138 +++++++ .../typescript/baml_client/sync_client.ts | 126 +++++++ .../typescript/baml_client/sync_request.ts | 150 ++++++++ 48 files changed, 4886 insertions(+), 66 deletions(-) diff --git a/engine/baml-lib/baml/tests/validation_files/client/http_config_composite_wrong_field.baml b/engine/baml-lib/baml/tests/validation_files/client/http_config_composite_wrong_field.baml index 9a1c836418..c4922a494a 100644 --- a/engine/baml-lib/baml/tests/validation_files/client/http_config_composite_wrong_field.baml +++ b/engine/baml-lib/baml/tests/validation_files/client/http_config_composite_wrong_field.baml @@ -10,27 +10,16 @@ client InvalidCompositeFields { options { strategy [BaseClient] http { - request_timeout_ms 5000 // Not allowed for composite - connect_timeout_ms 3000 // Not allowed for composite + invalid_field_name 5000 // Unrecognized field } } } -// error: Unrecognized field 'request_timeout_ms' in http configuration block. Did you mean 'total_timeout_ms'? Composite clients (fallback/round-robin) only support: total_timeout_ms +// error: Unrecognized field 'invalid_field_name' in http configuration block. Composite clients (fallback/round-robin) support: connect_timeout_ms, request_timeout_ms, time_to_first_token_timeout_ms, idle_timeout_ms, total_timeout_ms // --> client/http_config_composite_wrong_field.baml:12 // | // 11 | strategy [BaseClient] // 12 | http { -// 13 | request_timeout_ms 5000 // Not allowed for composite -// 14 | connect_timeout_ms 3000 // Not allowed for composite -// 15 | } -// | -// error: Unrecognized field 'connect_timeout_ms' in http configuration block. Did you mean 'total_timeout_ms'? Composite clients (fallback/round-robin) only support: total_timeout_ms -// --> client/http_config_composite_wrong_field.baml:12 -// | -// 11 | strategy [BaseClient] -// 12 | http { -// 13 | request_timeout_ms 5000 // Not allowed for composite -// 14 | connect_timeout_ms 3000 // Not allowed for composite -// 15 | } +// 13 | invalid_field_name 5000 // Unrecognized field +// 14 | } // | diff --git a/engine/baml-lib/llm-client/src/clients/helpers.rs b/engine/baml-lib/llm-client/src/clients/helpers.rs index 41ec947626..4764d915fe 100644 --- a/engine/baml-lib/llm-client/src/clients/helpers.rs +++ b/engine/baml-lib/llm-client/src/clients/helpers.rs @@ -515,9 +515,18 @@ impl PropertyHandler { // Define allowed fields based on provider type let is_composite = provider_type == "fallback" || provider_type == "round-robin"; + // All timeouts are now allowed on both regular and composite clients + // Composite clients additionally support total_timeout_ms let allowed_fields: HashSet<&str> = if is_composite { - // Composite clients only support total_timeout_ms - vec!["total_timeout_ms"].into_iter().collect() + vec![ + "connect_timeout_ms", + "request_timeout_ms", + "time_to_first_token_timeout_ms", + "idle_timeout_ms", + "total_timeout_ms", + ] + .into_iter() + .collect() } else { // Regular clients support all timeout types except total_timeout_ms vec![ @@ -532,7 +541,7 @@ impl PropertyHandler { for (key, (_, value)) in config_map { match key.as_str() { - "connect_timeout_ms" if !is_composite => { + "connect_timeout_ms" => { let value_meta = value.meta().clone(); match value.into_numeric() { Ok((val_str, _)) => { @@ -556,7 +565,7 @@ impl PropertyHandler { } } } - "request_timeout_ms" if !is_composite => { + "request_timeout_ms" => { let value_meta = value.meta().clone(); match value.into_numeric() { Ok((val_str, _)) => { @@ -580,7 +589,7 @@ impl PropertyHandler { } } } - "time_to_first_token_timeout_ms" if !is_composite => { + "time_to_first_token_timeout_ms" => { let value_meta = value.meta().clone(); match value.into_numeric() { Ok((val_str, _)) => { @@ -603,7 +612,7 @@ impl PropertyHandler { } } } - "idle_timeout_ms" if !is_composite => { + "idle_timeout_ms" => { let value_meta = value.meta().clone(); match value.into_numeric() { Ok((val_str, _)) => { @@ -665,21 +674,28 @@ impl PropertyHandler { // Build error messages with suggestions for unrecognized_field in &unrecognized_fields { let error_msg = if is_composite { - // For composite clients - if unrecognized_field == "total_timeout_ms" { - // This shouldn't happen as it's in the allowed list for composites - continue; - } else if let Some(suggestion) = - find_best_match(unrecognized_field, &["total_timeout_ms"]) + // For composite clients - all timeouts are supported + let all_timeout_fields = vec![ + "connect_timeout_ms", + "request_timeout_ms", + "time_to_first_token_timeout_ms", + "idle_timeout_ms", + "total_timeout_ms", + ]; + + if let Some(suggestion) = + find_best_match(unrecognized_field, &all_timeout_fields) { format!( "Unrecognized field '{unrecognized_field}' in http configuration block. Did you mean '{suggestion}'? \ - Composite clients (fallback/round-robin) only support: total_timeout_ms" + Composite clients (fallback/round-robin) support: connect_timeout_ms, request_timeout_ms, \ + time_to_first_token_timeout_ms, idle_timeout_ms, total_timeout_ms" ) } else { format!( "Unrecognized field '{unrecognized_field}' in http configuration block. \ - Composite clients (fallback/round-robin) only support: total_timeout_ms" + Composite clients (fallback/round-robin) support: connect_timeout_ms, request_timeout_ms, \ + time_to_first_token_timeout_ms, idle_timeout_ms, total_timeout_ms" ) } } else { diff --git a/engine/baml-runtime/src/internal/llm_client/orchestrator/call.rs b/engine/baml-runtime/src/internal/llm_client/orchestrator/call.rs index c5a31593f0..f7ad350681 100644 --- a/engine/baml-runtime/src/internal/llm_client/orchestrator/call.rs +++ b/engine/baml-runtime/src/internal/llm_client/orchestrator/call.rs @@ -62,6 +62,18 @@ pub async fn orchestrate( let mut results = Vec::new(); let mut total_sleep_duration = std::time::Duration::from_secs(0); + // Extract total_timeout_ms from strategy if present + let total_timeout_ms: Option = iter.first().and_then(|node| { + node.scope.scope.iter().find_map(|scope| match scope { + super::ExecutionScope::Fallback(strategy, _) => strategy.http_config.total_timeout_ms, + super::ExecutionScope::RoundRobin(strategy, _) => strategy.http_config.total_timeout_ms, + _ => None, + }) + }); + + // Track the start time for total timeout + let start_time = web_time::Instant::now(); + // Create a future that either waits for cancellation or never completes let cancel_future = match cancel_tripwire { Some(tripwire) => Box::pin(async move { @@ -73,8 +85,59 @@ pub async fn orchestrate( tokio::pin!(cancel_future); for node in iter { + // Check for total timeout before starting each client + if let Some(timeout_ms) = total_timeout_ms { + let elapsed = start_time.elapsed(); + if elapsed.as_millis() >= timeout_ms as u128 { + let cancel_scope = node.scope.clone(); + results.push(( + cancel_scope, + LLMResponse::LLMFailure(crate::internal::llm_client::LLMErrorResponse { + client: node.provider.name().to_string(), + model: None, + message: format!("Total timeout of {}ms exceeded", timeout_ms), + code: crate::internal::llm_client::ErrorCode::Timeout, + prompt: internal_baml_jinja::RenderedPrompt::Completion(String::new()), + start_time: web_time::SystemTime::now(), + latency: elapsed, + request_options: Default::default(), + }), + Some(Err(anyhow::anyhow!( + crate::errors::ExposedError::TimeoutError { + client_name: node.provider.name().to_string(), + message: format!( + "Total timeout of {}ms exceeded (elapsed: {}ms)", + timeout_ms, + elapsed.as_millis() + ), + } + ))), + )); + break; + } + } + // Check for cancellation at the start of each iteration let cancel_scope = node.scope.clone(); + + // Clone data needed for timeout error before moving node + let client_name_for_timeout = node.provider.name().to_string(); + + // Create a timeout future if total_timeout_ms is set + let timeout_future = if let Some(timeout_ms) = total_timeout_ms { + let remaining_time = timeout_ms.saturating_sub(start_time.elapsed().as_millis() as u64); + if remaining_time == 0 { + // Already exceeded, will be caught by the check above + Box::pin(async_std::task::sleep(std::time::Duration::from_millis(0))) + as std::pin::Pin + Send>> + } else { + Box::pin(async_std::task::sleep(std::time::Duration::from_millis(remaining_time))) + } + } else { + Box::pin(futures::future::pending()) + }; + tokio::pin!(timeout_future); + tokio::select! { biased; @@ -90,6 +153,34 @@ pub async fn orchestrate( )); break; } + _ = &mut timeout_future => { + // Total timeout exceeded during client execution + let elapsed = start_time.elapsed(); + results.push(( + cancel_scope, + LLMResponse::LLMFailure(crate::internal::llm_client::LLMErrorResponse { + client: client_name_for_timeout.clone(), + model: None, + message: format!("Total timeout of {}ms exceeded", total_timeout_ms.unwrap()), + code: crate::internal::llm_client::ErrorCode::Timeout, + prompt: internal_baml_jinja::RenderedPrompt::Completion(String::new()), + start_time: web_time::SystemTime::now(), + latency: elapsed, + request_options: Default::default(), + }), + Some(Err(anyhow::anyhow!( + crate::errors::ExposedError::TimeoutError { + client_name: client_name_for_timeout, + message: format!( + "Total timeout of {}ms exceeded (elapsed: {}ms)", + total_timeout_ms.unwrap(), + elapsed.as_millis() + ), + } + ))), + )); + break; + } result = async { let prompt = match node.render_prompt(ir, prompt, ctx, params).await { Ok(p) => p, diff --git a/engine/baml-runtime/src/internal/llm_client/orchestrator/mod.rs b/engine/baml-runtime/src/internal/llm_client/orchestrator/mod.rs index e7ed1a4a67..3c1b86b81e 100644 --- a/engine/baml-runtime/src/internal/llm_client/orchestrator/mod.rs +++ b/engine/baml-runtime/src/internal/llm_client/orchestrator/mod.rs @@ -53,7 +53,7 @@ impl std::fmt::Display for ExecutionScope { write!(f, "RoundRobin({}, {})", strategy.name, index) } ExecutionScope::Fallback(strategy, index) => { - write!(f, "Fallback({strategy}, {index})") + write!(f, "Fallback({}, {})", strategy.name, index) } } } @@ -152,8 +152,8 @@ pub enum ExecutionScope { Retry(String, usize, Duration), // StrategyName, ClientIndex RoundRobin(Arc, usize), - // StrategyName, ClientIndex - Fallback(String, usize), + // Strategy (with http_config), ClientIndex + Fallback(Arc, usize), } pub type OrchestratorNodeIterator = Vec; diff --git a/engine/baml-runtime/src/internal/llm_client/orchestrator/stream.rs b/engine/baml-runtime/src/internal/llm_client/orchestrator/stream.rs index a40b213ab5..e132e94a1c 100644 --- a/engine/baml-runtime/src/internal/llm_client/orchestrator/stream.rs +++ b/engine/baml-runtime/src/internal/llm_client/orchestrator/stream.rs @@ -172,6 +172,18 @@ where let mut results = Vec::new(); let mut total_sleep_duration = web_time::Duration::from_secs(0); + // Extract total_timeout_ms from strategy if present + let total_timeout_ms: Option = iter.first().and_then(|node| { + node.scope.scope.iter().find_map(|scope| match scope { + ExecutionScope::Fallback(strategy, _) => strategy.http_config.total_timeout_ms, + ExecutionScope::RoundRobin(strategy, _) => strategy.http_config.total_timeout_ms, + _ => None, + }) + }); + + // Track the start time for total timeout + let start_time = web_time::Instant::now(); + // Create a future that either waits for cancellation or never completes let cancel_future = match cancel_tripwire { Some(tripwire) => Box::pin(async move { @@ -184,8 +196,59 @@ where //advanced curl viewing, use render_raw_curl on each node. TODO for node in iter { + // Check for total timeout before starting each client + if let Some(timeout_ms) = total_timeout_ms { + let elapsed = start_time.elapsed(); + if elapsed.as_millis() >= timeout_ms as u128 { + let cancel_scope = node.scope.clone(); + results.push(( + cancel_scope, + LLMResponse::LLMFailure(crate::internal::llm_client::LLMErrorResponse { + client: node.provider.name().to_string(), + model: None, + message: format!("Total timeout of {}ms exceeded", timeout_ms), + code: crate::internal::llm_client::ErrorCode::Timeout, + prompt: internal_baml_jinja::RenderedPrompt::Completion(String::new()), + start_time: web_time::SystemTime::now(), + latency: elapsed, + request_options: Default::default(), + }), + Some(Err(anyhow::anyhow!( + crate::errors::ExposedError::TimeoutError { + client_name: node.provider.name().to_string(), + message: format!( + "Total timeout of {}ms exceeded (elapsed: {}ms)", + timeout_ms, + elapsed.as_millis() + ), + } + ))), + )); + break; + } + } + // Check for cancellation at the start of each iteration let cancel_scope = node.scope.clone(); + + // Clone data needed for timeout error before moving node + let client_name_for_timeout = node.provider.name().to_string(); + + // Create a timeout future if total_timeout_ms is set + let timeout_future = if let Some(timeout_ms) = total_timeout_ms { + let remaining_time = timeout_ms.saturating_sub(start_time.elapsed().as_millis() as u64); + if remaining_time == 0 { + // Already exceeded, will be caught by the check above + Box::pin(async_std::task::sleep(std::time::Duration::from_millis(0))) + as std::pin::Pin + Send>> + } else { + Box::pin(async_std::task::sleep(std::time::Duration::from_millis(remaining_time))) + } + } else { + Box::pin(futures::future::pending()) + }; + tokio::pin!(timeout_future); + tokio::select! { biased; @@ -201,6 +264,34 @@ where )); break; } + _ = &mut timeout_future => { + // Total timeout exceeded during client execution + let elapsed = start_time.elapsed(); + results.push(( + cancel_scope, + LLMResponse::LLMFailure(crate::internal::llm_client::LLMErrorResponse { + client: client_name_for_timeout.clone(), + model: None, + message: format!("Total timeout of {}ms exceeded", total_timeout_ms.unwrap()), + code: crate::internal::llm_client::ErrorCode::Timeout, + prompt: internal_baml_jinja::RenderedPrompt::Completion(String::new()), + start_time: web_time::SystemTime::now(), + latency: elapsed, + request_options: Default::default(), + }), + Some(Err(anyhow::anyhow!( + crate::errors::ExposedError::TimeoutError { + client_name: client_name_for_timeout, + message: format!( + "Total timeout of {}ms exceeded (elapsed: {}ms)", + total_timeout_ms.unwrap(), + elapsed.as_millis() + ), + } + ))), + )); + break; + } result = async { let prompt = match node.render_prompt(ir, prompt, ctx, params).await { Ok(p) => p, diff --git a/engine/baml-runtime/src/internal/llm_client/strategy/fallback.rs b/engine/baml-runtime/src/internal/llm_client/strategy/fallback.rs index c426cad523..1a54b4e13d 100644 --- a/engine/baml-runtime/src/internal/llm_client/strategy/fallback.rs +++ b/engine/baml-runtime/src/internal/llm_client/strategy/fallback.rs @@ -15,18 +15,20 @@ use crate::{ RuntimeContext, }; +#[derive(Debug)] pub struct FallbackStrategy { pub name: String, pub(super) retry_policy: Option, // TODO: We can add conditions to each client client_specs: Vec, + pub http_config: internal_llm_client::HttpConfig, } fn resolve_strategy( provider: &ClientProvider, properties: &UnresolvedClientProperty<()>, ctx: &RuntimeContext, -) -> Result> { +) -> Result<(Vec, internal_llm_client::HttpConfig)> { let properties = properties.resolve(provider, &ctx.eval_ctx(false))?; let ResolvedClientProperty::Fallback(props) = properties else { anyhow::bail!( @@ -34,7 +36,7 @@ fn resolve_strategy( properties.name() ); }; - Ok(props.strategy) + Ok((props.strategy, props.http_config)) } impl TryFrom<(&ClientProperty, &RuntimeContext)> for FallbackStrategy { @@ -43,11 +45,13 @@ impl TryFrom<(&ClientProperty, &RuntimeContext)> for FallbackStrategy { fn try_from( (client, ctx): (&ClientProperty, &RuntimeContext), ) -> std::result::Result { - let strategy = resolve_strategy(&client.provider, &client.unresolved_options()?, ctx)?; + let (strategy, http_config) = + resolve_strategy(&client.provider, &client.unresolved_options()?, ctx)?; Ok(Self { name: client.name.clone(), retry_policy: client.retry_policy.clone(), client_specs: strategy, + http_config, }) } } @@ -56,16 +60,18 @@ impl TryFrom<(&ClientWalker<'_>, &RuntimeContext)> for FallbackStrategy { type Error = anyhow::Error; fn try_from((client, ctx): (&ClientWalker, &RuntimeContext)) -> Result { - let strategy = resolve_strategy(&client.elem().provider, client.options(), ctx)?; + let (strategy, http_config) = + resolve_strategy(&client.elem().provider, client.options(), ctx)?; Ok(Self { name: client.item.elem.name.clone(), retry_policy: client.retry_policy().as_ref().map(String::from), client_specs: strategy, + http_config, }) } } -impl IterOrchestrator for FallbackStrategy { +impl IterOrchestrator for std::sync::Arc { fn iter_orchestrator<'a>( &self, state: &mut OrchestrationState, @@ -83,7 +89,7 @@ impl IterOrchestrator for FallbackStrategy { let client = client.clone(); Ok(client.iter_orchestrator( state, - ExecutionScope::Fallback(self.name.clone(), idx).into(), + ExecutionScope::Fallback(self.clone(), idx).into(), ctx, client_lookup, )) diff --git a/engine/baml-runtime/src/internal/llm_client/strategy/mod.rs b/engine/baml-runtime/src/internal/llm_client/strategy/mod.rs index 3aad701f1a..610da65f9f 100644 --- a/engine/baml-runtime/src/internal/llm_client/strategy/mod.rs +++ b/engine/baml-runtime/src/internal/llm_client/strategy/mod.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use anyhow::Result; -mod fallback; +pub mod fallback; pub mod roundrobin; use internal_baml_core::ir::ClientWalker; @@ -20,7 +20,7 @@ use crate::{ pub enum LLMStrategyProvider { RoundRobin(Arc), - Fallback(FallbackStrategy), + Fallback(Arc), } impl std::fmt::Display for LLMStrategyProvider { @@ -45,9 +45,9 @@ impl TryFrom<(&ClientWalker<'_>, &RuntimeContext)> for LLMStrategyProvider { StrategyClientProvider::RoundRobin => RoundRobinStrategy::try_from((client, ctx)) .map(Arc::new) .map(LLMStrategyProvider::RoundRobin), - StrategyClientProvider::Fallback => { - FallbackStrategy::try_from((client, ctx)).map(LLMStrategyProvider::Fallback) - } + StrategyClientProvider::Fallback => FallbackStrategy::try_from((client, ctx)) + .map(Arc::new) + .map(LLMStrategyProvider::Fallback), }, _ => { anyhow::bail!("Unsupported strategy provider: {}", client.elem().provider,) @@ -65,9 +65,9 @@ impl TryFrom<(&ClientProperty, &RuntimeContext)> for LLMStrategyProvider { StrategyClientProvider::RoundRobin => RoundRobinStrategy::try_from((client, ctx)) .map(Arc::new) .map(LLMStrategyProvider::RoundRobin), - StrategyClientProvider::Fallback => { - FallbackStrategy::try_from((client, ctx)).map(LLMStrategyProvider::Fallback) - } + StrategyClientProvider::Fallback => FallbackStrategy::try_from((client, ctx)) + .map(Arc::new) + .map(LLMStrategyProvider::Fallback), }, other => { let options = ["round-robin", "fallback"]; diff --git a/engine/baml-runtime/src/internal/llm_client/strategy/roundrobin.rs b/engine/baml-runtime/src/internal/llm_client/strategy/roundrobin.rs index 1c32567c01..d801fd34ef 100644 --- a/engine/baml-runtime/src/internal/llm_client/strategy/roundrobin.rs +++ b/engine/baml-runtime/src/internal/llm_client/strategy/roundrobin.rs @@ -28,6 +28,7 @@ pub struct RoundRobinStrategy { // TODO: We can add conditions to each client client_specs: Vec, current_index: AtomicUsize, + pub http_config: internal_llm_client::HttpConfig, } fn serialize_atomic(value: &AtomicUsize, serializer: S) -> Result @@ -53,7 +54,7 @@ fn resolve_strategy( provider: &ClientProvider, properties: &UnresolvedClientProperty<()>, ctx: &RuntimeContext, -) -> Result<(Vec, usize)> { +) -> Result<(Vec, usize, internal_llm_client::HttpConfig)> { let properties = properties.resolve(provider, &ctx.eval_ctx(false))?; let ResolvedClientProperty::RoundRobin(props) = properties else { anyhow::bail!( @@ -73,7 +74,7 @@ fn resolve_strategy( } } }; - Ok((props.strategy, start)) + Ok((props.strategy, start, props.http_config)) } impl TryFrom<(&ClientProperty, &RuntimeContext)> for RoundRobinStrategy { @@ -82,7 +83,7 @@ impl TryFrom<(&ClientProperty, &RuntimeContext)> for RoundRobinStrategy { fn try_from( (client, ctx): (&ClientProperty, &RuntimeContext), ) -> std::result::Result { - let (strategy, start) = + let (strategy, start, http_config) = resolve_strategy(&client.provider, &client.unresolved_options()?, ctx)?; Ok(RoundRobinStrategy { @@ -90,6 +91,7 @@ impl TryFrom<(&ClientProperty, &RuntimeContext)> for RoundRobinStrategy { retry_policy: client.retry_policy.clone(), client_specs: strategy, current_index: AtomicUsize::new(start), + http_config, }) } } @@ -98,12 +100,14 @@ impl TryFrom<(&ClientWalker<'_>, &RuntimeContext)> for RoundRobinStrategy { type Error = anyhow::Error; fn try_from((client, ctx): (&ClientWalker, &RuntimeContext)) -> Result { - let (strategy, start) = resolve_strategy(&client.elem().provider, client.options(), ctx)?; + let (strategy, start, http_config) = + resolve_strategy(&client.elem().provider, client.options(), ctx)?; Ok(Self { name: client.item.elem.name.clone(), retry_policy: client.retry_policy().as_ref().map(String::from), client_specs: strategy, current_index: AtomicUsize::new(start), + http_config, }) } } diff --git a/fern/01-guide/04-baml-basics/timeouts.mdx b/fern/01-guide/04-baml-basics/timeouts.mdx index e1131b3e73..c9a5fbb29a 100644 --- a/fern/01-guide/04-baml-basics/timeouts.mdx +++ b/fern/01-guide/04-baml-basics/timeouts.mdx @@ -37,7 +37,7 @@ client MyClient { ## Available Timeout Types -BAML supports four types of timeouts for individual requests, plus a fifth timeout type for composite clients (fallback, round-robin): +BAML supports multiple timeout types that can be used with both individual clients and composite clients (fallback, round-robin): ### `connect_timeout_ms` @@ -102,9 +102,9 @@ client MyClient { ### `request_timeout_ms` -Maximum total time for the entire request-response cycle. +Maximum total time for the entire request-response cycle for a single client attempt. -**When to use:** Ensure requests complete within your application's latency requirements. +**When to use:** Ensure individual requests complete within your application's latency requirements. ```baml client MyClient { @@ -119,6 +119,24 @@ client MyClient { } ``` +### `total_timeout_ms` (Composite Clients Only) + +Maximum total time for the entire fallback/round-robin strategy, including all client attempts. Unlike `request_timeout_ms`, this timeout measures from when the first client starts and does not reset between fallback clients. + +**When to use:** Set an overall deadline for your entire composite client strategy. + +```baml +client ResilientClient { + provider fallback + options { + strategy [Primary, Backup, LastResort] + http { + total_timeout_ms 120000 // 2 minutes for all attempts combined + } + } +} +``` + ## Timeouts with Retry Policies Each retry attempt gets the full timeout duration: @@ -233,21 +251,48 @@ client ProductionClient { For fallback clients with stricter requirements: ```baml +client Primary { + provider openai + options { + model "gpt-4" + api_key env.OPENAI_API_KEY + http { + request_timeout_ms 30000 // 30s per attempt + } + } +} + +client Secondary { + provider anthropic + options { + model "claude-3-opus" + api_key env.ANTHROPIC_API_KEY + http { + request_timeout_ms 30000 // 30s per attempt + } + } +} + client FallbackClient { provider fallback options { - strategy [Primary, Secondary, Tertiary] - + strategy [Primary, Secondary] http { - connect_timeout_ms 5000 // Faster failover - time_to_first_token_timeout_ms 15000 - idle_timeout_ms 2000 - request_timeout_ms 120000 // 2 min per attempt + // All timeout types can be specified on composite clients + connect_timeout_ms 5000 // Faster failover + time_to_first_token_timeout_ms 15000 // Applies to each client + idle_timeout_ms 2000 // Applies to each client + request_timeout_ms 30000 // Per-client timeout + total_timeout_ms 90000 // Overall strategy timeout (90s) } } } ``` + +When a composite client specifies timeout values, those values apply to all clients in the strategy. The `total_timeout_ms` is unique to composite clients and measures the time from when the first client starts, continuing to count even as the strategy moves from one client to the next on failures. + + ## Tips and Best Practices ### Start Conservative, Then Optimize diff --git a/integ-tests/baml_src/test-files/client-timeout-config.baml b/integ-tests/baml_src/test-files/client-timeout-config.baml index 35afd201f8..664434d868 100644 --- a/integ-tests/baml_src/test-files/client-timeout-config.baml +++ b/integ-tests/baml_src/test-files/client-timeout-config.baml @@ -92,3 +92,60 @@ function TestStreamingTimeout(input: string) -> string { Stream a long response about: {{input}} "# } + +// Compound client with request_timeout_ms override +// This overrides the tight 10ms timeout of TestTimeoutClient with a more generous 5000ms +client CompoundTimeoutClient { + provider fallback + options { + strategy [TestTimeoutClient, TestZeroTimeoutClient] + http { + request_timeout_ms 5000 // Override the tight timeout from primitives + } + } +} + +function TestCompoundRequestTimeout(input: string) -> string { + client CompoundTimeoutClient + prompt #" + Simple echo: {{input}} + "# +} + +// Compound client with total_timeout_ms +// Even though the second client has infinite timeout, total_timeout_ms enforces an overall limit +client CompoundTotalTimeoutClient { + provider fallback + options { + strategy [TestTimeoutClient, TestZeroTimeoutClient] + http { + total_timeout_ms 1000 // 1 second total timeout for the entire strategy + } + } +} + +function TestCompoundTotalTimeout(input: string) -> string { + client CompoundTotalTimeoutClient + prompt #" + Generate a very long detailed essay about: {{input}} + "# +} + +// Compound client with both request_timeout_ms and total_timeout_ms +client CompoundCombinedTimeoutsClient { + provider fallback + options { + strategy [TestTimeoutClient, TestZeroTimeoutClient] + http { + request_timeout_ms 5000 // Generous per-request timeout + total_timeout_ms 10000 // 10 second total timeout + } + } +} + +function TestCompoundCombinedTimeouts(input: string) -> string { + client CompoundCombinedTimeoutsClient + prompt #" + Echo: {{input}} + "# +} \ No newline at end of file diff --git a/integ-tests/go/baml_client/baml_source_map.go b/integ-tests/go/baml_client/baml_source_map.go index 835d37005c..89e4dfd77f 100644 --- a/integ-tests/go/baml_client/baml_source_map.go +++ b/integ-tests/go/baml_client/baml_source_map.go @@ -32,7 +32,7 @@ var file_map = map[string]string{ "test-files/aliases/classes.baml": "class TestClassAlias {\n key string @alias(\"key-dash\") @description(#\"\n This is a description for key\n af asdf\n \"#)\n key2 string @alias(\"key21\")\n key3 string @alias(\"key with space\")\n key4 string //unaliased\n key5 string @alias(\"key.with.punctuation/123\")\n}\n\nfunction FnTestClassAlias(input: string) -> TestClassAlias {\n client GPT35\n prompt #\"\n {{ctx.output_format}}\n \"#\n}\n\ntest FnTestClassAlias {\n functions [FnTestClassAlias]\n args {\n input \"example input\"\n }\n}\n", "test-files/aliases/enums.baml": "enum TestEnum {\n A @alias(\"k1\") @description(#\"\n User is angry\n \"#)\n B @alias(\"k22\") @description(#\"\n User is happy\n \"#)\n // tests whether k1 doesnt incorrectly get matched with k11\n C @alias(\"k11\") @description(#\"\n User is sad\n \"#)\n D @alias(\"k44\") @description(#\"\n User is confused\n \"#)\n E @description(#\"\n User is excited\n \"#)\n F @alias(\"k5\") // only alias\n \n G @alias(\"k6\") @description(#\"\n User is bored\n With a long description\n \"#)\n \n @@alias(\"Category\")\n}\n\nfunction FnTestAliasedEnumOutput(input: string) -> TestEnum {\n client GPT35\n prompt #\"\n Classify the user input into the following category\n \n {{ ctx.output_format }}\n\n {{ _.role('user') }}\n {{input}}\n\n {{ _.role('assistant') }}\n Category ID:\n \"#\n}\n\ntest FnTestAliasedEnumOutput {\n functions [FnTestAliasedEnumOutput]\n args {\n input \"mehhhhh\"\n }\n}", "test-files/builtin/fetch.baml": " // class Todo {\n // id int\n // todo string\n // completed bool\n // userId int\n // }\n\n // function GetTodo() -> Todo {\n // Todo {\n // id: 1,\n // }\n // }\n\n // function LlmDescribeTodo(todo: Todo) -> string {\n // client GPT4o\n // prompt #\"Describe the following todo in detail: {{ todo }}\"#\n // }\n\n // function UseGetTodoFunction() -> string {\n // let todo = GetTodo();\n // LlmDescribeTodo(todo)\n // }\n\n // test UseGetTodoFunction() {\n // functions [UseGetTodoFunction]\n // args { }\n // }\n\n // function SearchWikipedia(query: string) -> string {\n // baml.fetch_value(baml.Request {\n // base_url: UrlEncode(\"https://en.wikipedia.org/wiki/Special:Search\", {search query})\n // })\n // }\n\n // function UrlEncode(base_url: string, query_params: map) -> string {\n // client \"openai/o3\"\n // prompt #\"\n // Encode the following base URL and query parameters into a valid URL:\n\n // URL: {{ base_url }}\n\n // Query Params: {{ query_params }}\n\n // Answer with just the final URL, no other text\n // \"#\n // }\n\n // function ConvertUserQuery(query: string) -> string {\n // client GPT4o\n // prompt #\"\n // Convert the following user query into a search query for Wikipedia: {{ query }}\n\n // Ideally it should be a single word that could map to an actual Wikipedia page\n // \"#\n // }\n\n // function LlmFormulateResponse(query: string, search_result: string?) -> string {\n // client GPT4o\n // prompt #\"\n // You are a helpful assistant. Answer the following user query: {{ query }}.\n\n // {% if search_result != \"\" %}\n // Use this Wikipedia search result as a reference for your answer: {{ search_result }}\n // {% endif %}\n // \"#\n // }\n\n // function NeedsWikipediaSearch(query: string) -> bool {\n // client GPT4o\n // prompt #\"Does the following user query need a Wikipedia search?: {{ query }}\"#\n // }\n\n // function ChatResponse(query: string) -> string {\n // let needs_search = NeedsWikipediaSearch(query);\n // let search_result = SearchWikipedia(ConvertUserQuery(query));\n // LlmFormulateResponse(query, search_result)\n // }\n\n // test TestName {\n // functions [ChatResponse]\n // args {\n // query #\"\n // Tell me everything about Rome\n // \"#\n // messages []\n // }\n // }\n\n // test UrlEncode {\n // functions [UrlEncode]\n // args {\n // base_url \"https://en.wikipedia.org/wiki/Special:Search\"\n // query_params {search \"Tell me everything about Rome\"\n // go \"Go\"}\n // }\n // }\n\n // test Convert {\n // functions [ConvertUserQuery]\n // args {\n // query #\"\n // Tell me everything about Rome\n // \"#\n // }\n // }\n", - "test-files/client-timeout-config.baml": "// Client with extremely short timeouts for testing\nclient TestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n connect_timeout_ms 1 // 1ms - will timeout on connect\n request_timeout_ms 10 // 10ms - very likely to timeout\n }\n }\n}\n\n// Function that will timeout\nfunction TestTimeoutError(input: string) -> string {\n client TestTimeoutClient\n prompt #\"\n This is a test that should timeout.\n Please write a very long response about: {{input}}\n \"#\n}\n\n// Client with only request timeout\nclient TestRequestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 50 // 50ms timeout\n }\n }\n}\n\nfunction TestRequestTimeout(input: string) -> string {\n client TestRequestTimeoutClient\n prompt #\"\n Generate a detailed 500 word essay about: {{input}}\n \"#\n}\n\n// Client with zero timeout (infinite)\nclient TestZeroTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 0 // 0 means infinite\n }\n }\n}\n\nfunction TestZeroTimeout(input: string) -> string {\n client TestZeroTimeoutClient\n prompt #\"\n Echo: {{input}}\n \"#\n}\n\n// Fallback client that includes timeout client\nclient TestTimeoutFallbackClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n }\n}\n\nfunction TestTimeoutFallback(input: string) -> string {\n client TestTimeoutFallbackClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Client for streaming timeout tests (Phase 4)\nclient TestStreamingTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n time_to_first_token_timeout_ms 1 // 1ms - extremely short, should always timeout\n idle_timeout_ms 1 // 1ms - extremely short, should always timeout\n }\n }\n}\n\nfunction TestStreamingTimeout(input: string) -> string {\n client TestStreamingTimeoutClient\n prompt #\"\n Stream a long response about: {{input}}\n \"#\n}\n", + "test-files/client-timeout-config.baml": "// Client with extremely short timeouts for testing\nclient TestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n connect_timeout_ms 1 // 1ms - will timeout on connect\n request_timeout_ms 10 // 10ms - very likely to timeout\n }\n }\n}\n\n// Function that will timeout\nfunction TestTimeoutError(input: string) -> string {\n client TestTimeoutClient\n prompt #\"\n This is a test that should timeout.\n Please write a very long response about: {{input}}\n \"#\n}\n\n// Client with only request timeout\nclient TestRequestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 50 // 50ms timeout\n }\n }\n}\n\nfunction TestRequestTimeout(input: string) -> string {\n client TestRequestTimeoutClient\n prompt #\"\n Generate a detailed 500 word essay about: {{input}}\n \"#\n}\n\n// Client with zero timeout (infinite)\nclient TestZeroTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 0 // 0 means infinite\n }\n }\n}\n\nfunction TestZeroTimeout(input: string) -> string {\n client TestZeroTimeoutClient\n prompt #\"\n Echo: {{input}}\n \"#\n}\n\n// Fallback client that includes timeout client\nclient TestTimeoutFallbackClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n }\n}\n\nfunction TestTimeoutFallback(input: string) -> string {\n client TestTimeoutFallbackClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Client for streaming timeout tests (Phase 4)\nclient TestStreamingTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n time_to_first_token_timeout_ms 1 // 1ms - extremely short, should always timeout\n idle_timeout_ms 1 // 1ms - extremely short, should always timeout\n }\n }\n}\n\nfunction TestStreamingTimeout(input: string) -> string {\n client TestStreamingTimeoutClient\n prompt #\"\n Stream a long response about: {{input}}\n \"#\n}\n\n// Compound client with request_timeout_ms override\n// This overrides the tight 10ms timeout of TestTimeoutClient with a more generous 5000ms\nclient CompoundTimeoutClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n request_timeout_ms 5000 // Override the tight timeout from primitives\n }\n }\n}\n\nfunction TestCompoundRequestTimeout(input: string) -> string {\n client CompoundTimeoutClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Compound client with total_timeout_ms\n// Even though the second client has infinite timeout, total_timeout_ms enforces an overall limit\nclient CompoundTotalTimeoutClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n total_timeout_ms 1000 // 1 second total timeout for the entire strategy\n }\n }\n}\n\nfunction TestCompoundTotalTimeout(input: string) -> string {\n client CompoundTotalTimeoutClient\n prompt #\"\n Generate a very long detailed essay about: {{input}}\n \"#\n}\n\n// Compound client with both request_timeout_ms and total_timeout_ms\nclient CompoundCombinedTimeoutsClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n request_timeout_ms 5000 // Generous per-request timeout\n total_timeout_ms 10000 // 10 second total timeout\n }\n }\n}\n\nfunction TestCompoundCombinedTimeouts(input: string) -> string {\n client CompoundCombinedTimeoutsClient\n prompt #\"\n Echo: {{input}}\n \"#\n}", "test-files/comments/comments.baml": "// add some functions, classes, enums etc with comments all over.", "test-files/constraints/constraints.baml": "// These classes and functions test several properties of\n// constrains:\n//\n// - The ability for constrains on fields to pass or fail.\n// - The ability for constraints on bare args and return types to pass or fail.\n// - The ability of constraints to influence which variant of a union is chosen\n// by the parser, when the structure is not sufficient to decide.\n\n/// A Martian organism with an age.\n/// Such a nice type.\nclass Martian {\n /// The age of the Martian in Mars years.\n /// So many Mars years.\n age int @check(young_enough, {{ this < 30 }})\n}\n\nclass Earthling {\n age int @check(earth_aged, {{this < 200 and this > 0}}) @check(no_infants, {{this >1}})\n}\n\n\nclass FooAny {\n planetary_age Martian | Earthling\n certainty int @check(unreasonably_certain, {{this == 102931}})\n species string @check(trivial, {{this == \"Homo sapiens\"}}) @check(regex_good, {{this|regex_match(\"Homo\")}}) @check(regex_bad, {{this|regex_match(\"neanderthalensis\")}})\n}\n\n\nfunction PredictAge(name: string) -> FooAny {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling\n and capitalization). I'll give you a hint: If the name\n is \"Greg\", his age is 41.\n\n {{ctx.output_format}}\n \"#\n}\n\n\nfunction PredictAgeBare(inp: string @assert(big_enough, {{this|length > 1}})) -> int @check(too_big, {{this == 10102}}) {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ inp.name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling).\n\n {{ctx.output_format}}\n \"#\n}\n\nfunction ReturnFailingAssert(inp: int @assert(small_int, {{this < 10}})) -> int @assert(big_int, {{this > 100}}) {\n client GPT35\n prompt #\"\n Return the next integer after {{ inp }}.\n\n {{ctx.output_format}}\n \"#\n}\n\nclass TwoStoriesOneTitle {\n title string\n story_a string @assert(too_long_story, {{this|length > 1000000}} )\n story_b string @assert(too_long_story, {{this|length > 1000000}} )\n}\n\nfunction StreamFailingAssertion(theme: string, length: int) -> TwoStoriesOneTitle {\n client GPT35\n prompt #\"\n Tell me two different stories along the theme of {{ theme }} with the same title.\n Please make each about {{ length }} words long.\n {{ctx.output_format}}\n \"#\n}\n\nclass TwoStoriesOneTitleCheck {\n title string\n story_a string @check(too_long_story, {{this|length > 1000000}} )\n story_b string @check(too_long_story, {{this|length > 1000000}} )\n}\n\nfunction StreamFailingCheck(theme: string, length: int) -> TwoStoriesOneTitleCheck {\n client GPT35\n prompt #\"\n Tell me two different stories along the theme of {{ theme }} with the same title.\n Please make each about {{ length }} words long.\n {{ctx.output_format}}\n \"#\n}\n\nclass BlockConstraint {\n foo int\n bar string\n @@check(cross_field, {{ this.bar|length > this.foo }})\n}\n\nfunction MakeBlockConstraint() -> BlockConstraint {\n client GPT35\n prompt #\"\n Generate an output in the following schema with a short string and a large int.\n\n {{ ctx.output_format }}\n \"#\n}\n\nclass NestedBlockConstraint {\n nbc BlockConstraint\n}\n\nclass BlockConstraintForParam {\n bcfp int\n bcfp2 string\n @@assert(hi, {{ this.bcfp2|length < this.bcfp }})\n}\n\nclass NestedBlockConstraintForParam {\n nbcfp BlockConstraintForParam\n}\n\nfunction MakeNestedBlockConstraint() -> NestedBlockConstraint {\n client GPT35\n prompt #\"Generate an output where the inner foo is 1 and the inner bar is \"hello\".\n {{ ctx.output_format }}\n \"#\n}\n\nfunction UseBlockConstraint(inp: BlockConstraintForParam) -> int {\n client GPT35\n prompt #\"\n Generate 3\n {{ ctx.output_format }}\n \"#\n}\n\nfunction UseNestedBlockConstraint(inp: NestedBlockConstraintForParam) -> int {\n client GPT35\n prompt #\"\n Generate 3\n {{ ctx.output_format }}\n \"#\n}\n", "test-files/constraints/contact-info.baml": "class PhoneNumber {\n value string @assert(valid_phone_number, {{this|regex_match(\"\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}\")}})\n}\n\nclass EmailAddress {\n value string @assert(valid_email, {{this|regex_match(\"^[_]*([a-z0-9]+(\\.|_*)?)+@([a-z][a-z0-9-]+(\\.|-*\\.))+[a-z]{2,6}$\")}})\n}\n\nclass ContactInfo {\n primary PhoneNumber | EmailAddress\n secondary (PhoneNumber | EmailAddress)?\n}\n\nfunction ExtractContactInfo(document: string) -> ContactInfo {\n client GPT35\n prompt #\"\n Extract a primary contact info, and if possible a secondary contact\n info, from this document:\n\n {{ document }}\n\n {{ ctx.output_format }}\n \"#\n}\n", diff --git a/integ-tests/go/baml_client/functions.go b/integ-tests/go/baml_client/functions.go index 4d4105107e..0a9401cd58 100644 --- a/integ-tests/go/baml_client/functions.go +++ b/integ-tests/go/baml_client/functions.go @@ -9929,6 +9929,204 @@ func TestCaching(ctx context.Context, input string, not_cached string, opts ...C } } +func TestCompoundCombinedTimeouts(ctx context.Context, input string, opts ...CallOptionFunc) (string, error) { + + var callOpts callOption + for _, opt := range opts { + opt(&callOpts) + } + + args := baml.BamlFunctionArguments{ + Kwargs: map[string]any{"input": input}, + Env: getEnvVars(callOpts.env), + } + + if callOpts.clientRegistry != nil { + args.ClientRegistry = callOpts.clientRegistry + } + + if callOpts.collectors != nil { + args.Collectors = callOpts.collectors + } + + if callOpts.typeBuilder != nil { + args.TypeBuilder = callOpts.typeBuilder + } + + if callOpts.tags != nil { + args.Tags = callOpts.tags + } + + encoded, err := args.Encode() + if err != nil { + panic(err) + } + + if callOpts.onTick == nil { + result, err := bamlRuntime.CallFunction(ctx, "TestCompoundCombinedTimeouts", encoded, callOpts.onTick) + if err != nil { + return "", err + } + + if result.Error != nil { + return "", result.Error + } + + casted := (result.Data).(string) + + return casted, nil + } else { + channel, err := bamlRuntime.CallFunctionStream(ctx, "TestCompoundCombinedTimeouts", encoded, callOpts.onTick) + if err != nil { + return "", err + } + + for result := range channel { + if result.Error != nil { + return "", result.Error + } + + if result.HasData { + return result.Data.(string), nil + } + } + + return "", fmt.Errorf("No data returned from stream") + } +} + +func TestCompoundRequestTimeout(ctx context.Context, input string, opts ...CallOptionFunc) (string, error) { + + var callOpts callOption + for _, opt := range opts { + opt(&callOpts) + } + + args := baml.BamlFunctionArguments{ + Kwargs: map[string]any{"input": input}, + Env: getEnvVars(callOpts.env), + } + + if callOpts.clientRegistry != nil { + args.ClientRegistry = callOpts.clientRegistry + } + + if callOpts.collectors != nil { + args.Collectors = callOpts.collectors + } + + if callOpts.typeBuilder != nil { + args.TypeBuilder = callOpts.typeBuilder + } + + if callOpts.tags != nil { + args.Tags = callOpts.tags + } + + encoded, err := args.Encode() + if err != nil { + panic(err) + } + + if callOpts.onTick == nil { + result, err := bamlRuntime.CallFunction(ctx, "TestCompoundRequestTimeout", encoded, callOpts.onTick) + if err != nil { + return "", err + } + + if result.Error != nil { + return "", result.Error + } + + casted := (result.Data).(string) + + return casted, nil + } else { + channel, err := bamlRuntime.CallFunctionStream(ctx, "TestCompoundRequestTimeout", encoded, callOpts.onTick) + if err != nil { + return "", err + } + + for result := range channel { + if result.Error != nil { + return "", result.Error + } + + if result.HasData { + return result.Data.(string), nil + } + } + + return "", fmt.Errorf("No data returned from stream") + } +} + +func TestCompoundTotalTimeout(ctx context.Context, input string, opts ...CallOptionFunc) (string, error) { + + var callOpts callOption + for _, opt := range opts { + opt(&callOpts) + } + + args := baml.BamlFunctionArguments{ + Kwargs: map[string]any{"input": input}, + Env: getEnvVars(callOpts.env), + } + + if callOpts.clientRegistry != nil { + args.ClientRegistry = callOpts.clientRegistry + } + + if callOpts.collectors != nil { + args.Collectors = callOpts.collectors + } + + if callOpts.typeBuilder != nil { + args.TypeBuilder = callOpts.typeBuilder + } + + if callOpts.tags != nil { + args.Tags = callOpts.tags + } + + encoded, err := args.Encode() + if err != nil { + panic(err) + } + + if callOpts.onTick == nil { + result, err := bamlRuntime.CallFunction(ctx, "TestCompoundTotalTimeout", encoded, callOpts.onTick) + if err != nil { + return "", err + } + + if result.Error != nil { + return "", result.Error + } + + casted := (result.Data).(string) + + return casted, nil + } else { + channel, err := bamlRuntime.CallFunctionStream(ctx, "TestCompoundTotalTimeout", encoded, callOpts.onTick) + if err != nil { + return "", err + } + + for result := range channel { + if result.Error != nil { + return "", result.Error + } + + if result.HasData { + return result.Data.(string), nil + } + } + + return "", fmt.Errorf("No data returned from stream") + } +} + func TestFallbackClient(ctx context.Context, opts ...CallOptionFunc) (string, error) { var callOpts callOption diff --git a/integ-tests/go/baml_client/functions_parse.go b/integ-tests/go/baml_client/functions_parse.go index 70747564de..7b2108e7e5 100644 --- a/integ-tests/go/baml_client/functions_parse.go +++ b/integ-tests/go/baml_client/functions_parse.go @@ -7083,6 +7083,147 @@ func (*parse) TestCaching(text string, opts ...CallOptionFunc) (string, error) { return casted, nil } +// / Parse version of TestCompoundCombinedTimeouts (Takes in string and returns string) +func (*parse) TestCompoundCombinedTimeouts(text string, opts ...CallOptionFunc) (string, error) { + + var callOpts callOption + for _, opt := range opts { + opt(&callOpts) + } + + args := baml.BamlFunctionArguments{ + Kwargs: map[string]any{"text": text, "stream": false}, + Env: getEnvVars(callOpts.env), + } + + if callOpts.clientRegistry != nil { + args.ClientRegistry = callOpts.clientRegistry + } + + if callOpts.collectors != nil { + args.Collectors = callOpts.collectors + } + + if callOpts.typeBuilder != nil { + args.TypeBuilder = callOpts.typeBuilder + } + + if callOpts.tags != nil { + args.Tags = callOpts.tags + } + + encoded, err := args.Encode() + if err != nil { + // This should never happen. if it does, please file an issue at https://github.com/boundaryml/baml/issues + // and include the type of the args you're passing in. + wrapped_err := fmt.Errorf("BAML INTERNAL ERROR: TestCompoundCombinedTimeouts: %w", err) + panic(wrapped_err) + } + + result, err := bamlRuntime.CallFunctionParse(context.Background(), "TestCompoundCombinedTimeouts", encoded) + if err != nil { + return "", err + } + + casted := (result).(string) + + return casted, nil +} + +// / Parse version of TestCompoundRequestTimeout (Takes in string and returns string) +func (*parse) TestCompoundRequestTimeout(text string, opts ...CallOptionFunc) (string, error) { + + var callOpts callOption + for _, opt := range opts { + opt(&callOpts) + } + + args := baml.BamlFunctionArguments{ + Kwargs: map[string]any{"text": text, "stream": false}, + Env: getEnvVars(callOpts.env), + } + + if callOpts.clientRegistry != nil { + args.ClientRegistry = callOpts.clientRegistry + } + + if callOpts.collectors != nil { + args.Collectors = callOpts.collectors + } + + if callOpts.typeBuilder != nil { + args.TypeBuilder = callOpts.typeBuilder + } + + if callOpts.tags != nil { + args.Tags = callOpts.tags + } + + encoded, err := args.Encode() + if err != nil { + // This should never happen. if it does, please file an issue at https://github.com/boundaryml/baml/issues + // and include the type of the args you're passing in. + wrapped_err := fmt.Errorf("BAML INTERNAL ERROR: TestCompoundRequestTimeout: %w", err) + panic(wrapped_err) + } + + result, err := bamlRuntime.CallFunctionParse(context.Background(), "TestCompoundRequestTimeout", encoded) + if err != nil { + return "", err + } + + casted := (result).(string) + + return casted, nil +} + +// / Parse version of TestCompoundTotalTimeout (Takes in string and returns string) +func (*parse) TestCompoundTotalTimeout(text string, opts ...CallOptionFunc) (string, error) { + + var callOpts callOption + for _, opt := range opts { + opt(&callOpts) + } + + args := baml.BamlFunctionArguments{ + Kwargs: map[string]any{"text": text, "stream": false}, + Env: getEnvVars(callOpts.env), + } + + if callOpts.clientRegistry != nil { + args.ClientRegistry = callOpts.clientRegistry + } + + if callOpts.collectors != nil { + args.Collectors = callOpts.collectors + } + + if callOpts.typeBuilder != nil { + args.TypeBuilder = callOpts.typeBuilder + } + + if callOpts.tags != nil { + args.Tags = callOpts.tags + } + + encoded, err := args.Encode() + if err != nil { + // This should never happen. if it does, please file an issue at https://github.com/boundaryml/baml/issues + // and include the type of the args you're passing in. + wrapped_err := fmt.Errorf("BAML INTERNAL ERROR: TestCompoundTotalTimeout: %w", err) + panic(wrapped_err) + } + + result, err := bamlRuntime.CallFunctionParse(context.Background(), "TestCompoundTotalTimeout", encoded) + if err != nil { + return "", err + } + + casted := (result).(string) + + return casted, nil +} + // / Parse version of TestFallbackClient (Takes in string and returns string) func (*parse) TestFallbackClient(text string, opts ...CallOptionFunc) (string, error) { diff --git a/integ-tests/go/baml_client/functions_parse_stream.go b/integ-tests/go/baml_client/functions_parse_stream.go index b1868879ee..c48e374d50 100644 --- a/integ-tests/go/baml_client/functions_parse_stream.go +++ b/integ-tests/go/baml_client/functions_parse_stream.go @@ -7084,6 +7084,147 @@ func (*parse_stream) TestCaching(text string, opts ...CallOptionFunc) (string, e return casted, nil } +// / Parse version of TestCompoundCombinedTimeouts (Takes in string and returns string) +func (*parse_stream) TestCompoundCombinedTimeouts(text string, opts ...CallOptionFunc) (string, error) { + + var callOpts callOption + for _, opt := range opts { + opt(&callOpts) + } + + args := baml.BamlFunctionArguments{ + Kwargs: map[string]any{"text": text, "stream": true}, + Env: getEnvVars(callOpts.env), + } + + if callOpts.clientRegistry != nil { + args.ClientRegistry = callOpts.clientRegistry + } + + if callOpts.collectors != nil { + args.Collectors = callOpts.collectors + } + + if callOpts.typeBuilder != nil { + args.TypeBuilder = callOpts.typeBuilder + } + + if callOpts.tags != nil { + args.Tags = callOpts.tags + } + + encoded, err := args.Encode() + if err != nil { + // This should never happen. if it does, please file an issue at https://github.com/boundaryml/baml/issues + // and include the type of the args you're passing in. + wrapped_err := fmt.Errorf("BAML INTERNAL ERROR: TestCompoundCombinedTimeouts: %w", err) + panic(wrapped_err) + } + + result, err := bamlRuntime.CallFunctionParse(context.Background(), "TestCompoundCombinedTimeouts", encoded) + if err != nil { + return "", err + } + + casted := (result).(string) + + return casted, nil +} + +// / Parse version of TestCompoundRequestTimeout (Takes in string and returns string) +func (*parse_stream) TestCompoundRequestTimeout(text string, opts ...CallOptionFunc) (string, error) { + + var callOpts callOption + for _, opt := range opts { + opt(&callOpts) + } + + args := baml.BamlFunctionArguments{ + Kwargs: map[string]any{"text": text, "stream": true}, + Env: getEnvVars(callOpts.env), + } + + if callOpts.clientRegistry != nil { + args.ClientRegistry = callOpts.clientRegistry + } + + if callOpts.collectors != nil { + args.Collectors = callOpts.collectors + } + + if callOpts.typeBuilder != nil { + args.TypeBuilder = callOpts.typeBuilder + } + + if callOpts.tags != nil { + args.Tags = callOpts.tags + } + + encoded, err := args.Encode() + if err != nil { + // This should never happen. if it does, please file an issue at https://github.com/boundaryml/baml/issues + // and include the type of the args you're passing in. + wrapped_err := fmt.Errorf("BAML INTERNAL ERROR: TestCompoundRequestTimeout: %w", err) + panic(wrapped_err) + } + + result, err := bamlRuntime.CallFunctionParse(context.Background(), "TestCompoundRequestTimeout", encoded) + if err != nil { + return "", err + } + + casted := (result).(string) + + return casted, nil +} + +// / Parse version of TestCompoundTotalTimeout (Takes in string and returns string) +func (*parse_stream) TestCompoundTotalTimeout(text string, opts ...CallOptionFunc) (string, error) { + + var callOpts callOption + for _, opt := range opts { + opt(&callOpts) + } + + args := baml.BamlFunctionArguments{ + Kwargs: map[string]any{"text": text, "stream": true}, + Env: getEnvVars(callOpts.env), + } + + if callOpts.clientRegistry != nil { + args.ClientRegistry = callOpts.clientRegistry + } + + if callOpts.collectors != nil { + args.Collectors = callOpts.collectors + } + + if callOpts.typeBuilder != nil { + args.TypeBuilder = callOpts.typeBuilder + } + + if callOpts.tags != nil { + args.Tags = callOpts.tags + } + + encoded, err := args.Encode() + if err != nil { + // This should never happen. if it does, please file an issue at https://github.com/boundaryml/baml/issues + // and include the type of the args you're passing in. + wrapped_err := fmt.Errorf("BAML INTERNAL ERROR: TestCompoundTotalTimeout: %w", err) + panic(wrapped_err) + } + + result, err := bamlRuntime.CallFunctionParse(context.Background(), "TestCompoundTotalTimeout", encoded) + if err != nil { + return "", err + } + + casted := (result).(string) + + return casted, nil +} + // / Parse version of TestFallbackClient (Takes in string and returns string) func (*parse_stream) TestFallbackClient(text string, opts ...CallOptionFunc) (string, error) { diff --git a/integ-tests/go/baml_client/functions_stream.go b/integ-tests/go/baml_client/functions_stream.go index 64d5b90203..1d57ffe7b7 100644 --- a/integ-tests/go/baml_client/functions_stream.go +++ b/integ-tests/go/baml_client/functions_stream.go @@ -11158,6 +11158,228 @@ func (*stream) TestCaching(ctx context.Context, input string, not_cached string, return channel, nil } +// / Streaming version of TestCompoundCombinedTimeouts +func (*stream) TestCompoundCombinedTimeouts(ctx context.Context, input string, opts ...CallOptionFunc) (<-chan StreamValue[string, string], error) { + + var callOpts callOption + for _, opt := range opts { + opt(&callOpts) + } + + args := baml.BamlFunctionArguments{ + Kwargs: map[string]any{"input": input}, + Env: getEnvVars(callOpts.env), + } + + if callOpts.clientRegistry != nil { + args.ClientRegistry = callOpts.clientRegistry + } + + if callOpts.collectors != nil { + args.Collectors = callOpts.collectors + } + + if callOpts.typeBuilder != nil { + args.TypeBuilder = callOpts.typeBuilder + } + + if callOpts.tags != nil { + args.Tags = callOpts.tags + } + + encoded, err := args.Encode() + if err != nil { + // This should never happen. if it does, please file an issue at https://github.com/boundaryml/baml/issues + // and include the type of the args you're passing in. + wrapped_err := fmt.Errorf("BAML INTERNAL ERROR: TestCompoundCombinedTimeouts: %w", err) + panic(wrapped_err) + } + + internal_channel, err := bamlRuntime.CallFunctionStream(ctx, "TestCompoundCombinedTimeouts", encoded, callOpts.onTick) + if err != nil { + return nil, err + } + + channel := make(chan StreamValue[string, string]) + go func() { + for result := range internal_channel { + if result.Error != nil { + channel <- StreamValue[string, string]{ + IsError: true, + Error: result.Error, + } + close(channel) + return + } + if result.HasData { + data := (result.Data).(string) + channel <- StreamValue[string, string]{ + IsFinal: true, + as_final: &data, + } + } else { + data := (result.StreamData).(string) + channel <- StreamValue[string, string]{ + IsFinal: false, + as_stream: &data, + } + } + } + + // when internal_channel is closed, close the output too + close(channel) + }() + return channel, nil +} + +// / Streaming version of TestCompoundRequestTimeout +func (*stream) TestCompoundRequestTimeout(ctx context.Context, input string, opts ...CallOptionFunc) (<-chan StreamValue[string, string], error) { + + var callOpts callOption + for _, opt := range opts { + opt(&callOpts) + } + + args := baml.BamlFunctionArguments{ + Kwargs: map[string]any{"input": input}, + Env: getEnvVars(callOpts.env), + } + + if callOpts.clientRegistry != nil { + args.ClientRegistry = callOpts.clientRegistry + } + + if callOpts.collectors != nil { + args.Collectors = callOpts.collectors + } + + if callOpts.typeBuilder != nil { + args.TypeBuilder = callOpts.typeBuilder + } + + if callOpts.tags != nil { + args.Tags = callOpts.tags + } + + encoded, err := args.Encode() + if err != nil { + // This should never happen. if it does, please file an issue at https://github.com/boundaryml/baml/issues + // and include the type of the args you're passing in. + wrapped_err := fmt.Errorf("BAML INTERNAL ERROR: TestCompoundRequestTimeout: %w", err) + panic(wrapped_err) + } + + internal_channel, err := bamlRuntime.CallFunctionStream(ctx, "TestCompoundRequestTimeout", encoded, callOpts.onTick) + if err != nil { + return nil, err + } + + channel := make(chan StreamValue[string, string]) + go func() { + for result := range internal_channel { + if result.Error != nil { + channel <- StreamValue[string, string]{ + IsError: true, + Error: result.Error, + } + close(channel) + return + } + if result.HasData { + data := (result.Data).(string) + channel <- StreamValue[string, string]{ + IsFinal: true, + as_final: &data, + } + } else { + data := (result.StreamData).(string) + channel <- StreamValue[string, string]{ + IsFinal: false, + as_stream: &data, + } + } + } + + // when internal_channel is closed, close the output too + close(channel) + }() + return channel, nil +} + +// / Streaming version of TestCompoundTotalTimeout +func (*stream) TestCompoundTotalTimeout(ctx context.Context, input string, opts ...CallOptionFunc) (<-chan StreamValue[string, string], error) { + + var callOpts callOption + for _, opt := range opts { + opt(&callOpts) + } + + args := baml.BamlFunctionArguments{ + Kwargs: map[string]any{"input": input}, + Env: getEnvVars(callOpts.env), + } + + if callOpts.clientRegistry != nil { + args.ClientRegistry = callOpts.clientRegistry + } + + if callOpts.collectors != nil { + args.Collectors = callOpts.collectors + } + + if callOpts.typeBuilder != nil { + args.TypeBuilder = callOpts.typeBuilder + } + + if callOpts.tags != nil { + args.Tags = callOpts.tags + } + + encoded, err := args.Encode() + if err != nil { + // This should never happen. if it does, please file an issue at https://github.com/boundaryml/baml/issues + // and include the type of the args you're passing in. + wrapped_err := fmt.Errorf("BAML INTERNAL ERROR: TestCompoundTotalTimeout: %w", err) + panic(wrapped_err) + } + + internal_channel, err := bamlRuntime.CallFunctionStream(ctx, "TestCompoundTotalTimeout", encoded, callOpts.onTick) + if err != nil { + return nil, err + } + + channel := make(chan StreamValue[string, string]) + go func() { + for result := range internal_channel { + if result.Error != nil { + channel <- StreamValue[string, string]{ + IsError: true, + Error: result.Error, + } + close(channel) + return + } + if result.HasData { + data := (result.Data).(string) + channel <- StreamValue[string, string]{ + IsFinal: true, + as_final: &data, + } + } else { + data := (result.StreamData).(string) + channel <- StreamValue[string, string]{ + IsFinal: false, + as_stream: &data, + } + } + } + + // when internal_channel is closed, close the output too + close(channel) + }() + return channel, nil +} + // / Streaming version of TestFallbackClient func (*stream) TestFallbackClient(ctx context.Context, opts ...CallOptionFunc) (<-chan StreamValue[string, string], error) { diff --git a/integ-tests/openapi/baml_client/openapi.yaml b/integ-tests/openapi/baml_client/openapi.yaml index 06e52fa22d..0940ab1d2a 100644 --- a/integ-tests/openapi/baml_client/openapi.yaml +++ b/integ-tests/openapi/baml_client/openapi.yaml @@ -2144,6 +2144,45 @@ paths: title: TestCachingResponse type: string operationId: TestCaching + /call/TestCompoundCombinedTimeouts: + post: + requestBody: + $ref: '#/components/requestBodies/TestCompoundCombinedTimeouts' + responses: + '200': + description: Successful operation + content: + application/json: + schema: + title: TestCompoundCombinedTimeoutsResponse + type: string + operationId: TestCompoundCombinedTimeouts + /call/TestCompoundRequestTimeout: + post: + requestBody: + $ref: '#/components/requestBodies/TestCompoundRequestTimeout' + responses: + '200': + description: Successful operation + content: + application/json: + schema: + title: TestCompoundRequestTimeoutResponse + type: string + operationId: TestCompoundRequestTimeout + /call/TestCompoundTotalTimeout: + post: + requestBody: + $ref: '#/components/requestBodies/TestCompoundTotalTimeout' + responses: + '200': + description: Successful operation + content: + application/json: + schema: + title: TestCompoundTotalTimeoutResponse + type: string + operationId: TestCompoundTotalTimeout /call/TestFallbackClient: post: requestBody: @@ -5746,6 +5785,54 @@ components: - input - not_cached additionalProperties: false + TestCompoundCombinedTimeouts: + required: true + content: + application/json: + schema: + title: TestCompoundCombinedTimeoutsRequest + type: object + properties: + input: + type: string + __baml_options__: + nullable: true + $ref: '#/components/schemas/BamlOptions' + required: + - input + additionalProperties: false + TestCompoundRequestTimeout: + required: true + content: + application/json: + schema: + title: TestCompoundRequestTimeoutRequest + type: object + properties: + input: + type: string + __baml_options__: + nullable: true + $ref: '#/components/schemas/BamlOptions' + required: + - input + additionalProperties: false + TestCompoundTotalTimeout: + required: true + content: + application/json: + schema: + title: TestCompoundTotalTimeoutRequest + type: object + properties: + input: + type: string + __baml_options__: + nullable: true + $ref: '#/components/schemas/BamlOptions' + required: + - input + additionalProperties: false TestFallbackClient: required: true content: diff --git a/integ-tests/python-v1/baml_client/async_client.py b/integ-tests/python-v1/baml_client/async_client.py index 843493b567..e02a9c84f3 100644 --- a/integ-tests/python-v1/baml_client/async_client.py +++ b/integ-tests/python-v1/baml_client/async_client.py @@ -2329,6 +2329,51 @@ async def TestCaching(self, input: str,not_cached: str, "input": input,"not_cached": not_cached, }) return typing.cast(str, result.cast_to(types, types, stream_types, False, __runtime__)) + async def TestCompoundCombinedTimeouts(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> str: + # Check if on_tick is provided + if 'on_tick' in baml_options: + # Use streaming internally when on_tick is provided + stream = self.stream.TestCompoundCombinedTimeouts(input=input, + baml_options=baml_options) + return await stream.get_final_response() + else: + # Original non-streaming code + result = await self.__options.merge_options(baml_options).call_function_async(function_name="TestCompoundCombinedTimeouts", args={ + "input": input, + }) + return typing.cast(str, result.cast_to(types, types, stream_types, False, __runtime__)) + async def TestCompoundRequestTimeout(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> str: + # Check if on_tick is provided + if 'on_tick' in baml_options: + # Use streaming internally when on_tick is provided + stream = self.stream.TestCompoundRequestTimeout(input=input, + baml_options=baml_options) + return await stream.get_final_response() + else: + # Original non-streaming code + result = await self.__options.merge_options(baml_options).call_function_async(function_name="TestCompoundRequestTimeout", args={ + "input": input, + }) + return typing.cast(str, result.cast_to(types, types, stream_types, False, __runtime__)) + async def TestCompoundTotalTimeout(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> str: + # Check if on_tick is provided + if 'on_tick' in baml_options: + # Use streaming internally when on_tick is provided + stream = self.stream.TestCompoundTotalTimeout(input=input, + baml_options=baml_options) + return await stream.get_final_response() + else: + # Original non-streaming code + result = await self.__options.merge_options(baml_options).call_function_async(function_name="TestCompoundTotalTimeout", args={ + "input": input, + }) + return typing.cast(str, result.cast_to(types, types, stream_types, False, __runtime__)) async def TestFallbackClient(self, baml_options: BamlCallOptions = {}, ) -> str: @@ -5848,6 +5893,42 @@ def TestCaching(self, input: str,not_cached: str, lambda x: typing.cast(str, x.cast_to(types, types, stream_types, False, __runtime__)), ctx, ) + def TestCompoundCombinedTimeouts(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.BamlStream[str, str]: + ctx, result = self.__options.merge_options(baml_options).create_async_stream(function_name="TestCompoundCombinedTimeouts", args={ + "input": input, + }) + return baml_py.BamlStream[str, str]( + result, + lambda x: typing.cast(str, x.cast_to(types, types, stream_types, True, __runtime__)), + lambda x: typing.cast(str, x.cast_to(types, types, stream_types, False, __runtime__)), + ctx, + ) + def TestCompoundRequestTimeout(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.BamlStream[str, str]: + ctx, result = self.__options.merge_options(baml_options).create_async_stream(function_name="TestCompoundRequestTimeout", args={ + "input": input, + }) + return baml_py.BamlStream[str, str]( + result, + lambda x: typing.cast(str, x.cast_to(types, types, stream_types, True, __runtime__)), + lambda x: typing.cast(str, x.cast_to(types, types, stream_types, False, __runtime__)), + ctx, + ) + def TestCompoundTotalTimeout(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.BamlStream[str, str]: + ctx, result = self.__options.merge_options(baml_options).create_async_stream(function_name="TestCompoundTotalTimeout", args={ + "input": input, + }) + return baml_py.BamlStream[str, str]( + result, + lambda x: typing.cast(str, x.cast_to(types, types, stream_types, True, __runtime__)), + lambda x: typing.cast(str, x.cast_to(types, types, stream_types, False, __runtime__)), + ctx, + ) def TestFallbackClient(self, baml_options: BamlCallOptions = {}, ) -> baml_py.BamlStream[str, str]: @@ -8274,6 +8355,27 @@ async def TestCaching(self, input: str,not_cached: str, "input": input,"not_cached": not_cached, }, mode="request") return result + async def TestCompoundCombinedTimeouts(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.baml_py.HTTPRequest: + result = await self.__options.merge_options(baml_options).create_http_request_async(function_name="TestCompoundCombinedTimeouts", args={ + "input": input, + }, mode="request") + return result + async def TestCompoundRequestTimeout(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.baml_py.HTTPRequest: + result = await self.__options.merge_options(baml_options).create_http_request_async(function_name="TestCompoundRequestTimeout", args={ + "input": input, + }, mode="request") + return result + async def TestCompoundTotalTimeout(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.baml_py.HTTPRequest: + result = await self.__options.merge_options(baml_options).create_http_request_async(function_name="TestCompoundTotalTimeout", args={ + "input": input, + }, mode="request") + return result async def TestFallbackClient(self, baml_options: BamlCallOptions = {}, ) -> baml_py.baml_py.HTTPRequest: @@ -10130,6 +10232,27 @@ async def TestCaching(self, input: str,not_cached: str, "input": input,"not_cached": not_cached, }, mode="stream") return result + async def TestCompoundCombinedTimeouts(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.baml_py.HTTPRequest: + result = await self.__options.merge_options(baml_options).create_http_request_async(function_name="TestCompoundCombinedTimeouts", args={ + "input": input, + }, mode="stream") + return result + async def TestCompoundRequestTimeout(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.baml_py.HTTPRequest: + result = await self.__options.merge_options(baml_options).create_http_request_async(function_name="TestCompoundRequestTimeout", args={ + "input": input, + }, mode="stream") + return result + async def TestCompoundTotalTimeout(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.baml_py.HTTPRequest: + result = await self.__options.merge_options(baml_options).create_http_request_async(function_name="TestCompoundTotalTimeout", args={ + "input": input, + }, mode="stream") + return result async def TestFallbackClient(self, baml_options: BamlCallOptions = {}, ) -> baml_py.baml_py.HTTPRequest: diff --git a/integ-tests/python-v1/baml_client/inlinedbaml.py b/integ-tests/python-v1/baml_client/inlinedbaml.py index 17c3bd421a..eb9325466e 100644 --- a/integ-tests/python-v1/baml_client/inlinedbaml.py +++ b/integ-tests/python-v1/baml_client/inlinedbaml.py @@ -29,7 +29,7 @@ "test-files/aliases/classes.baml": "class TestClassAlias {\n key string @alias(\"key-dash\") @description(#\"\n This is a description for key\n af asdf\n \"#)\n key2 string @alias(\"key21\")\n key3 string @alias(\"key with space\")\n key4 string //unaliased\n key5 string @alias(\"key.with.punctuation/123\")\n}\n\nfunction FnTestClassAlias(input: string) -> TestClassAlias {\n client GPT35\n prompt #\"\n {{ctx.output_format}}\n \"#\n}\n\ntest FnTestClassAlias {\n functions [FnTestClassAlias]\n args {\n input \"example input\"\n }\n}\n", "test-files/aliases/enums.baml": "enum TestEnum {\n A @alias(\"k1\") @description(#\"\n User is angry\n \"#)\n B @alias(\"k22\") @description(#\"\n User is happy\n \"#)\n // tests whether k1 doesnt incorrectly get matched with k11\n C @alias(\"k11\") @description(#\"\n User is sad\n \"#)\n D @alias(\"k44\") @description(#\"\n User is confused\n \"#)\n E @description(#\"\n User is excited\n \"#)\n F @alias(\"k5\") // only alias\n \n G @alias(\"k6\") @description(#\"\n User is bored\n With a long description\n \"#)\n \n @@alias(\"Category\")\n}\n\nfunction FnTestAliasedEnumOutput(input: string) -> TestEnum {\n client GPT35\n prompt #\"\n Classify the user input into the following category\n \n {{ ctx.output_format }}\n\n {{ _.role('user') }}\n {{input}}\n\n {{ _.role('assistant') }}\n Category ID:\n \"#\n}\n\ntest FnTestAliasedEnumOutput {\n functions [FnTestAliasedEnumOutput]\n args {\n input \"mehhhhh\"\n }\n}", "test-files/builtin/fetch.baml": " // class Todo {\n // id int\n // todo string\n // completed bool\n // userId int\n // }\n\n // function GetTodo() -> Todo {\n // Todo {\n // id: 1,\n // }\n // }\n\n // function LlmDescribeTodo(todo: Todo) -> string {\n // client GPT4o\n // prompt #\"Describe the following todo in detail: {{ todo }}\"#\n // }\n\n // function UseGetTodoFunction() -> string {\n // let todo = GetTodo();\n // LlmDescribeTodo(todo)\n // }\n\n // test UseGetTodoFunction() {\n // functions [UseGetTodoFunction]\n // args { }\n // }\n\n // function SearchWikipedia(query: string) -> string {\n // baml.fetch_value(baml.Request {\n // base_url: UrlEncode(\"https://en.wikipedia.org/wiki/Special:Search\", {search query})\n // })\n // }\n\n // function UrlEncode(base_url: string, query_params: map) -> string {\n // client \"openai/o3\"\n // prompt #\"\n // Encode the following base URL and query parameters into a valid URL:\n\n // URL: {{ base_url }}\n\n // Query Params: {{ query_params }}\n\n // Answer with just the final URL, no other text\n // \"#\n // }\n\n // function ConvertUserQuery(query: string) -> string {\n // client GPT4o\n // prompt #\"\n // Convert the following user query into a search query for Wikipedia: {{ query }}\n\n // Ideally it should be a single word that could map to an actual Wikipedia page\n // \"#\n // }\n\n // function LlmFormulateResponse(query: string, search_result: string?) -> string {\n // client GPT4o\n // prompt #\"\n // You are a helpful assistant. Answer the following user query: {{ query }}.\n\n // {% if search_result != \"\" %}\n // Use this Wikipedia search result as a reference for your answer: {{ search_result }}\n // {% endif %}\n // \"#\n // }\n\n // function NeedsWikipediaSearch(query: string) -> bool {\n // client GPT4o\n // prompt #\"Does the following user query need a Wikipedia search?: {{ query }}\"#\n // }\n\n // function ChatResponse(query: string) -> string {\n // let needs_search = NeedsWikipediaSearch(query);\n // let search_result = SearchWikipedia(ConvertUserQuery(query));\n // LlmFormulateResponse(query, search_result)\n // }\n\n // test TestName {\n // functions [ChatResponse]\n // args {\n // query #\"\n // Tell me everything about Rome\n // \"#\n // messages []\n // }\n // }\n\n // test UrlEncode {\n // functions [UrlEncode]\n // args {\n // base_url \"https://en.wikipedia.org/wiki/Special:Search\"\n // query_params {search \"Tell me everything about Rome\"\n // go \"Go\"}\n // }\n // }\n\n // test Convert {\n // functions [ConvertUserQuery]\n // args {\n // query #\"\n // Tell me everything about Rome\n // \"#\n // }\n // }\n", - "test-files/client-timeout-config.baml": "// Client with extremely short timeouts for testing\nclient TestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n connect_timeout_ms 1 // 1ms - will timeout on connect\n request_timeout_ms 10 // 10ms - very likely to timeout\n }\n }\n}\n\n// Function that will timeout\nfunction TestTimeoutError(input: string) -> string {\n client TestTimeoutClient\n prompt #\"\n This is a test that should timeout.\n Please write a very long response about: {{input}}\n \"#\n}\n\n// Client with only request timeout\nclient TestRequestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 50 // 50ms timeout\n }\n }\n}\n\nfunction TestRequestTimeout(input: string) -> string {\n client TestRequestTimeoutClient\n prompt #\"\n Generate a detailed 500 word essay about: {{input}}\n \"#\n}\n\n// Client with zero timeout (infinite)\nclient TestZeroTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 0 // 0 means infinite\n }\n }\n}\n\nfunction TestZeroTimeout(input: string) -> string {\n client TestZeroTimeoutClient\n prompt #\"\n Echo: {{input}}\n \"#\n}\n\n// Fallback client that includes timeout client\nclient TestTimeoutFallbackClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n }\n}\n\nfunction TestTimeoutFallback(input: string) -> string {\n client TestTimeoutFallbackClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Client for streaming timeout tests (Phase 4)\nclient TestStreamingTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n time_to_first_token_timeout_ms 1 // 1ms - extremely short, should always timeout\n idle_timeout_ms 1 // 1ms - extremely short, should always timeout\n }\n }\n}\n\nfunction TestStreamingTimeout(input: string) -> string {\n client TestStreamingTimeoutClient\n prompt #\"\n Stream a long response about: {{input}}\n \"#\n}\n", + "test-files/client-timeout-config.baml": "// Client with extremely short timeouts for testing\nclient TestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n connect_timeout_ms 1 // 1ms - will timeout on connect\n request_timeout_ms 10 // 10ms - very likely to timeout\n }\n }\n}\n\n// Function that will timeout\nfunction TestTimeoutError(input: string) -> string {\n client TestTimeoutClient\n prompt #\"\n This is a test that should timeout.\n Please write a very long response about: {{input}}\n \"#\n}\n\n// Client with only request timeout\nclient TestRequestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 50 // 50ms timeout\n }\n }\n}\n\nfunction TestRequestTimeout(input: string) -> string {\n client TestRequestTimeoutClient\n prompt #\"\n Generate a detailed 500 word essay about: {{input}}\n \"#\n}\n\n// Client with zero timeout (infinite)\nclient TestZeroTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 0 // 0 means infinite\n }\n }\n}\n\nfunction TestZeroTimeout(input: string) -> string {\n client TestZeroTimeoutClient\n prompt #\"\n Echo: {{input}}\n \"#\n}\n\n// Fallback client that includes timeout client\nclient TestTimeoutFallbackClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n }\n}\n\nfunction TestTimeoutFallback(input: string) -> string {\n client TestTimeoutFallbackClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Client for streaming timeout tests (Phase 4)\nclient TestStreamingTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n time_to_first_token_timeout_ms 1 // 1ms - extremely short, should always timeout\n idle_timeout_ms 1 // 1ms - extremely short, should always timeout\n }\n }\n}\n\nfunction TestStreamingTimeout(input: string) -> string {\n client TestStreamingTimeoutClient\n prompt #\"\n Stream a long response about: {{input}}\n \"#\n}\n\n// Compound client with request_timeout_ms override\n// This overrides the tight 10ms timeout of TestTimeoutClient with a more generous 5000ms\nclient CompoundTimeoutClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n request_timeout_ms 5000 // Override the tight timeout from primitives\n }\n }\n}\n\nfunction TestCompoundRequestTimeout(input: string) -> string {\n client CompoundTimeoutClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Compound client with total_timeout_ms\n// Even though the second client has infinite timeout, total_timeout_ms enforces an overall limit\nclient CompoundTotalTimeoutClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n total_timeout_ms 1000 // 1 second total timeout for the entire strategy\n }\n }\n}\n\nfunction TestCompoundTotalTimeout(input: string) -> string {\n client CompoundTotalTimeoutClient\n prompt #\"\n Generate a very long detailed essay about: {{input}}\n \"#\n}\n\n// Compound client with both request_timeout_ms and total_timeout_ms\nclient CompoundCombinedTimeoutsClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n request_timeout_ms 5000 // Generous per-request timeout\n total_timeout_ms 10000 // 10 second total timeout\n }\n }\n}\n\nfunction TestCompoundCombinedTimeouts(input: string) -> string {\n client CompoundCombinedTimeoutsClient\n prompt #\"\n Echo: {{input}}\n \"#\n}", "test-files/comments/comments.baml": "// add some functions, classes, enums etc with comments all over.", "test-files/constraints/constraints.baml": "// These classes and functions test several properties of\n// constrains:\n//\n// - The ability for constrains on fields to pass or fail.\n// - The ability for constraints on bare args and return types to pass or fail.\n// - The ability of constraints to influence which variant of a union is chosen\n// by the parser, when the structure is not sufficient to decide.\n\n/// A Martian organism with an age.\n/// Such a nice type.\nclass Martian {\n /// The age of the Martian in Mars years.\n /// So many Mars years.\n age int @check(young_enough, {{ this < 30 }})\n}\n\nclass Earthling {\n age int @check(earth_aged, {{this < 200 and this > 0}}) @check(no_infants, {{this >1}})\n}\n\n\nclass FooAny {\n planetary_age Martian | Earthling\n certainty int @check(unreasonably_certain, {{this == 102931}})\n species string @check(trivial, {{this == \"Homo sapiens\"}}) @check(regex_good, {{this|regex_match(\"Homo\")}}) @check(regex_bad, {{this|regex_match(\"neanderthalensis\")}})\n}\n\n\nfunction PredictAge(name: string) -> FooAny {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling\n and capitalization). I'll give you a hint: If the name\n is \"Greg\", his age is 41.\n\n {{ctx.output_format}}\n \"#\n}\n\n\nfunction PredictAgeBare(inp: string @assert(big_enough, {{this|length > 1}})) -> int @check(too_big, {{this == 10102}}) {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ inp.name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling).\n\n {{ctx.output_format}}\n \"#\n}\n\nfunction ReturnFailingAssert(inp: int @assert(small_int, {{this < 10}})) -> int @assert(big_int, {{this > 100}}) {\n client GPT35\n prompt #\"\n Return the next integer after {{ inp }}.\n\n {{ctx.output_format}}\n \"#\n}\n\nclass TwoStoriesOneTitle {\n title string\n story_a string @assert(too_long_story, {{this|length > 1000000}} )\n story_b string @assert(too_long_story, {{this|length > 1000000}} )\n}\n\nfunction StreamFailingAssertion(theme: string, length: int) -> TwoStoriesOneTitle {\n client GPT35\n prompt #\"\n Tell me two different stories along the theme of {{ theme }} with the same title.\n Please make each about {{ length }} words long.\n {{ctx.output_format}}\n \"#\n}\n\nclass TwoStoriesOneTitleCheck {\n title string\n story_a string @check(too_long_story, {{this|length > 1000000}} )\n story_b string @check(too_long_story, {{this|length > 1000000}} )\n}\n\nfunction StreamFailingCheck(theme: string, length: int) -> TwoStoriesOneTitleCheck {\n client GPT35\n prompt #\"\n Tell me two different stories along the theme of {{ theme }} with the same title.\n Please make each about {{ length }} words long.\n {{ctx.output_format}}\n \"#\n}\n\nclass BlockConstraint {\n foo int\n bar string\n @@check(cross_field, {{ this.bar|length > this.foo }})\n}\n\nfunction MakeBlockConstraint() -> BlockConstraint {\n client GPT35\n prompt #\"\n Generate an output in the following schema with a short string and a large int.\n\n {{ ctx.output_format }}\n \"#\n}\n\nclass NestedBlockConstraint {\n nbc BlockConstraint\n}\n\nclass BlockConstraintForParam {\n bcfp int\n bcfp2 string\n @@assert(hi, {{ this.bcfp2|length < this.bcfp }})\n}\n\nclass NestedBlockConstraintForParam {\n nbcfp BlockConstraintForParam\n}\n\nfunction MakeNestedBlockConstraint() -> NestedBlockConstraint {\n client GPT35\n prompt #\"Generate an output where the inner foo is 1 and the inner bar is \"hello\".\n {{ ctx.output_format }}\n \"#\n}\n\nfunction UseBlockConstraint(inp: BlockConstraintForParam) -> int {\n client GPT35\n prompt #\"\n Generate 3\n {{ ctx.output_format }}\n \"#\n}\n\nfunction UseNestedBlockConstraint(inp: NestedBlockConstraintForParam) -> int {\n client GPT35\n prompt #\"\n Generate 3\n {{ ctx.output_format }}\n \"#\n}\n", "test-files/constraints/contact-info.baml": "class PhoneNumber {\n value string @assert(valid_phone_number, {{this|regex_match(\"\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}\")}})\n}\n\nclass EmailAddress {\n value string @assert(valid_email, {{this|regex_match(\"^[_]*([a-z0-9]+(\\.|_*)?)+@([a-z][a-z0-9-]+(\\.|-*\\.))+[a-z]{2,6}$\")}})\n}\n\nclass ContactInfo {\n primary PhoneNumber | EmailAddress\n secondary (PhoneNumber | EmailAddress)?\n}\n\nfunction ExtractContactInfo(document: string) -> ContactInfo {\n client GPT35\n prompt #\"\n Extract a primary contact info, and if possible a secondary contact\n info, from this document:\n\n {{ document }}\n\n {{ ctx.output_format }}\n \"#\n}\n", diff --git a/integ-tests/python-v1/baml_client/parser.py b/integ-tests/python-v1/baml_client/parser.py index 4829433621..5ab932bdf9 100644 --- a/integ-tests/python-v1/baml_client/parser.py +++ b/integ-tests/python-v1/baml_client/parser.py @@ -924,6 +924,24 @@ def TestCaching( result = self.__options.merge_options(baml_options).parse_response(function_name="TestCaching", llm_response=llm_response, mode="request") return typing.cast(str, result) + def TestCompoundCombinedTimeouts( + self, llm_response: str, baml_options: BamlCallOptions = {}, + ) -> str: + result = self.__options.merge_options(baml_options).parse_response(function_name="TestCompoundCombinedTimeouts", llm_response=llm_response, mode="request") + return typing.cast(str, result) + + def TestCompoundRequestTimeout( + self, llm_response: str, baml_options: BamlCallOptions = {}, + ) -> str: + result = self.__options.merge_options(baml_options).parse_response(function_name="TestCompoundRequestTimeout", llm_response=llm_response, mode="request") + return typing.cast(str, result) + + def TestCompoundTotalTimeout( + self, llm_response: str, baml_options: BamlCallOptions = {}, + ) -> str: + result = self.__options.merge_options(baml_options).parse_response(function_name="TestCompoundTotalTimeout", llm_response=llm_response, mode="request") + return typing.cast(str, result) + def TestFallbackClient( self, llm_response: str, baml_options: BamlCallOptions = {}, ) -> str: @@ -2516,6 +2534,24 @@ def TestCaching( result = self.__options.merge_options(baml_options).parse_response(function_name="TestCaching", llm_response=llm_response, mode="stream") return typing.cast(str, result) + def TestCompoundCombinedTimeouts( + self, llm_response: str, baml_options: BamlCallOptions = {}, + ) -> str: + result = self.__options.merge_options(baml_options).parse_response(function_name="TestCompoundCombinedTimeouts", llm_response=llm_response, mode="stream") + return typing.cast(str, result) + + def TestCompoundRequestTimeout( + self, llm_response: str, baml_options: BamlCallOptions = {}, + ) -> str: + result = self.__options.merge_options(baml_options).parse_response(function_name="TestCompoundRequestTimeout", llm_response=llm_response, mode="stream") + return typing.cast(str, result) + + def TestCompoundTotalTimeout( + self, llm_response: str, baml_options: BamlCallOptions = {}, + ) -> str: + result = self.__options.merge_options(baml_options).parse_response(function_name="TestCompoundTotalTimeout", llm_response=llm_response, mode="stream") + return typing.cast(str, result) + def TestFallbackClient( self, llm_response: str, baml_options: BamlCallOptions = {}, ) -> str: diff --git a/integ-tests/python-v1/baml_client/sync_client.py b/integ-tests/python-v1/baml_client/sync_client.py index 30c730bb3b..2e29ff228d 100644 --- a/integ-tests/python-v1/baml_client/sync_client.py +++ b/integ-tests/python-v1/baml_client/sync_client.py @@ -2191,6 +2191,48 @@ def TestCaching(self, input: str,not_cached: str, "input": input,"not_cached": not_cached, }) return typing.cast(str, result.cast_to(types, types, stream_types, False, __runtime__)) + def TestCompoundCombinedTimeouts(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> str: + # Check if on_tick is provided + if 'on_tick' in baml_options: + stream = self.stream.TestCompoundCombinedTimeouts(input=input, + baml_options=baml_options) + return stream.get_final_response() + else: + # Original non-streaming code + result = self.__options.merge_options(baml_options).call_function_sync(function_name="TestCompoundCombinedTimeouts", args={ + "input": input, + }) + return typing.cast(str, result.cast_to(types, types, stream_types, False, __runtime__)) + def TestCompoundRequestTimeout(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> str: + # Check if on_tick is provided + if 'on_tick' in baml_options: + stream = self.stream.TestCompoundRequestTimeout(input=input, + baml_options=baml_options) + return stream.get_final_response() + else: + # Original non-streaming code + result = self.__options.merge_options(baml_options).call_function_sync(function_name="TestCompoundRequestTimeout", args={ + "input": input, + }) + return typing.cast(str, result.cast_to(types, types, stream_types, False, __runtime__)) + def TestCompoundTotalTimeout(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> str: + # Check if on_tick is provided + if 'on_tick' in baml_options: + stream = self.stream.TestCompoundTotalTimeout(input=input, + baml_options=baml_options) + return stream.get_final_response() + else: + # Original non-streaming code + result = self.__options.merge_options(baml_options).call_function_sync(function_name="TestCompoundTotalTimeout", args={ + "input": input, + }) + return typing.cast(str, result.cast_to(types, types, stream_types, False, __runtime__)) def TestFallbackClient(self, baml_options: BamlCallOptions = {}, ) -> str: @@ -5596,6 +5638,42 @@ def TestCaching(self, input: str,not_cached: str, lambda x: typing.cast(str, x.cast_to(types, types, stream_types, False, __runtime__)), ctx, ) + def TestCompoundCombinedTimeouts(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.BamlSyncStream[str, str]: + ctx, result = self.__options.merge_options(baml_options).create_sync_stream(function_name="TestCompoundCombinedTimeouts", args={ + "input": input, + }) + return baml_py.BamlSyncStream[str, str]( + result, + lambda x: typing.cast(str, x.cast_to(types, types, stream_types, True, __runtime__)), + lambda x: typing.cast(str, x.cast_to(types, types, stream_types, False, __runtime__)), + ctx, + ) + def TestCompoundRequestTimeout(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.BamlSyncStream[str, str]: + ctx, result = self.__options.merge_options(baml_options).create_sync_stream(function_name="TestCompoundRequestTimeout", args={ + "input": input, + }) + return baml_py.BamlSyncStream[str, str]( + result, + lambda x: typing.cast(str, x.cast_to(types, types, stream_types, True, __runtime__)), + lambda x: typing.cast(str, x.cast_to(types, types, stream_types, False, __runtime__)), + ctx, + ) + def TestCompoundTotalTimeout(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.BamlSyncStream[str, str]: + ctx, result = self.__options.merge_options(baml_options).create_sync_stream(function_name="TestCompoundTotalTimeout", args={ + "input": input, + }) + return baml_py.BamlSyncStream[str, str]( + result, + lambda x: typing.cast(str, x.cast_to(types, types, stream_types, True, __runtime__)), + lambda x: typing.cast(str, x.cast_to(types, types, stream_types, False, __runtime__)), + ctx, + ) def TestFallbackClient(self, baml_options: BamlCallOptions = {}, ) -> baml_py.BamlSyncStream[str, str]: @@ -8022,6 +8100,27 @@ def TestCaching(self, input: str,not_cached: str, "input": input,"not_cached": not_cached, }, mode="request") return result + def TestCompoundCombinedTimeouts(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.baml_py.HTTPRequest: + result = self.__options.merge_options(baml_options).create_http_request_sync(function_name="TestCompoundCombinedTimeouts", args={ + "input": input, + }, mode="request") + return result + def TestCompoundRequestTimeout(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.baml_py.HTTPRequest: + result = self.__options.merge_options(baml_options).create_http_request_sync(function_name="TestCompoundRequestTimeout", args={ + "input": input, + }, mode="request") + return result + def TestCompoundTotalTimeout(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.baml_py.HTTPRequest: + result = self.__options.merge_options(baml_options).create_http_request_sync(function_name="TestCompoundTotalTimeout", args={ + "input": input, + }, mode="request") + return result def TestFallbackClient(self, baml_options: BamlCallOptions = {}, ) -> baml_py.baml_py.HTTPRequest: @@ -9878,6 +9977,27 @@ def TestCaching(self, input: str,not_cached: str, "input": input,"not_cached": not_cached, }, mode="stream") return result + def TestCompoundCombinedTimeouts(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.baml_py.HTTPRequest: + result = self.__options.merge_options(baml_options).create_http_request_sync(function_name="TestCompoundCombinedTimeouts", args={ + "input": input, + }, mode="stream") + return result + def TestCompoundRequestTimeout(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.baml_py.HTTPRequest: + result = self.__options.merge_options(baml_options).create_http_request_sync(function_name="TestCompoundRequestTimeout", args={ + "input": input, + }, mode="stream") + return result + def TestCompoundTotalTimeout(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.baml_py.HTTPRequest: + result = self.__options.merge_options(baml_options).create_http_request_sync(function_name="TestCompoundTotalTimeout", args={ + "input": input, + }, mode="stream") + return result def TestFallbackClient(self, baml_options: BamlCallOptions = {}, ) -> baml_py.baml_py.HTTPRequest: diff --git a/integ-tests/python/baml_client/async_client.py b/integ-tests/python/baml_client/async_client.py index 843493b567..e02a9c84f3 100644 --- a/integ-tests/python/baml_client/async_client.py +++ b/integ-tests/python/baml_client/async_client.py @@ -2329,6 +2329,51 @@ async def TestCaching(self, input: str,not_cached: str, "input": input,"not_cached": not_cached, }) return typing.cast(str, result.cast_to(types, types, stream_types, False, __runtime__)) + async def TestCompoundCombinedTimeouts(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> str: + # Check if on_tick is provided + if 'on_tick' in baml_options: + # Use streaming internally when on_tick is provided + stream = self.stream.TestCompoundCombinedTimeouts(input=input, + baml_options=baml_options) + return await stream.get_final_response() + else: + # Original non-streaming code + result = await self.__options.merge_options(baml_options).call_function_async(function_name="TestCompoundCombinedTimeouts", args={ + "input": input, + }) + return typing.cast(str, result.cast_to(types, types, stream_types, False, __runtime__)) + async def TestCompoundRequestTimeout(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> str: + # Check if on_tick is provided + if 'on_tick' in baml_options: + # Use streaming internally when on_tick is provided + stream = self.stream.TestCompoundRequestTimeout(input=input, + baml_options=baml_options) + return await stream.get_final_response() + else: + # Original non-streaming code + result = await self.__options.merge_options(baml_options).call_function_async(function_name="TestCompoundRequestTimeout", args={ + "input": input, + }) + return typing.cast(str, result.cast_to(types, types, stream_types, False, __runtime__)) + async def TestCompoundTotalTimeout(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> str: + # Check if on_tick is provided + if 'on_tick' in baml_options: + # Use streaming internally when on_tick is provided + stream = self.stream.TestCompoundTotalTimeout(input=input, + baml_options=baml_options) + return await stream.get_final_response() + else: + # Original non-streaming code + result = await self.__options.merge_options(baml_options).call_function_async(function_name="TestCompoundTotalTimeout", args={ + "input": input, + }) + return typing.cast(str, result.cast_to(types, types, stream_types, False, __runtime__)) async def TestFallbackClient(self, baml_options: BamlCallOptions = {}, ) -> str: @@ -5848,6 +5893,42 @@ def TestCaching(self, input: str,not_cached: str, lambda x: typing.cast(str, x.cast_to(types, types, stream_types, False, __runtime__)), ctx, ) + def TestCompoundCombinedTimeouts(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.BamlStream[str, str]: + ctx, result = self.__options.merge_options(baml_options).create_async_stream(function_name="TestCompoundCombinedTimeouts", args={ + "input": input, + }) + return baml_py.BamlStream[str, str]( + result, + lambda x: typing.cast(str, x.cast_to(types, types, stream_types, True, __runtime__)), + lambda x: typing.cast(str, x.cast_to(types, types, stream_types, False, __runtime__)), + ctx, + ) + def TestCompoundRequestTimeout(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.BamlStream[str, str]: + ctx, result = self.__options.merge_options(baml_options).create_async_stream(function_name="TestCompoundRequestTimeout", args={ + "input": input, + }) + return baml_py.BamlStream[str, str]( + result, + lambda x: typing.cast(str, x.cast_to(types, types, stream_types, True, __runtime__)), + lambda x: typing.cast(str, x.cast_to(types, types, stream_types, False, __runtime__)), + ctx, + ) + def TestCompoundTotalTimeout(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.BamlStream[str, str]: + ctx, result = self.__options.merge_options(baml_options).create_async_stream(function_name="TestCompoundTotalTimeout", args={ + "input": input, + }) + return baml_py.BamlStream[str, str]( + result, + lambda x: typing.cast(str, x.cast_to(types, types, stream_types, True, __runtime__)), + lambda x: typing.cast(str, x.cast_to(types, types, stream_types, False, __runtime__)), + ctx, + ) def TestFallbackClient(self, baml_options: BamlCallOptions = {}, ) -> baml_py.BamlStream[str, str]: @@ -8274,6 +8355,27 @@ async def TestCaching(self, input: str,not_cached: str, "input": input,"not_cached": not_cached, }, mode="request") return result + async def TestCompoundCombinedTimeouts(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.baml_py.HTTPRequest: + result = await self.__options.merge_options(baml_options).create_http_request_async(function_name="TestCompoundCombinedTimeouts", args={ + "input": input, + }, mode="request") + return result + async def TestCompoundRequestTimeout(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.baml_py.HTTPRequest: + result = await self.__options.merge_options(baml_options).create_http_request_async(function_name="TestCompoundRequestTimeout", args={ + "input": input, + }, mode="request") + return result + async def TestCompoundTotalTimeout(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.baml_py.HTTPRequest: + result = await self.__options.merge_options(baml_options).create_http_request_async(function_name="TestCompoundTotalTimeout", args={ + "input": input, + }, mode="request") + return result async def TestFallbackClient(self, baml_options: BamlCallOptions = {}, ) -> baml_py.baml_py.HTTPRequest: @@ -10130,6 +10232,27 @@ async def TestCaching(self, input: str,not_cached: str, "input": input,"not_cached": not_cached, }, mode="stream") return result + async def TestCompoundCombinedTimeouts(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.baml_py.HTTPRequest: + result = await self.__options.merge_options(baml_options).create_http_request_async(function_name="TestCompoundCombinedTimeouts", args={ + "input": input, + }, mode="stream") + return result + async def TestCompoundRequestTimeout(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.baml_py.HTTPRequest: + result = await self.__options.merge_options(baml_options).create_http_request_async(function_name="TestCompoundRequestTimeout", args={ + "input": input, + }, mode="stream") + return result + async def TestCompoundTotalTimeout(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.baml_py.HTTPRequest: + result = await self.__options.merge_options(baml_options).create_http_request_async(function_name="TestCompoundTotalTimeout", args={ + "input": input, + }, mode="stream") + return result async def TestFallbackClient(self, baml_options: BamlCallOptions = {}, ) -> baml_py.baml_py.HTTPRequest: diff --git a/integ-tests/python/baml_client/inlinedbaml.py b/integ-tests/python/baml_client/inlinedbaml.py index 17c3bd421a..eb9325466e 100644 --- a/integ-tests/python/baml_client/inlinedbaml.py +++ b/integ-tests/python/baml_client/inlinedbaml.py @@ -29,7 +29,7 @@ "test-files/aliases/classes.baml": "class TestClassAlias {\n key string @alias(\"key-dash\") @description(#\"\n This is a description for key\n af asdf\n \"#)\n key2 string @alias(\"key21\")\n key3 string @alias(\"key with space\")\n key4 string //unaliased\n key5 string @alias(\"key.with.punctuation/123\")\n}\n\nfunction FnTestClassAlias(input: string) -> TestClassAlias {\n client GPT35\n prompt #\"\n {{ctx.output_format}}\n \"#\n}\n\ntest FnTestClassAlias {\n functions [FnTestClassAlias]\n args {\n input \"example input\"\n }\n}\n", "test-files/aliases/enums.baml": "enum TestEnum {\n A @alias(\"k1\") @description(#\"\n User is angry\n \"#)\n B @alias(\"k22\") @description(#\"\n User is happy\n \"#)\n // tests whether k1 doesnt incorrectly get matched with k11\n C @alias(\"k11\") @description(#\"\n User is sad\n \"#)\n D @alias(\"k44\") @description(#\"\n User is confused\n \"#)\n E @description(#\"\n User is excited\n \"#)\n F @alias(\"k5\") // only alias\n \n G @alias(\"k6\") @description(#\"\n User is bored\n With a long description\n \"#)\n \n @@alias(\"Category\")\n}\n\nfunction FnTestAliasedEnumOutput(input: string) -> TestEnum {\n client GPT35\n prompt #\"\n Classify the user input into the following category\n \n {{ ctx.output_format }}\n\n {{ _.role('user') }}\n {{input}}\n\n {{ _.role('assistant') }}\n Category ID:\n \"#\n}\n\ntest FnTestAliasedEnumOutput {\n functions [FnTestAliasedEnumOutput]\n args {\n input \"mehhhhh\"\n }\n}", "test-files/builtin/fetch.baml": " // class Todo {\n // id int\n // todo string\n // completed bool\n // userId int\n // }\n\n // function GetTodo() -> Todo {\n // Todo {\n // id: 1,\n // }\n // }\n\n // function LlmDescribeTodo(todo: Todo) -> string {\n // client GPT4o\n // prompt #\"Describe the following todo in detail: {{ todo }}\"#\n // }\n\n // function UseGetTodoFunction() -> string {\n // let todo = GetTodo();\n // LlmDescribeTodo(todo)\n // }\n\n // test UseGetTodoFunction() {\n // functions [UseGetTodoFunction]\n // args { }\n // }\n\n // function SearchWikipedia(query: string) -> string {\n // baml.fetch_value(baml.Request {\n // base_url: UrlEncode(\"https://en.wikipedia.org/wiki/Special:Search\", {search query})\n // })\n // }\n\n // function UrlEncode(base_url: string, query_params: map) -> string {\n // client \"openai/o3\"\n // prompt #\"\n // Encode the following base URL and query parameters into a valid URL:\n\n // URL: {{ base_url }}\n\n // Query Params: {{ query_params }}\n\n // Answer with just the final URL, no other text\n // \"#\n // }\n\n // function ConvertUserQuery(query: string) -> string {\n // client GPT4o\n // prompt #\"\n // Convert the following user query into a search query for Wikipedia: {{ query }}\n\n // Ideally it should be a single word that could map to an actual Wikipedia page\n // \"#\n // }\n\n // function LlmFormulateResponse(query: string, search_result: string?) -> string {\n // client GPT4o\n // prompt #\"\n // You are a helpful assistant. Answer the following user query: {{ query }}.\n\n // {% if search_result != \"\" %}\n // Use this Wikipedia search result as a reference for your answer: {{ search_result }}\n // {% endif %}\n // \"#\n // }\n\n // function NeedsWikipediaSearch(query: string) -> bool {\n // client GPT4o\n // prompt #\"Does the following user query need a Wikipedia search?: {{ query }}\"#\n // }\n\n // function ChatResponse(query: string) -> string {\n // let needs_search = NeedsWikipediaSearch(query);\n // let search_result = SearchWikipedia(ConvertUserQuery(query));\n // LlmFormulateResponse(query, search_result)\n // }\n\n // test TestName {\n // functions [ChatResponse]\n // args {\n // query #\"\n // Tell me everything about Rome\n // \"#\n // messages []\n // }\n // }\n\n // test UrlEncode {\n // functions [UrlEncode]\n // args {\n // base_url \"https://en.wikipedia.org/wiki/Special:Search\"\n // query_params {search \"Tell me everything about Rome\"\n // go \"Go\"}\n // }\n // }\n\n // test Convert {\n // functions [ConvertUserQuery]\n // args {\n // query #\"\n // Tell me everything about Rome\n // \"#\n // }\n // }\n", - "test-files/client-timeout-config.baml": "// Client with extremely short timeouts for testing\nclient TestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n connect_timeout_ms 1 // 1ms - will timeout on connect\n request_timeout_ms 10 // 10ms - very likely to timeout\n }\n }\n}\n\n// Function that will timeout\nfunction TestTimeoutError(input: string) -> string {\n client TestTimeoutClient\n prompt #\"\n This is a test that should timeout.\n Please write a very long response about: {{input}}\n \"#\n}\n\n// Client with only request timeout\nclient TestRequestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 50 // 50ms timeout\n }\n }\n}\n\nfunction TestRequestTimeout(input: string) -> string {\n client TestRequestTimeoutClient\n prompt #\"\n Generate a detailed 500 word essay about: {{input}}\n \"#\n}\n\n// Client with zero timeout (infinite)\nclient TestZeroTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 0 // 0 means infinite\n }\n }\n}\n\nfunction TestZeroTimeout(input: string) -> string {\n client TestZeroTimeoutClient\n prompt #\"\n Echo: {{input}}\n \"#\n}\n\n// Fallback client that includes timeout client\nclient TestTimeoutFallbackClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n }\n}\n\nfunction TestTimeoutFallback(input: string) -> string {\n client TestTimeoutFallbackClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Client for streaming timeout tests (Phase 4)\nclient TestStreamingTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n time_to_first_token_timeout_ms 1 // 1ms - extremely short, should always timeout\n idle_timeout_ms 1 // 1ms - extremely short, should always timeout\n }\n }\n}\n\nfunction TestStreamingTimeout(input: string) -> string {\n client TestStreamingTimeoutClient\n prompt #\"\n Stream a long response about: {{input}}\n \"#\n}\n", + "test-files/client-timeout-config.baml": "// Client with extremely short timeouts for testing\nclient TestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n connect_timeout_ms 1 // 1ms - will timeout on connect\n request_timeout_ms 10 // 10ms - very likely to timeout\n }\n }\n}\n\n// Function that will timeout\nfunction TestTimeoutError(input: string) -> string {\n client TestTimeoutClient\n prompt #\"\n This is a test that should timeout.\n Please write a very long response about: {{input}}\n \"#\n}\n\n// Client with only request timeout\nclient TestRequestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 50 // 50ms timeout\n }\n }\n}\n\nfunction TestRequestTimeout(input: string) -> string {\n client TestRequestTimeoutClient\n prompt #\"\n Generate a detailed 500 word essay about: {{input}}\n \"#\n}\n\n// Client with zero timeout (infinite)\nclient TestZeroTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 0 // 0 means infinite\n }\n }\n}\n\nfunction TestZeroTimeout(input: string) -> string {\n client TestZeroTimeoutClient\n prompt #\"\n Echo: {{input}}\n \"#\n}\n\n// Fallback client that includes timeout client\nclient TestTimeoutFallbackClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n }\n}\n\nfunction TestTimeoutFallback(input: string) -> string {\n client TestTimeoutFallbackClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Client for streaming timeout tests (Phase 4)\nclient TestStreamingTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n time_to_first_token_timeout_ms 1 // 1ms - extremely short, should always timeout\n idle_timeout_ms 1 // 1ms - extremely short, should always timeout\n }\n }\n}\n\nfunction TestStreamingTimeout(input: string) -> string {\n client TestStreamingTimeoutClient\n prompt #\"\n Stream a long response about: {{input}}\n \"#\n}\n\n// Compound client with request_timeout_ms override\n// This overrides the tight 10ms timeout of TestTimeoutClient with a more generous 5000ms\nclient CompoundTimeoutClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n request_timeout_ms 5000 // Override the tight timeout from primitives\n }\n }\n}\n\nfunction TestCompoundRequestTimeout(input: string) -> string {\n client CompoundTimeoutClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Compound client with total_timeout_ms\n// Even though the second client has infinite timeout, total_timeout_ms enforces an overall limit\nclient CompoundTotalTimeoutClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n total_timeout_ms 1000 // 1 second total timeout for the entire strategy\n }\n }\n}\n\nfunction TestCompoundTotalTimeout(input: string) -> string {\n client CompoundTotalTimeoutClient\n prompt #\"\n Generate a very long detailed essay about: {{input}}\n \"#\n}\n\n// Compound client with both request_timeout_ms and total_timeout_ms\nclient CompoundCombinedTimeoutsClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n request_timeout_ms 5000 // Generous per-request timeout\n total_timeout_ms 10000 // 10 second total timeout\n }\n }\n}\n\nfunction TestCompoundCombinedTimeouts(input: string) -> string {\n client CompoundCombinedTimeoutsClient\n prompt #\"\n Echo: {{input}}\n \"#\n}", "test-files/comments/comments.baml": "// add some functions, classes, enums etc with comments all over.", "test-files/constraints/constraints.baml": "// These classes and functions test several properties of\n// constrains:\n//\n// - The ability for constrains on fields to pass or fail.\n// - The ability for constraints on bare args and return types to pass or fail.\n// - The ability of constraints to influence which variant of a union is chosen\n// by the parser, when the structure is not sufficient to decide.\n\n/// A Martian organism with an age.\n/// Such a nice type.\nclass Martian {\n /// The age of the Martian in Mars years.\n /// So many Mars years.\n age int @check(young_enough, {{ this < 30 }})\n}\n\nclass Earthling {\n age int @check(earth_aged, {{this < 200 and this > 0}}) @check(no_infants, {{this >1}})\n}\n\n\nclass FooAny {\n planetary_age Martian | Earthling\n certainty int @check(unreasonably_certain, {{this == 102931}})\n species string @check(trivial, {{this == \"Homo sapiens\"}}) @check(regex_good, {{this|regex_match(\"Homo\")}}) @check(regex_bad, {{this|regex_match(\"neanderthalensis\")}})\n}\n\n\nfunction PredictAge(name: string) -> FooAny {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling\n and capitalization). I'll give you a hint: If the name\n is \"Greg\", his age is 41.\n\n {{ctx.output_format}}\n \"#\n}\n\n\nfunction PredictAgeBare(inp: string @assert(big_enough, {{this|length > 1}})) -> int @check(too_big, {{this == 10102}}) {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ inp.name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling).\n\n {{ctx.output_format}}\n \"#\n}\n\nfunction ReturnFailingAssert(inp: int @assert(small_int, {{this < 10}})) -> int @assert(big_int, {{this > 100}}) {\n client GPT35\n prompt #\"\n Return the next integer after {{ inp }}.\n\n {{ctx.output_format}}\n \"#\n}\n\nclass TwoStoriesOneTitle {\n title string\n story_a string @assert(too_long_story, {{this|length > 1000000}} )\n story_b string @assert(too_long_story, {{this|length > 1000000}} )\n}\n\nfunction StreamFailingAssertion(theme: string, length: int) -> TwoStoriesOneTitle {\n client GPT35\n prompt #\"\n Tell me two different stories along the theme of {{ theme }} with the same title.\n Please make each about {{ length }} words long.\n {{ctx.output_format}}\n \"#\n}\n\nclass TwoStoriesOneTitleCheck {\n title string\n story_a string @check(too_long_story, {{this|length > 1000000}} )\n story_b string @check(too_long_story, {{this|length > 1000000}} )\n}\n\nfunction StreamFailingCheck(theme: string, length: int) -> TwoStoriesOneTitleCheck {\n client GPT35\n prompt #\"\n Tell me two different stories along the theme of {{ theme }} with the same title.\n Please make each about {{ length }} words long.\n {{ctx.output_format}}\n \"#\n}\n\nclass BlockConstraint {\n foo int\n bar string\n @@check(cross_field, {{ this.bar|length > this.foo }})\n}\n\nfunction MakeBlockConstraint() -> BlockConstraint {\n client GPT35\n prompt #\"\n Generate an output in the following schema with a short string and a large int.\n\n {{ ctx.output_format }}\n \"#\n}\n\nclass NestedBlockConstraint {\n nbc BlockConstraint\n}\n\nclass BlockConstraintForParam {\n bcfp int\n bcfp2 string\n @@assert(hi, {{ this.bcfp2|length < this.bcfp }})\n}\n\nclass NestedBlockConstraintForParam {\n nbcfp BlockConstraintForParam\n}\n\nfunction MakeNestedBlockConstraint() -> NestedBlockConstraint {\n client GPT35\n prompt #\"Generate an output where the inner foo is 1 and the inner bar is \"hello\".\n {{ ctx.output_format }}\n \"#\n}\n\nfunction UseBlockConstraint(inp: BlockConstraintForParam) -> int {\n client GPT35\n prompt #\"\n Generate 3\n {{ ctx.output_format }}\n \"#\n}\n\nfunction UseNestedBlockConstraint(inp: NestedBlockConstraintForParam) -> int {\n client GPT35\n prompt #\"\n Generate 3\n {{ ctx.output_format }}\n \"#\n}\n", "test-files/constraints/contact-info.baml": "class PhoneNumber {\n value string @assert(valid_phone_number, {{this|regex_match(\"\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}\")}})\n}\n\nclass EmailAddress {\n value string @assert(valid_email, {{this|regex_match(\"^[_]*([a-z0-9]+(\\.|_*)?)+@([a-z][a-z0-9-]+(\\.|-*\\.))+[a-z]{2,6}$\")}})\n}\n\nclass ContactInfo {\n primary PhoneNumber | EmailAddress\n secondary (PhoneNumber | EmailAddress)?\n}\n\nfunction ExtractContactInfo(document: string) -> ContactInfo {\n client GPT35\n prompt #\"\n Extract a primary contact info, and if possible a secondary contact\n info, from this document:\n\n {{ document }}\n\n {{ ctx.output_format }}\n \"#\n}\n", diff --git a/integ-tests/python/baml_client/parser.py b/integ-tests/python/baml_client/parser.py index 4829433621..5ab932bdf9 100644 --- a/integ-tests/python/baml_client/parser.py +++ b/integ-tests/python/baml_client/parser.py @@ -924,6 +924,24 @@ def TestCaching( result = self.__options.merge_options(baml_options).parse_response(function_name="TestCaching", llm_response=llm_response, mode="request") return typing.cast(str, result) + def TestCompoundCombinedTimeouts( + self, llm_response: str, baml_options: BamlCallOptions = {}, + ) -> str: + result = self.__options.merge_options(baml_options).parse_response(function_name="TestCompoundCombinedTimeouts", llm_response=llm_response, mode="request") + return typing.cast(str, result) + + def TestCompoundRequestTimeout( + self, llm_response: str, baml_options: BamlCallOptions = {}, + ) -> str: + result = self.__options.merge_options(baml_options).parse_response(function_name="TestCompoundRequestTimeout", llm_response=llm_response, mode="request") + return typing.cast(str, result) + + def TestCompoundTotalTimeout( + self, llm_response: str, baml_options: BamlCallOptions = {}, + ) -> str: + result = self.__options.merge_options(baml_options).parse_response(function_name="TestCompoundTotalTimeout", llm_response=llm_response, mode="request") + return typing.cast(str, result) + def TestFallbackClient( self, llm_response: str, baml_options: BamlCallOptions = {}, ) -> str: @@ -2516,6 +2534,24 @@ def TestCaching( result = self.__options.merge_options(baml_options).parse_response(function_name="TestCaching", llm_response=llm_response, mode="stream") return typing.cast(str, result) + def TestCompoundCombinedTimeouts( + self, llm_response: str, baml_options: BamlCallOptions = {}, + ) -> str: + result = self.__options.merge_options(baml_options).parse_response(function_name="TestCompoundCombinedTimeouts", llm_response=llm_response, mode="stream") + return typing.cast(str, result) + + def TestCompoundRequestTimeout( + self, llm_response: str, baml_options: BamlCallOptions = {}, + ) -> str: + result = self.__options.merge_options(baml_options).parse_response(function_name="TestCompoundRequestTimeout", llm_response=llm_response, mode="stream") + return typing.cast(str, result) + + def TestCompoundTotalTimeout( + self, llm_response: str, baml_options: BamlCallOptions = {}, + ) -> str: + result = self.__options.merge_options(baml_options).parse_response(function_name="TestCompoundTotalTimeout", llm_response=llm_response, mode="stream") + return typing.cast(str, result) + def TestFallbackClient( self, llm_response: str, baml_options: BamlCallOptions = {}, ) -> str: diff --git a/integ-tests/python/baml_client/sync_client.py b/integ-tests/python/baml_client/sync_client.py index 30c730bb3b..2e29ff228d 100644 --- a/integ-tests/python/baml_client/sync_client.py +++ b/integ-tests/python/baml_client/sync_client.py @@ -2191,6 +2191,48 @@ def TestCaching(self, input: str,not_cached: str, "input": input,"not_cached": not_cached, }) return typing.cast(str, result.cast_to(types, types, stream_types, False, __runtime__)) + def TestCompoundCombinedTimeouts(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> str: + # Check if on_tick is provided + if 'on_tick' in baml_options: + stream = self.stream.TestCompoundCombinedTimeouts(input=input, + baml_options=baml_options) + return stream.get_final_response() + else: + # Original non-streaming code + result = self.__options.merge_options(baml_options).call_function_sync(function_name="TestCompoundCombinedTimeouts", args={ + "input": input, + }) + return typing.cast(str, result.cast_to(types, types, stream_types, False, __runtime__)) + def TestCompoundRequestTimeout(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> str: + # Check if on_tick is provided + if 'on_tick' in baml_options: + stream = self.stream.TestCompoundRequestTimeout(input=input, + baml_options=baml_options) + return stream.get_final_response() + else: + # Original non-streaming code + result = self.__options.merge_options(baml_options).call_function_sync(function_name="TestCompoundRequestTimeout", args={ + "input": input, + }) + return typing.cast(str, result.cast_to(types, types, stream_types, False, __runtime__)) + def TestCompoundTotalTimeout(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> str: + # Check if on_tick is provided + if 'on_tick' in baml_options: + stream = self.stream.TestCompoundTotalTimeout(input=input, + baml_options=baml_options) + return stream.get_final_response() + else: + # Original non-streaming code + result = self.__options.merge_options(baml_options).call_function_sync(function_name="TestCompoundTotalTimeout", args={ + "input": input, + }) + return typing.cast(str, result.cast_to(types, types, stream_types, False, __runtime__)) def TestFallbackClient(self, baml_options: BamlCallOptions = {}, ) -> str: @@ -5596,6 +5638,42 @@ def TestCaching(self, input: str,not_cached: str, lambda x: typing.cast(str, x.cast_to(types, types, stream_types, False, __runtime__)), ctx, ) + def TestCompoundCombinedTimeouts(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.BamlSyncStream[str, str]: + ctx, result = self.__options.merge_options(baml_options).create_sync_stream(function_name="TestCompoundCombinedTimeouts", args={ + "input": input, + }) + return baml_py.BamlSyncStream[str, str]( + result, + lambda x: typing.cast(str, x.cast_to(types, types, stream_types, True, __runtime__)), + lambda x: typing.cast(str, x.cast_to(types, types, stream_types, False, __runtime__)), + ctx, + ) + def TestCompoundRequestTimeout(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.BamlSyncStream[str, str]: + ctx, result = self.__options.merge_options(baml_options).create_sync_stream(function_name="TestCompoundRequestTimeout", args={ + "input": input, + }) + return baml_py.BamlSyncStream[str, str]( + result, + lambda x: typing.cast(str, x.cast_to(types, types, stream_types, True, __runtime__)), + lambda x: typing.cast(str, x.cast_to(types, types, stream_types, False, __runtime__)), + ctx, + ) + def TestCompoundTotalTimeout(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.BamlSyncStream[str, str]: + ctx, result = self.__options.merge_options(baml_options).create_sync_stream(function_name="TestCompoundTotalTimeout", args={ + "input": input, + }) + return baml_py.BamlSyncStream[str, str]( + result, + lambda x: typing.cast(str, x.cast_to(types, types, stream_types, True, __runtime__)), + lambda x: typing.cast(str, x.cast_to(types, types, stream_types, False, __runtime__)), + ctx, + ) def TestFallbackClient(self, baml_options: BamlCallOptions = {}, ) -> baml_py.BamlSyncStream[str, str]: @@ -8022,6 +8100,27 @@ def TestCaching(self, input: str,not_cached: str, "input": input,"not_cached": not_cached, }, mode="request") return result + def TestCompoundCombinedTimeouts(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.baml_py.HTTPRequest: + result = self.__options.merge_options(baml_options).create_http_request_sync(function_name="TestCompoundCombinedTimeouts", args={ + "input": input, + }, mode="request") + return result + def TestCompoundRequestTimeout(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.baml_py.HTTPRequest: + result = self.__options.merge_options(baml_options).create_http_request_sync(function_name="TestCompoundRequestTimeout", args={ + "input": input, + }, mode="request") + return result + def TestCompoundTotalTimeout(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.baml_py.HTTPRequest: + result = self.__options.merge_options(baml_options).create_http_request_sync(function_name="TestCompoundTotalTimeout", args={ + "input": input, + }, mode="request") + return result def TestFallbackClient(self, baml_options: BamlCallOptions = {}, ) -> baml_py.baml_py.HTTPRequest: @@ -9878,6 +9977,27 @@ def TestCaching(self, input: str,not_cached: str, "input": input,"not_cached": not_cached, }, mode="stream") return result + def TestCompoundCombinedTimeouts(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.baml_py.HTTPRequest: + result = self.__options.merge_options(baml_options).create_http_request_sync(function_name="TestCompoundCombinedTimeouts", args={ + "input": input, + }, mode="stream") + return result + def TestCompoundRequestTimeout(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.baml_py.HTTPRequest: + result = self.__options.merge_options(baml_options).create_http_request_sync(function_name="TestCompoundRequestTimeout", args={ + "input": input, + }, mode="stream") + return result + def TestCompoundTotalTimeout(self, input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.baml_py.HTTPRequest: + result = self.__options.merge_options(baml_options).create_http_request_sync(function_name="TestCompoundTotalTimeout", args={ + "input": input, + }, mode="stream") + return result def TestFallbackClient(self, baml_options: BamlCallOptions = {}, ) -> baml_py.baml_py.HTTPRequest: diff --git a/integ-tests/python/tests/test_timeouts.py b/integ-tests/python/tests/test_timeouts.py index ffba19d21c..98715ee5c6 100644 --- a/integ-tests/python/tests/test_timeouts.py +++ b/integ-tests/python/tests/test_timeouts.py @@ -121,6 +121,48 @@ async def test_timeout_error_includes_client_name(): assert "TestTimeoutClient" in error_str or "client" in error_str.lower() +@pytest.mark.asyncio +async def test_compound_request_timeout_override(): + """Test that compound client's request_timeout_ms overrides primitives""" + # CompoundTimeoutClient has request_timeout_ms 5000, overriding the tight 10ms timeout of TestTimeoutClient + # It should use the second client (TestZeroTimeoutClient) which should succeed + result = await b.TestCompoundRequestTimeout("hello world") + + # Should have succeeded with the second client + assert result is not None + assert isinstance(result, str) + assert len(result) > 10 # Should have gotten a reasonable response + + +@pytest.mark.asyncio +async def test_compound_total_timeout(): + """Test that compound client's total_timeout_ms is enforced""" + # CompoundTotalTimeoutClient has total_timeout_ms 1000 (1 second) + # Even though the second client has infinite timeout, the total timeout should kick in + start_time = time.time() + + with pytest.raises(BamlTimeoutError) as exc_info: + await b.TestCompoundTotalTimeout("test total timeout") + + elapsed = time.time() - start_time + error = exc_info.value + + # Should have timed out relatively quickly (around 1 second, with some overhead) + assert elapsed < 2.0, f"Total timeout took too long: {elapsed}s" + assert "timeout" in str(error).lower() + + +@pytest.mark.asyncio +async def test_compound_combined_timeouts(): + """Test compound client with both request_timeout_ms and total_timeout_ms""" + result = await b.TestCompoundCombinedTimeouts("test combined timeouts") + + # Should have succeeded with the fallback mechanism + assert result is not None + assert isinstance(result, str) + assert len(result) > 10 # Should have gotten a reasonable response + + # Mock OpenAI-compatible streaming server that sends many chunks with delays # This will send 200 chunks with 10ms between each, taking ~2.5 seconds total # BUT chunk 3 has a 500ms delay to trigger the idle timeout @@ -249,4 +291,4 @@ async def test_timeout_on_idle_with_mock_server(): finally: # Clean up server server.shutdown() - server_thread.join(timeout=1) + server_thread.join(timeout=1) \ No newline at end of file diff --git a/integ-tests/react/baml_client/async_client.ts b/integ-tests/react/baml_client/async_client.ts index cf08ed9075..c39eb762ed 100644 --- a/integ-tests/react/baml_client/async_client.ts +++ b/integ-tests/react/baml_client/async_client.ts @@ -7296,6 +7296,150 @@ export type RecursivePartialNull = MovedRecursivePartialNull } } + async TestCompoundCombinedTimeouts( + input: string, + __baml_options__?: BamlCallOptions + ): Promise { + try { + const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } + const signal = options.signal; + + if (signal?.aborted) { + throw new BamlAbortError('Operation was aborted', signal.reason); + } + + // Check if onTick is provided - route through streaming if so + if (options.onTick) { + const stream = this.stream.TestCompoundCombinedTimeouts( + input, + __baml_options__ + ); + + return await stream.getFinalResponse(); + } + + const collector = options.collector ? (Array.isArray(options.collector) ? options.collector : + [options.collector]) : []; + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + const raw = await this.runtime.callFunction( + "TestCompoundCombinedTimeouts", + { + "input": input + }, + this.ctxManager.cloneContext(), + options.tb?.__tb(), + options.clientRegistry, + collector, + options.tags || {}, + env, + signal, + options.watchers, + ) + return raw.parsed(false) as string + } catch (error) { + throw toBamlError(error); + } + } + + async TestCompoundRequestTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): Promise { + try { + const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } + const signal = options.signal; + + if (signal?.aborted) { + throw new BamlAbortError('Operation was aborted', signal.reason); + } + + // Check if onTick is provided - route through streaming if so + if (options.onTick) { + const stream = this.stream.TestCompoundRequestTimeout( + input, + __baml_options__ + ); + + return await stream.getFinalResponse(); + } + + const collector = options.collector ? (Array.isArray(options.collector) ? options.collector : + [options.collector]) : []; + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + const raw = await this.runtime.callFunction( + "TestCompoundRequestTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + options.tb?.__tb(), + options.clientRegistry, + collector, + options.tags || {}, + env, + signal, + options.watchers, + ) + return raw.parsed(false) as string + } catch (error) { + throw toBamlError(error); + } + } + + async TestCompoundTotalTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): Promise { + try { + const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } + const signal = options.signal; + + if (signal?.aborted) { + throw new BamlAbortError('Operation was aborted', signal.reason); + } + + // Check if onTick is provided - route through streaming if so + if (options.onTick) { + const stream = this.stream.TestCompoundTotalTimeout( + input, + __baml_options__ + ); + + return await stream.getFinalResponse(); + } + + const collector = options.collector ? (Array.isArray(options.collector) ? options.collector : + [options.collector]) : []; + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + const raw = await this.runtime.callFunction( + "TestCompoundTotalTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + options.tb?.__tb(), + options.clientRegistry, + collector, + options.tags || {}, + env, + signal, + options.watchers, + ) + return raw.parsed(false) as string + } catch (error) { + throw toBamlError(error); + } + } + async TestFallbackClient( __baml_options__?: BamlCallOptions @@ -22682,6 +22826,204 @@ export type RecursivePartialNull = MovedRecursivePartialNull } } + TestCompoundCombinedTimeouts( + input: string, + __baml_options__?: BamlCallOptions + ): BamlStream + { + try { + const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } + const signal = options.signal; + + if (signal?.aborted) { + throw new BamlAbortError('Operation was aborted', signal.reason); + } + + let collector = options.collector ? (Array.isArray(options.collector) ? options.collector : + [options.collector]) : []; + + let onTickWrapper: (() => void) | undefined; + + // Create collector and wrap onTick if provided + if (options.onTick) { + const tickCollector = new Collector("on-tick-collector"); + collector = [...collector, tickCollector]; + + onTickWrapper = () => { + const log = tickCollector.last; + if (log) { + try { + options.onTick!("Unknown", log); + } catch (error) { + console.error("Error in onTick callback for TestCompoundCombinedTimeouts", error); + } + } + }; + } + + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + const raw = this.runtime.streamFunction( + "TestCompoundCombinedTimeouts", + { + "input": input + }, + undefined, + this.ctxManager.cloneContext(), + options.tb?.__tb(), + options.clientRegistry, + collector, + options.tags || {}, + env, + signal, + onTickWrapper, + ) + return new BamlStream( + raw, + (a): string => a, + (a): string => a, + this.ctxManager.cloneContext(), + options.signal, + ) + } catch (error) { + throw toBamlError(error); + } + } + + TestCompoundRequestTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): BamlStream + { + try { + const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } + const signal = options.signal; + + if (signal?.aborted) { + throw new BamlAbortError('Operation was aborted', signal.reason); + } + + let collector = options.collector ? (Array.isArray(options.collector) ? options.collector : + [options.collector]) : []; + + let onTickWrapper: (() => void) | undefined; + + // Create collector and wrap onTick if provided + if (options.onTick) { + const tickCollector = new Collector("on-tick-collector"); + collector = [...collector, tickCollector]; + + onTickWrapper = () => { + const log = tickCollector.last; + if (log) { + try { + options.onTick!("Unknown", log); + } catch (error) { + console.error("Error in onTick callback for TestCompoundRequestTimeout", error); + } + } + }; + } + + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + const raw = this.runtime.streamFunction( + "TestCompoundRequestTimeout", + { + "input": input + }, + undefined, + this.ctxManager.cloneContext(), + options.tb?.__tb(), + options.clientRegistry, + collector, + options.tags || {}, + env, + signal, + onTickWrapper, + ) + return new BamlStream( + raw, + (a): string => a, + (a): string => a, + this.ctxManager.cloneContext(), + options.signal, + ) + } catch (error) { + throw toBamlError(error); + } + } + + TestCompoundTotalTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): BamlStream + { + try { + const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } + const signal = options.signal; + + if (signal?.aborted) { + throw new BamlAbortError('Operation was aborted', signal.reason); + } + + let collector = options.collector ? (Array.isArray(options.collector) ? options.collector : + [options.collector]) : []; + + let onTickWrapper: (() => void) | undefined; + + // Create collector and wrap onTick if provided + if (options.onTick) { + const tickCollector = new Collector("on-tick-collector"); + collector = [...collector, tickCollector]; + + onTickWrapper = () => { + const log = tickCollector.last; + if (log) { + try { + options.onTick!("Unknown", log); + } catch (error) { + console.error("Error in onTick callback for TestCompoundTotalTimeout", error); + } + } + }; + } + + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + const raw = this.runtime.streamFunction( + "TestCompoundTotalTimeout", + { + "input": input + }, + undefined, + this.ctxManager.cloneContext(), + options.tb?.__tb(), + options.clientRegistry, + collector, + options.tags || {}, + env, + signal, + onTickWrapper, + ) + return new BamlStream( + raw, + (a): string => a, + (a): string => a, + this.ctxManager.cloneContext(), + options.signal, + ) + } catch (error) { + throw toBamlError(error); + } + } + TestFallbackClient( __baml_options__?: BamlCallOptions diff --git a/integ-tests/react/baml_client/async_request.ts b/integ-tests/react/baml_client/async_request.ts index f4e3620974..a72341b250 100644 --- a/integ-tests/react/baml_client/async_request.ts +++ b/integ-tests/react/baml_client/async_request.ts @@ -3791,6 +3791,81 @@ env?: Record } } + async TestCompoundCombinedTimeouts( + input: string, + __baml_options__?: BamlCallOptions + ): Promise { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return await this.runtime.buildRequest( + "TestCompoundCombinedTimeouts", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + false, + env + ) + } catch (error) { + throw toBamlError(error); + } + } + + async TestCompoundRequestTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): Promise { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return await this.runtime.buildRequest( + "TestCompoundRequestTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + false, + env + ) + } catch (error) { + throw toBamlError(error); + } + } + + async TestCompoundTotalTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): Promise { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return await this.runtime.buildRequest( + "TestCompoundTotalTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + false, + env + ) + } catch (error) { + throw toBamlError(error); + } + } + async TestFallbackClient( __baml_options__?: BamlCallOptions @@ -10397,6 +10472,81 @@ env?: Record } } + async TestCompoundCombinedTimeouts( + input: string, + __baml_options__?: BamlCallOptions + ): Promise { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return await this.runtime.buildRequest( + "TestCompoundCombinedTimeouts", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + true, + env + ) + } catch (error) { + throw toBamlError(error); + } + } + + async TestCompoundRequestTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): Promise { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return await this.runtime.buildRequest( + "TestCompoundRequestTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + true, + env + ) + } catch (error) { + throw toBamlError(error); + } + } + + async TestCompoundTotalTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): Promise { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return await this.runtime.buildRequest( + "TestCompoundTotalTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + true, + env + ) + } catch (error) { + throw toBamlError(error); + } + } + async TestFallbackClient( __baml_options__?: BamlCallOptions diff --git a/integ-tests/react/baml_client/inlinedbaml.ts b/integ-tests/react/baml_client/inlinedbaml.ts index 95f93b5397..ba04fc3b0d 100644 --- a/integ-tests/react/baml_client/inlinedbaml.ts +++ b/integ-tests/react/baml_client/inlinedbaml.ts @@ -37,7 +37,7 @@ const fileMap = { "test-files/aliases/classes.baml": "class TestClassAlias {\n key string @alias(\"key-dash\") @description(#\"\n This is a description for key\n af asdf\n \"#)\n key2 string @alias(\"key21\")\n key3 string @alias(\"key with space\")\n key4 string //unaliased\n key5 string @alias(\"key.with.punctuation/123\")\n}\n\nfunction FnTestClassAlias(input: string) -> TestClassAlias {\n client GPT35\n prompt #\"\n {{ctx.output_format}}\n \"#\n}\n\ntest FnTestClassAlias {\n functions [FnTestClassAlias]\n args {\n input \"example input\"\n }\n}\n", "test-files/aliases/enums.baml": "enum TestEnum {\n A @alias(\"k1\") @description(#\"\n User is angry\n \"#)\n B @alias(\"k22\") @description(#\"\n User is happy\n \"#)\n // tests whether k1 doesnt incorrectly get matched with k11\n C @alias(\"k11\") @description(#\"\n User is sad\n \"#)\n D @alias(\"k44\") @description(#\"\n User is confused\n \"#)\n E @description(#\"\n User is excited\n \"#)\n F @alias(\"k5\") // only alias\n \n G @alias(\"k6\") @description(#\"\n User is bored\n With a long description\n \"#)\n \n @@alias(\"Category\")\n}\n\nfunction FnTestAliasedEnumOutput(input: string) -> TestEnum {\n client GPT35\n prompt #\"\n Classify the user input into the following category\n \n {{ ctx.output_format }}\n\n {{ _.role('user') }}\n {{input}}\n\n {{ _.role('assistant') }}\n Category ID:\n \"#\n}\n\ntest FnTestAliasedEnumOutput {\n functions [FnTestAliasedEnumOutput]\n args {\n input \"mehhhhh\"\n }\n}", "test-files/builtin/fetch.baml": " // class Todo {\n // id int\n // todo string\n // completed bool\n // userId int\n // }\n\n // function GetTodo() -> Todo {\n // Todo {\n // id: 1,\n // }\n // }\n\n // function LlmDescribeTodo(todo: Todo) -> string {\n // client GPT4o\n // prompt #\"Describe the following todo in detail: {{ todo }}\"#\n // }\n\n // function UseGetTodoFunction() -> string {\n // let todo = GetTodo();\n // LlmDescribeTodo(todo)\n // }\n\n // test UseGetTodoFunction() {\n // functions [UseGetTodoFunction]\n // args { }\n // }\n\n // function SearchWikipedia(query: string) -> string {\n // baml.fetch_value(baml.Request {\n // base_url: UrlEncode(\"https://en.wikipedia.org/wiki/Special:Search\", {search query})\n // })\n // }\n\n // function UrlEncode(base_url: string, query_params: map) -> string {\n // client \"openai/o3\"\n // prompt #\"\n // Encode the following base URL and query parameters into a valid URL:\n\n // URL: {{ base_url }}\n\n // Query Params: {{ query_params }}\n\n // Answer with just the final URL, no other text\n // \"#\n // }\n\n // function ConvertUserQuery(query: string) -> string {\n // client GPT4o\n // prompt #\"\n // Convert the following user query into a search query for Wikipedia: {{ query }}\n\n // Ideally it should be a single word that could map to an actual Wikipedia page\n // \"#\n // }\n\n // function LlmFormulateResponse(query: string, search_result: string?) -> string {\n // client GPT4o\n // prompt #\"\n // You are a helpful assistant. Answer the following user query: {{ query }}.\n\n // {% if search_result != \"\" %}\n // Use this Wikipedia search result as a reference for your answer: {{ search_result }}\n // {% endif %}\n // \"#\n // }\n\n // function NeedsWikipediaSearch(query: string) -> bool {\n // client GPT4o\n // prompt #\"Does the following user query need a Wikipedia search?: {{ query }}\"#\n // }\n\n // function ChatResponse(query: string) -> string {\n // let needs_search = NeedsWikipediaSearch(query);\n // let search_result = SearchWikipedia(ConvertUserQuery(query));\n // LlmFormulateResponse(query, search_result)\n // }\n\n // test TestName {\n // functions [ChatResponse]\n // args {\n // query #\"\n // Tell me everything about Rome\n // \"#\n // messages []\n // }\n // }\n\n // test UrlEncode {\n // functions [UrlEncode]\n // args {\n // base_url \"https://en.wikipedia.org/wiki/Special:Search\"\n // query_params {search \"Tell me everything about Rome\"\n // go \"Go\"}\n // }\n // }\n\n // test Convert {\n // functions [ConvertUserQuery]\n // args {\n // query #\"\n // Tell me everything about Rome\n // \"#\n // }\n // }\n", - "test-files/client-timeout-config.baml": "// Client with extremely short timeouts for testing\nclient TestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n connect_timeout_ms 1 // 1ms - will timeout on connect\n request_timeout_ms 10 // 10ms - very likely to timeout\n }\n }\n}\n\n// Function that will timeout\nfunction TestTimeoutError(input: string) -> string {\n client TestTimeoutClient\n prompt #\"\n This is a test that should timeout.\n Please write a very long response about: {{input}}\n \"#\n}\n\n// Client with only request timeout\nclient TestRequestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 50 // 50ms timeout\n }\n }\n}\n\nfunction TestRequestTimeout(input: string) -> string {\n client TestRequestTimeoutClient\n prompt #\"\n Generate a detailed 500 word essay about: {{input}}\n \"#\n}\n\n// Client with zero timeout (infinite)\nclient TestZeroTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 0 // 0 means infinite\n }\n }\n}\n\nfunction TestZeroTimeout(input: string) -> string {\n client TestZeroTimeoutClient\n prompt #\"\n Echo: {{input}}\n \"#\n}\n\n// Fallback client that includes timeout client\nclient TestTimeoutFallbackClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n }\n}\n\nfunction TestTimeoutFallback(input: string) -> string {\n client TestTimeoutFallbackClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Client for streaming timeout tests (Phase 4)\nclient TestStreamingTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n time_to_first_token_timeout_ms 1 // 1ms - extremely short, should always timeout\n idle_timeout_ms 1 // 1ms - extremely short, should always timeout\n }\n }\n}\n\nfunction TestStreamingTimeout(input: string) -> string {\n client TestStreamingTimeoutClient\n prompt #\"\n Stream a long response about: {{input}}\n \"#\n}\n", + "test-files/client-timeout-config.baml": "// Client with extremely short timeouts for testing\nclient TestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n connect_timeout_ms 1 // 1ms - will timeout on connect\n request_timeout_ms 10 // 10ms - very likely to timeout\n }\n }\n}\n\n// Function that will timeout\nfunction TestTimeoutError(input: string) -> string {\n client TestTimeoutClient\n prompt #\"\n This is a test that should timeout.\n Please write a very long response about: {{input}}\n \"#\n}\n\n// Client with only request timeout\nclient TestRequestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 50 // 50ms timeout\n }\n }\n}\n\nfunction TestRequestTimeout(input: string) -> string {\n client TestRequestTimeoutClient\n prompt #\"\n Generate a detailed 500 word essay about: {{input}}\n \"#\n}\n\n// Client with zero timeout (infinite)\nclient TestZeroTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 0 // 0 means infinite\n }\n }\n}\n\nfunction TestZeroTimeout(input: string) -> string {\n client TestZeroTimeoutClient\n prompt #\"\n Echo: {{input}}\n \"#\n}\n\n// Fallback client that includes timeout client\nclient TestTimeoutFallbackClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n }\n}\n\nfunction TestTimeoutFallback(input: string) -> string {\n client TestTimeoutFallbackClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Client for streaming timeout tests (Phase 4)\nclient TestStreamingTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n time_to_first_token_timeout_ms 1 // 1ms - extremely short, should always timeout\n idle_timeout_ms 1 // 1ms - extremely short, should always timeout\n }\n }\n}\n\nfunction TestStreamingTimeout(input: string) -> string {\n client TestStreamingTimeoutClient\n prompt #\"\n Stream a long response about: {{input}}\n \"#\n}\n\n// Compound client with request_timeout_ms override\n// This overrides the tight 10ms timeout of TestTimeoutClient with a more generous 5000ms\nclient CompoundTimeoutClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n request_timeout_ms 5000 // Override the tight timeout from primitives\n }\n }\n}\n\nfunction TestCompoundRequestTimeout(input: string) -> string {\n client CompoundTimeoutClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Compound client with total_timeout_ms\n// Even though the second client has infinite timeout, total_timeout_ms enforces an overall limit\nclient CompoundTotalTimeoutClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n total_timeout_ms 1000 // 1 second total timeout for the entire strategy\n }\n }\n}\n\nfunction TestCompoundTotalTimeout(input: string) -> string {\n client CompoundTotalTimeoutClient\n prompt #\"\n Generate a very long detailed essay about: {{input}}\n \"#\n}\n\n// Compound client with both request_timeout_ms and total_timeout_ms\nclient CompoundCombinedTimeoutsClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n request_timeout_ms 5000 // Generous per-request timeout\n total_timeout_ms 10000 // 10 second total timeout\n }\n }\n}\n\nfunction TestCompoundCombinedTimeouts(input: string) -> string {\n client CompoundCombinedTimeoutsClient\n prompt #\"\n Echo: {{input}}\n \"#\n}", "test-files/comments/comments.baml": "// add some functions, classes, enums etc with comments all over.", "test-files/constraints/constraints.baml": "// These classes and functions test several properties of\n// constrains:\n//\n// - The ability for constrains on fields to pass or fail.\n// - The ability for constraints on bare args and return types to pass or fail.\n// - The ability of constraints to influence which variant of a union is chosen\n// by the parser, when the structure is not sufficient to decide.\n\n/// A Martian organism with an age.\n/// Such a nice type.\nclass Martian {\n /// The age of the Martian in Mars years.\n /// So many Mars years.\n age int @check(young_enough, {{ this < 30 }})\n}\n\nclass Earthling {\n age int @check(earth_aged, {{this < 200 and this > 0}}) @check(no_infants, {{this >1}})\n}\n\n\nclass FooAny {\n planetary_age Martian | Earthling\n certainty int @check(unreasonably_certain, {{this == 102931}})\n species string @check(trivial, {{this == \"Homo sapiens\"}}) @check(regex_good, {{this|regex_match(\"Homo\")}}) @check(regex_bad, {{this|regex_match(\"neanderthalensis\")}})\n}\n\n\nfunction PredictAge(name: string) -> FooAny {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling\n and capitalization). I'll give you a hint: If the name\n is \"Greg\", his age is 41.\n\n {{ctx.output_format}}\n \"#\n}\n\n\nfunction PredictAgeBare(inp: string @assert(big_enough, {{this|length > 1}})) -> int @check(too_big, {{this == 10102}}) {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ inp.name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling).\n\n {{ctx.output_format}}\n \"#\n}\n\nfunction ReturnFailingAssert(inp: int @assert(small_int, {{this < 10}})) -> int @assert(big_int, {{this > 100}}) {\n client GPT35\n prompt #\"\n Return the next integer after {{ inp }}.\n\n {{ctx.output_format}}\n \"#\n}\n\nclass TwoStoriesOneTitle {\n title string\n story_a string @assert(too_long_story, {{this|length > 1000000}} )\n story_b string @assert(too_long_story, {{this|length > 1000000}} )\n}\n\nfunction StreamFailingAssertion(theme: string, length: int) -> TwoStoriesOneTitle {\n client GPT35\n prompt #\"\n Tell me two different stories along the theme of {{ theme }} with the same title.\n Please make each about {{ length }} words long.\n {{ctx.output_format}}\n \"#\n}\n\nclass TwoStoriesOneTitleCheck {\n title string\n story_a string @check(too_long_story, {{this|length > 1000000}} )\n story_b string @check(too_long_story, {{this|length > 1000000}} )\n}\n\nfunction StreamFailingCheck(theme: string, length: int) -> TwoStoriesOneTitleCheck {\n client GPT35\n prompt #\"\n Tell me two different stories along the theme of {{ theme }} with the same title.\n Please make each about {{ length }} words long.\n {{ctx.output_format}}\n \"#\n}\n\nclass BlockConstraint {\n foo int\n bar string\n @@check(cross_field, {{ this.bar|length > this.foo }})\n}\n\nfunction MakeBlockConstraint() -> BlockConstraint {\n client GPT35\n prompt #\"\n Generate an output in the following schema with a short string and a large int.\n\n {{ ctx.output_format }}\n \"#\n}\n\nclass NestedBlockConstraint {\n nbc BlockConstraint\n}\n\nclass BlockConstraintForParam {\n bcfp int\n bcfp2 string\n @@assert(hi, {{ this.bcfp2|length < this.bcfp }})\n}\n\nclass NestedBlockConstraintForParam {\n nbcfp BlockConstraintForParam\n}\n\nfunction MakeNestedBlockConstraint() -> NestedBlockConstraint {\n client GPT35\n prompt #\"Generate an output where the inner foo is 1 and the inner bar is \"hello\".\n {{ ctx.output_format }}\n \"#\n}\n\nfunction UseBlockConstraint(inp: BlockConstraintForParam) -> int {\n client GPT35\n prompt #\"\n Generate 3\n {{ ctx.output_format }}\n \"#\n}\n\nfunction UseNestedBlockConstraint(inp: NestedBlockConstraintForParam) -> int {\n client GPT35\n prompt #\"\n Generate 3\n {{ ctx.output_format }}\n \"#\n}\n", "test-files/constraints/contact-info.baml": "class PhoneNumber {\n value string @assert(valid_phone_number, {{this|regex_match(\"\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}\")}})\n}\n\nclass EmailAddress {\n value string @assert(valid_email, {{this|regex_match(\"^[_]*([a-z0-9]+(\\.|_*)?)+@([a-z][a-z0-9-]+(\\.|-*\\.))+[a-z]{2,6}$\")}})\n}\n\nclass ContactInfo {\n primary PhoneNumber | EmailAddress\n secondary (PhoneNumber | EmailAddress)?\n}\n\nfunction ExtractContactInfo(document: string) -> ContactInfo {\n client GPT35\n prompt #\"\n Extract a primary contact info, and if possible a secondary contact\n info, from this document:\n\n {{ document }}\n\n {{ ctx.output_format }}\n \"#\n}\n", diff --git a/integ-tests/react/baml_client/parser.ts b/integ-tests/react/baml_client/parser.ts index 5498b48009..9be638e168 100644 --- a/integ-tests/react/baml_client/parser.ts +++ b/integ-tests/react/baml_client/parser.ts @@ -3480,6 +3480,75 @@ export class LlmResponseParser { } } + TestCompoundCombinedTimeouts( + llmResponse: string, + __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } + ): string { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.parseLlmResponse( + "TestCompoundCombinedTimeouts", + llmResponse, + false, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + env, + ) as string + } catch (error) { + throw toBamlError(error); + } + } + + TestCompoundRequestTimeout( + llmResponse: string, + __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } + ): string { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.parseLlmResponse( + "TestCompoundRequestTimeout", + llmResponse, + false, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + env, + ) as string + } catch (error) { + throw toBamlError(error); + } + } + + TestCompoundTotalTimeout( + llmResponse: string, + __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } + ): string { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.parseLlmResponse( + "TestCompoundTotalTimeout", + llmResponse, + false, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + env, + ) as string + } catch (error) { + throw toBamlError(error); + } + } + TestFallbackClient( llmResponse: string, __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } @@ -9558,6 +9627,75 @@ export class LlmStreamParser { } } + TestCompoundCombinedTimeouts( + llmResponse: string, + __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } + ): string { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.parseLlmResponse( + "TestCompoundCombinedTimeouts", + llmResponse, + true, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + env, + ) as string + } catch (error) { + throw toBamlError(error); + } + } + + TestCompoundRequestTimeout( + llmResponse: string, + __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } + ): string { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.parseLlmResponse( + "TestCompoundRequestTimeout", + llmResponse, + true, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + env, + ) as string + } catch (error) { + throw toBamlError(error); + } + } + + TestCompoundTotalTimeout( + llmResponse: string, + __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } + ): string { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.parseLlmResponse( + "TestCompoundTotalTimeout", + llmResponse, + true, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + env, + ) as string + } catch (error) { + throw toBamlError(error); + } + } + TestFallbackClient( llmResponse: string, __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } diff --git a/integ-tests/react/baml_client/react/hooks.tsx b/integ-tests/react/baml_client/react/hooks.tsx index 557480cdea..4c987262fd 100644 --- a/integ-tests/react/baml_client/react/hooks.tsx +++ b/integ-tests/react/baml_client/react/hooks.tsx @@ -7875,6 +7875,156 @@ export function useTestCaching( return useBamlAction(action, props as HookInput<'TestCaching', { stream: false }>) } } +/** + * A specialized hook for the TestCompoundCombinedTimeouts BAML function that supports both streaming and non‑streaming responses. + * + * **Input Types:** + * + * - input: string + * + * + * **Return Type:** + * - **Non‑streaming:** string + * - **Streaming Partial:** string + * - **Streaming Final:** string + * + * **Usage Patterns:** + * 1. **Non‑streaming (Default)** + * - Best for quick responses and simple UI updates. + * 2. **Streaming** + * - Ideal for long‑running operations or real‑time feedback. + * + * **Edge Cases:** + * - Ensure robust error handling via `onError`. + * - Handle cases where partial data may be incomplete or missing. + * + * @example + * ```tsx + * // Basic non‑streaming usage: + * const { data, error, isLoading, mutate } = useTestCompoundCombinedTimeouts({ stream: false}); + * + * // Streaming usage: + * const { data, streamData, isLoading, error, mutate } = useTestCompoundCombinedTimeouts({ + * stream: true | undefined, + * onStreamData: (partial) => console.log('Partial update:', partial), + * onFinalData: (final) => console.log('Final result:', final), + * onError: (err) => console.error('Error:', err), + * }); + * ``` + */ +export function useTestCompoundCombinedTimeouts(props: HookInput<'TestCompoundCombinedTimeouts', { stream: false }>): HookOutput<'TestCompoundCombinedTimeouts', { stream: false }> +export function useTestCompoundCombinedTimeouts(props?: HookInput<'TestCompoundCombinedTimeouts', { stream?: true }>): HookOutput<'TestCompoundCombinedTimeouts', { stream: true }> +export function useTestCompoundCombinedTimeouts( + props: HookInput<'TestCompoundCombinedTimeouts', { stream?: boolean }> = {}, +): HookOutput<'TestCompoundCombinedTimeouts', { stream: true }> | HookOutput<'TestCompoundCombinedTimeouts', { stream: false }> { + let action: ServerAction = Actions.TestCompoundCombinedTimeouts; + if (isStreamingProps(props)) { + action = StreamingActions.TestCompoundCombinedTimeouts; + return useBamlAction(action, props) + } else { + return useBamlAction(action, props as HookInput<'TestCompoundCombinedTimeouts', { stream: false }>) + } +} +/** + * A specialized hook for the TestCompoundRequestTimeout BAML function that supports both streaming and non‑streaming responses. + * + * **Input Types:** + * + * - input: string + * + * + * **Return Type:** + * - **Non‑streaming:** string + * - **Streaming Partial:** string + * - **Streaming Final:** string + * + * **Usage Patterns:** + * 1. **Non‑streaming (Default)** + * - Best for quick responses and simple UI updates. + * 2. **Streaming** + * - Ideal for long‑running operations or real‑time feedback. + * + * **Edge Cases:** + * - Ensure robust error handling via `onError`. + * - Handle cases where partial data may be incomplete or missing. + * + * @example + * ```tsx + * // Basic non‑streaming usage: + * const { data, error, isLoading, mutate } = useTestCompoundRequestTimeout({ stream: false}); + * + * // Streaming usage: + * const { data, streamData, isLoading, error, mutate } = useTestCompoundRequestTimeout({ + * stream: true | undefined, + * onStreamData: (partial) => console.log('Partial update:', partial), + * onFinalData: (final) => console.log('Final result:', final), + * onError: (err) => console.error('Error:', err), + * }); + * ``` + */ +export function useTestCompoundRequestTimeout(props: HookInput<'TestCompoundRequestTimeout', { stream: false }>): HookOutput<'TestCompoundRequestTimeout', { stream: false }> +export function useTestCompoundRequestTimeout(props?: HookInput<'TestCompoundRequestTimeout', { stream?: true }>): HookOutput<'TestCompoundRequestTimeout', { stream: true }> +export function useTestCompoundRequestTimeout( + props: HookInput<'TestCompoundRequestTimeout', { stream?: boolean }> = {}, +): HookOutput<'TestCompoundRequestTimeout', { stream: true }> | HookOutput<'TestCompoundRequestTimeout', { stream: false }> { + let action: ServerAction = Actions.TestCompoundRequestTimeout; + if (isStreamingProps(props)) { + action = StreamingActions.TestCompoundRequestTimeout; + return useBamlAction(action, props) + } else { + return useBamlAction(action, props as HookInput<'TestCompoundRequestTimeout', { stream: false }>) + } +} +/** + * A specialized hook for the TestCompoundTotalTimeout BAML function that supports both streaming and non‑streaming responses. + * + * **Input Types:** + * + * - input: string + * + * + * **Return Type:** + * - **Non‑streaming:** string + * - **Streaming Partial:** string + * - **Streaming Final:** string + * + * **Usage Patterns:** + * 1. **Non‑streaming (Default)** + * - Best for quick responses and simple UI updates. + * 2. **Streaming** + * - Ideal for long‑running operations or real‑time feedback. + * + * **Edge Cases:** + * - Ensure robust error handling via `onError`. + * - Handle cases where partial data may be incomplete or missing. + * + * @example + * ```tsx + * // Basic non‑streaming usage: + * const { data, error, isLoading, mutate } = useTestCompoundTotalTimeout({ stream: false}); + * + * // Streaming usage: + * const { data, streamData, isLoading, error, mutate } = useTestCompoundTotalTimeout({ + * stream: true | undefined, + * onStreamData: (partial) => console.log('Partial update:', partial), + * onFinalData: (final) => console.log('Final result:', final), + * onError: (err) => console.error('Error:', err), + * }); + * ``` + */ +export function useTestCompoundTotalTimeout(props: HookInput<'TestCompoundTotalTimeout', { stream: false }>): HookOutput<'TestCompoundTotalTimeout', { stream: false }> +export function useTestCompoundTotalTimeout(props?: HookInput<'TestCompoundTotalTimeout', { stream?: true }>): HookOutput<'TestCompoundTotalTimeout', { stream: true }> +export function useTestCompoundTotalTimeout( + props: HookInput<'TestCompoundTotalTimeout', { stream?: boolean }> = {}, +): HookOutput<'TestCompoundTotalTimeout', { stream: true }> | HookOutput<'TestCompoundTotalTimeout', { stream: false }> { + let action: ServerAction = Actions.TestCompoundTotalTimeout; + if (isStreamingProps(props)) { + action = StreamingActions.TestCompoundTotalTimeout; + return useBamlAction(action, props) + } else { + return useBamlAction(action, props as HookInput<'TestCompoundTotalTimeout', { stream: false }>) + } +} /** * A specialized hook for the TestFallbackClient BAML function that supports both streaming and non‑streaming responses. * diff --git a/integ-tests/react/baml_client/react/server.ts b/integ-tests/react/baml_client/react/server.ts index 50d52f7d47..5d4118253c 100644 --- a/integ-tests/react/baml_client/react/server.ts +++ b/integ-tests/react/baml_client/react/server.ts @@ -2774,6 +2774,60 @@ export const TestCaching = async ( ); }; +/** + * Executes the "TestCompoundCombinedTimeouts" BAML action. + * + * This server action calls the underlying BAML function "TestCompoundCombinedTimeouts" + * with the specified parameters. + * + * @param { string } input - Input parameter. + * + * @returns {Promise} A promise that resolves with the result of the action. + */ +export const TestCompoundCombinedTimeouts = async ( + input: string, +): Promise => { + return b.TestCompoundCombinedTimeouts( + input, + ); +}; + +/** + * Executes the "TestCompoundRequestTimeout" BAML action. + * + * This server action calls the underlying BAML function "TestCompoundRequestTimeout" + * with the specified parameters. + * + * @param { string } input - Input parameter. + * + * @returns {Promise} A promise that resolves with the result of the action. + */ +export const TestCompoundRequestTimeout = async ( + input: string, +): Promise => { + return b.TestCompoundRequestTimeout( + input, + ); +}; + +/** + * Executes the "TestCompoundTotalTimeout" BAML action. + * + * This server action calls the underlying BAML function "TestCompoundTotalTimeout" + * with the specified parameters. + * + * @param { string } input - Input parameter. + * + * @returns {Promise} A promise that resolves with the result of the action. + */ +export const TestCompoundTotalTimeout = async ( + input: string, +): Promise => { + return b.TestCompoundTotalTimeout( + input, + ); +}; + /** * Executes the "TestFallbackClient" BAML action. * diff --git a/integ-tests/react/baml_client/react/server_streaming.ts b/integ-tests/react/baml_client/react/server_streaming.ts index 31bad146a9..c5aeab52bc 100644 --- a/integ-tests/react/baml_client/react/server_streaming.ts +++ b/integ-tests/react/baml_client/react/server_streaming.ts @@ -2924,6 +2924,63 @@ export const TestCaching = async ( return Promise.resolve(stream.toStreamable()); }; +/** + * Executes the streaming variant of the "TestCompoundCombinedTimeouts" BAML action. + * + * This action initiates a streaming response by calling the corresponding + * BAML stream function. The returned stream yields incremental updates. + * + * @param { string } input - Input parameter. + * + * @returns {ReadableStream} A stream that yields incremental updates from the action. + */ +export const TestCompoundCombinedTimeouts = async ( + input: string, +): Promise> => { + const stream = b.stream.TestCompoundCombinedTimeouts( + input, + ); + return Promise.resolve(stream.toStreamable()); +}; + +/** + * Executes the streaming variant of the "TestCompoundRequestTimeout" BAML action. + * + * This action initiates a streaming response by calling the corresponding + * BAML stream function. The returned stream yields incremental updates. + * + * @param { string } input - Input parameter. + * + * @returns {ReadableStream} A stream that yields incremental updates from the action. + */ +export const TestCompoundRequestTimeout = async ( + input: string, +): Promise> => { + const stream = b.stream.TestCompoundRequestTimeout( + input, + ); + return Promise.resolve(stream.toStreamable()); +}; + +/** + * Executes the streaming variant of the "TestCompoundTotalTimeout" BAML action. + * + * This action initiates a streaming response by calling the corresponding + * BAML stream function. The returned stream yields incremental updates. + * + * @param { string } input - Input parameter. + * + * @returns {ReadableStream} A stream that yields incremental updates from the action. + */ +export const TestCompoundTotalTimeout = async ( + input: string, +): Promise> => { + const stream = b.stream.TestCompoundTotalTimeout( + input, + ); + return Promise.resolve(stream.toStreamable()); +}; + /** * Executes the streaming variant of the "TestFallbackClient" BAML action. * diff --git a/integ-tests/react/baml_client/react/server_streaming_types.ts b/integ-tests/react/baml_client/react/server_streaming_types.ts index 6271adc77f..7818c943ce 100644 --- a/integ-tests/react/baml_client/react/server_streaming_types.ts +++ b/integ-tests/react/baml_client/react/server_streaming_types.ts @@ -205,6 +205,9 @@ export type StreamingServerTypes = { TestAzureO3WithMaxCompletionTokens: string, TestAzureWithMaxTokens: string, TestCaching: string, + TestCompoundCombinedTimeouts: string, + TestCompoundRequestTimeout: string, + TestCompoundTotalTimeout: string, TestFallbackClient: string, TestFallbackStrategy: string, TestFallbackToShorthand: string, diff --git a/integ-tests/react/baml_client/sync_client.ts b/integ-tests/react/baml_client/sync_client.ts index 57839f3e25..8b06d35739 100644 --- a/integ-tests/react/baml_client/sync_client.ts +++ b/integ-tests/react/baml_client/sync_client.ts @@ -6396,6 +6396,132 @@ export class BamlSyncClient { } } + TestCompoundCombinedTimeouts( + input: string, + __baml_options__?: BamlCallOptions + ): string { + try { + const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } + const signal = options.signal; + + if (signal?.aborted) { + throw new BamlAbortError('Operation was aborted', signal.reason); + } + + // Check if onTick is provided and reject for sync operations + if (options.onTick) { + throw new Error("onTick is not supported for synchronous functions. Please use the async client instead."); + } + + const collector = options.collector ? (Array.isArray(options.collector) ? options.collector : [options.collector]) : []; + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + const raw = this.runtime.callFunctionSync( + "TestCompoundCombinedTimeouts", + { + "input": input + }, + this.ctxManager.cloneContext(), + options.tb?.__tb(), + options.clientRegistry, + collector, + options.tags || {}, + env, + signal, + options.watchers, + ) + return raw.parsed(false) as string + } catch (error: any) { + throw toBamlError(error); + } + } + + TestCompoundRequestTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): string { + try { + const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } + const signal = options.signal; + + if (signal?.aborted) { + throw new BamlAbortError('Operation was aborted', signal.reason); + } + + // Check if onTick is provided and reject for sync operations + if (options.onTick) { + throw new Error("onTick is not supported for synchronous functions. Please use the async client instead."); + } + + const collector = options.collector ? (Array.isArray(options.collector) ? options.collector : [options.collector]) : []; + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + const raw = this.runtime.callFunctionSync( + "TestCompoundRequestTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + options.tb?.__tb(), + options.clientRegistry, + collector, + options.tags || {}, + env, + signal, + options.watchers, + ) + return raw.parsed(false) as string + } catch (error: any) { + throw toBamlError(error); + } + } + + TestCompoundTotalTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): string { + try { + const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } + const signal = options.signal; + + if (signal?.aborted) { + throw new BamlAbortError('Operation was aborted', signal.reason); + } + + // Check if onTick is provided and reject for sync operations + if (options.onTick) { + throw new Error("onTick is not supported for synchronous functions. Please use the async client instead."); + } + + const collector = options.collector ? (Array.isArray(options.collector) ? options.collector : [options.collector]) : []; + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + const raw = this.runtime.callFunctionSync( + "TestCompoundTotalTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + options.tb?.__tb(), + options.clientRegistry, + collector, + options.tags || {}, + env, + signal, + options.watchers, + ) + return raw.parsed(false) as string + } catch (error: any) { + throw toBamlError(error); + } + } + TestFallbackClient( __baml_options__?: BamlCallOptions diff --git a/integ-tests/react/baml_client/sync_request.ts b/integ-tests/react/baml_client/sync_request.ts index bdc0762fe9..a04d1c204f 100644 --- a/integ-tests/react/baml_client/sync_request.ts +++ b/integ-tests/react/baml_client/sync_request.ts @@ -3787,6 +3787,81 @@ export class HttpRequest { } } + TestCompoundCombinedTimeouts( + input: string, + __baml_options__?: BamlCallOptions + ): HTTPRequest { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.buildRequestSync( + "TestCompoundCombinedTimeouts", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + false, + env, + ) + } catch (error) { + throw toBamlError(error); + } + } + + TestCompoundRequestTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): HTTPRequest { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.buildRequestSync( + "TestCompoundRequestTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + false, + env, + ) + } catch (error) { + throw toBamlError(error); + } + } + + TestCompoundTotalTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): HTTPRequest { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.buildRequestSync( + "TestCompoundTotalTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + false, + env, + ) + } catch (error) { + throw toBamlError(error); + } + } + TestFallbackClient( __baml_options__?: BamlCallOptions @@ -10393,6 +10468,81 @@ export class HttpStreamRequest { } } + TestCompoundCombinedTimeouts( + input: string, + __baml_options__?: BamlCallOptions + ): HTTPRequest { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.buildRequestSync( + "TestCompoundCombinedTimeouts", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + true, + env, + ) + } catch (error) { + throw toBamlError(error); + } + } + + TestCompoundRequestTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): HTTPRequest { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.buildRequestSync( + "TestCompoundRequestTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + true, + env, + ) + } catch (error) { + throw toBamlError(error); + } + } + + TestCompoundTotalTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): HTTPRequest { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.buildRequestSync( + "TestCompoundTotalTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + true, + env, + ) + } catch (error) { + throw toBamlError(error); + } + } + TestFallbackClient( __baml_options__?: BamlCallOptions diff --git a/integ-tests/ruby/baml_client/client.rb b/integ-tests/ruby/baml_client/client.rb index 023e1370b6..52d2b498cd 100644 --- a/integ-tests/ruby/baml_client/client.rb +++ b/integ-tests/ruby/baml_client/client.rb @@ -3796,6 +3796,81 @@ def TestCaching( # We just need to tell sorbet that the return type is the right type parsed.cast_to(String) end + sig {params( + varargs: T.untyped, + input: String, + baml_options: T::Hash[Symbol, T.any(BamlClient::TypeBuilder, Baml::ClientRegistry, T.any(Baml::Collector, T::Array[Baml::Collector]), T::Hash[Symbol, String], T::Hash[String, String])] + ).returns(String)} + def TestCompoundCombinedTimeouts( + *varargs, + input:, + baml_options: {} + ) + if varargs.any? + raise ArgumentError.new("TestCompoundCombinedTimeouts may only be called with keyword arguments") + end + + options = @options.merge_options(BamlCallOptions.from_hash(baml_options)) + + result = options.call_function_sync(function_name: "TestCompoundCombinedTimeouts", args: { + input: input, + }) + + parsed = result.parsed_using_types(BamlClient::Types, BamlClient::PartialTypes, false) + # for sorbet we need to cast to the return type since parsed is now the right value + # We just need to tell sorbet that the return type is the right type + parsed.cast_to(String) + end + sig {params( + varargs: T.untyped, + input: String, + baml_options: T::Hash[Symbol, T.any(BamlClient::TypeBuilder, Baml::ClientRegistry, T.any(Baml::Collector, T::Array[Baml::Collector]), T::Hash[Symbol, String], T::Hash[String, String])] + ).returns(String)} + def TestCompoundRequestTimeout( + *varargs, + input:, + baml_options: {} + ) + if varargs.any? + raise ArgumentError.new("TestCompoundRequestTimeout may only be called with keyword arguments") + end + + options = @options.merge_options(BamlCallOptions.from_hash(baml_options)) + + result = options.call_function_sync(function_name: "TestCompoundRequestTimeout", args: { + input: input, + }) + + parsed = result.parsed_using_types(BamlClient::Types, BamlClient::PartialTypes, false) + # for sorbet we need to cast to the return type since parsed is now the right value + # We just need to tell sorbet that the return type is the right type + parsed.cast_to(String) + end + sig {params( + varargs: T.untyped, + input: String, + baml_options: T::Hash[Symbol, T.any(BamlClient::TypeBuilder, Baml::ClientRegistry, T.any(Baml::Collector, T::Array[Baml::Collector]), T::Hash[Symbol, String], T::Hash[String, String])] + ).returns(String)} + def TestCompoundTotalTimeout( + *varargs, + input:, + baml_options: {} + ) + if varargs.any? + raise ArgumentError.new("TestCompoundTotalTimeout may only be called with keyword arguments") + end + + options = @options.merge_options(BamlCallOptions.from_hash(baml_options)) + + result = options.call_function_sync(function_name: "TestCompoundTotalTimeout", args: { + input: input, + }) + + parsed = result.parsed_using_types(BamlClient::Types, BamlClient::PartialTypes, false) + # for sorbet we need to cast to the return type since parsed is now the right value + # We just need to tell sorbet that the return type is the right type + parsed.cast_to(String) + end sig {params( varargs: T.untyped, @@ -9632,6 +9707,81 @@ def TestCaching( ctx_manager: ctx ) end + sig {params( + varargs: T.untyped, + input: String, + baml_options: T::Hash[Symbol, T.any(BamlClient::TypeBuilder, Baml::ClientRegistry, T.any(Baml::Collector, T::Array[Baml::Collector]), T::Hash[Symbol, String], T::Hash[String, String])] + ).returns(Baml::BamlStream[String, String])} + def TestCompoundCombinedTimeouts( + *varargs, + input:, + baml_options: {} + ) + if varargs.any? + raise ArgumentError.new("TestCompoundCombinedTimeouts may only be called with keyword arguments") + end + + options = @options.merge_options(BamlCallOptions.from_hash(baml_options)) + + ctx, result = options.create_sync_stream(function_name: "TestCompoundCombinedTimeouts", args: { + input: input, + }) + + Baml::BamlStream[String, String].new( + ffi_stream: result, + ctx_manager: ctx + ) + end + sig {params( + varargs: T.untyped, + input: String, + baml_options: T::Hash[Symbol, T.any(BamlClient::TypeBuilder, Baml::ClientRegistry, T.any(Baml::Collector, T::Array[Baml::Collector]), T::Hash[Symbol, String], T::Hash[String, String])] + ).returns(Baml::BamlStream[String, String])} + def TestCompoundRequestTimeout( + *varargs, + input:, + baml_options: {} + ) + if varargs.any? + raise ArgumentError.new("TestCompoundRequestTimeout may only be called with keyword arguments") + end + + options = @options.merge_options(BamlCallOptions.from_hash(baml_options)) + + ctx, result = options.create_sync_stream(function_name: "TestCompoundRequestTimeout", args: { + input: input, + }) + + Baml::BamlStream[String, String].new( + ffi_stream: result, + ctx_manager: ctx + ) + end + sig {params( + varargs: T.untyped, + input: String, + baml_options: T::Hash[Symbol, T.any(BamlClient::TypeBuilder, Baml::ClientRegistry, T.any(Baml::Collector, T::Array[Baml::Collector]), T::Hash[Symbol, String], T::Hash[String, String])] + ).returns(Baml::BamlStream[String, String])} + def TestCompoundTotalTimeout( + *varargs, + input:, + baml_options: {} + ) + if varargs.any? + raise ArgumentError.new("TestCompoundTotalTimeout may only be called with keyword arguments") + end + + options = @options.merge_options(BamlCallOptions.from_hash(baml_options)) + + ctx, result = options.create_sync_stream(function_name: "TestCompoundTotalTimeout", args: { + input: input, + }) + + Baml::BamlStream[String, String].new( + ffi_stream: result, + ctx_manager: ctx + ) + end sig {params( varargs: T.untyped, diff --git a/integ-tests/typescript-esm/baml_client/async_client.ts b/integ-tests/typescript-esm/baml_client/async_client.ts index 076dc734b7..e7b134016b 100644 --- a/integ-tests/typescript-esm/baml_client/async_client.ts +++ b/integ-tests/typescript-esm/baml_client/async_client.ts @@ -7296,6 +7296,150 @@ export type RecursivePartialNull = MovedRecursivePartialNull } } + async TestCompoundCombinedTimeouts( + input: string, + __baml_options__?: BamlCallOptions + ): Promise { + try { + const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } + const signal = options.signal; + + if (signal?.aborted) { + throw new BamlAbortError('Operation was aborted', signal.reason); + } + + // Check if onTick is provided - route through streaming if so + if (options.onTick) { + const stream = this.stream.TestCompoundCombinedTimeouts( + input, + __baml_options__ + ); + + return await stream.getFinalResponse(); + } + + const collector = options.collector ? (Array.isArray(options.collector) ? options.collector : + [options.collector]) : []; + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + const raw = await this.runtime.callFunction( + "TestCompoundCombinedTimeouts", + { + "input": input + }, + this.ctxManager.cloneContext(), + options.tb?.__tb(), + options.clientRegistry, + collector, + options.tags || {}, + env, + signal, + options.watchers, + ) + return raw.parsed(false) as string + } catch (error) { + throw toBamlError(error); + } + } + + async TestCompoundRequestTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): Promise { + try { + const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } + const signal = options.signal; + + if (signal?.aborted) { + throw new BamlAbortError('Operation was aborted', signal.reason); + } + + // Check if onTick is provided - route through streaming if so + if (options.onTick) { + const stream = this.stream.TestCompoundRequestTimeout( + input, + __baml_options__ + ); + + return await stream.getFinalResponse(); + } + + const collector = options.collector ? (Array.isArray(options.collector) ? options.collector : + [options.collector]) : []; + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + const raw = await this.runtime.callFunction( + "TestCompoundRequestTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + options.tb?.__tb(), + options.clientRegistry, + collector, + options.tags || {}, + env, + signal, + options.watchers, + ) + return raw.parsed(false) as string + } catch (error) { + throw toBamlError(error); + } + } + + async TestCompoundTotalTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): Promise { + try { + const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } + const signal = options.signal; + + if (signal?.aborted) { + throw new BamlAbortError('Operation was aborted', signal.reason); + } + + // Check if onTick is provided - route through streaming if so + if (options.onTick) { + const stream = this.stream.TestCompoundTotalTimeout( + input, + __baml_options__ + ); + + return await stream.getFinalResponse(); + } + + const collector = options.collector ? (Array.isArray(options.collector) ? options.collector : + [options.collector]) : []; + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + const raw = await this.runtime.callFunction( + "TestCompoundTotalTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + options.tb?.__tb(), + options.clientRegistry, + collector, + options.tags || {}, + env, + signal, + options.watchers, + ) + return raw.parsed(false) as string + } catch (error) { + throw toBamlError(error); + } + } + async TestFallbackClient( __baml_options__?: BamlCallOptions @@ -22682,6 +22826,204 @@ export type RecursivePartialNull = MovedRecursivePartialNull } } + TestCompoundCombinedTimeouts( + input: string, + __baml_options__?: BamlCallOptions + ): BamlStream + { + try { + const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } + const signal = options.signal; + + if (signal?.aborted) { + throw new BamlAbortError('Operation was aborted', signal.reason); + } + + let collector = options.collector ? (Array.isArray(options.collector) ? options.collector : + [options.collector]) : []; + + let onTickWrapper: (() => void) | undefined; + + // Create collector and wrap onTick if provided + if (options.onTick) { + const tickCollector = new Collector("on-tick-collector"); + collector = [...collector, tickCollector]; + + onTickWrapper = () => { + const log = tickCollector.last; + if (log) { + try { + options.onTick!("Unknown", log); + } catch (error) { + console.error("Error in onTick callback for TestCompoundCombinedTimeouts", error); + } + } + }; + } + + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + const raw = this.runtime.streamFunction( + "TestCompoundCombinedTimeouts", + { + "input": input + }, + undefined, + this.ctxManager.cloneContext(), + options.tb?.__tb(), + options.clientRegistry, + collector, + options.tags || {}, + env, + signal, + onTickWrapper, + ) + return new BamlStream( + raw, + (a): string => a, + (a): string => a, + this.ctxManager.cloneContext(), + options.signal, + ) + } catch (error) { + throw toBamlError(error); + } + } + + TestCompoundRequestTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): BamlStream + { + try { + const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } + const signal = options.signal; + + if (signal?.aborted) { + throw new BamlAbortError('Operation was aborted', signal.reason); + } + + let collector = options.collector ? (Array.isArray(options.collector) ? options.collector : + [options.collector]) : []; + + let onTickWrapper: (() => void) | undefined; + + // Create collector and wrap onTick if provided + if (options.onTick) { + const tickCollector = new Collector("on-tick-collector"); + collector = [...collector, tickCollector]; + + onTickWrapper = () => { + const log = tickCollector.last; + if (log) { + try { + options.onTick!("Unknown", log); + } catch (error) { + console.error("Error in onTick callback for TestCompoundRequestTimeout", error); + } + } + }; + } + + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + const raw = this.runtime.streamFunction( + "TestCompoundRequestTimeout", + { + "input": input + }, + undefined, + this.ctxManager.cloneContext(), + options.tb?.__tb(), + options.clientRegistry, + collector, + options.tags || {}, + env, + signal, + onTickWrapper, + ) + return new BamlStream( + raw, + (a): string => a, + (a): string => a, + this.ctxManager.cloneContext(), + options.signal, + ) + } catch (error) { + throw toBamlError(error); + } + } + + TestCompoundTotalTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): BamlStream + { + try { + const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } + const signal = options.signal; + + if (signal?.aborted) { + throw new BamlAbortError('Operation was aborted', signal.reason); + } + + let collector = options.collector ? (Array.isArray(options.collector) ? options.collector : + [options.collector]) : []; + + let onTickWrapper: (() => void) | undefined; + + // Create collector and wrap onTick if provided + if (options.onTick) { + const tickCollector = new Collector("on-tick-collector"); + collector = [...collector, tickCollector]; + + onTickWrapper = () => { + const log = tickCollector.last; + if (log) { + try { + options.onTick!("Unknown", log); + } catch (error) { + console.error("Error in onTick callback for TestCompoundTotalTimeout", error); + } + } + }; + } + + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + const raw = this.runtime.streamFunction( + "TestCompoundTotalTimeout", + { + "input": input + }, + undefined, + this.ctxManager.cloneContext(), + options.tb?.__tb(), + options.clientRegistry, + collector, + options.tags || {}, + env, + signal, + onTickWrapper, + ) + return new BamlStream( + raw, + (a): string => a, + (a): string => a, + this.ctxManager.cloneContext(), + options.signal, + ) + } catch (error) { + throw toBamlError(error); + } + } + TestFallbackClient( __baml_options__?: BamlCallOptions diff --git a/integ-tests/typescript-esm/baml_client/async_request.ts b/integ-tests/typescript-esm/baml_client/async_request.ts index a56c920f13..5b7c07e94a 100644 --- a/integ-tests/typescript-esm/baml_client/async_request.ts +++ b/integ-tests/typescript-esm/baml_client/async_request.ts @@ -3791,6 +3791,81 @@ env?: Record } } + async TestCompoundCombinedTimeouts( + input: string, + __baml_options__?: BamlCallOptions + ): Promise { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return await this.runtime.buildRequest( + "TestCompoundCombinedTimeouts", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + false, + env + ) + } catch (error) { + throw toBamlError(error); + } + } + + async TestCompoundRequestTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): Promise { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return await this.runtime.buildRequest( + "TestCompoundRequestTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + false, + env + ) + } catch (error) { + throw toBamlError(error); + } + } + + async TestCompoundTotalTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): Promise { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return await this.runtime.buildRequest( + "TestCompoundTotalTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + false, + env + ) + } catch (error) { + throw toBamlError(error); + } + } + async TestFallbackClient( __baml_options__?: BamlCallOptions @@ -10397,6 +10472,81 @@ env?: Record } } + async TestCompoundCombinedTimeouts( + input: string, + __baml_options__?: BamlCallOptions + ): Promise { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return await this.runtime.buildRequest( + "TestCompoundCombinedTimeouts", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + true, + env + ) + } catch (error) { + throw toBamlError(error); + } + } + + async TestCompoundRequestTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): Promise { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return await this.runtime.buildRequest( + "TestCompoundRequestTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + true, + env + ) + } catch (error) { + throw toBamlError(error); + } + } + + async TestCompoundTotalTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): Promise { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return await this.runtime.buildRequest( + "TestCompoundTotalTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + true, + env + ) + } catch (error) { + throw toBamlError(error); + } + } + async TestFallbackClient( __baml_options__?: BamlCallOptions diff --git a/integ-tests/typescript-esm/baml_client/inlinedbaml.ts b/integ-tests/typescript-esm/baml_client/inlinedbaml.ts index 95f93b5397..ba04fc3b0d 100644 --- a/integ-tests/typescript-esm/baml_client/inlinedbaml.ts +++ b/integ-tests/typescript-esm/baml_client/inlinedbaml.ts @@ -37,7 +37,7 @@ const fileMap = { "test-files/aliases/classes.baml": "class TestClassAlias {\n key string @alias(\"key-dash\") @description(#\"\n This is a description for key\n af asdf\n \"#)\n key2 string @alias(\"key21\")\n key3 string @alias(\"key with space\")\n key4 string //unaliased\n key5 string @alias(\"key.with.punctuation/123\")\n}\n\nfunction FnTestClassAlias(input: string) -> TestClassAlias {\n client GPT35\n prompt #\"\n {{ctx.output_format}}\n \"#\n}\n\ntest FnTestClassAlias {\n functions [FnTestClassAlias]\n args {\n input \"example input\"\n }\n}\n", "test-files/aliases/enums.baml": "enum TestEnum {\n A @alias(\"k1\") @description(#\"\n User is angry\n \"#)\n B @alias(\"k22\") @description(#\"\n User is happy\n \"#)\n // tests whether k1 doesnt incorrectly get matched with k11\n C @alias(\"k11\") @description(#\"\n User is sad\n \"#)\n D @alias(\"k44\") @description(#\"\n User is confused\n \"#)\n E @description(#\"\n User is excited\n \"#)\n F @alias(\"k5\") // only alias\n \n G @alias(\"k6\") @description(#\"\n User is bored\n With a long description\n \"#)\n \n @@alias(\"Category\")\n}\n\nfunction FnTestAliasedEnumOutput(input: string) -> TestEnum {\n client GPT35\n prompt #\"\n Classify the user input into the following category\n \n {{ ctx.output_format }}\n\n {{ _.role('user') }}\n {{input}}\n\n {{ _.role('assistant') }}\n Category ID:\n \"#\n}\n\ntest FnTestAliasedEnumOutput {\n functions [FnTestAliasedEnumOutput]\n args {\n input \"mehhhhh\"\n }\n}", "test-files/builtin/fetch.baml": " // class Todo {\n // id int\n // todo string\n // completed bool\n // userId int\n // }\n\n // function GetTodo() -> Todo {\n // Todo {\n // id: 1,\n // }\n // }\n\n // function LlmDescribeTodo(todo: Todo) -> string {\n // client GPT4o\n // prompt #\"Describe the following todo in detail: {{ todo }}\"#\n // }\n\n // function UseGetTodoFunction() -> string {\n // let todo = GetTodo();\n // LlmDescribeTodo(todo)\n // }\n\n // test UseGetTodoFunction() {\n // functions [UseGetTodoFunction]\n // args { }\n // }\n\n // function SearchWikipedia(query: string) -> string {\n // baml.fetch_value(baml.Request {\n // base_url: UrlEncode(\"https://en.wikipedia.org/wiki/Special:Search\", {search query})\n // })\n // }\n\n // function UrlEncode(base_url: string, query_params: map) -> string {\n // client \"openai/o3\"\n // prompt #\"\n // Encode the following base URL and query parameters into a valid URL:\n\n // URL: {{ base_url }}\n\n // Query Params: {{ query_params }}\n\n // Answer with just the final URL, no other text\n // \"#\n // }\n\n // function ConvertUserQuery(query: string) -> string {\n // client GPT4o\n // prompt #\"\n // Convert the following user query into a search query for Wikipedia: {{ query }}\n\n // Ideally it should be a single word that could map to an actual Wikipedia page\n // \"#\n // }\n\n // function LlmFormulateResponse(query: string, search_result: string?) -> string {\n // client GPT4o\n // prompt #\"\n // You are a helpful assistant. Answer the following user query: {{ query }}.\n\n // {% if search_result != \"\" %}\n // Use this Wikipedia search result as a reference for your answer: {{ search_result }}\n // {% endif %}\n // \"#\n // }\n\n // function NeedsWikipediaSearch(query: string) -> bool {\n // client GPT4o\n // prompt #\"Does the following user query need a Wikipedia search?: {{ query }}\"#\n // }\n\n // function ChatResponse(query: string) -> string {\n // let needs_search = NeedsWikipediaSearch(query);\n // let search_result = SearchWikipedia(ConvertUserQuery(query));\n // LlmFormulateResponse(query, search_result)\n // }\n\n // test TestName {\n // functions [ChatResponse]\n // args {\n // query #\"\n // Tell me everything about Rome\n // \"#\n // messages []\n // }\n // }\n\n // test UrlEncode {\n // functions [UrlEncode]\n // args {\n // base_url \"https://en.wikipedia.org/wiki/Special:Search\"\n // query_params {search \"Tell me everything about Rome\"\n // go \"Go\"}\n // }\n // }\n\n // test Convert {\n // functions [ConvertUserQuery]\n // args {\n // query #\"\n // Tell me everything about Rome\n // \"#\n // }\n // }\n", - "test-files/client-timeout-config.baml": "// Client with extremely short timeouts for testing\nclient TestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n connect_timeout_ms 1 // 1ms - will timeout on connect\n request_timeout_ms 10 // 10ms - very likely to timeout\n }\n }\n}\n\n// Function that will timeout\nfunction TestTimeoutError(input: string) -> string {\n client TestTimeoutClient\n prompt #\"\n This is a test that should timeout.\n Please write a very long response about: {{input}}\n \"#\n}\n\n// Client with only request timeout\nclient TestRequestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 50 // 50ms timeout\n }\n }\n}\n\nfunction TestRequestTimeout(input: string) -> string {\n client TestRequestTimeoutClient\n prompt #\"\n Generate a detailed 500 word essay about: {{input}}\n \"#\n}\n\n// Client with zero timeout (infinite)\nclient TestZeroTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 0 // 0 means infinite\n }\n }\n}\n\nfunction TestZeroTimeout(input: string) -> string {\n client TestZeroTimeoutClient\n prompt #\"\n Echo: {{input}}\n \"#\n}\n\n// Fallback client that includes timeout client\nclient TestTimeoutFallbackClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n }\n}\n\nfunction TestTimeoutFallback(input: string) -> string {\n client TestTimeoutFallbackClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Client for streaming timeout tests (Phase 4)\nclient TestStreamingTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n time_to_first_token_timeout_ms 1 // 1ms - extremely short, should always timeout\n idle_timeout_ms 1 // 1ms - extremely short, should always timeout\n }\n }\n}\n\nfunction TestStreamingTimeout(input: string) -> string {\n client TestStreamingTimeoutClient\n prompt #\"\n Stream a long response about: {{input}}\n \"#\n}\n", + "test-files/client-timeout-config.baml": "// Client with extremely short timeouts for testing\nclient TestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n connect_timeout_ms 1 // 1ms - will timeout on connect\n request_timeout_ms 10 // 10ms - very likely to timeout\n }\n }\n}\n\n// Function that will timeout\nfunction TestTimeoutError(input: string) -> string {\n client TestTimeoutClient\n prompt #\"\n This is a test that should timeout.\n Please write a very long response about: {{input}}\n \"#\n}\n\n// Client with only request timeout\nclient TestRequestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 50 // 50ms timeout\n }\n }\n}\n\nfunction TestRequestTimeout(input: string) -> string {\n client TestRequestTimeoutClient\n prompt #\"\n Generate a detailed 500 word essay about: {{input}}\n \"#\n}\n\n// Client with zero timeout (infinite)\nclient TestZeroTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 0 // 0 means infinite\n }\n }\n}\n\nfunction TestZeroTimeout(input: string) -> string {\n client TestZeroTimeoutClient\n prompt #\"\n Echo: {{input}}\n \"#\n}\n\n// Fallback client that includes timeout client\nclient TestTimeoutFallbackClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n }\n}\n\nfunction TestTimeoutFallback(input: string) -> string {\n client TestTimeoutFallbackClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Client for streaming timeout tests (Phase 4)\nclient TestStreamingTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n time_to_first_token_timeout_ms 1 // 1ms - extremely short, should always timeout\n idle_timeout_ms 1 // 1ms - extremely short, should always timeout\n }\n }\n}\n\nfunction TestStreamingTimeout(input: string) -> string {\n client TestStreamingTimeoutClient\n prompt #\"\n Stream a long response about: {{input}}\n \"#\n}\n\n// Compound client with request_timeout_ms override\n// This overrides the tight 10ms timeout of TestTimeoutClient with a more generous 5000ms\nclient CompoundTimeoutClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n request_timeout_ms 5000 // Override the tight timeout from primitives\n }\n }\n}\n\nfunction TestCompoundRequestTimeout(input: string) -> string {\n client CompoundTimeoutClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Compound client with total_timeout_ms\n// Even though the second client has infinite timeout, total_timeout_ms enforces an overall limit\nclient CompoundTotalTimeoutClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n total_timeout_ms 1000 // 1 second total timeout for the entire strategy\n }\n }\n}\n\nfunction TestCompoundTotalTimeout(input: string) -> string {\n client CompoundTotalTimeoutClient\n prompt #\"\n Generate a very long detailed essay about: {{input}}\n \"#\n}\n\n// Compound client with both request_timeout_ms and total_timeout_ms\nclient CompoundCombinedTimeoutsClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n request_timeout_ms 5000 // Generous per-request timeout\n total_timeout_ms 10000 // 10 second total timeout\n }\n }\n}\n\nfunction TestCompoundCombinedTimeouts(input: string) -> string {\n client CompoundCombinedTimeoutsClient\n prompt #\"\n Echo: {{input}}\n \"#\n}", "test-files/comments/comments.baml": "// add some functions, classes, enums etc with comments all over.", "test-files/constraints/constraints.baml": "// These classes and functions test several properties of\n// constrains:\n//\n// - The ability for constrains on fields to pass or fail.\n// - The ability for constraints on bare args and return types to pass or fail.\n// - The ability of constraints to influence which variant of a union is chosen\n// by the parser, when the structure is not sufficient to decide.\n\n/// A Martian organism with an age.\n/// Such a nice type.\nclass Martian {\n /// The age of the Martian in Mars years.\n /// So many Mars years.\n age int @check(young_enough, {{ this < 30 }})\n}\n\nclass Earthling {\n age int @check(earth_aged, {{this < 200 and this > 0}}) @check(no_infants, {{this >1}})\n}\n\n\nclass FooAny {\n planetary_age Martian | Earthling\n certainty int @check(unreasonably_certain, {{this == 102931}})\n species string @check(trivial, {{this == \"Homo sapiens\"}}) @check(regex_good, {{this|regex_match(\"Homo\")}}) @check(regex_bad, {{this|regex_match(\"neanderthalensis\")}})\n}\n\n\nfunction PredictAge(name: string) -> FooAny {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling\n and capitalization). I'll give you a hint: If the name\n is \"Greg\", his age is 41.\n\n {{ctx.output_format}}\n \"#\n}\n\n\nfunction PredictAgeBare(inp: string @assert(big_enough, {{this|length > 1}})) -> int @check(too_big, {{this == 10102}}) {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ inp.name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling).\n\n {{ctx.output_format}}\n \"#\n}\n\nfunction ReturnFailingAssert(inp: int @assert(small_int, {{this < 10}})) -> int @assert(big_int, {{this > 100}}) {\n client GPT35\n prompt #\"\n Return the next integer after {{ inp }}.\n\n {{ctx.output_format}}\n \"#\n}\n\nclass TwoStoriesOneTitle {\n title string\n story_a string @assert(too_long_story, {{this|length > 1000000}} )\n story_b string @assert(too_long_story, {{this|length > 1000000}} )\n}\n\nfunction StreamFailingAssertion(theme: string, length: int) -> TwoStoriesOneTitle {\n client GPT35\n prompt #\"\n Tell me two different stories along the theme of {{ theme }} with the same title.\n Please make each about {{ length }} words long.\n {{ctx.output_format}}\n \"#\n}\n\nclass TwoStoriesOneTitleCheck {\n title string\n story_a string @check(too_long_story, {{this|length > 1000000}} )\n story_b string @check(too_long_story, {{this|length > 1000000}} )\n}\n\nfunction StreamFailingCheck(theme: string, length: int) -> TwoStoriesOneTitleCheck {\n client GPT35\n prompt #\"\n Tell me two different stories along the theme of {{ theme }} with the same title.\n Please make each about {{ length }} words long.\n {{ctx.output_format}}\n \"#\n}\n\nclass BlockConstraint {\n foo int\n bar string\n @@check(cross_field, {{ this.bar|length > this.foo }})\n}\n\nfunction MakeBlockConstraint() -> BlockConstraint {\n client GPT35\n prompt #\"\n Generate an output in the following schema with a short string and a large int.\n\n {{ ctx.output_format }}\n \"#\n}\n\nclass NestedBlockConstraint {\n nbc BlockConstraint\n}\n\nclass BlockConstraintForParam {\n bcfp int\n bcfp2 string\n @@assert(hi, {{ this.bcfp2|length < this.bcfp }})\n}\n\nclass NestedBlockConstraintForParam {\n nbcfp BlockConstraintForParam\n}\n\nfunction MakeNestedBlockConstraint() -> NestedBlockConstraint {\n client GPT35\n prompt #\"Generate an output where the inner foo is 1 and the inner bar is \"hello\".\n {{ ctx.output_format }}\n \"#\n}\n\nfunction UseBlockConstraint(inp: BlockConstraintForParam) -> int {\n client GPT35\n prompt #\"\n Generate 3\n {{ ctx.output_format }}\n \"#\n}\n\nfunction UseNestedBlockConstraint(inp: NestedBlockConstraintForParam) -> int {\n client GPT35\n prompt #\"\n Generate 3\n {{ ctx.output_format }}\n \"#\n}\n", "test-files/constraints/contact-info.baml": "class PhoneNumber {\n value string @assert(valid_phone_number, {{this|regex_match(\"\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}\")}})\n}\n\nclass EmailAddress {\n value string @assert(valid_email, {{this|regex_match(\"^[_]*([a-z0-9]+(\\.|_*)?)+@([a-z][a-z0-9-]+(\\.|-*\\.))+[a-z]{2,6}$\")}})\n}\n\nclass ContactInfo {\n primary PhoneNumber | EmailAddress\n secondary (PhoneNumber | EmailAddress)?\n}\n\nfunction ExtractContactInfo(document: string) -> ContactInfo {\n client GPT35\n prompt #\"\n Extract a primary contact info, and if possible a secondary contact\n info, from this document:\n\n {{ document }}\n\n {{ ctx.output_format }}\n \"#\n}\n", diff --git a/integ-tests/typescript-esm/baml_client/parser.ts b/integ-tests/typescript-esm/baml_client/parser.ts index b7ab141a90..434d9a8883 100644 --- a/integ-tests/typescript-esm/baml_client/parser.ts +++ b/integ-tests/typescript-esm/baml_client/parser.ts @@ -3480,6 +3480,75 @@ export class LlmResponseParser { } } + TestCompoundCombinedTimeouts( + llmResponse: string, + __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } + ): string { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.parseLlmResponse( + "TestCompoundCombinedTimeouts", + llmResponse, + false, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + env, + ) as string + } catch (error) { + throw toBamlError(error); + } + } + + TestCompoundRequestTimeout( + llmResponse: string, + __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } + ): string { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.parseLlmResponse( + "TestCompoundRequestTimeout", + llmResponse, + false, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + env, + ) as string + } catch (error) { + throw toBamlError(error); + } + } + + TestCompoundTotalTimeout( + llmResponse: string, + __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } + ): string { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.parseLlmResponse( + "TestCompoundTotalTimeout", + llmResponse, + false, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + env, + ) as string + } catch (error) { + throw toBamlError(error); + } + } + TestFallbackClient( llmResponse: string, __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } @@ -9558,6 +9627,75 @@ export class LlmStreamParser { } } + TestCompoundCombinedTimeouts( + llmResponse: string, + __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } + ): string { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.parseLlmResponse( + "TestCompoundCombinedTimeouts", + llmResponse, + true, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + env, + ) as string + } catch (error) { + throw toBamlError(error); + } + } + + TestCompoundRequestTimeout( + llmResponse: string, + __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } + ): string { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.parseLlmResponse( + "TestCompoundRequestTimeout", + llmResponse, + true, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + env, + ) as string + } catch (error) { + throw toBamlError(error); + } + } + + TestCompoundTotalTimeout( + llmResponse: string, + __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } + ): string { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.parseLlmResponse( + "TestCompoundTotalTimeout", + llmResponse, + true, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + env, + ) as string + } catch (error) { + throw toBamlError(error); + } + } + TestFallbackClient( llmResponse: string, __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } diff --git a/integ-tests/typescript-esm/baml_client/sync_client.ts b/integ-tests/typescript-esm/baml_client/sync_client.ts index fb71581b3a..659d7e1724 100644 --- a/integ-tests/typescript-esm/baml_client/sync_client.ts +++ b/integ-tests/typescript-esm/baml_client/sync_client.ts @@ -6396,6 +6396,132 @@ export class BamlSyncClient { } } + TestCompoundCombinedTimeouts( + input: string, + __baml_options__?: BamlCallOptions + ): string { + try { + const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } + const signal = options.signal; + + if (signal?.aborted) { + throw new BamlAbortError('Operation was aborted', signal.reason); + } + + // Check if onTick is provided and reject for sync operations + if (options.onTick) { + throw new Error("onTick is not supported for synchronous functions. Please use the async client instead."); + } + + const collector = options.collector ? (Array.isArray(options.collector) ? options.collector : [options.collector]) : []; + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + const raw = this.runtime.callFunctionSync( + "TestCompoundCombinedTimeouts", + { + "input": input + }, + this.ctxManager.cloneContext(), + options.tb?.__tb(), + options.clientRegistry, + collector, + options.tags || {}, + env, + signal, + options.watchers, + ) + return raw.parsed(false) as string + } catch (error: any) { + throw toBamlError(error); + } + } + + TestCompoundRequestTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): string { + try { + const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } + const signal = options.signal; + + if (signal?.aborted) { + throw new BamlAbortError('Operation was aborted', signal.reason); + } + + // Check if onTick is provided and reject for sync operations + if (options.onTick) { + throw new Error("onTick is not supported for synchronous functions. Please use the async client instead."); + } + + const collector = options.collector ? (Array.isArray(options.collector) ? options.collector : [options.collector]) : []; + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + const raw = this.runtime.callFunctionSync( + "TestCompoundRequestTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + options.tb?.__tb(), + options.clientRegistry, + collector, + options.tags || {}, + env, + signal, + options.watchers, + ) + return raw.parsed(false) as string + } catch (error: any) { + throw toBamlError(error); + } + } + + TestCompoundTotalTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): string { + try { + const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } + const signal = options.signal; + + if (signal?.aborted) { + throw new BamlAbortError('Operation was aborted', signal.reason); + } + + // Check if onTick is provided and reject for sync operations + if (options.onTick) { + throw new Error("onTick is not supported for synchronous functions. Please use the async client instead."); + } + + const collector = options.collector ? (Array.isArray(options.collector) ? options.collector : [options.collector]) : []; + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + const raw = this.runtime.callFunctionSync( + "TestCompoundTotalTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + options.tb?.__tb(), + options.clientRegistry, + collector, + options.tags || {}, + env, + signal, + options.watchers, + ) + return raw.parsed(false) as string + } catch (error: any) { + throw toBamlError(error); + } + } + TestFallbackClient( __baml_options__?: BamlCallOptions diff --git a/integ-tests/typescript-esm/baml_client/sync_request.ts b/integ-tests/typescript-esm/baml_client/sync_request.ts index 0f7ae8d326..d207dd5e5c 100644 --- a/integ-tests/typescript-esm/baml_client/sync_request.ts +++ b/integ-tests/typescript-esm/baml_client/sync_request.ts @@ -3787,6 +3787,81 @@ export class HttpRequest { } } + TestCompoundCombinedTimeouts( + input: string, + __baml_options__?: BamlCallOptions + ): HTTPRequest { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.buildRequestSync( + "TestCompoundCombinedTimeouts", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + false, + env, + ) + } catch (error) { + throw toBamlError(error); + } + } + + TestCompoundRequestTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): HTTPRequest { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.buildRequestSync( + "TestCompoundRequestTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + false, + env, + ) + } catch (error) { + throw toBamlError(error); + } + } + + TestCompoundTotalTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): HTTPRequest { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.buildRequestSync( + "TestCompoundTotalTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + false, + env, + ) + } catch (error) { + throw toBamlError(error); + } + } + TestFallbackClient( __baml_options__?: BamlCallOptions @@ -10393,6 +10468,81 @@ export class HttpStreamRequest { } } + TestCompoundCombinedTimeouts( + input: string, + __baml_options__?: BamlCallOptions + ): HTTPRequest { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.buildRequestSync( + "TestCompoundCombinedTimeouts", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + true, + env, + ) + } catch (error) { + throw toBamlError(error); + } + } + + TestCompoundRequestTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): HTTPRequest { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.buildRequestSync( + "TestCompoundRequestTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + true, + env, + ) + } catch (error) { + throw toBamlError(error); + } + } + + TestCompoundTotalTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): HTTPRequest { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.buildRequestSync( + "TestCompoundTotalTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + true, + env, + ) + } catch (error) { + throw toBamlError(error); + } + } + TestFallbackClient( __baml_options__?: BamlCallOptions diff --git a/integ-tests/typescript/baml_client/async_client.ts b/integ-tests/typescript/baml_client/async_client.ts index cf08ed9075..c39eb762ed 100644 --- a/integ-tests/typescript/baml_client/async_client.ts +++ b/integ-tests/typescript/baml_client/async_client.ts @@ -7296,6 +7296,150 @@ export type RecursivePartialNull = MovedRecursivePartialNull } } + async TestCompoundCombinedTimeouts( + input: string, + __baml_options__?: BamlCallOptions + ): Promise { + try { + const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } + const signal = options.signal; + + if (signal?.aborted) { + throw new BamlAbortError('Operation was aborted', signal.reason); + } + + // Check if onTick is provided - route through streaming if so + if (options.onTick) { + const stream = this.stream.TestCompoundCombinedTimeouts( + input, + __baml_options__ + ); + + return await stream.getFinalResponse(); + } + + const collector = options.collector ? (Array.isArray(options.collector) ? options.collector : + [options.collector]) : []; + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + const raw = await this.runtime.callFunction( + "TestCompoundCombinedTimeouts", + { + "input": input + }, + this.ctxManager.cloneContext(), + options.tb?.__tb(), + options.clientRegistry, + collector, + options.tags || {}, + env, + signal, + options.watchers, + ) + return raw.parsed(false) as string + } catch (error) { + throw toBamlError(error); + } + } + + async TestCompoundRequestTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): Promise { + try { + const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } + const signal = options.signal; + + if (signal?.aborted) { + throw new BamlAbortError('Operation was aborted', signal.reason); + } + + // Check if onTick is provided - route through streaming if so + if (options.onTick) { + const stream = this.stream.TestCompoundRequestTimeout( + input, + __baml_options__ + ); + + return await stream.getFinalResponse(); + } + + const collector = options.collector ? (Array.isArray(options.collector) ? options.collector : + [options.collector]) : []; + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + const raw = await this.runtime.callFunction( + "TestCompoundRequestTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + options.tb?.__tb(), + options.clientRegistry, + collector, + options.tags || {}, + env, + signal, + options.watchers, + ) + return raw.parsed(false) as string + } catch (error) { + throw toBamlError(error); + } + } + + async TestCompoundTotalTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): Promise { + try { + const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } + const signal = options.signal; + + if (signal?.aborted) { + throw new BamlAbortError('Operation was aborted', signal.reason); + } + + // Check if onTick is provided - route through streaming if so + if (options.onTick) { + const stream = this.stream.TestCompoundTotalTimeout( + input, + __baml_options__ + ); + + return await stream.getFinalResponse(); + } + + const collector = options.collector ? (Array.isArray(options.collector) ? options.collector : + [options.collector]) : []; + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + const raw = await this.runtime.callFunction( + "TestCompoundTotalTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + options.tb?.__tb(), + options.clientRegistry, + collector, + options.tags || {}, + env, + signal, + options.watchers, + ) + return raw.parsed(false) as string + } catch (error) { + throw toBamlError(error); + } + } + async TestFallbackClient( __baml_options__?: BamlCallOptions @@ -22682,6 +22826,204 @@ export type RecursivePartialNull = MovedRecursivePartialNull } } + TestCompoundCombinedTimeouts( + input: string, + __baml_options__?: BamlCallOptions + ): BamlStream + { + try { + const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } + const signal = options.signal; + + if (signal?.aborted) { + throw new BamlAbortError('Operation was aborted', signal.reason); + } + + let collector = options.collector ? (Array.isArray(options.collector) ? options.collector : + [options.collector]) : []; + + let onTickWrapper: (() => void) | undefined; + + // Create collector and wrap onTick if provided + if (options.onTick) { + const tickCollector = new Collector("on-tick-collector"); + collector = [...collector, tickCollector]; + + onTickWrapper = () => { + const log = tickCollector.last; + if (log) { + try { + options.onTick!("Unknown", log); + } catch (error) { + console.error("Error in onTick callback for TestCompoundCombinedTimeouts", error); + } + } + }; + } + + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + const raw = this.runtime.streamFunction( + "TestCompoundCombinedTimeouts", + { + "input": input + }, + undefined, + this.ctxManager.cloneContext(), + options.tb?.__tb(), + options.clientRegistry, + collector, + options.tags || {}, + env, + signal, + onTickWrapper, + ) + return new BamlStream( + raw, + (a): string => a, + (a): string => a, + this.ctxManager.cloneContext(), + options.signal, + ) + } catch (error) { + throw toBamlError(error); + } + } + + TestCompoundRequestTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): BamlStream + { + try { + const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } + const signal = options.signal; + + if (signal?.aborted) { + throw new BamlAbortError('Operation was aborted', signal.reason); + } + + let collector = options.collector ? (Array.isArray(options.collector) ? options.collector : + [options.collector]) : []; + + let onTickWrapper: (() => void) | undefined; + + // Create collector and wrap onTick if provided + if (options.onTick) { + const tickCollector = new Collector("on-tick-collector"); + collector = [...collector, tickCollector]; + + onTickWrapper = () => { + const log = tickCollector.last; + if (log) { + try { + options.onTick!("Unknown", log); + } catch (error) { + console.error("Error in onTick callback for TestCompoundRequestTimeout", error); + } + } + }; + } + + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + const raw = this.runtime.streamFunction( + "TestCompoundRequestTimeout", + { + "input": input + }, + undefined, + this.ctxManager.cloneContext(), + options.tb?.__tb(), + options.clientRegistry, + collector, + options.tags || {}, + env, + signal, + onTickWrapper, + ) + return new BamlStream( + raw, + (a): string => a, + (a): string => a, + this.ctxManager.cloneContext(), + options.signal, + ) + } catch (error) { + throw toBamlError(error); + } + } + + TestCompoundTotalTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): BamlStream + { + try { + const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } + const signal = options.signal; + + if (signal?.aborted) { + throw new BamlAbortError('Operation was aborted', signal.reason); + } + + let collector = options.collector ? (Array.isArray(options.collector) ? options.collector : + [options.collector]) : []; + + let onTickWrapper: (() => void) | undefined; + + // Create collector and wrap onTick if provided + if (options.onTick) { + const tickCollector = new Collector("on-tick-collector"); + collector = [...collector, tickCollector]; + + onTickWrapper = () => { + const log = tickCollector.last; + if (log) { + try { + options.onTick!("Unknown", log); + } catch (error) { + console.error("Error in onTick callback for TestCompoundTotalTimeout", error); + } + } + }; + } + + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + const raw = this.runtime.streamFunction( + "TestCompoundTotalTimeout", + { + "input": input + }, + undefined, + this.ctxManager.cloneContext(), + options.tb?.__tb(), + options.clientRegistry, + collector, + options.tags || {}, + env, + signal, + onTickWrapper, + ) + return new BamlStream( + raw, + (a): string => a, + (a): string => a, + this.ctxManager.cloneContext(), + options.signal, + ) + } catch (error) { + throw toBamlError(error); + } + } + TestFallbackClient( __baml_options__?: BamlCallOptions diff --git a/integ-tests/typescript/baml_client/async_request.ts b/integ-tests/typescript/baml_client/async_request.ts index f4e3620974..a72341b250 100644 --- a/integ-tests/typescript/baml_client/async_request.ts +++ b/integ-tests/typescript/baml_client/async_request.ts @@ -3791,6 +3791,81 @@ env?: Record } } + async TestCompoundCombinedTimeouts( + input: string, + __baml_options__?: BamlCallOptions + ): Promise { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return await this.runtime.buildRequest( + "TestCompoundCombinedTimeouts", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + false, + env + ) + } catch (error) { + throw toBamlError(error); + } + } + + async TestCompoundRequestTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): Promise { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return await this.runtime.buildRequest( + "TestCompoundRequestTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + false, + env + ) + } catch (error) { + throw toBamlError(error); + } + } + + async TestCompoundTotalTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): Promise { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return await this.runtime.buildRequest( + "TestCompoundTotalTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + false, + env + ) + } catch (error) { + throw toBamlError(error); + } + } + async TestFallbackClient( __baml_options__?: BamlCallOptions @@ -10397,6 +10472,81 @@ env?: Record } } + async TestCompoundCombinedTimeouts( + input: string, + __baml_options__?: BamlCallOptions + ): Promise { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return await this.runtime.buildRequest( + "TestCompoundCombinedTimeouts", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + true, + env + ) + } catch (error) { + throw toBamlError(error); + } + } + + async TestCompoundRequestTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): Promise { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return await this.runtime.buildRequest( + "TestCompoundRequestTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + true, + env + ) + } catch (error) { + throw toBamlError(error); + } + } + + async TestCompoundTotalTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): Promise { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return await this.runtime.buildRequest( + "TestCompoundTotalTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + true, + env + ) + } catch (error) { + throw toBamlError(error); + } + } + async TestFallbackClient( __baml_options__?: BamlCallOptions diff --git a/integ-tests/typescript/baml_client/inlinedbaml.ts b/integ-tests/typescript/baml_client/inlinedbaml.ts index 95f93b5397..ba04fc3b0d 100644 --- a/integ-tests/typescript/baml_client/inlinedbaml.ts +++ b/integ-tests/typescript/baml_client/inlinedbaml.ts @@ -37,7 +37,7 @@ const fileMap = { "test-files/aliases/classes.baml": "class TestClassAlias {\n key string @alias(\"key-dash\") @description(#\"\n This is a description for key\n af asdf\n \"#)\n key2 string @alias(\"key21\")\n key3 string @alias(\"key with space\")\n key4 string //unaliased\n key5 string @alias(\"key.with.punctuation/123\")\n}\n\nfunction FnTestClassAlias(input: string) -> TestClassAlias {\n client GPT35\n prompt #\"\n {{ctx.output_format}}\n \"#\n}\n\ntest FnTestClassAlias {\n functions [FnTestClassAlias]\n args {\n input \"example input\"\n }\n}\n", "test-files/aliases/enums.baml": "enum TestEnum {\n A @alias(\"k1\") @description(#\"\n User is angry\n \"#)\n B @alias(\"k22\") @description(#\"\n User is happy\n \"#)\n // tests whether k1 doesnt incorrectly get matched with k11\n C @alias(\"k11\") @description(#\"\n User is sad\n \"#)\n D @alias(\"k44\") @description(#\"\n User is confused\n \"#)\n E @description(#\"\n User is excited\n \"#)\n F @alias(\"k5\") // only alias\n \n G @alias(\"k6\") @description(#\"\n User is bored\n With a long description\n \"#)\n \n @@alias(\"Category\")\n}\n\nfunction FnTestAliasedEnumOutput(input: string) -> TestEnum {\n client GPT35\n prompt #\"\n Classify the user input into the following category\n \n {{ ctx.output_format }}\n\n {{ _.role('user') }}\n {{input}}\n\n {{ _.role('assistant') }}\n Category ID:\n \"#\n}\n\ntest FnTestAliasedEnumOutput {\n functions [FnTestAliasedEnumOutput]\n args {\n input \"mehhhhh\"\n }\n}", "test-files/builtin/fetch.baml": " // class Todo {\n // id int\n // todo string\n // completed bool\n // userId int\n // }\n\n // function GetTodo() -> Todo {\n // Todo {\n // id: 1,\n // }\n // }\n\n // function LlmDescribeTodo(todo: Todo) -> string {\n // client GPT4o\n // prompt #\"Describe the following todo in detail: {{ todo }}\"#\n // }\n\n // function UseGetTodoFunction() -> string {\n // let todo = GetTodo();\n // LlmDescribeTodo(todo)\n // }\n\n // test UseGetTodoFunction() {\n // functions [UseGetTodoFunction]\n // args { }\n // }\n\n // function SearchWikipedia(query: string) -> string {\n // baml.fetch_value(baml.Request {\n // base_url: UrlEncode(\"https://en.wikipedia.org/wiki/Special:Search\", {search query})\n // })\n // }\n\n // function UrlEncode(base_url: string, query_params: map) -> string {\n // client \"openai/o3\"\n // prompt #\"\n // Encode the following base URL and query parameters into a valid URL:\n\n // URL: {{ base_url }}\n\n // Query Params: {{ query_params }}\n\n // Answer with just the final URL, no other text\n // \"#\n // }\n\n // function ConvertUserQuery(query: string) -> string {\n // client GPT4o\n // prompt #\"\n // Convert the following user query into a search query for Wikipedia: {{ query }}\n\n // Ideally it should be a single word that could map to an actual Wikipedia page\n // \"#\n // }\n\n // function LlmFormulateResponse(query: string, search_result: string?) -> string {\n // client GPT4o\n // prompt #\"\n // You are a helpful assistant. Answer the following user query: {{ query }}.\n\n // {% if search_result != \"\" %}\n // Use this Wikipedia search result as a reference for your answer: {{ search_result }}\n // {% endif %}\n // \"#\n // }\n\n // function NeedsWikipediaSearch(query: string) -> bool {\n // client GPT4o\n // prompt #\"Does the following user query need a Wikipedia search?: {{ query }}\"#\n // }\n\n // function ChatResponse(query: string) -> string {\n // let needs_search = NeedsWikipediaSearch(query);\n // let search_result = SearchWikipedia(ConvertUserQuery(query));\n // LlmFormulateResponse(query, search_result)\n // }\n\n // test TestName {\n // functions [ChatResponse]\n // args {\n // query #\"\n // Tell me everything about Rome\n // \"#\n // messages []\n // }\n // }\n\n // test UrlEncode {\n // functions [UrlEncode]\n // args {\n // base_url \"https://en.wikipedia.org/wiki/Special:Search\"\n // query_params {search \"Tell me everything about Rome\"\n // go \"Go\"}\n // }\n // }\n\n // test Convert {\n // functions [ConvertUserQuery]\n // args {\n // query #\"\n // Tell me everything about Rome\n // \"#\n // }\n // }\n", - "test-files/client-timeout-config.baml": "// Client with extremely short timeouts for testing\nclient TestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n connect_timeout_ms 1 // 1ms - will timeout on connect\n request_timeout_ms 10 // 10ms - very likely to timeout\n }\n }\n}\n\n// Function that will timeout\nfunction TestTimeoutError(input: string) -> string {\n client TestTimeoutClient\n prompt #\"\n This is a test that should timeout.\n Please write a very long response about: {{input}}\n \"#\n}\n\n// Client with only request timeout\nclient TestRequestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 50 // 50ms timeout\n }\n }\n}\n\nfunction TestRequestTimeout(input: string) -> string {\n client TestRequestTimeoutClient\n prompt #\"\n Generate a detailed 500 word essay about: {{input}}\n \"#\n}\n\n// Client with zero timeout (infinite)\nclient TestZeroTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 0 // 0 means infinite\n }\n }\n}\n\nfunction TestZeroTimeout(input: string) -> string {\n client TestZeroTimeoutClient\n prompt #\"\n Echo: {{input}}\n \"#\n}\n\n// Fallback client that includes timeout client\nclient TestTimeoutFallbackClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n }\n}\n\nfunction TestTimeoutFallback(input: string) -> string {\n client TestTimeoutFallbackClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Client for streaming timeout tests (Phase 4)\nclient TestStreamingTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n time_to_first_token_timeout_ms 1 // 1ms - extremely short, should always timeout\n idle_timeout_ms 1 // 1ms - extremely short, should always timeout\n }\n }\n}\n\nfunction TestStreamingTimeout(input: string) -> string {\n client TestStreamingTimeoutClient\n prompt #\"\n Stream a long response about: {{input}}\n \"#\n}\n", + "test-files/client-timeout-config.baml": "// Client with extremely short timeouts for testing\nclient TestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n connect_timeout_ms 1 // 1ms - will timeout on connect\n request_timeout_ms 10 // 10ms - very likely to timeout\n }\n }\n}\n\n// Function that will timeout\nfunction TestTimeoutError(input: string) -> string {\n client TestTimeoutClient\n prompt #\"\n This is a test that should timeout.\n Please write a very long response about: {{input}}\n \"#\n}\n\n// Client with only request timeout\nclient TestRequestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 50 // 50ms timeout\n }\n }\n}\n\nfunction TestRequestTimeout(input: string) -> string {\n client TestRequestTimeoutClient\n prompt #\"\n Generate a detailed 500 word essay about: {{input}}\n \"#\n}\n\n// Client with zero timeout (infinite)\nclient TestZeroTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 0 // 0 means infinite\n }\n }\n}\n\nfunction TestZeroTimeout(input: string) -> string {\n client TestZeroTimeoutClient\n prompt #\"\n Echo: {{input}}\n \"#\n}\n\n// Fallback client that includes timeout client\nclient TestTimeoutFallbackClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n }\n}\n\nfunction TestTimeoutFallback(input: string) -> string {\n client TestTimeoutFallbackClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Client for streaming timeout tests (Phase 4)\nclient TestStreamingTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n time_to_first_token_timeout_ms 1 // 1ms - extremely short, should always timeout\n idle_timeout_ms 1 // 1ms - extremely short, should always timeout\n }\n }\n}\n\nfunction TestStreamingTimeout(input: string) -> string {\n client TestStreamingTimeoutClient\n prompt #\"\n Stream a long response about: {{input}}\n \"#\n}\n\n// Compound client with request_timeout_ms override\n// This overrides the tight 10ms timeout of TestTimeoutClient with a more generous 5000ms\nclient CompoundTimeoutClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n request_timeout_ms 5000 // Override the tight timeout from primitives\n }\n }\n}\n\nfunction TestCompoundRequestTimeout(input: string) -> string {\n client CompoundTimeoutClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Compound client with total_timeout_ms\n// Even though the second client has infinite timeout, total_timeout_ms enforces an overall limit\nclient CompoundTotalTimeoutClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n total_timeout_ms 1000 // 1 second total timeout for the entire strategy\n }\n }\n}\n\nfunction TestCompoundTotalTimeout(input: string) -> string {\n client CompoundTotalTimeoutClient\n prompt #\"\n Generate a very long detailed essay about: {{input}}\n \"#\n}\n\n// Compound client with both request_timeout_ms and total_timeout_ms\nclient CompoundCombinedTimeoutsClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n request_timeout_ms 5000 // Generous per-request timeout\n total_timeout_ms 10000 // 10 second total timeout\n }\n }\n}\n\nfunction TestCompoundCombinedTimeouts(input: string) -> string {\n client CompoundCombinedTimeoutsClient\n prompt #\"\n Echo: {{input}}\n \"#\n}", "test-files/comments/comments.baml": "// add some functions, classes, enums etc with comments all over.", "test-files/constraints/constraints.baml": "// These classes and functions test several properties of\n// constrains:\n//\n// - The ability for constrains on fields to pass or fail.\n// - The ability for constraints on bare args and return types to pass or fail.\n// - The ability of constraints to influence which variant of a union is chosen\n// by the parser, when the structure is not sufficient to decide.\n\n/// A Martian organism with an age.\n/// Such a nice type.\nclass Martian {\n /// The age of the Martian in Mars years.\n /// So many Mars years.\n age int @check(young_enough, {{ this < 30 }})\n}\n\nclass Earthling {\n age int @check(earth_aged, {{this < 200 and this > 0}}) @check(no_infants, {{this >1}})\n}\n\n\nclass FooAny {\n planetary_age Martian | Earthling\n certainty int @check(unreasonably_certain, {{this == 102931}})\n species string @check(trivial, {{this == \"Homo sapiens\"}}) @check(regex_good, {{this|regex_match(\"Homo\")}}) @check(regex_bad, {{this|regex_match(\"neanderthalensis\")}})\n}\n\n\nfunction PredictAge(name: string) -> FooAny {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling\n and capitalization). I'll give you a hint: If the name\n is \"Greg\", his age is 41.\n\n {{ctx.output_format}}\n \"#\n}\n\n\nfunction PredictAgeBare(inp: string @assert(big_enough, {{this|length > 1}})) -> int @check(too_big, {{this == 10102}}) {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ inp.name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling).\n\n {{ctx.output_format}}\n \"#\n}\n\nfunction ReturnFailingAssert(inp: int @assert(small_int, {{this < 10}})) -> int @assert(big_int, {{this > 100}}) {\n client GPT35\n prompt #\"\n Return the next integer after {{ inp }}.\n\n {{ctx.output_format}}\n \"#\n}\n\nclass TwoStoriesOneTitle {\n title string\n story_a string @assert(too_long_story, {{this|length > 1000000}} )\n story_b string @assert(too_long_story, {{this|length > 1000000}} )\n}\n\nfunction StreamFailingAssertion(theme: string, length: int) -> TwoStoriesOneTitle {\n client GPT35\n prompt #\"\n Tell me two different stories along the theme of {{ theme }} with the same title.\n Please make each about {{ length }} words long.\n {{ctx.output_format}}\n \"#\n}\n\nclass TwoStoriesOneTitleCheck {\n title string\n story_a string @check(too_long_story, {{this|length > 1000000}} )\n story_b string @check(too_long_story, {{this|length > 1000000}} )\n}\n\nfunction StreamFailingCheck(theme: string, length: int) -> TwoStoriesOneTitleCheck {\n client GPT35\n prompt #\"\n Tell me two different stories along the theme of {{ theme }} with the same title.\n Please make each about {{ length }} words long.\n {{ctx.output_format}}\n \"#\n}\n\nclass BlockConstraint {\n foo int\n bar string\n @@check(cross_field, {{ this.bar|length > this.foo }})\n}\n\nfunction MakeBlockConstraint() -> BlockConstraint {\n client GPT35\n prompt #\"\n Generate an output in the following schema with a short string and a large int.\n\n {{ ctx.output_format }}\n \"#\n}\n\nclass NestedBlockConstraint {\n nbc BlockConstraint\n}\n\nclass BlockConstraintForParam {\n bcfp int\n bcfp2 string\n @@assert(hi, {{ this.bcfp2|length < this.bcfp }})\n}\n\nclass NestedBlockConstraintForParam {\n nbcfp BlockConstraintForParam\n}\n\nfunction MakeNestedBlockConstraint() -> NestedBlockConstraint {\n client GPT35\n prompt #\"Generate an output where the inner foo is 1 and the inner bar is \"hello\".\n {{ ctx.output_format }}\n \"#\n}\n\nfunction UseBlockConstraint(inp: BlockConstraintForParam) -> int {\n client GPT35\n prompt #\"\n Generate 3\n {{ ctx.output_format }}\n \"#\n}\n\nfunction UseNestedBlockConstraint(inp: NestedBlockConstraintForParam) -> int {\n client GPT35\n prompt #\"\n Generate 3\n {{ ctx.output_format }}\n \"#\n}\n", "test-files/constraints/contact-info.baml": "class PhoneNumber {\n value string @assert(valid_phone_number, {{this|regex_match(\"\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}\")}})\n}\n\nclass EmailAddress {\n value string @assert(valid_email, {{this|regex_match(\"^[_]*([a-z0-9]+(\\.|_*)?)+@([a-z][a-z0-9-]+(\\.|-*\\.))+[a-z]{2,6}$\")}})\n}\n\nclass ContactInfo {\n primary PhoneNumber | EmailAddress\n secondary (PhoneNumber | EmailAddress)?\n}\n\nfunction ExtractContactInfo(document: string) -> ContactInfo {\n client GPT35\n prompt #\"\n Extract a primary contact info, and if possible a secondary contact\n info, from this document:\n\n {{ document }}\n\n {{ ctx.output_format }}\n \"#\n}\n", diff --git a/integ-tests/typescript/baml_client/parser.ts b/integ-tests/typescript/baml_client/parser.ts index 5498b48009..9be638e168 100644 --- a/integ-tests/typescript/baml_client/parser.ts +++ b/integ-tests/typescript/baml_client/parser.ts @@ -3480,6 +3480,75 @@ export class LlmResponseParser { } } + TestCompoundCombinedTimeouts( + llmResponse: string, + __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } + ): string { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.parseLlmResponse( + "TestCompoundCombinedTimeouts", + llmResponse, + false, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + env, + ) as string + } catch (error) { + throw toBamlError(error); + } + } + + TestCompoundRequestTimeout( + llmResponse: string, + __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } + ): string { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.parseLlmResponse( + "TestCompoundRequestTimeout", + llmResponse, + false, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + env, + ) as string + } catch (error) { + throw toBamlError(error); + } + } + + TestCompoundTotalTimeout( + llmResponse: string, + __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } + ): string { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.parseLlmResponse( + "TestCompoundTotalTimeout", + llmResponse, + false, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + env, + ) as string + } catch (error) { + throw toBamlError(error); + } + } + TestFallbackClient( llmResponse: string, __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } @@ -9558,6 +9627,75 @@ export class LlmStreamParser { } } + TestCompoundCombinedTimeouts( + llmResponse: string, + __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } + ): string { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.parseLlmResponse( + "TestCompoundCombinedTimeouts", + llmResponse, + true, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + env, + ) as string + } catch (error) { + throw toBamlError(error); + } + } + + TestCompoundRequestTimeout( + llmResponse: string, + __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } + ): string { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.parseLlmResponse( + "TestCompoundRequestTimeout", + llmResponse, + true, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + env, + ) as string + } catch (error) { + throw toBamlError(error); + } + } + + TestCompoundTotalTimeout( + llmResponse: string, + __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } + ): string { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.parseLlmResponse( + "TestCompoundTotalTimeout", + llmResponse, + true, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + env, + ) as string + } catch (error) { + throw toBamlError(error); + } + } + TestFallbackClient( llmResponse: string, __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } diff --git a/integ-tests/typescript/baml_client/sync_client.ts b/integ-tests/typescript/baml_client/sync_client.ts index 57839f3e25..8b06d35739 100644 --- a/integ-tests/typescript/baml_client/sync_client.ts +++ b/integ-tests/typescript/baml_client/sync_client.ts @@ -6396,6 +6396,132 @@ export class BamlSyncClient { } } + TestCompoundCombinedTimeouts( + input: string, + __baml_options__?: BamlCallOptions + ): string { + try { + const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } + const signal = options.signal; + + if (signal?.aborted) { + throw new BamlAbortError('Operation was aborted', signal.reason); + } + + // Check if onTick is provided and reject for sync operations + if (options.onTick) { + throw new Error("onTick is not supported for synchronous functions. Please use the async client instead."); + } + + const collector = options.collector ? (Array.isArray(options.collector) ? options.collector : [options.collector]) : []; + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + const raw = this.runtime.callFunctionSync( + "TestCompoundCombinedTimeouts", + { + "input": input + }, + this.ctxManager.cloneContext(), + options.tb?.__tb(), + options.clientRegistry, + collector, + options.tags || {}, + env, + signal, + options.watchers, + ) + return raw.parsed(false) as string + } catch (error: any) { + throw toBamlError(error); + } + } + + TestCompoundRequestTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): string { + try { + const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } + const signal = options.signal; + + if (signal?.aborted) { + throw new BamlAbortError('Operation was aborted', signal.reason); + } + + // Check if onTick is provided and reject for sync operations + if (options.onTick) { + throw new Error("onTick is not supported for synchronous functions. Please use the async client instead."); + } + + const collector = options.collector ? (Array.isArray(options.collector) ? options.collector : [options.collector]) : []; + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + const raw = this.runtime.callFunctionSync( + "TestCompoundRequestTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + options.tb?.__tb(), + options.clientRegistry, + collector, + options.tags || {}, + env, + signal, + options.watchers, + ) + return raw.parsed(false) as string + } catch (error: any) { + throw toBamlError(error); + } + } + + TestCompoundTotalTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): string { + try { + const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } + const signal = options.signal; + + if (signal?.aborted) { + throw new BamlAbortError('Operation was aborted', signal.reason); + } + + // Check if onTick is provided and reject for sync operations + if (options.onTick) { + throw new Error("onTick is not supported for synchronous functions. Please use the async client instead."); + } + + const collector = options.collector ? (Array.isArray(options.collector) ? options.collector : [options.collector]) : []; + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + const raw = this.runtime.callFunctionSync( + "TestCompoundTotalTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + options.tb?.__tb(), + options.clientRegistry, + collector, + options.tags || {}, + env, + signal, + options.watchers, + ) + return raw.parsed(false) as string + } catch (error: any) { + throw toBamlError(error); + } + } + TestFallbackClient( __baml_options__?: BamlCallOptions diff --git a/integ-tests/typescript/baml_client/sync_request.ts b/integ-tests/typescript/baml_client/sync_request.ts index bdc0762fe9..a04d1c204f 100644 --- a/integ-tests/typescript/baml_client/sync_request.ts +++ b/integ-tests/typescript/baml_client/sync_request.ts @@ -3787,6 +3787,81 @@ export class HttpRequest { } } + TestCompoundCombinedTimeouts( + input: string, + __baml_options__?: BamlCallOptions + ): HTTPRequest { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.buildRequestSync( + "TestCompoundCombinedTimeouts", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + false, + env, + ) + } catch (error) { + throw toBamlError(error); + } + } + + TestCompoundRequestTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): HTTPRequest { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.buildRequestSync( + "TestCompoundRequestTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + false, + env, + ) + } catch (error) { + throw toBamlError(error); + } + } + + TestCompoundTotalTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): HTTPRequest { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.buildRequestSync( + "TestCompoundTotalTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + false, + env, + ) + } catch (error) { + throw toBamlError(error); + } + } + TestFallbackClient( __baml_options__?: BamlCallOptions @@ -10393,6 +10468,81 @@ export class HttpStreamRequest { } } + TestCompoundCombinedTimeouts( + input: string, + __baml_options__?: BamlCallOptions + ): HTTPRequest { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.buildRequestSync( + "TestCompoundCombinedTimeouts", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + true, + env, + ) + } catch (error) { + throw toBamlError(error); + } + } + + TestCompoundRequestTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): HTTPRequest { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.buildRequestSync( + "TestCompoundRequestTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + true, + env, + ) + } catch (error) { + throw toBamlError(error); + } + } + + TestCompoundTotalTimeout( + input: string, + __baml_options__?: BamlCallOptions + ): HTTPRequest { + try { + const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; + const env: Record = Object.fromEntries( + Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] + ); + return this.runtime.buildRequestSync( + "TestCompoundTotalTimeout", + { + "input": input + }, + this.ctxManager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + true, + env, + ) + } catch (error) { + throw toBamlError(error); + } + } + TestFallbackClient( __baml_options__?: BamlCallOptions From 739bf227eff7b4ce3701e5248c44810c42d5d93b Mon Sep 17 00:00:00 2001 From: Greg Hale Date: Sat, 1 Nov 2025 16:17:30 -0700 Subject: [PATCH 2/2] no overrides --- .../test-files/client-timeout-config.baml | 38 --- integ-tests/go/baml_client/baml_source_map.go | 2 +- integ-tests/go/baml_client/functions.go | 132 ---------- integ-tests/go/baml_client/functions_parse.go | 94 -------- .../go/baml_client/functions_parse_stream.go | 94 -------- .../go/baml_client/functions_stream.go | 148 ------------ integ-tests/openapi/baml_client/openapi.yaml | 58 ----- .../python-v1/baml_client/async_client.py | 82 ------- .../python-v1/baml_client/inlinedbaml.py | 2 +- integ-tests/python-v1/baml_client/parser.py | 24 -- .../python-v1/baml_client/sync_client.py | 80 ------ .../python/baml_client/async_client.py | 82 ------- integ-tests/python/baml_client/inlinedbaml.py | 2 +- integ-tests/python/baml_client/parser.py | 24 -- integ-tests/python/baml_client/sync_client.py | 80 ------ integ-tests/python/tests/test_timeouts.py | 24 -- integ-tests/react/baml_client/async_client.ts | 228 ------------------ .../react/baml_client/async_request.ts | 100 -------- integ-tests/react/baml_client/inlinedbaml.ts | 2 +- integ-tests/react/baml_client/parser.ts | 92 ------- integ-tests/react/baml_client/react/hooks.tsx | 100 -------- integ-tests/react/baml_client/react/server.ts | 36 --- .../baml_client/react/server_streaming.ts | 38 --- .../react/server_streaming_types.ts | 2 - integ-tests/react/baml_client/sync_client.ts | 84 ------- integ-tests/react/baml_client/sync_request.ts | 100 -------- integ-tests/ruby/baml_client/client.rb | 100 -------- .../baml_client/async_client.ts | 228 ------------------ .../baml_client/async_request.ts | 100 -------- .../typescript-esm/baml_client/inlinedbaml.ts | 2 +- .../typescript-esm/baml_client/parser.ts | 92 ------- .../typescript-esm/baml_client/sync_client.ts | 84 ------- .../baml_client/sync_request.ts | 100 -------- .../typescript/baml_client/async_client.ts | 228 ------------------ .../typescript/baml_client/async_request.ts | 100 -------- .../typescript/baml_client/inlinedbaml.ts | 2 +- integ-tests/typescript/baml_client/parser.ts | 92 ------- .../typescript/baml_client/sync_client.ts | 84 ------- .../typescript/baml_client/sync_request.ts | 100 -------- 39 files changed, 6 insertions(+), 3054 deletions(-) diff --git a/integ-tests/baml_src/test-files/client-timeout-config.baml b/integ-tests/baml_src/test-files/client-timeout-config.baml index 664434d868..776d87c9fe 100644 --- a/integ-tests/baml_src/test-files/client-timeout-config.baml +++ b/integ-tests/baml_src/test-files/client-timeout-config.baml @@ -93,25 +93,6 @@ function TestStreamingTimeout(input: string) -> string { "# } -// Compound client with request_timeout_ms override -// This overrides the tight 10ms timeout of TestTimeoutClient with a more generous 5000ms -client CompoundTimeoutClient { - provider fallback - options { - strategy [TestTimeoutClient, TestZeroTimeoutClient] - http { - request_timeout_ms 5000 // Override the tight timeout from primitives - } - } -} - -function TestCompoundRequestTimeout(input: string) -> string { - client CompoundTimeoutClient - prompt #" - Simple echo: {{input}} - "# -} - // Compound client with total_timeout_ms // Even though the second client has infinite timeout, total_timeout_ms enforces an overall limit client CompoundTotalTimeoutClient { @@ -129,23 +110,4 @@ function TestCompoundTotalTimeout(input: string) -> string { prompt #" Generate a very long detailed essay about: {{input}} "# -} - -// Compound client with both request_timeout_ms and total_timeout_ms -client CompoundCombinedTimeoutsClient { - provider fallback - options { - strategy [TestTimeoutClient, TestZeroTimeoutClient] - http { - request_timeout_ms 5000 // Generous per-request timeout - total_timeout_ms 10000 // 10 second total timeout - } - } -} - -function TestCompoundCombinedTimeouts(input: string) -> string { - client CompoundCombinedTimeoutsClient - prompt #" - Echo: {{input}} - "# } \ No newline at end of file diff --git a/integ-tests/go/baml_client/baml_source_map.go b/integ-tests/go/baml_client/baml_source_map.go index 89e4dfd77f..0544c7b26f 100644 --- a/integ-tests/go/baml_client/baml_source_map.go +++ b/integ-tests/go/baml_client/baml_source_map.go @@ -32,7 +32,7 @@ var file_map = map[string]string{ "test-files/aliases/classes.baml": "class TestClassAlias {\n key string @alias(\"key-dash\") @description(#\"\n This is a description for key\n af asdf\n \"#)\n key2 string @alias(\"key21\")\n key3 string @alias(\"key with space\")\n key4 string //unaliased\n key5 string @alias(\"key.with.punctuation/123\")\n}\n\nfunction FnTestClassAlias(input: string) -> TestClassAlias {\n client GPT35\n prompt #\"\n {{ctx.output_format}}\n \"#\n}\n\ntest FnTestClassAlias {\n functions [FnTestClassAlias]\n args {\n input \"example input\"\n }\n}\n", "test-files/aliases/enums.baml": "enum TestEnum {\n A @alias(\"k1\") @description(#\"\n User is angry\n \"#)\n B @alias(\"k22\") @description(#\"\n User is happy\n \"#)\n // tests whether k1 doesnt incorrectly get matched with k11\n C @alias(\"k11\") @description(#\"\n User is sad\n \"#)\n D @alias(\"k44\") @description(#\"\n User is confused\n \"#)\n E @description(#\"\n User is excited\n \"#)\n F @alias(\"k5\") // only alias\n \n G @alias(\"k6\") @description(#\"\n User is bored\n With a long description\n \"#)\n \n @@alias(\"Category\")\n}\n\nfunction FnTestAliasedEnumOutput(input: string) -> TestEnum {\n client GPT35\n prompt #\"\n Classify the user input into the following category\n \n {{ ctx.output_format }}\n\n {{ _.role('user') }}\n {{input}}\n\n {{ _.role('assistant') }}\n Category ID:\n \"#\n}\n\ntest FnTestAliasedEnumOutput {\n functions [FnTestAliasedEnumOutput]\n args {\n input \"mehhhhh\"\n }\n}", "test-files/builtin/fetch.baml": " // class Todo {\n // id int\n // todo string\n // completed bool\n // userId int\n // }\n\n // function GetTodo() -> Todo {\n // Todo {\n // id: 1,\n // }\n // }\n\n // function LlmDescribeTodo(todo: Todo) -> string {\n // client GPT4o\n // prompt #\"Describe the following todo in detail: {{ todo }}\"#\n // }\n\n // function UseGetTodoFunction() -> string {\n // let todo = GetTodo();\n // LlmDescribeTodo(todo)\n // }\n\n // test UseGetTodoFunction() {\n // functions [UseGetTodoFunction]\n // args { }\n // }\n\n // function SearchWikipedia(query: string) -> string {\n // baml.fetch_value(baml.Request {\n // base_url: UrlEncode(\"https://en.wikipedia.org/wiki/Special:Search\", {search query})\n // })\n // }\n\n // function UrlEncode(base_url: string, query_params: map) -> string {\n // client \"openai/o3\"\n // prompt #\"\n // Encode the following base URL and query parameters into a valid URL:\n\n // URL: {{ base_url }}\n\n // Query Params: {{ query_params }}\n\n // Answer with just the final URL, no other text\n // \"#\n // }\n\n // function ConvertUserQuery(query: string) -> string {\n // client GPT4o\n // prompt #\"\n // Convert the following user query into a search query for Wikipedia: {{ query }}\n\n // Ideally it should be a single word that could map to an actual Wikipedia page\n // \"#\n // }\n\n // function LlmFormulateResponse(query: string, search_result: string?) -> string {\n // client GPT4o\n // prompt #\"\n // You are a helpful assistant. Answer the following user query: {{ query }}.\n\n // {% if search_result != \"\" %}\n // Use this Wikipedia search result as a reference for your answer: {{ search_result }}\n // {% endif %}\n // \"#\n // }\n\n // function NeedsWikipediaSearch(query: string) -> bool {\n // client GPT4o\n // prompt #\"Does the following user query need a Wikipedia search?: {{ query }}\"#\n // }\n\n // function ChatResponse(query: string) -> string {\n // let needs_search = NeedsWikipediaSearch(query);\n // let search_result = SearchWikipedia(ConvertUserQuery(query));\n // LlmFormulateResponse(query, search_result)\n // }\n\n // test TestName {\n // functions [ChatResponse]\n // args {\n // query #\"\n // Tell me everything about Rome\n // \"#\n // messages []\n // }\n // }\n\n // test UrlEncode {\n // functions [UrlEncode]\n // args {\n // base_url \"https://en.wikipedia.org/wiki/Special:Search\"\n // query_params {search \"Tell me everything about Rome\"\n // go \"Go\"}\n // }\n // }\n\n // test Convert {\n // functions [ConvertUserQuery]\n // args {\n // query #\"\n // Tell me everything about Rome\n // \"#\n // }\n // }\n", - "test-files/client-timeout-config.baml": "// Client with extremely short timeouts for testing\nclient TestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n connect_timeout_ms 1 // 1ms - will timeout on connect\n request_timeout_ms 10 // 10ms - very likely to timeout\n }\n }\n}\n\n// Function that will timeout\nfunction TestTimeoutError(input: string) -> string {\n client TestTimeoutClient\n prompt #\"\n This is a test that should timeout.\n Please write a very long response about: {{input}}\n \"#\n}\n\n// Client with only request timeout\nclient TestRequestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 50 // 50ms timeout\n }\n }\n}\n\nfunction TestRequestTimeout(input: string) -> string {\n client TestRequestTimeoutClient\n prompt #\"\n Generate a detailed 500 word essay about: {{input}}\n \"#\n}\n\n// Client with zero timeout (infinite)\nclient TestZeroTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 0 // 0 means infinite\n }\n }\n}\n\nfunction TestZeroTimeout(input: string) -> string {\n client TestZeroTimeoutClient\n prompt #\"\n Echo: {{input}}\n \"#\n}\n\n// Fallback client that includes timeout client\nclient TestTimeoutFallbackClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n }\n}\n\nfunction TestTimeoutFallback(input: string) -> string {\n client TestTimeoutFallbackClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Client for streaming timeout tests (Phase 4)\nclient TestStreamingTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n time_to_first_token_timeout_ms 1 // 1ms - extremely short, should always timeout\n idle_timeout_ms 1 // 1ms - extremely short, should always timeout\n }\n }\n}\n\nfunction TestStreamingTimeout(input: string) -> string {\n client TestStreamingTimeoutClient\n prompt #\"\n Stream a long response about: {{input}}\n \"#\n}\n\n// Compound client with request_timeout_ms override\n// This overrides the tight 10ms timeout of TestTimeoutClient with a more generous 5000ms\nclient CompoundTimeoutClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n request_timeout_ms 5000 // Override the tight timeout from primitives\n }\n }\n}\n\nfunction TestCompoundRequestTimeout(input: string) -> string {\n client CompoundTimeoutClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Compound client with total_timeout_ms\n// Even though the second client has infinite timeout, total_timeout_ms enforces an overall limit\nclient CompoundTotalTimeoutClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n total_timeout_ms 1000 // 1 second total timeout for the entire strategy\n }\n }\n}\n\nfunction TestCompoundTotalTimeout(input: string) -> string {\n client CompoundTotalTimeoutClient\n prompt #\"\n Generate a very long detailed essay about: {{input}}\n \"#\n}\n\n// Compound client with both request_timeout_ms and total_timeout_ms\nclient CompoundCombinedTimeoutsClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n request_timeout_ms 5000 // Generous per-request timeout\n total_timeout_ms 10000 // 10 second total timeout\n }\n }\n}\n\nfunction TestCompoundCombinedTimeouts(input: string) -> string {\n client CompoundCombinedTimeoutsClient\n prompt #\"\n Echo: {{input}}\n \"#\n}", + "test-files/client-timeout-config.baml": "// Client with extremely short timeouts for testing\nclient TestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n connect_timeout_ms 1 // 1ms - will timeout on connect\n request_timeout_ms 10 // 10ms - very likely to timeout\n }\n }\n}\n\n// Function that will timeout\nfunction TestTimeoutError(input: string) -> string {\n client TestTimeoutClient\n prompt #\"\n This is a test that should timeout.\n Please write a very long response about: {{input}}\n \"#\n}\n\n// Client with only request timeout\nclient TestRequestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 50 // 50ms timeout\n }\n }\n}\n\nfunction TestRequestTimeout(input: string) -> string {\n client TestRequestTimeoutClient\n prompt #\"\n Generate a detailed 500 word essay about: {{input}}\n \"#\n}\n\n// Client with zero timeout (infinite)\nclient TestZeroTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 0 // 0 means infinite\n }\n }\n}\n\nfunction TestZeroTimeout(input: string) -> string {\n client TestZeroTimeoutClient\n prompt #\"\n Echo: {{input}}\n \"#\n}\n\n// Fallback client that includes timeout client\nclient TestTimeoutFallbackClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n }\n}\n\nfunction TestTimeoutFallback(input: string) -> string {\n client TestTimeoutFallbackClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Client for streaming timeout tests (Phase 4)\nclient TestStreamingTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n time_to_first_token_timeout_ms 1 // 1ms - extremely short, should always timeout\n idle_timeout_ms 1 // 1ms - extremely short, should always timeout\n }\n }\n}\n\nfunction TestStreamingTimeout(input: string) -> string {\n client TestStreamingTimeoutClient\n prompt #\"\n Stream a long response about: {{input}}\n \"#\n}\n\n// Compound client with total_timeout_ms\n// Even though the second client has infinite timeout, total_timeout_ms enforces an overall limit\nclient CompoundTotalTimeoutClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n total_timeout_ms 1000 // 1 second total timeout for the entire strategy\n }\n }\n}\n\nfunction TestCompoundTotalTimeout(input: string) -> string {\n client CompoundTotalTimeoutClient\n prompt #\"\n Generate a very long detailed essay about: {{input}}\n \"#\n}", "test-files/comments/comments.baml": "// add some functions, classes, enums etc with comments all over.", "test-files/constraints/constraints.baml": "// These classes and functions test several properties of\n// constrains:\n//\n// - The ability for constrains on fields to pass or fail.\n// - The ability for constraints on bare args and return types to pass or fail.\n// - The ability of constraints to influence which variant of a union is chosen\n// by the parser, when the structure is not sufficient to decide.\n\n/// A Martian organism with an age.\n/// Such a nice type.\nclass Martian {\n /// The age of the Martian in Mars years.\n /// So many Mars years.\n age int @check(young_enough, {{ this < 30 }})\n}\n\nclass Earthling {\n age int @check(earth_aged, {{this < 200 and this > 0}}) @check(no_infants, {{this >1}})\n}\n\n\nclass FooAny {\n planetary_age Martian | Earthling\n certainty int @check(unreasonably_certain, {{this == 102931}})\n species string @check(trivial, {{this == \"Homo sapiens\"}}) @check(regex_good, {{this|regex_match(\"Homo\")}}) @check(regex_bad, {{this|regex_match(\"neanderthalensis\")}})\n}\n\n\nfunction PredictAge(name: string) -> FooAny {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling\n and capitalization). I'll give you a hint: If the name\n is \"Greg\", his age is 41.\n\n {{ctx.output_format}}\n \"#\n}\n\n\nfunction PredictAgeBare(inp: string @assert(big_enough, {{this|length > 1}})) -> int @check(too_big, {{this == 10102}}) {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ inp.name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling).\n\n {{ctx.output_format}}\n \"#\n}\n\nfunction ReturnFailingAssert(inp: int @assert(small_int, {{this < 10}})) -> int @assert(big_int, {{this > 100}}) {\n client GPT35\n prompt #\"\n Return the next integer after {{ inp }}.\n\n {{ctx.output_format}}\n \"#\n}\n\nclass TwoStoriesOneTitle {\n title string\n story_a string @assert(too_long_story, {{this|length > 1000000}} )\n story_b string @assert(too_long_story, {{this|length > 1000000}} )\n}\n\nfunction StreamFailingAssertion(theme: string, length: int) -> TwoStoriesOneTitle {\n client GPT35\n prompt #\"\n Tell me two different stories along the theme of {{ theme }} with the same title.\n Please make each about {{ length }} words long.\n {{ctx.output_format}}\n \"#\n}\n\nclass TwoStoriesOneTitleCheck {\n title string\n story_a string @check(too_long_story, {{this|length > 1000000}} )\n story_b string @check(too_long_story, {{this|length > 1000000}} )\n}\n\nfunction StreamFailingCheck(theme: string, length: int) -> TwoStoriesOneTitleCheck {\n client GPT35\n prompt #\"\n Tell me two different stories along the theme of {{ theme }} with the same title.\n Please make each about {{ length }} words long.\n {{ctx.output_format}}\n \"#\n}\n\nclass BlockConstraint {\n foo int\n bar string\n @@check(cross_field, {{ this.bar|length > this.foo }})\n}\n\nfunction MakeBlockConstraint() -> BlockConstraint {\n client GPT35\n prompt #\"\n Generate an output in the following schema with a short string and a large int.\n\n {{ ctx.output_format }}\n \"#\n}\n\nclass NestedBlockConstraint {\n nbc BlockConstraint\n}\n\nclass BlockConstraintForParam {\n bcfp int\n bcfp2 string\n @@assert(hi, {{ this.bcfp2|length < this.bcfp }})\n}\n\nclass NestedBlockConstraintForParam {\n nbcfp BlockConstraintForParam\n}\n\nfunction MakeNestedBlockConstraint() -> NestedBlockConstraint {\n client GPT35\n prompt #\"Generate an output where the inner foo is 1 and the inner bar is \"hello\".\n {{ ctx.output_format }}\n \"#\n}\n\nfunction UseBlockConstraint(inp: BlockConstraintForParam) -> int {\n client GPT35\n prompt #\"\n Generate 3\n {{ ctx.output_format }}\n \"#\n}\n\nfunction UseNestedBlockConstraint(inp: NestedBlockConstraintForParam) -> int {\n client GPT35\n prompt #\"\n Generate 3\n {{ ctx.output_format }}\n \"#\n}\n", "test-files/constraints/contact-info.baml": "class PhoneNumber {\n value string @assert(valid_phone_number, {{this|regex_match(\"\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}\")}})\n}\n\nclass EmailAddress {\n value string @assert(valid_email, {{this|regex_match(\"^[_]*([a-z0-9]+(\\.|_*)?)+@([a-z][a-z0-9-]+(\\.|-*\\.))+[a-z]{2,6}$\")}})\n}\n\nclass ContactInfo {\n primary PhoneNumber | EmailAddress\n secondary (PhoneNumber | EmailAddress)?\n}\n\nfunction ExtractContactInfo(document: string) -> ContactInfo {\n client GPT35\n prompt #\"\n Extract a primary contact info, and if possible a secondary contact\n info, from this document:\n\n {{ document }}\n\n {{ ctx.output_format }}\n \"#\n}\n", diff --git a/integ-tests/go/baml_client/functions.go b/integ-tests/go/baml_client/functions.go index 0a9401cd58..3e52009c64 100644 --- a/integ-tests/go/baml_client/functions.go +++ b/integ-tests/go/baml_client/functions.go @@ -9929,138 +9929,6 @@ func TestCaching(ctx context.Context, input string, not_cached string, opts ...C } } -func TestCompoundCombinedTimeouts(ctx context.Context, input string, opts ...CallOptionFunc) (string, error) { - - var callOpts callOption - for _, opt := range opts { - opt(&callOpts) - } - - args := baml.BamlFunctionArguments{ - Kwargs: map[string]any{"input": input}, - Env: getEnvVars(callOpts.env), - } - - if callOpts.clientRegistry != nil { - args.ClientRegistry = callOpts.clientRegistry - } - - if callOpts.collectors != nil { - args.Collectors = callOpts.collectors - } - - if callOpts.typeBuilder != nil { - args.TypeBuilder = callOpts.typeBuilder - } - - if callOpts.tags != nil { - args.Tags = callOpts.tags - } - - encoded, err := args.Encode() - if err != nil { - panic(err) - } - - if callOpts.onTick == nil { - result, err := bamlRuntime.CallFunction(ctx, "TestCompoundCombinedTimeouts", encoded, callOpts.onTick) - if err != nil { - return "", err - } - - if result.Error != nil { - return "", result.Error - } - - casted := (result.Data).(string) - - return casted, nil - } else { - channel, err := bamlRuntime.CallFunctionStream(ctx, "TestCompoundCombinedTimeouts", encoded, callOpts.onTick) - if err != nil { - return "", err - } - - for result := range channel { - if result.Error != nil { - return "", result.Error - } - - if result.HasData { - return result.Data.(string), nil - } - } - - return "", fmt.Errorf("No data returned from stream") - } -} - -func TestCompoundRequestTimeout(ctx context.Context, input string, opts ...CallOptionFunc) (string, error) { - - var callOpts callOption - for _, opt := range opts { - opt(&callOpts) - } - - args := baml.BamlFunctionArguments{ - Kwargs: map[string]any{"input": input}, - Env: getEnvVars(callOpts.env), - } - - if callOpts.clientRegistry != nil { - args.ClientRegistry = callOpts.clientRegistry - } - - if callOpts.collectors != nil { - args.Collectors = callOpts.collectors - } - - if callOpts.typeBuilder != nil { - args.TypeBuilder = callOpts.typeBuilder - } - - if callOpts.tags != nil { - args.Tags = callOpts.tags - } - - encoded, err := args.Encode() - if err != nil { - panic(err) - } - - if callOpts.onTick == nil { - result, err := bamlRuntime.CallFunction(ctx, "TestCompoundRequestTimeout", encoded, callOpts.onTick) - if err != nil { - return "", err - } - - if result.Error != nil { - return "", result.Error - } - - casted := (result.Data).(string) - - return casted, nil - } else { - channel, err := bamlRuntime.CallFunctionStream(ctx, "TestCompoundRequestTimeout", encoded, callOpts.onTick) - if err != nil { - return "", err - } - - for result := range channel { - if result.Error != nil { - return "", result.Error - } - - if result.HasData { - return result.Data.(string), nil - } - } - - return "", fmt.Errorf("No data returned from stream") - } -} - func TestCompoundTotalTimeout(ctx context.Context, input string, opts ...CallOptionFunc) (string, error) { var callOpts callOption diff --git a/integ-tests/go/baml_client/functions_parse.go b/integ-tests/go/baml_client/functions_parse.go index 7b2108e7e5..87bd308619 100644 --- a/integ-tests/go/baml_client/functions_parse.go +++ b/integ-tests/go/baml_client/functions_parse.go @@ -7083,100 +7083,6 @@ func (*parse) TestCaching(text string, opts ...CallOptionFunc) (string, error) { return casted, nil } -// / Parse version of TestCompoundCombinedTimeouts (Takes in string and returns string) -func (*parse) TestCompoundCombinedTimeouts(text string, opts ...CallOptionFunc) (string, error) { - - var callOpts callOption - for _, opt := range opts { - opt(&callOpts) - } - - args := baml.BamlFunctionArguments{ - Kwargs: map[string]any{"text": text, "stream": false}, - Env: getEnvVars(callOpts.env), - } - - if callOpts.clientRegistry != nil { - args.ClientRegistry = callOpts.clientRegistry - } - - if callOpts.collectors != nil { - args.Collectors = callOpts.collectors - } - - if callOpts.typeBuilder != nil { - args.TypeBuilder = callOpts.typeBuilder - } - - if callOpts.tags != nil { - args.Tags = callOpts.tags - } - - encoded, err := args.Encode() - if err != nil { - // This should never happen. if it does, please file an issue at https://github.com/boundaryml/baml/issues - // and include the type of the args you're passing in. - wrapped_err := fmt.Errorf("BAML INTERNAL ERROR: TestCompoundCombinedTimeouts: %w", err) - panic(wrapped_err) - } - - result, err := bamlRuntime.CallFunctionParse(context.Background(), "TestCompoundCombinedTimeouts", encoded) - if err != nil { - return "", err - } - - casted := (result).(string) - - return casted, nil -} - -// / Parse version of TestCompoundRequestTimeout (Takes in string and returns string) -func (*parse) TestCompoundRequestTimeout(text string, opts ...CallOptionFunc) (string, error) { - - var callOpts callOption - for _, opt := range opts { - opt(&callOpts) - } - - args := baml.BamlFunctionArguments{ - Kwargs: map[string]any{"text": text, "stream": false}, - Env: getEnvVars(callOpts.env), - } - - if callOpts.clientRegistry != nil { - args.ClientRegistry = callOpts.clientRegistry - } - - if callOpts.collectors != nil { - args.Collectors = callOpts.collectors - } - - if callOpts.typeBuilder != nil { - args.TypeBuilder = callOpts.typeBuilder - } - - if callOpts.tags != nil { - args.Tags = callOpts.tags - } - - encoded, err := args.Encode() - if err != nil { - // This should never happen. if it does, please file an issue at https://github.com/boundaryml/baml/issues - // and include the type of the args you're passing in. - wrapped_err := fmt.Errorf("BAML INTERNAL ERROR: TestCompoundRequestTimeout: %w", err) - panic(wrapped_err) - } - - result, err := bamlRuntime.CallFunctionParse(context.Background(), "TestCompoundRequestTimeout", encoded) - if err != nil { - return "", err - } - - casted := (result).(string) - - return casted, nil -} - // / Parse version of TestCompoundTotalTimeout (Takes in string and returns string) func (*parse) TestCompoundTotalTimeout(text string, opts ...CallOptionFunc) (string, error) { diff --git a/integ-tests/go/baml_client/functions_parse_stream.go b/integ-tests/go/baml_client/functions_parse_stream.go index c48e374d50..a36c721701 100644 --- a/integ-tests/go/baml_client/functions_parse_stream.go +++ b/integ-tests/go/baml_client/functions_parse_stream.go @@ -7084,100 +7084,6 @@ func (*parse_stream) TestCaching(text string, opts ...CallOptionFunc) (string, e return casted, nil } -// / Parse version of TestCompoundCombinedTimeouts (Takes in string and returns string) -func (*parse_stream) TestCompoundCombinedTimeouts(text string, opts ...CallOptionFunc) (string, error) { - - var callOpts callOption - for _, opt := range opts { - opt(&callOpts) - } - - args := baml.BamlFunctionArguments{ - Kwargs: map[string]any{"text": text, "stream": true}, - Env: getEnvVars(callOpts.env), - } - - if callOpts.clientRegistry != nil { - args.ClientRegistry = callOpts.clientRegistry - } - - if callOpts.collectors != nil { - args.Collectors = callOpts.collectors - } - - if callOpts.typeBuilder != nil { - args.TypeBuilder = callOpts.typeBuilder - } - - if callOpts.tags != nil { - args.Tags = callOpts.tags - } - - encoded, err := args.Encode() - if err != nil { - // This should never happen. if it does, please file an issue at https://github.com/boundaryml/baml/issues - // and include the type of the args you're passing in. - wrapped_err := fmt.Errorf("BAML INTERNAL ERROR: TestCompoundCombinedTimeouts: %w", err) - panic(wrapped_err) - } - - result, err := bamlRuntime.CallFunctionParse(context.Background(), "TestCompoundCombinedTimeouts", encoded) - if err != nil { - return "", err - } - - casted := (result).(string) - - return casted, nil -} - -// / Parse version of TestCompoundRequestTimeout (Takes in string and returns string) -func (*parse_stream) TestCompoundRequestTimeout(text string, opts ...CallOptionFunc) (string, error) { - - var callOpts callOption - for _, opt := range opts { - opt(&callOpts) - } - - args := baml.BamlFunctionArguments{ - Kwargs: map[string]any{"text": text, "stream": true}, - Env: getEnvVars(callOpts.env), - } - - if callOpts.clientRegistry != nil { - args.ClientRegistry = callOpts.clientRegistry - } - - if callOpts.collectors != nil { - args.Collectors = callOpts.collectors - } - - if callOpts.typeBuilder != nil { - args.TypeBuilder = callOpts.typeBuilder - } - - if callOpts.tags != nil { - args.Tags = callOpts.tags - } - - encoded, err := args.Encode() - if err != nil { - // This should never happen. if it does, please file an issue at https://github.com/boundaryml/baml/issues - // and include the type of the args you're passing in. - wrapped_err := fmt.Errorf("BAML INTERNAL ERROR: TestCompoundRequestTimeout: %w", err) - panic(wrapped_err) - } - - result, err := bamlRuntime.CallFunctionParse(context.Background(), "TestCompoundRequestTimeout", encoded) - if err != nil { - return "", err - } - - casted := (result).(string) - - return casted, nil -} - // / Parse version of TestCompoundTotalTimeout (Takes in string and returns string) func (*parse_stream) TestCompoundTotalTimeout(text string, opts ...CallOptionFunc) (string, error) { diff --git a/integ-tests/go/baml_client/functions_stream.go b/integ-tests/go/baml_client/functions_stream.go index 1d57ffe7b7..38bde311af 100644 --- a/integ-tests/go/baml_client/functions_stream.go +++ b/integ-tests/go/baml_client/functions_stream.go @@ -11158,154 +11158,6 @@ func (*stream) TestCaching(ctx context.Context, input string, not_cached string, return channel, nil } -// / Streaming version of TestCompoundCombinedTimeouts -func (*stream) TestCompoundCombinedTimeouts(ctx context.Context, input string, opts ...CallOptionFunc) (<-chan StreamValue[string, string], error) { - - var callOpts callOption - for _, opt := range opts { - opt(&callOpts) - } - - args := baml.BamlFunctionArguments{ - Kwargs: map[string]any{"input": input}, - Env: getEnvVars(callOpts.env), - } - - if callOpts.clientRegistry != nil { - args.ClientRegistry = callOpts.clientRegistry - } - - if callOpts.collectors != nil { - args.Collectors = callOpts.collectors - } - - if callOpts.typeBuilder != nil { - args.TypeBuilder = callOpts.typeBuilder - } - - if callOpts.tags != nil { - args.Tags = callOpts.tags - } - - encoded, err := args.Encode() - if err != nil { - // This should never happen. if it does, please file an issue at https://github.com/boundaryml/baml/issues - // and include the type of the args you're passing in. - wrapped_err := fmt.Errorf("BAML INTERNAL ERROR: TestCompoundCombinedTimeouts: %w", err) - panic(wrapped_err) - } - - internal_channel, err := bamlRuntime.CallFunctionStream(ctx, "TestCompoundCombinedTimeouts", encoded, callOpts.onTick) - if err != nil { - return nil, err - } - - channel := make(chan StreamValue[string, string]) - go func() { - for result := range internal_channel { - if result.Error != nil { - channel <- StreamValue[string, string]{ - IsError: true, - Error: result.Error, - } - close(channel) - return - } - if result.HasData { - data := (result.Data).(string) - channel <- StreamValue[string, string]{ - IsFinal: true, - as_final: &data, - } - } else { - data := (result.StreamData).(string) - channel <- StreamValue[string, string]{ - IsFinal: false, - as_stream: &data, - } - } - } - - // when internal_channel is closed, close the output too - close(channel) - }() - return channel, nil -} - -// / Streaming version of TestCompoundRequestTimeout -func (*stream) TestCompoundRequestTimeout(ctx context.Context, input string, opts ...CallOptionFunc) (<-chan StreamValue[string, string], error) { - - var callOpts callOption - for _, opt := range opts { - opt(&callOpts) - } - - args := baml.BamlFunctionArguments{ - Kwargs: map[string]any{"input": input}, - Env: getEnvVars(callOpts.env), - } - - if callOpts.clientRegistry != nil { - args.ClientRegistry = callOpts.clientRegistry - } - - if callOpts.collectors != nil { - args.Collectors = callOpts.collectors - } - - if callOpts.typeBuilder != nil { - args.TypeBuilder = callOpts.typeBuilder - } - - if callOpts.tags != nil { - args.Tags = callOpts.tags - } - - encoded, err := args.Encode() - if err != nil { - // This should never happen. if it does, please file an issue at https://github.com/boundaryml/baml/issues - // and include the type of the args you're passing in. - wrapped_err := fmt.Errorf("BAML INTERNAL ERROR: TestCompoundRequestTimeout: %w", err) - panic(wrapped_err) - } - - internal_channel, err := bamlRuntime.CallFunctionStream(ctx, "TestCompoundRequestTimeout", encoded, callOpts.onTick) - if err != nil { - return nil, err - } - - channel := make(chan StreamValue[string, string]) - go func() { - for result := range internal_channel { - if result.Error != nil { - channel <- StreamValue[string, string]{ - IsError: true, - Error: result.Error, - } - close(channel) - return - } - if result.HasData { - data := (result.Data).(string) - channel <- StreamValue[string, string]{ - IsFinal: true, - as_final: &data, - } - } else { - data := (result.StreamData).(string) - channel <- StreamValue[string, string]{ - IsFinal: false, - as_stream: &data, - } - } - } - - // when internal_channel is closed, close the output too - close(channel) - }() - return channel, nil -} - // / Streaming version of TestCompoundTotalTimeout func (*stream) TestCompoundTotalTimeout(ctx context.Context, input string, opts ...CallOptionFunc) (<-chan StreamValue[string, string], error) { diff --git a/integ-tests/openapi/baml_client/openapi.yaml b/integ-tests/openapi/baml_client/openapi.yaml index 0940ab1d2a..d8e299a30c 100644 --- a/integ-tests/openapi/baml_client/openapi.yaml +++ b/integ-tests/openapi/baml_client/openapi.yaml @@ -2144,32 +2144,6 @@ paths: title: TestCachingResponse type: string operationId: TestCaching - /call/TestCompoundCombinedTimeouts: - post: - requestBody: - $ref: '#/components/requestBodies/TestCompoundCombinedTimeouts' - responses: - '200': - description: Successful operation - content: - application/json: - schema: - title: TestCompoundCombinedTimeoutsResponse - type: string - operationId: TestCompoundCombinedTimeouts - /call/TestCompoundRequestTimeout: - post: - requestBody: - $ref: '#/components/requestBodies/TestCompoundRequestTimeout' - responses: - '200': - description: Successful operation - content: - application/json: - schema: - title: TestCompoundRequestTimeoutResponse - type: string - operationId: TestCompoundRequestTimeout /call/TestCompoundTotalTimeout: post: requestBody: @@ -5785,38 +5759,6 @@ components: - input - not_cached additionalProperties: false - TestCompoundCombinedTimeouts: - required: true - content: - application/json: - schema: - title: TestCompoundCombinedTimeoutsRequest - type: object - properties: - input: - type: string - __baml_options__: - nullable: true - $ref: '#/components/schemas/BamlOptions' - required: - - input - additionalProperties: false - TestCompoundRequestTimeout: - required: true - content: - application/json: - schema: - title: TestCompoundRequestTimeoutRequest - type: object - properties: - input: - type: string - __baml_options__: - nullable: true - $ref: '#/components/schemas/BamlOptions' - required: - - input - additionalProperties: false TestCompoundTotalTimeout: required: true content: diff --git a/integ-tests/python-v1/baml_client/async_client.py b/integ-tests/python-v1/baml_client/async_client.py index e02a9c84f3..6612ecf00c 100644 --- a/integ-tests/python-v1/baml_client/async_client.py +++ b/integ-tests/python-v1/baml_client/async_client.py @@ -2329,36 +2329,6 @@ async def TestCaching(self, input: str,not_cached: str, "input": input,"not_cached": not_cached, }) return typing.cast(str, result.cast_to(types, types, stream_types, False, __runtime__)) - async def TestCompoundCombinedTimeouts(self, input: str, - baml_options: BamlCallOptions = {}, - ) -> str: - # Check if on_tick is provided - if 'on_tick' in baml_options: - # Use streaming internally when on_tick is provided - stream = self.stream.TestCompoundCombinedTimeouts(input=input, - baml_options=baml_options) - return await stream.get_final_response() - else: - # Original non-streaming code - result = await self.__options.merge_options(baml_options).call_function_async(function_name="TestCompoundCombinedTimeouts", args={ - "input": input, - }) - return typing.cast(str, result.cast_to(types, types, stream_types, False, __runtime__)) - async def TestCompoundRequestTimeout(self, input: str, - baml_options: BamlCallOptions = {}, - ) -> str: - # Check if on_tick is provided - if 'on_tick' in baml_options: - # Use streaming internally when on_tick is provided - stream = self.stream.TestCompoundRequestTimeout(input=input, - baml_options=baml_options) - return await stream.get_final_response() - else: - # Original non-streaming code - result = await self.__options.merge_options(baml_options).call_function_async(function_name="TestCompoundRequestTimeout", args={ - "input": input, - }) - return typing.cast(str, result.cast_to(types, types, stream_types, False, __runtime__)) async def TestCompoundTotalTimeout(self, input: str, baml_options: BamlCallOptions = {}, ) -> str: @@ -5893,30 +5863,6 @@ def TestCaching(self, input: str,not_cached: str, lambda x: typing.cast(str, x.cast_to(types, types, stream_types, False, __runtime__)), ctx, ) - def TestCompoundCombinedTimeouts(self, input: str, - baml_options: BamlCallOptions = {}, - ) -> baml_py.BamlStream[str, str]: - ctx, result = self.__options.merge_options(baml_options).create_async_stream(function_name="TestCompoundCombinedTimeouts", args={ - "input": input, - }) - return baml_py.BamlStream[str, str]( - result, - lambda x: typing.cast(str, x.cast_to(types, types, stream_types, True, __runtime__)), - lambda x: typing.cast(str, x.cast_to(types, types, stream_types, False, __runtime__)), - ctx, - ) - def TestCompoundRequestTimeout(self, input: str, - baml_options: BamlCallOptions = {}, - ) -> baml_py.BamlStream[str, str]: - ctx, result = self.__options.merge_options(baml_options).create_async_stream(function_name="TestCompoundRequestTimeout", args={ - "input": input, - }) - return baml_py.BamlStream[str, str]( - result, - lambda x: typing.cast(str, x.cast_to(types, types, stream_types, True, __runtime__)), - lambda x: typing.cast(str, x.cast_to(types, types, stream_types, False, __runtime__)), - ctx, - ) def TestCompoundTotalTimeout(self, input: str, baml_options: BamlCallOptions = {}, ) -> baml_py.BamlStream[str, str]: @@ -8355,20 +8301,6 @@ async def TestCaching(self, input: str,not_cached: str, "input": input,"not_cached": not_cached, }, mode="request") return result - async def TestCompoundCombinedTimeouts(self, input: str, - baml_options: BamlCallOptions = {}, - ) -> baml_py.baml_py.HTTPRequest: - result = await self.__options.merge_options(baml_options).create_http_request_async(function_name="TestCompoundCombinedTimeouts", args={ - "input": input, - }, mode="request") - return result - async def TestCompoundRequestTimeout(self, input: str, - baml_options: BamlCallOptions = {}, - ) -> baml_py.baml_py.HTTPRequest: - result = await self.__options.merge_options(baml_options).create_http_request_async(function_name="TestCompoundRequestTimeout", args={ - "input": input, - }, mode="request") - return result async def TestCompoundTotalTimeout(self, input: str, baml_options: BamlCallOptions = {}, ) -> baml_py.baml_py.HTTPRequest: @@ -10232,20 +10164,6 @@ async def TestCaching(self, input: str,not_cached: str, "input": input,"not_cached": not_cached, }, mode="stream") return result - async def TestCompoundCombinedTimeouts(self, input: str, - baml_options: BamlCallOptions = {}, - ) -> baml_py.baml_py.HTTPRequest: - result = await self.__options.merge_options(baml_options).create_http_request_async(function_name="TestCompoundCombinedTimeouts", args={ - "input": input, - }, mode="stream") - return result - async def TestCompoundRequestTimeout(self, input: str, - baml_options: BamlCallOptions = {}, - ) -> baml_py.baml_py.HTTPRequest: - result = await self.__options.merge_options(baml_options).create_http_request_async(function_name="TestCompoundRequestTimeout", args={ - "input": input, - }, mode="stream") - return result async def TestCompoundTotalTimeout(self, input: str, baml_options: BamlCallOptions = {}, ) -> baml_py.baml_py.HTTPRequest: diff --git a/integ-tests/python-v1/baml_client/inlinedbaml.py b/integ-tests/python-v1/baml_client/inlinedbaml.py index eb9325466e..69a4f9ae8e 100644 --- a/integ-tests/python-v1/baml_client/inlinedbaml.py +++ b/integ-tests/python-v1/baml_client/inlinedbaml.py @@ -29,7 +29,7 @@ "test-files/aliases/classes.baml": "class TestClassAlias {\n key string @alias(\"key-dash\") @description(#\"\n This is a description for key\n af asdf\n \"#)\n key2 string @alias(\"key21\")\n key3 string @alias(\"key with space\")\n key4 string //unaliased\n key5 string @alias(\"key.with.punctuation/123\")\n}\n\nfunction FnTestClassAlias(input: string) -> TestClassAlias {\n client GPT35\n prompt #\"\n {{ctx.output_format}}\n \"#\n}\n\ntest FnTestClassAlias {\n functions [FnTestClassAlias]\n args {\n input \"example input\"\n }\n}\n", "test-files/aliases/enums.baml": "enum TestEnum {\n A @alias(\"k1\") @description(#\"\n User is angry\n \"#)\n B @alias(\"k22\") @description(#\"\n User is happy\n \"#)\n // tests whether k1 doesnt incorrectly get matched with k11\n C @alias(\"k11\") @description(#\"\n User is sad\n \"#)\n D @alias(\"k44\") @description(#\"\n User is confused\n \"#)\n E @description(#\"\n User is excited\n \"#)\n F @alias(\"k5\") // only alias\n \n G @alias(\"k6\") @description(#\"\n User is bored\n With a long description\n \"#)\n \n @@alias(\"Category\")\n}\n\nfunction FnTestAliasedEnumOutput(input: string) -> TestEnum {\n client GPT35\n prompt #\"\n Classify the user input into the following category\n \n {{ ctx.output_format }}\n\n {{ _.role('user') }}\n {{input}}\n\n {{ _.role('assistant') }}\n Category ID:\n \"#\n}\n\ntest FnTestAliasedEnumOutput {\n functions [FnTestAliasedEnumOutput]\n args {\n input \"mehhhhh\"\n }\n}", "test-files/builtin/fetch.baml": " // class Todo {\n // id int\n // todo string\n // completed bool\n // userId int\n // }\n\n // function GetTodo() -> Todo {\n // Todo {\n // id: 1,\n // }\n // }\n\n // function LlmDescribeTodo(todo: Todo) -> string {\n // client GPT4o\n // prompt #\"Describe the following todo in detail: {{ todo }}\"#\n // }\n\n // function UseGetTodoFunction() -> string {\n // let todo = GetTodo();\n // LlmDescribeTodo(todo)\n // }\n\n // test UseGetTodoFunction() {\n // functions [UseGetTodoFunction]\n // args { }\n // }\n\n // function SearchWikipedia(query: string) -> string {\n // baml.fetch_value(baml.Request {\n // base_url: UrlEncode(\"https://en.wikipedia.org/wiki/Special:Search\", {search query})\n // })\n // }\n\n // function UrlEncode(base_url: string, query_params: map) -> string {\n // client \"openai/o3\"\n // prompt #\"\n // Encode the following base URL and query parameters into a valid URL:\n\n // URL: {{ base_url }}\n\n // Query Params: {{ query_params }}\n\n // Answer with just the final URL, no other text\n // \"#\n // }\n\n // function ConvertUserQuery(query: string) -> string {\n // client GPT4o\n // prompt #\"\n // Convert the following user query into a search query for Wikipedia: {{ query }}\n\n // Ideally it should be a single word that could map to an actual Wikipedia page\n // \"#\n // }\n\n // function LlmFormulateResponse(query: string, search_result: string?) -> string {\n // client GPT4o\n // prompt #\"\n // You are a helpful assistant. Answer the following user query: {{ query }}.\n\n // {% if search_result != \"\" %}\n // Use this Wikipedia search result as a reference for your answer: {{ search_result }}\n // {% endif %}\n // \"#\n // }\n\n // function NeedsWikipediaSearch(query: string) -> bool {\n // client GPT4o\n // prompt #\"Does the following user query need a Wikipedia search?: {{ query }}\"#\n // }\n\n // function ChatResponse(query: string) -> string {\n // let needs_search = NeedsWikipediaSearch(query);\n // let search_result = SearchWikipedia(ConvertUserQuery(query));\n // LlmFormulateResponse(query, search_result)\n // }\n\n // test TestName {\n // functions [ChatResponse]\n // args {\n // query #\"\n // Tell me everything about Rome\n // \"#\n // messages []\n // }\n // }\n\n // test UrlEncode {\n // functions [UrlEncode]\n // args {\n // base_url \"https://en.wikipedia.org/wiki/Special:Search\"\n // query_params {search \"Tell me everything about Rome\"\n // go \"Go\"}\n // }\n // }\n\n // test Convert {\n // functions [ConvertUserQuery]\n // args {\n // query #\"\n // Tell me everything about Rome\n // \"#\n // }\n // }\n", - "test-files/client-timeout-config.baml": "// Client with extremely short timeouts for testing\nclient TestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n connect_timeout_ms 1 // 1ms - will timeout on connect\n request_timeout_ms 10 // 10ms - very likely to timeout\n }\n }\n}\n\n// Function that will timeout\nfunction TestTimeoutError(input: string) -> string {\n client TestTimeoutClient\n prompt #\"\n This is a test that should timeout.\n Please write a very long response about: {{input}}\n \"#\n}\n\n// Client with only request timeout\nclient TestRequestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 50 // 50ms timeout\n }\n }\n}\n\nfunction TestRequestTimeout(input: string) -> string {\n client TestRequestTimeoutClient\n prompt #\"\n Generate a detailed 500 word essay about: {{input}}\n \"#\n}\n\n// Client with zero timeout (infinite)\nclient TestZeroTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 0 // 0 means infinite\n }\n }\n}\n\nfunction TestZeroTimeout(input: string) -> string {\n client TestZeroTimeoutClient\n prompt #\"\n Echo: {{input}}\n \"#\n}\n\n// Fallback client that includes timeout client\nclient TestTimeoutFallbackClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n }\n}\n\nfunction TestTimeoutFallback(input: string) -> string {\n client TestTimeoutFallbackClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Client for streaming timeout tests (Phase 4)\nclient TestStreamingTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n time_to_first_token_timeout_ms 1 // 1ms - extremely short, should always timeout\n idle_timeout_ms 1 // 1ms - extremely short, should always timeout\n }\n }\n}\n\nfunction TestStreamingTimeout(input: string) -> string {\n client TestStreamingTimeoutClient\n prompt #\"\n Stream a long response about: {{input}}\n \"#\n}\n\n// Compound client with request_timeout_ms override\n// This overrides the tight 10ms timeout of TestTimeoutClient with a more generous 5000ms\nclient CompoundTimeoutClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n request_timeout_ms 5000 // Override the tight timeout from primitives\n }\n }\n}\n\nfunction TestCompoundRequestTimeout(input: string) -> string {\n client CompoundTimeoutClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Compound client with total_timeout_ms\n// Even though the second client has infinite timeout, total_timeout_ms enforces an overall limit\nclient CompoundTotalTimeoutClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n total_timeout_ms 1000 // 1 second total timeout for the entire strategy\n }\n }\n}\n\nfunction TestCompoundTotalTimeout(input: string) -> string {\n client CompoundTotalTimeoutClient\n prompt #\"\n Generate a very long detailed essay about: {{input}}\n \"#\n}\n\n// Compound client with both request_timeout_ms and total_timeout_ms\nclient CompoundCombinedTimeoutsClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n request_timeout_ms 5000 // Generous per-request timeout\n total_timeout_ms 10000 // 10 second total timeout\n }\n }\n}\n\nfunction TestCompoundCombinedTimeouts(input: string) -> string {\n client CompoundCombinedTimeoutsClient\n prompt #\"\n Echo: {{input}}\n \"#\n}", + "test-files/client-timeout-config.baml": "// Client with extremely short timeouts for testing\nclient TestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n connect_timeout_ms 1 // 1ms - will timeout on connect\n request_timeout_ms 10 // 10ms - very likely to timeout\n }\n }\n}\n\n// Function that will timeout\nfunction TestTimeoutError(input: string) -> string {\n client TestTimeoutClient\n prompt #\"\n This is a test that should timeout.\n Please write a very long response about: {{input}}\n \"#\n}\n\n// Client with only request timeout\nclient TestRequestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 50 // 50ms timeout\n }\n }\n}\n\nfunction TestRequestTimeout(input: string) -> string {\n client TestRequestTimeoutClient\n prompt #\"\n Generate a detailed 500 word essay about: {{input}}\n \"#\n}\n\n// Client with zero timeout (infinite)\nclient TestZeroTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 0 // 0 means infinite\n }\n }\n}\n\nfunction TestZeroTimeout(input: string) -> string {\n client TestZeroTimeoutClient\n prompt #\"\n Echo: {{input}}\n \"#\n}\n\n// Fallback client that includes timeout client\nclient TestTimeoutFallbackClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n }\n}\n\nfunction TestTimeoutFallback(input: string) -> string {\n client TestTimeoutFallbackClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Client for streaming timeout tests (Phase 4)\nclient TestStreamingTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n time_to_first_token_timeout_ms 1 // 1ms - extremely short, should always timeout\n idle_timeout_ms 1 // 1ms - extremely short, should always timeout\n }\n }\n}\n\nfunction TestStreamingTimeout(input: string) -> string {\n client TestStreamingTimeoutClient\n prompt #\"\n Stream a long response about: {{input}}\n \"#\n}\n\n// Compound client with total_timeout_ms\n// Even though the second client has infinite timeout, total_timeout_ms enforces an overall limit\nclient CompoundTotalTimeoutClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n total_timeout_ms 1000 // 1 second total timeout for the entire strategy\n }\n }\n}\n\nfunction TestCompoundTotalTimeout(input: string) -> string {\n client CompoundTotalTimeoutClient\n prompt #\"\n Generate a very long detailed essay about: {{input}}\n \"#\n}", "test-files/comments/comments.baml": "// add some functions, classes, enums etc with comments all over.", "test-files/constraints/constraints.baml": "// These classes and functions test several properties of\n// constrains:\n//\n// - The ability for constrains on fields to pass or fail.\n// - The ability for constraints on bare args and return types to pass or fail.\n// - The ability of constraints to influence which variant of a union is chosen\n// by the parser, when the structure is not sufficient to decide.\n\n/// A Martian organism with an age.\n/// Such a nice type.\nclass Martian {\n /// The age of the Martian in Mars years.\n /// So many Mars years.\n age int @check(young_enough, {{ this < 30 }})\n}\n\nclass Earthling {\n age int @check(earth_aged, {{this < 200 and this > 0}}) @check(no_infants, {{this >1}})\n}\n\n\nclass FooAny {\n planetary_age Martian | Earthling\n certainty int @check(unreasonably_certain, {{this == 102931}})\n species string @check(trivial, {{this == \"Homo sapiens\"}}) @check(regex_good, {{this|regex_match(\"Homo\")}}) @check(regex_bad, {{this|regex_match(\"neanderthalensis\")}})\n}\n\n\nfunction PredictAge(name: string) -> FooAny {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling\n and capitalization). I'll give you a hint: If the name\n is \"Greg\", his age is 41.\n\n {{ctx.output_format}}\n \"#\n}\n\n\nfunction PredictAgeBare(inp: string @assert(big_enough, {{this|length > 1}})) -> int @check(too_big, {{this == 10102}}) {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ inp.name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling).\n\n {{ctx.output_format}}\n \"#\n}\n\nfunction ReturnFailingAssert(inp: int @assert(small_int, {{this < 10}})) -> int @assert(big_int, {{this > 100}}) {\n client GPT35\n prompt #\"\n Return the next integer after {{ inp }}.\n\n {{ctx.output_format}}\n \"#\n}\n\nclass TwoStoriesOneTitle {\n title string\n story_a string @assert(too_long_story, {{this|length > 1000000}} )\n story_b string @assert(too_long_story, {{this|length > 1000000}} )\n}\n\nfunction StreamFailingAssertion(theme: string, length: int) -> TwoStoriesOneTitle {\n client GPT35\n prompt #\"\n Tell me two different stories along the theme of {{ theme }} with the same title.\n Please make each about {{ length }} words long.\n {{ctx.output_format}}\n \"#\n}\n\nclass TwoStoriesOneTitleCheck {\n title string\n story_a string @check(too_long_story, {{this|length > 1000000}} )\n story_b string @check(too_long_story, {{this|length > 1000000}} )\n}\n\nfunction StreamFailingCheck(theme: string, length: int) -> TwoStoriesOneTitleCheck {\n client GPT35\n prompt #\"\n Tell me two different stories along the theme of {{ theme }} with the same title.\n Please make each about {{ length }} words long.\n {{ctx.output_format}}\n \"#\n}\n\nclass BlockConstraint {\n foo int\n bar string\n @@check(cross_field, {{ this.bar|length > this.foo }})\n}\n\nfunction MakeBlockConstraint() -> BlockConstraint {\n client GPT35\n prompt #\"\n Generate an output in the following schema with a short string and a large int.\n\n {{ ctx.output_format }}\n \"#\n}\n\nclass NestedBlockConstraint {\n nbc BlockConstraint\n}\n\nclass BlockConstraintForParam {\n bcfp int\n bcfp2 string\n @@assert(hi, {{ this.bcfp2|length < this.bcfp }})\n}\n\nclass NestedBlockConstraintForParam {\n nbcfp BlockConstraintForParam\n}\n\nfunction MakeNestedBlockConstraint() -> NestedBlockConstraint {\n client GPT35\n prompt #\"Generate an output where the inner foo is 1 and the inner bar is \"hello\".\n {{ ctx.output_format }}\n \"#\n}\n\nfunction UseBlockConstraint(inp: BlockConstraintForParam) -> int {\n client GPT35\n prompt #\"\n Generate 3\n {{ ctx.output_format }}\n \"#\n}\n\nfunction UseNestedBlockConstraint(inp: NestedBlockConstraintForParam) -> int {\n client GPT35\n prompt #\"\n Generate 3\n {{ ctx.output_format }}\n \"#\n}\n", "test-files/constraints/contact-info.baml": "class PhoneNumber {\n value string @assert(valid_phone_number, {{this|regex_match(\"\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}\")}})\n}\n\nclass EmailAddress {\n value string @assert(valid_email, {{this|regex_match(\"^[_]*([a-z0-9]+(\\.|_*)?)+@([a-z][a-z0-9-]+(\\.|-*\\.))+[a-z]{2,6}$\")}})\n}\n\nclass ContactInfo {\n primary PhoneNumber | EmailAddress\n secondary (PhoneNumber | EmailAddress)?\n}\n\nfunction ExtractContactInfo(document: string) -> ContactInfo {\n client GPT35\n prompt #\"\n Extract a primary contact info, and if possible a secondary contact\n info, from this document:\n\n {{ document }}\n\n {{ ctx.output_format }}\n \"#\n}\n", diff --git a/integ-tests/python-v1/baml_client/parser.py b/integ-tests/python-v1/baml_client/parser.py index 5ab932bdf9..837057306e 100644 --- a/integ-tests/python-v1/baml_client/parser.py +++ b/integ-tests/python-v1/baml_client/parser.py @@ -924,18 +924,6 @@ def TestCaching( result = self.__options.merge_options(baml_options).parse_response(function_name="TestCaching", llm_response=llm_response, mode="request") return typing.cast(str, result) - def TestCompoundCombinedTimeouts( - self, llm_response: str, baml_options: BamlCallOptions = {}, - ) -> str: - result = self.__options.merge_options(baml_options).parse_response(function_name="TestCompoundCombinedTimeouts", llm_response=llm_response, mode="request") - return typing.cast(str, result) - - def TestCompoundRequestTimeout( - self, llm_response: str, baml_options: BamlCallOptions = {}, - ) -> str: - result = self.__options.merge_options(baml_options).parse_response(function_name="TestCompoundRequestTimeout", llm_response=llm_response, mode="request") - return typing.cast(str, result) - def TestCompoundTotalTimeout( self, llm_response: str, baml_options: BamlCallOptions = {}, ) -> str: @@ -2534,18 +2522,6 @@ def TestCaching( result = self.__options.merge_options(baml_options).parse_response(function_name="TestCaching", llm_response=llm_response, mode="stream") return typing.cast(str, result) - def TestCompoundCombinedTimeouts( - self, llm_response: str, baml_options: BamlCallOptions = {}, - ) -> str: - result = self.__options.merge_options(baml_options).parse_response(function_name="TestCompoundCombinedTimeouts", llm_response=llm_response, mode="stream") - return typing.cast(str, result) - - def TestCompoundRequestTimeout( - self, llm_response: str, baml_options: BamlCallOptions = {}, - ) -> str: - result = self.__options.merge_options(baml_options).parse_response(function_name="TestCompoundRequestTimeout", llm_response=llm_response, mode="stream") - return typing.cast(str, result) - def TestCompoundTotalTimeout( self, llm_response: str, baml_options: BamlCallOptions = {}, ) -> str: diff --git a/integ-tests/python-v1/baml_client/sync_client.py b/integ-tests/python-v1/baml_client/sync_client.py index 2e29ff228d..7f44c22aca 100644 --- a/integ-tests/python-v1/baml_client/sync_client.py +++ b/integ-tests/python-v1/baml_client/sync_client.py @@ -2191,34 +2191,6 @@ def TestCaching(self, input: str,not_cached: str, "input": input,"not_cached": not_cached, }) return typing.cast(str, result.cast_to(types, types, stream_types, False, __runtime__)) - def TestCompoundCombinedTimeouts(self, input: str, - baml_options: BamlCallOptions = {}, - ) -> str: - # Check if on_tick is provided - if 'on_tick' in baml_options: - stream = self.stream.TestCompoundCombinedTimeouts(input=input, - baml_options=baml_options) - return stream.get_final_response() - else: - # Original non-streaming code - result = self.__options.merge_options(baml_options).call_function_sync(function_name="TestCompoundCombinedTimeouts", args={ - "input": input, - }) - return typing.cast(str, result.cast_to(types, types, stream_types, False, __runtime__)) - def TestCompoundRequestTimeout(self, input: str, - baml_options: BamlCallOptions = {}, - ) -> str: - # Check if on_tick is provided - if 'on_tick' in baml_options: - stream = self.stream.TestCompoundRequestTimeout(input=input, - baml_options=baml_options) - return stream.get_final_response() - else: - # Original non-streaming code - result = self.__options.merge_options(baml_options).call_function_sync(function_name="TestCompoundRequestTimeout", args={ - "input": input, - }) - return typing.cast(str, result.cast_to(types, types, stream_types, False, __runtime__)) def TestCompoundTotalTimeout(self, input: str, baml_options: BamlCallOptions = {}, ) -> str: @@ -5638,30 +5610,6 @@ def TestCaching(self, input: str,not_cached: str, lambda x: typing.cast(str, x.cast_to(types, types, stream_types, False, __runtime__)), ctx, ) - def TestCompoundCombinedTimeouts(self, input: str, - baml_options: BamlCallOptions = {}, - ) -> baml_py.BamlSyncStream[str, str]: - ctx, result = self.__options.merge_options(baml_options).create_sync_stream(function_name="TestCompoundCombinedTimeouts", args={ - "input": input, - }) - return baml_py.BamlSyncStream[str, str]( - result, - lambda x: typing.cast(str, x.cast_to(types, types, stream_types, True, __runtime__)), - lambda x: typing.cast(str, x.cast_to(types, types, stream_types, False, __runtime__)), - ctx, - ) - def TestCompoundRequestTimeout(self, input: str, - baml_options: BamlCallOptions = {}, - ) -> baml_py.BamlSyncStream[str, str]: - ctx, result = self.__options.merge_options(baml_options).create_sync_stream(function_name="TestCompoundRequestTimeout", args={ - "input": input, - }) - return baml_py.BamlSyncStream[str, str]( - result, - lambda x: typing.cast(str, x.cast_to(types, types, stream_types, True, __runtime__)), - lambda x: typing.cast(str, x.cast_to(types, types, stream_types, False, __runtime__)), - ctx, - ) def TestCompoundTotalTimeout(self, input: str, baml_options: BamlCallOptions = {}, ) -> baml_py.BamlSyncStream[str, str]: @@ -8100,20 +8048,6 @@ def TestCaching(self, input: str,not_cached: str, "input": input,"not_cached": not_cached, }, mode="request") return result - def TestCompoundCombinedTimeouts(self, input: str, - baml_options: BamlCallOptions = {}, - ) -> baml_py.baml_py.HTTPRequest: - result = self.__options.merge_options(baml_options).create_http_request_sync(function_name="TestCompoundCombinedTimeouts", args={ - "input": input, - }, mode="request") - return result - def TestCompoundRequestTimeout(self, input: str, - baml_options: BamlCallOptions = {}, - ) -> baml_py.baml_py.HTTPRequest: - result = self.__options.merge_options(baml_options).create_http_request_sync(function_name="TestCompoundRequestTimeout", args={ - "input": input, - }, mode="request") - return result def TestCompoundTotalTimeout(self, input: str, baml_options: BamlCallOptions = {}, ) -> baml_py.baml_py.HTTPRequest: @@ -9977,20 +9911,6 @@ def TestCaching(self, input: str,not_cached: str, "input": input,"not_cached": not_cached, }, mode="stream") return result - def TestCompoundCombinedTimeouts(self, input: str, - baml_options: BamlCallOptions = {}, - ) -> baml_py.baml_py.HTTPRequest: - result = self.__options.merge_options(baml_options).create_http_request_sync(function_name="TestCompoundCombinedTimeouts", args={ - "input": input, - }, mode="stream") - return result - def TestCompoundRequestTimeout(self, input: str, - baml_options: BamlCallOptions = {}, - ) -> baml_py.baml_py.HTTPRequest: - result = self.__options.merge_options(baml_options).create_http_request_sync(function_name="TestCompoundRequestTimeout", args={ - "input": input, - }, mode="stream") - return result def TestCompoundTotalTimeout(self, input: str, baml_options: BamlCallOptions = {}, ) -> baml_py.baml_py.HTTPRequest: diff --git a/integ-tests/python/baml_client/async_client.py b/integ-tests/python/baml_client/async_client.py index e02a9c84f3..6612ecf00c 100644 --- a/integ-tests/python/baml_client/async_client.py +++ b/integ-tests/python/baml_client/async_client.py @@ -2329,36 +2329,6 @@ async def TestCaching(self, input: str,not_cached: str, "input": input,"not_cached": not_cached, }) return typing.cast(str, result.cast_to(types, types, stream_types, False, __runtime__)) - async def TestCompoundCombinedTimeouts(self, input: str, - baml_options: BamlCallOptions = {}, - ) -> str: - # Check if on_tick is provided - if 'on_tick' in baml_options: - # Use streaming internally when on_tick is provided - stream = self.stream.TestCompoundCombinedTimeouts(input=input, - baml_options=baml_options) - return await stream.get_final_response() - else: - # Original non-streaming code - result = await self.__options.merge_options(baml_options).call_function_async(function_name="TestCompoundCombinedTimeouts", args={ - "input": input, - }) - return typing.cast(str, result.cast_to(types, types, stream_types, False, __runtime__)) - async def TestCompoundRequestTimeout(self, input: str, - baml_options: BamlCallOptions = {}, - ) -> str: - # Check if on_tick is provided - if 'on_tick' in baml_options: - # Use streaming internally when on_tick is provided - stream = self.stream.TestCompoundRequestTimeout(input=input, - baml_options=baml_options) - return await stream.get_final_response() - else: - # Original non-streaming code - result = await self.__options.merge_options(baml_options).call_function_async(function_name="TestCompoundRequestTimeout", args={ - "input": input, - }) - return typing.cast(str, result.cast_to(types, types, stream_types, False, __runtime__)) async def TestCompoundTotalTimeout(self, input: str, baml_options: BamlCallOptions = {}, ) -> str: @@ -5893,30 +5863,6 @@ def TestCaching(self, input: str,not_cached: str, lambda x: typing.cast(str, x.cast_to(types, types, stream_types, False, __runtime__)), ctx, ) - def TestCompoundCombinedTimeouts(self, input: str, - baml_options: BamlCallOptions = {}, - ) -> baml_py.BamlStream[str, str]: - ctx, result = self.__options.merge_options(baml_options).create_async_stream(function_name="TestCompoundCombinedTimeouts", args={ - "input": input, - }) - return baml_py.BamlStream[str, str]( - result, - lambda x: typing.cast(str, x.cast_to(types, types, stream_types, True, __runtime__)), - lambda x: typing.cast(str, x.cast_to(types, types, stream_types, False, __runtime__)), - ctx, - ) - def TestCompoundRequestTimeout(self, input: str, - baml_options: BamlCallOptions = {}, - ) -> baml_py.BamlStream[str, str]: - ctx, result = self.__options.merge_options(baml_options).create_async_stream(function_name="TestCompoundRequestTimeout", args={ - "input": input, - }) - return baml_py.BamlStream[str, str]( - result, - lambda x: typing.cast(str, x.cast_to(types, types, stream_types, True, __runtime__)), - lambda x: typing.cast(str, x.cast_to(types, types, stream_types, False, __runtime__)), - ctx, - ) def TestCompoundTotalTimeout(self, input: str, baml_options: BamlCallOptions = {}, ) -> baml_py.BamlStream[str, str]: @@ -8355,20 +8301,6 @@ async def TestCaching(self, input: str,not_cached: str, "input": input,"not_cached": not_cached, }, mode="request") return result - async def TestCompoundCombinedTimeouts(self, input: str, - baml_options: BamlCallOptions = {}, - ) -> baml_py.baml_py.HTTPRequest: - result = await self.__options.merge_options(baml_options).create_http_request_async(function_name="TestCompoundCombinedTimeouts", args={ - "input": input, - }, mode="request") - return result - async def TestCompoundRequestTimeout(self, input: str, - baml_options: BamlCallOptions = {}, - ) -> baml_py.baml_py.HTTPRequest: - result = await self.__options.merge_options(baml_options).create_http_request_async(function_name="TestCompoundRequestTimeout", args={ - "input": input, - }, mode="request") - return result async def TestCompoundTotalTimeout(self, input: str, baml_options: BamlCallOptions = {}, ) -> baml_py.baml_py.HTTPRequest: @@ -10232,20 +10164,6 @@ async def TestCaching(self, input: str,not_cached: str, "input": input,"not_cached": not_cached, }, mode="stream") return result - async def TestCompoundCombinedTimeouts(self, input: str, - baml_options: BamlCallOptions = {}, - ) -> baml_py.baml_py.HTTPRequest: - result = await self.__options.merge_options(baml_options).create_http_request_async(function_name="TestCompoundCombinedTimeouts", args={ - "input": input, - }, mode="stream") - return result - async def TestCompoundRequestTimeout(self, input: str, - baml_options: BamlCallOptions = {}, - ) -> baml_py.baml_py.HTTPRequest: - result = await self.__options.merge_options(baml_options).create_http_request_async(function_name="TestCompoundRequestTimeout", args={ - "input": input, - }, mode="stream") - return result async def TestCompoundTotalTimeout(self, input: str, baml_options: BamlCallOptions = {}, ) -> baml_py.baml_py.HTTPRequest: diff --git a/integ-tests/python/baml_client/inlinedbaml.py b/integ-tests/python/baml_client/inlinedbaml.py index eb9325466e..69a4f9ae8e 100644 --- a/integ-tests/python/baml_client/inlinedbaml.py +++ b/integ-tests/python/baml_client/inlinedbaml.py @@ -29,7 +29,7 @@ "test-files/aliases/classes.baml": "class TestClassAlias {\n key string @alias(\"key-dash\") @description(#\"\n This is a description for key\n af asdf\n \"#)\n key2 string @alias(\"key21\")\n key3 string @alias(\"key with space\")\n key4 string //unaliased\n key5 string @alias(\"key.with.punctuation/123\")\n}\n\nfunction FnTestClassAlias(input: string) -> TestClassAlias {\n client GPT35\n prompt #\"\n {{ctx.output_format}}\n \"#\n}\n\ntest FnTestClassAlias {\n functions [FnTestClassAlias]\n args {\n input \"example input\"\n }\n}\n", "test-files/aliases/enums.baml": "enum TestEnum {\n A @alias(\"k1\") @description(#\"\n User is angry\n \"#)\n B @alias(\"k22\") @description(#\"\n User is happy\n \"#)\n // tests whether k1 doesnt incorrectly get matched with k11\n C @alias(\"k11\") @description(#\"\n User is sad\n \"#)\n D @alias(\"k44\") @description(#\"\n User is confused\n \"#)\n E @description(#\"\n User is excited\n \"#)\n F @alias(\"k5\") // only alias\n \n G @alias(\"k6\") @description(#\"\n User is bored\n With a long description\n \"#)\n \n @@alias(\"Category\")\n}\n\nfunction FnTestAliasedEnumOutput(input: string) -> TestEnum {\n client GPT35\n prompt #\"\n Classify the user input into the following category\n \n {{ ctx.output_format }}\n\n {{ _.role('user') }}\n {{input}}\n\n {{ _.role('assistant') }}\n Category ID:\n \"#\n}\n\ntest FnTestAliasedEnumOutput {\n functions [FnTestAliasedEnumOutput]\n args {\n input \"mehhhhh\"\n }\n}", "test-files/builtin/fetch.baml": " // class Todo {\n // id int\n // todo string\n // completed bool\n // userId int\n // }\n\n // function GetTodo() -> Todo {\n // Todo {\n // id: 1,\n // }\n // }\n\n // function LlmDescribeTodo(todo: Todo) -> string {\n // client GPT4o\n // prompt #\"Describe the following todo in detail: {{ todo }}\"#\n // }\n\n // function UseGetTodoFunction() -> string {\n // let todo = GetTodo();\n // LlmDescribeTodo(todo)\n // }\n\n // test UseGetTodoFunction() {\n // functions [UseGetTodoFunction]\n // args { }\n // }\n\n // function SearchWikipedia(query: string) -> string {\n // baml.fetch_value(baml.Request {\n // base_url: UrlEncode(\"https://en.wikipedia.org/wiki/Special:Search\", {search query})\n // })\n // }\n\n // function UrlEncode(base_url: string, query_params: map) -> string {\n // client \"openai/o3\"\n // prompt #\"\n // Encode the following base URL and query parameters into a valid URL:\n\n // URL: {{ base_url }}\n\n // Query Params: {{ query_params }}\n\n // Answer with just the final URL, no other text\n // \"#\n // }\n\n // function ConvertUserQuery(query: string) -> string {\n // client GPT4o\n // prompt #\"\n // Convert the following user query into a search query for Wikipedia: {{ query }}\n\n // Ideally it should be a single word that could map to an actual Wikipedia page\n // \"#\n // }\n\n // function LlmFormulateResponse(query: string, search_result: string?) -> string {\n // client GPT4o\n // prompt #\"\n // You are a helpful assistant. Answer the following user query: {{ query }}.\n\n // {% if search_result != \"\" %}\n // Use this Wikipedia search result as a reference for your answer: {{ search_result }}\n // {% endif %}\n // \"#\n // }\n\n // function NeedsWikipediaSearch(query: string) -> bool {\n // client GPT4o\n // prompt #\"Does the following user query need a Wikipedia search?: {{ query }}\"#\n // }\n\n // function ChatResponse(query: string) -> string {\n // let needs_search = NeedsWikipediaSearch(query);\n // let search_result = SearchWikipedia(ConvertUserQuery(query));\n // LlmFormulateResponse(query, search_result)\n // }\n\n // test TestName {\n // functions [ChatResponse]\n // args {\n // query #\"\n // Tell me everything about Rome\n // \"#\n // messages []\n // }\n // }\n\n // test UrlEncode {\n // functions [UrlEncode]\n // args {\n // base_url \"https://en.wikipedia.org/wiki/Special:Search\"\n // query_params {search \"Tell me everything about Rome\"\n // go \"Go\"}\n // }\n // }\n\n // test Convert {\n // functions [ConvertUserQuery]\n // args {\n // query #\"\n // Tell me everything about Rome\n // \"#\n // }\n // }\n", - "test-files/client-timeout-config.baml": "// Client with extremely short timeouts for testing\nclient TestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n connect_timeout_ms 1 // 1ms - will timeout on connect\n request_timeout_ms 10 // 10ms - very likely to timeout\n }\n }\n}\n\n// Function that will timeout\nfunction TestTimeoutError(input: string) -> string {\n client TestTimeoutClient\n prompt #\"\n This is a test that should timeout.\n Please write a very long response about: {{input}}\n \"#\n}\n\n// Client with only request timeout\nclient TestRequestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 50 // 50ms timeout\n }\n }\n}\n\nfunction TestRequestTimeout(input: string) -> string {\n client TestRequestTimeoutClient\n prompt #\"\n Generate a detailed 500 word essay about: {{input}}\n \"#\n}\n\n// Client with zero timeout (infinite)\nclient TestZeroTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 0 // 0 means infinite\n }\n }\n}\n\nfunction TestZeroTimeout(input: string) -> string {\n client TestZeroTimeoutClient\n prompt #\"\n Echo: {{input}}\n \"#\n}\n\n// Fallback client that includes timeout client\nclient TestTimeoutFallbackClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n }\n}\n\nfunction TestTimeoutFallback(input: string) -> string {\n client TestTimeoutFallbackClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Client for streaming timeout tests (Phase 4)\nclient TestStreamingTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n time_to_first_token_timeout_ms 1 // 1ms - extremely short, should always timeout\n idle_timeout_ms 1 // 1ms - extremely short, should always timeout\n }\n }\n}\n\nfunction TestStreamingTimeout(input: string) -> string {\n client TestStreamingTimeoutClient\n prompt #\"\n Stream a long response about: {{input}}\n \"#\n}\n\n// Compound client with request_timeout_ms override\n// This overrides the tight 10ms timeout of TestTimeoutClient with a more generous 5000ms\nclient CompoundTimeoutClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n request_timeout_ms 5000 // Override the tight timeout from primitives\n }\n }\n}\n\nfunction TestCompoundRequestTimeout(input: string) -> string {\n client CompoundTimeoutClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Compound client with total_timeout_ms\n// Even though the second client has infinite timeout, total_timeout_ms enforces an overall limit\nclient CompoundTotalTimeoutClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n total_timeout_ms 1000 // 1 second total timeout for the entire strategy\n }\n }\n}\n\nfunction TestCompoundTotalTimeout(input: string) -> string {\n client CompoundTotalTimeoutClient\n prompt #\"\n Generate a very long detailed essay about: {{input}}\n \"#\n}\n\n// Compound client with both request_timeout_ms and total_timeout_ms\nclient CompoundCombinedTimeoutsClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n request_timeout_ms 5000 // Generous per-request timeout\n total_timeout_ms 10000 // 10 second total timeout\n }\n }\n}\n\nfunction TestCompoundCombinedTimeouts(input: string) -> string {\n client CompoundCombinedTimeoutsClient\n prompt #\"\n Echo: {{input}}\n \"#\n}", + "test-files/client-timeout-config.baml": "// Client with extremely short timeouts for testing\nclient TestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n connect_timeout_ms 1 // 1ms - will timeout on connect\n request_timeout_ms 10 // 10ms - very likely to timeout\n }\n }\n}\n\n// Function that will timeout\nfunction TestTimeoutError(input: string) -> string {\n client TestTimeoutClient\n prompt #\"\n This is a test that should timeout.\n Please write a very long response about: {{input}}\n \"#\n}\n\n// Client with only request timeout\nclient TestRequestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 50 // 50ms timeout\n }\n }\n}\n\nfunction TestRequestTimeout(input: string) -> string {\n client TestRequestTimeoutClient\n prompt #\"\n Generate a detailed 500 word essay about: {{input}}\n \"#\n}\n\n// Client with zero timeout (infinite)\nclient TestZeroTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 0 // 0 means infinite\n }\n }\n}\n\nfunction TestZeroTimeout(input: string) -> string {\n client TestZeroTimeoutClient\n prompt #\"\n Echo: {{input}}\n \"#\n}\n\n// Fallback client that includes timeout client\nclient TestTimeoutFallbackClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n }\n}\n\nfunction TestTimeoutFallback(input: string) -> string {\n client TestTimeoutFallbackClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Client for streaming timeout tests (Phase 4)\nclient TestStreamingTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n time_to_first_token_timeout_ms 1 // 1ms - extremely short, should always timeout\n idle_timeout_ms 1 // 1ms - extremely short, should always timeout\n }\n }\n}\n\nfunction TestStreamingTimeout(input: string) -> string {\n client TestStreamingTimeoutClient\n prompt #\"\n Stream a long response about: {{input}}\n \"#\n}\n\n// Compound client with total_timeout_ms\n// Even though the second client has infinite timeout, total_timeout_ms enforces an overall limit\nclient CompoundTotalTimeoutClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n total_timeout_ms 1000 // 1 second total timeout for the entire strategy\n }\n }\n}\n\nfunction TestCompoundTotalTimeout(input: string) -> string {\n client CompoundTotalTimeoutClient\n prompt #\"\n Generate a very long detailed essay about: {{input}}\n \"#\n}", "test-files/comments/comments.baml": "// add some functions, classes, enums etc with comments all over.", "test-files/constraints/constraints.baml": "// These classes and functions test several properties of\n// constrains:\n//\n// - The ability for constrains on fields to pass or fail.\n// - The ability for constraints on bare args and return types to pass or fail.\n// - The ability of constraints to influence which variant of a union is chosen\n// by the parser, when the structure is not sufficient to decide.\n\n/// A Martian organism with an age.\n/// Such a nice type.\nclass Martian {\n /// The age of the Martian in Mars years.\n /// So many Mars years.\n age int @check(young_enough, {{ this < 30 }})\n}\n\nclass Earthling {\n age int @check(earth_aged, {{this < 200 and this > 0}}) @check(no_infants, {{this >1}})\n}\n\n\nclass FooAny {\n planetary_age Martian | Earthling\n certainty int @check(unreasonably_certain, {{this == 102931}})\n species string @check(trivial, {{this == \"Homo sapiens\"}}) @check(regex_good, {{this|regex_match(\"Homo\")}}) @check(regex_bad, {{this|regex_match(\"neanderthalensis\")}})\n}\n\n\nfunction PredictAge(name: string) -> FooAny {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling\n and capitalization). I'll give you a hint: If the name\n is \"Greg\", his age is 41.\n\n {{ctx.output_format}}\n \"#\n}\n\n\nfunction PredictAgeBare(inp: string @assert(big_enough, {{this|length > 1}})) -> int @check(too_big, {{this == 10102}}) {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ inp.name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling).\n\n {{ctx.output_format}}\n \"#\n}\n\nfunction ReturnFailingAssert(inp: int @assert(small_int, {{this < 10}})) -> int @assert(big_int, {{this > 100}}) {\n client GPT35\n prompt #\"\n Return the next integer after {{ inp }}.\n\n {{ctx.output_format}}\n \"#\n}\n\nclass TwoStoriesOneTitle {\n title string\n story_a string @assert(too_long_story, {{this|length > 1000000}} )\n story_b string @assert(too_long_story, {{this|length > 1000000}} )\n}\n\nfunction StreamFailingAssertion(theme: string, length: int) -> TwoStoriesOneTitle {\n client GPT35\n prompt #\"\n Tell me two different stories along the theme of {{ theme }} with the same title.\n Please make each about {{ length }} words long.\n {{ctx.output_format}}\n \"#\n}\n\nclass TwoStoriesOneTitleCheck {\n title string\n story_a string @check(too_long_story, {{this|length > 1000000}} )\n story_b string @check(too_long_story, {{this|length > 1000000}} )\n}\n\nfunction StreamFailingCheck(theme: string, length: int) -> TwoStoriesOneTitleCheck {\n client GPT35\n prompt #\"\n Tell me two different stories along the theme of {{ theme }} with the same title.\n Please make each about {{ length }} words long.\n {{ctx.output_format}}\n \"#\n}\n\nclass BlockConstraint {\n foo int\n bar string\n @@check(cross_field, {{ this.bar|length > this.foo }})\n}\n\nfunction MakeBlockConstraint() -> BlockConstraint {\n client GPT35\n prompt #\"\n Generate an output in the following schema with a short string and a large int.\n\n {{ ctx.output_format }}\n \"#\n}\n\nclass NestedBlockConstraint {\n nbc BlockConstraint\n}\n\nclass BlockConstraintForParam {\n bcfp int\n bcfp2 string\n @@assert(hi, {{ this.bcfp2|length < this.bcfp }})\n}\n\nclass NestedBlockConstraintForParam {\n nbcfp BlockConstraintForParam\n}\n\nfunction MakeNestedBlockConstraint() -> NestedBlockConstraint {\n client GPT35\n prompt #\"Generate an output where the inner foo is 1 and the inner bar is \"hello\".\n {{ ctx.output_format }}\n \"#\n}\n\nfunction UseBlockConstraint(inp: BlockConstraintForParam) -> int {\n client GPT35\n prompt #\"\n Generate 3\n {{ ctx.output_format }}\n \"#\n}\n\nfunction UseNestedBlockConstraint(inp: NestedBlockConstraintForParam) -> int {\n client GPT35\n prompt #\"\n Generate 3\n {{ ctx.output_format }}\n \"#\n}\n", "test-files/constraints/contact-info.baml": "class PhoneNumber {\n value string @assert(valid_phone_number, {{this|regex_match(\"\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}\")}})\n}\n\nclass EmailAddress {\n value string @assert(valid_email, {{this|regex_match(\"^[_]*([a-z0-9]+(\\.|_*)?)+@([a-z][a-z0-9-]+(\\.|-*\\.))+[a-z]{2,6}$\")}})\n}\n\nclass ContactInfo {\n primary PhoneNumber | EmailAddress\n secondary (PhoneNumber | EmailAddress)?\n}\n\nfunction ExtractContactInfo(document: string) -> ContactInfo {\n client GPT35\n prompt #\"\n Extract a primary contact info, and if possible a secondary contact\n info, from this document:\n\n {{ document }}\n\n {{ ctx.output_format }}\n \"#\n}\n", diff --git a/integ-tests/python/baml_client/parser.py b/integ-tests/python/baml_client/parser.py index 5ab932bdf9..837057306e 100644 --- a/integ-tests/python/baml_client/parser.py +++ b/integ-tests/python/baml_client/parser.py @@ -924,18 +924,6 @@ def TestCaching( result = self.__options.merge_options(baml_options).parse_response(function_name="TestCaching", llm_response=llm_response, mode="request") return typing.cast(str, result) - def TestCompoundCombinedTimeouts( - self, llm_response: str, baml_options: BamlCallOptions = {}, - ) -> str: - result = self.__options.merge_options(baml_options).parse_response(function_name="TestCompoundCombinedTimeouts", llm_response=llm_response, mode="request") - return typing.cast(str, result) - - def TestCompoundRequestTimeout( - self, llm_response: str, baml_options: BamlCallOptions = {}, - ) -> str: - result = self.__options.merge_options(baml_options).parse_response(function_name="TestCompoundRequestTimeout", llm_response=llm_response, mode="request") - return typing.cast(str, result) - def TestCompoundTotalTimeout( self, llm_response: str, baml_options: BamlCallOptions = {}, ) -> str: @@ -2534,18 +2522,6 @@ def TestCaching( result = self.__options.merge_options(baml_options).parse_response(function_name="TestCaching", llm_response=llm_response, mode="stream") return typing.cast(str, result) - def TestCompoundCombinedTimeouts( - self, llm_response: str, baml_options: BamlCallOptions = {}, - ) -> str: - result = self.__options.merge_options(baml_options).parse_response(function_name="TestCompoundCombinedTimeouts", llm_response=llm_response, mode="stream") - return typing.cast(str, result) - - def TestCompoundRequestTimeout( - self, llm_response: str, baml_options: BamlCallOptions = {}, - ) -> str: - result = self.__options.merge_options(baml_options).parse_response(function_name="TestCompoundRequestTimeout", llm_response=llm_response, mode="stream") - return typing.cast(str, result) - def TestCompoundTotalTimeout( self, llm_response: str, baml_options: BamlCallOptions = {}, ) -> str: diff --git a/integ-tests/python/baml_client/sync_client.py b/integ-tests/python/baml_client/sync_client.py index 2e29ff228d..7f44c22aca 100644 --- a/integ-tests/python/baml_client/sync_client.py +++ b/integ-tests/python/baml_client/sync_client.py @@ -2191,34 +2191,6 @@ def TestCaching(self, input: str,not_cached: str, "input": input,"not_cached": not_cached, }) return typing.cast(str, result.cast_to(types, types, stream_types, False, __runtime__)) - def TestCompoundCombinedTimeouts(self, input: str, - baml_options: BamlCallOptions = {}, - ) -> str: - # Check if on_tick is provided - if 'on_tick' in baml_options: - stream = self.stream.TestCompoundCombinedTimeouts(input=input, - baml_options=baml_options) - return stream.get_final_response() - else: - # Original non-streaming code - result = self.__options.merge_options(baml_options).call_function_sync(function_name="TestCompoundCombinedTimeouts", args={ - "input": input, - }) - return typing.cast(str, result.cast_to(types, types, stream_types, False, __runtime__)) - def TestCompoundRequestTimeout(self, input: str, - baml_options: BamlCallOptions = {}, - ) -> str: - # Check if on_tick is provided - if 'on_tick' in baml_options: - stream = self.stream.TestCompoundRequestTimeout(input=input, - baml_options=baml_options) - return stream.get_final_response() - else: - # Original non-streaming code - result = self.__options.merge_options(baml_options).call_function_sync(function_name="TestCompoundRequestTimeout", args={ - "input": input, - }) - return typing.cast(str, result.cast_to(types, types, stream_types, False, __runtime__)) def TestCompoundTotalTimeout(self, input: str, baml_options: BamlCallOptions = {}, ) -> str: @@ -5638,30 +5610,6 @@ def TestCaching(self, input: str,not_cached: str, lambda x: typing.cast(str, x.cast_to(types, types, stream_types, False, __runtime__)), ctx, ) - def TestCompoundCombinedTimeouts(self, input: str, - baml_options: BamlCallOptions = {}, - ) -> baml_py.BamlSyncStream[str, str]: - ctx, result = self.__options.merge_options(baml_options).create_sync_stream(function_name="TestCompoundCombinedTimeouts", args={ - "input": input, - }) - return baml_py.BamlSyncStream[str, str]( - result, - lambda x: typing.cast(str, x.cast_to(types, types, stream_types, True, __runtime__)), - lambda x: typing.cast(str, x.cast_to(types, types, stream_types, False, __runtime__)), - ctx, - ) - def TestCompoundRequestTimeout(self, input: str, - baml_options: BamlCallOptions = {}, - ) -> baml_py.BamlSyncStream[str, str]: - ctx, result = self.__options.merge_options(baml_options).create_sync_stream(function_name="TestCompoundRequestTimeout", args={ - "input": input, - }) - return baml_py.BamlSyncStream[str, str]( - result, - lambda x: typing.cast(str, x.cast_to(types, types, stream_types, True, __runtime__)), - lambda x: typing.cast(str, x.cast_to(types, types, stream_types, False, __runtime__)), - ctx, - ) def TestCompoundTotalTimeout(self, input: str, baml_options: BamlCallOptions = {}, ) -> baml_py.BamlSyncStream[str, str]: @@ -8100,20 +8048,6 @@ def TestCaching(self, input: str,not_cached: str, "input": input,"not_cached": not_cached, }, mode="request") return result - def TestCompoundCombinedTimeouts(self, input: str, - baml_options: BamlCallOptions = {}, - ) -> baml_py.baml_py.HTTPRequest: - result = self.__options.merge_options(baml_options).create_http_request_sync(function_name="TestCompoundCombinedTimeouts", args={ - "input": input, - }, mode="request") - return result - def TestCompoundRequestTimeout(self, input: str, - baml_options: BamlCallOptions = {}, - ) -> baml_py.baml_py.HTTPRequest: - result = self.__options.merge_options(baml_options).create_http_request_sync(function_name="TestCompoundRequestTimeout", args={ - "input": input, - }, mode="request") - return result def TestCompoundTotalTimeout(self, input: str, baml_options: BamlCallOptions = {}, ) -> baml_py.baml_py.HTTPRequest: @@ -9977,20 +9911,6 @@ def TestCaching(self, input: str,not_cached: str, "input": input,"not_cached": not_cached, }, mode="stream") return result - def TestCompoundCombinedTimeouts(self, input: str, - baml_options: BamlCallOptions = {}, - ) -> baml_py.baml_py.HTTPRequest: - result = self.__options.merge_options(baml_options).create_http_request_sync(function_name="TestCompoundCombinedTimeouts", args={ - "input": input, - }, mode="stream") - return result - def TestCompoundRequestTimeout(self, input: str, - baml_options: BamlCallOptions = {}, - ) -> baml_py.baml_py.HTTPRequest: - result = self.__options.merge_options(baml_options).create_http_request_sync(function_name="TestCompoundRequestTimeout", args={ - "input": input, - }, mode="stream") - return result def TestCompoundTotalTimeout(self, input: str, baml_options: BamlCallOptions = {}, ) -> baml_py.baml_py.HTTPRequest: diff --git a/integ-tests/python/tests/test_timeouts.py b/integ-tests/python/tests/test_timeouts.py index 98715ee5c6..6e1d7a7a12 100644 --- a/integ-tests/python/tests/test_timeouts.py +++ b/integ-tests/python/tests/test_timeouts.py @@ -121,19 +121,6 @@ async def test_timeout_error_includes_client_name(): assert "TestTimeoutClient" in error_str or "client" in error_str.lower() -@pytest.mark.asyncio -async def test_compound_request_timeout_override(): - """Test that compound client's request_timeout_ms overrides primitives""" - # CompoundTimeoutClient has request_timeout_ms 5000, overriding the tight 10ms timeout of TestTimeoutClient - # It should use the second client (TestZeroTimeoutClient) which should succeed - result = await b.TestCompoundRequestTimeout("hello world") - - # Should have succeeded with the second client - assert result is not None - assert isinstance(result, str) - assert len(result) > 10 # Should have gotten a reasonable response - - @pytest.mark.asyncio async def test_compound_total_timeout(): """Test that compound client's total_timeout_ms is enforced""" @@ -152,17 +139,6 @@ async def test_compound_total_timeout(): assert "timeout" in str(error).lower() -@pytest.mark.asyncio -async def test_compound_combined_timeouts(): - """Test compound client with both request_timeout_ms and total_timeout_ms""" - result = await b.TestCompoundCombinedTimeouts("test combined timeouts") - - # Should have succeeded with the fallback mechanism - assert result is not None - assert isinstance(result, str) - assert len(result) > 10 # Should have gotten a reasonable response - - # Mock OpenAI-compatible streaming server that sends many chunks with delays # This will send 200 chunks with 10ms between each, taking ~2.5 seconds total # BUT chunk 3 has a 500ms delay to trigger the idle timeout diff --git a/integ-tests/react/baml_client/async_client.ts b/integ-tests/react/baml_client/async_client.ts index c39eb762ed..2b08d37047 100644 --- a/integ-tests/react/baml_client/async_client.ts +++ b/integ-tests/react/baml_client/async_client.ts @@ -7296,102 +7296,6 @@ export type RecursivePartialNull = MovedRecursivePartialNull } } - async TestCompoundCombinedTimeouts( - input: string, - __baml_options__?: BamlCallOptions - ): Promise { - try { - const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } - const signal = options.signal; - - if (signal?.aborted) { - throw new BamlAbortError('Operation was aborted', signal.reason); - } - - // Check if onTick is provided - route through streaming if so - if (options.onTick) { - const stream = this.stream.TestCompoundCombinedTimeouts( - input, - __baml_options__ - ); - - return await stream.getFinalResponse(); - } - - const collector = options.collector ? (Array.isArray(options.collector) ? options.collector : - [options.collector]) : []; - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - const raw = await this.runtime.callFunction( - "TestCompoundCombinedTimeouts", - { - "input": input - }, - this.ctxManager.cloneContext(), - options.tb?.__tb(), - options.clientRegistry, - collector, - options.tags || {}, - env, - signal, - options.watchers, - ) - return raw.parsed(false) as string - } catch (error) { - throw toBamlError(error); - } - } - - async TestCompoundRequestTimeout( - input: string, - __baml_options__?: BamlCallOptions - ): Promise { - try { - const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } - const signal = options.signal; - - if (signal?.aborted) { - throw new BamlAbortError('Operation was aborted', signal.reason); - } - - // Check if onTick is provided - route through streaming if so - if (options.onTick) { - const stream = this.stream.TestCompoundRequestTimeout( - input, - __baml_options__ - ); - - return await stream.getFinalResponse(); - } - - const collector = options.collector ? (Array.isArray(options.collector) ? options.collector : - [options.collector]) : []; - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - const raw = await this.runtime.callFunction( - "TestCompoundRequestTimeout", - { - "input": input - }, - this.ctxManager.cloneContext(), - options.tb?.__tb(), - options.clientRegistry, - collector, - options.tags || {}, - env, - signal, - options.watchers, - ) - return raw.parsed(false) as string - } catch (error) { - throw toBamlError(error); - } - } - async TestCompoundTotalTimeout( input: string, __baml_options__?: BamlCallOptions @@ -22826,138 +22730,6 @@ export type RecursivePartialNull = MovedRecursivePartialNull } } - TestCompoundCombinedTimeouts( - input: string, - __baml_options__?: BamlCallOptions - ): BamlStream - { - try { - const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } - const signal = options.signal; - - if (signal?.aborted) { - throw new BamlAbortError('Operation was aborted', signal.reason); - } - - let collector = options.collector ? (Array.isArray(options.collector) ? options.collector : - [options.collector]) : []; - - let onTickWrapper: (() => void) | undefined; - - // Create collector and wrap onTick if provided - if (options.onTick) { - const tickCollector = new Collector("on-tick-collector"); - collector = [...collector, tickCollector]; - - onTickWrapper = () => { - const log = tickCollector.last; - if (log) { - try { - options.onTick!("Unknown", log); - } catch (error) { - console.error("Error in onTick callback for TestCompoundCombinedTimeouts", error); - } - } - }; - } - - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - const raw = this.runtime.streamFunction( - "TestCompoundCombinedTimeouts", - { - "input": input - }, - undefined, - this.ctxManager.cloneContext(), - options.tb?.__tb(), - options.clientRegistry, - collector, - options.tags || {}, - env, - signal, - onTickWrapper, - ) - return new BamlStream( - raw, - (a): string => a, - (a): string => a, - this.ctxManager.cloneContext(), - options.signal, - ) - } catch (error) { - throw toBamlError(error); - } - } - - TestCompoundRequestTimeout( - input: string, - __baml_options__?: BamlCallOptions - ): BamlStream - { - try { - const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } - const signal = options.signal; - - if (signal?.aborted) { - throw new BamlAbortError('Operation was aborted', signal.reason); - } - - let collector = options.collector ? (Array.isArray(options.collector) ? options.collector : - [options.collector]) : []; - - let onTickWrapper: (() => void) | undefined; - - // Create collector and wrap onTick if provided - if (options.onTick) { - const tickCollector = new Collector("on-tick-collector"); - collector = [...collector, tickCollector]; - - onTickWrapper = () => { - const log = tickCollector.last; - if (log) { - try { - options.onTick!("Unknown", log); - } catch (error) { - console.error("Error in onTick callback for TestCompoundRequestTimeout", error); - } - } - }; - } - - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - const raw = this.runtime.streamFunction( - "TestCompoundRequestTimeout", - { - "input": input - }, - undefined, - this.ctxManager.cloneContext(), - options.tb?.__tb(), - options.clientRegistry, - collector, - options.tags || {}, - env, - signal, - onTickWrapper, - ) - return new BamlStream( - raw, - (a): string => a, - (a): string => a, - this.ctxManager.cloneContext(), - options.signal, - ) - } catch (error) { - throw toBamlError(error); - } - } - TestCompoundTotalTimeout( input: string, __baml_options__?: BamlCallOptions diff --git a/integ-tests/react/baml_client/async_request.ts b/integ-tests/react/baml_client/async_request.ts index a72341b250..d6dd35ebda 100644 --- a/integ-tests/react/baml_client/async_request.ts +++ b/integ-tests/react/baml_client/async_request.ts @@ -3791,56 +3791,6 @@ env?: Record } } - async TestCompoundCombinedTimeouts( - input: string, - __baml_options__?: BamlCallOptions - ): Promise { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return await this.runtime.buildRequest( - "TestCompoundCombinedTimeouts", - { - "input": input - }, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - false, - env - ) - } catch (error) { - throw toBamlError(error); - } - } - - async TestCompoundRequestTimeout( - input: string, - __baml_options__?: BamlCallOptions - ): Promise { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return await this.runtime.buildRequest( - "TestCompoundRequestTimeout", - { - "input": input - }, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - false, - env - ) - } catch (error) { - throw toBamlError(error); - } - } - async TestCompoundTotalTimeout( input: string, __baml_options__?: BamlCallOptions @@ -10472,56 +10422,6 @@ env?: Record } } - async TestCompoundCombinedTimeouts( - input: string, - __baml_options__?: BamlCallOptions - ): Promise { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return await this.runtime.buildRequest( - "TestCompoundCombinedTimeouts", - { - "input": input - }, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - true, - env - ) - } catch (error) { - throw toBamlError(error); - } - } - - async TestCompoundRequestTimeout( - input: string, - __baml_options__?: BamlCallOptions - ): Promise { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return await this.runtime.buildRequest( - "TestCompoundRequestTimeout", - { - "input": input - }, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - true, - env - ) - } catch (error) { - throw toBamlError(error); - } - } - async TestCompoundTotalTimeout( input: string, __baml_options__?: BamlCallOptions diff --git a/integ-tests/react/baml_client/inlinedbaml.ts b/integ-tests/react/baml_client/inlinedbaml.ts index ba04fc3b0d..8743e11dc1 100644 --- a/integ-tests/react/baml_client/inlinedbaml.ts +++ b/integ-tests/react/baml_client/inlinedbaml.ts @@ -37,7 +37,7 @@ const fileMap = { "test-files/aliases/classes.baml": "class TestClassAlias {\n key string @alias(\"key-dash\") @description(#\"\n This is a description for key\n af asdf\n \"#)\n key2 string @alias(\"key21\")\n key3 string @alias(\"key with space\")\n key4 string //unaliased\n key5 string @alias(\"key.with.punctuation/123\")\n}\n\nfunction FnTestClassAlias(input: string) -> TestClassAlias {\n client GPT35\n prompt #\"\n {{ctx.output_format}}\n \"#\n}\n\ntest FnTestClassAlias {\n functions [FnTestClassAlias]\n args {\n input \"example input\"\n }\n}\n", "test-files/aliases/enums.baml": "enum TestEnum {\n A @alias(\"k1\") @description(#\"\n User is angry\n \"#)\n B @alias(\"k22\") @description(#\"\n User is happy\n \"#)\n // tests whether k1 doesnt incorrectly get matched with k11\n C @alias(\"k11\") @description(#\"\n User is sad\n \"#)\n D @alias(\"k44\") @description(#\"\n User is confused\n \"#)\n E @description(#\"\n User is excited\n \"#)\n F @alias(\"k5\") // only alias\n \n G @alias(\"k6\") @description(#\"\n User is bored\n With a long description\n \"#)\n \n @@alias(\"Category\")\n}\n\nfunction FnTestAliasedEnumOutput(input: string) -> TestEnum {\n client GPT35\n prompt #\"\n Classify the user input into the following category\n \n {{ ctx.output_format }}\n\n {{ _.role('user') }}\n {{input}}\n\n {{ _.role('assistant') }}\n Category ID:\n \"#\n}\n\ntest FnTestAliasedEnumOutput {\n functions [FnTestAliasedEnumOutput]\n args {\n input \"mehhhhh\"\n }\n}", "test-files/builtin/fetch.baml": " // class Todo {\n // id int\n // todo string\n // completed bool\n // userId int\n // }\n\n // function GetTodo() -> Todo {\n // Todo {\n // id: 1,\n // }\n // }\n\n // function LlmDescribeTodo(todo: Todo) -> string {\n // client GPT4o\n // prompt #\"Describe the following todo in detail: {{ todo }}\"#\n // }\n\n // function UseGetTodoFunction() -> string {\n // let todo = GetTodo();\n // LlmDescribeTodo(todo)\n // }\n\n // test UseGetTodoFunction() {\n // functions [UseGetTodoFunction]\n // args { }\n // }\n\n // function SearchWikipedia(query: string) -> string {\n // baml.fetch_value(baml.Request {\n // base_url: UrlEncode(\"https://en.wikipedia.org/wiki/Special:Search\", {search query})\n // })\n // }\n\n // function UrlEncode(base_url: string, query_params: map) -> string {\n // client \"openai/o3\"\n // prompt #\"\n // Encode the following base URL and query parameters into a valid URL:\n\n // URL: {{ base_url }}\n\n // Query Params: {{ query_params }}\n\n // Answer with just the final URL, no other text\n // \"#\n // }\n\n // function ConvertUserQuery(query: string) -> string {\n // client GPT4o\n // prompt #\"\n // Convert the following user query into a search query for Wikipedia: {{ query }}\n\n // Ideally it should be a single word that could map to an actual Wikipedia page\n // \"#\n // }\n\n // function LlmFormulateResponse(query: string, search_result: string?) -> string {\n // client GPT4o\n // prompt #\"\n // You are a helpful assistant. Answer the following user query: {{ query }}.\n\n // {% if search_result != \"\" %}\n // Use this Wikipedia search result as a reference for your answer: {{ search_result }}\n // {% endif %}\n // \"#\n // }\n\n // function NeedsWikipediaSearch(query: string) -> bool {\n // client GPT4o\n // prompt #\"Does the following user query need a Wikipedia search?: {{ query }}\"#\n // }\n\n // function ChatResponse(query: string) -> string {\n // let needs_search = NeedsWikipediaSearch(query);\n // let search_result = SearchWikipedia(ConvertUserQuery(query));\n // LlmFormulateResponse(query, search_result)\n // }\n\n // test TestName {\n // functions [ChatResponse]\n // args {\n // query #\"\n // Tell me everything about Rome\n // \"#\n // messages []\n // }\n // }\n\n // test UrlEncode {\n // functions [UrlEncode]\n // args {\n // base_url \"https://en.wikipedia.org/wiki/Special:Search\"\n // query_params {search \"Tell me everything about Rome\"\n // go \"Go\"}\n // }\n // }\n\n // test Convert {\n // functions [ConvertUserQuery]\n // args {\n // query #\"\n // Tell me everything about Rome\n // \"#\n // }\n // }\n", - "test-files/client-timeout-config.baml": "// Client with extremely short timeouts for testing\nclient TestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n connect_timeout_ms 1 // 1ms - will timeout on connect\n request_timeout_ms 10 // 10ms - very likely to timeout\n }\n }\n}\n\n// Function that will timeout\nfunction TestTimeoutError(input: string) -> string {\n client TestTimeoutClient\n prompt #\"\n This is a test that should timeout.\n Please write a very long response about: {{input}}\n \"#\n}\n\n// Client with only request timeout\nclient TestRequestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 50 // 50ms timeout\n }\n }\n}\n\nfunction TestRequestTimeout(input: string) -> string {\n client TestRequestTimeoutClient\n prompt #\"\n Generate a detailed 500 word essay about: {{input}}\n \"#\n}\n\n// Client with zero timeout (infinite)\nclient TestZeroTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 0 // 0 means infinite\n }\n }\n}\n\nfunction TestZeroTimeout(input: string) -> string {\n client TestZeroTimeoutClient\n prompt #\"\n Echo: {{input}}\n \"#\n}\n\n// Fallback client that includes timeout client\nclient TestTimeoutFallbackClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n }\n}\n\nfunction TestTimeoutFallback(input: string) -> string {\n client TestTimeoutFallbackClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Client for streaming timeout tests (Phase 4)\nclient TestStreamingTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n time_to_first_token_timeout_ms 1 // 1ms - extremely short, should always timeout\n idle_timeout_ms 1 // 1ms - extremely short, should always timeout\n }\n }\n}\n\nfunction TestStreamingTimeout(input: string) -> string {\n client TestStreamingTimeoutClient\n prompt #\"\n Stream a long response about: {{input}}\n \"#\n}\n\n// Compound client with request_timeout_ms override\n// This overrides the tight 10ms timeout of TestTimeoutClient with a more generous 5000ms\nclient CompoundTimeoutClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n request_timeout_ms 5000 // Override the tight timeout from primitives\n }\n }\n}\n\nfunction TestCompoundRequestTimeout(input: string) -> string {\n client CompoundTimeoutClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Compound client with total_timeout_ms\n// Even though the second client has infinite timeout, total_timeout_ms enforces an overall limit\nclient CompoundTotalTimeoutClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n total_timeout_ms 1000 // 1 second total timeout for the entire strategy\n }\n }\n}\n\nfunction TestCompoundTotalTimeout(input: string) -> string {\n client CompoundTotalTimeoutClient\n prompt #\"\n Generate a very long detailed essay about: {{input}}\n \"#\n}\n\n// Compound client with both request_timeout_ms and total_timeout_ms\nclient CompoundCombinedTimeoutsClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n request_timeout_ms 5000 // Generous per-request timeout\n total_timeout_ms 10000 // 10 second total timeout\n }\n }\n}\n\nfunction TestCompoundCombinedTimeouts(input: string) -> string {\n client CompoundCombinedTimeoutsClient\n prompt #\"\n Echo: {{input}}\n \"#\n}", + "test-files/client-timeout-config.baml": "// Client with extremely short timeouts for testing\nclient TestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n connect_timeout_ms 1 // 1ms - will timeout on connect\n request_timeout_ms 10 // 10ms - very likely to timeout\n }\n }\n}\n\n// Function that will timeout\nfunction TestTimeoutError(input: string) -> string {\n client TestTimeoutClient\n prompt #\"\n This is a test that should timeout.\n Please write a very long response about: {{input}}\n \"#\n}\n\n// Client with only request timeout\nclient TestRequestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 50 // 50ms timeout\n }\n }\n}\n\nfunction TestRequestTimeout(input: string) -> string {\n client TestRequestTimeoutClient\n prompt #\"\n Generate a detailed 500 word essay about: {{input}}\n \"#\n}\n\n// Client with zero timeout (infinite)\nclient TestZeroTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 0 // 0 means infinite\n }\n }\n}\n\nfunction TestZeroTimeout(input: string) -> string {\n client TestZeroTimeoutClient\n prompt #\"\n Echo: {{input}}\n \"#\n}\n\n// Fallback client that includes timeout client\nclient TestTimeoutFallbackClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n }\n}\n\nfunction TestTimeoutFallback(input: string) -> string {\n client TestTimeoutFallbackClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Client for streaming timeout tests (Phase 4)\nclient TestStreamingTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n time_to_first_token_timeout_ms 1 // 1ms - extremely short, should always timeout\n idle_timeout_ms 1 // 1ms - extremely short, should always timeout\n }\n }\n}\n\nfunction TestStreamingTimeout(input: string) -> string {\n client TestStreamingTimeoutClient\n prompt #\"\n Stream a long response about: {{input}}\n \"#\n}\n\n// Compound client with total_timeout_ms\n// Even though the second client has infinite timeout, total_timeout_ms enforces an overall limit\nclient CompoundTotalTimeoutClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n total_timeout_ms 1000 // 1 second total timeout for the entire strategy\n }\n }\n}\n\nfunction TestCompoundTotalTimeout(input: string) -> string {\n client CompoundTotalTimeoutClient\n prompt #\"\n Generate a very long detailed essay about: {{input}}\n \"#\n}", "test-files/comments/comments.baml": "// add some functions, classes, enums etc with comments all over.", "test-files/constraints/constraints.baml": "// These classes and functions test several properties of\n// constrains:\n//\n// - The ability for constrains on fields to pass or fail.\n// - The ability for constraints on bare args and return types to pass or fail.\n// - The ability of constraints to influence which variant of a union is chosen\n// by the parser, when the structure is not sufficient to decide.\n\n/// A Martian organism with an age.\n/// Such a nice type.\nclass Martian {\n /// The age of the Martian in Mars years.\n /// So many Mars years.\n age int @check(young_enough, {{ this < 30 }})\n}\n\nclass Earthling {\n age int @check(earth_aged, {{this < 200 and this > 0}}) @check(no_infants, {{this >1}})\n}\n\n\nclass FooAny {\n planetary_age Martian | Earthling\n certainty int @check(unreasonably_certain, {{this == 102931}})\n species string @check(trivial, {{this == \"Homo sapiens\"}}) @check(regex_good, {{this|regex_match(\"Homo\")}}) @check(regex_bad, {{this|regex_match(\"neanderthalensis\")}})\n}\n\n\nfunction PredictAge(name: string) -> FooAny {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling\n and capitalization). I'll give you a hint: If the name\n is \"Greg\", his age is 41.\n\n {{ctx.output_format}}\n \"#\n}\n\n\nfunction PredictAgeBare(inp: string @assert(big_enough, {{this|length > 1}})) -> int @check(too_big, {{this == 10102}}) {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ inp.name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling).\n\n {{ctx.output_format}}\n \"#\n}\n\nfunction ReturnFailingAssert(inp: int @assert(small_int, {{this < 10}})) -> int @assert(big_int, {{this > 100}}) {\n client GPT35\n prompt #\"\n Return the next integer after {{ inp }}.\n\n {{ctx.output_format}}\n \"#\n}\n\nclass TwoStoriesOneTitle {\n title string\n story_a string @assert(too_long_story, {{this|length > 1000000}} )\n story_b string @assert(too_long_story, {{this|length > 1000000}} )\n}\n\nfunction StreamFailingAssertion(theme: string, length: int) -> TwoStoriesOneTitle {\n client GPT35\n prompt #\"\n Tell me two different stories along the theme of {{ theme }} with the same title.\n Please make each about {{ length }} words long.\n {{ctx.output_format}}\n \"#\n}\n\nclass TwoStoriesOneTitleCheck {\n title string\n story_a string @check(too_long_story, {{this|length > 1000000}} )\n story_b string @check(too_long_story, {{this|length > 1000000}} )\n}\n\nfunction StreamFailingCheck(theme: string, length: int) -> TwoStoriesOneTitleCheck {\n client GPT35\n prompt #\"\n Tell me two different stories along the theme of {{ theme }} with the same title.\n Please make each about {{ length }} words long.\n {{ctx.output_format}}\n \"#\n}\n\nclass BlockConstraint {\n foo int\n bar string\n @@check(cross_field, {{ this.bar|length > this.foo }})\n}\n\nfunction MakeBlockConstraint() -> BlockConstraint {\n client GPT35\n prompt #\"\n Generate an output in the following schema with a short string and a large int.\n\n {{ ctx.output_format }}\n \"#\n}\n\nclass NestedBlockConstraint {\n nbc BlockConstraint\n}\n\nclass BlockConstraintForParam {\n bcfp int\n bcfp2 string\n @@assert(hi, {{ this.bcfp2|length < this.bcfp }})\n}\n\nclass NestedBlockConstraintForParam {\n nbcfp BlockConstraintForParam\n}\n\nfunction MakeNestedBlockConstraint() -> NestedBlockConstraint {\n client GPT35\n prompt #\"Generate an output where the inner foo is 1 and the inner bar is \"hello\".\n {{ ctx.output_format }}\n \"#\n}\n\nfunction UseBlockConstraint(inp: BlockConstraintForParam) -> int {\n client GPT35\n prompt #\"\n Generate 3\n {{ ctx.output_format }}\n \"#\n}\n\nfunction UseNestedBlockConstraint(inp: NestedBlockConstraintForParam) -> int {\n client GPT35\n prompt #\"\n Generate 3\n {{ ctx.output_format }}\n \"#\n}\n", "test-files/constraints/contact-info.baml": "class PhoneNumber {\n value string @assert(valid_phone_number, {{this|regex_match(\"\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}\")}})\n}\n\nclass EmailAddress {\n value string @assert(valid_email, {{this|regex_match(\"^[_]*([a-z0-9]+(\\.|_*)?)+@([a-z][a-z0-9-]+(\\.|-*\\.))+[a-z]{2,6}$\")}})\n}\n\nclass ContactInfo {\n primary PhoneNumber | EmailAddress\n secondary (PhoneNumber | EmailAddress)?\n}\n\nfunction ExtractContactInfo(document: string) -> ContactInfo {\n client GPT35\n prompt #\"\n Extract a primary contact info, and if possible a secondary contact\n info, from this document:\n\n {{ document }}\n\n {{ ctx.output_format }}\n \"#\n}\n", diff --git a/integ-tests/react/baml_client/parser.ts b/integ-tests/react/baml_client/parser.ts index 9be638e168..7c08e9088f 100644 --- a/integ-tests/react/baml_client/parser.ts +++ b/integ-tests/react/baml_client/parser.ts @@ -3480,52 +3480,6 @@ export class LlmResponseParser { } } - TestCompoundCombinedTimeouts( - llmResponse: string, - __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } - ): string { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return this.runtime.parseLlmResponse( - "TestCompoundCombinedTimeouts", - llmResponse, - false, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - env, - ) as string - } catch (error) { - throw toBamlError(error); - } - } - - TestCompoundRequestTimeout( - llmResponse: string, - __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } - ): string { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return this.runtime.parseLlmResponse( - "TestCompoundRequestTimeout", - llmResponse, - false, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - env, - ) as string - } catch (error) { - throw toBamlError(error); - } - } - TestCompoundTotalTimeout( llmResponse: string, __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } @@ -9627,52 +9581,6 @@ export class LlmStreamParser { } } - TestCompoundCombinedTimeouts( - llmResponse: string, - __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } - ): string { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return this.runtime.parseLlmResponse( - "TestCompoundCombinedTimeouts", - llmResponse, - true, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - env, - ) as string - } catch (error) { - throw toBamlError(error); - } - } - - TestCompoundRequestTimeout( - llmResponse: string, - __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } - ): string { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return this.runtime.parseLlmResponse( - "TestCompoundRequestTimeout", - llmResponse, - true, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - env, - ) as string - } catch (error) { - throw toBamlError(error); - } - } - TestCompoundTotalTimeout( llmResponse: string, __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } diff --git a/integ-tests/react/baml_client/react/hooks.tsx b/integ-tests/react/baml_client/react/hooks.tsx index 4c987262fd..5c6a5448f4 100644 --- a/integ-tests/react/baml_client/react/hooks.tsx +++ b/integ-tests/react/baml_client/react/hooks.tsx @@ -7875,106 +7875,6 @@ export function useTestCaching( return useBamlAction(action, props as HookInput<'TestCaching', { stream: false }>) } } -/** - * A specialized hook for the TestCompoundCombinedTimeouts BAML function that supports both streaming and non‑streaming responses. - * - * **Input Types:** - * - * - input: string - * - * - * **Return Type:** - * - **Non‑streaming:** string - * - **Streaming Partial:** string - * - **Streaming Final:** string - * - * **Usage Patterns:** - * 1. **Non‑streaming (Default)** - * - Best for quick responses and simple UI updates. - * 2. **Streaming** - * - Ideal for long‑running operations or real‑time feedback. - * - * **Edge Cases:** - * - Ensure robust error handling via `onError`. - * - Handle cases where partial data may be incomplete or missing. - * - * @example - * ```tsx - * // Basic non‑streaming usage: - * const { data, error, isLoading, mutate } = useTestCompoundCombinedTimeouts({ stream: false}); - * - * // Streaming usage: - * const { data, streamData, isLoading, error, mutate } = useTestCompoundCombinedTimeouts({ - * stream: true | undefined, - * onStreamData: (partial) => console.log('Partial update:', partial), - * onFinalData: (final) => console.log('Final result:', final), - * onError: (err) => console.error('Error:', err), - * }); - * ``` - */ -export function useTestCompoundCombinedTimeouts(props: HookInput<'TestCompoundCombinedTimeouts', { stream: false }>): HookOutput<'TestCompoundCombinedTimeouts', { stream: false }> -export function useTestCompoundCombinedTimeouts(props?: HookInput<'TestCompoundCombinedTimeouts', { stream?: true }>): HookOutput<'TestCompoundCombinedTimeouts', { stream: true }> -export function useTestCompoundCombinedTimeouts( - props: HookInput<'TestCompoundCombinedTimeouts', { stream?: boolean }> = {}, -): HookOutput<'TestCompoundCombinedTimeouts', { stream: true }> | HookOutput<'TestCompoundCombinedTimeouts', { stream: false }> { - let action: ServerAction = Actions.TestCompoundCombinedTimeouts; - if (isStreamingProps(props)) { - action = StreamingActions.TestCompoundCombinedTimeouts; - return useBamlAction(action, props) - } else { - return useBamlAction(action, props as HookInput<'TestCompoundCombinedTimeouts', { stream: false }>) - } -} -/** - * A specialized hook for the TestCompoundRequestTimeout BAML function that supports both streaming and non‑streaming responses. - * - * **Input Types:** - * - * - input: string - * - * - * **Return Type:** - * - **Non‑streaming:** string - * - **Streaming Partial:** string - * - **Streaming Final:** string - * - * **Usage Patterns:** - * 1. **Non‑streaming (Default)** - * - Best for quick responses and simple UI updates. - * 2. **Streaming** - * - Ideal for long‑running operations or real‑time feedback. - * - * **Edge Cases:** - * - Ensure robust error handling via `onError`. - * - Handle cases where partial data may be incomplete or missing. - * - * @example - * ```tsx - * // Basic non‑streaming usage: - * const { data, error, isLoading, mutate } = useTestCompoundRequestTimeout({ stream: false}); - * - * // Streaming usage: - * const { data, streamData, isLoading, error, mutate } = useTestCompoundRequestTimeout({ - * stream: true | undefined, - * onStreamData: (partial) => console.log('Partial update:', partial), - * onFinalData: (final) => console.log('Final result:', final), - * onError: (err) => console.error('Error:', err), - * }); - * ``` - */ -export function useTestCompoundRequestTimeout(props: HookInput<'TestCompoundRequestTimeout', { stream: false }>): HookOutput<'TestCompoundRequestTimeout', { stream: false }> -export function useTestCompoundRequestTimeout(props?: HookInput<'TestCompoundRequestTimeout', { stream?: true }>): HookOutput<'TestCompoundRequestTimeout', { stream: true }> -export function useTestCompoundRequestTimeout( - props: HookInput<'TestCompoundRequestTimeout', { stream?: boolean }> = {}, -): HookOutput<'TestCompoundRequestTimeout', { stream: true }> | HookOutput<'TestCompoundRequestTimeout', { stream: false }> { - let action: ServerAction = Actions.TestCompoundRequestTimeout; - if (isStreamingProps(props)) { - action = StreamingActions.TestCompoundRequestTimeout; - return useBamlAction(action, props) - } else { - return useBamlAction(action, props as HookInput<'TestCompoundRequestTimeout', { stream: false }>) - } -} /** * A specialized hook for the TestCompoundTotalTimeout BAML function that supports both streaming and non‑streaming responses. * diff --git a/integ-tests/react/baml_client/react/server.ts b/integ-tests/react/baml_client/react/server.ts index 5d4118253c..ac749aec20 100644 --- a/integ-tests/react/baml_client/react/server.ts +++ b/integ-tests/react/baml_client/react/server.ts @@ -2774,42 +2774,6 @@ export const TestCaching = async ( ); }; -/** - * Executes the "TestCompoundCombinedTimeouts" BAML action. - * - * This server action calls the underlying BAML function "TestCompoundCombinedTimeouts" - * with the specified parameters. - * - * @param { string } input - Input parameter. - * - * @returns {Promise} A promise that resolves with the result of the action. - */ -export const TestCompoundCombinedTimeouts = async ( - input: string, -): Promise => { - return b.TestCompoundCombinedTimeouts( - input, - ); -}; - -/** - * Executes the "TestCompoundRequestTimeout" BAML action. - * - * This server action calls the underlying BAML function "TestCompoundRequestTimeout" - * with the specified parameters. - * - * @param { string } input - Input parameter. - * - * @returns {Promise} A promise that resolves with the result of the action. - */ -export const TestCompoundRequestTimeout = async ( - input: string, -): Promise => { - return b.TestCompoundRequestTimeout( - input, - ); -}; - /** * Executes the "TestCompoundTotalTimeout" BAML action. * diff --git a/integ-tests/react/baml_client/react/server_streaming.ts b/integ-tests/react/baml_client/react/server_streaming.ts index c5aeab52bc..d591e58cb2 100644 --- a/integ-tests/react/baml_client/react/server_streaming.ts +++ b/integ-tests/react/baml_client/react/server_streaming.ts @@ -2924,44 +2924,6 @@ export const TestCaching = async ( return Promise.resolve(stream.toStreamable()); }; -/** - * Executes the streaming variant of the "TestCompoundCombinedTimeouts" BAML action. - * - * This action initiates a streaming response by calling the corresponding - * BAML stream function. The returned stream yields incremental updates. - * - * @param { string } input - Input parameter. - * - * @returns {ReadableStream} A stream that yields incremental updates from the action. - */ -export const TestCompoundCombinedTimeouts = async ( - input: string, -): Promise> => { - const stream = b.stream.TestCompoundCombinedTimeouts( - input, - ); - return Promise.resolve(stream.toStreamable()); -}; - -/** - * Executes the streaming variant of the "TestCompoundRequestTimeout" BAML action. - * - * This action initiates a streaming response by calling the corresponding - * BAML stream function. The returned stream yields incremental updates. - * - * @param { string } input - Input parameter. - * - * @returns {ReadableStream} A stream that yields incremental updates from the action. - */ -export const TestCompoundRequestTimeout = async ( - input: string, -): Promise> => { - const stream = b.stream.TestCompoundRequestTimeout( - input, - ); - return Promise.resolve(stream.toStreamable()); -}; - /** * Executes the streaming variant of the "TestCompoundTotalTimeout" BAML action. * diff --git a/integ-tests/react/baml_client/react/server_streaming_types.ts b/integ-tests/react/baml_client/react/server_streaming_types.ts index 7818c943ce..cacce9e181 100644 --- a/integ-tests/react/baml_client/react/server_streaming_types.ts +++ b/integ-tests/react/baml_client/react/server_streaming_types.ts @@ -205,8 +205,6 @@ export type StreamingServerTypes = { TestAzureO3WithMaxCompletionTokens: string, TestAzureWithMaxTokens: string, TestCaching: string, - TestCompoundCombinedTimeouts: string, - TestCompoundRequestTimeout: string, TestCompoundTotalTimeout: string, TestFallbackClient: string, TestFallbackStrategy: string, diff --git a/integ-tests/react/baml_client/sync_client.ts b/integ-tests/react/baml_client/sync_client.ts index 8b06d35739..b7225d762e 100644 --- a/integ-tests/react/baml_client/sync_client.ts +++ b/integ-tests/react/baml_client/sync_client.ts @@ -6396,90 +6396,6 @@ export class BamlSyncClient { } } - TestCompoundCombinedTimeouts( - input: string, - __baml_options__?: BamlCallOptions - ): string { - try { - const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } - const signal = options.signal; - - if (signal?.aborted) { - throw new BamlAbortError('Operation was aborted', signal.reason); - } - - // Check if onTick is provided and reject for sync operations - if (options.onTick) { - throw new Error("onTick is not supported for synchronous functions. Please use the async client instead."); - } - - const collector = options.collector ? (Array.isArray(options.collector) ? options.collector : [options.collector]) : []; - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - const raw = this.runtime.callFunctionSync( - "TestCompoundCombinedTimeouts", - { - "input": input - }, - this.ctxManager.cloneContext(), - options.tb?.__tb(), - options.clientRegistry, - collector, - options.tags || {}, - env, - signal, - options.watchers, - ) - return raw.parsed(false) as string - } catch (error: any) { - throw toBamlError(error); - } - } - - TestCompoundRequestTimeout( - input: string, - __baml_options__?: BamlCallOptions - ): string { - try { - const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } - const signal = options.signal; - - if (signal?.aborted) { - throw new BamlAbortError('Operation was aborted', signal.reason); - } - - // Check if onTick is provided and reject for sync operations - if (options.onTick) { - throw new Error("onTick is not supported for synchronous functions. Please use the async client instead."); - } - - const collector = options.collector ? (Array.isArray(options.collector) ? options.collector : [options.collector]) : []; - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - const raw = this.runtime.callFunctionSync( - "TestCompoundRequestTimeout", - { - "input": input - }, - this.ctxManager.cloneContext(), - options.tb?.__tb(), - options.clientRegistry, - collector, - options.tags || {}, - env, - signal, - options.watchers, - ) - return raw.parsed(false) as string - } catch (error: any) { - throw toBamlError(error); - } - } - TestCompoundTotalTimeout( input: string, __baml_options__?: BamlCallOptions diff --git a/integ-tests/react/baml_client/sync_request.ts b/integ-tests/react/baml_client/sync_request.ts index a04d1c204f..46ea3ddae2 100644 --- a/integ-tests/react/baml_client/sync_request.ts +++ b/integ-tests/react/baml_client/sync_request.ts @@ -3787,56 +3787,6 @@ export class HttpRequest { } } - TestCompoundCombinedTimeouts( - input: string, - __baml_options__?: BamlCallOptions - ): HTTPRequest { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return this.runtime.buildRequestSync( - "TestCompoundCombinedTimeouts", - { - "input": input - }, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - false, - env, - ) - } catch (error) { - throw toBamlError(error); - } - } - - TestCompoundRequestTimeout( - input: string, - __baml_options__?: BamlCallOptions - ): HTTPRequest { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return this.runtime.buildRequestSync( - "TestCompoundRequestTimeout", - { - "input": input - }, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - false, - env, - ) - } catch (error) { - throw toBamlError(error); - } - } - TestCompoundTotalTimeout( input: string, __baml_options__?: BamlCallOptions @@ -10468,56 +10418,6 @@ export class HttpStreamRequest { } } - TestCompoundCombinedTimeouts( - input: string, - __baml_options__?: BamlCallOptions - ): HTTPRequest { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return this.runtime.buildRequestSync( - "TestCompoundCombinedTimeouts", - { - "input": input - }, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - true, - env, - ) - } catch (error) { - throw toBamlError(error); - } - } - - TestCompoundRequestTimeout( - input: string, - __baml_options__?: BamlCallOptions - ): HTTPRequest { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return this.runtime.buildRequestSync( - "TestCompoundRequestTimeout", - { - "input": input - }, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - true, - env, - ) - } catch (error) { - throw toBamlError(error); - } - } - TestCompoundTotalTimeout( input: string, __baml_options__?: BamlCallOptions diff --git a/integ-tests/ruby/baml_client/client.rb b/integ-tests/ruby/baml_client/client.rb index 52d2b498cd..6b7aac2f84 100644 --- a/integ-tests/ruby/baml_client/client.rb +++ b/integ-tests/ruby/baml_client/client.rb @@ -3801,56 +3801,6 @@ def TestCaching( input: String, baml_options: T::Hash[Symbol, T.any(BamlClient::TypeBuilder, Baml::ClientRegistry, T.any(Baml::Collector, T::Array[Baml::Collector]), T::Hash[Symbol, String], T::Hash[String, String])] ).returns(String)} - def TestCompoundCombinedTimeouts( - *varargs, - input:, - baml_options: {} - ) - if varargs.any? - raise ArgumentError.new("TestCompoundCombinedTimeouts may only be called with keyword arguments") - end - - options = @options.merge_options(BamlCallOptions.from_hash(baml_options)) - - result = options.call_function_sync(function_name: "TestCompoundCombinedTimeouts", args: { - input: input, - }) - - parsed = result.parsed_using_types(BamlClient::Types, BamlClient::PartialTypes, false) - # for sorbet we need to cast to the return type since parsed is now the right value - # We just need to tell sorbet that the return type is the right type - parsed.cast_to(String) - end - sig {params( - varargs: T.untyped, - input: String, - baml_options: T::Hash[Symbol, T.any(BamlClient::TypeBuilder, Baml::ClientRegistry, T.any(Baml::Collector, T::Array[Baml::Collector]), T::Hash[Symbol, String], T::Hash[String, String])] - ).returns(String)} - def TestCompoundRequestTimeout( - *varargs, - input:, - baml_options: {} - ) - if varargs.any? - raise ArgumentError.new("TestCompoundRequestTimeout may only be called with keyword arguments") - end - - options = @options.merge_options(BamlCallOptions.from_hash(baml_options)) - - result = options.call_function_sync(function_name: "TestCompoundRequestTimeout", args: { - input: input, - }) - - parsed = result.parsed_using_types(BamlClient::Types, BamlClient::PartialTypes, false) - # for sorbet we need to cast to the return type since parsed is now the right value - # We just need to tell sorbet that the return type is the right type - parsed.cast_to(String) - end - sig {params( - varargs: T.untyped, - input: String, - baml_options: T::Hash[Symbol, T.any(BamlClient::TypeBuilder, Baml::ClientRegistry, T.any(Baml::Collector, T::Array[Baml::Collector]), T::Hash[Symbol, String], T::Hash[String, String])] - ).returns(String)} def TestCompoundTotalTimeout( *varargs, input:, @@ -9712,56 +9662,6 @@ def TestCaching( input: String, baml_options: T::Hash[Symbol, T.any(BamlClient::TypeBuilder, Baml::ClientRegistry, T.any(Baml::Collector, T::Array[Baml::Collector]), T::Hash[Symbol, String], T::Hash[String, String])] ).returns(Baml::BamlStream[String, String])} - def TestCompoundCombinedTimeouts( - *varargs, - input:, - baml_options: {} - ) - if varargs.any? - raise ArgumentError.new("TestCompoundCombinedTimeouts may only be called with keyword arguments") - end - - options = @options.merge_options(BamlCallOptions.from_hash(baml_options)) - - ctx, result = options.create_sync_stream(function_name: "TestCompoundCombinedTimeouts", args: { - input: input, - }) - - Baml::BamlStream[String, String].new( - ffi_stream: result, - ctx_manager: ctx - ) - end - sig {params( - varargs: T.untyped, - input: String, - baml_options: T::Hash[Symbol, T.any(BamlClient::TypeBuilder, Baml::ClientRegistry, T.any(Baml::Collector, T::Array[Baml::Collector]), T::Hash[Symbol, String], T::Hash[String, String])] - ).returns(Baml::BamlStream[String, String])} - def TestCompoundRequestTimeout( - *varargs, - input:, - baml_options: {} - ) - if varargs.any? - raise ArgumentError.new("TestCompoundRequestTimeout may only be called with keyword arguments") - end - - options = @options.merge_options(BamlCallOptions.from_hash(baml_options)) - - ctx, result = options.create_sync_stream(function_name: "TestCompoundRequestTimeout", args: { - input: input, - }) - - Baml::BamlStream[String, String].new( - ffi_stream: result, - ctx_manager: ctx - ) - end - sig {params( - varargs: T.untyped, - input: String, - baml_options: T::Hash[Symbol, T.any(BamlClient::TypeBuilder, Baml::ClientRegistry, T.any(Baml::Collector, T::Array[Baml::Collector]), T::Hash[Symbol, String], T::Hash[String, String])] - ).returns(Baml::BamlStream[String, String])} def TestCompoundTotalTimeout( *varargs, input:, diff --git a/integ-tests/typescript-esm/baml_client/async_client.ts b/integ-tests/typescript-esm/baml_client/async_client.ts index e7b134016b..d4e1af38d0 100644 --- a/integ-tests/typescript-esm/baml_client/async_client.ts +++ b/integ-tests/typescript-esm/baml_client/async_client.ts @@ -7296,102 +7296,6 @@ export type RecursivePartialNull = MovedRecursivePartialNull } } - async TestCompoundCombinedTimeouts( - input: string, - __baml_options__?: BamlCallOptions - ): Promise { - try { - const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } - const signal = options.signal; - - if (signal?.aborted) { - throw new BamlAbortError('Operation was aborted', signal.reason); - } - - // Check if onTick is provided - route through streaming if so - if (options.onTick) { - const stream = this.stream.TestCompoundCombinedTimeouts( - input, - __baml_options__ - ); - - return await stream.getFinalResponse(); - } - - const collector = options.collector ? (Array.isArray(options.collector) ? options.collector : - [options.collector]) : []; - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - const raw = await this.runtime.callFunction( - "TestCompoundCombinedTimeouts", - { - "input": input - }, - this.ctxManager.cloneContext(), - options.tb?.__tb(), - options.clientRegistry, - collector, - options.tags || {}, - env, - signal, - options.watchers, - ) - return raw.parsed(false) as string - } catch (error) { - throw toBamlError(error); - } - } - - async TestCompoundRequestTimeout( - input: string, - __baml_options__?: BamlCallOptions - ): Promise { - try { - const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } - const signal = options.signal; - - if (signal?.aborted) { - throw new BamlAbortError('Operation was aborted', signal.reason); - } - - // Check if onTick is provided - route through streaming if so - if (options.onTick) { - const stream = this.stream.TestCompoundRequestTimeout( - input, - __baml_options__ - ); - - return await stream.getFinalResponse(); - } - - const collector = options.collector ? (Array.isArray(options.collector) ? options.collector : - [options.collector]) : []; - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - const raw = await this.runtime.callFunction( - "TestCompoundRequestTimeout", - { - "input": input - }, - this.ctxManager.cloneContext(), - options.tb?.__tb(), - options.clientRegistry, - collector, - options.tags || {}, - env, - signal, - options.watchers, - ) - return raw.parsed(false) as string - } catch (error) { - throw toBamlError(error); - } - } - async TestCompoundTotalTimeout( input: string, __baml_options__?: BamlCallOptions @@ -22826,138 +22730,6 @@ export type RecursivePartialNull = MovedRecursivePartialNull } } - TestCompoundCombinedTimeouts( - input: string, - __baml_options__?: BamlCallOptions - ): BamlStream - { - try { - const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } - const signal = options.signal; - - if (signal?.aborted) { - throw new BamlAbortError('Operation was aborted', signal.reason); - } - - let collector = options.collector ? (Array.isArray(options.collector) ? options.collector : - [options.collector]) : []; - - let onTickWrapper: (() => void) | undefined; - - // Create collector and wrap onTick if provided - if (options.onTick) { - const tickCollector = new Collector("on-tick-collector"); - collector = [...collector, tickCollector]; - - onTickWrapper = () => { - const log = tickCollector.last; - if (log) { - try { - options.onTick!("Unknown", log); - } catch (error) { - console.error("Error in onTick callback for TestCompoundCombinedTimeouts", error); - } - } - }; - } - - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - const raw = this.runtime.streamFunction( - "TestCompoundCombinedTimeouts", - { - "input": input - }, - undefined, - this.ctxManager.cloneContext(), - options.tb?.__tb(), - options.clientRegistry, - collector, - options.tags || {}, - env, - signal, - onTickWrapper, - ) - return new BamlStream( - raw, - (a): string => a, - (a): string => a, - this.ctxManager.cloneContext(), - options.signal, - ) - } catch (error) { - throw toBamlError(error); - } - } - - TestCompoundRequestTimeout( - input: string, - __baml_options__?: BamlCallOptions - ): BamlStream - { - try { - const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } - const signal = options.signal; - - if (signal?.aborted) { - throw new BamlAbortError('Operation was aborted', signal.reason); - } - - let collector = options.collector ? (Array.isArray(options.collector) ? options.collector : - [options.collector]) : []; - - let onTickWrapper: (() => void) | undefined; - - // Create collector and wrap onTick if provided - if (options.onTick) { - const tickCollector = new Collector("on-tick-collector"); - collector = [...collector, tickCollector]; - - onTickWrapper = () => { - const log = tickCollector.last; - if (log) { - try { - options.onTick!("Unknown", log); - } catch (error) { - console.error("Error in onTick callback for TestCompoundRequestTimeout", error); - } - } - }; - } - - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - const raw = this.runtime.streamFunction( - "TestCompoundRequestTimeout", - { - "input": input - }, - undefined, - this.ctxManager.cloneContext(), - options.tb?.__tb(), - options.clientRegistry, - collector, - options.tags || {}, - env, - signal, - onTickWrapper, - ) - return new BamlStream( - raw, - (a): string => a, - (a): string => a, - this.ctxManager.cloneContext(), - options.signal, - ) - } catch (error) { - throw toBamlError(error); - } - } - TestCompoundTotalTimeout( input: string, __baml_options__?: BamlCallOptions diff --git a/integ-tests/typescript-esm/baml_client/async_request.ts b/integ-tests/typescript-esm/baml_client/async_request.ts index 5b7c07e94a..b42d0d8a95 100644 --- a/integ-tests/typescript-esm/baml_client/async_request.ts +++ b/integ-tests/typescript-esm/baml_client/async_request.ts @@ -3791,56 +3791,6 @@ env?: Record } } - async TestCompoundCombinedTimeouts( - input: string, - __baml_options__?: BamlCallOptions - ): Promise { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return await this.runtime.buildRequest( - "TestCompoundCombinedTimeouts", - { - "input": input - }, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - false, - env - ) - } catch (error) { - throw toBamlError(error); - } - } - - async TestCompoundRequestTimeout( - input: string, - __baml_options__?: BamlCallOptions - ): Promise { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return await this.runtime.buildRequest( - "TestCompoundRequestTimeout", - { - "input": input - }, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - false, - env - ) - } catch (error) { - throw toBamlError(error); - } - } - async TestCompoundTotalTimeout( input: string, __baml_options__?: BamlCallOptions @@ -10472,56 +10422,6 @@ env?: Record } } - async TestCompoundCombinedTimeouts( - input: string, - __baml_options__?: BamlCallOptions - ): Promise { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return await this.runtime.buildRequest( - "TestCompoundCombinedTimeouts", - { - "input": input - }, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - true, - env - ) - } catch (error) { - throw toBamlError(error); - } - } - - async TestCompoundRequestTimeout( - input: string, - __baml_options__?: BamlCallOptions - ): Promise { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return await this.runtime.buildRequest( - "TestCompoundRequestTimeout", - { - "input": input - }, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - true, - env - ) - } catch (error) { - throw toBamlError(error); - } - } - async TestCompoundTotalTimeout( input: string, __baml_options__?: BamlCallOptions diff --git a/integ-tests/typescript-esm/baml_client/inlinedbaml.ts b/integ-tests/typescript-esm/baml_client/inlinedbaml.ts index ba04fc3b0d..8743e11dc1 100644 --- a/integ-tests/typescript-esm/baml_client/inlinedbaml.ts +++ b/integ-tests/typescript-esm/baml_client/inlinedbaml.ts @@ -37,7 +37,7 @@ const fileMap = { "test-files/aliases/classes.baml": "class TestClassAlias {\n key string @alias(\"key-dash\") @description(#\"\n This is a description for key\n af asdf\n \"#)\n key2 string @alias(\"key21\")\n key3 string @alias(\"key with space\")\n key4 string //unaliased\n key5 string @alias(\"key.with.punctuation/123\")\n}\n\nfunction FnTestClassAlias(input: string) -> TestClassAlias {\n client GPT35\n prompt #\"\n {{ctx.output_format}}\n \"#\n}\n\ntest FnTestClassAlias {\n functions [FnTestClassAlias]\n args {\n input \"example input\"\n }\n}\n", "test-files/aliases/enums.baml": "enum TestEnum {\n A @alias(\"k1\") @description(#\"\n User is angry\n \"#)\n B @alias(\"k22\") @description(#\"\n User is happy\n \"#)\n // tests whether k1 doesnt incorrectly get matched with k11\n C @alias(\"k11\") @description(#\"\n User is sad\n \"#)\n D @alias(\"k44\") @description(#\"\n User is confused\n \"#)\n E @description(#\"\n User is excited\n \"#)\n F @alias(\"k5\") // only alias\n \n G @alias(\"k6\") @description(#\"\n User is bored\n With a long description\n \"#)\n \n @@alias(\"Category\")\n}\n\nfunction FnTestAliasedEnumOutput(input: string) -> TestEnum {\n client GPT35\n prompt #\"\n Classify the user input into the following category\n \n {{ ctx.output_format }}\n\n {{ _.role('user') }}\n {{input}}\n\n {{ _.role('assistant') }}\n Category ID:\n \"#\n}\n\ntest FnTestAliasedEnumOutput {\n functions [FnTestAliasedEnumOutput]\n args {\n input \"mehhhhh\"\n }\n}", "test-files/builtin/fetch.baml": " // class Todo {\n // id int\n // todo string\n // completed bool\n // userId int\n // }\n\n // function GetTodo() -> Todo {\n // Todo {\n // id: 1,\n // }\n // }\n\n // function LlmDescribeTodo(todo: Todo) -> string {\n // client GPT4o\n // prompt #\"Describe the following todo in detail: {{ todo }}\"#\n // }\n\n // function UseGetTodoFunction() -> string {\n // let todo = GetTodo();\n // LlmDescribeTodo(todo)\n // }\n\n // test UseGetTodoFunction() {\n // functions [UseGetTodoFunction]\n // args { }\n // }\n\n // function SearchWikipedia(query: string) -> string {\n // baml.fetch_value(baml.Request {\n // base_url: UrlEncode(\"https://en.wikipedia.org/wiki/Special:Search\", {search query})\n // })\n // }\n\n // function UrlEncode(base_url: string, query_params: map) -> string {\n // client \"openai/o3\"\n // prompt #\"\n // Encode the following base URL and query parameters into a valid URL:\n\n // URL: {{ base_url }}\n\n // Query Params: {{ query_params }}\n\n // Answer with just the final URL, no other text\n // \"#\n // }\n\n // function ConvertUserQuery(query: string) -> string {\n // client GPT4o\n // prompt #\"\n // Convert the following user query into a search query for Wikipedia: {{ query }}\n\n // Ideally it should be a single word that could map to an actual Wikipedia page\n // \"#\n // }\n\n // function LlmFormulateResponse(query: string, search_result: string?) -> string {\n // client GPT4o\n // prompt #\"\n // You are a helpful assistant. Answer the following user query: {{ query }}.\n\n // {% if search_result != \"\" %}\n // Use this Wikipedia search result as a reference for your answer: {{ search_result }}\n // {% endif %}\n // \"#\n // }\n\n // function NeedsWikipediaSearch(query: string) -> bool {\n // client GPT4o\n // prompt #\"Does the following user query need a Wikipedia search?: {{ query }}\"#\n // }\n\n // function ChatResponse(query: string) -> string {\n // let needs_search = NeedsWikipediaSearch(query);\n // let search_result = SearchWikipedia(ConvertUserQuery(query));\n // LlmFormulateResponse(query, search_result)\n // }\n\n // test TestName {\n // functions [ChatResponse]\n // args {\n // query #\"\n // Tell me everything about Rome\n // \"#\n // messages []\n // }\n // }\n\n // test UrlEncode {\n // functions [UrlEncode]\n // args {\n // base_url \"https://en.wikipedia.org/wiki/Special:Search\"\n // query_params {search \"Tell me everything about Rome\"\n // go \"Go\"}\n // }\n // }\n\n // test Convert {\n // functions [ConvertUserQuery]\n // args {\n // query #\"\n // Tell me everything about Rome\n // \"#\n // }\n // }\n", - "test-files/client-timeout-config.baml": "// Client with extremely short timeouts for testing\nclient TestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n connect_timeout_ms 1 // 1ms - will timeout on connect\n request_timeout_ms 10 // 10ms - very likely to timeout\n }\n }\n}\n\n// Function that will timeout\nfunction TestTimeoutError(input: string) -> string {\n client TestTimeoutClient\n prompt #\"\n This is a test that should timeout.\n Please write a very long response about: {{input}}\n \"#\n}\n\n// Client with only request timeout\nclient TestRequestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 50 // 50ms timeout\n }\n }\n}\n\nfunction TestRequestTimeout(input: string) -> string {\n client TestRequestTimeoutClient\n prompt #\"\n Generate a detailed 500 word essay about: {{input}}\n \"#\n}\n\n// Client with zero timeout (infinite)\nclient TestZeroTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 0 // 0 means infinite\n }\n }\n}\n\nfunction TestZeroTimeout(input: string) -> string {\n client TestZeroTimeoutClient\n prompt #\"\n Echo: {{input}}\n \"#\n}\n\n// Fallback client that includes timeout client\nclient TestTimeoutFallbackClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n }\n}\n\nfunction TestTimeoutFallback(input: string) -> string {\n client TestTimeoutFallbackClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Client for streaming timeout tests (Phase 4)\nclient TestStreamingTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n time_to_first_token_timeout_ms 1 // 1ms - extremely short, should always timeout\n idle_timeout_ms 1 // 1ms - extremely short, should always timeout\n }\n }\n}\n\nfunction TestStreamingTimeout(input: string) -> string {\n client TestStreamingTimeoutClient\n prompt #\"\n Stream a long response about: {{input}}\n \"#\n}\n\n// Compound client with request_timeout_ms override\n// This overrides the tight 10ms timeout of TestTimeoutClient with a more generous 5000ms\nclient CompoundTimeoutClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n request_timeout_ms 5000 // Override the tight timeout from primitives\n }\n }\n}\n\nfunction TestCompoundRequestTimeout(input: string) -> string {\n client CompoundTimeoutClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Compound client with total_timeout_ms\n// Even though the second client has infinite timeout, total_timeout_ms enforces an overall limit\nclient CompoundTotalTimeoutClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n total_timeout_ms 1000 // 1 second total timeout for the entire strategy\n }\n }\n}\n\nfunction TestCompoundTotalTimeout(input: string) -> string {\n client CompoundTotalTimeoutClient\n prompt #\"\n Generate a very long detailed essay about: {{input}}\n \"#\n}\n\n// Compound client with both request_timeout_ms and total_timeout_ms\nclient CompoundCombinedTimeoutsClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n request_timeout_ms 5000 // Generous per-request timeout\n total_timeout_ms 10000 // 10 second total timeout\n }\n }\n}\n\nfunction TestCompoundCombinedTimeouts(input: string) -> string {\n client CompoundCombinedTimeoutsClient\n prompt #\"\n Echo: {{input}}\n \"#\n}", + "test-files/client-timeout-config.baml": "// Client with extremely short timeouts for testing\nclient TestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n connect_timeout_ms 1 // 1ms - will timeout on connect\n request_timeout_ms 10 // 10ms - very likely to timeout\n }\n }\n}\n\n// Function that will timeout\nfunction TestTimeoutError(input: string) -> string {\n client TestTimeoutClient\n prompt #\"\n This is a test that should timeout.\n Please write a very long response about: {{input}}\n \"#\n}\n\n// Client with only request timeout\nclient TestRequestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 50 // 50ms timeout\n }\n }\n}\n\nfunction TestRequestTimeout(input: string) -> string {\n client TestRequestTimeoutClient\n prompt #\"\n Generate a detailed 500 word essay about: {{input}}\n \"#\n}\n\n// Client with zero timeout (infinite)\nclient TestZeroTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 0 // 0 means infinite\n }\n }\n}\n\nfunction TestZeroTimeout(input: string) -> string {\n client TestZeroTimeoutClient\n prompt #\"\n Echo: {{input}}\n \"#\n}\n\n// Fallback client that includes timeout client\nclient TestTimeoutFallbackClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n }\n}\n\nfunction TestTimeoutFallback(input: string) -> string {\n client TestTimeoutFallbackClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Client for streaming timeout tests (Phase 4)\nclient TestStreamingTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n time_to_first_token_timeout_ms 1 // 1ms - extremely short, should always timeout\n idle_timeout_ms 1 // 1ms - extremely short, should always timeout\n }\n }\n}\n\nfunction TestStreamingTimeout(input: string) -> string {\n client TestStreamingTimeoutClient\n prompt #\"\n Stream a long response about: {{input}}\n \"#\n}\n\n// Compound client with total_timeout_ms\n// Even though the second client has infinite timeout, total_timeout_ms enforces an overall limit\nclient CompoundTotalTimeoutClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n total_timeout_ms 1000 // 1 second total timeout for the entire strategy\n }\n }\n}\n\nfunction TestCompoundTotalTimeout(input: string) -> string {\n client CompoundTotalTimeoutClient\n prompt #\"\n Generate a very long detailed essay about: {{input}}\n \"#\n}", "test-files/comments/comments.baml": "// add some functions, classes, enums etc with comments all over.", "test-files/constraints/constraints.baml": "// These classes and functions test several properties of\n// constrains:\n//\n// - The ability for constrains on fields to pass or fail.\n// - The ability for constraints on bare args and return types to pass or fail.\n// - The ability of constraints to influence which variant of a union is chosen\n// by the parser, when the structure is not sufficient to decide.\n\n/// A Martian organism with an age.\n/// Such a nice type.\nclass Martian {\n /// The age of the Martian in Mars years.\n /// So many Mars years.\n age int @check(young_enough, {{ this < 30 }})\n}\n\nclass Earthling {\n age int @check(earth_aged, {{this < 200 and this > 0}}) @check(no_infants, {{this >1}})\n}\n\n\nclass FooAny {\n planetary_age Martian | Earthling\n certainty int @check(unreasonably_certain, {{this == 102931}})\n species string @check(trivial, {{this == \"Homo sapiens\"}}) @check(regex_good, {{this|regex_match(\"Homo\")}}) @check(regex_bad, {{this|regex_match(\"neanderthalensis\")}})\n}\n\n\nfunction PredictAge(name: string) -> FooAny {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling\n and capitalization). I'll give you a hint: If the name\n is \"Greg\", his age is 41.\n\n {{ctx.output_format}}\n \"#\n}\n\n\nfunction PredictAgeBare(inp: string @assert(big_enough, {{this|length > 1}})) -> int @check(too_big, {{this == 10102}}) {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ inp.name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling).\n\n {{ctx.output_format}}\n \"#\n}\n\nfunction ReturnFailingAssert(inp: int @assert(small_int, {{this < 10}})) -> int @assert(big_int, {{this > 100}}) {\n client GPT35\n prompt #\"\n Return the next integer after {{ inp }}.\n\n {{ctx.output_format}}\n \"#\n}\n\nclass TwoStoriesOneTitle {\n title string\n story_a string @assert(too_long_story, {{this|length > 1000000}} )\n story_b string @assert(too_long_story, {{this|length > 1000000}} )\n}\n\nfunction StreamFailingAssertion(theme: string, length: int) -> TwoStoriesOneTitle {\n client GPT35\n prompt #\"\n Tell me two different stories along the theme of {{ theme }} with the same title.\n Please make each about {{ length }} words long.\n {{ctx.output_format}}\n \"#\n}\n\nclass TwoStoriesOneTitleCheck {\n title string\n story_a string @check(too_long_story, {{this|length > 1000000}} )\n story_b string @check(too_long_story, {{this|length > 1000000}} )\n}\n\nfunction StreamFailingCheck(theme: string, length: int) -> TwoStoriesOneTitleCheck {\n client GPT35\n prompt #\"\n Tell me two different stories along the theme of {{ theme }} with the same title.\n Please make each about {{ length }} words long.\n {{ctx.output_format}}\n \"#\n}\n\nclass BlockConstraint {\n foo int\n bar string\n @@check(cross_field, {{ this.bar|length > this.foo }})\n}\n\nfunction MakeBlockConstraint() -> BlockConstraint {\n client GPT35\n prompt #\"\n Generate an output in the following schema with a short string and a large int.\n\n {{ ctx.output_format }}\n \"#\n}\n\nclass NestedBlockConstraint {\n nbc BlockConstraint\n}\n\nclass BlockConstraintForParam {\n bcfp int\n bcfp2 string\n @@assert(hi, {{ this.bcfp2|length < this.bcfp }})\n}\n\nclass NestedBlockConstraintForParam {\n nbcfp BlockConstraintForParam\n}\n\nfunction MakeNestedBlockConstraint() -> NestedBlockConstraint {\n client GPT35\n prompt #\"Generate an output where the inner foo is 1 and the inner bar is \"hello\".\n {{ ctx.output_format }}\n \"#\n}\n\nfunction UseBlockConstraint(inp: BlockConstraintForParam) -> int {\n client GPT35\n prompt #\"\n Generate 3\n {{ ctx.output_format }}\n \"#\n}\n\nfunction UseNestedBlockConstraint(inp: NestedBlockConstraintForParam) -> int {\n client GPT35\n prompt #\"\n Generate 3\n {{ ctx.output_format }}\n \"#\n}\n", "test-files/constraints/contact-info.baml": "class PhoneNumber {\n value string @assert(valid_phone_number, {{this|regex_match(\"\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}\")}})\n}\n\nclass EmailAddress {\n value string @assert(valid_email, {{this|regex_match(\"^[_]*([a-z0-9]+(\\.|_*)?)+@([a-z][a-z0-9-]+(\\.|-*\\.))+[a-z]{2,6}$\")}})\n}\n\nclass ContactInfo {\n primary PhoneNumber | EmailAddress\n secondary (PhoneNumber | EmailAddress)?\n}\n\nfunction ExtractContactInfo(document: string) -> ContactInfo {\n client GPT35\n prompt #\"\n Extract a primary contact info, and if possible a secondary contact\n info, from this document:\n\n {{ document }}\n\n {{ ctx.output_format }}\n \"#\n}\n", diff --git a/integ-tests/typescript-esm/baml_client/parser.ts b/integ-tests/typescript-esm/baml_client/parser.ts index 434d9a8883..e98669e81b 100644 --- a/integ-tests/typescript-esm/baml_client/parser.ts +++ b/integ-tests/typescript-esm/baml_client/parser.ts @@ -3480,52 +3480,6 @@ export class LlmResponseParser { } } - TestCompoundCombinedTimeouts( - llmResponse: string, - __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } - ): string { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return this.runtime.parseLlmResponse( - "TestCompoundCombinedTimeouts", - llmResponse, - false, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - env, - ) as string - } catch (error) { - throw toBamlError(error); - } - } - - TestCompoundRequestTimeout( - llmResponse: string, - __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } - ): string { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return this.runtime.parseLlmResponse( - "TestCompoundRequestTimeout", - llmResponse, - false, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - env, - ) as string - } catch (error) { - throw toBamlError(error); - } - } - TestCompoundTotalTimeout( llmResponse: string, __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } @@ -9627,52 +9581,6 @@ export class LlmStreamParser { } } - TestCompoundCombinedTimeouts( - llmResponse: string, - __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } - ): string { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return this.runtime.parseLlmResponse( - "TestCompoundCombinedTimeouts", - llmResponse, - true, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - env, - ) as string - } catch (error) { - throw toBamlError(error); - } - } - - TestCompoundRequestTimeout( - llmResponse: string, - __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } - ): string { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return this.runtime.parseLlmResponse( - "TestCompoundRequestTimeout", - llmResponse, - true, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - env, - ) as string - } catch (error) { - throw toBamlError(error); - } - } - TestCompoundTotalTimeout( llmResponse: string, __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } diff --git a/integ-tests/typescript-esm/baml_client/sync_client.ts b/integ-tests/typescript-esm/baml_client/sync_client.ts index 659d7e1724..4dab71016e 100644 --- a/integ-tests/typescript-esm/baml_client/sync_client.ts +++ b/integ-tests/typescript-esm/baml_client/sync_client.ts @@ -6396,90 +6396,6 @@ export class BamlSyncClient { } } - TestCompoundCombinedTimeouts( - input: string, - __baml_options__?: BamlCallOptions - ): string { - try { - const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } - const signal = options.signal; - - if (signal?.aborted) { - throw new BamlAbortError('Operation was aborted', signal.reason); - } - - // Check if onTick is provided and reject for sync operations - if (options.onTick) { - throw new Error("onTick is not supported for synchronous functions. Please use the async client instead."); - } - - const collector = options.collector ? (Array.isArray(options.collector) ? options.collector : [options.collector]) : []; - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - const raw = this.runtime.callFunctionSync( - "TestCompoundCombinedTimeouts", - { - "input": input - }, - this.ctxManager.cloneContext(), - options.tb?.__tb(), - options.clientRegistry, - collector, - options.tags || {}, - env, - signal, - options.watchers, - ) - return raw.parsed(false) as string - } catch (error: any) { - throw toBamlError(error); - } - } - - TestCompoundRequestTimeout( - input: string, - __baml_options__?: BamlCallOptions - ): string { - try { - const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } - const signal = options.signal; - - if (signal?.aborted) { - throw new BamlAbortError('Operation was aborted', signal.reason); - } - - // Check if onTick is provided and reject for sync operations - if (options.onTick) { - throw new Error("onTick is not supported for synchronous functions. Please use the async client instead."); - } - - const collector = options.collector ? (Array.isArray(options.collector) ? options.collector : [options.collector]) : []; - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - const raw = this.runtime.callFunctionSync( - "TestCompoundRequestTimeout", - { - "input": input - }, - this.ctxManager.cloneContext(), - options.tb?.__tb(), - options.clientRegistry, - collector, - options.tags || {}, - env, - signal, - options.watchers, - ) - return raw.parsed(false) as string - } catch (error: any) { - throw toBamlError(error); - } - } - TestCompoundTotalTimeout( input: string, __baml_options__?: BamlCallOptions diff --git a/integ-tests/typescript-esm/baml_client/sync_request.ts b/integ-tests/typescript-esm/baml_client/sync_request.ts index d207dd5e5c..0545a5fd91 100644 --- a/integ-tests/typescript-esm/baml_client/sync_request.ts +++ b/integ-tests/typescript-esm/baml_client/sync_request.ts @@ -3787,56 +3787,6 @@ export class HttpRequest { } } - TestCompoundCombinedTimeouts( - input: string, - __baml_options__?: BamlCallOptions - ): HTTPRequest { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return this.runtime.buildRequestSync( - "TestCompoundCombinedTimeouts", - { - "input": input - }, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - false, - env, - ) - } catch (error) { - throw toBamlError(error); - } - } - - TestCompoundRequestTimeout( - input: string, - __baml_options__?: BamlCallOptions - ): HTTPRequest { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return this.runtime.buildRequestSync( - "TestCompoundRequestTimeout", - { - "input": input - }, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - false, - env, - ) - } catch (error) { - throw toBamlError(error); - } - } - TestCompoundTotalTimeout( input: string, __baml_options__?: BamlCallOptions @@ -10468,56 +10418,6 @@ export class HttpStreamRequest { } } - TestCompoundCombinedTimeouts( - input: string, - __baml_options__?: BamlCallOptions - ): HTTPRequest { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return this.runtime.buildRequestSync( - "TestCompoundCombinedTimeouts", - { - "input": input - }, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - true, - env, - ) - } catch (error) { - throw toBamlError(error); - } - } - - TestCompoundRequestTimeout( - input: string, - __baml_options__?: BamlCallOptions - ): HTTPRequest { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return this.runtime.buildRequestSync( - "TestCompoundRequestTimeout", - { - "input": input - }, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - true, - env, - ) - } catch (error) { - throw toBamlError(error); - } - } - TestCompoundTotalTimeout( input: string, __baml_options__?: BamlCallOptions diff --git a/integ-tests/typescript/baml_client/async_client.ts b/integ-tests/typescript/baml_client/async_client.ts index c39eb762ed..2b08d37047 100644 --- a/integ-tests/typescript/baml_client/async_client.ts +++ b/integ-tests/typescript/baml_client/async_client.ts @@ -7296,102 +7296,6 @@ export type RecursivePartialNull = MovedRecursivePartialNull } } - async TestCompoundCombinedTimeouts( - input: string, - __baml_options__?: BamlCallOptions - ): Promise { - try { - const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } - const signal = options.signal; - - if (signal?.aborted) { - throw new BamlAbortError('Operation was aborted', signal.reason); - } - - // Check if onTick is provided - route through streaming if so - if (options.onTick) { - const stream = this.stream.TestCompoundCombinedTimeouts( - input, - __baml_options__ - ); - - return await stream.getFinalResponse(); - } - - const collector = options.collector ? (Array.isArray(options.collector) ? options.collector : - [options.collector]) : []; - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - const raw = await this.runtime.callFunction( - "TestCompoundCombinedTimeouts", - { - "input": input - }, - this.ctxManager.cloneContext(), - options.tb?.__tb(), - options.clientRegistry, - collector, - options.tags || {}, - env, - signal, - options.watchers, - ) - return raw.parsed(false) as string - } catch (error) { - throw toBamlError(error); - } - } - - async TestCompoundRequestTimeout( - input: string, - __baml_options__?: BamlCallOptions - ): Promise { - try { - const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } - const signal = options.signal; - - if (signal?.aborted) { - throw new BamlAbortError('Operation was aborted', signal.reason); - } - - // Check if onTick is provided - route through streaming if so - if (options.onTick) { - const stream = this.stream.TestCompoundRequestTimeout( - input, - __baml_options__ - ); - - return await stream.getFinalResponse(); - } - - const collector = options.collector ? (Array.isArray(options.collector) ? options.collector : - [options.collector]) : []; - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - const raw = await this.runtime.callFunction( - "TestCompoundRequestTimeout", - { - "input": input - }, - this.ctxManager.cloneContext(), - options.tb?.__tb(), - options.clientRegistry, - collector, - options.tags || {}, - env, - signal, - options.watchers, - ) - return raw.parsed(false) as string - } catch (error) { - throw toBamlError(error); - } - } - async TestCompoundTotalTimeout( input: string, __baml_options__?: BamlCallOptions @@ -22826,138 +22730,6 @@ export type RecursivePartialNull = MovedRecursivePartialNull } } - TestCompoundCombinedTimeouts( - input: string, - __baml_options__?: BamlCallOptions - ): BamlStream - { - try { - const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } - const signal = options.signal; - - if (signal?.aborted) { - throw new BamlAbortError('Operation was aborted', signal.reason); - } - - let collector = options.collector ? (Array.isArray(options.collector) ? options.collector : - [options.collector]) : []; - - let onTickWrapper: (() => void) | undefined; - - // Create collector and wrap onTick if provided - if (options.onTick) { - const tickCollector = new Collector("on-tick-collector"); - collector = [...collector, tickCollector]; - - onTickWrapper = () => { - const log = tickCollector.last; - if (log) { - try { - options.onTick!("Unknown", log); - } catch (error) { - console.error("Error in onTick callback for TestCompoundCombinedTimeouts", error); - } - } - }; - } - - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - const raw = this.runtime.streamFunction( - "TestCompoundCombinedTimeouts", - { - "input": input - }, - undefined, - this.ctxManager.cloneContext(), - options.tb?.__tb(), - options.clientRegistry, - collector, - options.tags || {}, - env, - signal, - onTickWrapper, - ) - return new BamlStream( - raw, - (a): string => a, - (a): string => a, - this.ctxManager.cloneContext(), - options.signal, - ) - } catch (error) { - throw toBamlError(error); - } - } - - TestCompoundRequestTimeout( - input: string, - __baml_options__?: BamlCallOptions - ): BamlStream - { - try { - const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } - const signal = options.signal; - - if (signal?.aborted) { - throw new BamlAbortError('Operation was aborted', signal.reason); - } - - let collector = options.collector ? (Array.isArray(options.collector) ? options.collector : - [options.collector]) : []; - - let onTickWrapper: (() => void) | undefined; - - // Create collector and wrap onTick if provided - if (options.onTick) { - const tickCollector = new Collector("on-tick-collector"); - collector = [...collector, tickCollector]; - - onTickWrapper = () => { - const log = tickCollector.last; - if (log) { - try { - options.onTick!("Unknown", log); - } catch (error) { - console.error("Error in onTick callback for TestCompoundRequestTimeout", error); - } - } - }; - } - - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - const raw = this.runtime.streamFunction( - "TestCompoundRequestTimeout", - { - "input": input - }, - undefined, - this.ctxManager.cloneContext(), - options.tb?.__tb(), - options.clientRegistry, - collector, - options.tags || {}, - env, - signal, - onTickWrapper, - ) - return new BamlStream( - raw, - (a): string => a, - (a): string => a, - this.ctxManager.cloneContext(), - options.signal, - ) - } catch (error) { - throw toBamlError(error); - } - } - TestCompoundTotalTimeout( input: string, __baml_options__?: BamlCallOptions diff --git a/integ-tests/typescript/baml_client/async_request.ts b/integ-tests/typescript/baml_client/async_request.ts index a72341b250..d6dd35ebda 100644 --- a/integ-tests/typescript/baml_client/async_request.ts +++ b/integ-tests/typescript/baml_client/async_request.ts @@ -3791,56 +3791,6 @@ env?: Record } } - async TestCompoundCombinedTimeouts( - input: string, - __baml_options__?: BamlCallOptions - ): Promise { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return await this.runtime.buildRequest( - "TestCompoundCombinedTimeouts", - { - "input": input - }, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - false, - env - ) - } catch (error) { - throw toBamlError(error); - } - } - - async TestCompoundRequestTimeout( - input: string, - __baml_options__?: BamlCallOptions - ): Promise { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return await this.runtime.buildRequest( - "TestCompoundRequestTimeout", - { - "input": input - }, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - false, - env - ) - } catch (error) { - throw toBamlError(error); - } - } - async TestCompoundTotalTimeout( input: string, __baml_options__?: BamlCallOptions @@ -10472,56 +10422,6 @@ env?: Record } } - async TestCompoundCombinedTimeouts( - input: string, - __baml_options__?: BamlCallOptions - ): Promise { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return await this.runtime.buildRequest( - "TestCompoundCombinedTimeouts", - { - "input": input - }, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - true, - env - ) - } catch (error) { - throw toBamlError(error); - } - } - - async TestCompoundRequestTimeout( - input: string, - __baml_options__?: BamlCallOptions - ): Promise { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return await this.runtime.buildRequest( - "TestCompoundRequestTimeout", - { - "input": input - }, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - true, - env - ) - } catch (error) { - throw toBamlError(error); - } - } - async TestCompoundTotalTimeout( input: string, __baml_options__?: BamlCallOptions diff --git a/integ-tests/typescript/baml_client/inlinedbaml.ts b/integ-tests/typescript/baml_client/inlinedbaml.ts index ba04fc3b0d..8743e11dc1 100644 --- a/integ-tests/typescript/baml_client/inlinedbaml.ts +++ b/integ-tests/typescript/baml_client/inlinedbaml.ts @@ -37,7 +37,7 @@ const fileMap = { "test-files/aliases/classes.baml": "class TestClassAlias {\n key string @alias(\"key-dash\") @description(#\"\n This is a description for key\n af asdf\n \"#)\n key2 string @alias(\"key21\")\n key3 string @alias(\"key with space\")\n key4 string //unaliased\n key5 string @alias(\"key.with.punctuation/123\")\n}\n\nfunction FnTestClassAlias(input: string) -> TestClassAlias {\n client GPT35\n prompt #\"\n {{ctx.output_format}}\n \"#\n}\n\ntest FnTestClassAlias {\n functions [FnTestClassAlias]\n args {\n input \"example input\"\n }\n}\n", "test-files/aliases/enums.baml": "enum TestEnum {\n A @alias(\"k1\") @description(#\"\n User is angry\n \"#)\n B @alias(\"k22\") @description(#\"\n User is happy\n \"#)\n // tests whether k1 doesnt incorrectly get matched with k11\n C @alias(\"k11\") @description(#\"\n User is sad\n \"#)\n D @alias(\"k44\") @description(#\"\n User is confused\n \"#)\n E @description(#\"\n User is excited\n \"#)\n F @alias(\"k5\") // only alias\n \n G @alias(\"k6\") @description(#\"\n User is bored\n With a long description\n \"#)\n \n @@alias(\"Category\")\n}\n\nfunction FnTestAliasedEnumOutput(input: string) -> TestEnum {\n client GPT35\n prompt #\"\n Classify the user input into the following category\n \n {{ ctx.output_format }}\n\n {{ _.role('user') }}\n {{input}}\n\n {{ _.role('assistant') }}\n Category ID:\n \"#\n}\n\ntest FnTestAliasedEnumOutput {\n functions [FnTestAliasedEnumOutput]\n args {\n input \"mehhhhh\"\n }\n}", "test-files/builtin/fetch.baml": " // class Todo {\n // id int\n // todo string\n // completed bool\n // userId int\n // }\n\n // function GetTodo() -> Todo {\n // Todo {\n // id: 1,\n // }\n // }\n\n // function LlmDescribeTodo(todo: Todo) -> string {\n // client GPT4o\n // prompt #\"Describe the following todo in detail: {{ todo }}\"#\n // }\n\n // function UseGetTodoFunction() -> string {\n // let todo = GetTodo();\n // LlmDescribeTodo(todo)\n // }\n\n // test UseGetTodoFunction() {\n // functions [UseGetTodoFunction]\n // args { }\n // }\n\n // function SearchWikipedia(query: string) -> string {\n // baml.fetch_value(baml.Request {\n // base_url: UrlEncode(\"https://en.wikipedia.org/wiki/Special:Search\", {search query})\n // })\n // }\n\n // function UrlEncode(base_url: string, query_params: map) -> string {\n // client \"openai/o3\"\n // prompt #\"\n // Encode the following base URL and query parameters into a valid URL:\n\n // URL: {{ base_url }}\n\n // Query Params: {{ query_params }}\n\n // Answer with just the final URL, no other text\n // \"#\n // }\n\n // function ConvertUserQuery(query: string) -> string {\n // client GPT4o\n // prompt #\"\n // Convert the following user query into a search query for Wikipedia: {{ query }}\n\n // Ideally it should be a single word that could map to an actual Wikipedia page\n // \"#\n // }\n\n // function LlmFormulateResponse(query: string, search_result: string?) -> string {\n // client GPT4o\n // prompt #\"\n // You are a helpful assistant. Answer the following user query: {{ query }}.\n\n // {% if search_result != \"\" %}\n // Use this Wikipedia search result as a reference for your answer: {{ search_result }}\n // {% endif %}\n // \"#\n // }\n\n // function NeedsWikipediaSearch(query: string) -> bool {\n // client GPT4o\n // prompt #\"Does the following user query need a Wikipedia search?: {{ query }}\"#\n // }\n\n // function ChatResponse(query: string) -> string {\n // let needs_search = NeedsWikipediaSearch(query);\n // let search_result = SearchWikipedia(ConvertUserQuery(query));\n // LlmFormulateResponse(query, search_result)\n // }\n\n // test TestName {\n // functions [ChatResponse]\n // args {\n // query #\"\n // Tell me everything about Rome\n // \"#\n // messages []\n // }\n // }\n\n // test UrlEncode {\n // functions [UrlEncode]\n // args {\n // base_url \"https://en.wikipedia.org/wiki/Special:Search\"\n // query_params {search \"Tell me everything about Rome\"\n // go \"Go\"}\n // }\n // }\n\n // test Convert {\n // functions [ConvertUserQuery]\n // args {\n // query #\"\n // Tell me everything about Rome\n // \"#\n // }\n // }\n", - "test-files/client-timeout-config.baml": "// Client with extremely short timeouts for testing\nclient TestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n connect_timeout_ms 1 // 1ms - will timeout on connect\n request_timeout_ms 10 // 10ms - very likely to timeout\n }\n }\n}\n\n// Function that will timeout\nfunction TestTimeoutError(input: string) -> string {\n client TestTimeoutClient\n prompt #\"\n This is a test that should timeout.\n Please write a very long response about: {{input}}\n \"#\n}\n\n// Client with only request timeout\nclient TestRequestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 50 // 50ms timeout\n }\n }\n}\n\nfunction TestRequestTimeout(input: string) -> string {\n client TestRequestTimeoutClient\n prompt #\"\n Generate a detailed 500 word essay about: {{input}}\n \"#\n}\n\n// Client with zero timeout (infinite)\nclient TestZeroTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 0 // 0 means infinite\n }\n }\n}\n\nfunction TestZeroTimeout(input: string) -> string {\n client TestZeroTimeoutClient\n prompt #\"\n Echo: {{input}}\n \"#\n}\n\n// Fallback client that includes timeout client\nclient TestTimeoutFallbackClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n }\n}\n\nfunction TestTimeoutFallback(input: string) -> string {\n client TestTimeoutFallbackClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Client for streaming timeout tests (Phase 4)\nclient TestStreamingTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n time_to_first_token_timeout_ms 1 // 1ms - extremely short, should always timeout\n idle_timeout_ms 1 // 1ms - extremely short, should always timeout\n }\n }\n}\n\nfunction TestStreamingTimeout(input: string) -> string {\n client TestStreamingTimeoutClient\n prompt #\"\n Stream a long response about: {{input}}\n \"#\n}\n\n// Compound client with request_timeout_ms override\n// This overrides the tight 10ms timeout of TestTimeoutClient with a more generous 5000ms\nclient CompoundTimeoutClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n request_timeout_ms 5000 // Override the tight timeout from primitives\n }\n }\n}\n\nfunction TestCompoundRequestTimeout(input: string) -> string {\n client CompoundTimeoutClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Compound client with total_timeout_ms\n// Even though the second client has infinite timeout, total_timeout_ms enforces an overall limit\nclient CompoundTotalTimeoutClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n total_timeout_ms 1000 // 1 second total timeout for the entire strategy\n }\n }\n}\n\nfunction TestCompoundTotalTimeout(input: string) -> string {\n client CompoundTotalTimeoutClient\n prompt #\"\n Generate a very long detailed essay about: {{input}}\n \"#\n}\n\n// Compound client with both request_timeout_ms and total_timeout_ms\nclient CompoundCombinedTimeoutsClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n request_timeout_ms 5000 // Generous per-request timeout\n total_timeout_ms 10000 // 10 second total timeout\n }\n }\n}\n\nfunction TestCompoundCombinedTimeouts(input: string) -> string {\n client CompoundCombinedTimeoutsClient\n prompt #\"\n Echo: {{input}}\n \"#\n}", + "test-files/client-timeout-config.baml": "// Client with extremely short timeouts for testing\nclient TestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n connect_timeout_ms 1 // 1ms - will timeout on connect\n request_timeout_ms 10 // 10ms - very likely to timeout\n }\n }\n}\n\n// Function that will timeout\nfunction TestTimeoutError(input: string) -> string {\n client TestTimeoutClient\n prompt #\"\n This is a test that should timeout.\n Please write a very long response about: {{input}}\n \"#\n}\n\n// Client with only request timeout\nclient TestRequestTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 50 // 50ms timeout\n }\n }\n}\n\nfunction TestRequestTimeout(input: string) -> string {\n client TestRequestTimeoutClient\n prompt #\"\n Generate a detailed 500 word essay about: {{input}}\n \"#\n}\n\n// Client with zero timeout (infinite)\nclient TestZeroTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n request_timeout_ms 0 // 0 means infinite\n }\n }\n}\n\nfunction TestZeroTimeout(input: string) -> string {\n client TestZeroTimeoutClient\n prompt #\"\n Echo: {{input}}\n \"#\n}\n\n// Fallback client that includes timeout client\nclient TestTimeoutFallbackClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n }\n}\n\nfunction TestTimeoutFallback(input: string) -> string {\n client TestTimeoutFallbackClient\n prompt #\"\n Simple echo: {{input}}\n \"#\n}\n\n// Client for streaming timeout tests (Phase 4)\nclient TestStreamingTimeoutClient {\n provider openai\n options {\n model \"gpt-4o-mini\"\n api_key env.OPENAI_API_KEY\n http {\n time_to_first_token_timeout_ms 1 // 1ms - extremely short, should always timeout\n idle_timeout_ms 1 // 1ms - extremely short, should always timeout\n }\n }\n}\n\nfunction TestStreamingTimeout(input: string) -> string {\n client TestStreamingTimeoutClient\n prompt #\"\n Stream a long response about: {{input}}\n \"#\n}\n\n// Compound client with total_timeout_ms\n// Even though the second client has infinite timeout, total_timeout_ms enforces an overall limit\nclient CompoundTotalTimeoutClient {\n provider fallback\n options {\n strategy [TestTimeoutClient, TestZeroTimeoutClient]\n http {\n total_timeout_ms 1000 // 1 second total timeout for the entire strategy\n }\n }\n}\n\nfunction TestCompoundTotalTimeout(input: string) -> string {\n client CompoundTotalTimeoutClient\n prompt #\"\n Generate a very long detailed essay about: {{input}}\n \"#\n}", "test-files/comments/comments.baml": "// add some functions, classes, enums etc with comments all over.", "test-files/constraints/constraints.baml": "// These classes and functions test several properties of\n// constrains:\n//\n// - The ability for constrains on fields to pass or fail.\n// - The ability for constraints on bare args and return types to pass or fail.\n// - The ability of constraints to influence which variant of a union is chosen\n// by the parser, when the structure is not sufficient to decide.\n\n/// A Martian organism with an age.\n/// Such a nice type.\nclass Martian {\n /// The age of the Martian in Mars years.\n /// So many Mars years.\n age int @check(young_enough, {{ this < 30 }})\n}\n\nclass Earthling {\n age int @check(earth_aged, {{this < 200 and this > 0}}) @check(no_infants, {{this >1}})\n}\n\n\nclass FooAny {\n planetary_age Martian | Earthling\n certainty int @check(unreasonably_certain, {{this == 102931}})\n species string @check(trivial, {{this == \"Homo sapiens\"}}) @check(regex_good, {{this|regex_match(\"Homo\")}}) @check(regex_bad, {{this|regex_match(\"neanderthalensis\")}})\n}\n\n\nfunction PredictAge(name: string) -> FooAny {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling\n and capitalization). I'll give you a hint: If the name\n is \"Greg\", his age is 41.\n\n {{ctx.output_format}}\n \"#\n}\n\n\nfunction PredictAgeBare(inp: string @assert(big_enough, {{this|length > 1}})) -> int @check(too_big, {{this == 10102}}) {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ inp.name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling).\n\n {{ctx.output_format}}\n \"#\n}\n\nfunction ReturnFailingAssert(inp: int @assert(small_int, {{this < 10}})) -> int @assert(big_int, {{this > 100}}) {\n client GPT35\n prompt #\"\n Return the next integer after {{ inp }}.\n\n {{ctx.output_format}}\n \"#\n}\n\nclass TwoStoriesOneTitle {\n title string\n story_a string @assert(too_long_story, {{this|length > 1000000}} )\n story_b string @assert(too_long_story, {{this|length > 1000000}} )\n}\n\nfunction StreamFailingAssertion(theme: string, length: int) -> TwoStoriesOneTitle {\n client GPT35\n prompt #\"\n Tell me two different stories along the theme of {{ theme }} with the same title.\n Please make each about {{ length }} words long.\n {{ctx.output_format}}\n \"#\n}\n\nclass TwoStoriesOneTitleCheck {\n title string\n story_a string @check(too_long_story, {{this|length > 1000000}} )\n story_b string @check(too_long_story, {{this|length > 1000000}} )\n}\n\nfunction StreamFailingCheck(theme: string, length: int) -> TwoStoriesOneTitleCheck {\n client GPT35\n prompt #\"\n Tell me two different stories along the theme of {{ theme }} with the same title.\n Please make each about {{ length }} words long.\n {{ctx.output_format}}\n \"#\n}\n\nclass BlockConstraint {\n foo int\n bar string\n @@check(cross_field, {{ this.bar|length > this.foo }})\n}\n\nfunction MakeBlockConstraint() -> BlockConstraint {\n client GPT35\n prompt #\"\n Generate an output in the following schema with a short string and a large int.\n\n {{ ctx.output_format }}\n \"#\n}\n\nclass NestedBlockConstraint {\n nbc BlockConstraint\n}\n\nclass BlockConstraintForParam {\n bcfp int\n bcfp2 string\n @@assert(hi, {{ this.bcfp2|length < this.bcfp }})\n}\n\nclass NestedBlockConstraintForParam {\n nbcfp BlockConstraintForParam\n}\n\nfunction MakeNestedBlockConstraint() -> NestedBlockConstraint {\n client GPT35\n prompt #\"Generate an output where the inner foo is 1 and the inner bar is \"hello\".\n {{ ctx.output_format }}\n \"#\n}\n\nfunction UseBlockConstraint(inp: BlockConstraintForParam) -> int {\n client GPT35\n prompt #\"\n Generate 3\n {{ ctx.output_format }}\n \"#\n}\n\nfunction UseNestedBlockConstraint(inp: NestedBlockConstraintForParam) -> int {\n client GPT35\n prompt #\"\n Generate 3\n {{ ctx.output_format }}\n \"#\n}\n", "test-files/constraints/contact-info.baml": "class PhoneNumber {\n value string @assert(valid_phone_number, {{this|regex_match(\"\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}\")}})\n}\n\nclass EmailAddress {\n value string @assert(valid_email, {{this|regex_match(\"^[_]*([a-z0-9]+(\\.|_*)?)+@([a-z][a-z0-9-]+(\\.|-*\\.))+[a-z]{2,6}$\")}})\n}\n\nclass ContactInfo {\n primary PhoneNumber | EmailAddress\n secondary (PhoneNumber | EmailAddress)?\n}\n\nfunction ExtractContactInfo(document: string) -> ContactInfo {\n client GPT35\n prompt #\"\n Extract a primary contact info, and if possible a secondary contact\n info, from this document:\n\n {{ document }}\n\n {{ ctx.output_format }}\n \"#\n}\n", diff --git a/integ-tests/typescript/baml_client/parser.ts b/integ-tests/typescript/baml_client/parser.ts index 9be638e168..7c08e9088f 100644 --- a/integ-tests/typescript/baml_client/parser.ts +++ b/integ-tests/typescript/baml_client/parser.ts @@ -3480,52 +3480,6 @@ export class LlmResponseParser { } } - TestCompoundCombinedTimeouts( - llmResponse: string, - __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } - ): string { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return this.runtime.parseLlmResponse( - "TestCompoundCombinedTimeouts", - llmResponse, - false, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - env, - ) as string - } catch (error) { - throw toBamlError(error); - } - } - - TestCompoundRequestTimeout( - llmResponse: string, - __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } - ): string { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return this.runtime.parseLlmResponse( - "TestCompoundRequestTimeout", - llmResponse, - false, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - env, - ) as string - } catch (error) { - throw toBamlError(error); - } - } - TestCompoundTotalTimeout( llmResponse: string, __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } @@ -9627,52 +9581,6 @@ export class LlmStreamParser { } } - TestCompoundCombinedTimeouts( - llmResponse: string, - __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } - ): string { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return this.runtime.parseLlmResponse( - "TestCompoundCombinedTimeouts", - llmResponse, - true, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - env, - ) as string - } catch (error) { - throw toBamlError(error); - } - } - - TestCompoundRequestTimeout( - llmResponse: string, - __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } - ): string { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return this.runtime.parseLlmResponse( - "TestCompoundRequestTimeout", - llmResponse, - true, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - env, - ) as string - } catch (error) { - throw toBamlError(error); - } - } - TestCompoundTotalTimeout( llmResponse: string, __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry, env?: Record } diff --git a/integ-tests/typescript/baml_client/sync_client.ts b/integ-tests/typescript/baml_client/sync_client.ts index 8b06d35739..b7225d762e 100644 --- a/integ-tests/typescript/baml_client/sync_client.ts +++ b/integ-tests/typescript/baml_client/sync_client.ts @@ -6396,90 +6396,6 @@ export class BamlSyncClient { } } - TestCompoundCombinedTimeouts( - input: string, - __baml_options__?: BamlCallOptions - ): string { - try { - const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } - const signal = options.signal; - - if (signal?.aborted) { - throw new BamlAbortError('Operation was aborted', signal.reason); - } - - // Check if onTick is provided and reject for sync operations - if (options.onTick) { - throw new Error("onTick is not supported for synchronous functions. Please use the async client instead."); - } - - const collector = options.collector ? (Array.isArray(options.collector) ? options.collector : [options.collector]) : []; - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - const raw = this.runtime.callFunctionSync( - "TestCompoundCombinedTimeouts", - { - "input": input - }, - this.ctxManager.cloneContext(), - options.tb?.__tb(), - options.clientRegistry, - collector, - options.tags || {}, - env, - signal, - options.watchers, - ) - return raw.parsed(false) as string - } catch (error: any) { - throw toBamlError(error); - } - } - - TestCompoundRequestTimeout( - input: string, - __baml_options__?: BamlCallOptions - ): string { - try { - const options = { ...this.bamlOptions, ...(__baml_options__ || {}) } - const signal = options.signal; - - if (signal?.aborted) { - throw new BamlAbortError('Operation was aborted', signal.reason); - } - - // Check if onTick is provided and reject for sync operations - if (options.onTick) { - throw new Error("onTick is not supported for synchronous functions. Please use the async client instead."); - } - - const collector = options.collector ? (Array.isArray(options.collector) ? options.collector : [options.collector]) : []; - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - const raw = this.runtime.callFunctionSync( - "TestCompoundRequestTimeout", - { - "input": input - }, - this.ctxManager.cloneContext(), - options.tb?.__tb(), - options.clientRegistry, - collector, - options.tags || {}, - env, - signal, - options.watchers, - ) - return raw.parsed(false) as string - } catch (error: any) { - throw toBamlError(error); - } - } - TestCompoundTotalTimeout( input: string, __baml_options__?: BamlCallOptions diff --git a/integ-tests/typescript/baml_client/sync_request.ts b/integ-tests/typescript/baml_client/sync_request.ts index a04d1c204f..46ea3ddae2 100644 --- a/integ-tests/typescript/baml_client/sync_request.ts +++ b/integ-tests/typescript/baml_client/sync_request.ts @@ -3787,56 +3787,6 @@ export class HttpRequest { } } - TestCompoundCombinedTimeouts( - input: string, - __baml_options__?: BamlCallOptions - ): HTTPRequest { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return this.runtime.buildRequestSync( - "TestCompoundCombinedTimeouts", - { - "input": input - }, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - false, - env, - ) - } catch (error) { - throw toBamlError(error); - } - } - - TestCompoundRequestTimeout( - input: string, - __baml_options__?: BamlCallOptions - ): HTTPRequest { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return this.runtime.buildRequestSync( - "TestCompoundRequestTimeout", - { - "input": input - }, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - false, - env, - ) - } catch (error) { - throw toBamlError(error); - } - } - TestCompoundTotalTimeout( input: string, __baml_options__?: BamlCallOptions @@ -10468,56 +10418,6 @@ export class HttpStreamRequest { } } - TestCompoundCombinedTimeouts( - input: string, - __baml_options__?: BamlCallOptions - ): HTTPRequest { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return this.runtime.buildRequestSync( - "TestCompoundCombinedTimeouts", - { - "input": input - }, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - true, - env, - ) - } catch (error) { - throw toBamlError(error); - } - } - - TestCompoundRequestTimeout( - input: string, - __baml_options__?: BamlCallOptions - ): HTTPRequest { - try { - const rawEnv = __baml_options__?.env ? { ...process.env, ...__baml_options__.env } : { ...process.env }; - const env: Record = Object.fromEntries( - Object.entries(rawEnv).filter(([_, value]) => value !== undefined) as [string, string][] - ); - return this.runtime.buildRequestSync( - "TestCompoundRequestTimeout", - { - "input": input - }, - this.ctxManager.cloneContext(), - __baml_options__?.tb?.__tb(), - __baml_options__?.clientRegistry, - true, - env, - ) - } catch (error) { - throw toBamlError(error); - } - } - TestCompoundTotalTimeout( input: string, __baml_options__?: BamlCallOptions