From ca41486d7643dc7a15d3343c031f8b5dca5da31b Mon Sep 17 00:00:00 2001 From: Sam Lijin Date: Wed, 11 Dec 2024 18:44:23 -0800 Subject: [PATCH 1/8] tracing impl for context --- engine/sandbox/Cargo.toml | 16 +++ engine/sandbox/src/btrace.rs | 173 +++++++++++++++++++++++++++++++ engine/sandbox/src/main.rs | 82 +++++++++++++++ engine/sandbox/src/web_server.rs | 40 +++++++ 4 files changed, 311 insertions(+) create mode 100644 engine/sandbox/Cargo.toml create mode 100644 engine/sandbox/src/btrace.rs create mode 100644 engine/sandbox/src/main.rs create mode 100644 engine/sandbox/src/web_server.rs diff --git a/engine/sandbox/Cargo.toml b/engine/sandbox/Cargo.toml new file mode 100644 index 000000000..aa334c586 --- /dev/null +++ b/engine/sandbox/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "sandbox" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow.workspace = true +axum = "0.7" +log.workspace = true +tokio = { version = "1.36", features = ["full"] } +uuid.workspace = true +tracing = { version = "0.1.40", features = ["valuable"] } +tracing-core = { version = "0.1.33" } +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +web-time.workspace = true +serde_json.workspace = true diff --git a/engine/sandbox/src/btrace.rs b/engine/sandbox/src/btrace.rs new file mode 100644 index 000000000..90386d2eb --- /dev/null +++ b/engine/sandbox/src/btrace.rs @@ -0,0 +1,173 @@ +#[derive(Clone)] +pub struct TraceContext { + /// The scope used for all spans/logs within this context. + pub scope: InstrumentationScope, + /// The channel used to send trace events to the trace agent. + pub tx: tokio::sync::mpsc::UnboundedSender, +} + +tokio::task_local! { + pub static BAML_TRACE_CTX: TraceContext; +} + +struct TraceSpanBuilder { + span_id: uuid::Uuid, + start_time: std::time::Instant, + meta: TraceMetadata, + fields: serde_json::Map, +} + +#[derive(Debug, Clone)] +pub enum InstrumentationScope { + Root, + Child { parent: uuid::Uuid }, +} + +#[derive(Debug)] +pub struct TraceMetadata { + /// human-readable callsite identifier, e.g. "ExtractResume" or "openai/gpt-4o/chat" + name: String, + /// verbosity level + verbosity: tracing_core::Level, +} + +#[derive(Debug)] +pub struct TraceSpan { + scope: InstrumentationScope, + span_id: uuid::Uuid, + start_time: web_time::Instant, + duration: web_time::Duration, + meta: TraceMetadata, + fields: serde_json::Map, +} + +pub struct TraceLog { + scope: InstrumentationScope, + start_time: web_time::Instant, + msg: String, + meta: TraceMetadata, + fields: serde_json::Map, +} + +pub enum TraceEvent { + Span(TraceSpan), + Log(TraceLog), +} + +impl TraceSpanBuilder { + pub fn start_span(name: String) -> Self { + Self { + span_id: uuid::Uuid::now_v7(), + start_time: web_time::Instant::now(), + meta: TraceMetadata { + name, + verbosity: tracing_core::Level::INFO, + }, + fields: serde_json::Map::new(), + } + } + + pub fn end(self) -> TraceSpan { + let duration = self.start_time.elapsed(); + let ctx = BAML_TRACE_CTX.get(); + let span = TraceSpan { + scope: ctx.scope.clone(), + span_id: self.span_id, + start_time: self.start_time, + duration, + meta: self.meta, + fields: self.fields, + }; + span + } +} + +fn log(callsite: String, msg: String, fields: serde_json::Map) { + let Ok(ctx) = BAML_TRACE_CTX.try_with(|ctx| ctx.clone()) else { + return; + }; + let _ = ctx.tx.send(TraceEvent::Log(TraceLog { + scope: ctx.scope.clone(), + start_time: web_time::Instant::now(), + msg, + meta: TraceMetadata { + name: callsite, + verbosity: tracing_core::Level::INFO, + }, + fields, + })); +} + +pub fn baml_trace_sync_scope(name: impl Into, f: F) -> R +where + F: FnOnce() -> R, +{ + let verbosity = tracing_core::Level::INFO; + let curr_ctx = BAML_TRACE_CTX.try_with(|ctx| ctx.clone()); + + match curr_ctx { + Ok(ctx) => { + let name = name.into(); + let span = TraceSpanBuilder::start_span(name.clone()); + println!( + "entering span: {:?} -> {:?} {}", + ctx.scope, span.span_id, name + ); + let new_ctx = TraceContext { + scope: InstrumentationScope::Child { + parent: span.span_id, + }, + tx: ctx.tx.clone(), + }; + let retval = BAML_TRACE_CTX.sync_scope(new_ctx, f); + let span = span.end(); + println!( + "exiting span: {:?} <- {:?} {}", + ctx.scope, span.span_id, name + ); + let _ = ctx.tx.send(TraceEvent::Span(span)); + retval + } + // We use TraceContext to propagate the tx channel to the trace agent, so if we have + // no context, just run the future without tracing. + Err(_) => f(), + } +} + +pub trait WithTraceContext: Sized + std::future::Future { + async fn baml_traced(self, name: impl Into) -> ::Output { + let verbosity = tracing_core::Level::INFO; + let curr_ctx = BAML_TRACE_CTX.try_with(|ctx| ctx.clone()); + + match curr_ctx { + Ok(ctx) => { + let name = name.into(); + let span = TraceSpanBuilder::start_span(name.clone()); + println!( + "entering span: {:?} -> {:?} {}", + ctx.scope, span.span_id, name + ); + let new_ctx = TraceContext { + scope: InstrumentationScope::Child { + parent: span.span_id, + }, + tx: ctx.tx.clone(), + }; + let retval = BAML_TRACE_CTX.scope(new_ctx, self).await; + let span = span.end(); + println!( + "exiting span: {:?} <- {:?} {}", + ctx.scope, span.span_id, name + ); + let _ = ctx.tx.send(TraceEvent::Span(span)); + retval + } + // We use TraceContext to propagate the tx channel to the trace agent, so if we have + // no context, just run the future without tracing. + Err(_) => self.await, + } + } +} + +// Auto-implement the trait for all futures +impl WithTraceContext for F where F: std::future::Future {} diff --git a/engine/sandbox/src/main.rs b/engine/sandbox/src/main.rs new file mode 100644 index 000000000..9e0d96abe --- /dev/null +++ b/engine/sandbox/src/main.rs @@ -0,0 +1,82 @@ +use anyhow::{Context, Result}; +use axum::{routing::get, Router}; +use std::rc::Rc; + +mod btrace; +use btrace::WithTraceContext; +mod web_server; + +fn main() { + let msg = Rc::new("my name is cabbage".to_string()); + + println!("starting server"); + // let localset = tokio::task::LocalSet::new(); + // localset.spawn_local(async move { + // if let Err(e) = run().await { + // tracing::error!("server error: {}", e); + // } + // }); + + let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel(); + let tctx = btrace::TraceContext { + scope: btrace::InstrumentationScope::Root, + tx, + }; + + let rt = tokio::runtime::Runtime::new().unwrap(); + rt.spawn(async move { + println!("starting span receiver"); + while let Some(span) = rx.recv().await { + // println!("received span: {:?}", span); + } + }); + rt.block_on(btrace::BAML_TRACE_CTX.scope(tctx, async { + println!("starting parallel tasks"); + parallel_tasks().await; + tokio::time::sleep(std::time::Duration::from_millis(3000)).await; + })); + println!("shutting down server"); +} + +// New async functions to create interesting tracing patterns +async fn parallel_tasks() -> Result<()> { + let task1 = task_a().baml_traced("task_a"); + let task2 = task_b().baml_traced("task_b"); + let task3 = task_c().baml_traced("task_c"); + + tokio::join!(task1, task2, task3); + Ok(()) +} + +async fn task_a() { + tokio::time::sleep(std::time::Duration::from_millis(100)).await; + let _ = nested_task_a().baml_traced("nested_task_a").await; +} + +async fn nested_task_a() -> Result<()> { + tokio::time::sleep(std::time::Duration::from_millis(50)).await; + task_d().baml_traced("task_d").await +} + +async fn task_b() { + tokio::time::sleep(std::time::Duration::from_millis(75)).await; + let _ = nested_task_b().baml_traced("nested_task_b").await; +} + +async fn nested_task_b() -> Result<()> { + tokio::time::sleep(std::time::Duration::from_millis(25)).await; + Ok(()) +} + +async fn task_c() { + tokio::time::sleep(std::time::Duration::from_millis(150)).await; +} + +async fn task_d() -> Result<()> { + tokio::time::sleep(std::time::Duration::from_millis(60)).await; + btrace::baml_trace_sync_scope("inside-task-d", || { + println!("inside task d"); + Ok::<(), anyhow::Error>(()) + }); + Ok(()) +} diff --git a/engine/sandbox/src/web_server.rs b/engine/sandbox/src/web_server.rs new file mode 100644 index 000000000..2eff4caab --- /dev/null +++ b/engine/sandbox/src/web_server.rs @@ -0,0 +1,40 @@ +use anyhow::{Context, Result}; +use axum::{routing::get, Router}; +use std::rc::Rc; + +fn main() { + let msg = Rc::new("my name is cabbage".to_string()); + + println!("starting server"); + let localset = tokio::task::LocalSet::new(); + localset.spawn_local(async move { + if let Err(e) = run().await { + tracing::error!("server error: {}", e); + } + }); + + println!("shutting down server"); +} + +async fn run() -> Result<()> { + let port = 4000; + let tcp_listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{}", port)) + .await + .context(format!( + "Failed to bind to port {}; try using --port PORT to specify a different port.", + port + ))?; + + let app = Router::new().route("/", get(handler)); + + axum::serve(tcp_listener, app) + .await + .context("Failed to start server")?; + + Ok(()) +} +// Basic handler that returns a greeting +async fn handler() -> &'static str { + tokio::time::sleep(std::time::Duration::from_millis(10)).await; + "Hello from sandbox server!" +} From 5dd25f75a7dc741e7b9118d19b55ace2c22212c9 Mon Sep 17 00:00:00 2001 From: Sam Lijin Date: Thu, 12 Dec 2024 14:38:22 -0800 Subject: [PATCH 2/8] add new tracing client impl --- engine/Cargo.lock | 21 ++++- engine/Cargo.toml | 2 + engine/baml-runtime/Cargo.toml | 13 ++-- .../src/btrace/mod.rs} | 78 ++++++------------- engine/baml-runtime/src/lib.rs | 36 ++++++++- .../src/runtime/runtime_interface.rs | 3 + engine/baml-runtime/src/runtime_interface.rs | 3 +- .../baml-runtime/src/types/runtime_context.rs | 3 + engine/baml-runtime/src/types/stream.rs | 29 ++++--- tools/build | 2 +- 10 files changed, 113 insertions(+), 77 deletions(-) rename engine/{sandbox/src/btrace.rs => baml-runtime/src/btrace/mod.rs} (62%) diff --git a/engine/Cargo.lock b/engine/Cargo.lock index e3e3ef421..80a0e081d 100644 --- a/engine/Cargo.lock +++ b/engine/Cargo.lock @@ -994,6 +994,7 @@ dependencies = [ "tokio", "tokio-stream", "tracing", + "tracing-core", "tracing-subscriber", "url", "uuid", @@ -4304,6 +4305,22 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "sandbox" +version = "0.1.0" +dependencies = [ + "anyhow", + "axum", + "log", + "serde_json", + "tokio", + "tracing", + "tracing-core", + "tracing-subscriber", + "uuid", + "web-time", +] + [[package]] name = "schannel" version = "0.1.23" @@ -5058,9 +5075,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", diff --git a/engine/Cargo.toml b/engine/Cargo.toml index 2f408ea46..fafdd19b0 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -10,6 +10,7 @@ members = [ "language_client_python", "language_client_ruby/ext/ruby_ffi", "language_client_typescript", + "sandbox", ] default-members = [ "baml-lib/*", @@ -85,6 +86,7 @@ strum_macros = "0.26.2" time = { version = "0.3.36", features = ["formatting"] } pin-project-lite = "0.2.14" url = "2.5.2" +uuid = { version = "1.8.0", features = ["v4", "v7", "serde"] } walkdir = "2.5.0" web-time = "1.1.0" diff --git a/engine/baml-runtime/Cargo.toml b/engine/baml-runtime/Cargo.toml index 679d0c272..6499aba5b 100644 --- a/engine/baml-runtime/Cargo.toml +++ b/engine/baml-runtime/Cargo.toml @@ -60,7 +60,7 @@ tokio = { version = "1", default-features = false, features = [ tokio-stream = "0.1.15" # NOTE(sam): adding this caused a build error, I suspect because tower uses nightly features or something # tower = "0.5.0" -uuid = { version = "1.8.0", features = ["v4", "serde"] } +uuid.workspace = true web-time.workspace = true static_assertions.workspace = true mime_guess = "2.0.4" @@ -89,7 +89,12 @@ aws-smithy-json = "0.60.7" pretty_assertions = "1.4.0" valuable = { version = "0.1.0", features = ["derive"] } tracing = { version = "0.1.40", features = ["valuable"] } -tracing-subscriber = { version = "0.3.18", features = ["json", "env-filter","valuable"] } +tracing-core = { version = "0.1.31" } +tracing-subscriber = { version = "0.3.18", features = [ + "json", + "env-filter", + "valuable", +] } thiserror = "2.0.1" log-once = "0.4.1" @@ -130,7 +135,7 @@ aws-sdk-bedrockruntime = "1.37.0" axum = "0.7.5" axum-extra = { version = "0.9.3", features = ["erased-json", "typed-header"] } hostname = "0.3.1" -jsonwebtoken = { version="9.3.0"} +jsonwebtoken = { version = "9.3.0" } notify-debouncer-full = "0.3.1" ring = { version = "0.17.4", features = ["std"] } tokio = { version = "1", features = ["full"] } @@ -139,8 +144,6 @@ walkdir = "2.5.0" which = "6.0.3" - - [features] defaults = ["skip-integ-tests"] internal = [] diff --git a/engine/sandbox/src/btrace.rs b/engine/baml-runtime/src/btrace/mod.rs similarity index 62% rename from engine/sandbox/src/btrace.rs rename to engine/baml-runtime/src/btrace/mod.rs index 90386d2eb..4a0dd0df8 100644 --- a/engine/sandbox/src/btrace.rs +++ b/engine/baml-runtime/src/btrace/mod.rs @@ -98,74 +98,46 @@ fn log(callsite: String, msg: String, fields: serde_json::Map(name: impl Into, f: F) -> R -where - F: FnOnce() -> R, -{ - let verbosity = tracing_core::Level::INFO; - let curr_ctx = BAML_TRACE_CTX.try_with(|ctx| ctx.clone()); - - match curr_ctx { - Ok(ctx) => { - let name = name.into(); - let span = TraceSpanBuilder::start_span(name.clone()); - println!( - "entering span: {:?} -> {:?} {}", - ctx.scope, span.span_id, name - ); - let new_ctx = TraceContext { - scope: InstrumentationScope::Child { - parent: span.span_id, - }, - tx: ctx.tx.clone(), - }; - let retval = BAML_TRACE_CTX.sync_scope(new_ctx, f); - let span = span.end(); - println!( - "exiting span: {:?} <- {:?} {}", - ctx.scope, span.span_id, name - ); - let _ = ctx.tx.send(TraceEvent::Span(span)); - retval - } - // We use TraceContext to propagate the tx channel to the trace agent, so if we have - // no context, just run the future without tracing. - Err(_) => f(), - } -} - -pub trait WithTraceContext: Sized + std::future::Future { - async fn baml_traced(self, name: impl Into) -> ::Output { - let verbosity = tracing_core::Level::INFO; +macro_rules! impl_trace_scope { + ($new_ctx:ident, $name:ident, $wrapped_fn:expr, $unwrapped_fn:expr) => {{ let curr_ctx = BAML_TRACE_CTX.try_with(|ctx| ctx.clone()); match curr_ctx { Ok(ctx) => { - let name = name.into(); + let name = $name.into(); let span = TraceSpanBuilder::start_span(name.clone()); - println!( - "entering span: {:?} -> {:?} {}", - ctx.scope, span.span_id, name - ); - let new_ctx = TraceContext { + let $new_ctx = TraceContext { scope: InstrumentationScope::Child { parent: span.span_id, }, tx: ctx.tx.clone(), }; - let retval = BAML_TRACE_CTX.scope(new_ctx, self).await; + let retval = $wrapped_fn; let span = span.end(); - println!( - "exiting span: {:?} <- {:?} {}", - ctx.scope, span.span_id, name - ); let _ = ctx.tx.send(TraceEvent::Span(span)); retval } - // We use TraceContext to propagate the tx channel to the trace agent, so if we have - // no context, just run the future without tracing. - Err(_) => self.await, + Err(_) => $unwrapped_fn, } + }}; +} + +pub fn btrace(name: impl Into, f: F) -> R +where + F: FnOnce() -> R, +{ + impl_trace_scope!(new_ctx, name, BAML_TRACE_CTX.sync_scope(new_ctx, f), f()) +} + +pub trait WithTraceContext: Sized + std::future::Future { + #[allow(async_fn_in_trait)] + async fn btrace(self, name: impl Into) -> ::Output { + impl_trace_scope!( + new_ctx, + name, + BAML_TRACE_CTX.scope(new_ctx, self).await, + self.await + ) } } diff --git a/engine/baml-runtime/src/lib.rs b/engine/baml-runtime/src/lib.rs index e847b53fb..7fc08f4af 100644 --- a/engine/baml-runtime/src/lib.rs +++ b/engine/baml-runtime/src/lib.rs @@ -5,6 +5,7 @@ pub mod internal; #[cfg(not(feature = "internal"))] pub(crate) mod internal; +pub mod btrace; #[cfg(not(target_arch = "wasm32"))] pub mod cli; pub mod client_registry; @@ -27,6 +28,7 @@ use anyhow::Result; use baml_types::BamlMap; use baml_types::BamlValue; use baml_types::Constraint; +use btrace::WithTraceContext; use cfg_if::cfg_if; use client_registry::ClientRegistry; use indexmap::IndexMap; @@ -48,8 +50,6 @@ use tracing::{BamlTracer, TracingSpan}; use type_builder::TypeBuilder; pub use types::*; -use clap::Parser; - #[cfg(feature = "internal")] pub use internal_baml_jinja::{ChatMessagePart, RenderedPrompt}; #[cfg(feature = "internal")] @@ -76,6 +76,8 @@ pub struct BamlRuntime { env_vars: HashMap, #[cfg(not(target_arch = "wasm32"))] pub async_runtime: Arc, + pub trace_agent_tx: tokio::sync::mpsc::UnboundedSender, + pub trace_agent_rx: tokio::sync::mpsc::UnboundedReceiver, } impl BamlRuntime { @@ -136,12 +138,17 @@ impl BamlRuntime { .iter() .map(|(k, v)| (k.as_ref().to_string(), v.as_ref().to_string())) .collect(); + + let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); + Ok(BamlRuntime { inner: InternalBamlRuntime::from_directory(&path)?, tracer: BamlTracer::new(None, env_vars.into_iter())?.into(), env_vars: copy, #[cfg(not(target_arch = "wasm32"))] async_runtime: Self::get_tokio_singleton()?, + trace_agent_tx: tx, + trace_agent_rx: rx, }) } @@ -154,12 +161,16 @@ impl BamlRuntime { .iter() .map(|(k, v)| (k.as_ref().to_string(), v.as_ref().to_string())) .collect(); + let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); + Ok(BamlRuntime { inner: InternalBamlRuntime::from_file_content(root_path, files)?, tracer: BamlTracer::new(None, env_vars.into_iter())?.into(), env_vars: copy, #[cfg(not(target_arch = "wasm32"))] async_runtime: Self::get_tokio_singleton()?, + trace_agent_tx: tx, + trace_agent_rx: rx, }) } @@ -236,6 +247,10 @@ impl BamlRuntime { rctx_stream, #[cfg(not(target_arch = "wasm32"))] self.async_runtime.clone(), + btrace::TraceContext { + scope: btrace::InstrumentationScope::Root, + tx: self.trace_agent_tx.clone(), + }, )?; let (response_res, span_uuid) = stream.run(on_event, ctx, None, None).await; let res = response_res?; @@ -315,10 +330,19 @@ impl BamlRuntime { ) -> (Result, Option) { log::trace!("Calling function: {}", function_name); let span = self.tracer.start_span(&function_name, ctx, params); + let tctx = btrace::TraceContext { + scope: btrace::InstrumentationScope::Root, + tx: self.trace_agent_tx.clone(), + }; let response = match ctx.create_ctx(tb, cb) { Ok(rctx) => { - self.inner - .call_function_impl(function_name, params, rctx) + btrace::BAML_TRACE_CTX + .scope( + tctx, + self.inner + .call_function_impl(function_name.clone(), params, rctx) + .btrace(format!("baml_function::{}", function_name)), + ) .await } Err(e) => Err(e), @@ -355,6 +379,10 @@ impl BamlRuntime { ctx.create_ctx(tb, cb)?, #[cfg(not(target_arch = "wasm32"))] self.async_runtime.clone(), + btrace::TraceContext { + scope: btrace::InstrumentationScope::Root, + tx: self.trace_agent_tx.clone(), + }, ) } diff --git a/engine/baml-runtime/src/runtime/runtime_interface.rs b/engine/baml-runtime/src/runtime/runtime_interface.rs index c1c9669bf..59f3188ac 100644 --- a/engine/baml-runtime/src/runtime/runtime_interface.rs +++ b/engine/baml-runtime/src/runtime/runtime_interface.rs @@ -1,6 +1,7 @@ use std::{collections::HashMap, path::PathBuf, sync::Arc}; use super::InternalBamlRuntime; +use crate::btrace; use crate::internal::llm_client::traits::WithClientProperties; use crate::internal::llm_client::LLMResponse; use crate::{ @@ -393,6 +394,7 @@ impl RuntimeInterface for InternalBamlRuntime { tracer: Arc, ctx: RuntimeContext, #[cfg(not(target_arch = "wasm32"))] tokio_runtime: Arc, + tctx: btrace::TraceContext, ) -> Result { let func = self.get_function(&function_name, &ctx)?; let renderer = PromptRenderer::from_function(&func, self.ir(), &ctx)?; @@ -420,6 +422,7 @@ impl RuntimeInterface for InternalBamlRuntime { renderer, #[cfg(not(target_arch = "wasm32"))] tokio_runtime, + tctx, }) } } diff --git a/engine/baml-runtime/src/runtime_interface.rs b/engine/baml-runtime/src/runtime_interface.rs index 0a43d488d..2d72648fc 100644 --- a/engine/baml-runtime/src/runtime_interface.rs +++ b/engine/baml-runtime/src/runtime_interface.rs @@ -10,13 +10,13 @@ use crate::internal::llm_client::llm_provider::LLMProvider; use crate::internal::llm_client::orchestrator::{OrchestrationScope, OrchestratorNode}; use crate::tracing::{BamlTracer, TracingSpan}; use crate::types::on_log_event::LogEventCallbackSync; +use crate::{btrace, RenderCurlSettings, RuntimeContextManager}; use crate::{ internal::{ir_features::IrFeatures, llm_client::retry_policy::CallablePolicy}, runtime::InternalBamlRuntime, types::FunctionResultStream, FunctionResult, RuntimeContext, }; -use crate::{RenderCurlSettings, RuntimeContextManager}; pub(crate) trait RuntimeConstructor { #[cfg(not(target_arch = "wasm32"))] @@ -45,6 +45,7 @@ pub trait RuntimeInterface { tracer: Arc, ctx: RuntimeContext, #[cfg(not(target_arch = "wasm32"))] tokio_runtime: Arc, + tctx: btrace::TraceContext, ) -> Result; } diff --git a/engine/baml-runtime/src/types/runtime_context.rs b/engine/baml-runtime/src/types/runtime_context.rs index 8abd3ea7a..cc6e889c6 100644 --- a/engine/baml-runtime/src/types/runtime_context.rs +++ b/engine/baml-runtime/src/types/runtime_context.rs @@ -51,6 +51,7 @@ cfg_if::cfg_if!( pub struct RuntimeContext { // path to baml_src in the local filesystem pub baml_src: Arc, + pub tracer_tx: tokio::sync::mpsc::UnboundedSender, env: HashMap, pub tags: HashMap, pub client_overrides: Option<(Option, HashMap>)>, @@ -79,6 +80,7 @@ impl RuntimeContext { class_override: IndexMap, enum_overrides: IndexMap, ) -> RuntimeContext { + let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); RuntimeContext { baml_src, env, @@ -86,6 +88,7 @@ impl RuntimeContext { client_overrides, class_override, enum_overrides, + tracer_tx: tx, } } diff --git a/engine/baml-runtime/src/types/stream.rs b/engine/baml-runtime/src/types/stream.rs index f9abe8123..4d0ec0ab9 100644 --- a/engine/baml-runtime/src/types/stream.rs +++ b/engine/baml-runtime/src/types/stream.rs @@ -5,6 +5,7 @@ use internal_baml_core::ir::repr::IntermediateRepr; use std::sync::Arc; use crate::{ + btrace::{self, WithTraceContext}, client_registry::ClientRegistry, internal::{ llm_client::orchestrator::{orchestrate_stream, OrchestratorNodeIterator}, @@ -29,6 +30,7 @@ pub struct FunctionResultStream { pub(crate) tracer: Arc, #[cfg(not(target_arch = "wasm32"))] pub(crate) tokio_runtime: Arc, + pub(crate) tctx: btrace::TraceContext, } #[cfg(target_arch = "wasm32")] @@ -94,17 +96,22 @@ impl FunctionResultStream { let rctx = ctx.create_ctx(tb, cb); let res = match rctx { Ok(rctx) => { - let (history, _) = orchestrate_stream( - local_orchestrator, - self.ir.as_ref(), - &rctx, - &self.renderer, - &baml_types::BamlValue::Map(local_params), - |content| self.renderer.parse(content, true), - |content| self.renderer.parse(content, false), - on_event, - ) - .await; + let (history, _) = btrace::BAML_TRACE_CTX + .scope( + self.tctx.clone(), + orchestrate_stream( + local_orchestrator, + self.ir.as_ref(), + &rctx, + &self.renderer, + &baml_types::BamlValue::Map(local_params), + |content| self.renderer.parse(content, true), + |content| self.renderer.parse(content, false), + on_event, + ) + .btrace(format!("baml_stream_function::{}", self.function_name)), + ) + .await; FunctionResult::new_chain(history) } diff --git a/tools/build b/tools/build index 0bac5abf9..67935c35e 100755 --- a/tools/build +++ b/tools/build @@ -157,7 +157,7 @@ case "$_path" in if [ "$_watch_mode" -eq 1 ]; then npx nodemon \ - --ext rs,hb,hbs,j2,toml,baml \ + --ext rs,hb,hbs,j2,toml \ --watch "${_repo_root}/engine" \ --ignore 'target/**' \ --exec "${command}" From a6bcd6778d87e4636c3f4d217c3e56e5eb5042da Mon Sep 17 00:00:00 2001 From: Sam Lijin Date: Thu, 19 Dec 2024 11:51:55 -0800 Subject: [PATCH 3/8] stash everything --- .mise.toml | 2 +- engine/Cargo.lock | 28 + engine/Cargo.toml | 2 + engine/baml-runtime/Cargo.toml | 1 + engine/baml-runtime/src/btrace/mod.rs | 145 -- .../internal/llm_client/orchestrator/mod.rs | 12 +- .../llm_client/orchestrator/stream.rs | 42 +- .../primitive/anthropic/anthropic_client.rs | 34 +- .../llm_client/primitive/aws/aws_client.rs | 36 +- .../primitive/google/googleai_client.rs | 40 +- .../primitive/openai/openai_client.rs | 31 +- .../primitive/vertex/vertex_client.rs | 28 +- .../src/internal/llm_client/traits/mod.rs | 2 +- engine/baml-runtime/src/lib.rs | 25 +- engine/baml-runtime/src/types/stream.rs | 50 +- engine/btrace/Cargo.toml | 53 + engine/btrace/src/lib.rs | 264 ++++ engine/btrace/tests.rs | 176 +++ engine/btrace/tracer_thread.rs | 69 + engine/language_client_typescript/native.js | 4 +- .../test-files/providers/providers.baml | 2 +- integ-tests/python/baml_client/inlinedbaml.py | 2 +- integ-tests/python/poetry.lock | 297 ++-- integ-tests/python/pyproject.toml | 1 - integ-tests/python/tests/test_functions.py | 2 +- integ-tests/ruby/baml_client/inlined.rb | 2 +- .../typescript/baml_client/inlinedbaml.ts | 2 +- integ-tests/typescript/test-report.html | 1243 +---------------- .../typescript/tests/integ-tests.test.ts | 4 +- tools/build | 4 +- 30 files changed, 1023 insertions(+), 1580 deletions(-) delete mode 100644 engine/baml-runtime/src/btrace/mod.rs create mode 100644 engine/btrace/Cargo.toml create mode 100644 engine/btrace/src/lib.rs create mode 100644 engine/btrace/tests.rs create mode 100644 engine/btrace/tracer_thread.rs diff --git a/.mise.toml b/.mise.toml index 401be3817..b73ec2125 100644 --- a/.mise.toml +++ b/.mise.toml @@ -2,4 +2,4 @@ node = "20.14" ruby = "3.1" pnpm = "9.9" -poetry = "1.8.4" +poetry = "1.8.5" diff --git a/engine/Cargo.lock b/engine/Cargo.lock index 80a0e081d..fd95a5f29 100644 --- a/engine/Cargo.lock +++ b/engine/Cargo.lock @@ -971,6 +971,7 @@ dependencies = [ "mime", "mime_guess", "minijinja", + "nonempty", "notify-debouncer-full", "pin-project-lite", "pretty_assertions", @@ -1227,6 +1228,27 @@ dependencies = [ "serde", ] +[[package]] +name = "btrace" +version = "0.70.1" +dependencies = [ + "anyhow", + "base64 0.22.1", + "bytes", + "futures-timer", + "hostname", + "js-sys", + "reqwest", + "serde_json", + "tokio", + "tracing-core", + "uuid", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "web-time", +] + [[package]] name = "bumpalo" version = "3.16.0" @@ -3195,6 +3217,12 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nonempty" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "549e471b99ccaf2f89101bec68f4d244457d5a95a9c3d0672e9564124397741d" + [[package]] name = "notify" version = "6.1.1" diff --git a/engine/Cargo.toml b/engine/Cargo.toml index fafdd19b0..159e1cd0b 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -5,6 +5,7 @@ members = [ "baml-runtime", "baml-schema-wasm", "bstd", + "btrace", "cli", "language_client_codegen", "language_client_python", @@ -21,6 +22,7 @@ default-members = [ # also have something to do with resolver=2 and how duplicate dep builds are handled) # "baml-schema-wasm", "bstd", + "btrace", "cli", "language_client_codegen", "language_client_python", diff --git a/engine/baml-runtime/Cargo.toml b/engine/baml-runtime/Cargo.toml index 6499aba5b..e4433314b 100644 --- a/engine/baml-runtime/Cargo.toml +++ b/engine/baml-runtime/Cargo.toml @@ -45,6 +45,7 @@ internal-baml-core = { path = "../baml-lib/baml-core" } internal-baml-jinja = { path = "../baml-lib/jinja-runtime" } log.workspace = true minijinja.workspace = true +nonempty = "0.11.0" pin-project-lite.workspace = true reqwest-eventsource = "0.6.0" scopeguard.workspace = true diff --git a/engine/baml-runtime/src/btrace/mod.rs b/engine/baml-runtime/src/btrace/mod.rs deleted file mode 100644 index 4a0dd0df8..000000000 --- a/engine/baml-runtime/src/btrace/mod.rs +++ /dev/null @@ -1,145 +0,0 @@ -#[derive(Clone)] -pub struct TraceContext { - /// The scope used for all spans/logs within this context. - pub scope: InstrumentationScope, - /// The channel used to send trace events to the trace agent. - pub tx: tokio::sync::mpsc::UnboundedSender, -} - -tokio::task_local! { - pub static BAML_TRACE_CTX: TraceContext; -} - -struct TraceSpanBuilder { - span_id: uuid::Uuid, - start_time: std::time::Instant, - meta: TraceMetadata, - fields: serde_json::Map, -} - -#[derive(Debug, Clone)] -pub enum InstrumentationScope { - Root, - Child { parent: uuid::Uuid }, -} - -#[derive(Debug)] -pub struct TraceMetadata { - /// human-readable callsite identifier, e.g. "ExtractResume" or "openai/gpt-4o/chat" - name: String, - /// verbosity level - verbosity: tracing_core::Level, -} - -#[derive(Debug)] -pub struct TraceSpan { - scope: InstrumentationScope, - span_id: uuid::Uuid, - start_time: web_time::Instant, - duration: web_time::Duration, - meta: TraceMetadata, - fields: serde_json::Map, -} - -pub struct TraceLog { - scope: InstrumentationScope, - start_time: web_time::Instant, - msg: String, - meta: TraceMetadata, - fields: serde_json::Map, -} - -pub enum TraceEvent { - Span(TraceSpan), - Log(TraceLog), -} - -impl TraceSpanBuilder { - pub fn start_span(name: String) -> Self { - Self { - span_id: uuid::Uuid::now_v7(), - start_time: web_time::Instant::now(), - meta: TraceMetadata { - name, - verbosity: tracing_core::Level::INFO, - }, - fields: serde_json::Map::new(), - } - } - - pub fn end(self) -> TraceSpan { - let duration = self.start_time.elapsed(); - let ctx = BAML_TRACE_CTX.get(); - let span = TraceSpan { - scope: ctx.scope.clone(), - span_id: self.span_id, - start_time: self.start_time, - duration, - meta: self.meta, - fields: self.fields, - }; - span - } -} - -fn log(callsite: String, msg: String, fields: serde_json::Map) { - let Ok(ctx) = BAML_TRACE_CTX.try_with(|ctx| ctx.clone()) else { - return; - }; - let _ = ctx.tx.send(TraceEvent::Log(TraceLog { - scope: ctx.scope.clone(), - start_time: web_time::Instant::now(), - msg, - meta: TraceMetadata { - name: callsite, - verbosity: tracing_core::Level::INFO, - }, - fields, - })); -} - -macro_rules! impl_trace_scope { - ($new_ctx:ident, $name:ident, $wrapped_fn:expr, $unwrapped_fn:expr) => {{ - let curr_ctx = BAML_TRACE_CTX.try_with(|ctx| ctx.clone()); - - match curr_ctx { - Ok(ctx) => { - let name = $name.into(); - let span = TraceSpanBuilder::start_span(name.clone()); - let $new_ctx = TraceContext { - scope: InstrumentationScope::Child { - parent: span.span_id, - }, - tx: ctx.tx.clone(), - }; - let retval = $wrapped_fn; - let span = span.end(); - let _ = ctx.tx.send(TraceEvent::Span(span)); - retval - } - Err(_) => $unwrapped_fn, - } - }}; -} - -pub fn btrace(name: impl Into, f: F) -> R -where - F: FnOnce() -> R, -{ - impl_trace_scope!(new_ctx, name, BAML_TRACE_CTX.sync_scope(new_ctx, f), f()) -} - -pub trait WithTraceContext: Sized + std::future::Future { - #[allow(async_fn_in_trait)] - async fn btrace(self, name: impl Into) -> ::Output { - impl_trace_scope!( - new_ctx, - name, - BAML_TRACE_CTX.scope(new_ctx, self).await, - self.await - ) - } -} - -// Auto-implement the trait for all futures -impl WithTraceContext for F where F: std::future::Future {} 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 a5ee1dd71..8a9238b60 100644 --- a/engine/baml-runtime/src/internal/llm_client/orchestrator/mod.rs +++ b/engine/baml-runtime/src/internal/llm_client/orchestrator/mod.rs @@ -1,8 +1,10 @@ mod call; mod stream; +use serde_json::json; use web_time::Duration; // Add this line +use crate::btrace::WithTraceContext; use crate::RenderCurlSettings; use crate::{ internal::prompt_renderer::PromptRenderer, runtime_interface::InternalClientLookup, @@ -216,7 +218,15 @@ impl WithStreamable for OrchestratorNode { }) .map(|a| a.increment_index()) .for_each(drop); - self.provider.stream(ctx, prompt).await + self.provider + .stream(ctx, prompt) + .btrace( + tracing::Level::INFO, + format!("stream_init::{}", self.provider.name()), + json!({}), + |_| serde_json::Value::Null, + ) + .await } } 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 c3cf30fa2..8561c9424 100644 --- a/engine/baml-runtime/src/internal/llm_client/orchestrator/stream.rs +++ b/engine/baml-runtime/src/internal/llm_client/orchestrator/stream.rs @@ -3,9 +3,11 @@ use async_std::stream::StreamExt; use baml_types::BamlValue; use internal_baml_core::ir::repr::IntermediateRepr; use jsonish::BamlValueWithFlags; +use serde_json::json; use web_time::Duration; use crate::{ + btrace::{self, WithTraceContext}, internal::{ llm_client::{ parsed_value_to_response, @@ -83,6 +85,16 @@ where stream_part }) .fold(None, |_, current| Some(current)) + .btrace( + tracing::Level::INFO, + format!("stream_run::{}", node.provider.name()), + json!({ + "prompt": prompt, + "params": params, + "node": node.scope.name(), + }), + |_| serde_json::Value::Null, + ) .await .unwrap_or_else(|| { LLMResponse::LLMFailure(LLMErrorResponse { @@ -105,16 +117,18 @@ where .finish_reason_filter() .is_allowed(s.metadata.finish_reason.as_ref()) { - Some(Err(anyhow::anyhow!(crate::errors::ExposedError::FinishReasonError { - prompt: s.prompt.to_string(), - raw_output: s.content.clone(), - message: "Finish reason not allowed".to_string(), - finish_reason: s.metadata.finish_reason.clone(), - }))) + Some(Err(anyhow::anyhow!( + crate::errors::ExposedError::FinishReasonError { + prompt: s.prompt.to_string(), + raw_output: s.content.clone(), + message: "Finish reason not allowed".to_string(), + finish_reason: s.metadata.finish_reason.clone(), + } + ))) } else { Some(parse_fn(&s.content)) } - }, + } _ => None, }; let (parsed_response, response_value) = match parsed_response { @@ -123,6 +137,7 @@ where None => (None, None), }; // parsed_response.map(|r| r.and_then(|v| parsed_value_to_response(v))); + let node_name = node.scope.name(); let sleep_duration = node.error_sleep_duration().cloned(); results.push((node.scope, final_response, parsed_response, response_value)); @@ -132,7 +147,18 @@ where .map_or(false, |(_, r, _, _)| matches!(r, LLMResponse::Success(_))) { break; - } else if let Some(duration) = sleep_duration { + } + btrace::log( + tracing_core::Level::INFO, + format!("baml_src::{}", node.provider.name()), + "Failed to start stream".to_string(), + json!({ + "sleep_duration": sleep_duration, + "baml.llm_client": node_name, + }), + ); + + if let Some(duration) = sleep_duration { total_sleep_duration += duration; async_std::task::sleep(duration).await; } diff --git a/engine/baml-runtime/src/internal/llm_client/primitive/anthropic/anthropic_client.rs b/engine/baml-runtime/src/internal/llm_client/primitive/anthropic/anthropic_client.rs index a285630e7..f947c412a 100644 --- a/engine/baml-runtime/src/internal/llm_client/primitive/anthropic/anthropic_client.rs +++ b/engine/baml-runtime/src/internal/llm_client/primitive/anthropic/anthropic_client.rs @@ -1,6 +1,9 @@ -use crate::internal::llm_client::{ - traits::{ToProviderMessage, ToProviderMessageExt, WithClientProperties}, - ResolveMediaUrls, +use crate::{ + btrace::{self, WithTraceContext}, + internal::llm_client::{ + traits::{ToProviderMessage, ToProviderMessageExt, WithClientProperties}, + ResolveMediaUrls, + }, }; use std::collections::HashMap; @@ -124,10 +127,8 @@ impl SseResponseTrait for AnthropicClient { Ok(Box::pin( resp.bytes_stream() - .inspect(|event| log::trace!("anthropic event bytes: {:#?}", event)) .eventsource() .map(|event| -> Result { Ok(serde_json::from_str(&event?.data)?) }) - .inspect(|event| log::trace!("anthropic eventsource: {:#?}", event)) .scan( Ok(LLMCompleteResponse { client: client_name.clone(), @@ -189,6 +190,14 @@ impl SseResponseTrait for AnthropicClient { Some(body.usage.input_tokens + body.usage.output_tokens); } MessageChunk::ContentBlockDelta(event) => { + btrace::log( + tracing::Level::INFO, + "anthropic_client".to_string(), + "event".to_string(), + json!({ + "delta.content": event.delta.text, + }), + ); inner.content += &event.delta.text; } MessageChunk::ContentBlockStart(_) => (), @@ -376,6 +385,21 @@ impl WithChat for AnthropicClient { >( self, either::Either::Right(prompt), false ) + .btrace( + tracing_core::Level::INFO, + "anthropic_chat", + json!({ + "prompt": prompt, + }), + |v| match v { + Ok((response, ..)) => json!({ + "llm.response": response, + }), + Err(e) => json!({ + "exception": e, + }), + }, + ) .await { Ok(v) => v, diff --git a/engine/baml-runtime/src/internal/llm_client/primitive/aws/aws_client.rs b/engine/baml-runtime/src/internal/llm_client/primitive/aws/aws_client.rs index 69c223642..55217f654 100644 --- a/engine/baml-runtime/src/internal/llm_client/primitive/aws/aws_client.rs +++ b/engine/baml-runtime/src/internal/llm_client/primitive/aws/aws_client.rs @@ -19,10 +19,11 @@ use internal_llm_client::{ AllowedRoleMetadata, ClientProvider, ResolvedClientProperty, UnresolvedClientProperty, }; use serde::Deserialize; -use serde_json::Map; +use serde_json::{json, Map}; use web_time::Instant; use web_time::SystemTime; +use crate::btrace::WithTraceContext; use crate::client_registry::ClientProperty; use crate::internal::llm_client::traits::{ToProviderMessageExt, WithClientProperties}; use crate::internal::llm_client::{ @@ -35,7 +36,7 @@ use crate::internal::llm_client::{ ModelFeatures, ResolveMediaUrls, }; -use crate::{RenderCurlSettings, RuntimeContext}; +use crate::{btrace, RenderCurlSettings, RuntimeContext}; // represents client that interacts with the Anthropic API pub struct AwsClient { @@ -434,6 +435,14 @@ impl WithStreamChat for AwsClient { ref delta, )) = content_block_delta.delta { + btrace::log( + btrace::Level::INFO, + "aws_client".to_string(), + "event".to_string(), + json!({ + "delta.content": delta, + }), + ); new_state.content += delta; // TODO- handle } @@ -650,7 +659,28 @@ impl WithChat for AwsClient { let system_start = SystemTime::now(); let instant_start = Instant::now(); - let response = match request.send().await { + let response = match request + .send() + .btrace( + tracing_core::Level::INFO, + "aws_chat", + json!({ + "prompt": chat_messages, + }), + |v| match v { + Ok(response) => json!({ + // TODO: structured tracing for bedrock responses + "llm.response": format!("{:?}", response), + }), + Err(e) => json!({ + "exception": { + "message": format!("{:?}", e), + }, + }), + }, + ) + .await + { Ok(resp) => resp, Err(e) => { return LLMResponse::LLMFailure(LLMErrorResponse { diff --git a/engine/baml-runtime/src/internal/llm_client/primitive/google/googleai_client.rs b/engine/baml-runtime/src/internal/llm_client/primitive/google/googleai_client.rs index 8ba6c8639..7002c3d60 100644 --- a/engine/baml-runtime/src/internal/llm_client/primitive/google/googleai_client.rs +++ b/engine/baml-runtime/src/internal/llm_client/primitive/google/googleai_client.rs @@ -1,9 +1,10 @@ +use crate::btrace::WithTraceContext; use crate::client_registry::ClientProperty; use crate::internal::llm_client::traits::{ ToProviderMessage, ToProviderMessageExt, WithClientProperties, }; use crate::internal::llm_client::ResolveMediaUrls; -use crate::RuntimeContext; +use crate::{btrace, RuntimeContext}; use crate::{ internal::llm_client::{ primitive::{ @@ -167,10 +168,28 @@ impl SseResponseTrait for GoogleAIClient { }; if let Some(choice) = event.candidates.get(0) { - if let Some(content) = choice.content.as_ref().and_then(|c| c.parts.get(0)) { + if let Some(content) = + choice.content.as_ref().and_then(|c| c.parts.get(0)) + { + btrace::log( + btrace::Level::INFO, + "googleai_client".to_string(), + "event".to_string(), + json!({ + "delta.content": content.text, + }), + ); inner.content += &content.text; } if let Some(FinishReason::Stop) = choice.finish_reason.as_ref() { + btrace::log( + btrace::Level::INFO, + "googleai_client".to_string(), + "event".to_string(), + json!({ + "finish_reason": "stop", + }), + ); inner.metadata.baml_is_complete = true; inner.metadata.finish_reason = Some(FinishReason::Stop.to_string()); } @@ -314,6 +333,23 @@ impl WithChat for GoogleAIClient { //non-streaming, complete response is returned let (response, system_now, instant_now) = match make_parsed_request::(self, either::Either::Right(prompt), false) + .btrace( + tracing_core::Level::INFO, + "googleai_chat", + json!({ + "prompt": prompt, + }), + |v| match v { + Ok((response, ..)) => json!({ + "llm.response": response, + }), + Err(e) => json!({ + "exception": { + "message": format!("{:?}", e), + }, + }), + }, + ) .await { Ok(v) => v, diff --git a/engine/baml-runtime/src/internal/llm_client/primitive/openai/openai_client.rs b/engine/baml-runtime/src/internal/llm_client/primitive/openai/openai_client.rs index 398fa0e52..6b0b86ec2 100644 --- a/engine/baml-runtime/src/internal/llm_client/primitive/openai/openai_client.rs +++ b/engine/baml-runtime/src/internal/llm_client/primitive/openai/openai_client.rs @@ -1,5 +1,6 @@ use std::collections::HashMap; +use crate::btrace::WithTraceContext; use crate::internal::llm_client::ResolveMediaUrls; use anyhow::Result; use baml_types::{BamlMap, BamlMedia, BamlMediaContent, BamlMediaType}; @@ -30,7 +31,7 @@ use crate::internal::llm_client::{ }; use crate::request::create_client; -use crate::RuntimeContext; +use crate::{btrace, RuntimeContext}; use eventsource_stream::Eventsource; use futures::StreamExt; @@ -164,6 +165,23 @@ impl WithChat for OpenAIClient { either::Either::Right(prompt), false, ) + .btrace( + tracing_core::Level::INFO, + "openai_chat", + json!({ + "prompt": prompt, + }), + |v| match v { + Ok((response, ..)) => json!({ + "llm.response": format!("{:?}", response), + }), + Err(e) => json!({ + "exception": { + "message": format!("{:?}", e), + }, + }), + }, + ) .await { Ok(v) => v, @@ -362,11 +380,20 @@ impl SseResponseTrait for OpenAIClient { }; if let Some(choice) = event.choices.first() { if let Some(content) = choice.delta.content.as_ref() { + btrace::log( + btrace::Level::INFO, + "openai_client".to_string(), + "event".to_string(), + json!({ + "delta.content": content, + }), + ); inner.content += content.as_str(); } inner.model = event.model; inner.metadata.finish_reason = choice.finish_reason.clone(); - inner.metadata.baml_is_complete = choice.finish_reason.as_ref().is_some_and(|s| s == "stop"); + inner.metadata.baml_is_complete = + choice.finish_reason.as_ref().is_some_and(|s| s == "stop"); } inner.latency = instant_start.elapsed(); if let Some(usage) = event.usage.as_ref() { diff --git a/engine/baml-runtime/src/internal/llm_client/primitive/vertex/vertex_client.rs b/engine/baml-runtime/src/internal/llm_client/primitive/vertex/vertex_client.rs index ec376f9c6..3825c658b 100644 --- a/engine/baml-runtime/src/internal/llm_client/primitive/vertex/vertex_client.rs +++ b/engine/baml-runtime/src/internal/llm_client/primitive/vertex/vertex_client.rs @@ -1,3 +1,4 @@ +use crate::btrace::WithTraceContext; use crate::client_registry::ClientProperty; use crate::internal::llm_client::traits::{ ToProviderMessage, ToProviderMessageExt, WithClientProperties, @@ -5,7 +6,7 @@ use crate::internal::llm_client::traits::{ use crate::internal::llm_client::ResolveMediaUrls; #[cfg(target_arch = "wasm32")] use crate::internal::wasm_jwt::{encode_jwt, JwtError}; -use crate::RuntimeContext; +use crate::{btrace, RuntimeContext}; use crate::{ internal::llm_client::{ primitive::{ @@ -209,6 +210,14 @@ impl SseResponseTrait for VertexClient { .as_ref() .and_then(|c| c.parts.first().map(|p| p.text.as_ref())) { + btrace::log( + btrace::Level::INFO, + "vertex_client".to_string(), + "event".to_string(), + json!({ + "delta.content": content, + }), + ); inner.content += content; } if let Some(FinishReason::Stop) = choice.finish_reason.as_ref() { @@ -403,6 +412,23 @@ impl WithChat for VertexClient { //non-streaming, complete response is returned let (response, system_now, instant_now) = match make_parsed_request::(self, either::Either::Right(prompt), false) + .btrace( + tracing_core::Level::INFO, + "vertex_chat", + json!({ + "prompt": prompt, + }), + |v| match v { + Ok((response, ..)) => json!({ + "llm.response": format!("{:?}", response), + }), + Err(e) => json!({ + "exception": { + "message": format!("{:?}", e), + }, + }), + }, + ) .await { Ok(v) => v, diff --git a/engine/baml-runtime/src/internal/llm_client/traits/mod.rs b/engine/baml-runtime/src/internal/llm_client/traits/mod.rs index 6e5172bb2..759020506 100644 --- a/engine/baml-runtime/src/internal/llm_client/traits/mod.rs +++ b/engine/baml-runtime/src/internal/llm_client/traits/mod.rs @@ -12,7 +12,7 @@ pub use self::{ completion::{WithCompletion, WithNoCompletion, WithStreamCompletion}, }; use super::{primitive::request::RequestBuilder, LLMResponse, ModelFeatures}; -use crate::{internal::llm_client::ResolveMediaUrls, RenderCurlSettings}; +use crate::{btrace::WithTraceContext, internal::llm_client::ResolveMediaUrls, RenderCurlSettings}; use crate::{internal::prompt_renderer::PromptRenderer, RuntimeContext}; use baml_types::{BamlMedia, BamlMediaContent, BamlMediaType, BamlValue, MediaBase64, MediaUrl}; use base64::{prelude::BASE64_STANDARD, Engine}; diff --git a/engine/baml-runtime/src/lib.rs b/engine/baml-runtime/src/lib.rs index 7fc08f4af..677267030 100644 --- a/engine/baml-runtime/src/lib.rs +++ b/engine/baml-runtime/src/lib.rs @@ -38,6 +38,7 @@ use internal_baml_core::configuration::Generator; use internal_baml_core::configuration::GeneratorOutputType; use on_log_event::LogEventCallbackSync; use runtime::InternalBamlRuntime; +use serde_json::json; use std::sync::OnceLock; #[cfg(not(target_arch = "wasm32"))] @@ -77,7 +78,6 @@ pub struct BamlRuntime { #[cfg(not(target_arch = "wasm32"))] pub async_runtime: Arc, pub trace_agent_tx: tokio::sync::mpsc::UnboundedSender, - pub trace_agent_rx: tokio::sync::mpsc::UnboundedReceiver, } impl BamlRuntime { @@ -140,6 +140,8 @@ impl BamlRuntime { .collect(); let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); + #[cfg(not(target_arch = "wasm32"))] + btrace::TracerThread::run(rx); Ok(BamlRuntime { inner: InternalBamlRuntime::from_directory(&path)?, @@ -148,7 +150,6 @@ impl BamlRuntime { #[cfg(not(target_arch = "wasm32"))] async_runtime: Self::get_tokio_singleton()?, trace_agent_tx: tx, - trace_agent_rx: rx, }) } @@ -162,6 +163,8 @@ impl BamlRuntime { .map(|(k, v)| (k.as_ref().to_string(), v.as_ref().to_string())) .collect(); let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); + #[cfg(not(target_arch = "wasm32"))] + btrace::TracerThread::run(rx); Ok(BamlRuntime { inner: InternalBamlRuntime::from_file_content(root_path, files)?, @@ -170,7 +173,6 @@ impl BamlRuntime { #[cfg(not(target_arch = "wasm32"))] async_runtime: Self::get_tokio_singleton()?, trace_agent_tx: tx, - trace_agent_rx: rx, }) } @@ -250,6 +252,7 @@ impl BamlRuntime { btrace::TraceContext { scope: btrace::InstrumentationScope::Root, tx: self.trace_agent_tx.clone(), + tags: Default::default(), }, )?; let (response_res, span_uuid) = stream.run(on_event, ctx, None, None).await; @@ -333,6 +336,7 @@ impl BamlRuntime { let tctx = btrace::TraceContext { scope: btrace::InstrumentationScope::Root, tx: self.trace_agent_tx.clone(), + tags: Default::default(), }; let response = match ctx.create_ctx(tb, cb) { Ok(rctx) => { @@ -341,7 +345,12 @@ impl BamlRuntime { tctx, self.inner .call_function_impl(function_name.clone(), params, rctx) - .btrace(format!("baml_function::{}", function_name)), + .btrace( + tracing_core::Level::INFO, + format!("baml_function::{}", function_name), + json!({}), + |_| serde_json::Value::Null, + ), ) .await } @@ -382,6 +391,14 @@ impl BamlRuntime { btrace::TraceContext { scope: btrace::InstrumentationScope::Root, tx: self.trace_agent_tx.clone(), + tags: json!({ + "user.id": "123", + "user.name": "John Doe", + "user.email": "john.doe@example.com", + }) + .as_object() + .unwrap() + .clone(), }, ) } diff --git a/engine/baml-runtime/src/types/stream.rs b/engine/baml-runtime/src/types/stream.rs index 4d0ec0ab9..fbc34866f 100644 --- a/engine/baml-runtime/src/types/stream.rs +++ b/engine/baml-runtime/src/types/stream.rs @@ -1,6 +1,7 @@ use anyhow::Result; use internal_baml_core::ir::repr::IntermediateRepr; +use serde_json::json; use std::sync::Arc; @@ -96,24 +97,43 @@ impl FunctionResultStream { let rctx = ctx.create_ctx(tb, cb); let res = match rctx { Ok(rctx) => { - let (history, _) = btrace::BAML_TRACE_CTX + btrace::BAML_TRACE_CTX .scope( self.tctx.clone(), - orchestrate_stream( - local_orchestrator, - self.ir.as_ref(), - &rctx, - &self.renderer, - &baml_types::BamlValue::Map(local_params), - |content| self.renderer.parse(content, true), - |content| self.renderer.parse(content, false), - on_event, - ) - .btrace(format!("baml_stream_function::{}", self.function_name)), + async { + let (history, _) = orchestrate_stream( + local_orchestrator, + self.ir.as_ref(), + &rctx, + &self.renderer, + &baml_types::BamlValue::Map(local_params), + |content| self.renderer.parse(content, true), + |content| self.renderer.parse(content, false), + on_event, + ) + .await; + FunctionResult::new_chain(history) + } + .btrace( + tracing::Level::INFO, + format!("baml_stream_function::{}", self.function_name), + json!({}), + |result| match result { + Ok(result) => match result.parsed_content() { + Ok(content) => json!({ + "result": format!("TODO: actually return this as a json object: {}", content.to_string()), + }), + Err(e) => json!({ + "exception": format!("parse error: {}", e) + }), + }, + Err(e) => json!({ + "exception": e.to_string() + }), + }, + ), ) - .await; - - FunctionResult::new_chain(history) + .await } Err(e) => Err(e), }; diff --git a/engine/btrace/Cargo.toml b/engine/btrace/Cargo.toml new file mode 100644 index 000000000..ba936e60b --- /dev/null +++ b/engine/btrace/Cargo.toml @@ -0,0 +1,53 @@ +[package] +edition = "2021" +name = "btrace" +version.workspace = true +authors.workspace = true +description.workspace = true +license-file.workspace = true + +[lib] +crate-type = ["cdylib", "rlib"] + +[package.metadata.rustflags] +RSTEST_TIMEOUT = "10" + +[lints.rust] +dead_code = "deny" +unused_imports = "deny" +unused_variables = "deny" + +[dependencies] +anyhow.workspace = true +base64.workspace = true +bytes.workspace = true +tracing-core = { version = "0.1.31" } +serde_json.workspace = true +uuid = { version = "1.8", features = ["v7", "js"] } +web-time.workspace = true + + +[target.'cfg(target_arch = "wasm32")'.dependencies] +futures-timer = { version = "3.0.3", features = ["wasm-bindgen"] } +js-sys = "0.3.69" +reqwest = { version = "0.12.5", features = ["stream", "json"] } +tokio = { version = "1", features = ["sync"] } +# WARNING: Do not add serde-serialize feature to wasm-bindgen. +# It may produce a dependency cycle in projects that use wasm and import baml. +wasm-bindgen = "^0.2.74" +wasm-bindgen-futures = "0.4" +web-sys = { version = "0.3.69", features = [ + "Crypto", + "CryptoKey", + "Headers", + "Request", + "RequestInit", + "Response", + "RequestMode", + "SubtleCrypto", + "Window", +] } + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +hostname = "0.3.1" +tokio = { version = "1", features = ["full"] } diff --git a/engine/btrace/src/lib.rs b/engine/btrace/src/lib.rs new file mode 100644 index 000000000..38c8011fb --- /dev/null +++ b/engine/btrace/src/lib.rs @@ -0,0 +1,264 @@ +#[cfg(not(target_arch = "wasm32"))] +mod tracer_thread; + +#[cfg(not(target_arch = "wasm32"))] +pub use tracer_thread::TracerThread; +pub use tracing_core::Level; + +type TraceTags = serde_json::Map; + +#[derive(Clone)] +pub struct TraceContext { + /// The scope used for all spans/logs within this context. + pub scope: InstrumentationScope, + /// The channel used to send trace events to the trace agent. + pub tx: tokio::sync::mpsc::UnboundedSender, + pub tags: TraceTags, +} + +impl TraceContext { + fn child_ctx(&self) -> (Self, SpanId) { + let new_uuid = uuid::Uuid::now_v7(); + let span_id = match &self.scope { + InstrumentationScope::Root => SpanId(vec![new_uuid]), + InstrumentationScope::Child { parent_span_id } => { + let mut parent_span_id = parent_span_id.clone(); + parent_span_id.0.push(new_uuid); + parent_span_id + } + }; + ( + Self { + scope: InstrumentationScope::Child { + parent_span_id: span_id.clone(), + }, + tx: self.tx.clone(), + tags: self.tags.clone(), + }, + span_id, + ) + } +} + +#[cfg(not(target_arch = "wasm32"))] +tokio::task_local! { + pub static BAML_TRACE_CTX: TraceContext; +} +#[cfg(target_arch = "wasm32")] +thread_local! { + pub static BAML_TRACE_CTX: TraceContext = TraceContext { + scope: InstrumentationScope::Root, + tx: tokio::sync::mpsc::unbounded_channel().0, + tags: serde_json::Map::new(), + }; +} + +#[derive(Clone, Debug)] +pub struct SpanId(Vec); + +impl Default for SpanId { + fn default() -> Self { + Self(vec![uuid::Uuid::now_v7()]) + } +} + +#[derive(Clone, Debug)] +pub enum InstrumentationScope { + Root, + Child { parent_span_id: SpanId }, +} + +pub enum TraceEvent { + SpanStart(TraceSpanStart), + SpanEnd(TraceSpanEnd), + Log(TraceLog), +} + +#[derive(Clone, Debug)] +pub struct TraceMetadata { + /// human-readable callsite identifier, e.g. "ExtractResume" or "openai/gpt-4o/chat" + callsite: String, + /// verbosity level + verbosity: tracing_core::Level, +} + +// ------------------------------------------------------------------------------------------------- + +pub struct TraceSpanStart { + span_id: SpanId, + meta: TraceMetadata, + start_time: web_time::Instant, + fields: serde_json::Map, +} + +#[derive(Debug)] +pub struct TraceSpanEnd { + span_id: SpanId, + meta: TraceMetadata, + start_time: web_time::Instant, + duration: web_time::Duration, + fields: serde_json::Map, +} + +pub struct TraceLog { + span_id: SpanId, + meta: TraceMetadata, + start_time: web_time::Instant, + msg: String, + tags: serde_json::Map, +} + +// ------------------------------------------------------------------------------------------------- + +impl TraceSpanStart { + pub fn new( + verbosity: tracing_core::Level, + callsite: String, + fields: serde_json::Value, + ) -> Self { + Self { + span_id: Default::default(), + start_time: web_time::Instant::now(), + meta: TraceMetadata { + callsite, + verbosity, + }, + fields: match fields { + serde_json::Value::Object(o) => o, + _ => serde_json::Map::new(), + }, + } + } +} + +pub fn log( + verbosity: tracing_core::Level, + callsite: String, + msg: String, + fields: serde_json::Value, +) { + let Ok(ctx) = BAML_TRACE_CTX.try_with(|ctx| ctx.clone()) else { + return; + }; + let mut tags = ctx.tags.clone(); + match fields { + serde_json::Value::Object(o) => tags.extend(o), + _ => (), + } + let _ = ctx.tx.send(TraceEvent::Log(TraceLog { + span_id: match ctx.scope { + InstrumentationScope::Root => SpanId(vec![]), + InstrumentationScope::Child { parent_span_id } => parent_span_id, + }, + start_time: web_time::Instant::now(), + msg, + meta: TraceMetadata { + callsite, + verbosity, + }, + tags, + })); +} + +macro_rules! impl_trace_scope { + ($new_ctx:ident, $verbosity:ident, $name:ident, $fields:ident, $wrapped_fn:expr, $unwrapped_fn:expr, $then:expr) => {{ + let curr_ctx = BAML_TRACE_CTX.try_with(|ctx| ctx.clone()); + + match curr_ctx { + Ok(ctx) => { + let ($new_ctx, span_id) = ctx.child_ctx(); + + let name = $name.into(); + let start_time = web_time::Instant::now(); + let meta = TraceMetadata { + callsite: name, + verbosity: $verbosity, + }; + let tags = $new_ctx.tags.clone(); + let span = TraceSpanStart { + span_id: span_id.clone(), + start_time, + meta: meta.clone(), + fields: { + let mut fields = $new_ctx.tags.clone(); + match $fields { + serde_json::Value::Object(o) => fields.extend(o), + _ => (), + } + fields + }, + }; + let _ = ctx.tx.send(TraceEvent::SpanStart(span)); + + let retval = $wrapped_fn; + + let span = TraceSpanEnd { + span_id, + meta, + start_time, + duration: start_time.elapsed(), + fields: { + let mut fields = tags; + match $then(&retval) { + serde_json::Value::Object(o) => fields.extend(o), + _ => (), + } + fields + }, + }; + let _ = ctx.tx.send(TraceEvent::SpanEnd(span)); + retval + } + Err(_) => $unwrapped_fn, + } + }}; +} + +pub fn btrace( + verbosity: tracing_core::Level, + name: impl Into, + fields: serde_json::Value, + f: F, + then: G, +) -> R +where + F: FnOnce() -> R, + G: FnOnce(&R) -> serde_json::Value, +{ + impl_trace_scope!( + new_ctx, + verbosity, + name, + fields, + BAML_TRACE_CTX.sync_scope(new_ctx, f), + f(), + then + ) +} + +pub trait WithTraceContext: Sized + std::future::Future { + #[allow(async_fn_in_trait)] + async fn btrace( + self, + verbosity: tracing_core::Level, + name: impl Into, + fields: serde_json::Value, + then: F, + ) -> ::Output + where + F: FnOnce(&::Output) -> serde_json::Value, + { + impl_trace_scope!( + new_ctx, + verbosity, + name, + fields, + BAML_TRACE_CTX.scope(new_ctx, self).await, + self.await, + then + ) + } +} + +// Auto-implement the trait for all futures +impl WithTraceContext for F where F: std::future::Future {} diff --git a/engine/btrace/tests.rs b/engine/btrace/tests.rs new file mode 100644 index 000000000..dfed4997a --- /dev/null +++ b/engine/btrace/tests.rs @@ -0,0 +1,176 @@ +// Run from the baml-schema-wasm folder with: +// wasm-pack test --node +#[cfg(target_arch = "wasm32")] +#[cfg(test)] +mod tests { + use std::collections::HashMap; + + use baml_schema_build::runtime_wasm::{WasmProject, WasmRuntime}; + + use baml_runtime::{BamlRuntime, RuntimeContext}; + use serde_wasm_bindgen::to_value; + use wasm_bindgen::JsValue; + use wasm_bindgen_test::*; + use wasm_logger; + + // instantiate logger + + // wasm_bindgen_test_configure!(run_in_browser); + + /// Sample BAML content for testing. + fn sample_baml_content() -> String { + r##" + + + class Email { + subject string + body string + from_address string + } + + enum OrderStatus { + ORDERED + SHIPPED + DELIVERED + CANCELLED + } + + class OrderInfo { + order_status OrderStatus + tracking_number string? + estimated_arrival_date string? + } + + client GPT4Turbo { + provider baml-openai-chat + options { + model gpt-4-1106-preview + api_key env.OPENAI_API_KEY + } + } + + function GetOrderInfo(input: string) -> OrderInfo { + client GPT4Turbo + prompt #" + Extract this info from the email in JSON format: + Before you output the JSON, please explain your + reasoning step-by-step. Here is an example on how to do this: + 'If we think step by step we can see that ... + therefore the output JSON is: + { + ... the json schema ... + }' + "# + } + "## + .to_string() + } + + /// Tests the `new` constructor for successful creation with BAML content. + #[wasm_bindgen_test] + fn test_new_project_with_baml_content() { + wasm_logger::init(wasm_logger::Config::new(log::Level::Info)); + let mut files = HashMap::new(); + files.insert("main.baml".to_string(), sample_baml_content()); + let files_js = to_value(&files).unwrap(); + let project = WasmProject::new("baml_src", files_js); + assert!(project.is_ok()); + } + + /// Tests retrieving BAML files correctly with `files` method. + #[wasm_bindgen_test] + fn test_files_method_with_baml() { + let mut files = HashMap::new(); + files.insert("main.baml".to_string(), sample_baml_content()); + let files_js = to_value(&files).unwrap(); + let project = WasmProject::new("baml_src", files_js) + .map_err(JsValue::from) + .unwrap(); + assert_eq!(project.files().len(), 1); + } + + /// Tests updating and removing BAML files. + #[wasm_bindgen_test] + fn test_update_and_remove_baml_file() { + wasm_logger::init(wasm_logger::Config::new(log::Level::Info)); + + let mut files = HashMap::new(); + files.insert("main.baml".to_string(), sample_baml_content()); + let files_js = to_value(&files).unwrap(); + let mut project = WasmProject::new("baml_src", files_js) + .map_err(JsValue::from) + .unwrap(); + + // Update BAML file + let updated_content = "// A COMMENT".to_string(); + project.update_file("main.baml", Some(updated_content.clone())); + let project_files = project.files(); + assert!(project + .files() + .contains(&"main.bamlBAML_PATH_SPLTTER// A COMMENT".to_string())); + + // Remove BAML file + project.update_file("main.baml", None); + assert!(project.files().is_empty()); + } + + #[wasm_bindgen_test] + fn test_diagnostics_no_errors() { + wasm_logger::init(wasm_logger::Config::new(log::Level::Info)); + + let mut files = HashMap::new(); + files.insert("error.baml".to_string(), sample_baml_content()); + let files_js = to_value(&files).unwrap(); + let project = WasmProject::new("baml_src", files_js) + .map_err(JsValue::from) + .unwrap(); + + let env_vars = [("OPENAI_API_KEY", "12345")] + .iter() + .cloned() + .collect::>(); + let env_vars_js = to_value(&env_vars).unwrap(); + + let current_runtime = project.runtime(env_vars_js).map_err(JsValue::from).unwrap(); + let diagnostics = project.diagnostics(¤t_runtime); + + assert!(diagnostics.errors().is_empty()); + } + + #[wasm_bindgen_test] + fn test_diagnostics_no_errors_2() { + wasm_logger::init(wasm_logger::Config::new(log::Level::Info)); + let sample_baml_content = r##" +function PredictAgeBare(inp: string @assert(big_enough, {{this|length > 1}}) ) -> int { + client "openai/gpt-4o" + prompt #" + Using your understanding of the historical popularity + of names, predict the age of a person with the name + {{ inp }} in years. Also predict their genus and + species. It's Homo sapiens (with exactly that spelling). + + {{ctx.output_format}} + "# +} + + "##; + let mut files = HashMap::new(); + files.insert("error.baml".to_string(), sample_baml_content.to_string()); + let files_js = to_value(&files).unwrap(); + let project = WasmProject::new("baml_src", files_js) + .map_err(JsValue::from) + .unwrap(); + + let env_vars = [("OPENAI_API_KEY", "12345")] + .iter() + .cloned() + .collect::>(); + let env_vars_js = to_value(&env_vars).unwrap(); + + let current_runtime = project.runtime(env_vars_js).map_err(JsValue::from).unwrap(); + let diagnostics = project.diagnostics(¤t_runtime); + current_runtime.list_functions(); + + assert!(diagnostics.errors().is_empty()); + } +} diff --git a/engine/btrace/tracer_thread.rs b/engine/btrace/tracer_thread.rs new file mode 100644 index 000000000..46492868f --- /dev/null +++ b/engine/btrace/tracer_thread.rs @@ -0,0 +1,69 @@ +use crate::btrace::InstrumentationScope; + +use super::TraceEvent; +pub struct TracerThread { + rx: tokio::sync::mpsc::UnboundedReceiver, +} + +impl TracerThread { + pub fn new(rx: tokio::sync::mpsc::UnboundedReceiver) -> Self { + Self { rx } + } + + pub fn run(rx: tokio::sync::mpsc::UnboundedReceiver) { + std::thread::spawn(move || { + let rt = tokio::runtime::Runtime::new().unwrap(); + rt.block_on(Self { rx }.run_impl()); + }); + } + + pub async fn run_impl(&mut self) { + while let Some(event) = self.rx.recv().await { + match event { + TraceEvent::SpanStart(span) => { + println!( + "--> [{} / {}]: {} {}", + span.span_id + .0 + .iter() + .map(|id| id.to_string()) + .collect::>() + .join(" > "), + span.meta.verbosity, + span.meta.callsite, + serde_json::to_string(&span.fields).unwrap_or("???".to_string()), + ); + } + TraceEvent::SpanEnd(span) => { + println!( + "<-- [{} / {}]: {} {}", + span.span_id + .0 + .iter() + .map(|id| id.to_string()) + .collect::>() + .join(" > "), + span.meta.verbosity, + span.meta.callsite, + serde_json::to_string(&span.fields).unwrap_or("???".to_string()), + ); + } + TraceEvent::Log(log) => { + println!( + "log [{} / {}]: {} {} {}", + log.span_id + .0 + .iter() + .map(|id| id.to_string()) + .collect::>() + .join(" > "), + log.meta.verbosity, + log.meta.callsite, + log.msg, + serde_json::to_string(&log.tags).unwrap_or("???".to_string()), + ); + } + } + } + } +} diff --git a/engine/language_client_typescript/native.js b/engine/language_client_typescript/native.js index aa5cd0994..44f680a98 100644 --- a/engine/language_client_typescript/native.js +++ b/engine/language_client_typescript/native.js @@ -336,7 +336,7 @@ if (!nativeBinding || process.env.NAPI_RS_FORCE_WASI) { nativeBinding = require('./baml.wasi.cjs') } catch (err) { if (process.env.NAPI_RS_FORCE_WASI) { - loadErrors.push(err) + console.error(err) } } if (!nativeBinding) { @@ -344,7 +344,7 @@ if (!nativeBinding || process.env.NAPI_RS_FORCE_WASI) { nativeBinding = require('@boundaryml/baml-wasm32-wasi') } catch (err) { if (process.env.NAPI_RS_FORCE_WASI) { - loadErrors.push(err) + console.error(err) } } } diff --git a/integ-tests/baml_src/test-files/providers/providers.baml b/integ-tests/baml_src/test-files/providers/providers.baml index d8225b21d..8d02fc7c4 100644 --- a/integ-tests/baml_src/test-files/providers/providers.baml +++ b/integ-tests/baml_src/test-files/providers/providers.baml @@ -65,7 +65,7 @@ function TestAwsInvalidRegion(input: string) -> string { function TestOpenAIShorthand(input: string) -> string { client "openai/gpt-4o-mini" prompt #" - Write a nice short story about {{ input }} + Write a nice short haiku about {{ input }} "# } diff --git a/integ-tests/python/baml_client/inlinedbaml.py b/integ-tests/python/baml_client/inlinedbaml.py index b07375e7b..fb87493f2 100644 --- a/integ-tests/python/baml_client/inlinedbaml.py +++ b/integ-tests/python/baml_client/inlinedbaml.py @@ -86,7 +86,7 @@ "test-files/functions/prompts/no-chat-messages.baml": "\n\nfunction PromptTestClaude(input: string) -> string {\n client Sonnet\n prompt #\"\n Tell me a haiku about {{ input }}\n \"#\n}\n\n\nfunction PromptTestStreaming(input: string) -> string {\n client GPT35\n prompt #\"\n Tell me a short story about {{ input }}\n \"#\n}\n\ntest TestName {\n functions [PromptTestStreaming]\n args {\n input #\"\n hello world\n \"#\n }\n}\n", "test-files/functions/prompts/with-chat-messages.baml": "\nfunction PromptTestOpenAIChat(input: string) -> string {\n client GPT35\n prompt #\"\n {{ _.role(\"system\") }}\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestOpenAIChatNoSystem(input: string) -> string {\n client GPT35\n prompt #\"\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestClaudeChat(input: string) -> string {\n client Claude\n prompt #\"\n {{ _.role(\"system\") }}\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestClaudeChatNoSystem(input: string) -> string {\n client Claude\n prompt #\"\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\ntest TestSystemAndNonSystemChat1 {\n functions [PromptTestClaude, PromptTestOpenAI, PromptTestOpenAIChat, PromptTestOpenAIChatNoSystem, PromptTestClaudeChat, PromptTestClaudeChatNoSystem]\n args {\n input \"cats\"\n }\n}\n\ntest TestSystemAndNonSystemChat2 {\n functions [PromptTestClaude, PromptTestOpenAI, PromptTestOpenAIChat, PromptTestOpenAIChatNoSystem, PromptTestClaudeChat, PromptTestClaudeChatNoSystem]\n args {\n input \"lion\"\n }\n}", "test-files/functions/v2/basic.baml": "\n\nfunction ExtractResume2(resume: string) -> Resume {\n client GPT4\n prompt #\"\n {{ _.role('system') }}\n\n Extract the following information from the resume:\n\n Resume:\n <<<<\n {{ resume }}\n <<<<\n\n Output JSON schema:\n {{ ctx.output_format }}\n\n JSON:\n \"#\n}\n\n\nclass WithReasoning {\n value string\n reasoning string @description(#\"\n Why the value is a good fit.\n \"#)\n}\n\n\nclass SearchParams {\n dateRange int? @description(#\"\n In ISO duration format, e.g. P1Y2M10D.\n \"#)\n location string[]\n jobTitle WithReasoning? @description(#\"\n An exact job title, not a general category.\n \"#)\n company WithReasoning? @description(#\"\n The exact name of the company, not a product or service.\n \"#)\n description WithReasoning[] @description(#\"\n Any specific projects or features the user is looking for.\n \"#)\n tags (Tag | string)[]\n}\n\nenum Tag {\n Security\n AI\n Blockchain\n}\n\nfunction GetQuery(query: string) -> SearchParams {\n client GPT4\n prompt #\"\n Extract the following information from the query:\n\n Query:\n <<<<\n {{ query }}\n <<<<\n\n OUTPUT_JSON_SCHEMA:\n {{ ctx.output_format }}\n\n Before OUTPUT_JSON_SCHEMA, list 5 intentions the user may have.\n --- EXAMPLES ---\n 1. \n 2. \n 3. \n 4. \n 5. \n\n {\n ... // OUTPUT_JSON_SCHEMA\n }\n \"#\n}\n\nclass RaysData {\n dataType DataType\n value Resume | Event\n}\n\nenum DataType {\n Resume\n Event\n}\n\nclass Event {\n title string\n date string\n location string\n description string\n}\n\nfunction GetDataType(text: string) -> RaysData {\n client GPT4\n prompt #\"\n Extract the relevant info.\n\n Text:\n <<<<\n {{ text }}\n <<<<\n\n Output JSON schema:\n {{ ctx.output_format }}\n\n JSON:\n \"#\n}", - "test-files/providers/providers.baml": "function TestAnthropic(input: string) -> string {\n client Claude\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestOpenAI(input: string) -> string {\n client GPT35\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestOpenAILegacyProvider(input: string) -> string {\n client GPT35LegacyProvider\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestAzure(input: string) -> string {\n client GPT35Azure\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestOllama(input: string) -> string {\n client Ollama\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestGemini(input: string) -> string {\n client Gemini\n prompt #\"\n Write a nice short story about {{ input }}\n \"#\n}\n\nfunction TestVertex(input: string) -> string {\n client Vertex\n prompt #\"\n Write a nice short story about {{ input }}\n \"#\n\n}\n\nfunction TestAws(input: string) -> string {\n client AwsBedrock\n prompt #\"\n Write a nice short story about {{ input }}\n \"#\n}\n\nfunction TestAwsInvalidRegion(input: string) -> string {\n client AwsBedrockInvalidRegion\n prompt #\"\n Write a nice short story about {{ input }}\n \"#\n}\n\nfunction TestOpenAIShorthand(input: string) -> string {\n client \"openai/gpt-4o-mini\"\n prompt #\"\n Write a nice short story about {{ input }}\n \"#\n}\n\nfunction TestAnthropicShorthand(input: string) -> string {\n client \"anthropic/claude-3-haiku-20240307\"\n prompt #\"\n Write a nice short story about {{ input }}\n \"#\n}\n\ntest TestProvider {\n functions [\n TestAnthropic, TestVertex, PromptTestOpenAI, TestAzure, TestOllama, TestGemini, TestAws,\n TestAwsInvalidRegion,\n TestOpenAIShorthand,\n TestAnthropicShorthand\n ]\n args {\n input \"Donkey kong and peanut butter\"\n }\n}\n\n\nfunction TestCaching(input: string, not_cached: string) -> string {\n client ClaudeWithCaching\n prompt #\"\n {{ _.role('system', cache_control={\"type\": \"ephemeral\"}) }}\n Generate the following story\n {{ input }}\n\n {# Haiku require 2048 tokens to cache -#}\n {{ input }}\n\n {{ _.role('user') }}\n {{ not_cached }}\n \"#\n}\n\ntest TestName {\n functions [TestCaching]\n args {\n input #\"\nIn a near-future society where dreams have become a tradable commodity and shared experience, a lonely and socially awkward teenager named Alex discovers they possess a rare and powerful ability to not only view but also manipulate the dreams of others. Initially thrilled by this newfound power, Alex begins subtly altering the dreams of classmates and family members, helping them overcome fears, boost confidence, or experience fantastical adventures. As Alex's skills grow, so does their influence. They start selling premium dream experiences on the black market, crafting intricate and addictive dreamscapes for wealthy clients. However, the line between dream and reality begins to blur for those exposed to Alex's creations. Some clients struggle to differentiate between their true memories and the artificial ones implanted by Alex's dream manipulation.\n\nComplications arise when a mysterious government agency takes notice of Alex's unique abilities. They offer Alex a chance to use their gift for \"the greater good,\" hinting at applications in therapy, criminal rehabilitation, and even national security. Simultaneously, an underground resistance movement reaches out, warning Alex about the dangers of dream manipulation and the potential for mass control and exploitation. Caught between these opposing forces, Alex must navigate a complex web of ethical dilemmas. They grapple with questions of free will, the nature of consciousness, and the responsibility that comes with having power over people's minds. As the consequences of their actions spiral outward, affecting the lives of loved ones and strangers alike, Alex is forced to confront the true nature of their ability and decide how—or if—it should be used.\n\nThe story explores themes of identity, the subconscious mind, the ethics of technology, and the power of imagination. It delves into the potential consequences of a world where our most private thoughts and experiences are no longer truly our own, and examines the fine line between helping others and manipulating them for personal gain or a perceived greater good. The narrative further expands on the societal implications of such abilities, questioning the moral boundaries of altering consciousness and the potential for abuse in a world where dreams can be commodified. It challenges the reader to consider the impact of technology on personal autonomy and the ethical responsibilities of those who wield such power.\n\nAs Alex's journey unfolds, they encounter various individuals whose lives have been touched by their dream manipulations, each presenting a unique perspective on the ethical quandaries at hand. From a classmate who gains newfound confidence to a wealthy client who becomes addicted to the dreamscapes, the ripple effects of Alex's actions are profound and far-reaching. The government agency's interest in Alex's abilities raises questions about the potential for state control and surveillance, while the resistance movement highlights the dangers of unchecked power and the importance of safeguarding individual freedoms.\n\nUltimately, Alex's story is one of self-discovery and moral reckoning, as they must decide whether to embrace their abilities for personal gain, align with the government's vision of a controlled utopia, or join the resistance in their fight for freedom and autonomy. The narrative invites readers to reflect on the nature of reality, the boundaries of human experience, and the ethical implications of a world where dreams are no longer private sanctuaries but shared and manipulated commodities. It also explores the psychological impact on Alex, who must deal with the burden of knowing the intimate fears and desires of others, and the isolation that comes from being unable to share their own dreams without altering them.\n\nThe story further examines the technological advancements that have made dream manipulation possible, questioning the role of innovation in society and the potential for both progress and peril. It considers the societal divide between those who can afford to buy enhanced dream experiences and those who cannot, highlighting issues of inequality and access. As Alex becomes more entangled in the web of their own making, they must confront the possibility that their actions could lead to unintended consequences, not just for themselves but for the fabric of society as a whole.\n\nIn the end, Alex's journey is a cautionary tale about the power of dreams and the responsibilities that come with wielding such influence. It serves as a reminder of the importance of ethical considerations in the face of technological advancement and the need to balance innovation with humanity. The story leaves readers pondering the true cost of a world where dreams are no longer sacred, and the potential for both wonder and danger in the uncharted territories of the mind. But it's also a story about the power of imagination and the potential for change, even in a world where our deepest thoughts are no longer our own. And it's a story about the power of choice, and the importance of fighting for the freedom to dream.\n\nIn conclusion, this story is a reflection on the power of dreams and the responsibilities that come with wielding such influence. It serves as a reminder of the importance of ethical considerations in the face of technological advancement and the need to balance innovation with humanity. The story leaves readers pondering the true cost of a world where dreams are no longer sacred, and the potential for both wonder and danger in the uncharted territories of the mind. But it's also a story about the power of imagination and the potential for change, even in a world where our deepest thoughts are no longer our own. And it's a story about the power of choice, and the importance of fighting for the freedom to dream.\n \"#\n not_cached #\"\n hello world\n \"#\n }\n}\n", + "test-files/providers/providers.baml": "function TestAnthropic(input: string) -> string {\n client Claude\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestOpenAI(input: string) -> string {\n client GPT35\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestOpenAILegacyProvider(input: string) -> string {\n client GPT35LegacyProvider\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestAzure(input: string) -> string {\n client GPT35Azure\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestOllama(input: string) -> string {\n client Ollama\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestGemini(input: string) -> string {\n client Gemini\n prompt #\"\n Write a nice short story about {{ input }}\n \"#\n}\n\nfunction TestVertex(input: string) -> string {\n client Vertex\n prompt #\"\n Write a nice short story about {{ input }}\n \"#\n\n}\n\nfunction TestAws(input: string) -> string {\n client AwsBedrock\n prompt #\"\n Write a nice short story about {{ input }}\n \"#\n}\n\nfunction TestAwsInvalidRegion(input: string) -> string {\n client AwsBedrockInvalidRegion\n prompt #\"\n Write a nice short story about {{ input }}\n \"#\n}\n\nfunction TestOpenAIShorthand(input: string) -> string {\n client \"openai/gpt-4o-mini\"\n prompt #\"\n Write a nice short haiku about {{ input }}\n \"#\n}\n\nfunction TestAnthropicShorthand(input: string) -> string {\n client \"anthropic/claude-3-haiku-20240307\"\n prompt #\"\n Write a nice short story about {{ input }}\n \"#\n}\n\ntest TestProvider {\n functions [\n TestAnthropic, TestVertex, PromptTestOpenAI, TestAzure, TestOllama, TestGemini, TestAws,\n TestAwsInvalidRegion,\n TestOpenAIShorthand,\n TestAnthropicShorthand\n ]\n args {\n input \"Donkey kong and peanut butter\"\n }\n}\n\n\nfunction TestCaching(input: string, not_cached: string) -> string {\n client ClaudeWithCaching\n prompt #\"\n {{ _.role('system', cache_control={\"type\": \"ephemeral\"}) }}\n Generate the following story\n {{ input }}\n\n {# Haiku require 2048 tokens to cache -#}\n {{ input }}\n\n {{ _.role('user') }}\n {{ not_cached }}\n \"#\n}\n\ntest TestName {\n functions [TestCaching]\n args {\n input #\"\nIn a near-future society where dreams have become a tradable commodity and shared experience, a lonely and socially awkward teenager named Alex discovers they possess a rare and powerful ability to not only view but also manipulate the dreams of others. Initially thrilled by this newfound power, Alex begins subtly altering the dreams of classmates and family members, helping them overcome fears, boost confidence, or experience fantastical adventures. As Alex's skills grow, so does their influence. They start selling premium dream experiences on the black market, crafting intricate and addictive dreamscapes for wealthy clients. However, the line between dream and reality begins to blur for those exposed to Alex's creations. Some clients struggle to differentiate between their true memories and the artificial ones implanted by Alex's dream manipulation.\n\nComplications arise when a mysterious government agency takes notice of Alex's unique abilities. They offer Alex a chance to use their gift for \"the greater good,\" hinting at applications in therapy, criminal rehabilitation, and even national security. Simultaneously, an underground resistance movement reaches out, warning Alex about the dangers of dream manipulation and the potential for mass control and exploitation. Caught between these opposing forces, Alex must navigate a complex web of ethical dilemmas. They grapple with questions of free will, the nature of consciousness, and the responsibility that comes with having power over people's minds. As the consequences of their actions spiral outward, affecting the lives of loved ones and strangers alike, Alex is forced to confront the true nature of their ability and decide how—or if—it should be used.\n\nThe story explores themes of identity, the subconscious mind, the ethics of technology, and the power of imagination. It delves into the potential consequences of a world where our most private thoughts and experiences are no longer truly our own, and examines the fine line between helping others and manipulating them for personal gain or a perceived greater good. The narrative further expands on the societal implications of such abilities, questioning the moral boundaries of altering consciousness and the potential for abuse in a world where dreams can be commodified. It challenges the reader to consider the impact of technology on personal autonomy and the ethical responsibilities of those who wield such power.\n\nAs Alex's journey unfolds, they encounter various individuals whose lives have been touched by their dream manipulations, each presenting a unique perspective on the ethical quandaries at hand. From a classmate who gains newfound confidence to a wealthy client who becomes addicted to the dreamscapes, the ripple effects of Alex's actions are profound and far-reaching. The government agency's interest in Alex's abilities raises questions about the potential for state control and surveillance, while the resistance movement highlights the dangers of unchecked power and the importance of safeguarding individual freedoms.\n\nUltimately, Alex's story is one of self-discovery and moral reckoning, as they must decide whether to embrace their abilities for personal gain, align with the government's vision of a controlled utopia, or join the resistance in their fight for freedom and autonomy. The narrative invites readers to reflect on the nature of reality, the boundaries of human experience, and the ethical implications of a world where dreams are no longer private sanctuaries but shared and manipulated commodities. It also explores the psychological impact on Alex, who must deal with the burden of knowing the intimate fears and desires of others, and the isolation that comes from being unable to share their own dreams without altering them.\n\nThe story further examines the technological advancements that have made dream manipulation possible, questioning the role of innovation in society and the potential for both progress and peril. It considers the societal divide between those who can afford to buy enhanced dream experiences and those who cannot, highlighting issues of inequality and access. As Alex becomes more entangled in the web of their own making, they must confront the possibility that their actions could lead to unintended consequences, not just for themselves but for the fabric of society as a whole.\n\nIn the end, Alex's journey is a cautionary tale about the power of dreams and the responsibilities that come with wielding such influence. It serves as a reminder of the importance of ethical considerations in the face of technological advancement and the need to balance innovation with humanity. The story leaves readers pondering the true cost of a world where dreams are no longer sacred, and the potential for both wonder and danger in the uncharted territories of the mind. But it's also a story about the power of imagination and the potential for change, even in a world where our deepest thoughts are no longer our own. And it's a story about the power of choice, and the importance of fighting for the freedom to dream.\n\nIn conclusion, this story is a reflection on the power of dreams and the responsibilities that come with wielding such influence. It serves as a reminder of the importance of ethical considerations in the face of technological advancement and the need to balance innovation with humanity. The story leaves readers pondering the true cost of a world where dreams are no longer sacred, and the potential for both wonder and danger in the uncharted territories of the mind. But it's also a story about the power of imagination and the potential for change, even in a world where our deepest thoughts are no longer our own. And it's a story about the power of choice, and the importance of fighting for the freedom to dream.\n \"#\n not_cached #\"\n hello world\n \"#\n }\n}\n", "test-files/strategies/fallback-shorthand.baml": "\nclient FallbackToShorthand {\n provider fallback\n options {\n strategy [\n \"openai/does-not-exist\",\n \"openai/gpt-4o-mini\"\n ]\n }\n}\n\n\nfunction TestFallbackToShorthand(input: string) -> string {\n client FallbackToShorthand\n // TODO make it return the client name instead\n prompt #\"\n Say a haiku about {{input}}.\n \"#\n}\n\ntest TestProvider_FallbackToShorthand {\n functions [\n TestFallbackToShorthand\n ]\n args {\n input \"Donkey kong and peanut butter\"\n }\n}\n", "test-files/strategies/fallback.baml": "// Happy path fallbacks.\nclient FaultyClient {\n provider openai\n options {\n model unknown-model\n api_key env.OPENAI_API_KEY\n }\n}\n\n\nclient FallbackClient {\n provider fallback\n options {\n // first 2 clients are expected to fail.\n strategy [\n FaultyClient,\n RetryClientConstant,\n GPT35\n Gemini\n\n ]\n }\n}\n\nfunction TestFallbackClient() -> string {\n client FallbackClient\n // TODO make it return the client name instead\n prompt #\"\n Say a haiku about mexico.\n \"#\n}\n\n// Fallbacks should fail gracefully.\nclient FaultyAzureClient {\n provider azure-openai\n options {\n model unknown-model\n resource_name \"unknown-resource-id\"\n deployment_id \"unknown-deployment-id\"\n }\n}\n\nclient SingleFallbackClient {\n provider fallback\n options {\n // first 2 clients are expected to fail.\n strategy [\n FaultyAzureClient\n ]\n }\n}\n\nfunction TestSingleFallbackClient() -> string {\n client SingleFallbackClient\n // TODO make it return the client name instead\n prompt #\"\n Say a haiku about mexico.\n \"#\n}\n", "test-files/strategies/retry.baml": "\nretry_policy Exponential {\n max_retries 3\n strategy {\n type exponential_backoff\n }\n}\n\nretry_policy Constant {\n max_retries 3\n strategy {\n type constant_delay\n delay_ms 100\n }\n}\n\nclient RetryClientConstant {\n provider openai\n retry_policy Constant\n options {\n model \"gpt-3.5-turbo\"\n api_key \"blah\"\n }\n}\n\nclient RetryClientExponential {\n provider openai\n retry_policy Exponential\n options {\n model \"gpt-3.5-turbo\"\n api_key \"blahh\"\n }\n}\n\nfunction TestRetryConstant() -> string {\n client RetryClientConstant\n prompt #\"\n Say a haiku\n \"#\n}\n\nfunction TestRetryExponential() -> string {\n client RetryClientExponential\n prompt #\"\n Say a haiku\n \"#\n}\n", diff --git a/integ-tests/python/poetry.lock b/integ-tests/python/poetry.lock index b13247d60..ee8815326 100644 --- a/integ-tests/python/poetry.lock +++ b/integ-tests/python/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. [[package]] name = "annotated-types" @@ -201,24 +201,24 @@ files = [ [[package]] name = "maturin" -version = "1.7.4" +version = "1.7.8" description = "Build and publish crates with pyo3, cffi and uniffi bindings as well as rust binaries as python packages" optional = false python-versions = ">=3.7" files = [ - {file = "maturin-1.7.4-py3-none-linux_armv6l.whl", hash = "sha256:eb7b7753b733ae302c08f80bca7b0c3fda1eea665c2b1922c58795f35a54c833"}, - {file = "maturin-1.7.4-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:0182a9638399c8835afd39d2aeacf56908e37cba3f7abb15816b9df6774fab81"}, - {file = "maturin-1.7.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:41a29c5b23f3ebdfe7633637e3de256579a1b2700c04cd68c16ed46934440c5a"}, - {file = "maturin-1.7.4-py3-none-manylinux_2_12_i686.manylinux2010_i686.musllinux_1_1_i686.whl", hash = "sha256:23fae44e345a2da5cb391ae878726fb793394826e2f97febe41710bd4099460e"}, - {file = "maturin-1.7.4-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:8b441521c151f0dbe70ed06fb1feb29b855d787bda038ff4330ca962e5d56641"}, - {file = "maturin-1.7.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:7ccb66d0c5297cf06652c5f72cb398f447d3a332eccf5d1e73b3fe14dbc9498c"}, - {file = "maturin-1.7.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:71f668f19e719048605dbca6a1f4d0dc03b987c922ad9c4bf5be03b9b278e4c3"}, - {file = "maturin-1.7.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.musllinux_1_1_ppc64le.whl", hash = "sha256:c179fcb2b494f19186781b667320e43d95b3e71fcb1c98fffad9ef6bd6e276b3"}, - {file = "maturin-1.7.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd5b4b95286f2f376437340f8a4908f4761587212170263084455be8099099a7"}, - {file = "maturin-1.7.4-py3-none-win32.whl", hash = "sha256:35487a424467d1fda4567cbb02d21f09febb10eda22f5fd647b130bc0767dc61"}, - {file = "maturin-1.7.4-py3-none-win_amd64.whl", hash = "sha256:f70c1c8ec9bd4749a53c0f3ae8fdbb326ce45be4f1c5551985ee25a6d7150328"}, - {file = "maturin-1.7.4-py3-none-win_arm64.whl", hash = "sha256:f3d38a6d0c7fd7b04bec30dd470b2173cf9bd184ab6220c1acaf49df6b48faf5"}, - {file = "maturin-1.7.4.tar.gz", hash = "sha256:2b349d742a07527d236f0b4b6cab26f53ebecad0ceabfc09ec4c6a396e3176f9"}, + {file = "maturin-1.7.8-py3-none-linux_armv6l.whl", hash = "sha256:c6950fd2790acd93265e1501cea66f9249cff19724654424ca75a3b17ebb315b"}, + {file = "maturin-1.7.8-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:f98288d5c382bacf0c076871dfd50c38f1eb2248f417551e98dd6f47f6ee8afa"}, + {file = "maturin-1.7.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b2d4e0f674ca29864e6b86c2eb9fee8236d1c7496c25f7300e34229272468f4c"}, + {file = "maturin-1.7.8-py3-none-manylinux_2_12_i686.manylinux2010_i686.musllinux_1_1_i686.whl", hash = "sha256:6cafb17bf57822bdc04423d9e3e766d42918d474848fe9833e397267514ba891"}, + {file = "maturin-1.7.8-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:2b2bdee0c3a84696b3a809054c43ead1a04b7b3321cbd5b8f5676e4ba4691d0f"}, + {file = "maturin-1.7.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:b8188b71259fc2bc568d9c8acc186fcfed96f42539bcb55b8e6f4ec26e411f37"}, + {file = "maturin-1.7.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:a4f58c2a53c2958a1bf090960b08b28e676136cd88ac2f5dfdcf1b14ea54ec06"}, + {file = "maturin-1.7.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.musllinux_1_1_ppc64le.whl", hash = "sha256:c5d6c0c631d1fc646cd3834795e6cfd72ab4271d289df7e0f911261a02bec75f"}, + {file = "maturin-1.7.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c23664d19dadcbf800ef70f26afb2e0485a985c62889930934f019c565534c23"}, + {file = "maturin-1.7.8-py3-none-win32.whl", hash = "sha256:403eebf1afa6f19b49425f089e39c53b8e597bc86a47f3a76e828dc78d27fa80"}, + {file = "maturin-1.7.8-py3-none-win_amd64.whl", hash = "sha256:1ce48d007438b895f8665314b6748ac0dab31e4f32049a60b52281dd2dccbdde"}, + {file = "maturin-1.7.8-py3-none-win_arm64.whl", hash = "sha256:cc92a62953205e8945b6cfe6943d6a8576a4442d30d9c67141f944f4f4640e62"}, + {file = "maturin-1.7.8.tar.gz", hash = "sha256:649c6ef3f0fa4c5f596140d761dc5a4d577c485cc32fb5b9b344a8280352880d"}, ] [package.dependencies] @@ -239,25 +239,6 @@ files = [ {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, ] -[[package]] -name = "patchelf" -version = "0.17.2.1" -description = "A small utility to modify the dynamic linker and RPATH of ELF executables." -optional = false -python-versions = "*" -files = [ - {file = "patchelf-0.17.2.1-py2.py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:fc329da0e8f628bd836dfb8eaf523547e342351fa8f739bf2b3fe4a6db5a297c"}, - {file = "patchelf-0.17.2.1-py2.py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:ccb266a94edf016efe80151172c26cff8c2ec120a57a1665d257b0442784195d"}, - {file = "patchelf-0.17.2.1-py2.py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.musllinux_1_1_ppc64le.whl", hash = "sha256:f47b5bdd6885cfb20abdd14c707d26eb6f499a7f52e911865548d4aa43385502"}, - {file = "patchelf-0.17.2.1-py2.py3-none-manylinux_2_17_s390x.manylinux2014_s390x.musllinux_1_1_s390x.whl", hash = "sha256:a9e6ebb0874a11f7ed56d2380bfaa95f00612b23b15f896583da30c2059fcfa8"}, - {file = "patchelf-0.17.2.1-py2.py3-none-manylinux_2_5_i686.manylinux1_i686.musllinux_1_1_i686.whl", hash = "sha256:3c8d58f0e4c1929b1c7c45ba8da5a84a8f1aa6a82a46e1cfb2e44a4d40f350e5"}, - {file = "patchelf-0.17.2.1-py2.py3-none-manylinux_2_5_x86_64.manylinux1_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:d1a9bc0d4fd80c038523ebdc451a1cce75237cfcc52dbd1aca224578001d5927"}, - {file = "patchelf-0.17.2.1.tar.gz", hash = "sha256:a6eb0dd452ce4127d0d5e1eb26515e39186fa609364274bc1b0b77539cfa7031"}, -] - -[package.extras] -test = ["importlib-metadata", "pytest"] - [[package]] name = "pluggy" version = "1.5.0" @@ -275,22 +256,19 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pydantic" -version = "2.9.2" +version = "2.10.3" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12"}, - {file = "pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f"}, + {file = "pydantic-2.10.3-py3-none-any.whl", hash = "sha256:be04d85bbc7b65651c5f8e6b9976ed9c6f41782a55524cef079a34a0bb82144d"}, + {file = "pydantic-2.10.3.tar.gz", hash = "sha256:cb5ac360ce894ceacd69c403187900a02c4b20b693a9dd1d643e1effab9eadf9"}, ] [package.dependencies] annotated-types = ">=0.6.0" -pydantic-core = "2.23.4" -typing-extensions = [ - {version = ">=4.12.2", markers = "python_version >= \"3.13\""}, - {version = ">=4.6.1", markers = "python_version < \"3.13\""}, -] +pydantic-core = "2.27.1" +typing-extensions = ">=4.12.2" [package.extras] email = ["email-validator (>=2.0.0)"] @@ -298,100 +276,111 @@ timezone = ["tzdata"] [[package]] name = "pydantic-core" -version = "2.23.4" +version = "2.27.1" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b"}, - {file = "pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f"}, - {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3"}, - {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071"}, - {file = "pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119"}, - {file = "pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f"}, - {file = "pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8"}, - {file = "pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b"}, - {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0"}, - {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64"}, - {file = "pydantic_core-2.23.4-cp311-none-win32.whl", hash = "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f"}, - {file = "pydantic_core-2.23.4-cp311-none-win_amd64.whl", hash = "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3"}, - {file = "pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231"}, - {file = "pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126"}, - {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e"}, - {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24"}, - {file = "pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84"}, - {file = "pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9"}, - {file = "pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc"}, - {file = "pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327"}, - {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6"}, - {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f"}, - {file = "pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769"}, - {file = "pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5"}, - {file = "pydantic_core-2.23.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d4488a93b071c04dc20f5cecc3631fc78b9789dd72483ba15d423b5b3689b555"}, - {file = "pydantic_core-2.23.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:81965a16b675b35e1d09dd14df53f190f9129c0202356ed44ab2728b1c905658"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffa2ebd4c8530079140dd2d7f794a9d9a73cbb8e9d59ffe24c63436efa8f271"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:61817945f2fe7d166e75fbfb28004034b48e44878177fc54d81688e7b85a3665"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29d2c342c4bc01b88402d60189f3df065fb0dda3654744d5a165a5288a657368"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e11661ce0fd30a6790e8bcdf263b9ec5988e95e63cf901972107efc49218b13"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d18368b137c6295db49ce7218b1a9ba15c5bc254c96d7c9f9e924a9bc7825ad"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec4e55f79b1c4ffb2eecd8a0cfba9955a2588497d96851f4c8f99aa4a1d39b12"}, - {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:374a5e5049eda9e0a44c696c7ade3ff355f06b1fe0bb945ea3cac2bc336478a2"}, - {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5c364564d17da23db1106787675fc7af45f2f7b58b4173bfdd105564e132e6fb"}, - {file = "pydantic_core-2.23.4-cp38-none-win32.whl", hash = "sha256:d7a80d21d613eec45e3d41eb22f8f94ddc758a6c4720842dc74c0581f54993d6"}, - {file = "pydantic_core-2.23.4-cp38-none-win_amd64.whl", hash = "sha256:5f5ff8d839f4566a474a969508fe1c5e59c31c80d9e140566f9a37bba7b8d556"}, - {file = "pydantic_core-2.23.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a4fa4fc04dff799089689f4fd502ce7d59de529fc2f40a2c8836886c03e0175a"}, - {file = "pydantic_core-2.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a7df63886be5e270da67e0966cf4afbae86069501d35c8c1b3b6c168f42cb36"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcedcd19a557e182628afa1d553c3895a9f825b936415d0dbd3cd0bbcfd29b4b"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f54b118ce5de9ac21c363d9b3caa6c800341e8c47a508787e5868c6b79c9323"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86d2f57d3e1379a9525c5ab067b27dbb8a0642fb5d454e17a9ac434f9ce523e3"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de6d1d1b9e5101508cb37ab0d972357cac5235f5c6533d1071964c47139257df"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1278e0d324f6908e872730c9102b0112477a7f7cf88b308e4fc36ce1bdb6d58c"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a6b5099eeec78827553827f4c6b8615978bb4b6a88e5d9b93eddf8bb6790f55"}, - {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e55541f756f9b3ee346b840103f32779c695a19826a4c442b7954550a0972040"}, - {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c7ba8ffb6d6f8f2ab08743be203654bb1aaa8c9dcb09f82ddd34eadb695605"}, - {file = "pydantic_core-2.23.4-cp39-none-win32.whl", hash = "sha256:37b0fe330e4a58d3c58b24d91d1eb102aeec675a3db4c292ec3928ecd892a9a6"}, - {file = "pydantic_core-2.23.4-cp39-none-win_amd64.whl", hash = "sha256:1498bec4c05c9c787bde9125cfdcc63a41004ff167f495063191b863399b1a29"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:78ddaaa81421a29574a682b3179d4cf9e6d405a09b99d93ddcf7e5239c742e21"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:883a91b5dd7d26492ff2f04f40fbb652de40fcc0afe07e8129e8ae779c2110eb"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88ad334a15b32a791ea935af224b9de1bf99bcd62fabf745d5f3442199d86d59"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:233710f069d251feb12a56da21e14cca67994eab08362207785cf8c598e74577"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19442362866a753485ba5e4be408964644dd6a09123d9416c54cd49171f50744"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:624e278a7d29b6445e4e813af92af37820fafb6dcc55c012c834f9e26f9aaaef"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5ef8f42bec47f21d07668a043f077d507e5bf4e668d5c6dfe6aaba89de1a5b8"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:aea443fffa9fbe3af1a9ba721a87f926fe548d32cab71d188a6ede77d0ff244e"}, - {file = "pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863"}, + {file = "pydantic_core-2.27.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:71a5e35c75c021aaf400ac048dacc855f000bdfed91614b4a726f7432f1f3d6a"}, + {file = "pydantic_core-2.27.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f82d068a2d6ecfc6e054726080af69a6764a10015467d7d7b9f66d6ed5afa23b"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:121ceb0e822f79163dd4699e4c54f5ad38b157084d97b34de8b232bcaad70278"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4603137322c18eaf2e06a4495f426aa8d8388940f3c457e7548145011bb68e05"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a33cd6ad9017bbeaa9ed78a2e0752c5e250eafb9534f308e7a5f7849b0b1bfb4"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15cc53a3179ba0fcefe1e3ae50beb2784dede4003ad2dfd24f81bba4b23a454f"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45d9c5eb9273aa50999ad6adc6be5e0ecea7e09dbd0d31bd0c65a55a2592ca08"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8bf7b66ce12a2ac52d16f776b31d16d91033150266eb796967a7e4621707e4f6"}, + {file = "pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:655d7dd86f26cb15ce8a431036f66ce0318648f8853d709b4167786ec2fa4807"}, + {file = "pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:5556470f1a2157031e676f776c2bc20acd34c1990ca5f7e56f1ebf938b9ab57c"}, + {file = "pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f69ed81ab24d5a3bd93861c8c4436f54afdf8e8cc421562b0c7504cf3be58206"}, + {file = "pydantic_core-2.27.1-cp310-none-win32.whl", hash = "sha256:f5a823165e6d04ccea61a9f0576f345f8ce40ed533013580e087bd4d7442b52c"}, + {file = "pydantic_core-2.27.1-cp310-none-win_amd64.whl", hash = "sha256:57866a76e0b3823e0b56692d1a0bf722bffb324839bb5b7226a7dbd6c9a40b17"}, + {file = "pydantic_core-2.27.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ac3b20653bdbe160febbea8aa6c079d3df19310d50ac314911ed8cc4eb7f8cb8"}, + {file = "pydantic_core-2.27.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a5a8e19d7c707c4cadb8c18f5f60c843052ae83c20fa7d44f41594c644a1d330"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f7059ca8d64fea7f238994c97d91f75965216bcbe5f695bb44f354893f11d52"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bed0f8a0eeea9fb72937ba118f9db0cb7e90773462af7962d382445f3005e5a4"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a3cb37038123447cf0f3ea4c74751f6a9d7afef0eb71aa07bf5f652b5e6a132c"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84286494f6c5d05243456e04223d5a9417d7f443c3b76065e75001beb26f88de"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acc07b2cfc5b835444b44a9956846b578d27beeacd4b52e45489e93276241025"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4fefee876e07a6e9aad7a8c8c9f85b0cdbe7df52b8a9552307b09050f7512c7e"}, + {file = "pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:258c57abf1188926c774a4c94dd29237e77eda19462e5bb901d88adcab6af919"}, + {file = "pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:35c14ac45fcfdf7167ca76cc80b2001205a8d5d16d80524e13508371fb8cdd9c"}, + {file = "pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d1b26e1dff225c31897696cab7d4f0a315d4c0d9e8666dbffdb28216f3b17fdc"}, + {file = "pydantic_core-2.27.1-cp311-none-win32.whl", hash = "sha256:2cdf7d86886bc6982354862204ae3b2f7f96f21a3eb0ba5ca0ac42c7b38598b9"}, + {file = "pydantic_core-2.27.1-cp311-none-win_amd64.whl", hash = "sha256:3af385b0cee8df3746c3f406f38bcbfdc9041b5c2d5ce3e5fc6637256e60bbc5"}, + {file = "pydantic_core-2.27.1-cp311-none-win_arm64.whl", hash = "sha256:81f2ec23ddc1b476ff96563f2e8d723830b06dceae348ce02914a37cb4e74b89"}, + {file = "pydantic_core-2.27.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9cbd94fc661d2bab2bc702cddd2d3370bbdcc4cd0f8f57488a81bcce90c7a54f"}, + {file = "pydantic_core-2.27.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5f8c4718cd44ec1580e180cb739713ecda2bdee1341084c1467802a417fe0f02"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15aae984e46de8d376df515f00450d1522077254ef6b7ce189b38ecee7c9677c"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1ba5e3963344ff25fc8c40da90f44b0afca8cfd89d12964feb79ac1411a260ac"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:992cea5f4f3b29d6b4f7f1726ed8ee46c8331c6b4eed6db5b40134c6fe1768bb"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0325336f348dbee6550d129b1627cb8f5351a9dc91aad141ffb96d4937bd9529"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7597c07fbd11515f654d6ece3d0e4e5093edc30a436c63142d9a4b8e22f19c35"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3bbd5d8cc692616d5ef6fbbbd50dbec142c7e6ad9beb66b78a96e9c16729b089"}, + {file = "pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:dc61505e73298a84a2f317255fcc72b710b72980f3a1f670447a21efc88f8381"}, + {file = "pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:e1f735dc43da318cad19b4173dd1ffce1d84aafd6c9b782b3abc04a0d5a6f5bb"}, + {file = "pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f4e5658dbffe8843a0f12366a4c2d1c316dbe09bb4dfbdc9d2d9cd6031de8aae"}, + {file = "pydantic_core-2.27.1-cp312-none-win32.whl", hash = "sha256:672ebbe820bb37988c4d136eca2652ee114992d5d41c7e4858cdd90ea94ffe5c"}, + {file = "pydantic_core-2.27.1-cp312-none-win_amd64.whl", hash = "sha256:66ff044fd0bb1768688aecbe28b6190f6e799349221fb0de0e6f4048eca14c16"}, + {file = "pydantic_core-2.27.1-cp312-none-win_arm64.whl", hash = "sha256:9a3b0793b1bbfd4146304e23d90045f2a9b5fd5823aa682665fbdaf2a6c28f3e"}, + {file = "pydantic_core-2.27.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f216dbce0e60e4d03e0c4353c7023b202d95cbaeff12e5fd2e82ea0a66905073"}, + {file = "pydantic_core-2.27.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a2e02889071850bbfd36b56fd6bc98945e23670773bc7a76657e90e6b6603c08"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42b0e23f119b2b456d07ca91b307ae167cc3f6c846a7b169fca5326e32fdc6cf"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:764be71193f87d460a03f1f7385a82e226639732214b402f9aa61f0d025f0737"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c00666a3bd2f84920a4e94434f5974d7bbc57e461318d6bb34ce9cdbbc1f6b2"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ccaa88b24eebc0f849ce0a4d09e8a408ec5a94afff395eb69baf868f5183107"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c65af9088ac534313e1963443d0ec360bb2b9cba6c2909478d22c2e363d98a51"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:206b5cf6f0c513baffaeae7bd817717140770c74528f3e4c3e1cec7871ddd61a"}, + {file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:062f60e512fc7fff8b8a9d680ff0ddaaef0193dba9fa83e679c0c5f5fbd018bc"}, + {file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:a0697803ed7d4af5e4c1adf1670af078f8fcab7a86350e969f454daf598c4960"}, + {file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:58ca98a950171f3151c603aeea9303ef6c235f692fe555e883591103da709b23"}, + {file = "pydantic_core-2.27.1-cp313-none-win32.whl", hash = "sha256:8065914ff79f7eab1599bd80406681f0ad08f8e47c880f17b416c9f8f7a26d05"}, + {file = "pydantic_core-2.27.1-cp313-none-win_amd64.whl", hash = "sha256:ba630d5e3db74c79300d9a5bdaaf6200172b107f263c98a0539eeecb857b2337"}, + {file = "pydantic_core-2.27.1-cp313-none-win_arm64.whl", hash = "sha256:45cf8588c066860b623cd11c4ba687f8d7175d5f7ef65f7129df8a394c502de5"}, + {file = "pydantic_core-2.27.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:5897bec80a09b4084aee23f9b73a9477a46c3304ad1d2d07acca19723fb1de62"}, + {file = "pydantic_core-2.27.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d0165ab2914379bd56908c02294ed8405c252250668ebcb438a55494c69f44ab"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b9af86e1d8e4cfc82c2022bfaa6f459381a50b94a29e95dcdda8442d6d83864"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f6c8a66741c5f5447e047ab0ba7a1c61d1e95580d64bce852e3df1f895c4067"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a42d6a8156ff78981f8aa56eb6394114e0dedb217cf8b729f438f643608cbcd"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64c65f40b4cd8b0e049a8edde07e38b476da7e3aaebe63287c899d2cff253fa5"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdcf339322a3fae5cbd504edcefddd5a50d9ee00d968696846f089b4432cf78"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bf99c8404f008750c846cb4ac4667b798a9f7de673ff719d705d9b2d6de49c5f"}, + {file = "pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8f1edcea27918d748c7e5e4d917297b2a0ab80cad10f86631e488b7cddf76a36"}, + {file = "pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:159cac0a3d096f79ab6a44d77a961917219707e2a130739c64d4dd46281f5c2a"}, + {file = "pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:029d9757eb621cc6e1848fa0b0310310de7301057f623985698ed7ebb014391b"}, + {file = "pydantic_core-2.27.1-cp38-none-win32.whl", hash = "sha256:a28af0695a45f7060e6f9b7092558a928a28553366519f64083c63a44f70e618"}, + {file = "pydantic_core-2.27.1-cp38-none-win_amd64.whl", hash = "sha256:2d4567c850905d5eaaed2f7a404e61012a51caf288292e016360aa2b96ff38d4"}, + {file = "pydantic_core-2.27.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:e9386266798d64eeb19dd3677051f5705bf873e98e15897ddb7d76f477131967"}, + {file = "pydantic_core-2.27.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4228b5b646caa73f119b1ae756216b59cc6e2267201c27d3912b592c5e323b60"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b3dfe500de26c52abe0477dde16192ac39c98f05bf2d80e76102d394bd13854"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:aee66be87825cdf72ac64cb03ad4c15ffef4143dbf5c113f64a5ff4f81477bf9"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b748c44bb9f53031c8cbc99a8a061bc181c1000c60a30f55393b6e9c45cc5bd"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ca038c7f6a0afd0b2448941b6ef9d5e1949e999f9e5517692eb6da58e9d44be"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e0bd57539da59a3e4671b90a502da9a28c72322a4f17866ba3ac63a82c4498e"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ac6c2c45c847bbf8f91930d88716a0fb924b51e0c6dad329b793d670ec5db792"}, + {file = "pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b94d4ba43739bbe8b0ce4262bcc3b7b9f31459ad120fb595627eaeb7f9b9ca01"}, + {file = "pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:00e6424f4b26fe82d44577b4c842d7df97c20be6439e8e685d0d715feceb9fb9"}, + {file = "pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:38de0a70160dd97540335b7ad3a74571b24f1dc3ed33f815f0880682e6880131"}, + {file = "pydantic_core-2.27.1-cp39-none-win32.whl", hash = "sha256:7ccebf51efc61634f6c2344da73e366c75e735960b5654b63d7e6f69a5885fa3"}, + {file = "pydantic_core-2.27.1-cp39-none-win_amd64.whl", hash = "sha256:a57847b090d7892f123726202b7daa20df6694cbd583b67a592e856bff603d6c"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3fa80ac2bd5856580e242dbc202db873c60a01b20309c8319b5c5986fbe53ce6"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d950caa237bb1954f1b8c9227b5065ba6875ac9771bb8ec790d956a699b78676"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e4216e64d203e39c62df627aa882f02a2438d18a5f21d7f721621f7a5d3611d"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02a3d637bd387c41d46b002f0e49c52642281edacd2740e5a42f7017feea3f2c"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:161c27ccce13b6b0c8689418da3885d3220ed2eae2ea5e9b2f7f3d48f1d52c27"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:19910754e4cc9c63bc1c7f6d73aa1cfee82f42007e407c0f413695c2f7ed777f"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:e173486019cc283dc9778315fa29a363579372fe67045e971e89b6365cc035ed"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:af52d26579b308921b73b956153066481f064875140ccd1dfd4e77db89dbb12f"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:981fb88516bd1ae8b0cbbd2034678a39dedc98752f264ac9bc5839d3923fa04c"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5fde892e6c697ce3e30c61b239330fc5d569a71fefd4eb6512fc6caec9dd9e2f"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:816f5aa087094099fff7edabb5e01cc370eb21aa1a1d44fe2d2aefdfb5599b31"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c10c309e18e443ddb108f0ef64e8729363adbfd92d6d57beec680f6261556f3"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98476c98b02c8e9b2eec76ac4156fd006628b1b2d0ef27e548ffa978393fd154"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c3027001c28434e7ca5a6e1e527487051136aa81803ac812be51802150d880dd"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:7699b1df36a48169cdebda7ab5a2bac265204003f153b4bd17276153d997670a"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1c39b07d90be6b48968ddc8c19e7585052088fd7ec8d568bb31ff64c70ae3c97"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:46ccfe3032b3915586e469d4972973f893c0a2bb65669194a5bdea9bacc088c2"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:62ba45e21cf6571d7f716d903b5b7b6d2617e2d5d67c0923dc47b9d41369f840"}, + {file = "pydantic_core-2.27.1.tar.gz", hash = "sha256:62a763352879b84aa31058fc931884055fd75089cccbd9d58bb6afd01141b235"}, ] [package.dependencies] @@ -399,13 +388,13 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" [[package]] name = "pytest" -version = "8.3.3" +version = "8.3.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"}, - {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"}, + {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, + {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, ] [package.dependencies] @@ -500,13 +489,43 @@ files = [ [[package]] name = "tomli" -version = "2.0.2" +version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" files = [ - {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"}, - {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, + {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, + {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, + {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, + {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, + {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, + {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, + {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, + {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, ] [[package]] @@ -551,4 +570,4 @@ zstd = ["zstandard (>=0.18.0)"] [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "afb27b68ef8ac9c50a89641e92f903500bd10d8f549ccbb78eccc51651846bf8" +content-hash = "324e77023537e384bec377bf8662498f9f2763d87bf28faa0e8c884d27f9a326" diff --git a/integ-tests/python/pyproject.toml b/integ-tests/python/pyproject.toml index e5b00cbbf..c4d2c2768 100644 --- a/integ-tests/python/pyproject.toml +++ b/integ-tests/python/pyproject.toml @@ -21,7 +21,6 @@ pydantic = "^2.7.1" python-dotenv = "^1.0.1" assertpy = "^1.1" requests = "^2.32.3" -patchelf = "^0.17.2.1" [tool.poetry.group.dev.dependencies] types-assertpy = "^1.1.0.20240712" diff --git a/integ-tests/python/tests/test_functions.py b/integ-tests/python/tests/test_functions.py index 961f897b9..dfc255e04 100644 --- a/integ-tests/python/tests/test_functions.py +++ b/integ-tests/python/tests/test_functions.py @@ -433,7 +433,7 @@ async def test_anthropic_shorthand_streaming(): @pytest.mark.asyncio async def test_fallback_to_shorthand(): - res = await b.TestFallbackToShorthand(input="Mt Rainier is tall") + res = await b.stream.TestFallbackToShorthand(input="Mt Rainier is tall").get_final_response() assert len(res) > 0, "Expected non-empty result but got empty." diff --git a/integ-tests/ruby/baml_client/inlined.rb b/integ-tests/ruby/baml_client/inlined.rb index d3d1f5a73..618d211c9 100644 --- a/integ-tests/ruby/baml_client/inlined.rb +++ b/integ-tests/ruby/baml_client/inlined.rb @@ -86,7 +86,7 @@ module Inlined "test-files/functions/prompts/no-chat-messages.baml" => "\n\nfunction PromptTestClaude(input: string) -> string {\n client Sonnet\n prompt #\"\n Tell me a haiku about {{ input }}\n \"#\n}\n\n\nfunction PromptTestStreaming(input: string) -> string {\n client GPT35\n prompt #\"\n Tell me a short story about {{ input }}\n \"#\n}\n\ntest TestName {\n functions [PromptTestStreaming]\n args {\n input #\"\n hello world\n \"#\n }\n}\n", "test-files/functions/prompts/with-chat-messages.baml" => "\nfunction PromptTestOpenAIChat(input: string) -> string {\n client GPT35\n prompt #\"\n {{ _.role(\"system\") }}\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestOpenAIChatNoSystem(input: string) -> string {\n client GPT35\n prompt #\"\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestClaudeChat(input: string) -> string {\n client Claude\n prompt #\"\n {{ _.role(\"system\") }}\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestClaudeChatNoSystem(input: string) -> string {\n client Claude\n prompt #\"\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\ntest TestSystemAndNonSystemChat1 {\n functions [PromptTestClaude, PromptTestOpenAI, PromptTestOpenAIChat, PromptTestOpenAIChatNoSystem, PromptTestClaudeChat, PromptTestClaudeChatNoSystem]\n args {\n input \"cats\"\n }\n}\n\ntest TestSystemAndNonSystemChat2 {\n functions [PromptTestClaude, PromptTestOpenAI, PromptTestOpenAIChat, PromptTestOpenAIChatNoSystem, PromptTestClaudeChat, PromptTestClaudeChatNoSystem]\n args {\n input \"lion\"\n }\n}", "test-files/functions/v2/basic.baml" => "\n\nfunction ExtractResume2(resume: string) -> Resume {\n client GPT4\n prompt #\"\n {{ _.role('system') }}\n\n Extract the following information from the resume:\n\n Resume:\n <<<<\n {{ resume }}\n <<<<\n\n Output JSON schema:\n {{ ctx.output_format }}\n\n JSON:\n \"#\n}\n\n\nclass WithReasoning {\n value string\n reasoning string @description(#\"\n Why the value is a good fit.\n \"#)\n}\n\n\nclass SearchParams {\n dateRange int? @description(#\"\n In ISO duration format, e.g. P1Y2M10D.\n \"#)\n location string[]\n jobTitle WithReasoning? @description(#\"\n An exact job title, not a general category.\n \"#)\n company WithReasoning? @description(#\"\n The exact name of the company, not a product or service.\n \"#)\n description WithReasoning[] @description(#\"\n Any specific projects or features the user is looking for.\n \"#)\n tags (Tag | string)[]\n}\n\nenum Tag {\n Security\n AI\n Blockchain\n}\n\nfunction GetQuery(query: string) -> SearchParams {\n client GPT4\n prompt #\"\n Extract the following information from the query:\n\n Query:\n <<<<\n {{ query }}\n <<<<\n\n OUTPUT_JSON_SCHEMA:\n {{ ctx.output_format }}\n\n Before OUTPUT_JSON_SCHEMA, list 5 intentions the user may have.\n --- EXAMPLES ---\n 1. \n 2. \n 3. \n 4. \n 5. \n\n {\n ... // OUTPUT_JSON_SCHEMA\n }\n \"#\n}\n\nclass RaysData {\n dataType DataType\n value Resume | Event\n}\n\nenum DataType {\n Resume\n Event\n}\n\nclass Event {\n title string\n date string\n location string\n description string\n}\n\nfunction GetDataType(text: string) -> RaysData {\n client GPT4\n prompt #\"\n Extract the relevant info.\n\n Text:\n <<<<\n {{ text }}\n <<<<\n\n Output JSON schema:\n {{ ctx.output_format }}\n\n JSON:\n \"#\n}", - "test-files/providers/providers.baml" => "function TestAnthropic(input: string) -> string {\n client Claude\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestOpenAI(input: string) -> string {\n client GPT35\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestOpenAILegacyProvider(input: string) -> string {\n client GPT35LegacyProvider\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestAzure(input: string) -> string {\n client GPT35Azure\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestOllama(input: string) -> string {\n client Ollama\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestGemini(input: string) -> string {\n client Gemini\n prompt #\"\n Write a nice short story about {{ input }}\n \"#\n}\n\nfunction TestVertex(input: string) -> string {\n client Vertex\n prompt #\"\n Write a nice short story about {{ input }}\n \"#\n\n}\n\nfunction TestAws(input: string) -> string {\n client AwsBedrock\n prompt #\"\n Write a nice short story about {{ input }}\n \"#\n}\n\nfunction TestAwsInvalidRegion(input: string) -> string {\n client AwsBedrockInvalidRegion\n prompt #\"\n Write a nice short story about {{ input }}\n \"#\n}\n\nfunction TestOpenAIShorthand(input: string) -> string {\n client \"openai/gpt-4o-mini\"\n prompt #\"\n Write a nice short story about {{ input }}\n \"#\n}\n\nfunction TestAnthropicShorthand(input: string) -> string {\n client \"anthropic/claude-3-haiku-20240307\"\n prompt #\"\n Write a nice short story about {{ input }}\n \"#\n}\n\ntest TestProvider {\n functions [\n TestAnthropic, TestVertex, PromptTestOpenAI, TestAzure, TestOllama, TestGemini, TestAws,\n TestAwsInvalidRegion,\n TestOpenAIShorthand,\n TestAnthropicShorthand\n ]\n args {\n input \"Donkey kong and peanut butter\"\n }\n}\n\n\nfunction TestCaching(input: string, not_cached: string) -> string {\n client ClaudeWithCaching\n prompt #\"\n {{ _.role('system', cache_control={\"type\": \"ephemeral\"}) }}\n Generate the following story\n {{ input }}\n\n {# Haiku require 2048 tokens to cache -#}\n {{ input }}\n\n {{ _.role('user') }}\n {{ not_cached }}\n \"#\n}\n\ntest TestName {\n functions [TestCaching]\n args {\n input #\"\nIn a near-future society where dreams have become a tradable commodity and shared experience, a lonely and socially awkward teenager named Alex discovers they possess a rare and powerful ability to not only view but also manipulate the dreams of others. Initially thrilled by this newfound power, Alex begins subtly altering the dreams of classmates and family members, helping them overcome fears, boost confidence, or experience fantastical adventures. As Alex's skills grow, so does their influence. They start selling premium dream experiences on the black market, crafting intricate and addictive dreamscapes for wealthy clients. However, the line between dream and reality begins to blur for those exposed to Alex's creations. Some clients struggle to differentiate between their true memories and the artificial ones implanted by Alex's dream manipulation.\n\nComplications arise when a mysterious government agency takes notice of Alex's unique abilities. They offer Alex a chance to use their gift for \"the greater good,\" hinting at applications in therapy, criminal rehabilitation, and even national security. Simultaneously, an underground resistance movement reaches out, warning Alex about the dangers of dream manipulation and the potential for mass control and exploitation. Caught between these opposing forces, Alex must navigate a complex web of ethical dilemmas. They grapple with questions of free will, the nature of consciousness, and the responsibility that comes with having power over people's minds. As the consequences of their actions spiral outward, affecting the lives of loved ones and strangers alike, Alex is forced to confront the true nature of their ability and decide how—or if—it should be used.\n\nThe story explores themes of identity, the subconscious mind, the ethics of technology, and the power of imagination. It delves into the potential consequences of a world where our most private thoughts and experiences are no longer truly our own, and examines the fine line between helping others and manipulating them for personal gain or a perceived greater good. The narrative further expands on the societal implications of such abilities, questioning the moral boundaries of altering consciousness and the potential for abuse in a world where dreams can be commodified. It challenges the reader to consider the impact of technology on personal autonomy and the ethical responsibilities of those who wield such power.\n\nAs Alex's journey unfolds, they encounter various individuals whose lives have been touched by their dream manipulations, each presenting a unique perspective on the ethical quandaries at hand. From a classmate who gains newfound confidence to a wealthy client who becomes addicted to the dreamscapes, the ripple effects of Alex's actions are profound and far-reaching. The government agency's interest in Alex's abilities raises questions about the potential for state control and surveillance, while the resistance movement highlights the dangers of unchecked power and the importance of safeguarding individual freedoms.\n\nUltimately, Alex's story is one of self-discovery and moral reckoning, as they must decide whether to embrace their abilities for personal gain, align with the government's vision of a controlled utopia, or join the resistance in their fight for freedom and autonomy. The narrative invites readers to reflect on the nature of reality, the boundaries of human experience, and the ethical implications of a world where dreams are no longer private sanctuaries but shared and manipulated commodities. It also explores the psychological impact on Alex, who must deal with the burden of knowing the intimate fears and desires of others, and the isolation that comes from being unable to share their own dreams without altering them.\n\nThe story further examines the technological advancements that have made dream manipulation possible, questioning the role of innovation in society and the potential for both progress and peril. It considers the societal divide between those who can afford to buy enhanced dream experiences and those who cannot, highlighting issues of inequality and access. As Alex becomes more entangled in the web of their own making, they must confront the possibility that their actions could lead to unintended consequences, not just for themselves but for the fabric of society as a whole.\n\nIn the end, Alex's journey is a cautionary tale about the power of dreams and the responsibilities that come with wielding such influence. It serves as a reminder of the importance of ethical considerations in the face of technological advancement and the need to balance innovation with humanity. The story leaves readers pondering the true cost of a world where dreams are no longer sacred, and the potential for both wonder and danger in the uncharted territories of the mind. But it's also a story about the power of imagination and the potential for change, even in a world where our deepest thoughts are no longer our own. And it's a story about the power of choice, and the importance of fighting for the freedom to dream.\n\nIn conclusion, this story is a reflection on the power of dreams and the responsibilities that come with wielding such influence. It serves as a reminder of the importance of ethical considerations in the face of technological advancement and the need to balance innovation with humanity. The story leaves readers pondering the true cost of a world where dreams are no longer sacred, and the potential for both wonder and danger in the uncharted territories of the mind. But it's also a story about the power of imagination and the potential for change, even in a world where our deepest thoughts are no longer our own. And it's a story about the power of choice, and the importance of fighting for the freedom to dream.\n \"#\n not_cached #\"\n hello world\n \"#\n }\n}\n", + "test-files/providers/providers.baml" => "function TestAnthropic(input: string) -> string {\n client Claude\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestOpenAI(input: string) -> string {\n client GPT35\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestOpenAILegacyProvider(input: string) -> string {\n client GPT35LegacyProvider\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestAzure(input: string) -> string {\n client GPT35Azure\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestOllama(input: string) -> string {\n client Ollama\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestGemini(input: string) -> string {\n client Gemini\n prompt #\"\n Write a nice short story about {{ input }}\n \"#\n}\n\nfunction TestVertex(input: string) -> string {\n client Vertex\n prompt #\"\n Write a nice short story about {{ input }}\n \"#\n\n}\n\nfunction TestAws(input: string) -> string {\n client AwsBedrock\n prompt #\"\n Write a nice short story about {{ input }}\n \"#\n}\n\nfunction TestAwsInvalidRegion(input: string) -> string {\n client AwsBedrockInvalidRegion\n prompt #\"\n Write a nice short story about {{ input }}\n \"#\n}\n\nfunction TestOpenAIShorthand(input: string) -> string {\n client \"openai/gpt-4o-mini\"\n prompt #\"\n Write a nice short haiku about {{ input }}\n \"#\n}\n\nfunction TestAnthropicShorthand(input: string) -> string {\n client \"anthropic/claude-3-haiku-20240307\"\n prompt #\"\n Write a nice short story about {{ input }}\n \"#\n}\n\ntest TestProvider {\n functions [\n TestAnthropic, TestVertex, PromptTestOpenAI, TestAzure, TestOllama, TestGemini, TestAws,\n TestAwsInvalidRegion,\n TestOpenAIShorthand,\n TestAnthropicShorthand\n ]\n args {\n input \"Donkey kong and peanut butter\"\n }\n}\n\n\nfunction TestCaching(input: string, not_cached: string) -> string {\n client ClaudeWithCaching\n prompt #\"\n {{ _.role('system', cache_control={\"type\": \"ephemeral\"}) }}\n Generate the following story\n {{ input }}\n\n {# Haiku require 2048 tokens to cache -#}\n {{ input }}\n\n {{ _.role('user') }}\n {{ not_cached }}\n \"#\n}\n\ntest TestName {\n functions [TestCaching]\n args {\n input #\"\nIn a near-future society where dreams have become a tradable commodity and shared experience, a lonely and socially awkward teenager named Alex discovers they possess a rare and powerful ability to not only view but also manipulate the dreams of others. Initially thrilled by this newfound power, Alex begins subtly altering the dreams of classmates and family members, helping them overcome fears, boost confidence, or experience fantastical adventures. As Alex's skills grow, so does their influence. They start selling premium dream experiences on the black market, crafting intricate and addictive dreamscapes for wealthy clients. However, the line between dream and reality begins to blur for those exposed to Alex's creations. Some clients struggle to differentiate between their true memories and the artificial ones implanted by Alex's dream manipulation.\n\nComplications arise when a mysterious government agency takes notice of Alex's unique abilities. They offer Alex a chance to use their gift for \"the greater good,\" hinting at applications in therapy, criminal rehabilitation, and even national security. Simultaneously, an underground resistance movement reaches out, warning Alex about the dangers of dream manipulation and the potential for mass control and exploitation. Caught between these opposing forces, Alex must navigate a complex web of ethical dilemmas. They grapple with questions of free will, the nature of consciousness, and the responsibility that comes with having power over people's minds. As the consequences of their actions spiral outward, affecting the lives of loved ones and strangers alike, Alex is forced to confront the true nature of their ability and decide how—or if—it should be used.\n\nThe story explores themes of identity, the subconscious mind, the ethics of technology, and the power of imagination. It delves into the potential consequences of a world where our most private thoughts and experiences are no longer truly our own, and examines the fine line between helping others and manipulating them for personal gain or a perceived greater good. The narrative further expands on the societal implications of such abilities, questioning the moral boundaries of altering consciousness and the potential for abuse in a world where dreams can be commodified. It challenges the reader to consider the impact of technology on personal autonomy and the ethical responsibilities of those who wield such power.\n\nAs Alex's journey unfolds, they encounter various individuals whose lives have been touched by their dream manipulations, each presenting a unique perspective on the ethical quandaries at hand. From a classmate who gains newfound confidence to a wealthy client who becomes addicted to the dreamscapes, the ripple effects of Alex's actions are profound and far-reaching. The government agency's interest in Alex's abilities raises questions about the potential for state control and surveillance, while the resistance movement highlights the dangers of unchecked power and the importance of safeguarding individual freedoms.\n\nUltimately, Alex's story is one of self-discovery and moral reckoning, as they must decide whether to embrace their abilities for personal gain, align with the government's vision of a controlled utopia, or join the resistance in their fight for freedom and autonomy. The narrative invites readers to reflect on the nature of reality, the boundaries of human experience, and the ethical implications of a world where dreams are no longer private sanctuaries but shared and manipulated commodities. It also explores the psychological impact on Alex, who must deal with the burden of knowing the intimate fears and desires of others, and the isolation that comes from being unable to share their own dreams without altering them.\n\nThe story further examines the technological advancements that have made dream manipulation possible, questioning the role of innovation in society and the potential for both progress and peril. It considers the societal divide between those who can afford to buy enhanced dream experiences and those who cannot, highlighting issues of inequality and access. As Alex becomes more entangled in the web of their own making, they must confront the possibility that their actions could lead to unintended consequences, not just for themselves but for the fabric of society as a whole.\n\nIn the end, Alex's journey is a cautionary tale about the power of dreams and the responsibilities that come with wielding such influence. It serves as a reminder of the importance of ethical considerations in the face of technological advancement and the need to balance innovation with humanity. The story leaves readers pondering the true cost of a world where dreams are no longer sacred, and the potential for both wonder and danger in the uncharted territories of the mind. But it's also a story about the power of imagination and the potential for change, even in a world where our deepest thoughts are no longer our own. And it's a story about the power of choice, and the importance of fighting for the freedom to dream.\n\nIn conclusion, this story is a reflection on the power of dreams and the responsibilities that come with wielding such influence. It serves as a reminder of the importance of ethical considerations in the face of technological advancement and the need to balance innovation with humanity. The story leaves readers pondering the true cost of a world where dreams are no longer sacred, and the potential for both wonder and danger in the uncharted territories of the mind. But it's also a story about the power of imagination and the potential for change, even in a world where our deepest thoughts are no longer our own. And it's a story about the power of choice, and the importance of fighting for the freedom to dream.\n \"#\n not_cached #\"\n hello world\n \"#\n }\n}\n", "test-files/strategies/fallback-shorthand.baml" => "\nclient FallbackToShorthand {\n provider fallback\n options {\n strategy [\n \"openai/does-not-exist\",\n \"openai/gpt-4o-mini\"\n ]\n }\n}\n\n\nfunction TestFallbackToShorthand(input: string) -> string {\n client FallbackToShorthand\n // TODO make it return the client name instead\n prompt #\"\n Say a haiku about {{input}}.\n \"#\n}\n\ntest TestProvider_FallbackToShorthand {\n functions [\n TestFallbackToShorthand\n ]\n args {\n input \"Donkey kong and peanut butter\"\n }\n}\n", "test-files/strategies/fallback.baml" => "// Happy path fallbacks.\nclient FaultyClient {\n provider openai\n options {\n model unknown-model\n api_key env.OPENAI_API_KEY\n }\n}\n\n\nclient FallbackClient {\n provider fallback\n options {\n // first 2 clients are expected to fail.\n strategy [\n FaultyClient,\n RetryClientConstant,\n GPT35\n Gemini\n\n ]\n }\n}\n\nfunction TestFallbackClient() -> string {\n client FallbackClient\n // TODO make it return the client name instead\n prompt #\"\n Say a haiku about mexico.\n \"#\n}\n\n// Fallbacks should fail gracefully.\nclient FaultyAzureClient {\n provider azure-openai\n options {\n model unknown-model\n resource_name \"unknown-resource-id\"\n deployment_id \"unknown-deployment-id\"\n }\n}\n\nclient SingleFallbackClient {\n provider fallback\n options {\n // first 2 clients are expected to fail.\n strategy [\n FaultyAzureClient\n ]\n }\n}\n\nfunction TestSingleFallbackClient() -> string {\n client SingleFallbackClient\n // TODO make it return the client name instead\n prompt #\"\n Say a haiku about mexico.\n \"#\n}\n", "test-files/strategies/retry.baml" => "\nretry_policy Exponential {\n max_retries 3\n strategy {\n type exponential_backoff\n }\n}\n\nretry_policy Constant {\n max_retries 3\n strategy {\n type constant_delay\n delay_ms 100\n }\n}\n\nclient RetryClientConstant {\n provider openai\n retry_policy Constant\n options {\n model \"gpt-3.5-turbo\"\n api_key \"blah\"\n }\n}\n\nclient RetryClientExponential {\n provider openai\n retry_policy Exponential\n options {\n model \"gpt-3.5-turbo\"\n api_key \"blahh\"\n }\n}\n\nfunction TestRetryConstant() -> string {\n client RetryClientConstant\n prompt #\"\n Say a haiku\n \"#\n}\n\nfunction TestRetryExponential() -> string {\n client RetryClientExponential\n prompt #\"\n Say a haiku\n \"#\n}\n", diff --git a/integ-tests/typescript/baml_client/inlinedbaml.ts b/integ-tests/typescript/baml_client/inlinedbaml.ts index 8d011450c..b98281b98 100644 --- a/integ-tests/typescript/baml_client/inlinedbaml.ts +++ b/integ-tests/typescript/baml_client/inlinedbaml.ts @@ -87,7 +87,7 @@ const fileMap = { "test-files/functions/prompts/no-chat-messages.baml": "\n\nfunction PromptTestClaude(input: string) -> string {\n client Sonnet\n prompt #\"\n Tell me a haiku about {{ input }}\n \"#\n}\n\n\nfunction PromptTestStreaming(input: string) -> string {\n client GPT35\n prompt #\"\n Tell me a short story about {{ input }}\n \"#\n}\n\ntest TestName {\n functions [PromptTestStreaming]\n args {\n input #\"\n hello world\n \"#\n }\n}\n", "test-files/functions/prompts/with-chat-messages.baml": "\nfunction PromptTestOpenAIChat(input: string) -> string {\n client GPT35\n prompt #\"\n {{ _.role(\"system\") }}\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestOpenAIChatNoSystem(input: string) -> string {\n client GPT35\n prompt #\"\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestClaudeChat(input: string) -> string {\n client Claude\n prompt #\"\n {{ _.role(\"system\") }}\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestClaudeChatNoSystem(input: string) -> string {\n client Claude\n prompt #\"\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\ntest TestSystemAndNonSystemChat1 {\n functions [PromptTestClaude, PromptTestOpenAI, PromptTestOpenAIChat, PromptTestOpenAIChatNoSystem, PromptTestClaudeChat, PromptTestClaudeChatNoSystem]\n args {\n input \"cats\"\n }\n}\n\ntest TestSystemAndNonSystemChat2 {\n functions [PromptTestClaude, PromptTestOpenAI, PromptTestOpenAIChat, PromptTestOpenAIChatNoSystem, PromptTestClaudeChat, PromptTestClaudeChatNoSystem]\n args {\n input \"lion\"\n }\n}", "test-files/functions/v2/basic.baml": "\n\nfunction ExtractResume2(resume: string) -> Resume {\n client GPT4\n prompt #\"\n {{ _.role('system') }}\n\n Extract the following information from the resume:\n\n Resume:\n <<<<\n {{ resume }}\n <<<<\n\n Output JSON schema:\n {{ ctx.output_format }}\n\n JSON:\n \"#\n}\n\n\nclass WithReasoning {\n value string\n reasoning string @description(#\"\n Why the value is a good fit.\n \"#)\n}\n\n\nclass SearchParams {\n dateRange int? @description(#\"\n In ISO duration format, e.g. P1Y2M10D.\n \"#)\n location string[]\n jobTitle WithReasoning? @description(#\"\n An exact job title, not a general category.\n \"#)\n company WithReasoning? @description(#\"\n The exact name of the company, not a product or service.\n \"#)\n description WithReasoning[] @description(#\"\n Any specific projects or features the user is looking for.\n \"#)\n tags (Tag | string)[]\n}\n\nenum Tag {\n Security\n AI\n Blockchain\n}\n\nfunction GetQuery(query: string) -> SearchParams {\n client GPT4\n prompt #\"\n Extract the following information from the query:\n\n Query:\n <<<<\n {{ query }}\n <<<<\n\n OUTPUT_JSON_SCHEMA:\n {{ ctx.output_format }}\n\n Before OUTPUT_JSON_SCHEMA, list 5 intentions the user may have.\n --- EXAMPLES ---\n 1. \n 2. \n 3. \n 4. \n 5. \n\n {\n ... // OUTPUT_JSON_SCHEMA\n }\n \"#\n}\n\nclass RaysData {\n dataType DataType\n value Resume | Event\n}\n\nenum DataType {\n Resume\n Event\n}\n\nclass Event {\n title string\n date string\n location string\n description string\n}\n\nfunction GetDataType(text: string) -> RaysData {\n client GPT4\n prompt #\"\n Extract the relevant info.\n\n Text:\n <<<<\n {{ text }}\n <<<<\n\n Output JSON schema:\n {{ ctx.output_format }}\n\n JSON:\n \"#\n}", - "test-files/providers/providers.baml": "function TestAnthropic(input: string) -> string {\n client Claude\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestOpenAI(input: string) -> string {\n client GPT35\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestOpenAILegacyProvider(input: string) -> string {\n client GPT35LegacyProvider\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestAzure(input: string) -> string {\n client GPT35Azure\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestOllama(input: string) -> string {\n client Ollama\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestGemini(input: string) -> string {\n client Gemini\n prompt #\"\n Write a nice short story about {{ input }}\n \"#\n}\n\nfunction TestVertex(input: string) -> string {\n client Vertex\n prompt #\"\n Write a nice short story about {{ input }}\n \"#\n\n}\n\nfunction TestAws(input: string) -> string {\n client AwsBedrock\n prompt #\"\n Write a nice short story about {{ input }}\n \"#\n}\n\nfunction TestAwsInvalidRegion(input: string) -> string {\n client AwsBedrockInvalidRegion\n prompt #\"\n Write a nice short story about {{ input }}\n \"#\n}\n\nfunction TestOpenAIShorthand(input: string) -> string {\n client \"openai/gpt-4o-mini\"\n prompt #\"\n Write a nice short story about {{ input }}\n \"#\n}\n\nfunction TestAnthropicShorthand(input: string) -> string {\n client \"anthropic/claude-3-haiku-20240307\"\n prompt #\"\n Write a nice short story about {{ input }}\n \"#\n}\n\ntest TestProvider {\n functions [\n TestAnthropic, TestVertex, PromptTestOpenAI, TestAzure, TestOllama, TestGemini, TestAws,\n TestAwsInvalidRegion,\n TestOpenAIShorthand,\n TestAnthropicShorthand\n ]\n args {\n input \"Donkey kong and peanut butter\"\n }\n}\n\n\nfunction TestCaching(input: string, not_cached: string) -> string {\n client ClaudeWithCaching\n prompt #\"\n {{ _.role('system', cache_control={\"type\": \"ephemeral\"}) }}\n Generate the following story\n {{ input }}\n\n {# Haiku require 2048 tokens to cache -#}\n {{ input }}\n\n {{ _.role('user') }}\n {{ not_cached }}\n \"#\n}\n\ntest TestName {\n functions [TestCaching]\n args {\n input #\"\nIn a near-future society where dreams have become a tradable commodity and shared experience, a lonely and socially awkward teenager named Alex discovers they possess a rare and powerful ability to not only view but also manipulate the dreams of others. Initially thrilled by this newfound power, Alex begins subtly altering the dreams of classmates and family members, helping them overcome fears, boost confidence, or experience fantastical adventures. As Alex's skills grow, so does their influence. They start selling premium dream experiences on the black market, crafting intricate and addictive dreamscapes for wealthy clients. However, the line between dream and reality begins to blur for those exposed to Alex's creations. Some clients struggle to differentiate between their true memories and the artificial ones implanted by Alex's dream manipulation.\n\nComplications arise when a mysterious government agency takes notice of Alex's unique abilities. They offer Alex a chance to use their gift for \"the greater good,\" hinting at applications in therapy, criminal rehabilitation, and even national security. Simultaneously, an underground resistance movement reaches out, warning Alex about the dangers of dream manipulation and the potential for mass control and exploitation. Caught between these opposing forces, Alex must navigate a complex web of ethical dilemmas. They grapple with questions of free will, the nature of consciousness, and the responsibility that comes with having power over people's minds. As the consequences of their actions spiral outward, affecting the lives of loved ones and strangers alike, Alex is forced to confront the true nature of their ability and decide how—or if—it should be used.\n\nThe story explores themes of identity, the subconscious mind, the ethics of technology, and the power of imagination. It delves into the potential consequences of a world where our most private thoughts and experiences are no longer truly our own, and examines the fine line between helping others and manipulating them for personal gain or a perceived greater good. The narrative further expands on the societal implications of such abilities, questioning the moral boundaries of altering consciousness and the potential for abuse in a world where dreams can be commodified. It challenges the reader to consider the impact of technology on personal autonomy and the ethical responsibilities of those who wield such power.\n\nAs Alex's journey unfolds, they encounter various individuals whose lives have been touched by their dream manipulations, each presenting a unique perspective on the ethical quandaries at hand. From a classmate who gains newfound confidence to a wealthy client who becomes addicted to the dreamscapes, the ripple effects of Alex's actions are profound and far-reaching. The government agency's interest in Alex's abilities raises questions about the potential for state control and surveillance, while the resistance movement highlights the dangers of unchecked power and the importance of safeguarding individual freedoms.\n\nUltimately, Alex's story is one of self-discovery and moral reckoning, as they must decide whether to embrace their abilities for personal gain, align with the government's vision of a controlled utopia, or join the resistance in their fight for freedom and autonomy. The narrative invites readers to reflect on the nature of reality, the boundaries of human experience, and the ethical implications of a world where dreams are no longer private sanctuaries but shared and manipulated commodities. It also explores the psychological impact on Alex, who must deal with the burden of knowing the intimate fears and desires of others, and the isolation that comes from being unable to share their own dreams without altering them.\n\nThe story further examines the technological advancements that have made dream manipulation possible, questioning the role of innovation in society and the potential for both progress and peril. It considers the societal divide between those who can afford to buy enhanced dream experiences and those who cannot, highlighting issues of inequality and access. As Alex becomes more entangled in the web of their own making, they must confront the possibility that their actions could lead to unintended consequences, not just for themselves but for the fabric of society as a whole.\n\nIn the end, Alex's journey is a cautionary tale about the power of dreams and the responsibilities that come with wielding such influence. It serves as a reminder of the importance of ethical considerations in the face of technological advancement and the need to balance innovation with humanity. The story leaves readers pondering the true cost of a world where dreams are no longer sacred, and the potential for both wonder and danger in the uncharted territories of the mind. But it's also a story about the power of imagination and the potential for change, even in a world where our deepest thoughts are no longer our own. And it's a story about the power of choice, and the importance of fighting for the freedom to dream.\n\nIn conclusion, this story is a reflection on the power of dreams and the responsibilities that come with wielding such influence. It serves as a reminder of the importance of ethical considerations in the face of technological advancement and the need to balance innovation with humanity. The story leaves readers pondering the true cost of a world where dreams are no longer sacred, and the potential for both wonder and danger in the uncharted territories of the mind. But it's also a story about the power of imagination and the potential for change, even in a world where our deepest thoughts are no longer our own. And it's a story about the power of choice, and the importance of fighting for the freedom to dream.\n \"#\n not_cached #\"\n hello world\n \"#\n }\n}\n", + "test-files/providers/providers.baml": "function TestAnthropic(input: string) -> string {\n client Claude\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestOpenAI(input: string) -> string {\n client GPT35\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestOpenAILegacyProvider(input: string) -> string {\n client GPT35LegacyProvider\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestAzure(input: string) -> string {\n client GPT35Azure\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestOllama(input: string) -> string {\n client Ollama\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestGemini(input: string) -> string {\n client Gemini\n prompt #\"\n Write a nice short story about {{ input }}\n \"#\n}\n\nfunction TestVertex(input: string) -> string {\n client Vertex\n prompt #\"\n Write a nice short story about {{ input }}\n \"#\n\n}\n\nfunction TestAws(input: string) -> string {\n client AwsBedrock\n prompt #\"\n Write a nice short story about {{ input }}\n \"#\n}\n\nfunction TestAwsInvalidRegion(input: string) -> string {\n client AwsBedrockInvalidRegion\n prompt #\"\n Write a nice short story about {{ input }}\n \"#\n}\n\nfunction TestOpenAIShorthand(input: string) -> string {\n client \"openai/gpt-4o-mini\"\n prompt #\"\n Write a nice short haiku about {{ input }}\n \"#\n}\n\nfunction TestAnthropicShorthand(input: string) -> string {\n client \"anthropic/claude-3-haiku-20240307\"\n prompt #\"\n Write a nice short story about {{ input }}\n \"#\n}\n\ntest TestProvider {\n functions [\n TestAnthropic, TestVertex, PromptTestOpenAI, TestAzure, TestOllama, TestGemini, TestAws,\n TestAwsInvalidRegion,\n TestOpenAIShorthand,\n TestAnthropicShorthand\n ]\n args {\n input \"Donkey kong and peanut butter\"\n }\n}\n\n\nfunction TestCaching(input: string, not_cached: string) -> string {\n client ClaudeWithCaching\n prompt #\"\n {{ _.role('system', cache_control={\"type\": \"ephemeral\"}) }}\n Generate the following story\n {{ input }}\n\n {# Haiku require 2048 tokens to cache -#}\n {{ input }}\n\n {{ _.role('user') }}\n {{ not_cached }}\n \"#\n}\n\ntest TestName {\n functions [TestCaching]\n args {\n input #\"\nIn a near-future society where dreams have become a tradable commodity and shared experience, a lonely and socially awkward teenager named Alex discovers they possess a rare and powerful ability to not only view but also manipulate the dreams of others. Initially thrilled by this newfound power, Alex begins subtly altering the dreams of classmates and family members, helping them overcome fears, boost confidence, or experience fantastical adventures. As Alex's skills grow, so does their influence. They start selling premium dream experiences on the black market, crafting intricate and addictive dreamscapes for wealthy clients. However, the line between dream and reality begins to blur for those exposed to Alex's creations. Some clients struggle to differentiate between their true memories and the artificial ones implanted by Alex's dream manipulation.\n\nComplications arise when a mysterious government agency takes notice of Alex's unique abilities. They offer Alex a chance to use their gift for \"the greater good,\" hinting at applications in therapy, criminal rehabilitation, and even national security. Simultaneously, an underground resistance movement reaches out, warning Alex about the dangers of dream manipulation and the potential for mass control and exploitation. Caught between these opposing forces, Alex must navigate a complex web of ethical dilemmas. They grapple with questions of free will, the nature of consciousness, and the responsibility that comes with having power over people's minds. As the consequences of their actions spiral outward, affecting the lives of loved ones and strangers alike, Alex is forced to confront the true nature of their ability and decide how—or if—it should be used.\n\nThe story explores themes of identity, the subconscious mind, the ethics of technology, and the power of imagination. It delves into the potential consequences of a world where our most private thoughts and experiences are no longer truly our own, and examines the fine line between helping others and manipulating them for personal gain or a perceived greater good. The narrative further expands on the societal implications of such abilities, questioning the moral boundaries of altering consciousness and the potential for abuse in a world where dreams can be commodified. It challenges the reader to consider the impact of technology on personal autonomy and the ethical responsibilities of those who wield such power.\n\nAs Alex's journey unfolds, they encounter various individuals whose lives have been touched by their dream manipulations, each presenting a unique perspective on the ethical quandaries at hand. From a classmate who gains newfound confidence to a wealthy client who becomes addicted to the dreamscapes, the ripple effects of Alex's actions are profound and far-reaching. The government agency's interest in Alex's abilities raises questions about the potential for state control and surveillance, while the resistance movement highlights the dangers of unchecked power and the importance of safeguarding individual freedoms.\n\nUltimately, Alex's story is one of self-discovery and moral reckoning, as they must decide whether to embrace their abilities for personal gain, align with the government's vision of a controlled utopia, or join the resistance in their fight for freedom and autonomy. The narrative invites readers to reflect on the nature of reality, the boundaries of human experience, and the ethical implications of a world where dreams are no longer private sanctuaries but shared and manipulated commodities. It also explores the psychological impact on Alex, who must deal with the burden of knowing the intimate fears and desires of others, and the isolation that comes from being unable to share their own dreams without altering them.\n\nThe story further examines the technological advancements that have made dream manipulation possible, questioning the role of innovation in society and the potential for both progress and peril. It considers the societal divide between those who can afford to buy enhanced dream experiences and those who cannot, highlighting issues of inequality and access. As Alex becomes more entangled in the web of their own making, they must confront the possibility that their actions could lead to unintended consequences, not just for themselves but for the fabric of society as a whole.\n\nIn the end, Alex's journey is a cautionary tale about the power of dreams and the responsibilities that come with wielding such influence. It serves as a reminder of the importance of ethical considerations in the face of technological advancement and the need to balance innovation with humanity. The story leaves readers pondering the true cost of a world where dreams are no longer sacred, and the potential for both wonder and danger in the uncharted territories of the mind. But it's also a story about the power of imagination and the potential for change, even in a world where our deepest thoughts are no longer our own. And it's a story about the power of choice, and the importance of fighting for the freedom to dream.\n\nIn conclusion, this story is a reflection on the power of dreams and the responsibilities that come with wielding such influence. It serves as a reminder of the importance of ethical considerations in the face of technological advancement and the need to balance innovation with humanity. The story leaves readers pondering the true cost of a world where dreams are no longer sacred, and the potential for both wonder and danger in the uncharted territories of the mind. But it's also a story about the power of imagination and the potential for change, even in a world where our deepest thoughts are no longer our own. And it's a story about the power of choice, and the importance of fighting for the freedom to dream.\n \"#\n not_cached #\"\n hello world\n \"#\n }\n}\n", "test-files/strategies/fallback-shorthand.baml": "\nclient FallbackToShorthand {\n provider fallback\n options {\n strategy [\n \"openai/does-not-exist\",\n \"openai/gpt-4o-mini\"\n ]\n }\n}\n\n\nfunction TestFallbackToShorthand(input: string) -> string {\n client FallbackToShorthand\n // TODO make it return the client name instead\n prompt #\"\n Say a haiku about {{input}}.\n \"#\n}\n\ntest TestProvider_FallbackToShorthand {\n functions [\n TestFallbackToShorthand\n ]\n args {\n input \"Donkey kong and peanut butter\"\n }\n}\n", "test-files/strategies/fallback.baml": "// Happy path fallbacks.\nclient FaultyClient {\n provider openai\n options {\n model unknown-model\n api_key env.OPENAI_API_KEY\n }\n}\n\n\nclient FallbackClient {\n provider fallback\n options {\n // first 2 clients are expected to fail.\n strategy [\n FaultyClient,\n RetryClientConstant,\n GPT35\n Gemini\n\n ]\n }\n}\n\nfunction TestFallbackClient() -> string {\n client FallbackClient\n // TODO make it return the client name instead\n prompt #\"\n Say a haiku about mexico.\n \"#\n}\n\n// Fallbacks should fail gracefully.\nclient FaultyAzureClient {\n provider azure-openai\n options {\n model unknown-model\n resource_name \"unknown-resource-id\"\n deployment_id \"unknown-deployment-id\"\n }\n}\n\nclient SingleFallbackClient {\n provider fallback\n options {\n // first 2 clients are expected to fail.\n strategy [\n FaultyAzureClient\n ]\n }\n}\n\nfunction TestSingleFallbackClient() -> string {\n client SingleFallbackClient\n // TODO make it return the client name instead\n prompt #\"\n Say a haiku about mexico.\n \"#\n}\n", "test-files/strategies/retry.baml": "\nretry_policy Exponential {\n max_retries 3\n strategy {\n type exponential_backoff\n }\n}\n\nretry_policy Constant {\n max_retries 3\n strategy {\n type constant_delay\n delay_ms 100\n }\n}\n\nclient RetryClientConstant {\n provider openai\n retry_policy Constant\n options {\n model \"gpt-3.5-turbo\"\n api_key \"blah\"\n }\n}\n\nclient RetryClientExponential {\n provider openai\n retry_policy Exponential\n options {\n model \"gpt-3.5-turbo\"\n api_key \"blahh\"\n }\n}\n\nfunction TestRetryConstant() -> string {\n client RetryClientConstant\n prompt #\"\n Say a haiku\n \"#\n}\n\nfunction TestRetryExponential() -> string {\n client RetryClientExponential\n prompt #\"\n Say a haiku\n \"#\n}\n", diff --git a/integ-tests/typescript/test-report.html b/integ-tests/typescript/test-report.html index b15e66c77..51961906a 100644 --- a/integ-tests/typescript/test-report.html +++ b/integ-tests/typescript/test-report.html @@ -257,1245 +257,4 @@ font-size: 1rem; padding: 0 0.5rem; } -

Test Report

Started: 2024-12-03 22:28:38
Suites (1)
0 passed
1 failed
0 pending
Tests (67)
65 passed
2 failed
0 pending
Integ tests > should work for all inputs
single bool
passed
0.783s
Integ tests > should work for all inputs
single string list
passed
0.427s
Integ tests > should work for all inputs
return literal union
passed
0.379s
Integ tests > should work for all inputs
single class
passed
0.455s
Integ tests > should work for all inputs
multiple classes
passed
0.354s
Integ tests > should work for all inputs
single enum list
passed
0.382s
Integ tests > should work for all inputs
single float
passed
0.424s
Integ tests > should work for all inputs
single int
passed
0.455s
Integ tests > should work for all inputs
single literal int
passed
0.3s
Integ tests > should work for all inputs
single literal bool
passed
0.601s
Integ tests > should work for all inputs
single literal string
passed
0.316s
Integ tests > should work for all inputs
single class with literal prop
passed
0.524s
Integ tests > should work for all inputs
single class with literal union prop
passed
3.24s
Integ tests > should work for all inputs
single optional string
passed
0.43s
Integ tests > should work for all inputs
single map string to string
passed
0.524s
Integ tests > should work for all inputs
single map string to class
passed
0.558s
Integ tests > should work for all inputs
single map string to map
passed
0.521s
Integ tests > should work for all inputs
enum key in map
passed
0.805s
Integ tests > should work for all inputs
literal string union key in map
passed
0.735s
Integ tests > should work for all inputs
single literal string key in map
passed
0.546s
Integ tests
should work for all outputs
passed
4.698s
Integ tests
works with retries1
passed
1.184s
Integ tests
works with retries2
passed
2.143s
Integ tests
works with fallbacks
passed
1.991s
Integ tests
should work with image from url
passed
1.839s
Integ tests
should work with image from base 64
passed
1.328s
Integ tests
should work with audio base 64
passed
1.158s
Integ tests
should work with audio from url
passed
1.171s
Integ tests
should support streaming in OpenAI
passed
3.161s
Integ tests
should support streaming in Gemini
passed
7.739s
Integ tests
should support AWS
passed
1.883s
Integ tests
should support streaming in AWS
passed
1.524s
Integ tests
should allow overriding the region
passed
0.08s
Integ tests
should support OpenAI shorthand
passed
9.47s
Integ tests
should support OpenAI shorthand streaming
passed
7.945s
Integ tests
should support anthropic shorthand
passed
3.566s
Integ tests
should support anthropic shorthand streaming
passed
3.256s
Integ tests
should support streaming without iterating
passed
2.014s
Integ tests
should support streaming in Claude
passed
1.12s
Integ tests
should support vertex
passed
11.387s
Integ tests
supports tracing sync
passed
0.01s
Integ tests
supports tracing async
passed
2.306s
Integ tests
should work with dynamic types single
passed
1.641s
Integ tests
should work with dynamic types enum
passed
0.848s
Integ tests
should work with dynamic literals
passed
0.961s
Integ tests
should work with dynamic types class
passed
1.049s
Integ tests
should work with dynamic inputs class
passed
0.597s
Integ tests
should work with dynamic inputs list
passed
0.995s
Integ tests
should work with dynamic output map
passed
0.759s
Integ tests
should work with dynamic output union
passed
1.62s
Integ tests
should work with nested classes
failed
10.39s
Error: expect(received).toEqual(expected) // deep equality
-
-- Expected  - 1
-+ Received  + 1
-
-  Object {
-    "prop1": "hello",
-    "prop2": Object {
-      "inner": Object {
-        "prop2": 42,
--       "prop3": 3.14,
-+       "prop3": null,
-      },
-      "prop1": "world",
-      "prop2": "again",
-    },
-  }
-    at Object.toEqual (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:604:25)
Integ tests
should work with dynamic client
passed
0.377s
Integ tests
should work with 'onLogEvent'
passed
1.83s
Integ tests
should work with a sync client
passed
0.718s
Integ tests
should raise an error when appropriate
passed
0.734s
Integ tests
should raise a BAMLValidationError
passed
0.496s
Integ tests
should reset environment variables correctly
passed
1.114s
Integ tests
should use aliases when serializing input objects - classes
passed
0.801s
Integ tests
should use aliases when serializing, but still have original keys in jinja
passed
0.944s
Integ tests
should use aliases when serializing input objects - enums
passed
0.531s
Integ tests
should use aliases when serializing input objects - lists
passed
0.518s
Integ tests
constraints: should handle checks in return types
passed
0.703s
Integ tests
constraints: should handle checks in returned unions
passed
0.614s
Integ tests
constraints: should handle block-level checks
passed
0.512s
Integ tests
constraints: should handle nested-block-level checks
passed
0.583s
Integ tests
simple recursive type
passed
3.062s
Integ tests
mutually recursive type
failed
1.974s
Error: expect(received).toEqual(expected) // deep equality
-
-- Expected  - 6
-+ Received  + 5
-
-@@ -4,30 +4,29 @@
-        Object {
-          "children": Object {
-            "trees": Array [
-              Object {
-                "children": Object {
--                 "trees": Array [
-+                 "trees": Array [],
-+               },
-+               "data": 1,
-+             },
-              Object {
-                "children": Object {
-                  "trees": Array [],
-                },
-                "data": 2,
-              },
-            ],
-          },
--               "data": 1,
-+         "data": 3,
-        },
-        Object {
-          "children": Object {
-            "trees": Array [],
-          },
-          "data": 4,
--             },
--           ],
--         },
--         "data": 3,
-        },
-        Object {
-          "children": Object {
-            "trees": Array [
-              Object {
-    at Object.toEqual (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:838:17)
Console Log
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:48:15)
-    at Promise.then.completed (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/utils.js:298:28)
-    at new Promise (<anonymous>)
-    at callAsyncCircusFn (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/utils.js:231:10)
-    at _callCircusTest (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:316:40)
-    at _runTest (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:252:3)
-    at _runTestsForDescribeBlock (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:126:9)
-    at _runTestsForDescribeBlock (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:121:9)
-    at _runTestsForDescribeBlock (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:121:9)
-    at run (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:71:3)
-    at runAndTransformResultsToJestFormat (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:122:21)
-    at jestAdapter (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:79:19)
-    at runTestInternal (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-runner@29.7.0/node_modules/jest-runner/build/runTest.js:367:16)
-    at runTest (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-runner@29.7.0/node_modules/jest-runner/build/runTest.js:444:34)
calling with class
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:54:15)
got response key
-true
-52
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:194:15)
Expected error Error: BamlError: BamlClientError: BamlClientHttpError: LLM call failed: LLMErrorResponse { client: "RetryClientConstant", model: None, prompt: Chat([RenderedChatMessage { role: "system", allow_duplicate_role: false, parts: [Text("Say a haiku")] }]), request_options: {"model": String("gpt-3.5-turbo")}, start_time: SystemTime { tv_sec: 1733293736, tv_nsec: 692524000 }, latency: 260.464458ms, message: "Request failed: https://api.openai.com/v1/chat/completions\n{\n    \"error\": {\n        \"message\": \"Incorrect API key provided: blah. You can find your API key at https://platform.openai.com/account/api-keys.\",\n        \"type\": \"invalid_request_error\",\n        \"param\": null,\n        \"code\": \"invalid_api_key\"\n    }\n}\n", code: InvalidAuthentication }
-    at BamlAsyncClient.parsed [as TestRetryConstant] (/Users/vbv/repos/gloo-lang/integ-tests/typescript/baml_client/async_client.ts:2810:18)
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:191:7) {
-  code: 'GenericFailure'
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:203:15)
Expected error Error: BamlError: BamlClientError: BamlClientHttpError: LLM call failed: LLMErrorResponse { client: "RetryClientExponential", model: None, prompt: Chat([RenderedChatMessage { role: "system", allow_duplicate_role: false, parts: [Text("Say a haiku")] }]), request_options: {"model": String("gpt-3.5-turbo")}, start_time: SystemTime { tv_sec: 1733293738, tv_nsec: 928829000 }, latency: 188.79025ms, message: "Request failed: https://api.openai.com/v1/chat/completions\n{\n    \"error\": {\n        \"message\": \"Incorrect API key provided: blahh. You can find your API key at https://platform.openai.com/account/api-keys.\",\n        \"type\": \"invalid_request_error\",\n        \"param\": null,\n        \"code\": \"invalid_api_key\"\n    }\n}\n", code: InvalidAuthentication }
-    at BamlAsyncClient.parsed [as TestRetryExponential] (/Users/vbv/repos/gloo-lang/integ-tests/typescript/baml_client/async_client.ts:2835:18)
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:200:7) {
-  code: 'GenericFailure'
-}
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:362:15)
-    at func (/Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:83:38)
-    at AsyncLocalStorage.run (node:async_hooks:338:14)
-    at run (/Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:81:22)
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:371:5)
-    at Promise.then.completed (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/utils.js:298:28)
-    at new Promise (<anonymous>)
-    at callAsyncCircusFn (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/utils.js:231:10)
-    at _callCircusTest (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:316:40)
-    at _runTest (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:252:3)
-    at _runTestsForDescribeBlock (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:126:9)
-    at _runTestsForDescribeBlock (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:121:9)
-    at run (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:71:3)
-    at runAndTransformResultsToJestFormat (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:122:21)
-    at jestAdapter (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:79:19)
-    at runTestInternal (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-runner@29.7.0/node_modules/jest-runner/build/runTest.js:367:16)
-    at runTest (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-runner@29.7.0/node_modules/jest-runner/build/runTest.js:444:34)
hello world
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:365:15)
-    at func (/Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:83:38)
-    at AsyncLocalStorage.run (node:async_hooks:338:14)
-    at run (/Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:81:22)
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:371:5)
-    at Promise.then.completed (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/utils.js:298:28)
-    at new Promise (<anonymous>)
-    at callAsyncCircusFn (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/utils.js:231:10)
-    at _callCircusTest (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:316:40)
-    at _runTest (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:252:3)
-    at _runTestsForDescribeBlock (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:126:9)
-    at _runTestsForDescribeBlock (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:121:9)
-    at run (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:71:3)
-    at runAndTransformResultsToJestFormat (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:122:21)
-    at jestAdapter (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:79:19)
-    at runTestInternal (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-runner@29.7.0/node_modules/jest-runner/build/runTest.js:367:16)
-    at runTest (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-runner@29.7.0/node_modules/jest-runner/build/runTest.js:444:34)
dummyFunc returned
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:368:15)
-    at func (/Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:83:38)
-    at AsyncLocalStorage.run (node:async_hooks:338:14)
-    at run (/Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:81:22)
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:371:5)
-    at Promise.then.completed (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/utils.js:298:28)
-    at new Promise (<anonymous>)
-    at callAsyncCircusFn (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/utils.js:231:10)
-    at _callCircusTest (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:316:40)
-    at _runTest (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:252:3)
-    at _runTestsForDescribeBlock (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:126:9)
-    at _runTestsForDescribeBlock (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:121:9)
-    at run (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:71:3)
-    at runAndTransformResultsToJestFormat (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:122:21)
-    at jestAdapter (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:79:19)
-    at runTestInternal (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-runner@29.7.0/node_modules/jest-runner/build/runTest.js:367:16)
-    at runTest (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-runner@29.7.0/node_modules/jest-runner/build/runTest.js:444:34)
dummyFunc2 returned
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:383:15)
-    at runNextTicks (node:internal/process/task_queues:60:5)
-    at listOnTimeout (node:internal/timers:538:9)
-    at processTimers (node:internal/timers:512:7)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at async Promise.all (index 0)
-    at dummyFn (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:389:22)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at async Promise.all (index 0)
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:401:5)
samDummyNested nested1
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:383:15)
-    at runNextTicks (node:internal/process/task_queues:60:5)
-    at listOnTimeout (node:internal/timers:538:9)
-    at processTimers (node:internal/timers:512:7)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at async Promise.all (index 1)
-    at dummyFn (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:389:22)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at async Promise.all (index 0)
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:401:5)
samDummyNested nested2
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:383:15)
-    at runNextTicks (node:internal/process/task_queues:60:5)
-    at listOnTimeout (node:internal/timers:538:9)
-    at processTimers (node:internal/timers:512:7)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at async Promise.all (index 2)
-    at dummyFn (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:389:22)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at async Promise.all (index 0)
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:401:5)
samDummyNested nested3
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:394:15)
-    at runNextTicks (node:internal/process/task_queues:60:5)
-    at listOnTimeout (node:internal/timers:538:9)
-    at processTimers (node:internal/timers:512:7)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at async Promise.all (index 0)
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:401:5)
dummy hi1
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:383:15)
-    at runNextTicks (node:internal/process/task_queues:60:5)
-    at listOnTimeout (node:internal/timers:538:9)
-    at processTimers (node:internal/timers:512:7)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at async Promise.all (index 0)
-    at dummyFn (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:389:22)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at async Promise.all (index 1)
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:401:5)
samDummyNested nested1
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:383:15)
-    at runNextTicks (node:internal/process/task_queues:60:5)
-    at listOnTimeout (node:internal/timers:538:9)
-    at processTimers (node:internal/timers:512:7)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at async Promise.all (index 1)
-    at dummyFn (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:389:22)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at async Promise.all (index 1)
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:401:5)
samDummyNested nested2
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:383:15)
-    at runNextTicks (node:internal/process/task_queues:60:5)
-    at listOnTimeout (node:internal/timers:538:9)
-    at processTimers (node:internal/timers:512:7)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at async Promise.all (index 2)
-    at dummyFn (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:389:22)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at async Promise.all (index 1)
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:401:5)
samDummyNested nested3
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:394:15)
-    at runNextTicks (node:internal/process/task_queues:60:5)
-    at listOnTimeout (node:internal/timers:538:9)
-    at processTimers (node:internal/timers:512:7)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at async Promise.all (index 1)
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:401:5)
dummy hi2
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:383:15)
-    at runNextTicks (node:internal/process/task_queues:60:5)
-    at listOnTimeout (node:internal/timers:538:9)
-    at processTimers (node:internal/timers:512:7)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at async Promise.all (index 0)
-    at dummyFn (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:389:22)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at async Promise.all (index 2)
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:401:5)
samDummyNested nested1
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:383:15)
-    at runNextTicks (node:internal/process/task_queues:60:5)
-    at listOnTimeout (node:internal/timers:538:9)
-    at processTimers (node:internal/timers:512:7)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at async Promise.all (index 1)
-    at dummyFn (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:389:22)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at async Promise.all (index 2)
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:401:5)
samDummyNested nested2
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:383:15)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at async Promise.all (index 2)
-    at dummyFn (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:389:22)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at async Promise.all (index 2)
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:401:5)
samDummyNested nested3
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:394:15)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at async Promise.all (index 2)
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:401:5)
dummy hi3
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:408:15)
-    at func (/Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:44)
-    at AsyncLocalStorage.run (node:async_hooks:338:14)
-    at run (/Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:28)
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:424:5)
hello world
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:383:15)
-    at runNextTicks (node:internal/process/task_queues:60:5)
-    at listOnTimeout (node:internal/timers:538:9)
-    at processTimers (node:internal/timers:512:7)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at async Promise.all (index 0)
-    at dummyFn (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:389:22)
samDummyNested nested1
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:383:15)
-    at runNextTicks (node:internal/process/task_queues:60:5)
-    at listOnTimeout (node:internal/timers:538:9)
-    at processTimers (node:internal/timers:512:7)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at async Promise.all (index 1)
-    at dummyFn (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:389:22)
samDummyNested nested2
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:383:15)
-    at runNextTicks (node:internal/process/task_queues:60:5)
-    at listOnTimeout (node:internal/timers:538:9)
-    at processTimers (node:internal/timers:512:7)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at async Promise.all (index 2)
-    at dummyFn (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:389:22)
samDummyNested nested3
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:394:15)
-    at runNextTicks (node:internal/process/task_queues:60:5)
-    at listOnTimeout (node:internal/timers:538:9)
-    at processTimers (node:internal/timers:512:7)
dummy firstDummyFuncArg
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:383:15)
-    at runNextTicks (node:internal/process/task_queues:60:5)
-    at listOnTimeout (node:internal/timers:538:9)
-    at processTimers (node:internal/timers:512:7)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at async Promise.all (index 0)
-    at dummyFn (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:389:22)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at /Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:413:20
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:407:17)
samDummyNested nested1
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:383:15)
-    at runNextTicks (node:internal/process/task_queues:60:5)
-    at listOnTimeout (node:internal/timers:538:9)
-    at processTimers (node:internal/timers:512:7)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at async Promise.all (index 1)
-    at dummyFn (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:389:22)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at /Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:413:20
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:407:17)
samDummyNested nested2
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:383:15)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at async Promise.all (index 2)
-    at dummyFn (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:389:22)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at /Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:413:20
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:407:17)
samDummyNested nested3
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:394:15)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at /Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:413:20
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:407:17)
dummy secondDummyFuncArg
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:383:15)
-    at runNextTicks (node:internal/process/task_queues:60:5)
-    at listOnTimeout (node:internal/timers:538:9)
-    at processTimers (node:internal/timers:512:7)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at async Promise.all (index 0)
-    at dummyFn (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:389:22)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at /Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:421:20
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:407:17)
samDummyNested nested1
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:383:15)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at async Promise.all (index 1)
-    at dummyFn (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:389:22)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at /Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:421:20
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:407:17)
samDummyNested nested2
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:383:15)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at async Promise.all (index 2)
-    at dummyFn (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:389:22)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at /Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:421:20
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:407:17)
samDummyNested nested3
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:394:15)
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at /Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:421:20
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:38
-    at /Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:13
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:407:17)
dummy thirdDummyFuncArg
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:427:15)
-    at func (/Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:104:44)
-    at AsyncLocalStorage.run (node:async_hooks:338:14)
-    at run (/Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:102:28)
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:433:5)
hello world
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:437:13)
stats {"failed":0,"started":30,"finalized":30,"submitted":30,"sent":30,"done":30}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:461:13)
[
-  {
-    name: 'Harrison',
-    hair_color: 'BLACK',
-    last_name: null,
-    height: 1.83,
-    hobbies: [ 'SPORTS' ]
-  }
-]
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:530:13)
-    at Promise.then.completed (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/utils.js:298:28)
-    at new Promise (<anonymous>)
-    at callAsyncCircusFn (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/utils.js:231:10)
-    at _callCircusTest (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:316:40)
-    at _runTest (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:252:3)
-    at _runTestsForDescribeBlock (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:126:9)
-    at _runTestsForDescribeBlock (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:121:9)
-    at run (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:71:3)
-    at runAndTransformResultsToJestFormat (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:122:21)
-    at jestAdapter (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:79:19)
-    at runTestInternal (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-runner@29.7.0/node_modules/jest-runner/build/runTest.js:367:16)
-    at runTest (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-runner@29.7.0/node_modules/jest-runner/build/runTest.js:444:34)
[
-  [
-    'hair_color',
-    ClassPropertyBuilder { bldr: ClassPropertyBuilder {} }
-  ],
-  [
-    'attributes',
-    ClassPropertyBuilder { bldr: ClassPropertyBuilder {} }
-  ]
-]
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:532:15)
-    at Promise.then.completed (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/utils.js:298:28)
-    at new Promise (<anonymous>)
-    at callAsyncCircusFn (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/utils.js:231:10)
-    at _callCircusTest (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:316:40)
-    at _runTest (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:252:3)
-    at _runTestsForDescribeBlock (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:126:9)
-    at _runTestsForDescribeBlock (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:121:9)
-    at run (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:71:3)
-    at runAndTransformResultsToJestFormat (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:122:21)
-    at jestAdapter (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:79:19)
-    at runTestInternal (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-runner@29.7.0/node_modules/jest-runner/build/runTest.js:367:16)
-    at runTest (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-runner@29.7.0/node_modules/jest-runner/build/runTest.js:444:34)
Property: hair_color
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:532:15)
-    at Promise.then.completed (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/utils.js:298:28)
-    at new Promise (<anonymous>)
-    at callAsyncCircusFn (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/utils.js:231:10)
-    at _callCircusTest (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:316:40)
-    at _runTest (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:252:3)
-    at _runTestsForDescribeBlock (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:126:9)
-    at _runTestsForDescribeBlock (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:121:9)
-    at run (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:71:3)
-    at runAndTransformResultsToJestFormat (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:122:21)
-    at jestAdapter (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:79:19)
-    at runTestInternal (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-runner@29.7.0/node_modules/jest-runner/build/runTest.js:367:16)
-    at runTest (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-runner@29.7.0/node_modules/jest-runner/build/runTest.js:444:34)
Property: attributes
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:540:13)
final  {
-  hair_color: 'black',
-  attributes: { height: '6 feet', eye_color: 'blue', facial_hair: 'beard' }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:564:13)
-    at Promise.then.completed (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/utils.js:298:28)
-    at new Promise (<anonymous>)
-    at callAsyncCircusFn (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/utils.js:231:10)
-    at _callCircusTest (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:316:40)
-    at _runTest (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:252:3)
-    at _runTestsForDescribeBlock (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:126:9)
-    at _runTestsForDescribeBlock (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:121:9)
-    at run (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:71:3)
-    at runAndTransformResultsToJestFormat (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:122:21)
-    at jestAdapter (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:79:19)
-    at runTestInternal (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-runner@29.7.0/node_modules/jest-runner/build/runTest.js:367:16)
-    at runTest (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-runner@29.7.0/node_modules/jest-runner/build/runTest.js:444:34)
[
-  [
-    'hair_color',
-    ClassPropertyBuilder { bldr: ClassPropertyBuilder {} }
-  ],
-  [
-    'attributes',
-    ClassPropertyBuilder { bldr: ClassPropertyBuilder {} }
-  ],
-  [ 'height', ClassPropertyBuilder { bldr: ClassPropertyBuilder {} } ]
-]
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:566:15)
-    at Promise.then.completed (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/utils.js:298:28)
-    at new Promise (<anonymous>)
-    at callAsyncCircusFn (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/utils.js:231:10)
-    at _callCircusTest (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:316:40)
-    at _runTest (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:252:3)
-    at _runTestsForDescribeBlock (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:126:9)
-    at _runTestsForDescribeBlock (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:121:9)
-    at run (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:71:3)
-    at runAndTransformResultsToJestFormat (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:122:21)
-    at jestAdapter (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:79:19)
-    at runTestInternal (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-runner@29.7.0/node_modules/jest-runner/build/runTest.js:367:16)
-    at runTest (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-runner@29.7.0/node_modules/jest-runner/build/runTest.js:444:34)
Property: hair_color
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:566:15)
-    at Promise.then.completed (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/utils.js:298:28)
-    at new Promise (<anonymous>)
-    at callAsyncCircusFn (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/utils.js:231:10)
-    at _callCircusTest (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:316:40)
-    at _runTest (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:252:3)
-    at _runTestsForDescribeBlock (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:126:9)
-    at _runTestsForDescribeBlock (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:121:9)
-    at run (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:71:3)
-    at runAndTransformResultsToJestFormat (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:122:21)
-    at jestAdapter (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:79:19)
-    at runTestInternal (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-runner@29.7.0/node_modules/jest-runner/build/runTest.js:367:16)
-    at runTest (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-runner@29.7.0/node_modules/jest-runner/build/runTest.js:444:34)
Property: attributes
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:566:15)
-    at Promise.then.completed (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/utils.js:298:28)
-    at new Promise (<anonymous>)
-    at callAsyncCircusFn (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/utils.js:231:10)
-    at _callCircusTest (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:316:40)
-    at _runTest (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:252:3)
-    at _runTestsForDescribeBlock (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:126:9)
-    at _runTestsForDescribeBlock (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:121:9)
-    at run (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:71:3)
-    at runAndTransformResultsToJestFormat (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:122:21)
-    at jestAdapter (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:79:19)
-    at runTestInternal (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-runner@29.7.0/node_modules/jest-runner/build/runTest.js:367:16)
-    at runTest (/Users/vbv/repos/gloo-lang/integ-tests/typescript/node_modules/.pnpm/jest-runner@29.7.0/node_modules/jest-runner/build/runTest.js:444:34)
Property: height
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:13)
final  {
-  hair_color: 'black',
-  attributes: { eye_color: 'blue', facial_hair: 'beard' },
-  height: { feet: 6, inches: null }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:585:13)
final  {
-  hair_color: 'black',
-  attributes: { eye_color: 'blue', facial_hair: 'beard' },
-  height: { meters: 1.8 }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg { prop1: null, prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg { prop1: null, prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg { prop1: null, prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg { prop1: null, prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg { prop1: null, prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg { prop1: null, prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg { prop1: null, prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg { prop1: '', prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg { prop1: 'hello', prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg { prop1: 'hello', prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg { prop1: 'hello', prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg { prop1: 'hello', prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg { prop1: 'hello', prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg { prop1: 'hello', prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg { prop1: 'hello', prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg { prop1: 'hello', prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg { prop1: 'hello', prop2: { prop1: null, prop2: null, inner: null } }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg { prop1: 'hello', prop2: { prop1: null, prop2: null, inner: null } }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg { prop1: 'hello', prop2: { prop1: null, prop2: null, inner: null } }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg { prop1: 'hello', prop2: { prop1: null, prop2: null, inner: null } }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg { prop1: 'hello', prop2: { prop1: null, prop2: null, inner: null } }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg { prop1: 'hello', prop2: { prop1: null, prop2: null, inner: null } }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg { prop1: 'hello', prop2: { prop1: null, prop2: null, inner: null } }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg { prop1: 'hello', prop2: { prop1: '', prop2: null, inner: null } }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg { prop1: 'hello', prop2: { prop1: 'world', prop2: null, inner: null } }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg { prop1: 'hello', prop2: { prop1: 'world', prop2: null, inner: null } }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg { prop1: 'hello', prop2: { prop1: 'world', prop2: null, inner: null } }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg { prop1: 'hello', prop2: { prop1: 'world', prop2: null, inner: null } }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg { prop1: 'hello', prop2: { prop1: 'world', prop2: null, inner: null } }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg { prop1: 'hello', prop2: { prop1: 'world', prop2: null, inner: null } }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg { prop1: 'hello', prop2: { prop1: 'world', prop2: null, inner: null } }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg { prop1: 'hello', prop2: { prop1: 'world', prop2: null, inner: null } }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg { prop1: 'hello', prop2: { prop1: 'world', prop2: '', inner: null } }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg { prop1: 'hello', prop2: { prop1: 'world', prop2: 'ag', inner: null } }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: null }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: null }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: null }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: null }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: null }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: null }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: null }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: {
-    prop1: 'world',
-    prop2: 'again',
-    inner: { prop2: null, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: {
-    prop1: 'world',
-    prop2: 'again',
-    inner: { prop2: null, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: {
-    prop1: 'world',
-    prop2: 'again',
-    inner: { prop2: null, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: {
-    prop1: 'world',
-    prop2: 'again',
-    inner: { prop2: null, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: {
-    prop1: 'world',
-    prop2: 'again',
-    inner: { prop2: null, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: {
-    prop1: 'world',
-    prop2: 'again',
-    inner: { prop2: null, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: {
-    prop1: 'world',
-    prop2: 'again',
-    inner: { prop2: null, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: {
-    prop1: 'world',
-    prop2: 'again',
-    inner: { prop2: null, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: {
-    prop1: 'world',
-    prop2: 'again',
-    inner: { prop2: null, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: {
-    prop1: 'world',
-    prop2: 'again',
-    inner: { prop2: null, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: null } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: null } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: null } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: null } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: null } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: {
-    prop1: 'world',
-    prop2: 'again',
-    inner: { prop2: null, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: null } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: null } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: null } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: null } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: null } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: null } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: null } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: null } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: null } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: null } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: null } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: null } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: null } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: null } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: null } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: null } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: null } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: null } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: null } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: null } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: null } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: null } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: null } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: null } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: 3.14 } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: null } }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:598:15)
msg {
-  prop1: 'hello',
-  prop2: { prop1: 'world', prop2: 'again', inner: { prop2: 42, prop3: null } }
-}
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:623:15)
-    at callback (/Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:70:17)
onLogEvent {
-  metadata: {
-    eventId: '3ba42dd3-7328-4270-b476-0d52a0c451b9',
-    rootEventId: '3ba42dd3-7328-4270-b476-0d52a0c451b9'
-  },
-  prompt: '[\n' +
-    '  {\n' +
-    '    "role": "system",\n' +
-    '    "content": [\n' +
-    '      {\n' +
-    '        "text": "Return this value back to me: [\\"a\\", \\"b\\", \\"c\\"]"\n' +
-    '      }\n' +
-    '    ]\n' +
-    '  }\n' +
-    ']',
-  rawOutput: '["a", "b", "c"]',
-  parsedOutput: '["a", "b", "c"]',
-  startTime: '2024-12-04T06:30:21.857Z'
-}
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:623:15)
-    at callback (/Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:70:17)
onLogEvent {
-  metadata: {
-    eventId: '85f3901f-ce3e-4eda-bf67-1e6b1164a424',
-    rootEventId: '85f3901f-ce3e-4eda-bf67-1e6b1164a424'
-  },
-  prompt: '[\n' +
-    '  {\n' +
-    '    "role": "system",\n' +
-    '    "content": [\n' +
-    '      {\n' +
-    '        "text": "Return this value back to me: [\\"d\\", \\"e\\", \\"f\\"]"\n' +
-    '      }\n' +
-    '    ]\n' +
-    '  }\n' +
-    ']',
-  rawOutput: '["d", "e", "f"]',
-  parsedOutput: '["d", "e", "f"]',
-  startTime: '2024-12-04T06:30:22.354Z'
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:657:15)
Error: Error: BamlError: BamlClientError: BamlClientHttpError: LLM call failed: LLMErrorResponse { client: "MyClient", model: None, prompt: Chat([RenderedChatMessage { role: "system", allow_duplicate_role: false, parts: [Text("Given a string, extract info using the schema:\n\nMy name is Harrison. My hair is black and I'm 6 feet tall.\n\nAnswer in JSON using this schema:\n{\n}")] }]), request_options: {"model": String("gpt-4o-mini")}, start_time: SystemTime { tv_sec: 1733293824, tv_nsec: 6034000 }, latency: 139.540125ms, message: "Request failed: https://api.openai.com/v1/chat/completions\n{\n    \"error\": {\n        \"message\": \"Incorrect API key provided: INVALID_KEY. You can find your API key at https://platform.openai.com/account/api-keys.\",\n        \"type\": \"invalid_request_error\",\n        \"param\": null,\n        \"code\": \"invalid_api_key\"\n    }\n}\n", code: InvalidAuthentication }
-    at BamlAsyncClient.parsed (/Users/vbv/repos/gloo-lang/integ-tests/typescript/baml_client/async_client.ts:1585:18)
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:654:7) {
-  code: 'GenericFailure'
-}
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:665:17)
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:661:5)
BamlValidationError: BamlValidationError: Failed to parse LLM response: Failed to coerce value: <root>: Failed while parsing required fields: missing=2, unparsed=0
-  - <root>: Missing required field: nonce
-  - <root>: Missing required field: nonce2
-    at Function.from (/Users/vbv/repos/gloo-lang/engine/language_client_typescript/index.js:79:28)
-    at from (/Users/vbv/repos/gloo-lang/engine/language_client_typescript/index.js:92:53)
-    at BamlAsyncClient.DummyOutputFunction (/Users/vbv/repos/gloo-lang/integ-tests/typescript/baml_client/async_client.ts:562:50)
-    at /Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:663:9
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:661:5) {
-  prompt: '[\x1B[2mchat\x1B[0m] \x1B[43msystem: \x1B[0mSay "hello there".\n',
-  raw_output: 'Hello there! How can I assist you today?'
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:677:17)
error BamlValidationError: BamlValidationError: Failed to parse LLM response: Failed to coerce value: <root>: Failed while parsing required fields: missing=2, unparsed=0
-  - <root>: Missing required field: nonce
-  - <root>: Missing required field: nonce2
-    at Function.from (/Users/vbv/repos/gloo-lang/engine/language_client_typescript/index.js:79:28)
-    at from (/Users/vbv/repos/gloo-lang/engine/language_client_typescript/index.js:92:53)
-    at BamlAsyncClient.DummyOutputFunction (/Users/vbv/repos/gloo-lang/integ-tests/typescript/baml_client/async_client.ts:562:50)
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:673:7) {
-  prompt: '[\x1B[2mchat\x1B[0m] \x1B[43msystem: \x1B[0mSay "hello there".\n',
-  raw_output: 'Hello there! How can I assist you today?'
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:777:13)
{"nbc":{"value":{"foo":1,"bar":"hello"},"checks":{"cross_field":{"name":"cross_field","expression":"this.bar|length > this.foo","status":"succeeded"}}}}
\ No newline at end of file +

Test Report

Started: 2024-12-12 15:52:09
Suites (1)
1 passed
0 failed
0 pending
Tests (67)
1 passed
0 failed
66 pending
Integ tests > should work for all inputs
single bool
pending
0s
Integ tests > should work for all inputs
single string list
pending
0s
Integ tests > should work for all inputs
return literal union
pending
0s
Integ tests > should work for all inputs
single class
pending
0s
Integ tests > should work for all inputs
multiple classes
pending
0s
Integ tests > should work for all inputs
single enum list
pending
0s
Integ tests > should work for all inputs
single float
pending
0s
Integ tests > should work for all inputs
single int
pending
0s
Integ tests > should work for all inputs
single literal int
pending
0s
Integ tests > should work for all inputs
single literal bool
pending
0s
Integ tests > should work for all inputs
single literal string
pending
0s
Integ tests > should work for all inputs
single class with literal prop
pending
0s
Integ tests > should work for all inputs
single class with literal union prop
pending
0s
Integ tests > should work for all inputs
single optional string
pending
0s
Integ tests > should work for all inputs
single map string to string
pending
0s
Integ tests > should work for all inputs
single map string to class
pending
0s
Integ tests > should work for all inputs
single map string to map
pending
0s
Integ tests > should work for all inputs
enum key in map
pending
0s
Integ tests > should work for all inputs
literal string union key in map
pending
0s
Integ tests > should work for all inputs
single literal string key in map
pending
0s
Integ tests
should work for all outputs
pending
0s
Integ tests
works with retries1
pending
0s
Integ tests
works with retries2
pending
0s
Integ tests
works with fallbacks
pending
0s
Integ tests
should work with image from url
pending
0s
Integ tests
should work with image from base 64
pending
0s
Integ tests
should work with audio base 64
pending
0s
Integ tests
should work with audio from url
pending
0s
Integ tests
should support streaming in OpenAI
pending
0s
Integ tests
should support streaming in Gemini
pending
0s
Integ tests
should support AWS
pending
0s
Integ tests
should support streaming in AWS
pending
0s
Integ tests
should allow overriding the region
pending
0s
Integ tests
should support OpenAI shorthand
pending
0s
Integ tests
should support OpenAI shorthand streaming
passed
9.347s
Integ tests
should support anthropic shorthand
pending
0s
Integ tests
should support anthropic shorthand streaming
pending
0s
Integ tests
should support streaming without iterating
pending
0s
Integ tests
should support streaming in Claude
pending
0s
Integ tests
should support vertex
pending
0s
Integ tests
supports tracing sync
pending
0s
Integ tests
supports tracing async
pending
0s
Integ tests
should work with dynamic types single
pending
0s
Integ tests
should work with dynamic types enum
pending
0s
Integ tests
should work with dynamic literals
pending
0s
Integ tests
should work with dynamic types class
pending
0s
Integ tests
should work with dynamic inputs class
pending
0s
Integ tests
should work with dynamic inputs list
pending
0s
Integ tests
should work with dynamic output map
pending
0s
Integ tests
should work with dynamic output union
pending
0s
Integ tests
should work with nested classes
pending
0s
Integ tests
should work with dynamic client
pending
0s
Integ tests
should work with 'onLogEvent'
pending
0s
Integ tests
should work with a sync client
pending
0s
Integ tests
should raise an error when appropriate
pending
0s
Integ tests
should raise a BAMLValidationError
pending
0s
Integ tests
should reset environment variables correctly
pending
0s
Integ tests
should use aliases when serializing input objects - classes
pending
0s
Integ tests
should use aliases when serializing, but still have original keys in jinja
pending
0s
Integ tests
should use aliases when serializing input objects - enums
pending
0s
Integ tests
should use aliases when serializing input objects - lists
pending
0s
Integ tests
constraints: should handle checks in return types
pending
0s
Integ tests
constraints: should handle checks in returned unions
pending
0s
Integ tests
constraints: should handle block-level checks
pending
0s
Integ tests
constraints: should handle nested-block-level checks
pending
0s
Integ tests
simple recursive type
pending
0s
Integ tests
mutually recursive type
pending
0s
\ No newline at end of file diff --git a/integ-tests/typescript/tests/integ-tests.test.ts b/integ-tests/typescript/tests/integ-tests.test.ts index 3ad7066d1..9005f8f97 100644 --- a/integ-tests/typescript/tests/integ-tests.test.ts +++ b/integ-tests/typescript/tests/integ-tests.test.ts @@ -25,6 +25,8 @@ import { DO_NOT_USE_DIRECTLY_UNLESS_YOU_KNOW_WHAT_YOURE_DOING_RUNTIME, resetBaml import exp from 'constants' config() +jest.setTimeout(15_000) + describe('Integ tests', () => { describe('should work for all inputs', () => { it('single bool', async () => { @@ -310,7 +312,7 @@ describe('Integ tests', () => { expect(res.length).toBeGreaterThan(0) }) - it('should support OpenAI shorthand streaming', async () => { + it.only('should support OpenAI shorthand streaming', async () => { const res = await b.stream.TestOpenAIShorthand('Dr. Pepper').getFinalResponse() expect(res.length).toBeGreaterThan(0) }) diff --git a/tools/build b/tools/build index 67935c35e..776d8c4ef 100755 --- a/tools/build +++ b/tools/build @@ -160,7 +160,7 @@ case "$_path" in --ext rs,hb,hbs,j2,toml \ --watch "${_repo_root}/engine" \ --ignore 'target/**' \ - --exec "${command}" + --exec "${command}" --verbose else eval "${command}" fi @@ -173,7 +173,7 @@ case "$_path" in if [ "$_test_mode" -eq 1 ]; then #command="${command} && BAML_LOG=trace infisical run --env=test -- poetry run python baml_example_tracing.py" # command="${command} && BAML_LOG=trace infisical run --env=test -- poetry run pytest -s tests/test_functions.py::test_aws_bedrock_invalid_region" - command="${command} && BAML_LOG=trace infisical run --env=test -- poetry run pytest" + command="${command} && BAML_LOG=trace infisical run --env=test -- poetry run pytest -s tests/test_functions.py::test_fallback_to_shorthand" fi if [ "$_watch_mode" -eq 1 ]; then npx nodemon \ From 5e439b42bec506b034e8cc03d74212e6345ef8d4 Mon Sep 17 00:00:00 2001 From: Sam Lijin Date: Thu, 19 Dec 2024 13:49:22 -0800 Subject: [PATCH 4/8] compile again --- engine/Cargo.lock | 8 +------- engine/Cargo.toml | 13 ++++++++----- engine/baml-runtime/Cargo.toml | 12 ++++++------ .../src/internal/llm_client/orchestrator/mod.rs | 2 +- .../src/internal/llm_client/orchestrator/stream.rs | 2 +- .../primitive/anthropic/anthropic_client.rs | 10 ++++------ .../internal/llm_client/primitive/aws/aws_client.rs | 4 ++-- .../llm_client/primitive/google/googleai_client.rs | 4 ++-- .../llm_client/primitive/openai/openai_client.rs | 4 ++-- .../llm_client/primitive/vertex/vertex_client.rs | 4 ++-- .../src/internal/llm_client/traits/mod.rs | 3 ++- engine/baml-runtime/src/lib.rs | 1 - .../baml-runtime/src/runtime/runtime_interface.rs | 1 - engine/baml-runtime/src/runtime_interface.rs | 2 +- engine/baml-runtime/src/types/stream.rs | 2 +- engine/btrace/Cargo.toml | 2 +- engine/btrace/{ => src}/tracer_thread.rs | 2 -- 17 files changed, 34 insertions(+), 42 deletions(-) rename engine/btrace/{ => src}/tracer_thread.rs (98%) diff --git a/engine/Cargo.lock b/engine/Cargo.lock index fd95a5f29..a2c0ea492 100644 --- a/engine/Cargo.lock +++ b/engine/Cargo.lock @@ -932,6 +932,7 @@ dependencies = [ "axum-extra", "baml-types", "base64 0.22.1", + "btrace", "bytes", "cfg-if", "chrono", @@ -971,7 +972,6 @@ dependencies = [ "mime", "mime_guess", "minijinja", - "nonempty", "notify-debouncer-full", "pin-project-lite", "pretty_assertions", @@ -3217,12 +3217,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "nonempty" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "549e471b99ccaf2f89101bec68f4d244457d5a95a9c3d0672e9564124397741d" - [[package]] name = "notify" version = "6.1.1" diff --git a/engine/Cargo.toml b/engine/Cargo.toml index 159e1cd0b..78c2e51f1 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -35,8 +35,10 @@ anyhow = "1.0" array-macro = "2" askama = "0.12.1" baml-cli = { path = "cli" } +baml-types = { path = "baml-lib/baml-types" } base64 = "0.22.1" bstd = { path = "bstd" } +btrace = { path = "btrace" } bytes = "1.6.0" cfg-if = "1.0.0" clap = { version = "4.4.6", features = ["cargo", "derive"] } @@ -50,6 +52,12 @@ http = "1.1.0" http-body = "1.0.0" indexmap = { version = "2.1.0", features = ["serde"] } indoc = "2.0.5" +jsonish = { path = "baml-lib/jsonish" } +internal-baml-codegen = { path = "language_client_codegen" } +internal-baml-core = { path = "baml-lib/baml-core" } +internal-baml-jinja = { path = "baml-lib/jinja-runtime" } +internal-baml-schema-ast = { path = "baml-lib/schema-ast" } +internal-llm-client = { path = "baml-lib/llm-client" } log = "0.4.20" # TODO: disable imports, etc minijinja = { version = "1.0.16", default-features = false, features = [ @@ -92,11 +100,6 @@ uuid = { version = "1.8.0", features = ["v4", "v7", "serde"] } walkdir = "2.5.0" web-time = "1.1.0" -baml-types = { path = "baml-lib/baml-types" } -internal-baml-codegen = { path = "language_client_codegen" } -internal-baml-core = { path = "baml-lib/baml-core" } -internal-baml-jinja = { path = "baml-lib/jinja" } -internal-baml-schema-ast = { path = "baml-lib/schema-ast" } [workspace.package] version = "0.70.1" diff --git a/engine/baml-runtime/Cargo.toml b/engine/baml-runtime/Cargo.toml index e4433314b..fc08dd52e 100644 --- a/engine/baml-runtime/Cargo.toml +++ b/engine/baml-runtime/Cargo.toml @@ -20,6 +20,7 @@ unused_variables = "allow" [dependencies] anyhow.workspace = true base64.workspace = true +btrace.workspace = true bytes.workspace = true cfg-if.workspace = true clap.workspace = true @@ -37,15 +38,14 @@ indexmap.workspace = true indoc.workspace = true # instant = "0.1" # do not use this or wasm-timer - use web-time instead json_comments = "0.2.2" -jsonish = { path = "../baml-lib/jsonish" } +jsonish.workspace = true internal-baml-codegen.workspace = true -baml-types = { path = "../baml-lib/baml-types" } -internal-llm-client = { path = "../baml-lib/llm-client" } -internal-baml-core = { path = "../baml-lib/baml-core" } -internal-baml-jinja = { path = "../baml-lib/jinja-runtime" } +baml-types.workspace = true +internal-llm-client.workspace = true +internal-baml-core.workspace = true +internal-baml-jinja.workspace = true log.workspace = true minijinja.workspace = true -nonempty = "0.11.0" pin-project-lite.workspace = true reqwest-eventsource = "0.6.0" scopeguard.workspace = true 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 8a9238b60..ff4111c38 100644 --- a/engine/baml-runtime/src/internal/llm_client/orchestrator/mod.rs +++ b/engine/baml-runtime/src/internal/llm_client/orchestrator/mod.rs @@ -4,7 +4,7 @@ mod stream; use serde_json::json; use web_time::Duration; // Add this line -use crate::btrace::WithTraceContext; +use btrace::WithTraceContext; use crate::RenderCurlSettings; use crate::{ internal::prompt_renderer::PromptRenderer, runtime_interface::InternalClientLookup, 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 8561c9424..91570b06a 100644 --- a/engine/baml-runtime/src/internal/llm_client/orchestrator/stream.rs +++ b/engine/baml-runtime/src/internal/llm_client/orchestrator/stream.rs @@ -1,13 +1,13 @@ use anyhow::Result; use async_std::stream::StreamExt; use baml_types::BamlValue; +use btrace::WithTraceContext; use internal_baml_core::ir::repr::IntermediateRepr; use jsonish::BamlValueWithFlags; use serde_json::json; use web_time::Duration; use crate::{ - btrace::{self, WithTraceContext}, internal::{ llm_client::{ parsed_value_to_response, diff --git a/engine/baml-runtime/src/internal/llm_client/primitive/anthropic/anthropic_client.rs b/engine/baml-runtime/src/internal/llm_client/primitive/anthropic/anthropic_client.rs index f947c412a..1109b8642 100644 --- a/engine/baml-runtime/src/internal/llm_client/primitive/anthropic/anthropic_client.rs +++ b/engine/baml-runtime/src/internal/llm_client/primitive/anthropic/anthropic_client.rs @@ -1,10 +1,8 @@ -use crate::{ - btrace::{self, WithTraceContext}, - internal::llm_client::{ - traits::{ToProviderMessage, ToProviderMessageExt, WithClientProperties}, - ResolveMediaUrls, - }, +use crate::internal::llm_client::{ + traits::{ToProviderMessage, ToProviderMessageExt, WithClientProperties}, + ResolveMediaUrls, }; +use btrace::WithTraceContext; use std::collections::HashMap; use anyhow::{Context, Result}; diff --git a/engine/baml-runtime/src/internal/llm_client/primitive/aws/aws_client.rs b/engine/baml-runtime/src/internal/llm_client/primitive/aws/aws_client.rs index 55217f654..2140691fb 100644 --- a/engine/baml-runtime/src/internal/llm_client/primitive/aws/aws_client.rs +++ b/engine/baml-runtime/src/internal/llm_client/primitive/aws/aws_client.rs @@ -23,7 +23,6 @@ use serde_json::{json, Map}; use web_time::Instant; use web_time::SystemTime; -use crate::btrace::WithTraceContext; use crate::client_registry::ClientProperty; use crate::internal::llm_client::traits::{ToProviderMessageExt, WithClientProperties}; use crate::internal::llm_client::{ @@ -35,8 +34,9 @@ use crate::internal::llm_client::{ ErrorCode, LLMCompleteResponse, LLMCompleteResponseMetadata, LLMErrorResponse, LLMResponse, ModelFeatures, ResolveMediaUrls, }; +use btrace::WithTraceContext; -use crate::{btrace, RenderCurlSettings, RuntimeContext}; +use crate::{RenderCurlSettings, RuntimeContext}; // represents client that interacts with the Anthropic API pub struct AwsClient { diff --git a/engine/baml-runtime/src/internal/llm_client/primitive/google/googleai_client.rs b/engine/baml-runtime/src/internal/llm_client/primitive/google/googleai_client.rs index 7002c3d60..d7c785371 100644 --- a/engine/baml-runtime/src/internal/llm_client/primitive/google/googleai_client.rs +++ b/engine/baml-runtime/src/internal/llm_client/primitive/google/googleai_client.rs @@ -1,10 +1,9 @@ -use crate::btrace::WithTraceContext; use crate::client_registry::ClientProperty; use crate::internal::llm_client::traits::{ ToProviderMessage, ToProviderMessageExt, WithClientProperties, }; use crate::internal::llm_client::ResolveMediaUrls; -use crate::{btrace, RuntimeContext}; +use crate::RuntimeContext; use crate::{ internal::llm_client::{ primitive::{ @@ -22,6 +21,7 @@ use crate::{ }; use anyhow::{Context, Result}; use baml_types::{BamlMap, BamlMedia, BamlMediaContent}; +use btrace::WithTraceContext; use eventsource_stream::Eventsource; use futures::StreamExt; use http::header; diff --git a/engine/baml-runtime/src/internal/llm_client/primitive/openai/openai_client.rs b/engine/baml-runtime/src/internal/llm_client/primitive/openai/openai_client.rs index 6b0b86ec2..0c9a2c129 100644 --- a/engine/baml-runtime/src/internal/llm_client/primitive/openai/openai_client.rs +++ b/engine/baml-runtime/src/internal/llm_client/primitive/openai/openai_client.rs @@ -1,9 +1,9 @@ use std::collections::HashMap; -use crate::btrace::WithTraceContext; use crate::internal::llm_client::ResolveMediaUrls; use anyhow::Result; use baml_types::{BamlMap, BamlMedia, BamlMediaContent, BamlMediaType}; +use btrace::WithTraceContext; use internal_baml_core::ir::ClientWalker; use internal_baml_jinja::{ChatMessagePart, RenderContext_Client, RenderedChatMessage}; use internal_llm_client::openai::ResolvedOpenAI; @@ -31,7 +31,7 @@ use crate::internal::llm_client::{ }; use crate::request::create_client; -use crate::{btrace, RuntimeContext}; +use crate::RuntimeContext; use eventsource_stream::Eventsource; use futures::StreamExt; diff --git a/engine/baml-runtime/src/internal/llm_client/primitive/vertex/vertex_client.rs b/engine/baml-runtime/src/internal/llm_client/primitive/vertex/vertex_client.rs index 3825c658b..7c1a2c05c 100644 --- a/engine/baml-runtime/src/internal/llm_client/primitive/vertex/vertex_client.rs +++ b/engine/baml-runtime/src/internal/llm_client/primitive/vertex/vertex_client.rs @@ -1,4 +1,3 @@ -use crate::btrace::WithTraceContext; use crate::client_registry::ClientProperty; use crate::internal::llm_client::traits::{ ToProviderMessage, ToProviderMessageExt, WithClientProperties, @@ -6,7 +5,7 @@ use crate::internal::llm_client::traits::{ use crate::internal::llm_client::ResolveMediaUrls; #[cfg(target_arch = "wasm32")] use crate::internal::wasm_jwt::{encode_jwt, JwtError}; -use crate::{btrace, RuntimeContext}; +use crate::RuntimeContext; use crate::{ internal::llm_client::{ primitive::{ @@ -23,6 +22,7 @@ use crate::{ request::create_client, }; use anyhow::{Context, Result}; +use btrace::WithTraceContext; use chrono::{Duration, Utc}; use futures::StreamExt; use internal_llm_client::vertex::{ResolvedServiceAccountDetails, ResolvedVertex, ServiceAccount}; diff --git a/engine/baml-runtime/src/internal/llm_client/traits/mod.rs b/engine/baml-runtime/src/internal/llm_client/traits/mod.rs index 759020506..b7e0e36f5 100644 --- a/engine/baml-runtime/src/internal/llm_client/traits/mod.rs +++ b/engine/baml-runtime/src/internal/llm_client/traits/mod.rs @@ -12,10 +12,11 @@ pub use self::{ completion::{WithCompletion, WithNoCompletion, WithStreamCompletion}, }; use super::{primitive::request::RequestBuilder, LLMResponse, ModelFeatures}; -use crate::{btrace::WithTraceContext, internal::llm_client::ResolveMediaUrls, RenderCurlSettings}; +use crate::{internal::llm_client::ResolveMediaUrls, RenderCurlSettings}; use crate::{internal::prompt_renderer::PromptRenderer, RuntimeContext}; use baml_types::{BamlMedia, BamlMediaContent, BamlMediaType, BamlValue, MediaBase64, MediaUrl}; use base64::{prelude::BASE64_STANDARD, Engine}; +use btrace::WithTraceContext; use futures::stream::StreamExt; use infer; use internal_baml_core::ir::repr::IntermediateRepr; diff --git a/engine/baml-runtime/src/lib.rs b/engine/baml-runtime/src/lib.rs index 677267030..ec6295d7b 100644 --- a/engine/baml-runtime/src/lib.rs +++ b/engine/baml-runtime/src/lib.rs @@ -5,7 +5,6 @@ pub mod internal; #[cfg(not(feature = "internal"))] pub(crate) mod internal; -pub mod btrace; #[cfg(not(target_arch = "wasm32"))] pub mod cli; pub mod client_registry; diff --git a/engine/baml-runtime/src/runtime/runtime_interface.rs b/engine/baml-runtime/src/runtime/runtime_interface.rs index 59f3188ac..68e3292ce 100644 --- a/engine/baml-runtime/src/runtime/runtime_interface.rs +++ b/engine/baml-runtime/src/runtime/runtime_interface.rs @@ -1,7 +1,6 @@ use std::{collections::HashMap, path::PathBuf, sync::Arc}; use super::InternalBamlRuntime; -use crate::btrace; use crate::internal::llm_client::traits::WithClientProperties; use crate::internal::llm_client::LLMResponse; use crate::{ diff --git a/engine/baml-runtime/src/runtime_interface.rs b/engine/baml-runtime/src/runtime_interface.rs index 2d72648fc..8202c2d00 100644 --- a/engine/baml-runtime/src/runtime_interface.rs +++ b/engine/baml-runtime/src/runtime_interface.rs @@ -10,13 +10,13 @@ use crate::internal::llm_client::llm_provider::LLMProvider; use crate::internal::llm_client::orchestrator::{OrchestrationScope, OrchestratorNode}; use crate::tracing::{BamlTracer, TracingSpan}; use crate::types::on_log_event::LogEventCallbackSync; -use crate::{btrace, RenderCurlSettings, RuntimeContextManager}; use crate::{ internal::{ir_features::IrFeatures, llm_client::retry_policy::CallablePolicy}, runtime::InternalBamlRuntime, types::FunctionResultStream, FunctionResult, RuntimeContext, }; +use crate::{RenderCurlSettings, RuntimeContextManager}; pub(crate) trait RuntimeConstructor { #[cfg(not(target_arch = "wasm32"))] diff --git a/engine/baml-runtime/src/types/stream.rs b/engine/baml-runtime/src/types/stream.rs index fbc34866f..79ae64133 100644 --- a/engine/baml-runtime/src/types/stream.rs +++ b/engine/baml-runtime/src/types/stream.rs @@ -6,7 +6,6 @@ use serde_json::json; use std::sync::Arc; use crate::{ - btrace::{self, WithTraceContext}, client_registry::ClientRegistry, internal::{ llm_client::orchestrator::{orchestrate_stream, OrchestratorNodeIterator}, @@ -16,6 +15,7 @@ use crate::{ type_builder::TypeBuilder, FunctionResult, RuntimeContextManager, }; +use btrace::WithTraceContext; /// Wrapper that holds a stream of responses from a BAML function call. /// diff --git a/engine/btrace/Cargo.toml b/engine/btrace/Cargo.toml index ba936e60b..c1bce32db 100644 --- a/engine/btrace/Cargo.toml +++ b/engine/btrace/Cargo.toml @@ -13,7 +13,7 @@ crate-type = ["cdylib", "rlib"] RSTEST_TIMEOUT = "10" [lints.rust] -dead_code = "deny" +dead_code = "warn" unused_imports = "deny" unused_variables = "deny" diff --git a/engine/btrace/tracer_thread.rs b/engine/btrace/src/tracer_thread.rs similarity index 98% rename from engine/btrace/tracer_thread.rs rename to engine/btrace/src/tracer_thread.rs index 46492868f..81b6bef4f 100644 --- a/engine/btrace/tracer_thread.rs +++ b/engine/btrace/src/tracer_thread.rs @@ -1,5 +1,3 @@ -use crate::btrace::InstrumentationScope; - use super::TraceEvent; pub struct TracerThread { rx: tokio::sync::mpsc::UnboundedReceiver, From 61c363932b981dd031c4e47c52732dc0109c63cd Mon Sep 17 00:00:00 2001 From: Sam Lijin Date: Thu, 9 Jan 2025 10:58:23 -0800 Subject: [PATCH 5/8] move tracing types around --- engine/Cargo.lock | 6 + engine/Cargo.toml | 2 + engine/baml-lib/baml-types/Cargo.toml | 10 +- engine/baml-lib/baml-types/src/lib.rs | 1 + engine/baml-lib/baml-types/src/tracing.rs | 121 ++++++++++++++ engine/btrace/Cargo.toml | 7 +- engine/btrace/src/lib.rs | 107 ++++-------- engine/btrace/src/tracer_thread.rs | 188 ++++++++++++++++------ 8 files changed, 311 insertions(+), 131 deletions(-) create mode 100644 engine/baml-lib/baml-types/src/tracing.rs diff --git a/engine/Cargo.lock b/engine/Cargo.lock index a2c0ea492..4c359580f 100644 --- a/engine/Cargo.lock +++ b/engine/Cargo.lock @@ -1062,6 +1062,8 @@ dependencies = [ "serde", "serde_json", "strum", + "tracing-core", + "web-time", ] [[package]] @@ -1233,14 +1235,18 @@ name = "btrace" version = "0.70.1" dependencies = [ "anyhow", + "baml-types", "base64 0.22.1", "bytes", "futures-timer", "hostname", "js-sys", + "log", "reqwest", + "serde", "serde_json", "tokio", + "tokio-stream", "tracing-core", "uuid", "wasm-bindgen", diff --git a/engine/Cargo.toml b/engine/Cargo.toml index 78c2e51f1..2edf08abf 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -78,6 +78,7 @@ minijinja = { version = "1.0.16", default-features = false, features = [ # loader # ] } +once_cell = "1" pretty_assertions = "1.4.0" rand = "0.8.5" regex = "1.10.4" @@ -94,6 +95,7 @@ static_assertions = "1.1.0" strum = { version = "0.26.2", features = ["derive"] } strum_macros = "0.26.2" time = { version = "0.3.36", features = ["formatting"] } +tracing-core = { version = "0.1.31" } pin-project-lite = "0.2.14" url = "2.5.2" uuid = { version = "1.8.0", features = ["v4", "v7", "serde"] } diff --git a/engine/baml-lib/baml-types/Cargo.toml b/engine/baml-lib/baml-types/Cargo.toml index 64c564a22..f9759f234 100644 --- a/engine/baml-lib/baml-types/Cargo.toml +++ b/engine/baml-lib/baml-types/Cargo.toml @@ -11,16 +11,20 @@ dead_code = "deny" unused_imports = "deny" unused_variables = "deny" +# Attention: +# Please try to keep this crate low-dependency! We want to keep this as minimal as possible. [dependencies] anyhow.workspace = true clap.workspace = true derive_builder.workspace = true +log.workspace = true +minijinja.workspace = true +once_cell.workspace = true serde.workspace = true serde_json.workspace = true strum.workspace = true -minijinja.workspace = true -log.workspace = true -once_cell = "1" +tracing-core.workspace = true +web-time.workspace = true [dependencies.indexmap] workspace = true diff --git a/engine/baml-lib/baml-types/src/lib.rs b/engine/baml-lib/baml-types/src/lib.rs index 2b56a7935..feb5af8ed 100644 --- a/engine/baml-lib/baml-types/src/lib.rs +++ b/engine/baml-lib/baml-types/src/lib.rs @@ -2,6 +2,7 @@ mod constraint; mod map; mod media; mod minijinja; +pub mod tracing; mod baml_value; mod field_type; diff --git a/engine/baml-lib/baml-types/src/tracing.rs b/engine/baml-lib/baml-types/src/tracing.rs new file mode 100644 index 000000000..6bc569c5f --- /dev/null +++ b/engine/baml-lib/baml-types/src/tracing.rs @@ -0,0 +1,121 @@ +use serde::{Deserialize, Serialize}; + +pub type TraceTags = serde_json::Map; + +#[derive(Clone, Debug, Serialize, Deserialize)] +// TODO: use a prefixed UUID type for this +pub struct SpanId(pub Vec); + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct TraceEventBatch { + pub events: Vec, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum TraceEvent { + SpanStart(TraceSpanStart), + SpanEnd(TraceSpanEnd), + Log(TraceLog), +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct TraceMetadata { + /// human-readable callsite identifier, e.g. "ExtractResume" or "openai/gpt-4o/chat" + pub callsite: String, + /// verbosity level + #[serde(with = "level_serde")] + pub verbosity: tracing_core::Level, +} + +// ------------------------------------------------------------------------------------------------- + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct TraceSpanStart { + pub span_id: SpanId, + pub meta: TraceMetadata, + #[serde(with = "timestamp_serde")] + pub start_time: web_time::Instant, + pub fields: serde_json::Map, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct TraceSpanEnd { + pub span_id: SpanId, + pub meta: TraceMetadata, + #[serde(with = "timestamp_serde")] + pub start_time: web_time::Instant, + pub duration: web_time::Duration, + pub fields: serde_json::Map, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct TraceLog { + pub span_id: SpanId, + pub meta: TraceMetadata, + #[serde(with = "timestamp_serde")] + pub start_time: web_time::Instant, + pub msg: String, + pub tags: serde_json::Map, +} + +// Add this helper module for serialization +mod timestamp_serde { + use serde::{Deserializer, Serializer}; + use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; + + // Serialize Instant as Unix timestamp (milliseconds since epoch) + pub fn serialize(instant: &Instant, serializer: S) -> Result + where + S: Serializer, + { + let now = Instant::now(); + let system_now = SystemTime::now(); + let duration_since_epoch = system_now + .duration_since(UNIX_EPOCH) + .unwrap_or(Duration::ZERO); + + let instant_duration = instant.duration_since(now); + let timestamp = duration_since_epoch - instant_duration; + + serializer.serialize_u128(timestamp.as_millis()) + } + + // Deserialize Unix timestamp back to Instant + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let timestamp_ms: u128 = serde::Deserialize::deserialize(deserializer)?; + let now = Instant::now(); + let system_now = SystemTime::now(); + let duration_since_epoch = system_now + .duration_since(UNIX_EPOCH) + .unwrap_or(Duration::ZERO); + + let timestamp_duration = Duration::from_millis(timestamp_ms as u64); + let instant = now - (duration_since_epoch - timestamp_duration); + + Ok(instant) + } +} + +// Add this helper module for tracing::Level serialization +mod level_serde { + use serde::{Deserializer, Serializer}; + use tracing_core::Level; + + pub fn serialize(level: &Level, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&level.to_string()) + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let level_str: String = serde::Deserialize::deserialize(deserializer)?; + level_str.parse().map_err(serde::de::Error::custom) + } +} diff --git a/engine/btrace/Cargo.toml b/engine/btrace/Cargo.toml index c1bce32db..27538fec4 100644 --- a/engine/btrace/Cargo.toml +++ b/engine/btrace/Cargo.toml @@ -19,10 +19,15 @@ unused_variables = "deny" [dependencies] anyhow.workspace = true +baml-types.workspace = true base64.workspace = true bytes.workspace = true -tracing-core = { version = "0.1.31" } +log.workspace = true +reqwest.workspace = true +serde.workspace = true serde_json.workspace = true +tokio-stream = "0.1" +tracing-core.workspace = true uuid = { version = "1.8", features = ["v7", "js"] } web-time.workspace = true diff --git a/engine/btrace/src/lib.rs b/engine/btrace/src/lib.rs index 38c8011fb..94d530ba1 100644 --- a/engine/btrace/src/lib.rs +++ b/engine/btrace/src/lib.rs @@ -1,3 +1,6 @@ +use baml_types::tracing::{ + SpanId, TraceEvent, TraceLog, TraceMetadata, TraceSpanEnd, TraceSpanStart, TraceTags, +}; #[cfg(not(target_arch = "wasm32"))] mod tracer_thread; @@ -5,7 +8,11 @@ mod tracer_thread; pub use tracer_thread::TracerThread; pub use tracing_core::Level; -type TraceTags = serde_json::Map; +#[derive(Clone, Debug)] +pub enum InstrumentationScope { + Root, + Child { parent_span_id: SpanId }, +} #[derive(Clone)] pub struct TraceContext { @@ -18,7 +25,7 @@ pub struct TraceContext { impl TraceContext { fn child_ctx(&self) -> (Self, SpanId) { - let new_uuid = uuid::Uuid::now_v7(); + let new_uuid = uuid::Uuid::now_v7().to_string(); let span_id = match &self.scope { InstrumentationScope::Root => SpanId(vec![new_uuid]), InstrumentationScope::Child { parent_span_id } => { @@ -52,84 +59,28 @@ thread_local! { tags: serde_json::Map::new(), }; } - -#[derive(Clone, Debug)] -pub struct SpanId(Vec); - -impl Default for SpanId { - fn default() -> Self { - Self(vec![uuid::Uuid::now_v7()]) - } -} - -#[derive(Clone, Debug)] -pub enum InstrumentationScope { - Root, - Child { parent_span_id: SpanId }, -} - -pub enum TraceEvent { - SpanStart(TraceSpanStart), - SpanEnd(TraceSpanEnd), - Log(TraceLog), -} - -#[derive(Clone, Debug)] -pub struct TraceMetadata { - /// human-readable callsite identifier, e.g. "ExtractResume" or "openai/gpt-4o/chat" - callsite: String, - /// verbosity level - verbosity: tracing_core::Level, -} - -// ------------------------------------------------------------------------------------------------- - -pub struct TraceSpanStart { - span_id: SpanId, - meta: TraceMetadata, - start_time: web_time::Instant, - fields: serde_json::Map, -} - -#[derive(Debug)] -pub struct TraceSpanEnd { - span_id: SpanId, - meta: TraceMetadata, - start_time: web_time::Instant, - duration: web_time::Duration, - fields: serde_json::Map, -} - -pub struct TraceLog { - span_id: SpanId, - meta: TraceMetadata, - start_time: web_time::Instant, - msg: String, - tags: serde_json::Map, -} - // ------------------------------------------------------------------------------------------------- -impl TraceSpanStart { - pub fn new( - verbosity: tracing_core::Level, - callsite: String, - fields: serde_json::Value, - ) -> Self { - Self { - span_id: Default::default(), - start_time: web_time::Instant::now(), - meta: TraceMetadata { - callsite, - verbosity, - }, - fields: match fields { - serde_json::Value::Object(o) => o, - _ => serde_json::Map::new(), - }, - } - } -} +// impl TraceSpanStart { +// pub fn new( +// verbosity: tracing_core::Level, +// callsite: String, +// fields: serde_json::Value, +// ) -> Self { +// Self { +// span_id: SpanId(vec![format!("span_{}", uuid::Uuid::now_v7())]), +// start_time: web_time::Instant::now(), +// meta: TraceMetadata { +// callsite, +// verbosity, +// }, +// fields: match fields { +// serde_json::Value::Object(o) => o, +// _ => serde_json::Map::new(), +// }, +// } +// } +// } pub fn log( verbosity: tracing_core::Level, diff --git a/engine/btrace/src/tracer_thread.rs b/engine/btrace/src/tracer_thread.rs index 81b6bef4f..8b6e2c070 100644 --- a/engine/btrace/src/tracer_thread.rs +++ b/engine/btrace/src/tracer_thread.rs @@ -1,67 +1,157 @@ +use anyhow::{Context, Result}; +use std::pin::pin; + +use std::time::Instant; + use super::TraceEvent; +use serde::{Deserialize, Serialize}; +use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt}; pub struct TracerThread { - rx: tokio::sync::mpsc::UnboundedReceiver, + rx: UnboundedReceiverStream, } impl TracerThread { pub fn new(rx: tokio::sync::mpsc::UnboundedReceiver) -> Self { - Self { rx } + Self { + rx: UnboundedReceiverStream::new(rx), + } } pub fn run(rx: tokio::sync::mpsc::UnboundedReceiver) { std::thread::spawn(move || { let rt = tokio::runtime::Runtime::new().unwrap(); - rt.block_on(Self { rx }.run_impl()); + rt.block_on( + Self { + rx: UnboundedReceiverStream::new(rx), + } + .run_impl(), + ); }); } - pub async fn run_impl(&mut self) { - while let Some(event) = self.rx.recv().await { - match event { - TraceEvent::SpanStart(span) => { - println!( - "--> [{} / {}]: {} {}", - span.span_id - .0 - .iter() - .map(|id| id.to_string()) - .collect::>() - .join(" > "), - span.meta.verbosity, - span.meta.callsite, - serde_json::to_string(&span.fields).unwrap_or("???".to_string()), - ); - } - TraceEvent::SpanEnd(span) => { - println!( - "<-- [{} / {}]: {} {}", - span.span_id - .0 - .iter() - .map(|id| id.to_string()) - .collect::>() - .join(" > "), - span.meta.verbosity, - span.meta.callsite, - serde_json::to_string(&span.fields).unwrap_or("???".to_string()), - ); - } - TraceEvent::Log(log) => { - println!( - "log [{} / {}]: {} {} {}", - log.span_id - .0 - .iter() - .map(|id| id.to_string()) - .collect::>() - .join(" > "), - log.meta.verbosity, - log.meta.callsite, - log.msg, - serde_json::to_string(&log.tags).unwrap_or("???".to_string()), - ); - } + pub async fn run_impl(self) { + let mut stream = pin!(self + .rx + .chunks_timeout(1024, std::time::Duration::from_secs(5))); + + while let Some(events) = stream.next().await { + let batch = TraceEventBatch { events }; + let body = serde_json::to_string(&batch).unwrap(); + + loop { + // TODO: this impl is wrong, every time a batch of trace events is ready, + // we should enqueue it for send, instead of blocking on send before processing the next batch + let Ok(presigned_url) = get_presigned_url( + "https://7ddxr6jp5gmzdu44srm2jszaq40mbazu.lambda-url.us-east-1.on.aws/", + "sam-boundary-studio-traceproce-tracebucket53be43bb-ow7chhxvu0ek", + "raw-events", + ) + .await + else { + continue; + }; + + let Ok(_) = upload_file(&presigned_url, &body).await else { + continue; + }; + break; } } + + log::debug!("Trace upload complete"); + } +} + +#[derive(Debug, Serialize, Deserialize)] +struct GetSignedUrlRequest { + bucket: String, + key: String, +} + +// TODO: responses can be errors +#[derive(Debug, Serialize, Deserialize)] +struct GetSignedUrlResponse { + upload_url: String, + // generation_time_ms: u64, +} + +async fn get_presigned_url( + lambda_url: &str, + bucket: &str, + key_prefix: &str, +) -> anyhow::Result { + let client = reqwest::Client::new(); + let start = Instant::now(); + + let request_body = GetSignedUrlRequest { + bucket: bucket.to_string(), + key: format!("{}/{}.json", key_prefix, uuid::Uuid::new_v4()), + }; + + eprintln!("Making POST request to URL: {}", lambda_url); + eprintln!( + "Request body: {}", + serde_json::to_string_pretty(&request_body)? + ); + + let response = client + .post(lambda_url) + .json(&request_body) + .send() + .await + .context("Failed to call lambda function")?; + + let duration_ms = start.elapsed().as_millis(); + eprintln!("Got response in {}ms", duration_ms); + eprintln!("Response status: {}", response.status()); + eprintln!("Response headers: {:#?}", response.headers()); + + // Get response body as text first + let response_text = response + .text() + .await + .context("Failed to get response body as text")?; + + // Try to parse the JSON, if it fails show the raw response + let response_data: GetSignedUrlResponse = + serde_json::from_str(&response_text).with_context(|| { + format!( + "Failed to parse response as GetSignedUrlResponse. Raw response: {}", + response_text + ) + })?; + + eprintln!("\nParsed response: {:?}", response_data); + + if response_data.upload_url.is_empty() { + return Err(anyhow::anyhow!("Lambda returned empty upload URL")); } + + Ok(response_data.upload_url) +} + +async fn upload_file(presigned_url: &str, body: &str) -> Result<()> { + let start = Instant::now(); + + let client = reqwest::Client::new(); + let response = client + .put(presigned_url) + .body(body.to_string()) + .send() + .await + .context("Failed to upload file")?; + + let duration_ms = start.elapsed().as_millis(); + log::info!( + "Upload completed with status {} in {}ms", + response.status(), + duration_ms + ); + + Ok(()) +} + +#[derive(Debug, Serialize, Deserialize)] +struct TraceEventBatch { + events: Vec, } From 7da6b9dbcd0c0c4ebc3337a7e1b1a0fce6fa88b6 Mon Sep 17 00:00:00 2001 From: Sam Lijin Date: Thu, 9 Jan 2025 11:43:05 -0800 Subject: [PATCH 6/8] one more --- engine/baml-runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/baml-runtime/src/lib.rs b/engine/baml-runtime/src/lib.rs index ec6295d7b..8ab68d280 100644 --- a/engine/baml-runtime/src/lib.rs +++ b/engine/baml-runtime/src/lib.rs @@ -76,7 +76,7 @@ pub struct BamlRuntime { env_vars: HashMap, #[cfg(not(target_arch = "wasm32"))] pub async_runtime: Arc, - pub trace_agent_tx: tokio::sync::mpsc::UnboundedSender, + pub trace_agent_tx: tokio::sync::mpsc::UnboundedSender, } impl BamlRuntime { From 39c686f8377a76062196ef5125ca3c28ef6cc8a5 Mon Sep 17 00:00:00 2001 From: Vaibhav Gupta Date: Thu, 9 Jan 2025 13:45:31 -0800 Subject: [PATCH 7/8] Merge latest --- engine/Cargo.lock | 2 +- .../baml/tests/validation_files/prompt_fiddle_example.baml | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/engine/Cargo.lock b/engine/Cargo.lock index d1e87bb91..e02b660ec 100644 --- a/engine/Cargo.lock +++ b/engine/Cargo.lock @@ -1253,7 +1253,7 @@ dependencies = [ [[package]] name = "btrace" -version = "0.70.1" +version = "0.72.0" dependencies = [ "anyhow", "baml-types", diff --git a/engine/baml-lib/baml/tests/validation_files/prompt_fiddle_example.baml b/engine/baml-lib/baml/tests/validation_files/prompt_fiddle_example.baml index d4330bda2..8ab1b79a5 100644 --- a/engine/baml-lib/baml/tests/validation_files/prompt_fiddle_example.baml +++ b/engine/baml-lib/baml/tests/validation_files/prompt_fiddle_example.baml @@ -175,12 +175,17 @@ client GPT4 { client GPT4o { provider openai + retry_policy CustomRetry options { model gpt-4o api_key env.OPENAI_API_KEY } } +retry_policy CustomRetry { + max_retries 3 +} + client Claude { provider anthropic options { From 7ba561b22d699c3c3ba51704f4adcbc02e3f3f1b Mon Sep 17 00:00:00 2001 From: Vaibhav Gupta Date: Mon, 20 Jan 2025 10:35:43 -0800 Subject: [PATCH 8/8] tracing WIP --- engine/baml-lib/baml-types/Cargo.toml | 1 + engine/baml-lib/baml-types/src/tracing.rs | 121 ---------------- .../baml-lib/baml-types/src/tracing/events.rs | 131 ++++++++++++++++++ engine/baml-lib/baml-types/src/tracing/mod.rs | 124 +++++++++++++++++ .../baml-types/src/tracing/storage.rs | 33 +++++ 5 files changed, 289 insertions(+), 121 deletions(-) delete mode 100644 engine/baml-lib/baml-types/src/tracing.rs create mode 100644 engine/baml-lib/baml-types/src/tracing/events.rs create mode 100644 engine/baml-lib/baml-types/src/tracing/mod.rs create mode 100644 engine/baml-lib/baml-types/src/tracing/storage.rs diff --git a/engine/baml-lib/baml-types/Cargo.toml b/engine/baml-lib/baml-types/Cargo.toml index f9759f234..ce98fcb32 100644 --- a/engine/baml-lib/baml-types/Cargo.toml +++ b/engine/baml-lib/baml-types/Cargo.toml @@ -25,6 +25,7 @@ serde_json.workspace = true strum.workspace = true tracing-core.workspace = true web-time.workspace = true +tokio.workspace = true [dependencies.indexmap] workspace = true diff --git a/engine/baml-lib/baml-types/src/tracing.rs b/engine/baml-lib/baml-types/src/tracing.rs deleted file mode 100644 index 6bc569c5f..000000000 --- a/engine/baml-lib/baml-types/src/tracing.rs +++ /dev/null @@ -1,121 +0,0 @@ -use serde::{Deserialize, Serialize}; - -pub type TraceTags = serde_json::Map; - -#[derive(Clone, Debug, Serialize, Deserialize)] -// TODO: use a prefixed UUID type for this -pub struct SpanId(pub Vec); - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct TraceEventBatch { - pub events: Vec, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum TraceEvent { - SpanStart(TraceSpanStart), - SpanEnd(TraceSpanEnd), - Log(TraceLog), -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct TraceMetadata { - /// human-readable callsite identifier, e.g. "ExtractResume" or "openai/gpt-4o/chat" - pub callsite: String, - /// verbosity level - #[serde(with = "level_serde")] - pub verbosity: tracing_core::Level, -} - -// ------------------------------------------------------------------------------------------------- - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct TraceSpanStart { - pub span_id: SpanId, - pub meta: TraceMetadata, - #[serde(with = "timestamp_serde")] - pub start_time: web_time::Instant, - pub fields: serde_json::Map, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct TraceSpanEnd { - pub span_id: SpanId, - pub meta: TraceMetadata, - #[serde(with = "timestamp_serde")] - pub start_time: web_time::Instant, - pub duration: web_time::Duration, - pub fields: serde_json::Map, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct TraceLog { - pub span_id: SpanId, - pub meta: TraceMetadata, - #[serde(with = "timestamp_serde")] - pub start_time: web_time::Instant, - pub msg: String, - pub tags: serde_json::Map, -} - -// Add this helper module for serialization -mod timestamp_serde { - use serde::{Deserializer, Serializer}; - use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; - - // Serialize Instant as Unix timestamp (milliseconds since epoch) - pub fn serialize(instant: &Instant, serializer: S) -> Result - where - S: Serializer, - { - let now = Instant::now(); - let system_now = SystemTime::now(); - let duration_since_epoch = system_now - .duration_since(UNIX_EPOCH) - .unwrap_or(Duration::ZERO); - - let instant_duration = instant.duration_since(now); - let timestamp = duration_since_epoch - instant_duration; - - serializer.serialize_u128(timestamp.as_millis()) - } - - // Deserialize Unix timestamp back to Instant - pub fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let timestamp_ms: u128 = serde::Deserialize::deserialize(deserializer)?; - let now = Instant::now(); - let system_now = SystemTime::now(); - let duration_since_epoch = system_now - .duration_since(UNIX_EPOCH) - .unwrap_or(Duration::ZERO); - - let timestamp_duration = Duration::from_millis(timestamp_ms as u64); - let instant = now - (duration_since_epoch - timestamp_duration); - - Ok(instant) - } -} - -// Add this helper module for tracing::Level serialization -mod level_serde { - use serde::{Deserializer, Serializer}; - use tracing_core::Level; - - pub fn serialize(level: &Level, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&level.to_string()) - } - - pub fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let level_str: String = serde::Deserialize::deserialize(deserializer)?; - level_str.parse().map_err(serde::de::Error::custom) - } -} diff --git a/engine/baml-lib/baml-types/src/tracing/events.rs b/engine/baml-lib/baml-types/src/tracing/events.rs new file mode 100644 index 000000000..f3ce0f1e5 --- /dev/null +++ b/engine/baml-lib/baml-types/src/tracing/events.rs @@ -0,0 +1,131 @@ +use anyhow::Result; +use crate::BamlValue; + +// TODO: use a prefixed UUID type for this +type SpanId = String; + +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct FunctionId(pub SpanId); + +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct ContentId(pub SpanId); + +// THESE ARE NOT CLONEABLE!! +pub struct LogEvent { + /* + * (span_id, content_span_id) is a unique identifier for a log event + * The query (span_id, *) gets all logs for a function call + */ + + pub span_id: FunctionId, + pub content_span_id: ContentId, + + // The chain of spans that lead to this log event + // Includes span_id at the last position (content_span_id is not included) + pub span_chain: Vec, + + // The timestamp of the log + pub timestamp: web_time::Instant, + // The content of the log + pub content: LogEventContent, +} + + +pub enum LogEventContent { + // All start events + FunctionStart(FunctionStart), + // All end events + FunctionEnd(FunctionEnd), + + // The rest are intermediate events that happen between start and end + + // LLM request + LLMRequest(LLMRequest), + // Raw HTTP request to the LLM + RawLLMRequest(HTTPRequest), + + // Do to streaming, its possible to have multiple responses for a single request + // ---- + // Raw HTTP response from the LLM + RawLLMResponse(HTTPResponse), + // Parsed LLM response + LLMResponse(Result), + // ---- + + // We don't want to store the parsed LLM response in the log event + // as we have it in FunctionEnd + Parsed(Result<()>), +} + +pub struct BamlOptions { + pub type_builder: Option, + pub client_registry: Option, +} + +pub struct FunctionStart { + pub name: String, + pub args: Vec, + pub options: BamlOptions, +} + +pub struct FunctionEnd { + pub result: Result, + // Everything below is duplicated from the start event + // to deal with the case where the log is dropped. + // P2: as we can for now assume logs are not dropped, + + // pub name: String, + // pub start_timestamp: web_time::Instant, + // pub start_args: Vec, +} + +// LLM specific events + +// TODO: fix this. +pub type Prompt = serde_json::Value; + +pub enum LLMClient { + Ref(String), + ShortHand(String, String), +} + +pub struct LLMRequest { + pub client: LLMClient, + pub params: serde_json::Value, + pub prompt: Prompt, +} + +pub struct HTTPRequest { + // since LLM requests could be made in parallel, we need to match the response to the request + pub request_id: ContentId, + pub url: String, + pub method: String, + pub headers: serde_json::Value, + pub body: serde_json::Value, +} + +pub struct HTTPResponse { + // since LLM requests could be made in parallel, we need to match the response to the request + pub request_id: ContentId, + pub status: u16, + pub headers: serde_json::Value, + pub body: serde_json::Value, +} + + +pub struct LLMResponse { + // since LLM requests could be made in parallel, we need to match the response to the request + pub request_id: ContentId, + // Fully qualified model name + pub finish_reason: String, + pub model: String, + pub usage: LLMUsage, + pub string_output: String, +} + +pub struct LLMUsage { + pub input_tokens: Option, + pub output_tokens: Option, + pub total_tokens: Option, +} + diff --git a/engine/baml-lib/baml-types/src/tracing/mod.rs b/engine/baml-lib/baml-types/src/tracing/mod.rs new file mode 100644 index 000000000..81a829391 --- /dev/null +++ b/engine/baml-lib/baml-types/src/tracing/mod.rs @@ -0,0 +1,124 @@ +mod events; +mod storage; + +// use serde::{Deserialize, Serialize}; + +// pub type TraceTags = serde_json::Map; + +// #[derive(Clone, Debug, Serialize, Deserialize)] +// // TODO: use a prefixed UUID type for this +// pub struct SpanId(pub Vec); + +// #[derive(Clone, Debug, Serialize, Deserialize)] +// pub struct TraceEventBatch { +// pub events: Vec, +// } + +// #[derive(Clone, Debug, Serialize, Deserialize)] +// pub enum TraceEvent { +// SpanStart(TraceSpanStart), +// SpanEnd(TraceSpanEnd), +// Log(TraceLog), +// } + +// #[derive(Clone, Debug, Serialize, Deserialize)] +// pub struct TraceMetadata { +// /// human-readable callsite identifier, e.g. "ExtractResume" or "openai/gpt-4o/chat" +// pub callsite: String, +// /// verbosity level +// #[serde(with = "level_serde")] +// pub verbosity: tracing_core::Level, +// } + +// // ------------------------------------------------------------------------------------------------- + +// #[derive(Clone, Debug, Serialize, Deserialize)] +// pub struct TraceSpanStart { +// pub span_id: SpanId, +// pub meta: TraceMetadata, +// #[serde(with = "timestamp_serde")] +// pub start_time: web_time::Instant, +// pub fields: serde_json::Map, +// } + +// #[derive(Clone, Debug, Serialize, Deserialize)] +// pub struct TraceSpanEnd { +// pub span_id: SpanId, +// pub meta: TraceMetadata, +// #[serde(with = "timestamp_serde")] +// pub start_time: web_time::Instant, +// pub duration: web_time::Duration, +// pub fields: serde_json::Map, +// } + +// #[derive(Clone, Debug, Serialize, Deserialize)] +// pub struct TraceLog { +// pub span_id: SpanId, +// pub meta: TraceMetadata, +// #[serde(with = "timestamp_serde")] +// pub start_time: web_time::Instant, +// pub msg: String, +// pub tags: serde_json::Map, +// } + +// // Add this helper module for serialization +// mod timestamp_serde { +// use serde::{Deserializer, Serializer}; +// use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; + +// // Serialize Instant as Unix timestamp (milliseconds since epoch) +// pub fn serialize(instant: &Instant, serializer: S) -> Result +// where +// S: Serializer, +// { +// let now = Instant::now(); +// let system_now = SystemTime::now(); +// let duration_since_epoch = system_now +// .duration_since(UNIX_EPOCH) +// .unwrap_or(Duration::ZERO); + +// let instant_duration = instant.duration_since(now); +// let timestamp = duration_since_epoch - instant_duration; + +// serializer.serialize_u128(timestamp.as_millis()) +// } + +// // Deserialize Unix timestamp back to Instant +// pub fn deserialize<'de, D>(deserializer: D) -> Result +// where +// D: Deserializer<'de>, +// { +// let timestamp_ms: u128 = serde::Deserialize::deserialize(deserializer)?; +// let now = Instant::now(); +// let system_now = SystemTime::now(); +// let duration_since_epoch = system_now +// .duration_since(UNIX_EPOCH) +// .unwrap_or(Duration::ZERO); + +// let timestamp_duration = Duration::from_millis(timestamp_ms as u64); +// let instant = now - (duration_since_epoch - timestamp_duration); + +// Ok(instant) +// } +// } + +// // Add this helper module for tracing::Level serialization +// mod level_serde { +// use serde::{Deserializer, Serializer}; +// use tracing_core::Level; + +// pub fn serialize(level: &Level, serializer: S) -> Result +// where +// S: Serializer, +// { +// serializer.serialize_str(&level.to_string()) +// } + +// pub fn deserialize<'de, D>(deserializer: D) -> Result +// where +// D: Deserializer<'de>, +// { +// let level_str: String = serde::Deserialize::deserialize(deserializer)?; +// level_str.parse().map_err(serde::de::Error::custom) +// } +// } diff --git a/engine/baml-lib/baml-types/src/tracing/storage.rs b/engine/baml-lib/baml-types/src/tracing/storage.rs new file mode 100644 index 000000000..e69801b3b --- /dev/null +++ b/engine/baml-lib/baml-types/src/tracing/storage.rs @@ -0,0 +1,33 @@ +use std::{collections::HashMap, sync::{Arc, Mutex}}; + +use super::events::{FunctionId, LogEvent}; + +use tokio::sync::Mutex; + +#[derive(Default)] +pub struct TraceStorage { + // This is a lookup of event -> data (not event -> event) + // For that you need to do multiple lookups + span_map: HashMap>>>>, + + // usage_count + usage_count: HashMap, +} + +impl TraceStorage { + pub fn get(&self, span_id: FunctionId) -> Option>>>> { + self.span_map.get(&span_id).cloned() + } + + pub fn put(&mut self, event: Arc) { + let span_id = event.span_id.clone(); + self.span_map.entry(span_id).or_insert(Arc::new(Mutex::new(vec![]))); + + match self.span_map.get_mut(&event.span_id).unwrap().lock() { + Ok(mut events) => events.push(event), + Err(e) => { + eprintln!("Failed to lock mutex: {}", e); + } + } + } +}