From daf16466fc3c56408c79ad63818a784aad7bdc90 Mon Sep 17 00:00:00 2001 From: Greg Hale Date: Mon, 28 Oct 2024 14:46:46 -0700 Subject: [PATCH] Allow block-level constraints --- engine/Cargo.lock | 2 + .../baml-core/src/ir/ir_helpers/mod.rs | 105 +- .../src/ir/ir_helpers/to_baml_arg.rs | 4 +- engine/baml-lib/baml-core/src/ir/repr.rs | 22 +- engine/baml-lib/baml-types/src/baml_value.rs | 93 +- .../baml-lib/baml-types/src/field_type/mod.rs | 77 - engine/baml-lib/baml/Cargo.toml | 1 + .../constraints/block_level.baml | 17 + .../jinja-runtime/src/output_format/types.rs | 6 +- .../src/deserializer/coercer/field_type.rs | 2 +- .../coercer/ir_ref/coerce_class.rs | 61 +- .../coercer/ir_ref/coerce_enum.rs | 19 +- .../jsonish/src/deserializer/coercer/mod.rs | 6 +- engine/baml-lib/jsonish/src/tests/mod.rs | 6 +- .../jsonish/src/tests/test_constraints.rs | 36 +- engine/baml-lib/parser-database/Cargo.toml | 1 + .../src/attributes/constraint.rs | 70 +- .../parser-database/src/attributes/mod.rs | 11 +- .../src/attributes/to_string_attribute.rs | 12 +- .../parser-database/src/context/mod.rs | 22 +- .../parser-database/src/walkers/class.rs | 6 + engine/baml-lib/schema-ast/src/ast/field.rs | 2 +- .../prompt_renderer/render_output_format.rs | 6 +- .../src/types/function_results.rs | 27 +- engine/language_client_ruby/Gemfile.lock | 2 +- .../test-files/constraints/constraints.baml | 52 + .../python/baml_client/async_client.py | 210 ++ integ-tests/python/baml_client/inlinedbaml.py | 2 +- .../python/baml_client/partial_types.py | 22 + integ-tests/python/baml_client/sync_client.py | 210 ++ .../python/baml_client/type_builder.py | 2 +- integ-tests/python/baml_client/types.py | 22 + integ-tests/python/tests/test_functions.py | 27 +- integ-tests/ruby/baml_client/client.rb | 268 +++ integ-tests/ruby/baml_client/inlined.rb | 2 +- integ-tests/ruby/baml_client/partial-types.rb | 56 + integ-tests/ruby/baml_client/type-registry.rb | 2 +- integ-tests/ruby/baml_client/types.rb | 56 + integ-tests/ruby/test_functions.rb | 25 +- .../typescript/baml_client/async_client.ts | 234 ++- .../typescript/baml_client/inlinedbaml.ts | 2 +- .../typescript/baml_client/sync_client.ts | 102 +- .../typescript/baml_client/type_builder.ts | 2 +- integ-tests/typescript/baml_client/types.ts | 22 + integ-tests/typescript/test-report.html | 1681 +---------------- .../typescript/tests/integ-tests.test.ts | 11 + 46 files changed, 1784 insertions(+), 1842 deletions(-) create mode 100644 engine/baml-lib/baml/tests/validation_files/constraints/block_level.baml diff --git a/engine/Cargo.lock b/engine/Cargo.lock index 47e8e7644..f87775b30 100644 --- a/engine/Cargo.lock +++ b/engine/Cargo.lock @@ -867,6 +867,7 @@ dependencies = [ "expect-test", "indoc", "internal-baml-core", + "itertools 0.13.0", "pretty_assertions", "strip-ansi-escapes", ] @@ -2659,6 +2660,7 @@ dependencies = [ "internal-baml-jinja-types", "internal-baml-prompt-parser", "internal-baml-schema-ast", + "itertools 0.13.0", "log", "regex", "rustc-hash", diff --git a/engine/baml-lib/baml-core/src/ir/ir_helpers/mod.rs b/engine/baml-lib/baml-core/src/ir/ir_helpers/mod.rs index 6223a0662..c2f57eaa3 100644 --- a/engine/baml-lib/baml-core/src/ir/ir_helpers/mod.rs +++ b/engine/baml-lib/baml-core/src/ir/ir_helpers/mod.rs @@ -13,7 +13,7 @@ use crate::{ }, }; use anyhow::Result; -use baml_types::{BamlMap, BamlValue, BamlValueWithMeta, FieldType, LiteralValue, TypeValue}; +use baml_types::{BamlMap, BamlValue, BamlValueWithMeta, Constraint, ConstraintLevel, FieldType, LiteralValue, TypeValue}; pub use to_baml_arg::ArgCoercer; use super::repr; @@ -51,6 +51,18 @@ pub trait IRHelper { value: BamlValue, field_type: FieldType, ) -> Result>; + fn distribute_constraints<'a>( + &'a self, + field_type: &'a FieldType + ) -> (&'a FieldType, Vec); + fn type_has_constraints( + &self, + field_type: &FieldType + ) -> bool; + fn type_has_checks( + &self, + field_type: &FieldType + ) -> bool; } impl IRHelper for IntermediateRepr { @@ -365,6 +377,66 @@ impl IRHelper for IntermediateRepr { } } } + + + /// Constraints may live in several places. A constrained base type stors its + /// constraints by wrapping itself in the `FieldType::Constrained` constructor. + /// Additionally, `FieldType::Class` may have constraints stored in its class node, + /// and `FieldType::Enum` can store constraints in its `Enum` node. + /// And the `FieldType::Constrained` constructor might wrap another + /// `FieldType::Constrained` constructor. + /// + /// This function collects constraints for a given type from all these + /// possible sources. Whenever querying a type for its constraints, you + /// should do so with this function, instead of searching manually for all + /// the places that Constraints can live. + fn distribute_constraints<'a>(&'a self, field_type: &'a FieldType) -> (&'a FieldType, Vec) { + match field_type { + FieldType::Class(class_name) => { + match self.find_class(class_name) { + Err(_) => (field_type, Vec::new()), + Ok(class_node) => (field_type, class_node.item.attributes.constraints.clone()) + } + } + FieldType::Enum(enum_name) => { + match self.find_enum(enum_name) { + Err(_) => (field_type, Vec::new()), + Ok(enum_node) => (field_type, enum_node.item.attributes.constraints.clone()) + } + } + // Check the first level to see if it's constrained. + FieldType::Constrained { base, constraints } => { + match base.as_ref() { + // If so, we must check the second level to see if we need to combine + // constraints across levels. + // The recursion here means that arbitrarily nested `FieldType::Constrained`s + // will be collapsed before the function returns. + FieldType::Constrained { .. } => { + let (sub_base, sub_constraints) = self.distribute_constraints(base.as_ref()); + let combined_constraints = vec![constraints.clone(), sub_constraints] + .into_iter() + .flatten() + .collect(); + (sub_base, combined_constraints) + } + _ => (base, constraints.clone()), + } + } + _ => (field_type, Vec::new()), + } + } + + fn type_has_constraints(&self, field_type: &FieldType) -> bool { + let (_, constraints) = self.distribute_constraints(field_type); + !constraints.is_empty() + } + + fn type_has_checks(&self, field_type: &FieldType) -> bool { + let (_, constraints) = self.distribute_constraints(field_type); + constraints + .iter() + .any(|Constraint { level, .. }| *level == ConstraintLevel::Check) + } } const UNIT_TYPE: FieldType = FieldType::Tuple(vec![]); @@ -686,4 +758,35 @@ mod tests { let res = ir.check_function_params(&function, ¶ms, arg_coercer); assert!(res.is_err()); } + + #[test] + fn test_nested_constraint_distribution() { + let ir = make_test_ir("").unwrap(); + fn mk_constraint(s: &str) -> Constraint { + Constraint { + level: ConstraintLevel::Assert, + expression: JinjaExpression(s.to_string()), + label: Some(s.to_string()), + } + } + + let input = FieldType::Constrained { + constraints: vec![mk_constraint("a")], + base: Box::new(FieldType::Constrained { + constraints: vec![mk_constraint("b")], + base: Box::new(FieldType::Constrained { + constraints: vec![mk_constraint("c")], + base: Box::new(FieldType::Primitive(TypeValue::Int)), + }), + }), + }; + + let expected_base = FieldType::Primitive(TypeValue::Int); + let expected_constraints = vec![mk_constraint("a"), mk_constraint("b"), mk_constraint("c")]; + + let (base, constraints) = ir.distribute_constraints(&input); + + assert_eq!(base, &expected_base); + assert_eq!(constraints, expected_constraints); + } } diff --git a/engine/baml-lib/baml-core/src/ir/ir_helpers/to_baml_arg.rs b/engine/baml-lib/baml-core/src/ir/ir_helpers/to_baml_arg.rs index ddb8861a9..65128b65e 100644 --- a/engine/baml-lib/baml-core/src/ir/ir_helpers/to_baml_arg.rs +++ b/engine/baml-lib/baml-core/src/ir/ir_helpers/to_baml_arg.rs @@ -43,7 +43,7 @@ impl ArgCoercer { value: &BamlValue, // original value passed in by user scope: &mut ScopeStack, ) -> Result { - let value = match field_type.distribute_constraints() { + let value = match ir.distribute_constraints(field_type) { (FieldType::Primitive(t), _) => match t { TypeValue::String if matches!(value, BamlValue::String(_)) => Ok(value.clone()), TypeValue::String if self.allow_implicit_cast_to_string => match value { @@ -372,7 +372,7 @@ fn first_failing_assert_nested<'a>( let first_failure = value_with_types .iter() .map(|value_node| { - let (_, constraints) = value_node.meta().distribute_constraints(); + let (_, constraints) = ir.distribute_constraints(value_node.meta()); constraints .into_iter() .filter_map(|c| { diff --git a/engine/baml-lib/baml-core/src/ir/repr.rs b/engine/baml-lib/baml-core/src/ir/repr.rs index b996211ee..19c8c2a2c 100644 --- a/engine/baml-lib/baml-core/src/ir/repr.rs +++ b/engine/baml-lib/baml-core/src/ir/repr.rs @@ -198,7 +198,7 @@ pub struct NodeAttributes { #[serde(with = "indexmap::map::serde_seq")] meta: IndexMap, - constraints: Vec, + pub constraints: Vec, // Spans #[serde(skip)] @@ -371,10 +371,26 @@ impl WithRepr for ast::FieldType { ast::FieldType::Symbol(arity, idn, ..) => type_with_arity( match db.find_type(idn) { Some(Either::Left(class_walker)) => { - FieldType::Class(class_walker.name().to_string()) + let base_class = FieldType::Class(class_walker.name().to_string()); + let maybe_constraints = class_walker.get_constraints(SubType::Class); + match maybe_constraints { + Some(constraints) if constraints.len() > 0 => FieldType::Constrained { + base: Box::new(base_class), + constraints, + }, + _ => base_class + } } Some(Either::Right(enum_walker)) => { - FieldType::Enum(enum_walker.name().to_string()) + let base_type = FieldType::Enum(enum_walker.name().to_string()); + let maybe_constraints = enum_walker.get_constraints(SubType::Enum); + match maybe_constraints { + Some(constraints) if constraints.len() > 0 => FieldType::Constrained { + base: Box::new(base_type), + constraints + }, + _ => base_type + } } None => return Err(anyhow!("Field type uses unresolvable local identifier")), }, diff --git a/engine/baml-lib/baml-types/src/baml_value.rs b/engine/baml-lib/baml-types/src/baml_value.rs index 0a9ee25d9..d33eddf25 100644 --- a/engine/baml-lib/baml-types/src/baml_value.rs +++ b/engine/baml-lib/baml-types/src/baml_value.rs @@ -625,13 +625,20 @@ impl Serialize for BamlValueWithMeta> { BamlValueWithMeta::Media(v, cr) => serialize_with_checks(v, cr, serializer), BamlValueWithMeta::Enum(_enum_name, v, cr) => serialize_with_checks(v, cr, serializer), BamlValueWithMeta::Class(_class_name, v, cr) => { - let mut map = serializer.serialize_map(None)?; - for (key, value) in v { - map.serialize_entry(key, value)?; + if cr.is_empty() { + let mut map = serializer.serialize_map(None)?; + for (key, value) in v { + map.serialize_entry(key, value)?; + } + add_checks(&mut map, cr)?; + map.end() + } else { + let mut checked_value = serializer.serialize_map(Some(2))?; + checked_value.serialize_entry("value", &v)?; + add_checks(&mut checked_value, cr)?; + checked_value.end() } - add_checks(&mut map, cr)?; - map.end() - } + }, BamlValueWithMeta::Null(cr) => serialize_with_checks(&(), cr, serializer), } } @@ -707,4 +714,78 @@ mod tests { assert!(serde_json::to_value(baml_value).is_ok()); assert!(serde_json::to_value(baml_value_2).is_ok()); } + + #[test] + fn test_serialize_class_checks() { + let baml_value: BamlValueWithMeta> = + BamlValueWithMeta::Class( + "Foo".to_string(), + vec![ + ("foo".to_string(), BamlValueWithMeta::Int(1, vec![])), + ("bar".to_string(), BamlValueWithMeta::String("hi".to_string(), vec![])), + ].into_iter().collect(), + vec![ + ResponseCheck { + name: "bar_len_lt_foo".to_string(), + expression: "this.bar|length < this.foo".to_string(), + status: "failed".to_string() + } + ] + ); + let expected = serde_json::json!({ + "value": {"foo": 1, "bar": "hi"}, + "checks": { + "bar_len_lt_foo": { + "name": "bar_len_lt_foo", + "expression": "this.bar|length < this.foo", + "status": "failed" + } + } + }); + let json = serde_json::to_value(baml_value).unwrap(); + assert_eq!(json, expected); + } + + #[test] + fn test_serialize_nested_class_checks() { + + // Prepare an object for wrapping. + let foo: BamlValueWithMeta> = + BamlValueWithMeta::Class( + "Foo".to_string(), + vec![ + ("foo".to_string(), BamlValueWithMeta::Int(1, vec![])), + ("bar".to_string(), BamlValueWithMeta::String("hi".to_string(), vec![])), + ].into_iter().collect(), + vec![ + ResponseCheck { + name: "bar_len_lt_foo".to_string(), + expression: "this.bar|length < this.foo".to_string(), + status: "failed".to_string() + } + ] + ); + + // Prepare the top-level value. + let baml_value = BamlValueWithMeta::Class( + "FooWrapper".to_string(), + vec![("foo".to_string(), foo)].into_iter().collect(), + vec![] + ); + let expected = serde_json::json!({ + "foo": { + "value": {"foo": 1, "bar": "hi"}, + "checks": { + "bar_len_lt_foo": { + "name": "bar_len_lt_foo", + "expression": "this.bar|length < this.foo", + "status": "failed" + } + } + } + }); + let json = serde_json::to_value(baml_value).unwrap(); + assert_eq!(json, expected); + } + } diff --git a/engine/baml-lib/baml-types/src/field_type/mod.rs b/engine/baml-lib/baml-types/src/field_type/mod.rs index 5bc10281b..97e578ff0 100644 --- a/engine/baml-lib/baml-types/src/field_type/mod.rs +++ b/engine/baml-lib/baml-types/src/field_type/mod.rs @@ -1,6 +1,5 @@ use crate::BamlMediaType; use crate::Constraint; -use crate::ConstraintLevel; mod builder; @@ -158,51 +157,6 @@ impl FieldType { } } - /// Eliminate the `FieldType::Constrained` variant by searching for it, and stripping - /// it off of its base type, returning a tulpe of the base type and any constraints found - /// (if called on an argument that is not Constrained, the returned constraints Vec is - /// empty). - /// - /// If the function encounters directly nested Constrained types, - /// (i.e. `FieldType::Constrained { base: FieldType::Constrained { .. }, .. } `) - /// then the constraints of the two levels will be flattened into a single vector. - /// So, we always return a base type that is not FieldType::Constrained. - pub fn distribute_constraints(self: &FieldType) -> (&FieldType, Vec) { - match self { - // Check the first level to see if it's constrained. - FieldType::Constrained { base, constraints } => { - match base.as_ref() { - // If so, we must check the second level to see if we need to combine - // constraints across levels. - // The recursion here means that arbitrarily nested `FieldType::Constrained`s - // will be collapsed before the function returns. - FieldType::Constrained { .. } => { - let (sub_base, sub_constraints) = base.as_ref().distribute_constraints(); - let combined_constraints = vec![constraints.clone(), sub_constraints] - .into_iter() - .flatten() - .collect(); - (sub_base, combined_constraints) - } - _ => (base, constraints.clone()), - } - } - _ => (self, Vec::new()), - } - } - - pub fn has_constraints(&self) -> bool { - let (_, constraints) = self.distribute_constraints(); - !constraints.is_empty() - } - - pub fn has_checks(&self) -> bool { - let (_, constraints) = self.distribute_constraints(); - constraints - .iter() - .any(|Constraint { level, .. }| level == &ConstraintLevel::Check) - } - /// BAML does not support class-based subtyping. Nonetheless some builtin /// BAML types are subtypes of others, and we need to be able to test this /// when checking the types of values. @@ -297,37 +251,6 @@ impl FieldType { #[cfg(test)] mod tests { use super::*; - use crate::{Constraint, ConstraintLevel, JinjaExpression}; - - #[test] - fn test_nested_constraint_distribution() { - fn mk_constraint(s: &str) -> Constraint { - Constraint { - level: ConstraintLevel::Assert, - expression: JinjaExpression(s.to_string()), - label: Some(s.to_string()), - } - } - - let input = FieldType::Constrained { - constraints: vec![mk_constraint("a")], - base: Box::new(FieldType::Constrained { - constraints: vec![mk_constraint("b")], - base: Box::new(FieldType::Constrained { - constraints: vec![mk_constraint("c")], - base: Box::new(FieldType::Primitive(TypeValue::Int)), - }), - }), - }; - - let expected_base = FieldType::Primitive(TypeValue::Int); - let expected_constraints = vec![mk_constraint("a"), mk_constraint("b"), mk_constraint("c")]; - - let (base, constraints) = input.distribute_constraints(); - - assert_eq!(base, &expected_base); - assert_eq!(constraints, expected_constraints); - } fn mk_int() -> FieldType { FieldType::Primitive(TypeValue::Int) diff --git a/engine/baml-lib/baml/Cargo.toml b/engine/baml-lib/baml/Cargo.toml index 0728c27b8..d770f70e7 100644 --- a/engine/baml-lib/baml/Cargo.toml +++ b/engine/baml-lib/baml/Cargo.toml @@ -9,6 +9,7 @@ license-file.workspace = true [dependencies] internal-baml-core = { path = "../baml-core" } +itertools = "0.13.0" [dev-dependencies] diff --git a/engine/baml-lib/baml/tests/validation_files/constraints/block_level.baml b/engine/baml-lib/baml/tests/validation_files/constraints/block_level.baml new file mode 100644 index 000000000..79c9a74e8 --- /dev/null +++ b/engine/baml-lib/baml/tests/validation_files/constraints/block_level.baml @@ -0,0 +1,17 @@ +class Foo { + foo int + bar string + @@check(foo_and_bar, {{ this.string|length < this.foo }}) +} + +class Foo2 { + foo int + bar string + @@assert({{ this.string|length < this.foo }}) +} + +class Foo3 { + foo int @check(foo_check, {{ this }}) + bar string @assert(hi, {{ this }}) @check(hi, {{ this }}) + @@assert({{ this.string|length < this.foo }}) +} diff --git a/engine/baml-lib/jinja-runtime/src/output_format/types.rs b/engine/baml-lib/jinja-runtime/src/output_format/types.rs index e0f11ab85..cf71dd575 100644 --- a/engine/baml-lib/jinja-runtime/src/output_format/types.rs +++ b/engine/baml-lib/jinja-runtime/src/output_format/types.rs @@ -55,9 +55,9 @@ pub struct Class { #[derive(Debug, Clone)] pub struct OutputFormatContent { - enums: Arc>, - classes: Arc>, - target: FieldType, + pub enums: Arc>, + pub classes: Arc>, + pub target: FieldType, } enum RenderSetting { diff --git a/engine/baml-lib/jsonish/src/deserializer/coercer/field_type.rs b/engine/baml-lib/jsonish/src/deserializer/coercer/field_type.rs index 72dac26ec..9de18218b 100644 --- a/engine/baml-lib/jsonish/src/deserializer/coercer/field_type.rs +++ b/engine/baml-lib/jsonish/src/deserializer/coercer/field_type.rs @@ -109,7 +109,7 @@ impl TypeCoercer for FieldType { } } -fn validate_asserts(constraints: &Vec<(Constraint, bool)>) -> Result<(), ParsingError> { +pub fn validate_asserts(constraints: &Vec<(Constraint, bool)>) -> Result<(), ParsingError> { let failing_asserts = constraints .iter() .filter_map( diff --git a/engine/baml-lib/jsonish/src/deserializer/coercer/ir_ref/coerce_class.rs b/engine/baml-lib/jsonish/src/deserializer/coercer/ir_ref/coerce_class.rs index 26e44175b..68cf1a713 100644 --- a/engine/baml-lib/jsonish/src/deserializer/coercer/ir_ref/coerce_class.rs +++ b/engine/baml-lib/jsonish/src/deserializer/coercer/ir_ref/coerce_class.rs @@ -1,10 +1,11 @@ use anyhow::Result; -use baml_types::BamlMap; +use baml_types::{BamlMap, Constraint}; use internal_baml_core::ir::FieldType; use internal_baml_jinja::types::{Class, Name}; use crate::deserializer::{ - coercer::{array_helper, DefaultValue, ParsingError, TypeCoercer}, + coercer::field_type::validate_asserts, + coercer::{array_helper, run_user_checks, DefaultValue, ParsingError, TypeCoercer}, deserialize_flags::{DeserializerConditions, Flag}, types::BamlValueWithFlags, }; @@ -29,6 +30,10 @@ impl TypeCoercer for Class { ); let (optional, required): (Vec<_>, Vec<_>) = self.fields.iter().partition(|f| f.1.is_optional()); + let constraints = ctx + .of + .find_class(self.name.real_name()) + .map_or(vec![], |class| class.constraints.clone()); let mut optional_values = optional .iter() @@ -115,12 +120,14 @@ impl TypeCoercer for Class { } // Coerce the each item into the class if possible - if let Ok(option1) = array_helper::coerce_array_to_singular( + let option1_result = array_helper::coerce_array_to_singular( ctx, target, &items.iter().collect::>(), &|value| self.coerce(ctx, target, Some(value)), - ) { + ) + .and_then(|value| apply_constraints(target, vec![], value, constraints.clone())); + if let Ok(option1) = option1_result { completed_cls.push(Ok(option1)); } } @@ -291,13 +298,15 @@ impl TypeCoercer for Class { } } + let completed_instance = Ok(BamlValueWithFlags::Class( + self.name.real_name().into(), + flags, + ordered_valid_fields.clone(), + )).and_then(|value| apply_constraints(target, vec![], value, constraints.clone())); + completed_cls.insert( 0, - Ok(BamlValueWithFlags::Class( - self.name.real_name().into(), - flags, - ordered_valid_fields, - )), + completed_instance, ); } } @@ -308,6 +317,40 @@ impl TypeCoercer for Class { } } +pub fn apply_constraints( + class_type: &FieldType, + scope: Vec, + mut value: BamlValueWithFlags, + constraints: Vec, +) -> Result { + let res = if constraints.is_empty() { + Ok(value) + } else { + let constrained_class = FieldType::Constrained { + base: Box::new(class_type.clone()), + constraints, + }; + let constraint_results = run_user_checks(&value.clone().into(), &constrained_class) + .map_err(|e| ParsingError { + reason: format!("Failed to evaluate constraints: {:?}", e), + scope, + causes: Vec::new(), + })?; + validate_asserts(&constraint_results)?; + let check_results = constraint_results + .into_iter() + .filter_map(|(maybe_check, result)| { + maybe_check + .as_check() + .map(|(label, expr)| (label, expr, result)) + }) + .collect(); + value.add_flag(Flag::ConstraintResults(check_results)); + Ok(value) + }; + res +} + fn update_map<'a>( required_values: &'a mut BamlMap>>, optional_values: &'a mut BamlMap>>, diff --git a/engine/baml-lib/jsonish/src/deserializer/coercer/ir_ref/coerce_enum.rs b/engine/baml-lib/jsonish/src/deserializer/coercer/ir_ref/coerce_enum.rs index 86eb85889..2a0c46e20 100644 --- a/engine/baml-lib/jsonish/src/deserializer/coercer/ir_ref/coerce_enum.rs +++ b/engine/baml-lib/jsonish/src/deserializer/coercer/ir_ref/coerce_enum.rs @@ -3,7 +3,7 @@ use baml_types::FieldType; use internal_baml_jinja::types::Enum; use crate::deserializer::{ - coercer::{match_string::match_string, ParsingError, TypeCoercer}, + coercer::{ir_ref::coerce_class::apply_constraints, match_string::match_string, ParsingError, TypeCoercer}, types::BamlValueWithFlags, }; @@ -42,11 +42,20 @@ impl TypeCoercer for Enum { current = value.map(|v| v.r#type()).unwrap_or("".into()) ); + let constraints = ctx + .of + .find_enum(self.name.real_name()) + .map_or(vec![], |class| class.constraints.clone()); + let variant_match = match_string(ctx, target, value, &enum_match_candidates(self))?; + let enum_match = + apply_constraints( + target, vec![], + + BamlValueWithFlags::Enum(self.name.real_name().to_string(), variant_match) + , constraints.clone() + )?; - Ok(BamlValueWithFlags::Enum( - self.name.real_name().to_string(), - variant_match, - )) + Ok(enum_match) } } diff --git a/engine/baml-lib/jsonish/src/deserializer/coercer/mod.rs b/engine/baml-lib/jsonish/src/deserializer/coercer/mod.rs index 8e6ec6c8d..696a2b623 100644 --- a/engine/baml-lib/jsonish/src/deserializer/coercer/mod.rs +++ b/engine/baml-lib/jsonish/src/deserializer/coercer/mod.rs @@ -19,9 +19,9 @@ use internal_baml_core::ir::{jinja_helpers::evaluate_predicate, FieldType}; use super::types::BamlValueWithFlags; pub struct ParsingContext<'a> { - scope: Vec, - of: &'a OutputFormatContent, - allow_partials: bool, + pub scope: Vec, + pub of: &'a OutputFormatContent, + pub allow_partials: bool, } impl ParsingContext<'_> { diff --git a/engine/baml-lib/jsonish/src/tests/mod.rs b/engine/baml-lib/jsonish/src/tests/mod.rs index c5d7dda0a..8f32eaaa1 100644 --- a/engine/baml-lib/jsonish/src/tests/mod.rs +++ b/engine/baml-lib/jsonish/src/tests/mod.rs @@ -120,10 +120,10 @@ fn relevant_data_models<'a>( while !start.is_empty() { let output = start.pop().unwrap(); - match output.distribute_constraints() { + match ir.distribute_constraints(&output) { (FieldType::Enum(enm), constraints) => { if checked_types.insert(output.to_string()) { - let walker = ir.find_enum(enm); + let walker = ir.find_enum(&enm); let real_values = walker .as_ref() @@ -134,7 +134,7 @@ fn relevant_data_models<'a>( .flatten() .into_iter() .map(|value| { - let meta = find_enum_value(enm, &value, &walker, env_values)?; + let meta = find_enum_value(enm.as_str(), &value, &walker, env_values)?; Ok(meta.map(|m| m)) }) .filter_map(|v| v.transpose()) diff --git a/engine/baml-lib/jsonish/src/tests/test_constraints.rs b/engine/baml-lib/jsonish/src/tests/test_constraints.rs index 1caadab65..61365686f 100644 --- a/engine/baml-lib/jsonish/src/tests/test_constraints.rs +++ b/engine/baml-lib/jsonish/src/tests/test_constraints.rs @@ -119,8 +119,40 @@ test_deserializer_with_expected_score!( 0 ); -const MISSPELLED_CONSTRAINT: &str = r#" +const BLOCK_LEVEL: &str = r#" class Foo { - foo int @description("hi") @check(hi, {{this == 1}}) + foo int + @@assert(hi, {{ this.foo > 0 }}) +} + +enum MyEnum { + ONE + TWO + THREE + @@assert(nonsense, {{ this == "TWO" }}) } "#; + +test_failing_deserializer!( + test_block_level_assert_failure, + BLOCK_LEVEL, + r#"{"foo": -1}"#, + FieldType::Class("Foo".to_string()) +); + + +test_deserializer!( + test_block_level_check_failure, + BLOCK_LEVEL, + r#"{"foo": 1}"#, + FieldType::Class("Foo".to_string()), + {"foo": 1} +); + + +test_failing_deserializer!( + test_block_level_enum_assert_failure, + BLOCK_LEVEL, + r#"THREE"#, + FieldType::Enum("MyEnum".to_string()) +); diff --git a/engine/baml-lib/parser-database/Cargo.toml b/engine/baml-lib/parser-database/Cargo.toml index 1d95a7126..32fe6c04d 100644 --- a/engine/baml-lib/parser-database/Cargo.toml +++ b/engine/baml-lib/parser-database/Cargo.toml @@ -30,6 +30,7 @@ serde.workspace = true serde_json.workspace = true regex = "1.10.2" anyhow.workspace = true +itertools = "0.13.0" [features] default = [] diff --git a/engine/baml-lib/parser-database/src/attributes/constraint.rs b/engine/baml-lib/parser-database/src/attributes/constraint.rs index 993944cf7..6a320abf2 100644 --- a/engine/baml-lib/parser-database/src/attributes/constraint.rs +++ b/engine/baml-lib/parser-database/src/attributes/constraint.rs @@ -1,38 +1,56 @@ use baml_types::{Constraint, ConstraintLevel}; -use internal_baml_schema_ast::ast::Expression; +use internal_baml_diagnostics::{DatamodelError, Span}; +use internal_baml_schema_ast::ast::{Attribute, Expression}; use crate::{context::Context, types::Attributes}; pub(super) fn visit_constraint_attributes( attribute_name: String, + span: Span, attributes: &mut Attributes, ctx: &mut Context<'_>, ) { - let expression_arg = ctx.visit_default_arg_with_idx("expression").map_err(|err| { - ctx.push_error(err); - }); - let label = ctx.visit_default_arg_with_idx("name"); - let label = match label { - Ok((_, Expression::StringValue(descr, _))) => Some(descr.clone()), - _ => None, + let arguments: Vec<&Expression> = ctx + .get_all_args() + .into_iter() + .map(|(_arg_id, arg)| arg) + .collect(); + + let level = match attribute_name.as_str() { + "assert" => ConstraintLevel::Assert, + "check" => ConstraintLevel::Check, + _ => panic!("Internal error - the parser should have ruled out other attribute names."), }; - match expression_arg { - Ok((_, Expression::JinjaExpressionValue(expression, _))) => { - let level = match attribute_name.as_str() { - "assert" => ConstraintLevel::Assert, - "check" => ConstraintLevel::Check, - _ => { - panic!("Internal error: Only \"assert\" and \"check\" are valid attribute names in this context."); - } - }; - attributes.constraints.push(Constraint { - level, - expression: expression.clone(), - label, - }); + + let (label, expression) = match arguments.as_slice() { + [Expression::JinjaExpressionValue(expression, _)] => { + if level == ConstraintLevel::Check { + ctx.push_error(DatamodelError::new_attribute_validation_error( + "Checks must specify a label.", + attribute_name.as_str(), + span, + )); + } + (None, expression.clone()) + } + [Expression::Identifier(label), Expression::JinjaExpressionValue(expression, _)] => { + (Some(label.to_string()), expression.clone()) } - _ => panic!( - "The impossible happened: Reached arguments that are ruled out by the tokenizer." - ), - } + _ => { + ctx.push_error( + DatamodelError::new_attribute_validation_error( + "Checks and asserts may have either a label and an expression, or a lone expression.", + attribute_name.as_str(), + span + ) + ); + return (); + } + }; + + attributes.constraints.push(Constraint { + level, + expression, + label, + }); } diff --git a/engine/baml-lib/parser-database/src/attributes/mod.rs b/engine/baml-lib/parser-database/src/attributes/mod.rs index ec26fec8b..95daebcba 100644 --- a/engine/baml-lib/parser-database/src/attributes/mod.rs +++ b/engine/baml-lib/parser-database/src/attributes/mod.rs @@ -89,13 +89,14 @@ fn resolve_type_exp_block_attributes<'db>( ctx: &mut Context<'db>, sub_type: SubType, ) { + let span = ast_typexpr.span.clone(); match sub_type { SubType::Enum => { let mut enum_attributes = EnumAttributes::default(); for (value_idx, _value) in ast_typexpr.iter_fields() { ctx.assert_all_attributes_processed((type_id, value_idx).into()); - if let Some(attrs) = to_string_attribute::visit(ctx, false) { + if let Some(attrs) = to_string_attribute::visit(ctx, &span, false) { enum_attributes.value_serilizers.insert(value_idx, attrs); } ctx.validate_visited_attributes(); @@ -103,7 +104,7 @@ fn resolve_type_exp_block_attributes<'db>( // Now validate the enum attributes. ctx.assert_all_attributes_processed(type_id.into()); - enum_attributes.serilizer = to_string_attribute::visit(ctx, true); + enum_attributes.serilizer = to_string_attribute::visit(ctx, &span, true); ctx.validate_visited_attributes(); ctx.types.enum_attributes.insert(type_id, enum_attributes); @@ -111,9 +112,9 @@ fn resolve_type_exp_block_attributes<'db>( SubType::Class => { let mut class_attributes = ClassAttributes::default(); - for (field_idx, _field) in ast_typexpr.iter_fields() { + for (field_idx, field) in ast_typexpr.iter_fields() { ctx.assert_all_attributes_processed((type_id, field_idx).into()); - if let Some(attrs) = to_string_attribute::visit(ctx, false) { + if let Some(attrs) = to_string_attribute::visit(ctx, &field.span, false) { class_attributes.field_serilizers.insert(field_idx, attrs); } ctx.validate_visited_attributes(); @@ -121,7 +122,7 @@ fn resolve_type_exp_block_attributes<'db>( // Now validate the class attributes. ctx.assert_all_attributes_processed(type_id.into()); - class_attributes.serilizer = to_string_attribute::visit(ctx, true); + class_attributes.serilizer = to_string_attribute::visit(ctx, &span, true); ctx.validate_visited_attributes(); ctx.types.class_attributes.insert(type_id, class_attributes); diff --git a/engine/baml-lib/parser-database/src/attributes/to_string_attribute.rs b/engine/baml-lib/parser-database/src/attributes/to_string_attribute.rs index a59e2acfc..61524d2f5 100644 --- a/engine/baml-lib/parser-database/src/attributes/to_string_attribute.rs +++ b/engine/baml-lib/parser-database/src/attributes/to_string_attribute.rs @@ -1,10 +1,15 @@ +use baml_types::Constraint; +use internal_baml_diagnostics::{DatamodelError, Span}; + +use itertools::Itertools; + use crate::{context::Context, types::Attributes}; use super::alias::visit_alias_attribute; use super::constraint::visit_constraint_attributes; use super::description::visit_description_attribute; -pub(super) fn visit(ctx: &mut Context<'_>, as_block: bool) -> Option { +pub(super) fn visit(ctx: &mut Context<'_>, span: &Span, as_block: bool) -> Option { let mut modified = false; let mut attributes = Attributes::default(); @@ -27,9 +32,8 @@ pub(super) fn visit(ctx: &mut Context<'_>, as_block: bool) -> Option ctx.validate_visited_arguments(); } - // TODO: (Greg) Note: This validation never gets run. - if let Some(attribute_name) = ctx.visit_repeated_attr_from_names(&["assert", "check"]) { - visit_constraint_attributes(attribute_name, &mut attributes, ctx); + if let Some((attribute_name, span)) = ctx.visit_repeated_attr_from_names(&["assert", "check"]) { + visit_constraint_attributes(attribute_name, span, &mut attributes, ctx); modified = true; ctx.validate_visited_arguments(); } diff --git a/engine/baml-lib/parser-database/src/context/mod.rs b/engine/baml-lib/parser-database/src/context/mod.rs index 5b7a95ae1..742741edb 100644 --- a/engine/baml-lib/parser-database/src/context/mod.rs +++ b/engine/baml-lib/parser-database/src/context/mod.rs @@ -1,5 +1,5 @@ -use internal_baml_diagnostics::DatamodelWarning; -use internal_baml_schema_ast::ast::ArgumentId; +use internal_baml_diagnostics::{DatamodelWarning, Span}; +use internal_baml_schema_ast::ast::{Argument, ArgumentId, Attribute}; use crate::{ ast, ast::WithName, interner::StringInterner, names::Names, types::Types, DatamodelError, @@ -128,12 +128,10 @@ impl<'db> Context<'db> { pub(crate) fn visit_repeated_attr_from_names( &mut self, names: &'static [&'static str], - ) -> Option { + ) -> Option<(String, Span)> { let mut has_valid_attribute = false; - let mut matching_name: Option = None; + let mut matching_attr: Option<(String, Span)> = None; - let all_attributes = - iter_attributes(self.attributes.attributes.as_ref(), self.ast).collect::>(); while !has_valid_attribute { let first_attr = iter_attributes(self.attributes.attributes.as_ref(), self.ast) .filter(|(_, attr)| names.contains(&attr.name.name())) @@ -145,10 +143,10 @@ impl<'db> Context<'db> { }; self.attributes.unused_attributes.remove(&attr_id); has_valid_attribute = self.set_attribute(attr_id, attr); - matching_name = Some(attr.name.name().to_string()); + matching_attr = Some((attr.name.to_string(), attr.span.clone())); } - matching_name + matching_attr } /// Validate an _optional_ attribute that should occur only once. Returns whether the attribute @@ -202,6 +200,14 @@ impl<'db> Context<'db> { } } + pub (crate) fn get_all_args(&mut self) -> Vec<(ArgumentId, &'db ast::Expression)> { + let args = self.attributes.args.iter().map(|arg_id| { + (arg_id.clone(), &self.arg_at(*arg_id).value) + }).collect(); + self.attributes.args.clear(); + args + } + /// This must be called at the end of arguments validation. It will report errors for each argument that was not used by the validators. The Drop impl will helpfully panic /// otherwise. pub(crate) fn validate_visited_arguments(&mut self) { diff --git a/engine/baml-lib/parser-database/src/walkers/class.rs b/engine/baml-lib/parser-database/src/walkers/class.rs index e096302f3..b3d7cc072 100644 --- a/engine/baml-lib/parser-database/src/walkers/class.rs +++ b/engine/baml-lib/parser-database/src/walkers/class.rs @@ -2,6 +2,7 @@ use std::collections::HashSet; use super::{field::FieldWalker, EnumWalker}; use crate::types::Attributes; +use baml_types::Constraint; use either::Either; use internal_baml_schema_ast::ast::Identifier; use internal_baml_schema_ast::ast::SubType; @@ -89,6 +90,11 @@ impl<'db> ClassWalker<'db> { } } + /// Get the constraints of a class or an enum. + pub fn get_constraints(&self, sub_type: SubType) -> Option> { + self.get_default_attributes(sub_type).map(|attrs| attrs.constraints.clone()) + } + /// Arguments of the function. pub fn find_input_arg_by_name(self, name: &str) -> Option> { self.ast_type_block().input().and_then(|args| { diff --git a/engine/baml-lib/schema-ast/src/ast/field.rs b/engine/baml-lib/schema-ast/src/ast/field.rs index c824d27ef..1641ca83b 100644 --- a/engine/baml-lib/schema-ast/src/ast/field.rs +++ b/engine/baml-lib/schema-ast/src/ast/field.rs @@ -38,7 +38,7 @@ pub struct Field { /// ``` pub attributes: Vec, /// The location of this field in the text representation. - pub(crate) span: Span, + pub span: Span, } impl Field { diff --git a/engine/baml-runtime/src/internal/prompt_renderer/render_output_format.rs b/engine/baml-runtime/src/internal/prompt_renderer/render_output_format.rs index f425e5448..1ef1ce19b 100644 --- a/engine/baml-runtime/src/internal/prompt_renderer/render_output_format.rs +++ b/engine/baml-runtime/src/internal/prompt_renderer/render_output_format.rs @@ -205,11 +205,11 @@ fn relevant_data_models<'a>( let mut start: Vec = vec![output.clone()]; while let Some(output) = start.pop() { - match output.distribute_constraints() { + match ir.distribute_constraints(&output) { (FieldType::Enum(enm), constraints) => { if checked_types.insert(output.to_string()) { let overrides = ctx.enum_overrides.get(enm); - let walker = ir.find_enum(enm); + let walker = ir.find_enum(&enm); let real_values = walker .as_ref() @@ -226,7 +226,7 @@ fn relevant_data_models<'a>( .collect::>() .into_iter() .map(|value| { - let meta = find_enum_value(enm, &value, &walker, &overrides, ctx)?; + let meta = find_enum_value(&enm, &value, &walker, &overrides, ctx)?; Ok(meta.map(|m| m)) }) .filter_map(|v| v.transpose()) diff --git a/engine/language_client_python/src/types/function_results.rs b/engine/language_client_python/src/types/function_results.rs index 763bdb70e..18e343b3c 100644 --- a/engine/language_client_python/src/types/function_results.rs +++ b/engine/language_client_python/src/types/function_results.rs @@ -1,7 +1,7 @@ use baml_types::{BamlValueWithMeta, ResponseCheck}; use pyo3::prelude::{pymethods, PyResult}; use pyo3::types::{PyAnyMethods, PyDict, PyModule, PyTuple, PyType}; -use pyo3::{Bound, IntoPy, PyObject, Python}; +use pyo3::{Bound, IntoPy, PyAny, PyObject, Python}; use crate::errors::BamlError; @@ -137,7 +137,24 @@ fn pythonize_strict( let properties_dict = pyo3::types::PyDict::new_bound(py); for (key, value) in properties { - properties_dict.set_item(key, value)?; + // For each field, try to call pydantic's `model_dump` on the + // field. This is necessary in case the field is `Checked[_,_]`, + // because pydantic requires to parse such fields from json, + // rather than from a Python object. The python object is an + // untyped Dict, but python expects a typed `Checked`. + // By turning such values into `json`, we allow pydantic's + // parser to more flexibly accept input at its expected + // type. + // + // This has the side-effect of calling `model_dump` on all + // classes inheriting `BaseModel`, which probably incurs some + // performance penalty. So we should consider testing whether + // the field is a `Checked` before doing a `model_dump`. + let value_model = value.call_method0(py, "model_dump"); + match value_model { + Err(_) => { properties_dict.set_item(key, value)?; } + Ok(m) => {properties_dict.set_item(key, m)?;} + } } let class_type = match cls_module.getattr(class_name.as_str()) { @@ -150,8 +167,8 @@ fn pythonize_strict( Err(_) => return Ok(properties_dict.into()), }; + let instance = class_type.call_method("model_validate", (properties_dict.clone(),), None)?; - let instance = class_type.call_method("model_validate", (properties_dict,), None)?; Ok(instance.into()) } BamlValueWithMeta::Null(_) => Ok(py.None()), @@ -189,11 +206,11 @@ fn pythonize_strict( let type_parameters_tuple = PyTuple::new_bound(py, &[value_type.as_ref(), &literal_check_names]); // Create the Checked type using __class_getitem__ - let class_checked_type = class_checked_type_constructor + let class_checked_type: Bound<'_, PyAny> = class_checked_type_constructor .call_method1("__class_getitem__", (type_parameters_tuple,))?; // Validate the model with the constructed type - let checked_instance = class_checked_type.call_method("model_validate", (properties_dict,), None)?; + let checked_instance = class_checked_type.call_method("model_validate", (properties_dict.clone(),), None)?; Ok(checked_instance.into()) } diff --git a/engine/language_client_ruby/Gemfile.lock b/engine/language_client_ruby/Gemfile.lock index 8b8f71576..caf011d02 100644 --- a/engine/language_client_ruby/Gemfile.lock +++ b/engine/language_client_ruby/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - baml (0.63.0) + baml (0.65.0) GEM remote: https://rubygems.org/ diff --git a/integ-tests/baml_src/test-files/constraints/constraints.baml b/integ-tests/baml_src/test-files/constraints/constraints.baml index 551af82db..b48099b05 100644 --- a/integ-tests/baml_src/test-files/constraints/constraints.baml +++ b/integ-tests/baml_src/test-files/constraints/constraints.baml @@ -73,3 +73,55 @@ function StreamFailingAssertion(theme: string, length: int) -> TwoStoriesOneTitl {{ctx.output_format}} "# } + +class BlockConstraint { + foo int + bar string + @@check(cross_field, {{ this.bar|length > this.foo }}) +} + +function MakeBlockConstraint() -> BlockConstraint { + client GPT35 + prompt #" + Generate an output in the following schema with a short string and a large int. + + {{ ctx.output_format }} + "# +} + +class NestedBlockConstraint { + nbc BlockConstraint +} + +class BlockConstraintForParam { + bcfp int + bcfp2 string + @@assert(hi, {{ this.bcfp2|length < this.bcfp }}) +} + +class NestedBlockConstraintForParam { + nbcfp BlockConstraintForParam +} + +function MakeNestedBlockConstraint() -> NestedBlockConstraint { + client GPT35 + prompt #"Generate an output where the inner foo is 1 and the inner bar is "hello". + {{ ctx.output_format }} + "# +} + +function UseBlockConstraint(inp: BlockConstraintForParam) -> int { + client GPT35 + prompt #" + Generate 3 + {{ ctx.output_format }} + "# +} + +function UseNestedBlockConstraint(inp: NestedBlockConstraintForParam) -> int { + client GPT35 + prompt #" + Generate 3 + {{ ctx.output_format }} + "# +} diff --git a/integ-tests/python/baml_client/async_client.py b/integ-tests/python/baml_client/async_client.py index 3cd77967f..2134574d5 100644 --- a/integ-tests/python/baml_client/async_client.py +++ b/integ-tests/python/baml_client/async_client.py @@ -1269,6 +1269,52 @@ async def LiteralUnionsTest( ) return cast(Union[Literal[1], Literal[True], Literal["string output"]], raw.cast_to(types, types)) + async def MakeBlockConstraint( + self, + + baml_options: BamlCallOptions = {}, + ) -> Checked[types.BlockConstraint,types.Literal["cross_field"]]: + __tb__ = baml_options.get("tb", None) + if __tb__ is not None: + tb = __tb__._tb + else: + tb = None + __cr__ = baml_options.get("client_registry", None) + + raw = await self.__runtime.call_function( + "MakeBlockConstraint", + { + + }, + self.__ctx_manager.get(), + tb, + __cr__, + ) + return cast(Checked[types.BlockConstraint,types.Literal["cross_field"]], raw.cast_to(types, types)) + + async def MakeNestedBlockConstraint( + self, + + baml_options: BamlCallOptions = {}, + ) -> types.NestedBlockConstraint: + __tb__ = baml_options.get("tb", None) + if __tb__ is not None: + tb = __tb__._tb + else: + tb = None + __cr__ = baml_options.get("client_registry", None) + + raw = await self.__runtime.call_function( + "MakeNestedBlockConstraint", + { + + }, + self.__ctx_manager.get(), + tb, + __cr__, + ) + return cast(types.NestedBlockConstraint, raw.cast_to(types, types)) + async def MyFunc( self, input: str, @@ -2488,6 +2534,29 @@ async def UnionTest_Function( ) return cast(types.UnionTest_ReturnType, raw.cast_to(types, types)) + async def UseBlockConstraint( + self, + inp: types.BlockConstraintForParam, + baml_options: BamlCallOptions = {}, + ) -> int: + __tb__ = baml_options.get("tb", None) + if __tb__ is not None: + tb = __tb__._tb + else: + tb = None + __cr__ = baml_options.get("client_registry", None) + + raw = await self.__runtime.call_function( + "UseBlockConstraint", + { + "inp": inp, + }, + self.__ctx_manager.get(), + tb, + __cr__, + ) + return cast(int, raw.cast_to(types, types)) + async def UseMalformedConstraints( self, a: types.MalformedConstraints2, @@ -2511,6 +2580,29 @@ async def UseMalformedConstraints( ) return cast(int, raw.cast_to(types, types)) + async def UseNestedBlockConstraint( + self, + inp: types.NestedBlockConstraintForParam, + baml_options: BamlCallOptions = {}, + ) -> int: + __tb__ = baml_options.get("tb", None) + if __tb__ is not None: + tb = __tb__._tb + else: + tb = None + __cr__ = baml_options.get("client_registry", None) + + raw = await self.__runtime.call_function( + "UseNestedBlockConstraint", + { + "inp": inp, + }, + self.__ctx_manager.get(), + tb, + __cr__, + ) + return cast(int, raw.cast_to(types, types)) + class BamlStreamClient: @@ -4115,6 +4207,64 @@ def LiteralUnionsTest( self.__ctx_manager.get(), ) + def MakeBlockConstraint( + self, + + baml_options: BamlCallOptions = {}, + ) -> baml_py.BamlStream[Checked[partial_types.BlockConstraint,types.Literal["cross_field"]], Checked[types.BlockConstraint,types.Literal["cross_field"]]]: + __tb__ = baml_options.get("tb", None) + if __tb__ is not None: + tb = __tb__._tb + else: + tb = None + __cr__ = baml_options.get("client_registry", None) + + raw = self.__runtime.stream_function( + "MakeBlockConstraint", + { + }, + None, + self.__ctx_manager.get(), + tb, + __cr__, + ) + + return baml_py.BamlStream[Checked[partial_types.BlockConstraint,types.Literal["cross_field"]], Checked[types.BlockConstraint,types.Literal["cross_field"]]]( + raw, + lambda x: cast(Checked[partial_types.BlockConstraint,types.Literal["cross_field"]], x.cast_to(types, partial_types)), + lambda x: cast(Checked[types.BlockConstraint,types.Literal["cross_field"]], x.cast_to(types, types)), + self.__ctx_manager.get(), + ) + + def MakeNestedBlockConstraint( + self, + + baml_options: BamlCallOptions = {}, + ) -> baml_py.BamlStream[partial_types.NestedBlockConstraint, types.NestedBlockConstraint]: + __tb__ = baml_options.get("tb", None) + if __tb__ is not None: + tb = __tb__._tb + else: + tb = None + __cr__ = baml_options.get("client_registry", None) + + raw = self.__runtime.stream_function( + "MakeNestedBlockConstraint", + { + }, + None, + self.__ctx_manager.get(), + tb, + __cr__, + ) + + return baml_py.BamlStream[partial_types.NestedBlockConstraint, types.NestedBlockConstraint]( + raw, + lambda x: cast(partial_types.NestedBlockConstraint, x.cast_to(types, partial_types)), + lambda x: cast(types.NestedBlockConstraint, x.cast_to(types, types)), + self.__ctx_manager.get(), + ) + def MyFunc( self, input: str, @@ -5705,6 +5855,36 @@ def UnionTest_Function( self.__ctx_manager.get(), ) + def UseBlockConstraint( + self, + inp: types.BlockConstraintForParam, + baml_options: BamlCallOptions = {}, + ) -> baml_py.BamlStream[Optional[int], int]: + __tb__ = baml_options.get("tb", None) + if __tb__ is not None: + tb = __tb__._tb + else: + tb = None + __cr__ = baml_options.get("client_registry", None) + + raw = self.__runtime.stream_function( + "UseBlockConstraint", + { + "inp": inp, + }, + None, + self.__ctx_manager.get(), + tb, + __cr__, + ) + + return baml_py.BamlStream[Optional[int], int]( + raw, + lambda x: cast(Optional[int], x.cast_to(types, partial_types)), + lambda x: cast(int, x.cast_to(types, types)), + self.__ctx_manager.get(), + ) + def UseMalformedConstraints( self, a: types.MalformedConstraints2, @@ -5735,6 +5915,36 @@ def UseMalformedConstraints( self.__ctx_manager.get(), ) + def UseNestedBlockConstraint( + self, + inp: types.NestedBlockConstraintForParam, + baml_options: BamlCallOptions = {}, + ) -> baml_py.BamlStream[Optional[int], int]: + __tb__ = baml_options.get("tb", None) + if __tb__ is not None: + tb = __tb__._tb + else: + tb = None + __cr__ = baml_options.get("client_registry", None) + + raw = self.__runtime.stream_function( + "UseNestedBlockConstraint", + { + "inp": inp, + }, + None, + self.__ctx_manager.get(), + tb, + __cr__, + ) + + return baml_py.BamlStream[Optional[int], int]( + raw, + lambda x: cast(Optional[int], x.cast_to(types, partial_types)), + lambda x: cast(int, x.cast_to(types, types)), + self.__ctx_manager.get(), + ) + b = BamlAsyncClient(DO_NOT_USE_DIRECTLY_UNLESS_YOU_KNOW_WHAT_YOURE_DOING_RUNTIME, DO_NOT_USE_DIRECTLY_UNLESS_YOU_KNOW_WHAT_YOURE_DOING_CTX) diff --git a/integ-tests/python/baml_client/inlinedbaml.py b/integ-tests/python/baml_client/inlinedbaml.py index f1c969991..6452eff9c 100644 --- a/integ-tests/python/baml_client/inlinedbaml.py +++ b/integ-tests/python/baml_client/inlinedbaml.py @@ -30,7 +30,7 @@ "test-files/aliases/classes.baml": "class TestClassAlias {\n key string @alias(\"key-dash\") @description(#\"\n This is a description for key\n af asdf\n \"#)\n key2 string @alias(\"key21\")\n key3 string @alias(\"key with space\")\n key4 string //unaliased\n key5 string @alias(\"key.with.punctuation/123\")\n}\n\nfunction FnTestClassAlias(input: string) -> TestClassAlias {\n client GPT35\n prompt #\"\n {{ctx.output_format}}\n \"#\n}\n\ntest FnTestClassAlias {\n functions [FnTestClassAlias]\n args {\n input \"example input\"\n }\n}\n", "test-files/aliases/enums.baml": "enum TestEnum {\n A @alias(\"k1\") @description(#\"\n User is angry\n \"#)\n B @alias(\"k22\") @description(#\"\n User is happy\n \"#)\n // tests whether k1 doesnt incorrectly get matched with k11\n C @alias(\"k11\") @description(#\"\n User is sad\n \"#)\n D @alias(\"k44\") @description(\n User is confused\n )\n E @description(\n User is excited\n )\n F @alias(\"k5\") // only alias\n \n G @alias(\"k6\") @description(#\"\n User is bored\n With a long description\n \"#)\n \n @@alias(\"Category\")\n}\n\nfunction FnTestAliasedEnumOutput(input: string) -> TestEnum {\n client GPT35\n prompt #\"\n Classify the user input into the following category\n \n {{ ctx.output_format }}\n\n {{ _.role('user') }}\n {{input}}\n\n {{ _.role('assistant') }}\n Category ID:\n \"#\n}\n\ntest FnTestAliasedEnumOutput {\n functions [FnTestAliasedEnumOutput]\n args {\n input \"mehhhhh\"\n }\n}", "test-files/comments/comments.baml": "// add some functions, classes, enums etc with comments all over.", - "test-files/constraints/constraints.baml": "// These classes and functions test several properties of\n// constrains:\n//\n// - The ability for constrains on fields to pass or fail.\n// - The ability for constraints on bare args and return types to pass or fail.\n// - The ability of constraints to influence which variant of a union is chosen\n// by the parser, when the structure is not sufficient to decide.\n\n\nclass Martian {\n age int @check(young_enough, {{ this < 30 }})\n}\n\nclass Earthling {\n age int @check(earth_aged, {{this < 200 and this > 0}}) @check(no_infants, {{this >1}})\n}\n\n\nclass FooAny {\n planetary_age Martian | Earthling\n certainty int @check(unreasonably_certain, {{this == 102931}})\n species string @check(trivial, {{this == \"Homo sapiens\"}}) @check(regex_good, {{this|regex_match(\"Homo\")}}) @check(regex_bad, {{this|regex_match(\"neanderthalensis\")}})\n}\n\n\nfunction PredictAge(name: string) -> FooAny {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling\n and capitalization). I'll give you a hint: If the name\n is \"Greg\", his age is 41.\n\n {{ctx.output_format}}\n \"#\n}\n\n\nfunction PredictAgeBare(inp: string @assert(big_enough, {{this|length > 1}})) -> int @check(too_big, {{this == 10102}}) {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ inp.name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling).\n\n {{ctx.output_format}}\n \"#\n}\n\nfunction ReturnFailingAssert(inp: int @assert(small_int, {{this < 10}})) -> int @assert(big_int, {{this > 100}}) {\n client GPT35\n prompt #\"\n Return the next integer after {{ inp }}.\n\n {{ctx.output_format}}\n \"#\n}\n\nclass TwoStoriesOneTitle {\n title string\n story_a string @assert(too_long_story, {{this|length > 1000000}} )\n story_b string @assert(too_long_story, {{this|length > 1000000}} )\n}\n\nfunction StreamFailingAssertion(theme: string, length: int) -> TwoStoriesOneTitle {\n client GPT35\n prompt #\"\n Tell me two different stories along the theme of {{ theme }} with the same title.\n Please make each about {{ length }} words long.\n {{ctx.output_format}}\n \"#\n}\n", + "test-files/constraints/constraints.baml": "// These classes and functions test several properties of\n// constrains:\n//\n// - The ability for constrains on fields to pass or fail.\n// - The ability for constraints on bare args and return types to pass or fail.\n// - The ability of constraints to influence which variant of a union is chosen\n// by the parser, when the structure is not sufficient to decide.\n\n\nclass Martian {\n age int @check(young_enough, {{ this < 30 }})\n}\n\nclass Earthling {\n age int @check(earth_aged, {{this < 200 and this > 0}}) @check(no_infants, {{this >1}})\n}\n\n\nclass FooAny {\n planetary_age Martian | Earthling\n certainty int @check(unreasonably_certain, {{this == 102931}})\n species string @check(trivial, {{this == \"Homo sapiens\"}}) @check(regex_good, {{this|regex_match(\"Homo\")}}) @check(regex_bad, {{this|regex_match(\"neanderthalensis\")}})\n}\n\n\nfunction PredictAge(name: string) -> FooAny {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling\n and capitalization). I'll give you a hint: If the name\n is \"Greg\", his age is 41.\n\n {{ctx.output_format}}\n \"#\n}\n\n\nfunction PredictAgeBare(inp: string @assert(big_enough, {{this|length > 1}})) -> int @check(too_big, {{this == 10102}}) {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ inp.name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling).\n\n {{ctx.output_format}}\n \"#\n}\n\nfunction ReturnFailingAssert(inp: int @assert(small_int, {{this < 10}})) -> int @assert(big_int, {{this > 100}}) {\n client GPT35\n prompt #\"\n Return the next integer after {{ inp }}.\n\n {{ctx.output_format}}\n \"#\n}\n\nclass TwoStoriesOneTitle {\n title string\n story_a string @assert(too_long_story, {{this|length > 1000000}} )\n story_b string @assert(too_long_story, {{this|length > 1000000}} )\n}\n\nfunction StreamFailingAssertion(theme: string, length: int) -> TwoStoriesOneTitle {\n client GPT35\n prompt #\"\n Tell me two different stories along the theme of {{ theme }} with the same title.\n Please make each about {{ length }} words long.\n {{ctx.output_format}}\n \"#\n}\n\nclass BlockConstraint {\n foo int\n bar string\n @@check(cross_field, {{ this.bar|length > this.foo }})\n}\n\nfunction MakeBlockConstraint() -> BlockConstraint {\n client GPT35\n prompt #\"\n Generate an output in the following schema with a short string and a large int.\n\n {{ ctx.output_format }}\n \"#\n}\n\nclass NestedBlockConstraint {\n nbc BlockConstraint\n}\n\nclass BlockConstraintForParam {\n bcfp int\n bcfp2 string\n @@assert(hi, {{ this.bcfp2|length < this.bcfp }})\n}\n\nclass NestedBlockConstraintForParam {\n nbcfp BlockConstraintForParam\n}\n\nfunction MakeNestedBlockConstraint() -> NestedBlockConstraint {\n client GPT35\n prompt #\"Generate an output where the inner foo is 1 and the inner bar is \"hello\".\n {{ ctx.output_format }}\n \"#\n}\n\nfunction UseBlockConstraint(inp: BlockConstraintForParam) -> int {\n client GPT35\n prompt #\"\n Generate 3\n {{ ctx.output_format }}\n \"#\n}\n\nfunction UseNestedBlockConstraint(inp: NestedBlockConstraintForParam) -> int {\n client GPT35\n prompt #\"\n Generate 3\n {{ ctx.output_format }}\n \"#\n}\n", "test-files/constraints/contact-info.baml": "class PhoneNumber {\n value string @assert(valid_phone_number, {{this|regex_match(\"\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}\")}})\n}\n\nclass EmailAddress {\n value string @assert(valid_email, {{this|regex_match(\"^[_]*([a-z0-9]+(\\.|_*)?)+@([a-z][a-z0-9-]+(\\.|-*\\.))+[a-z]{2,6}$\")}})\n}\n\nclass ContactInfo {\n primary PhoneNumber | EmailAddress\n secondary (PhoneNumber | EmailAddress)?\n}\n\nfunction ExtractContactInfo(document: string) -> ContactInfo {\n client GPT35\n prompt #\"\n Extract a primary contact info, and if possible a secondary contact\n info, from this document:\n\n {{ document }}\n\n {{ ctx.output_format }}\n \"#\n}\n", "test-files/constraints/malformed-constraints.baml": "class MalformedConstraints {\n foo int @check(foo_check, {{ this.length() > 0 }})\n}\n\nclass MalformedConstraints2 {\n foo int @assert(foo_check, {{ this.length() > 0 }})\n}\n\nfunction ReturnMalformedConstraints(a: int) -> MalformedConstraints {\n client GPT35\n prompt #\"\n Return the integer after {{ a }}\n\n {{ ctx.output_format }}\n \"#\n}\n\nfunction UseMalformedConstraints(a: MalformedConstraints2) -> int {\n client GPT35\n prompt #\"\n Return the integer after {{ a.foo }}\n\n {{ ctx.output_format }}\n \"#\n}\n", "test-files/descriptions/descriptions.baml": "\nclass Nested {\n prop3 string | null @description(#\"\n write \"three\"\n \"#)\n prop4 string | null @description(#\"\n write \"four\"\n \"#) @alias(\"blah\")\n prop20 Nested2\n}\n\nclass Nested2 {\n prop11 string | null @description(#\"\n write \"three\"\n \"#)\n prop12 string | null @description(#\"\n write \"four\"\n \"#) @alias(\"blah\")\n}\n\nclass Schema {\n prop1 string | null @description(#\"\n write \"one\"\n \"#)\n prop2 Nested | string @description(#\"\n write \"two\"\n \"#)\n prop5 (string | null)[] @description(#\"\n write \"hi\"\n \"#)\n prop6 string | Nested[] @alias(\"blah\") @description(#\"\n write the string \"blah\" regardless of the other types here\n \"#)\n nested_attrs (string | null | Nested)[] @description(#\"\n write the string \"nested\" regardless of other types\n \"#)\n parens (string | null) @description(#\"\n write \"parens1\"\n \"#)\n other_group (string | (int | string)) @description(#\"\n write \"other\"\n \"#) @alias(other)\n}\n\n\nfunction SchemaDescriptions(input: string) -> Schema {\n client GPT4o\n prompt #\"\n Return a schema with this format:\n\n {{ctx.output_format}}\n \"#\n}", diff --git a/integ-tests/python/baml_client/partial_types.py b/integ-tests/python/baml_client/partial_types.py index 9dbcdae3e..1e2c7edf9 100644 --- a/integ-tests/python/baml_client/partial_types.py +++ b/integ-tests/python/baml_client/partial_types.py @@ -40,6 +40,18 @@ class Blah(BaseModel): prop4: Optional[str] = None +class BlockConstraint(BaseModel): + + + foo: Optional[int] = None + bar: Optional[str] = None + +class BlockConstraintForParam(BaseModel): + + + bcfp: Optional[int] = None + bcfp2: Optional[str] = None + class BookOrder(BaseModel): @@ -257,6 +269,16 @@ class Nested2(BaseModel): prop11: Optional[Union[Optional[str], Optional[None]]] = None prop12: Optional[Union[Optional[str], Optional[None]]] = None +class NestedBlockConstraint(BaseModel): + + + nbc: Checked[Optional["BlockConstraint"],Literal["cross_field"]] + +class NestedBlockConstraintForParam(BaseModel): + + + nbcfp: Optional["BlockConstraintForParam"] = None + class OptionalTest_Prop1(BaseModel): diff --git a/integ-tests/python/baml_client/sync_client.py b/integ-tests/python/baml_client/sync_client.py index 27dfb4c13..02a20e227 100644 --- a/integ-tests/python/baml_client/sync_client.py +++ b/integ-tests/python/baml_client/sync_client.py @@ -1266,6 +1266,52 @@ def LiteralUnionsTest( ) return cast(Union[Literal[1], Literal[True], Literal["string output"]], raw.cast_to(types, types)) + def MakeBlockConstraint( + self, + + baml_options: BamlCallOptions = {}, + ) -> Checked[types.BlockConstraint,types.Literal["cross_field"]]: + __tb__ = baml_options.get("tb", None) + if __tb__ is not None: + tb = __tb__._tb + else: + tb = None + __cr__ = baml_options.get("client_registry", None) + + raw = self.__runtime.call_function_sync( + "MakeBlockConstraint", + { + + }, + self.__ctx_manager.get(), + tb, + __cr__, + ) + return cast(Checked[types.BlockConstraint,types.Literal["cross_field"]], raw.cast_to(types, types)) + + def MakeNestedBlockConstraint( + self, + + baml_options: BamlCallOptions = {}, + ) -> types.NestedBlockConstraint: + __tb__ = baml_options.get("tb", None) + if __tb__ is not None: + tb = __tb__._tb + else: + tb = None + __cr__ = baml_options.get("client_registry", None) + + raw = self.__runtime.call_function_sync( + "MakeNestedBlockConstraint", + { + + }, + self.__ctx_manager.get(), + tb, + __cr__, + ) + return cast(types.NestedBlockConstraint, raw.cast_to(types, types)) + def MyFunc( self, input: str, @@ -2485,6 +2531,29 @@ def UnionTest_Function( ) return cast(types.UnionTest_ReturnType, raw.cast_to(types, types)) + def UseBlockConstraint( + self, + inp: types.BlockConstraintForParam, + baml_options: BamlCallOptions = {}, + ) -> int: + __tb__ = baml_options.get("tb", None) + if __tb__ is not None: + tb = __tb__._tb + else: + tb = None + __cr__ = baml_options.get("client_registry", None) + + raw = self.__runtime.call_function_sync( + "UseBlockConstraint", + { + "inp": inp, + }, + self.__ctx_manager.get(), + tb, + __cr__, + ) + return cast(int, raw.cast_to(types, types)) + def UseMalformedConstraints( self, a: types.MalformedConstraints2, @@ -2508,6 +2577,29 @@ def UseMalformedConstraints( ) return cast(int, raw.cast_to(types, types)) + def UseNestedBlockConstraint( + self, + inp: types.NestedBlockConstraintForParam, + baml_options: BamlCallOptions = {}, + ) -> int: + __tb__ = baml_options.get("tb", None) + if __tb__ is not None: + tb = __tb__._tb + else: + tb = None + __cr__ = baml_options.get("client_registry", None) + + raw = self.__runtime.call_function_sync( + "UseNestedBlockConstraint", + { + "inp": inp, + }, + self.__ctx_manager.get(), + tb, + __cr__, + ) + return cast(int, raw.cast_to(types, types)) + @@ -4113,6 +4205,64 @@ def LiteralUnionsTest( self.__ctx_manager.get(), ) + def MakeBlockConstraint( + self, + + baml_options: BamlCallOptions = {}, + ) -> baml_py.BamlSyncStream[Checked[partial_types.BlockConstraint,types.Literal["cross_field"]], Checked[types.BlockConstraint,types.Literal["cross_field"]]]: + __tb__ = baml_options.get("tb", None) + if __tb__ is not None: + tb = __tb__._tb + else: + tb = None + __cr__ = baml_options.get("client_registry", None) + + raw = self.__runtime.stream_function_sync( + "MakeBlockConstraint", + { + }, + None, + self.__ctx_manager.get(), + tb, + __cr__, + ) + + return baml_py.BamlSyncStream[Checked[partial_types.BlockConstraint,types.Literal["cross_field"]], Checked[types.BlockConstraint,types.Literal["cross_field"]]]( + raw, + lambda x: cast(Checked[partial_types.BlockConstraint,types.Literal["cross_field"]], x.cast_to(types, partial_types)), + lambda x: cast(Checked[types.BlockConstraint,types.Literal["cross_field"]], x.cast_to(types, types)), + self.__ctx_manager.get(), + ) + + def MakeNestedBlockConstraint( + self, + + baml_options: BamlCallOptions = {}, + ) -> baml_py.BamlSyncStream[partial_types.NestedBlockConstraint, types.NestedBlockConstraint]: + __tb__ = baml_options.get("tb", None) + if __tb__ is not None: + tb = __tb__._tb + else: + tb = None + __cr__ = baml_options.get("client_registry", None) + + raw = self.__runtime.stream_function_sync( + "MakeNestedBlockConstraint", + { + }, + None, + self.__ctx_manager.get(), + tb, + __cr__, + ) + + return baml_py.BamlSyncStream[partial_types.NestedBlockConstraint, types.NestedBlockConstraint]( + raw, + lambda x: cast(partial_types.NestedBlockConstraint, x.cast_to(types, partial_types)), + lambda x: cast(types.NestedBlockConstraint, x.cast_to(types, types)), + self.__ctx_manager.get(), + ) + def MyFunc( self, input: str, @@ -5703,6 +5853,36 @@ def UnionTest_Function( self.__ctx_manager.get(), ) + def UseBlockConstraint( + self, + inp: types.BlockConstraintForParam, + baml_options: BamlCallOptions = {}, + ) -> baml_py.BamlSyncStream[Optional[int], int]: + __tb__ = baml_options.get("tb", None) + if __tb__ is not None: + tb = __tb__._tb + else: + tb = None + __cr__ = baml_options.get("client_registry", None) + + raw = self.__runtime.stream_function_sync( + "UseBlockConstraint", + { + "inp": inp, + }, + None, + self.__ctx_manager.get(), + tb, + __cr__, + ) + + return baml_py.BamlSyncStream[Optional[int], int]( + raw, + lambda x: cast(Optional[int], x.cast_to(types, partial_types)), + lambda x: cast(int, x.cast_to(types, types)), + self.__ctx_manager.get(), + ) + def UseMalformedConstraints( self, a: types.MalformedConstraints2, @@ -5733,6 +5913,36 @@ def UseMalformedConstraints( self.__ctx_manager.get(), ) + def UseNestedBlockConstraint( + self, + inp: types.NestedBlockConstraintForParam, + baml_options: BamlCallOptions = {}, + ) -> baml_py.BamlSyncStream[Optional[int], int]: + __tb__ = baml_options.get("tb", None) + if __tb__ is not None: + tb = __tb__._tb + else: + tb = None + __cr__ = baml_options.get("client_registry", None) + + raw = self.__runtime.stream_function_sync( + "UseNestedBlockConstraint", + { + "inp": inp, + }, + None, + self.__ctx_manager.get(), + tb, + __cr__, + ) + + return baml_py.BamlSyncStream[Optional[int], int]( + raw, + lambda x: cast(Optional[int], x.cast_to(types, partial_types)), + lambda x: cast(int, x.cast_to(types, types)), + self.__ctx_manager.get(), + ) + b = BamlSyncClient(DO_NOT_USE_DIRECTLY_UNLESS_YOU_KNOW_WHAT_YOURE_DOING_RUNTIME, DO_NOT_USE_DIRECTLY_UNLESS_YOU_KNOW_WHAT_YOURE_DOING_CTX) diff --git a/integ-tests/python/baml_client/type_builder.py b/integ-tests/python/baml_client/type_builder.py index 931cef447..8bdb4777d 100644 --- a/integ-tests/python/baml_client/type_builder.py +++ b/integ-tests/python/baml_client/type_builder.py @@ -19,7 +19,7 @@ class TypeBuilder(_TypeBuilder): def __init__(self): super().__init__(classes=set( - ["BigNumbers","Blah","BookOrder","ClassOptionalOutput","ClassOptionalOutput2","ClassWithImage","CompoundBigNumbers","ContactInfo","CustomTaskResult","DummyOutput","DynInputOutput","DynamicClassOne","DynamicClassTwo","DynamicOutput","Earthling","Education","Email","EmailAddress","Event","FakeImage","FlightConfirmation","FooAny","GroceryReceipt","InnerClass","InnerClass2","InputClass","InputClassNested","LiteralClassHello","LiteralClassOne","LiteralClassTwo","MalformedConstraints","MalformedConstraints2","Martian","NamedArgsSingleClass","Nested","Nested2","OptionalTest_Prop1","OptionalTest_ReturnType","OrderInfo","OriginalA","OriginalB","Person","PhoneNumber","Quantity","RaysData","ReceiptInfo","ReceiptItem","Recipe","Resume","Schema","SearchParams","SomeClassNestedDynamic","StringToClassEntry","TestClassAlias","TestClassNested","TestClassWithEnum","TestOutputClass","TwoStoriesOneTitle","UnionTest_ReturnType","WithReasoning",] + ["BigNumbers","Blah","BlockConstraint","BlockConstraintForParam","BookOrder","ClassOptionalOutput","ClassOptionalOutput2","ClassWithImage","CompoundBigNumbers","ContactInfo","CustomTaskResult","DummyOutput","DynInputOutput","DynamicClassOne","DynamicClassTwo","DynamicOutput","Earthling","Education","Email","EmailAddress","Event","FakeImage","FlightConfirmation","FooAny","GroceryReceipt","InnerClass","InnerClass2","InputClass","InputClassNested","LiteralClassHello","LiteralClassOne","LiteralClassTwo","MalformedConstraints","MalformedConstraints2","Martian","NamedArgsSingleClass","Nested","Nested2","NestedBlockConstraint","NestedBlockConstraintForParam","OptionalTest_Prop1","OptionalTest_ReturnType","OrderInfo","OriginalA","OriginalB","Person","PhoneNumber","Quantity","RaysData","ReceiptInfo","ReceiptItem","Recipe","Resume","Schema","SearchParams","SomeClassNestedDynamic","StringToClassEntry","TestClassAlias","TestClassNested","TestClassWithEnum","TestOutputClass","TwoStoriesOneTitle","UnionTest_ReturnType","WithReasoning",] ), enums=set( ["AliasedEnum","Category","Category2","Category3","Color","DataType","DynEnumOne","DynEnumTwo","EnumInClass","EnumOutput","Hobby","NamedArgsSingleEnum","NamedArgsSingleEnumList","OptionalTest_CategoryType","OrderStatus","Tag","TestEnum",] )) diff --git a/integ-tests/python/baml_client/types.py b/integ-tests/python/baml_client/types.py index 51ea0c49c..d5aaf93b4 100644 --- a/integ-tests/python/baml_client/types.py +++ b/integ-tests/python/baml_client/types.py @@ -155,6 +155,18 @@ class Blah(BaseModel): prop4: Optional[str] = None +class BlockConstraint(BaseModel): + + + foo: int + bar: str + +class BlockConstraintForParam(BaseModel): + + + bcfp: int + bcfp2: str + class BookOrder(BaseModel): @@ -372,6 +384,16 @@ class Nested2(BaseModel): prop11: Union[str, Optional[None]] prop12: Union[str, Optional[None]] +class NestedBlockConstraint(BaseModel): + + + nbc: Checked["BlockConstraint",Literal["cross_field"]] + +class NestedBlockConstraintForParam(BaseModel): + + + nbcfp: "BlockConstraintForParam" + class OptionalTest_Prop1(BaseModel): diff --git a/integ-tests/python/tests/test_functions.py b/integ-tests/python/tests/test_functions.py index 91e18dc47..7726b2175 100644 --- a/integ-tests/python/tests/test_functions.py +++ b/integ-tests/python/tests/test_functions.py @@ -30,7 +30,9 @@ MalformedConstraints2, LiteralClassHello, LiteralClassOne, - all_succeeded + all_succeeded, + BlockConstraintForParam, + NestedBlockConstraintForParam, ) import baml_client.types as types from ..baml_client.tracing import trace, set_tags, flush, on_log_event @@ -1387,3 +1389,26 @@ async def test_failing_assert_can_stream(): with pytest.raises(errors.BamlValidationError): final = await stream.get_final_response() assert "Yoshimi" in final.story_a + +@pytest.mark.asyncio +async def test_block_constraints(): + ret = await b.MakeBlockConstraint() + assert ret.checks["cross_field"].status == "failed" + +@pytest.mark.asyncio +async def test_nested_block_constraints(): + ret = await b.MakeNestedBlockConstraint() + print(ret) + assert ret.nbc.checks["cross_field"].status == "succeeded" + +@pytest.mark.asyncio +async def test_block_constraint_arguments(): + with pytest.raises(errors.BamlInvalidArgumentError) as e: + block_constraint = BlockConstraintForParam(bcfp=1, bcfp2="too long!") + await b.UseBlockConstraint(block_constraint) + assert "Failed assert: hi" in str(e) + + with pytest.raises(errors.BamlInvalidArgumentError) as e: + nested_block_constraint = NestedBlockConstraintForParam(nbcfp=block_constraint) + await b.UseNestedBlockConstraint(nested_block_constraint) + assert "Failed assert: hi" in str(e) diff --git a/integ-tests/ruby/baml_client/client.rb b/integ-tests/ruby/baml_client/client.rb index de87b572f..63edaef31 100644 --- a/integ-tests/ruby/baml_client/client.rb +++ b/integ-tests/ruby/baml_client/client.rb @@ -1746,6 +1746,70 @@ def LiteralUnionsTest( (raw.parsed_using_types(Baml::Types)) end + sig { + params( + varargs: T.untyped, + + baml_options: T::Hash[Symbol, T.any(Baml::TypeBuilder, Baml::ClientRegistry)] + ).returns(Baml::Checked[Baml::Types::BlockConstraint]) + } + def MakeBlockConstraint( + *varargs, + + baml_options: {} + ) + if varargs.any? + + raise ArgumentError.new("MakeBlockConstraint may only be called with keyword arguments") + end + if (baml_options.keys - [:client_registry, :tb]).any? + raise ArgumentError.new("Received unknown keys in baml_options (valid keys: :client_registry, :tb): #{baml_options.keys - [:client_registry, :tb]}") + end + + raw = @runtime.call_function( + "MakeBlockConstraint", + { + + }, + @ctx_manager, + baml_options[:tb]&.instance_variable_get(:@registry), + baml_options[:client_registry], + ) + (raw.parsed_using_types(Baml::Types)) + end + + sig { + params( + varargs: T.untyped, + + baml_options: T::Hash[Symbol, T.any(Baml::TypeBuilder, Baml::ClientRegistry)] + ).returns(Baml::Types::NestedBlockConstraint) + } + def MakeNestedBlockConstraint( + *varargs, + + baml_options: {} + ) + if varargs.any? + + raise ArgumentError.new("MakeNestedBlockConstraint may only be called with keyword arguments") + end + if (baml_options.keys - [:client_registry, :tb]).any? + raise ArgumentError.new("Received unknown keys in baml_options (valid keys: :client_registry, :tb): #{baml_options.keys - [:client_registry, :tb]}") + end + + raw = @runtime.call_function( + "MakeNestedBlockConstraint", + { + + }, + @ctx_manager, + baml_options[:tb]&.instance_variable_get(:@registry), + baml_options[:client_registry], + ) + (raw.parsed_using_types(Baml::Types)) + end + sig { params( varargs: T.untyped, @@ -3442,6 +3506,38 @@ def UnionTest_Function( (raw.parsed_using_types(Baml::Types)) end + sig { + params( + varargs: T.untyped, + inp: Baml::Types::BlockConstraintForParam, + baml_options: T::Hash[Symbol, T.any(Baml::TypeBuilder, Baml::ClientRegistry)] + ).returns(Integer) + } + def UseBlockConstraint( + *varargs, + inp:, + baml_options: {} + ) + if varargs.any? + + raise ArgumentError.new("UseBlockConstraint may only be called with keyword arguments") + end + if (baml_options.keys - [:client_registry, :tb]).any? + raise ArgumentError.new("Received unknown keys in baml_options (valid keys: :client_registry, :tb): #{baml_options.keys - [:client_registry, :tb]}") + end + + raw = @runtime.call_function( + "UseBlockConstraint", + { + inp: inp, + }, + @ctx_manager, + baml_options[:tb]&.instance_variable_get(:@registry), + baml_options[:client_registry], + ) + (raw.parsed_using_types(Baml::Types)) + end + sig { params( varargs: T.untyped, @@ -3474,6 +3570,38 @@ def UseMalformedConstraints( (raw.parsed_using_types(Baml::Types)) end + sig { + params( + varargs: T.untyped, + inp: Baml::Types::NestedBlockConstraintForParam, + baml_options: T::Hash[Symbol, T.any(Baml::TypeBuilder, Baml::ClientRegistry)] + ).returns(Integer) + } + def UseNestedBlockConstraint( + *varargs, + inp:, + baml_options: {} + ) + if varargs.any? + + raise ArgumentError.new("UseNestedBlockConstraint may only be called with keyword arguments") + end + if (baml_options.keys - [:client_registry, :tb]).any? + raise ArgumentError.new("Received unknown keys in baml_options (valid keys: :client_registry, :tb): #{baml_options.keys - [:client_registry, :tb]}") + end + + raw = @runtime.call_function( + "UseNestedBlockConstraint", + { + inp: inp, + }, + @ctx_manager, + baml_options[:tb]&.instance_variable_get(:@registry), + baml_options[:client_registry], + ) + (raw.parsed_using_types(Baml::Types)) + end + end @@ -5342,6 +5470,76 @@ def LiteralUnionsTest( ) end + sig { + params( + varargs: T.untyped, + + baml_options: T::Hash[Symbol, T.any(Baml::TypeBuilder, Baml::ClientRegistry)] + ).returns(Baml::BamlStream[Baml::Checked[Baml::Types::BlockConstraint]]) + } + def MakeBlockConstraint( + *varargs, + + baml_options: {} + ) + if varargs.any? + + raise ArgumentError.new("MakeBlockConstraint may only be called with keyword arguments") + end + if (baml_options.keys - [:client_registry, :tb]).any? + raise ArgumentError.new("Received unknown keys in baml_options (valid keys: :client_registry, :tb): #{baml_options.keys - [:client_registry, :tb]}") + end + + raw = @runtime.stream_function( + "MakeBlockConstraint", + { + + }, + @ctx_manager, + baml_options[:tb]&.instance_variable_get(:@registry), + baml_options[:client_registry], + ) + Baml::BamlStream[Baml::Checked[Baml::PartialTypes::BlockConstraint], Baml::Checked[Baml::Types::BlockConstraint]].new( + ffi_stream: raw, + ctx_manager: @ctx_manager + ) + end + + sig { + params( + varargs: T.untyped, + + baml_options: T::Hash[Symbol, T.any(Baml::TypeBuilder, Baml::ClientRegistry)] + ).returns(Baml::BamlStream[Baml::Types::NestedBlockConstraint]) + } + def MakeNestedBlockConstraint( + *varargs, + + baml_options: {} + ) + if varargs.any? + + raise ArgumentError.new("MakeNestedBlockConstraint may only be called with keyword arguments") + end + if (baml_options.keys - [:client_registry, :tb]).any? + raise ArgumentError.new("Received unknown keys in baml_options (valid keys: :client_registry, :tb): #{baml_options.keys - [:client_registry, :tb]}") + end + + raw = @runtime.stream_function( + "MakeNestedBlockConstraint", + { + + }, + @ctx_manager, + baml_options[:tb]&.instance_variable_get(:@registry), + baml_options[:client_registry], + ) + Baml::BamlStream[Baml::PartialTypes::NestedBlockConstraint, Baml::Types::NestedBlockConstraint].new( + ffi_stream: raw, + ctx_manager: @ctx_manager + ) + end + sig { params( varargs: T.untyped, @@ -7197,6 +7395,41 @@ def UnionTest_Function( ) end + sig { + params( + varargs: T.untyped, + inp: Baml::Types::BlockConstraintForParam, + baml_options: T::Hash[Symbol, T.any(Baml::TypeBuilder, Baml::ClientRegistry)] + ).returns(Baml::BamlStream[Integer]) + } + def UseBlockConstraint( + *varargs, + inp:, + baml_options: {} + ) + if varargs.any? + + raise ArgumentError.new("UseBlockConstraint may only be called with keyword arguments") + end + if (baml_options.keys - [:client_registry, :tb]).any? + raise ArgumentError.new("Received unknown keys in baml_options (valid keys: :client_registry, :tb): #{baml_options.keys - [:client_registry, :tb]}") + end + + raw = @runtime.stream_function( + "UseBlockConstraint", + { + inp: inp, + }, + @ctx_manager, + baml_options[:tb]&.instance_variable_get(:@registry), + baml_options[:client_registry], + ) + Baml::BamlStream[T.nilable(Integer), Integer].new( + ffi_stream: raw, + ctx_manager: @ctx_manager + ) + end + sig { params( varargs: T.untyped, @@ -7232,6 +7465,41 @@ def UseMalformedConstraints( ) end + sig { + params( + varargs: T.untyped, + inp: Baml::Types::NestedBlockConstraintForParam, + baml_options: T::Hash[Symbol, T.any(Baml::TypeBuilder, Baml::ClientRegistry)] + ).returns(Baml::BamlStream[Integer]) + } + def UseNestedBlockConstraint( + *varargs, + inp:, + baml_options: {} + ) + if varargs.any? + + raise ArgumentError.new("UseNestedBlockConstraint may only be called with keyword arguments") + end + if (baml_options.keys - [:client_registry, :tb]).any? + raise ArgumentError.new("Received unknown keys in baml_options (valid keys: :client_registry, :tb): #{baml_options.keys - [:client_registry, :tb]}") + end + + raw = @runtime.stream_function( + "UseNestedBlockConstraint", + { + inp: inp, + }, + @ctx_manager, + baml_options[:tb]&.instance_variable_get(:@registry), + baml_options[:client_registry], + ) + Baml::BamlStream[T.nilable(Integer), Integer].new( + ffi_stream: raw, + ctx_manager: @ctx_manager + ) + end + end end \ No newline at end of file diff --git a/integ-tests/ruby/baml_client/inlined.rb b/integ-tests/ruby/baml_client/inlined.rb index 91b2a9fd0..a5cb50a7f 100644 --- a/integ-tests/ruby/baml_client/inlined.rb +++ b/integ-tests/ruby/baml_client/inlined.rb @@ -30,7 +30,7 @@ module Inlined "test-files/aliases/classes.baml" => "class TestClassAlias {\n key string @alias(\"key-dash\") @description(#\"\n This is a description for key\n af asdf\n \"#)\n key2 string @alias(\"key21\")\n key3 string @alias(\"key with space\")\n key4 string //unaliased\n key5 string @alias(\"key.with.punctuation/123\")\n}\n\nfunction FnTestClassAlias(input: string) -> TestClassAlias {\n client GPT35\n prompt #\"\n {{ctx.output_format}}\n \"#\n}\n\ntest FnTestClassAlias {\n functions [FnTestClassAlias]\n args {\n input \"example input\"\n }\n}\n", "test-files/aliases/enums.baml" => "enum TestEnum {\n A @alias(\"k1\") @description(#\"\n User is angry\n \"#)\n B @alias(\"k22\") @description(#\"\n User is happy\n \"#)\n // tests whether k1 doesnt incorrectly get matched with k11\n C @alias(\"k11\") @description(#\"\n User is sad\n \"#)\n D @alias(\"k44\") @description(\n User is confused\n )\n E @description(\n User is excited\n )\n F @alias(\"k5\") // only alias\n \n G @alias(\"k6\") @description(#\"\n User is bored\n With a long description\n \"#)\n \n @@alias(\"Category\")\n}\n\nfunction FnTestAliasedEnumOutput(input: string) -> TestEnum {\n client GPT35\n prompt #\"\n Classify the user input into the following category\n \n {{ ctx.output_format }}\n\n {{ _.role('user') }}\n {{input}}\n\n {{ _.role('assistant') }}\n Category ID:\n \"#\n}\n\ntest FnTestAliasedEnumOutput {\n functions [FnTestAliasedEnumOutput]\n args {\n input \"mehhhhh\"\n }\n}", "test-files/comments/comments.baml" => "// add some functions, classes, enums etc with comments all over.", - "test-files/constraints/constraints.baml" => "// These classes and functions test several properties of\n// constrains:\n//\n// - The ability for constrains on fields to pass or fail.\n// - The ability for constraints on bare args and return types to pass or fail.\n// - The ability of constraints to influence which variant of a union is chosen\n// by the parser, when the structure is not sufficient to decide.\n\n\nclass Martian {\n age int @check(young_enough, {{ this < 30 }})\n}\n\nclass Earthling {\n age int @check(earth_aged, {{this < 200 and this > 0}}) @check(no_infants, {{this >1}})\n}\n\n\nclass FooAny {\n planetary_age Martian | Earthling\n certainty int @check(unreasonably_certain, {{this == 102931}})\n species string @check(trivial, {{this == \"Homo sapiens\"}}) @check(regex_good, {{this|regex_match(\"Homo\")}}) @check(regex_bad, {{this|regex_match(\"neanderthalensis\")}})\n}\n\n\nfunction PredictAge(name: string) -> FooAny {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling\n and capitalization). I'll give you a hint: If the name\n is \"Greg\", his age is 41.\n\n {{ctx.output_format}}\n \"#\n}\n\n\nfunction PredictAgeBare(inp: string @assert(big_enough, {{this|length > 1}})) -> int @check(too_big, {{this == 10102}}) {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ inp.name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling).\n\n {{ctx.output_format}}\n \"#\n}\n\nfunction ReturnFailingAssert(inp: int @assert(small_int, {{this < 10}})) -> int @assert(big_int, {{this > 100}}) {\n client GPT35\n prompt #\"\n Return the next integer after {{ inp }}.\n\n {{ctx.output_format}}\n \"#\n}\n\nclass TwoStoriesOneTitle {\n title string\n story_a string @assert(too_long_story, {{this|length > 1000000}} )\n story_b string @assert(too_long_story, {{this|length > 1000000}} )\n}\n\nfunction StreamFailingAssertion(theme: string, length: int) -> TwoStoriesOneTitle {\n client GPT35\n prompt #\"\n Tell me two different stories along the theme of {{ theme }} with the same title.\n Please make each about {{ length }} words long.\n {{ctx.output_format}}\n \"#\n}\n", + "test-files/constraints/constraints.baml" => "// These classes and functions test several properties of\n// constrains:\n//\n// - The ability for constrains on fields to pass or fail.\n// - The ability for constraints on bare args and return types to pass or fail.\n// - The ability of constraints to influence which variant of a union is chosen\n// by the parser, when the structure is not sufficient to decide.\n\n\nclass Martian {\n age int @check(young_enough, {{ this < 30 }})\n}\n\nclass Earthling {\n age int @check(earth_aged, {{this < 200 and this > 0}}) @check(no_infants, {{this >1}})\n}\n\n\nclass FooAny {\n planetary_age Martian | Earthling\n certainty int @check(unreasonably_certain, {{this == 102931}})\n species string @check(trivial, {{this == \"Homo sapiens\"}}) @check(regex_good, {{this|regex_match(\"Homo\")}}) @check(regex_bad, {{this|regex_match(\"neanderthalensis\")}})\n}\n\n\nfunction PredictAge(name: string) -> FooAny {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling\n and capitalization). I'll give you a hint: If the name\n is \"Greg\", his age is 41.\n\n {{ctx.output_format}}\n \"#\n}\n\n\nfunction PredictAgeBare(inp: string @assert(big_enough, {{this|length > 1}})) -> int @check(too_big, {{this == 10102}}) {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ inp.name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling).\n\n {{ctx.output_format}}\n \"#\n}\n\nfunction ReturnFailingAssert(inp: int @assert(small_int, {{this < 10}})) -> int @assert(big_int, {{this > 100}}) {\n client GPT35\n prompt #\"\n Return the next integer after {{ inp }}.\n\n {{ctx.output_format}}\n \"#\n}\n\nclass TwoStoriesOneTitle {\n title string\n story_a string @assert(too_long_story, {{this|length > 1000000}} )\n story_b string @assert(too_long_story, {{this|length > 1000000}} )\n}\n\nfunction StreamFailingAssertion(theme: string, length: int) -> TwoStoriesOneTitle {\n client GPT35\n prompt #\"\n Tell me two different stories along the theme of {{ theme }} with the same title.\n Please make each about {{ length }} words long.\n {{ctx.output_format}}\n \"#\n}\n\nclass BlockConstraint {\n foo int\n bar string\n @@check(cross_field, {{ this.bar|length > this.foo }})\n}\n\nfunction MakeBlockConstraint() -> BlockConstraint {\n client GPT35\n prompt #\"\n Generate an output in the following schema with a short string and a large int.\n\n {{ ctx.output_format }}\n \"#\n}\n\nclass NestedBlockConstraint {\n nbc BlockConstraint\n}\n\nclass BlockConstraintForParam {\n bcfp int\n bcfp2 string\n @@assert(hi, {{ this.bcfp2|length < this.bcfp }})\n}\n\nclass NestedBlockConstraintForParam {\n nbcfp BlockConstraintForParam\n}\n\nfunction MakeNestedBlockConstraint() -> NestedBlockConstraint {\n client GPT35\n prompt #\"Generate an output where the inner foo is 1 and the inner bar is \"hello\".\n {{ ctx.output_format }}\n \"#\n}\n\nfunction UseBlockConstraint(inp: BlockConstraintForParam) -> int {\n client GPT35\n prompt #\"\n Generate 3\n {{ ctx.output_format }}\n \"#\n}\n\nfunction UseNestedBlockConstraint(inp: NestedBlockConstraintForParam) -> int {\n client GPT35\n prompt #\"\n Generate 3\n {{ ctx.output_format }}\n \"#\n}\n", "test-files/constraints/contact-info.baml" => "class PhoneNumber {\n value string @assert(valid_phone_number, {{this|regex_match(\"\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}\")}})\n}\n\nclass EmailAddress {\n value string @assert(valid_email, {{this|regex_match(\"^[_]*([a-z0-9]+(\\.|_*)?)+@([a-z][a-z0-9-]+(\\.|-*\\.))+[a-z]{2,6}$\")}})\n}\n\nclass ContactInfo {\n primary PhoneNumber | EmailAddress\n secondary (PhoneNumber | EmailAddress)?\n}\n\nfunction ExtractContactInfo(document: string) -> ContactInfo {\n client GPT35\n prompt #\"\n Extract a primary contact info, and if possible a secondary contact\n info, from this document:\n\n {{ document }}\n\n {{ ctx.output_format }}\n \"#\n}\n", "test-files/constraints/malformed-constraints.baml" => "class MalformedConstraints {\n foo int @check(foo_check, {{ this.length() > 0 }})\n}\n\nclass MalformedConstraints2 {\n foo int @assert(foo_check, {{ this.length() > 0 }})\n}\n\nfunction ReturnMalformedConstraints(a: int) -> MalformedConstraints {\n client GPT35\n prompt #\"\n Return the integer after {{ a }}\n\n {{ ctx.output_format }}\n \"#\n}\n\nfunction UseMalformedConstraints(a: MalformedConstraints2) -> int {\n client GPT35\n prompt #\"\n Return the integer after {{ a.foo }}\n\n {{ ctx.output_format }}\n \"#\n}\n", "test-files/descriptions/descriptions.baml" => "\nclass Nested {\n prop3 string | null @description(#\"\n write \"three\"\n \"#)\n prop4 string | null @description(#\"\n write \"four\"\n \"#) @alias(\"blah\")\n prop20 Nested2\n}\n\nclass Nested2 {\n prop11 string | null @description(#\"\n write \"three\"\n \"#)\n prop12 string | null @description(#\"\n write \"four\"\n \"#) @alias(\"blah\")\n}\n\nclass Schema {\n prop1 string | null @description(#\"\n write \"one\"\n \"#)\n prop2 Nested | string @description(#\"\n write \"two\"\n \"#)\n prop5 (string | null)[] @description(#\"\n write \"hi\"\n \"#)\n prop6 string | Nested[] @alias(\"blah\") @description(#\"\n write the string \"blah\" regardless of the other types here\n \"#)\n nested_attrs (string | null | Nested)[] @description(#\"\n write the string \"nested\" regardless of other types\n \"#)\n parens (string | null) @description(#\"\n write \"parens1\"\n \"#)\n other_group (string | (int | string)) @description(#\"\n write \"other\"\n \"#) @alias(other)\n}\n\n\nfunction SchemaDescriptions(input: string) -> Schema {\n client GPT4o\n prompt #\"\n Return a schema with this format:\n\n {{ctx.output_format}}\n \"#\n}", diff --git a/integ-tests/ruby/baml_client/partial-types.rb b/integ-tests/ruby/baml_client/partial-types.rb index 628f34e92..85e6cc7f7 100644 --- a/integ-tests/ruby/baml_client/partial-types.rb +++ b/integ-tests/ruby/baml_client/partial-types.rb @@ -22,6 +22,8 @@ module Baml module PartialTypes class BigNumbers < T::Struct; end class Blah < T::Struct; end + class BlockConstraint < T::Struct; end + class BlockConstraintForParam < T::Struct; end class BookOrder < T::Struct; end class ClassOptionalOutput < T::Struct; end class ClassOptionalOutput2 < T::Struct; end @@ -56,6 +58,8 @@ class Martian < T::Struct; end class NamedArgsSingleClass < T::Struct; end class Nested < T::Struct; end class Nested2 < T::Struct; end + class NestedBlockConstraint < T::Struct; end + class NestedBlockConstraintForParam < T::Struct; end class OptionalTest_Prop1 < T::Struct; end class OptionalTest_ReturnType < T::Struct; end class OrderInfo < T::Struct; end @@ -106,6 +110,34 @@ def initialize(props) @props = props end end + class BlockConstraint < T::Struct + include Baml::Sorbet::Struct + const :foo, T.nilable(Integer) + const :bar, T.nilable(String) + + def initialize(props) + super( + foo: props[:foo], + bar: props[:bar], + ) + + @props = props + end + end + class BlockConstraintForParam < T::Struct + include Baml::Sorbet::Struct + const :bcfp, T.nilable(Integer) + const :bcfp2, T.nilable(String) + + def initialize(props) + super( + bcfp: props[:bcfp], + bcfp2: props[:bcfp2], + ) + + @props = props + end + end class BookOrder < T::Struct include Baml::Sorbet::Struct const :orderId, T.nilable(String) @@ -598,6 +630,30 @@ def initialize(props) @props = props end end + class NestedBlockConstraint < T::Struct + include Baml::Sorbet::Struct + const :nbc, Baml::Checked[Baml::PartialTypes::BlockConstraint] + + def initialize(props) + super( + nbc: props[:nbc], + ) + + @props = props + end + end + class NestedBlockConstraintForParam < T::Struct + include Baml::Sorbet::Struct + const :nbcfp, Baml::PartialTypes::BlockConstraintForParam + + def initialize(props) + super( + nbcfp: props[:nbcfp], + ) + + @props = props + end + end class OptionalTest_Prop1 < T::Struct include Baml::Sorbet::Struct const :omega_a, T.nilable(String) diff --git a/integ-tests/ruby/baml_client/type-registry.rb b/integ-tests/ruby/baml_client/type-registry.rb index 96d4bf14f..bd9672676 100644 --- a/integ-tests/ruby/baml_client/type-registry.rb +++ b/integ-tests/ruby/baml_client/type-registry.rb @@ -18,7 +18,7 @@ module Baml class TypeBuilder def initialize @registry = Baml::Ffi::TypeBuilder.new - @classes = Set[ "BigNumbers", "Blah", "BookOrder", "ClassOptionalOutput", "ClassOptionalOutput2", "ClassWithImage", "CompoundBigNumbers", "ContactInfo", "CustomTaskResult", "DummyOutput", "DynInputOutput", "DynamicClassOne", "DynamicClassTwo", "DynamicOutput", "Earthling", "Education", "Email", "EmailAddress", "Event", "FakeImage", "FlightConfirmation", "FooAny", "GroceryReceipt", "InnerClass", "InnerClass2", "InputClass", "InputClassNested", "LiteralClassHello", "LiteralClassOne", "LiteralClassTwo", "MalformedConstraints", "MalformedConstraints2", "Martian", "NamedArgsSingleClass", "Nested", "Nested2", "OptionalTest_Prop1", "OptionalTest_ReturnType", "OrderInfo", "OriginalA", "OriginalB", "Person", "PhoneNumber", "Quantity", "RaysData", "ReceiptInfo", "ReceiptItem", "Recipe", "Resume", "Schema", "SearchParams", "SomeClassNestedDynamic", "StringToClassEntry", "TestClassAlias", "TestClassNested", "TestClassWithEnum", "TestOutputClass", "TwoStoriesOneTitle", "UnionTest_ReturnType", "WithReasoning", ] + @classes = Set[ "BigNumbers", "Blah", "BlockConstraint", "BlockConstraintForParam", "BookOrder", "ClassOptionalOutput", "ClassOptionalOutput2", "ClassWithImage", "CompoundBigNumbers", "ContactInfo", "CustomTaskResult", "DummyOutput", "DynInputOutput", "DynamicClassOne", "DynamicClassTwo", "DynamicOutput", "Earthling", "Education", "Email", "EmailAddress", "Event", "FakeImage", "FlightConfirmation", "FooAny", "GroceryReceipt", "InnerClass", "InnerClass2", "InputClass", "InputClassNested", "LiteralClassHello", "LiteralClassOne", "LiteralClassTwo", "MalformedConstraints", "MalformedConstraints2", "Martian", "NamedArgsSingleClass", "Nested", "Nested2", "NestedBlockConstraint", "NestedBlockConstraintForParam", "OptionalTest_Prop1", "OptionalTest_ReturnType", "OrderInfo", "OriginalA", "OriginalB", "Person", "PhoneNumber", "Quantity", "RaysData", "ReceiptInfo", "ReceiptItem", "Recipe", "Resume", "Schema", "SearchParams", "SomeClassNestedDynamic", "StringToClassEntry", "TestClassAlias", "TestClassNested", "TestClassWithEnum", "TestOutputClass", "TwoStoriesOneTitle", "UnionTest_ReturnType", "WithReasoning", ] @enums = Set[ "AliasedEnum", "Category", "Category2", "Category3", "Color", "DataType", "DynEnumOne", "DynEnumTwo", "EnumInClass", "EnumOutput", "Hobby", "NamedArgsSingleEnum", "NamedArgsSingleEnumList", "OptionalTest_CategoryType", "OrderStatus", "Tag", "TestEnum", ] end diff --git a/integ-tests/ruby/baml_client/types.rb b/integ-tests/ruby/baml_client/types.rb index 84d7e33cc..fef84427b 100644 --- a/integ-tests/ruby/baml_client/types.rb +++ b/integ-tests/ruby/baml_client/types.rb @@ -138,6 +138,8 @@ class TestEnum < T::Enum end class BigNumbers < T::Struct; end class Blah < T::Struct; end + class BlockConstraint < T::Struct; end + class BlockConstraintForParam < T::Struct; end class BookOrder < T::Struct; end class ClassOptionalOutput < T::Struct; end class ClassOptionalOutput2 < T::Struct; end @@ -172,6 +174,8 @@ class Martian < T::Struct; end class NamedArgsSingleClass < T::Struct; end class Nested < T::Struct; end class Nested2 < T::Struct; end + class NestedBlockConstraint < T::Struct; end + class NestedBlockConstraintForParam < T::Struct; end class OptionalTest_Prop1 < T::Struct; end class OptionalTest_ReturnType < T::Struct; end class OrderInfo < T::Struct; end @@ -222,6 +226,34 @@ def initialize(props) @props = props end end + class BlockConstraint < T::Struct + include Baml::Sorbet::Struct + const :foo, Integer + const :bar, String + + def initialize(props) + super( + foo: props[:foo], + bar: props[:bar], + ) + + @props = props + end + end + class BlockConstraintForParam < T::Struct + include Baml::Sorbet::Struct + const :bcfp, Integer + const :bcfp2, String + + def initialize(props) + super( + bcfp: props[:bcfp], + bcfp2: props[:bcfp2], + ) + + @props = props + end + end class BookOrder < T::Struct include Baml::Sorbet::Struct const :orderId, String @@ -714,6 +746,30 @@ def initialize(props) @props = props end end + class NestedBlockConstraint < T::Struct + include Baml::Sorbet::Struct + const :nbc, Baml::Checked[Baml::Types::BlockConstraint] + + def initialize(props) + super( + nbc: props[:nbc], + ) + + @props = props + end + end + class NestedBlockConstraintForParam < T::Struct + include Baml::Sorbet::Struct + const :nbcfp, Baml::Types::BlockConstraintForParam + + def initialize(props) + super( + nbcfp: props[:nbcfp], + ) + + @props = props + end + end class OptionalTest_Prop1 < T::Struct include Baml::Sorbet::Struct const :omega_a, String diff --git a/integ-tests/ruby/test_functions.rb b/integ-tests/ruby/test_functions.rb index e4ea441c5..9c3f39e42 100644 --- a/integ-tests/ruby/test_functions.rb +++ b/integ-tests/ruby/test_functions.rb @@ -190,7 +190,6 @@ stream = b.stream.FnOutputClassNested(input: "a") msgs = [] stream.each do |msg| - puts msg msgs << msg end final = stream.get_final_response @@ -319,4 +318,28 @@ res = b.PredictAge(name: "Greg") assert_equal res["certainty"].checks[:unreasonably_certain].status, "failed" end + + it "uses block_level constraints" do + res = b.MakeBlockConstraint() + assert_equal res.checks[:cross_field].status, "failed" + end + + it "uses nested_block_level constraints" do + res = b.MakeNestedBlockConstraint() + assert_equal res["nbc"].checks[:cross_field].status, "succeeded" + end + + it "uses block_level constraints in function parameters" do + block_constraint = Baml::Types::BlockConstraintForParam.new(bcfp: 1, bcfp2: "too long!") + assert_raises Exception do + res = b.UseBlockConstraint(inp: block_constraint) + end + nested_block_constraint = Baml::Types::NestedBlockConstraintForParam.new( + nbcfp: block_constraint + ) + assert_raises Exception do + res = b.UseNestedBlockConstraint(inp: nested_block_constraint) + end + end + end diff --git a/integ-tests/typescript/baml_client/async_client.ts b/integ-tests/typescript/baml_client/async_client.ts index 29c8fa051..e793c6ea8 100644 --- a/integ-tests/typescript/baml_client/async_client.ts +++ b/integ-tests/typescript/baml_client/async_client.ts @@ -16,7 +16,7 @@ $ pnpm add @boundaryml/baml // @ts-nocheck // biome-ignore format: autogenerated code import { BamlRuntime, FunctionResult, BamlCtxManager, BamlStream, Image, ClientRegistry, BamlValidationError, createBamlValidationError } from "@boundaryml/baml" -import {BigNumbers, Blah, BookOrder, ClassOptionalOutput, ClassOptionalOutput2, ClassWithImage, CompoundBigNumbers, ContactInfo, CustomTaskResult, DummyOutput, DynInputOutput, DynamicClassOne, DynamicClassTwo, DynamicOutput, Earthling, Education, Email, EmailAddress, Event, FakeImage, FlightConfirmation, FooAny, GroceryReceipt, InnerClass, InnerClass2, InputClass, InputClassNested, LiteralClassHello, LiteralClassOne, LiteralClassTwo, MalformedConstraints, MalformedConstraints2, Martian, NamedArgsSingleClass, Nested, Nested2, OptionalTest_Prop1, OptionalTest_ReturnType, OrderInfo, OriginalA, OriginalB, Person, PhoneNumber, Quantity, RaysData, ReceiptInfo, ReceiptItem, Recipe, Resume, Schema, SearchParams, SomeClassNestedDynamic, StringToClassEntry, TestClassAlias, TestClassNested, TestClassWithEnum, TestOutputClass, TwoStoriesOneTitle, UnionTest_ReturnType, WithReasoning, AliasedEnum, Category, Category2, Category3, Color, DataType, DynEnumOne, DynEnumTwo, EnumInClass, EnumOutput, Hobby, NamedArgsSingleEnum, NamedArgsSingleEnumList, OptionalTest_CategoryType, OrderStatus, Tag, TestEnum} from "./types" +import {BigNumbers, Blah, BlockConstraint, BlockConstraintForParam, BookOrder, ClassOptionalOutput, ClassOptionalOutput2, ClassWithImage, CompoundBigNumbers, ContactInfo, CustomTaskResult, DummyOutput, DynInputOutput, DynamicClassOne, DynamicClassTwo, DynamicOutput, Earthling, Education, Email, EmailAddress, Event, FakeImage, FlightConfirmation, FooAny, GroceryReceipt, InnerClass, InnerClass2, InputClass, InputClassNested, LiteralClassHello, LiteralClassOne, LiteralClassTwo, MalformedConstraints, MalformedConstraints2, Martian, NamedArgsSingleClass, Nested, Nested2, NestedBlockConstraint, NestedBlockConstraintForParam, OptionalTest_Prop1, OptionalTest_ReturnType, OrderInfo, OriginalA, OriginalB, Person, PhoneNumber, Quantity, RaysData, ReceiptInfo, ReceiptItem, Recipe, Resume, Schema, SearchParams, SomeClassNestedDynamic, StringToClassEntry, TestClassAlias, TestClassNested, TestClassWithEnum, TestOutputClass, TwoStoriesOneTitle, UnionTest_ReturnType, WithReasoning, AliasedEnum, Category, Category2, Category3, Color, DataType, DynEnumOne, DynEnumTwo, EnumInClass, EnumOutput, Hobby, NamedArgsSingleEnum, NamedArgsSingleEnumList, OptionalTest_CategoryType, OrderStatus, Tag, TestEnum} from "./types" import TypeBuilder from "./type_builder" import { DO_NOT_USE_DIRECTLY_UNLESS_YOU_KNOW_WHAT_YOURE_DOING_CTX, DO_NOT_USE_DIRECTLY_UNLESS_YOU_KNOW_WHAT_YOURE_DOING_RUNTIME } from "./globals" @@ -1367,6 +1367,56 @@ export class BamlAsyncClient { } } + async MakeBlockConstraint( + + __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry } + ): Promise> { + try { + const raw = await this.runtime.callFunction( + "MakeBlockConstraint", + { + + }, + this.ctx_manager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + ) + return raw.parsed() as Checked + } catch (error: any) { + const bamlError = createBamlValidationError(error); + if (bamlError instanceof BamlValidationError) { + throw bamlError; + } else { + throw error; + } + } + } + + async MakeNestedBlockConstraint( + + __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry } + ): Promise { + try { + const raw = await this.runtime.callFunction( + "MakeNestedBlockConstraint", + { + + }, + this.ctx_manager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + ) + return raw.parsed() as NestedBlockConstraint + } catch (error: any) { + const bamlError = createBamlValidationError(error); + if (bamlError instanceof BamlValidationError) { + throw bamlError; + } else { + throw error; + } + } + } + async MyFunc( input: string, __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry } @@ -2692,6 +2742,31 @@ export class BamlAsyncClient { } } + async UseBlockConstraint( + inp: BlockConstraintForParam, + __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry } + ): Promise { + try { + const raw = await this.runtime.callFunction( + "UseBlockConstraint", + { + "inp": inp + }, + this.ctx_manager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + ) + return raw.parsed() as number + } catch (error: any) { + const bamlError = createBamlValidationError(error); + if (bamlError instanceof BamlValidationError) { + throw bamlError; + } else { + throw error; + } + } + } + async UseMalformedConstraints( a: MalformedConstraints2, __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry } @@ -2717,6 +2792,31 @@ export class BamlAsyncClient { } } + async UseNestedBlockConstraint( + inp: NestedBlockConstraintForParam, + __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry } + ): Promise { + try { + const raw = await this.runtime.callFunction( + "UseNestedBlockConstraint", + { + "inp": inp + }, + this.ctx_manager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + ) + return raw.parsed() as number + } catch (error: any) { + const bamlError = createBamlValidationError(error); + if (bamlError instanceof BamlValidationError) { + throw bamlError; + } else { + throw error; + } + } + } + } class BamlStreamClient { @@ -4472,6 +4572,72 @@ class BamlStreamClient { } } + MakeBlockConstraint( + + __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry } + ): BamlStream>, Checked> { + try { + const raw = this.runtime.streamFunction( + "MakeBlockConstraint", + { + + }, + undefined, + this.ctx_manager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + ) + return new BamlStream>, Checked>( + raw, + (a): a is RecursivePartialNull> => a, + (a): a is Checked => a, + this.ctx_manager.cloneContext(), + __baml_options__?.tb?.__tb(), + ) + } catch (error) { + if (error instanceof Error) { + const bamlError = createBamlValidationError(error); + if (bamlError instanceof BamlValidationError) { + throw bamlError; + } + } + throw error; + } + } + + MakeNestedBlockConstraint( + + __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry } + ): BamlStream, NestedBlockConstraint> { + try { + const raw = this.runtime.streamFunction( + "MakeNestedBlockConstraint", + { + + }, + undefined, + this.ctx_manager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + ) + return new BamlStream, NestedBlockConstraint>( + raw, + (a): a is RecursivePartialNull => a, + (a): a is NestedBlockConstraint => a, + this.ctx_manager.cloneContext(), + __baml_options__?.tb?.__tb(), + ) + } catch (error) { + if (error instanceof Error) { + const bamlError = createBamlValidationError(error); + if (bamlError instanceof BamlValidationError) { + throw bamlError; + } + } + throw error; + } + } + MyFunc( input: string, __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry } @@ -6221,6 +6387,39 @@ class BamlStreamClient { } } + UseBlockConstraint( + inp: BlockConstraintForParam, + __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry } + ): BamlStream, number> { + try { + const raw = this.runtime.streamFunction( + "UseBlockConstraint", + { + "inp": inp + }, + undefined, + this.ctx_manager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + ) + return new BamlStream, number>( + raw, + (a): a is RecursivePartialNull => a, + (a): a is number => a, + this.ctx_manager.cloneContext(), + __baml_options__?.tb?.__tb(), + ) + } catch (error) { + if (error instanceof Error) { + const bamlError = createBamlValidationError(error); + if (bamlError instanceof BamlValidationError) { + throw bamlError; + } + } + throw error; + } + } + UseMalformedConstraints( a: MalformedConstraints2, __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry } @@ -6254,6 +6453,39 @@ class BamlStreamClient { } } + UseNestedBlockConstraint( + inp: NestedBlockConstraintForParam, + __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry } + ): BamlStream, number> { + try { + const raw = this.runtime.streamFunction( + "UseNestedBlockConstraint", + { + "inp": inp + }, + undefined, + this.ctx_manager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + ) + return new BamlStream, number>( + raw, + (a): a is RecursivePartialNull => a, + (a): a is number => a, + this.ctx_manager.cloneContext(), + __baml_options__?.tb?.__tb(), + ) + } catch (error) { + if (error instanceof Error) { + const bamlError = createBamlValidationError(error); + if (bamlError instanceof BamlValidationError) { + throw bamlError; + } + } + throw error; + } + } + } export const b = new BamlAsyncClient(DO_NOT_USE_DIRECTLY_UNLESS_YOU_KNOW_WHAT_YOURE_DOING_RUNTIME, DO_NOT_USE_DIRECTLY_UNLESS_YOU_KNOW_WHAT_YOURE_DOING_CTX) \ No newline at end of file diff --git a/integ-tests/typescript/baml_client/inlinedbaml.ts b/integ-tests/typescript/baml_client/inlinedbaml.ts index b5b075e3d..648f85bf0 100644 --- a/integ-tests/typescript/baml_client/inlinedbaml.ts +++ b/integ-tests/typescript/baml_client/inlinedbaml.ts @@ -31,7 +31,7 @@ const fileMap = { "test-files/aliases/classes.baml": "class TestClassAlias {\n key string @alias(\"key-dash\") @description(#\"\n This is a description for key\n af asdf\n \"#)\n key2 string @alias(\"key21\")\n key3 string @alias(\"key with space\")\n key4 string //unaliased\n key5 string @alias(\"key.with.punctuation/123\")\n}\n\nfunction FnTestClassAlias(input: string) -> TestClassAlias {\n client GPT35\n prompt #\"\n {{ctx.output_format}}\n \"#\n}\n\ntest FnTestClassAlias {\n functions [FnTestClassAlias]\n args {\n input \"example input\"\n }\n}\n", "test-files/aliases/enums.baml": "enum TestEnum {\n A @alias(\"k1\") @description(#\"\n User is angry\n \"#)\n B @alias(\"k22\") @description(#\"\n User is happy\n \"#)\n // tests whether k1 doesnt incorrectly get matched with k11\n C @alias(\"k11\") @description(#\"\n User is sad\n \"#)\n D @alias(\"k44\") @description(\n User is confused\n )\n E @description(\n User is excited\n )\n F @alias(\"k5\") // only alias\n \n G @alias(\"k6\") @description(#\"\n User is bored\n With a long description\n \"#)\n \n @@alias(\"Category\")\n}\n\nfunction FnTestAliasedEnumOutput(input: string) -> TestEnum {\n client GPT35\n prompt #\"\n Classify the user input into the following category\n \n {{ ctx.output_format }}\n\n {{ _.role('user') }}\n {{input}}\n\n {{ _.role('assistant') }}\n Category ID:\n \"#\n}\n\ntest FnTestAliasedEnumOutput {\n functions [FnTestAliasedEnumOutput]\n args {\n input \"mehhhhh\"\n }\n}", "test-files/comments/comments.baml": "// add some functions, classes, enums etc with comments all over.", - "test-files/constraints/constraints.baml": "// These classes and functions test several properties of\n// constrains:\n//\n// - The ability for constrains on fields to pass or fail.\n// - The ability for constraints on bare args and return types to pass or fail.\n// - The ability of constraints to influence which variant of a union is chosen\n// by the parser, when the structure is not sufficient to decide.\n\n\nclass Martian {\n age int @check(young_enough, {{ this < 30 }})\n}\n\nclass Earthling {\n age int @check(earth_aged, {{this < 200 and this > 0}}) @check(no_infants, {{this >1}})\n}\n\n\nclass FooAny {\n planetary_age Martian | Earthling\n certainty int @check(unreasonably_certain, {{this == 102931}})\n species string @check(trivial, {{this == \"Homo sapiens\"}}) @check(regex_good, {{this|regex_match(\"Homo\")}}) @check(regex_bad, {{this|regex_match(\"neanderthalensis\")}})\n}\n\n\nfunction PredictAge(name: string) -> FooAny {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling\n and capitalization). I'll give you a hint: If the name\n is \"Greg\", his age is 41.\n\n {{ctx.output_format}}\n \"#\n}\n\n\nfunction PredictAgeBare(inp: string @assert(big_enough, {{this|length > 1}})) -> int @check(too_big, {{this == 10102}}) {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ inp.name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling).\n\n {{ctx.output_format}}\n \"#\n}\n\nfunction ReturnFailingAssert(inp: int @assert(small_int, {{this < 10}})) -> int @assert(big_int, {{this > 100}}) {\n client GPT35\n prompt #\"\n Return the next integer after {{ inp }}.\n\n {{ctx.output_format}}\n \"#\n}\n\nclass TwoStoriesOneTitle {\n title string\n story_a string @assert(too_long_story, {{this|length > 1000000}} )\n story_b string @assert(too_long_story, {{this|length > 1000000}} )\n}\n\nfunction StreamFailingAssertion(theme: string, length: int) -> TwoStoriesOneTitle {\n client GPT35\n prompt #\"\n Tell me two different stories along the theme of {{ theme }} with the same title.\n Please make each about {{ length }} words long.\n {{ctx.output_format}}\n \"#\n}\n", + "test-files/constraints/constraints.baml": "// These classes and functions test several properties of\n// constrains:\n//\n// - The ability for constrains on fields to pass or fail.\n// - The ability for constraints on bare args and return types to pass or fail.\n// - The ability of constraints to influence which variant of a union is chosen\n// by the parser, when the structure is not sufficient to decide.\n\n\nclass Martian {\n age int @check(young_enough, {{ this < 30 }})\n}\n\nclass Earthling {\n age int @check(earth_aged, {{this < 200 and this > 0}}) @check(no_infants, {{this >1}})\n}\n\n\nclass FooAny {\n planetary_age Martian | Earthling\n certainty int @check(unreasonably_certain, {{this == 102931}})\n species string @check(trivial, {{this == \"Homo sapiens\"}}) @check(regex_good, {{this|regex_match(\"Homo\")}}) @check(regex_bad, {{this|regex_match(\"neanderthalensis\")}})\n}\n\n\nfunction PredictAge(name: string) -> FooAny {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling\n and capitalization). I'll give you a hint: If the name\n is \"Greg\", his age is 41.\n\n {{ctx.output_format}}\n \"#\n}\n\n\nfunction PredictAgeBare(inp: string @assert(big_enough, {{this|length > 1}})) -> int @check(too_big, {{this == 10102}}) {\n client GPT35\n prompt #\"\n Using your understanding of the historical popularity\n of names, predict the age of a person with the name\n {{ inp.name }} in years. Also predict their genus and\n species. It's Homo sapiens (with exactly that spelling).\n\n {{ctx.output_format}}\n \"#\n}\n\nfunction ReturnFailingAssert(inp: int @assert(small_int, {{this < 10}})) -> int @assert(big_int, {{this > 100}}) {\n client GPT35\n prompt #\"\n Return the next integer after {{ inp }}.\n\n {{ctx.output_format}}\n \"#\n}\n\nclass TwoStoriesOneTitle {\n title string\n story_a string @assert(too_long_story, {{this|length > 1000000}} )\n story_b string @assert(too_long_story, {{this|length > 1000000}} )\n}\n\nfunction StreamFailingAssertion(theme: string, length: int) -> TwoStoriesOneTitle {\n client GPT35\n prompt #\"\n Tell me two different stories along the theme of {{ theme }} with the same title.\n Please make each about {{ length }} words long.\n {{ctx.output_format}}\n \"#\n}\n\nclass BlockConstraint {\n foo int\n bar string\n @@check(cross_field, {{ this.bar|length > this.foo }})\n}\n\nfunction MakeBlockConstraint() -> BlockConstraint {\n client GPT35\n prompt #\"\n Generate an output in the following schema with a short string and a large int.\n\n {{ ctx.output_format }}\n \"#\n}\n\nclass NestedBlockConstraint {\n nbc BlockConstraint\n}\n\nclass BlockConstraintForParam {\n bcfp int\n bcfp2 string\n @@assert(hi, {{ this.bcfp2|length < this.bcfp }})\n}\n\nclass NestedBlockConstraintForParam {\n nbcfp BlockConstraintForParam\n}\n\nfunction MakeNestedBlockConstraint() -> NestedBlockConstraint {\n client GPT35\n prompt #\"Generate an output where the inner foo is 1 and the inner bar is \"hello\".\n {{ ctx.output_format }}\n \"#\n}\n\nfunction UseBlockConstraint(inp: BlockConstraintForParam) -> int {\n client GPT35\n prompt #\"\n Generate 3\n {{ ctx.output_format }}\n \"#\n}\n\nfunction UseNestedBlockConstraint(inp: NestedBlockConstraintForParam) -> int {\n client GPT35\n prompt #\"\n Generate 3\n {{ ctx.output_format }}\n \"#\n}\n", "test-files/constraints/contact-info.baml": "class PhoneNumber {\n value string @assert(valid_phone_number, {{this|regex_match(\"\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}\")}})\n}\n\nclass EmailAddress {\n value string @assert(valid_email, {{this|regex_match(\"^[_]*([a-z0-9]+(\\.|_*)?)+@([a-z][a-z0-9-]+(\\.|-*\\.))+[a-z]{2,6}$\")}})\n}\n\nclass ContactInfo {\n primary PhoneNumber | EmailAddress\n secondary (PhoneNumber | EmailAddress)?\n}\n\nfunction ExtractContactInfo(document: string) -> ContactInfo {\n client GPT35\n prompt #\"\n Extract a primary contact info, and if possible a secondary contact\n info, from this document:\n\n {{ document }}\n\n {{ ctx.output_format }}\n \"#\n}\n", "test-files/constraints/malformed-constraints.baml": "class MalformedConstraints {\n foo int @check(foo_check, {{ this.length() > 0 }})\n}\n\nclass MalformedConstraints2 {\n foo int @assert(foo_check, {{ this.length() > 0 }})\n}\n\nfunction ReturnMalformedConstraints(a: int) -> MalformedConstraints {\n client GPT35\n prompt #\"\n Return the integer after {{ a }}\n\n {{ ctx.output_format }}\n \"#\n}\n\nfunction UseMalformedConstraints(a: MalformedConstraints2) -> int {\n client GPT35\n prompt #\"\n Return the integer after {{ a.foo }}\n\n {{ ctx.output_format }}\n \"#\n}\n", "test-files/descriptions/descriptions.baml": "\nclass Nested {\n prop3 string | null @description(#\"\n write \"three\"\n \"#)\n prop4 string | null @description(#\"\n write \"four\"\n \"#) @alias(\"blah\")\n prop20 Nested2\n}\n\nclass Nested2 {\n prop11 string | null @description(#\"\n write \"three\"\n \"#)\n prop12 string | null @description(#\"\n write \"four\"\n \"#) @alias(\"blah\")\n}\n\nclass Schema {\n prop1 string | null @description(#\"\n write \"one\"\n \"#)\n prop2 Nested | string @description(#\"\n write \"two\"\n \"#)\n prop5 (string | null)[] @description(#\"\n write \"hi\"\n \"#)\n prop6 string | Nested[] @alias(\"blah\") @description(#\"\n write the string \"blah\" regardless of the other types here\n \"#)\n nested_attrs (string | null | Nested)[] @description(#\"\n write the string \"nested\" regardless of other types\n \"#)\n parens (string | null) @description(#\"\n write \"parens1\"\n \"#)\n other_group (string | (int | string)) @description(#\"\n write \"other\"\n \"#) @alias(other)\n}\n\n\nfunction SchemaDescriptions(input: string) -> Schema {\n client GPT4o\n prompt #\"\n Return a schema with this format:\n\n {{ctx.output_format}}\n \"#\n}", diff --git a/integ-tests/typescript/baml_client/sync_client.ts b/integ-tests/typescript/baml_client/sync_client.ts index 5f1a1ddd4..d3220200c 100644 --- a/integ-tests/typescript/baml_client/sync_client.ts +++ b/integ-tests/typescript/baml_client/sync_client.ts @@ -16,7 +16,7 @@ $ pnpm add @boundaryml/baml // @ts-nocheck // biome-ignore format: autogenerated code import { BamlRuntime, FunctionResult, BamlCtxManager, BamlSyncStream, Image, ClientRegistry } from "@boundaryml/baml" -import {BigNumbers, Blah, BookOrder, ClassOptionalOutput, ClassOptionalOutput2, ClassWithImage, CompoundBigNumbers, ContactInfo, CustomTaskResult, DummyOutput, DynInputOutput, DynamicClassOne, DynamicClassTwo, DynamicOutput, Earthling, Education, Email, EmailAddress, Event, FakeImage, FlightConfirmation, FooAny, GroceryReceipt, InnerClass, InnerClass2, InputClass, InputClassNested, LiteralClassHello, LiteralClassOne, LiteralClassTwo, MalformedConstraints, MalformedConstraints2, Martian, NamedArgsSingleClass, Nested, Nested2, OptionalTest_Prop1, OptionalTest_ReturnType, OrderInfo, OriginalA, OriginalB, Person, PhoneNumber, Quantity, RaysData, ReceiptInfo, ReceiptItem, Recipe, Resume, Schema, SearchParams, SomeClassNestedDynamic, StringToClassEntry, TestClassAlias, TestClassNested, TestClassWithEnum, TestOutputClass, TwoStoriesOneTitle, UnionTest_ReturnType, WithReasoning, AliasedEnum, Category, Category2, Category3, Color, DataType, DynEnumOne, DynEnumTwo, EnumInClass, EnumOutput, Hobby, NamedArgsSingleEnum, NamedArgsSingleEnumList, OptionalTest_CategoryType, OrderStatus, Tag, TestEnum} from "./types" +import {BigNumbers, Blah, BlockConstraint, BlockConstraintForParam, BookOrder, ClassOptionalOutput, ClassOptionalOutput2, ClassWithImage, CompoundBigNumbers, ContactInfo, CustomTaskResult, DummyOutput, DynInputOutput, DynamicClassOne, DynamicClassTwo, DynamicOutput, Earthling, Education, Email, EmailAddress, Event, FakeImage, FlightConfirmation, FooAny, GroceryReceipt, InnerClass, InnerClass2, InputClass, InputClassNested, LiteralClassHello, LiteralClassOne, LiteralClassTwo, MalformedConstraints, MalformedConstraints2, Martian, NamedArgsSingleClass, Nested, Nested2, NestedBlockConstraint, NestedBlockConstraintForParam, OptionalTest_Prop1, OptionalTest_ReturnType, OrderInfo, OriginalA, OriginalB, Person, PhoneNumber, Quantity, RaysData, ReceiptInfo, ReceiptItem, Recipe, Resume, Schema, SearchParams, SomeClassNestedDynamic, StringToClassEntry, TestClassAlias, TestClassNested, TestClassWithEnum, TestOutputClass, TwoStoriesOneTitle, UnionTest_ReturnType, WithReasoning, AliasedEnum, Category, Category2, Category3, Color, DataType, DynEnumOne, DynEnumTwo, EnumInClass, EnumOutput, Hobby, NamedArgsSingleEnum, NamedArgsSingleEnumList, OptionalTest_CategoryType, OrderStatus, Tag, TestEnum} from "./types" import TypeBuilder from "./type_builder" import { DO_NOT_USE_DIRECTLY_UNLESS_YOU_KNOW_WHAT_YOURE_DOING_CTX, DO_NOT_USE_DIRECTLY_UNLESS_YOU_KNOW_WHAT_YOURE_DOING_RUNTIME } from "./globals" @@ -1367,6 +1367,56 @@ export class BamlSyncClient { } } + MakeBlockConstraint( + + __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry } + ): Checked { + try { + const raw = this.runtime.callFunctionSync( + "MakeBlockConstraint", + { + + }, + this.ctx_manager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + ) + return raw.parsed() as Checked + } catch (error: any) { + const bamlError = createBamlValidationError(error); + if (bamlError instanceof BamlValidationError) { + throw bamlError; + } else { + throw error; + } + } + } + + MakeNestedBlockConstraint( + + __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry } + ): NestedBlockConstraint { + try { + const raw = this.runtime.callFunctionSync( + "MakeNestedBlockConstraint", + { + + }, + this.ctx_manager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + ) + return raw.parsed() as NestedBlockConstraint + } catch (error: any) { + const bamlError = createBamlValidationError(error); + if (bamlError instanceof BamlValidationError) { + throw bamlError; + } else { + throw error; + } + } + } + MyFunc( input: string, __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry } @@ -2692,6 +2742,31 @@ export class BamlSyncClient { } } + UseBlockConstraint( + inp: BlockConstraintForParam, + __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry } + ): number { + try { + const raw = this.runtime.callFunctionSync( + "UseBlockConstraint", + { + "inp": inp + }, + this.ctx_manager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + ) + return raw.parsed() as number + } catch (error: any) { + const bamlError = createBamlValidationError(error); + if (bamlError instanceof BamlValidationError) { + throw bamlError; + } else { + throw error; + } + } + } + UseMalformedConstraints( a: MalformedConstraints2, __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry } @@ -2717,6 +2792,31 @@ export class BamlSyncClient { } } + UseNestedBlockConstraint( + inp: NestedBlockConstraintForParam, + __baml_options__?: { tb?: TypeBuilder, clientRegistry?: ClientRegistry } + ): number { + try { + const raw = this.runtime.callFunctionSync( + "UseNestedBlockConstraint", + { + "inp": inp + }, + this.ctx_manager.cloneContext(), + __baml_options__?.tb?.__tb(), + __baml_options__?.clientRegistry, + ) + return raw.parsed() as number + } catch (error: any) { + const bamlError = createBamlValidationError(error); + if (bamlError instanceof BamlValidationError) { + throw bamlError; + } else { + throw error; + } + } + } + } export const b = new BamlSyncClient(DO_NOT_USE_DIRECTLY_UNLESS_YOU_KNOW_WHAT_YOURE_DOING_RUNTIME, DO_NOT_USE_DIRECTLY_UNLESS_YOU_KNOW_WHAT_YOURE_DOING_CTX) \ No newline at end of file diff --git a/integ-tests/typescript/baml_client/type_builder.ts b/integ-tests/typescript/baml_client/type_builder.ts index f90fc46fd..15eb7982c 100644 --- a/integ-tests/typescript/baml_client/type_builder.ts +++ b/integ-tests/typescript/baml_client/type_builder.ts @@ -50,7 +50,7 @@ export default class TypeBuilder { constructor() { this.tb = new _TypeBuilder({ classes: new Set([ - "BigNumbers","Blah","BookOrder","ClassOptionalOutput","ClassOptionalOutput2","ClassWithImage","CompoundBigNumbers","ContactInfo","CustomTaskResult","DummyOutput","DynInputOutput","DynamicClassOne","DynamicClassTwo","DynamicOutput","Earthling","Education","Email","EmailAddress","Event","FakeImage","FlightConfirmation","FooAny","GroceryReceipt","InnerClass","InnerClass2","InputClass","InputClassNested","LiteralClassHello","LiteralClassOne","LiteralClassTwo","MalformedConstraints","MalformedConstraints2","Martian","NamedArgsSingleClass","Nested","Nested2","OptionalTest_Prop1","OptionalTest_ReturnType","OrderInfo","OriginalA","OriginalB","Person","PhoneNumber","Quantity","RaysData","ReceiptInfo","ReceiptItem","Recipe","Resume","Schema","SearchParams","SomeClassNestedDynamic","StringToClassEntry","TestClassAlias","TestClassNested","TestClassWithEnum","TestOutputClass","TwoStoriesOneTitle","UnionTest_ReturnType","WithReasoning", + "BigNumbers","Blah","BlockConstraint","BlockConstraintForParam","BookOrder","ClassOptionalOutput","ClassOptionalOutput2","ClassWithImage","CompoundBigNumbers","ContactInfo","CustomTaskResult","DummyOutput","DynInputOutput","DynamicClassOne","DynamicClassTwo","DynamicOutput","Earthling","Education","Email","EmailAddress","Event","FakeImage","FlightConfirmation","FooAny","GroceryReceipt","InnerClass","InnerClass2","InputClass","InputClassNested","LiteralClassHello","LiteralClassOne","LiteralClassTwo","MalformedConstraints","MalformedConstraints2","Martian","NamedArgsSingleClass","Nested","Nested2","NestedBlockConstraint","NestedBlockConstraintForParam","OptionalTest_Prop1","OptionalTest_ReturnType","OrderInfo","OriginalA","OriginalB","Person","PhoneNumber","Quantity","RaysData","ReceiptInfo","ReceiptItem","Recipe","Resume","Schema","SearchParams","SomeClassNestedDynamic","StringToClassEntry","TestClassAlias","TestClassNested","TestClassWithEnum","TestOutputClass","TwoStoriesOneTitle","UnionTest_ReturnType","WithReasoning", ]), enums: new Set([ "AliasedEnum","Category","Category2","Category3","Color","DataType","DynEnumOne","DynEnumTwo","EnumInClass","EnumOutput","Hobby","NamedArgsSingleEnum","NamedArgsSingleEnumList","OptionalTest_CategoryType","OrderStatus","Tag","TestEnum", diff --git a/integ-tests/typescript/baml_client/types.ts b/integ-tests/typescript/baml_client/types.ts index eb70bd298..d43a9c50c 100644 --- a/integ-tests/typescript/baml_client/types.ts +++ b/integ-tests/typescript/baml_client/types.ts @@ -132,6 +132,18 @@ export interface Blah { } +export interface BlockConstraint { + foo: number + bar: string + +} + +export interface BlockConstraintForParam { + bcfp: number + bcfp2: string + +} + export interface BookOrder { orderId: string title: string @@ -349,6 +361,16 @@ export interface Nested2 { } +export interface NestedBlockConstraint { + nbc: Checked + +} + +export interface NestedBlockConstraintForParam { + nbcfp: BlockConstraintForParam + +} + export interface OptionalTest_Prop1 { omega_a: string omega_b: number diff --git a/integ-tests/typescript/test-report.html b/integ-tests/typescript/test-report.html index e95352936..c2747f4a5 100644 --- a/integ-tests/typescript/test-report.html +++ b/integ-tests/typescript/test-report.html @@ -257,20 +257,23 @@ font-size: 1rem; padding: 0 0.5rem; } -

Test Report

Started: 2024-10-30 18:05:38
Suites (1)
0 passed
1 failed
0 pending
Tests (59)
55 passed
4 failed
0 pending
Integ tests > should work for all inputs
single bool
passed
1.16s
Integ tests > should work for all inputs
single string list
passed
0.455s
Integ tests > should work for all inputs
return literal union
failed
0.57s
BamlValidationError: BamlValidationError: Failed to parse LLM response: Failed to coerce value: <root>: Failed to find any (1 | true | "string output") in 3 items
-  - <root>: Expected 1, got Object([("response", String("true"))]).
-  - <root>: Expected true, got Object([("response", String("true"))]).
-  - <root>: Expected "string output", got Object([("response", String("true"))]).
-    at Function.from (/Users/vbv/repos/gloo-lang/engine/language_client_typescript/index.js:33:28)
-    at from (/Users/vbv/repos/gloo-lang/engine/language_client_typescript/index.js:58:32)
-    at BamlAsyncClient.LiteralUnionsTest (/Users/vbv/repos/gloo-lang/integ-tests/typescript/baml_client/async_client.ts:1361:50)
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:42:19)
Integ tests > should work for all inputs
single class
passed
0.394s
Integ tests > should work for all inputs
multiple classes
passed
0.641s
Integ tests > should work for all inputs
single enum list
passed
0.542s
Integ tests > should work for all inputs
single float
passed
0.496s
Integ tests > should work for all inputs
single int
passed
0.443s
Integ tests > should work for all inputs
single literal int
passed
0.48s
Integ tests > should work for all inputs
single literal bool
passed
0.357s
Integ tests > should work for all inputs
single literal string
passed
0.459s
Integ tests > should work for all inputs
single class with literal prop
passed
0.692s
Integ tests > should work for all inputs
single class with literal union prop
passed
0.659s
Integ tests > should work for all inputs
single optional string
passed
0.386s
Integ tests > should work for all inputs
single map string to string
passed
0.644s
Integ tests > should work for all inputs
single map string to class
passed
0.78s
Integ tests > should work for all inputs
single map string to map
passed
0.695s
Integ tests
should work for all outputs
passed
5.913s
Integ tests
works with retries1
passed
1.206s
Integ tests
works with retries2
passed
2.318s
Integ tests
works with fallbacks
passed
2.638s
Integ tests
should work with image from url
passed
1.751s
Integ tests
should work with image from base 64
passed
1.431s
Integ tests
should work with audio base 64
passed
1.039s
Integ tests
should work with audio from url
passed
1.083s
Integ tests
should support streaming in OpenAI
passed
2.851s
Integ tests
should support streaming in Gemini
failed
1.82s
Error: BamlError: BamlClientError: BamlClientHttpError: LLM call failed: LLMErrorResponse { client: "Gemini", model: Some("gemini-1.5-pro-001"), prompt: Chat([RenderedChatMessage { role: "user", allow_duplicate_role: false, parts: [Text("Write a nice short story about Dr. Pepper")] }]), request_options: {"safetySettings": Object {"category": String("HARM_CATEGORY_HATE_SPEECH"), "threshold": String("BLOCK_LOW_AND_ABOVE")}}, start_time: SystemTime { tv_sec: 1730336768, tv_nsec: 715426000 }, latency: 1.757292958s, message: "Failed to parse event: Error(\"missing field `content`\", line: 1, column: 359)", code: UnsupportedResponse(2) }
-    at BamlStream.parsed [as getFinalResponse] (/Users/vbv/repos/gloo-lang/engine/language_client_typescript/stream.js:58:39)
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:253:19)
Integ tests
should support AWS
passed
2.103s
Integ tests
should support streaming in AWS
passed
2.137s
Integ tests
should support OpenAI shorthand
passed
8.493s
Integ tests
should support OpenAI shorthand streaming
passed
9.091s
Integ tests
should support anthropic shorthand
passed
3.297s
Integ tests
should support anthropic shorthand streaming
passed
2.857s
Integ tests
should support streaming without iterating
passed
5.102s
Integ tests
should support streaming in Claude
passed
1.016s
Integ tests
should support vertex
passed
11.441s
Integ tests
supports tracing sync
passed
0.008s
Integ tests
supports tracing async
failed
11.651s
Error: expect(received).toEqual(expected) // deep equality
+

Test Report

Started: 2024-10-31 15:23:13
Suites (1)
0 passed
1 failed
0 pending
Tests (61)
58 passed
3 failed
0 pending
Integ tests > should work for all inputs
single bool
passed
0.528s
Integ tests > should work for all inputs
single string list
passed
0.596s
Integ tests > should work for all inputs
return literal union
failed
0.625s
BamlValidationError: BamlValidationError: Failed to parse LLM response: Failed to coerce value: <root>: Failed to find any (1 | true | "string output") in 3 items
+  - <root>: Expected 1, got Object([("result", String("true"))]).
+  - <root>: Expected true, got Object([("result", String("true"))]).
+  - <root>: Expected "string output", got Object([("result", String("true"))]).
+    at Function.from (/Users/greghale/code/baml/engine/language_client_typescript/index.js:33:28)
+    at from (/Users/greghale/code/baml/engine/language_client_typescript/index.js:58:32)
+    at BamlAsyncClient.LiteralUnionsTest (/Users/greghale/code/baml/integ-tests/typescript/baml_client/async_client.ts:1361:50)
+    at Object.<anonymous> (/Users/greghale/code/baml/integ-tests/typescript/tests/integ-tests.test.ts:42:19)
Integ tests > should work for all inputs
single class
passed
0.57s
Integ tests > should work for all inputs
multiple classes
passed
0.691s
Integ tests > should work for all inputs
single enum list
passed
0.512s
Integ tests > should work for all inputs
single float
passed
0.446s
Integ tests > should work for all inputs
single int
passed
0.351s
Integ tests > should work for all inputs
single literal int
passed
0.542s
Integ tests > should work for all inputs
single literal bool
passed
0.804s
Integ tests > should work for all inputs
single literal string
passed
0.62s
Integ tests > should work for all inputs
single class with literal prop
passed
0.636s
Integ tests > should work for all inputs
single class with literal union prop
failed
0.595s
Error: expect(received).toEqual(expected) // deep equality
 
-Expected: 30
-Received: 29
-    at Object.toEqual (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:418:24)
Integ tests
should work with dynamic types single
passed
0.924s
Integ tests
should work with dynamic types enum
passed
0.919s
Integ tests
should work with dynamic literals
passed
0.904s
Integ tests
should work with dynamic types class
passed
0.881s
Integ tests
should work with dynamic inputs class
passed
0.59s
Integ tests
should work with dynamic inputs list
passed
0.612s
Integ tests
should work with dynamic output map
passed
0.915s
Integ tests
should work with dynamic output union
passed
2.34s
Integ tests
should work with nested classes
failed
10.05s
Error: expect(received).toEqual(expected) // deep equality
+- Expected  - 1
++ Received  + 1
+
+  Object {
+-   "prop": "one",
++   "prop": "two",
+  }
+    at Object.toEqual (/Users/greghale/code/baml/integ-tests/typescript/tests/integ-tests.test.ts:111:19)
Integ tests > should work for all inputs
single optional string
passed
0.756s
Integ tests > should work for all inputs
single map string to string
passed
0.665s
Integ tests > should work for all inputs
single map string to class
passed
0.855s
Integ tests > should work for all inputs
single map string to map
passed
0.823s
Integ tests
should work for all outputs
passed
5.464s
Integ tests
works with retries1
passed
1.251s
Integ tests
works with retries2
passed
2.199s
Integ tests
works with fallbacks
passed
2.065s
Integ tests
should work with image from url
passed
1.571s
Integ tests
should work with image from base 64
passed
1.011s
Integ tests
should work with audio base 64
passed
1.135s
Integ tests
should work with audio from url
passed
1.38s
Integ tests
should support streaming in OpenAI
passed
2.451s
Integ tests
should support streaming in Gemini
passed
10.416s
Integ tests
should support AWS
passed
2.138s
Integ tests
should support streaming in AWS
passed
1.937s
Integ tests
should support OpenAI shorthand
passed
7.929s
Integ tests
should support OpenAI shorthand streaming
passed
7.467s
Integ tests
should support anthropic shorthand
passed
2.847s
Integ tests
should support anthropic shorthand streaming
passed
2.415s
Integ tests
should support streaming without iterating
passed
2.793s
Integ tests
should support streaming in Claude
passed
1.12s
Integ tests
should support vertex
passed
7.511s
Integ tests
supports tracing sync
passed
0.021s
Integ tests
supports tracing async
passed
3.391s
Integ tests
should work with dynamic types single
passed
1.142s
Integ tests
should work with dynamic types enum
passed
0.868s
Integ tests
should work with dynamic literals
passed
0.66s
Integ tests
should work with dynamic types class
passed
0.98s
Integ tests
should work with dynamic inputs class
passed
0.715s
Integ tests
should work with dynamic inputs list
passed
0.675s
Integ tests
should work with dynamic output map
passed
1.07s
Integ tests
should work with dynamic output union
passed
2.144s
Integ tests
should work with nested classes
failed
4.175s
Error: expect(received).toEqual(expected) // deep equality
 
 - Expected  - 1
 + Received  + 1
@@ -287,1654 +290,4 @@
       "prop2": "value3",
     },
   }
-    at Object.toEqual (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:580:25)
Integ tests
should work with dynamic client
passed
0.5s
Integ tests
should work with 'onLogEvent'
passed
2.102s
Integ tests
should work with a sync client
passed
0.619s
Integ tests
should raise an error when appropriate
passed
1.098s
Integ tests
should raise a BAMLValidationError
passed
0.512s
Integ tests
should reset environment variables correctly
passed
1.098s
Integ tests
should use aliases when serializing input objects - classes
passed
0.955s
Integ tests
should use aliases when serializing, but still have original keys in jinja
passed
0.958s
Integ tests
should use aliases when serializing input objects - enums
passed
0.417s
Integ tests
should use aliases when serializing input objects - lists
passed
0.522s
Integ tests
constraints: should handle checks in return types
passed
0.946s
Integ tests
constraints: should handle checks in returned unions
passed
0.885s
Console Log
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:47: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:53:15)
got response key
-true
-52
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:176: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: 1730336755, tv_nsec: 372750000 }, latency: 188.1645ms, message: "Request failed: {\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:2584:18)
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:173:7) {
-  code: 'GenericFailure'
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:185: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: 1730336757, tv_nsec: 705143000 }, latency: 209.715416ms, message: "Request failed: {\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:2609:18)
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:182:7) {
-  code: 'GenericFailure'
-}
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:338: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:347: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:341: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:347: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:344: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:347: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:359: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 0)
-    at dummyFn (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:365: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:377:5)
samDummyNested nested1
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:359: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:365: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:377:5)
samDummyNested nested2
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:359: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:365: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:377:5)
samDummyNested nested3
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:370: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:377:5)
dummy hi1
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:359: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:365: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:377:5)
samDummyNested nested1
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:359: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:365: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:377:5)
samDummyNested nested2
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:359: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:365: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:377:5)
samDummyNested nested3
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:370: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:377:5)
dummy hi2
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:359: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:365: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:377:5)
samDummyNested nested1
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:359: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:365: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:377:5)
samDummyNested nested2
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:359: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:365: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:377:5)
samDummyNested nested3
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:370: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:377:5)
dummy hi3
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:384: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:400:5)
hello world
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:359: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:365:22)
samDummyNested nested1
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:359: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:365:22)
samDummyNested nested2
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:359: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:365:22)
samDummyNested nested3
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:370: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:359: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:365: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:389: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:383:17)
samDummyNested nested1
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:359: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:365: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:389: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:383:17)
samDummyNested nested2
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:359: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:365: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:389: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:383:17)
samDummyNested nested3
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:370: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:389: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:383:17)
dummy secondDummyFuncArg
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:359: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:365: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:397: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:383:17)
samDummyNested nested1
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:359: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:365: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:397: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:383:17)
samDummyNested nested2
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:359: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:365: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:397: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:383:17)
samDummyNested nested3
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:370: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:397: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:383:17)
dummy thirdDummyFuncArg
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:403: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:409:5)
hello world
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:413:13)
stats {"failed":1,"started":30,"finalized":30,"submitted":30,"sent":30,"done":29}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:437: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:506: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:508: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:508: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:516:13)
final  {
-  hair_color: 'black',
-  attributes: { eye_color: 'blue', facial_hair: 'beard' }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:540: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:542: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:542: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:542: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:550:13)
final  {
-  hair_color: 'black',
-  attributes: { eye_color: 'blue', facial_hair: 'beard', age: '30' },
-  height: { feet: 6, inches: null }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:561:13)
final  {
-  hair_color: 'black',
-  attributes: { eye_color: 'blue', facial_hair: 'beard', age: '30' },
-  height: { meters: 1.8 }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: null, prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: null, prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: null, prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: null, prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: null, prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: null, prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: null, prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: null, prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: null, prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: null, prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: null, prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: null, prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: null, prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: null, prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: null, prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: null, prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: null, prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: null, prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: null, prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: null, prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: null, prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: null, prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: null, prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: '', prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: 'value', prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: 'value1', prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: 'value1', prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: 'value1', prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: 'value1', prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: 'value1', prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: 'value1', prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: 'value1', prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: 'value1', prop2: null }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: 'value1', prop2: { prop1: null, prop2: null, inner: null } }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: 'value1', prop2: { prop1: null, prop2: null, inner: null } }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: 'value1', prop2: { prop1: null, prop2: null, inner: null } }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: 'value1', prop2: { prop1: null, prop2: null, inner: null } }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: 'value1', prop2: { prop1: null, prop2: null, inner: null } }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: 'value1', prop2: { prop1: null, prop2: null, inner: null } }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: 'value1', prop2: { prop1: null, prop2: null, inner: null } }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: 'value1', prop2: { prop1: '', prop2: null, inner: null } }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: { prop1: 'value', prop2: null, inner: null }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: { prop1: 'value2', prop2: null, inner: null }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: { prop1: 'value2', prop2: null, inner: null }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: { prop1: 'value2', prop2: null, inner: null }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: { prop1: 'value2', prop2: null, inner: null }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: { prop1: 'value2', prop2: null, inner: null }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: { prop1: 'value2', prop2: null, inner: null }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: { prop1: 'value2', prop2: null, inner: null }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: { prop1: 'value2', prop2: null, inner: null }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg { prop1: 'value1', prop2: { prop1: 'value2', prop2: '', inner: null } }
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: { prop1: 'value2', prop2: 'value', inner: null }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: { prop1: 'value2', prop2: 'value3', inner: null }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: { prop1: 'value2', prop2: 'value3', inner: null }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: { prop1: 'value2', prop2: 'value3', inner: null }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: { prop1: 'value2', prop2: 'value3', inner: null }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: { prop1: 'value2', prop2: 'value3', inner: null }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: { prop1: 'value2', prop2: 'value3', inner: null }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: { prop1: 'value2', prop2: 'value3', inner: null }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: null, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: null, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: null, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: null, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: null, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: null, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: null, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: null, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: null, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: null, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: null, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: 3.14 }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: null }
-  }
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:574:15)
msg {
-  prop1: 'value1',
-  prop2: {
-    prop1: 'value2',
-    prop2: 'value3',
-    inner: { prop2: 42, prop3: null }
-  }
-}
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:599:15)
-    at callback (/Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:70:17)
onLogEvent {
-  metadata: {
-    eventId: 'c7061648-fbde-492e-86bc-95d36938d217',
-    rootEventId: 'c7061648-fbde-492e-86bc-95d36938d217'
-  },
-  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-10-31T01:07:26.809Z'
-}
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:599:15)
-    at callback (/Users/vbv/repos/gloo-lang/engine/language_client_typescript/async_context_vars.js:70:17)
onLogEvent {
-  metadata: {
-    eventId: 'ab924921-5f42-4c11-b428-133f26a98300',
-    rootEventId: 'ab924921-5f42-4c11-b428-133f26a98300'
-  },
-  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-10-31T01:07:27.345Z'
-}
    at Object.log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:633: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: 1730336849, tv_nsec: 305778000 }, latency: 206.536791ms, message: "Request failed: {\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:1384:18)
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:630:7) {
-  code: 'GenericFailure'
-}
    at log (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:641:17)
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:637: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:33:28)
-    at from (/Users/vbv/repos/gloo-lang/engine/language_client_typescript/index.js:58:32)
-    at BamlAsyncClient.DummyOutputFunction (/Users/vbv/repos/gloo-lang/integ-tests/typescript/baml_client/async_client.ts:486:50)
-    at /Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:639:9
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:637: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:653: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:33:28)
-    at from (/Users/vbv/repos/gloo-lang/engine/language_client_typescript/index.js:58:32)
-    at BamlAsyncClient.DummyOutputFunction (/Users/vbv/repos/gloo-lang/integ-tests/typescript/baml_client/async_client.ts:486:50)
-    at Object.<anonymous> (/Users/vbv/repos/gloo-lang/integ-tests/typescript/tests/integ-tests.test.ts:649:7) {
-  prompt: '[\x1B[2mchat\x1B[0m] \x1B[43msystem: \x1B[0mSay "hello there".\n',
-  raw_output: 'Hello there! How can I assist you today?'
-}
\ No newline at end of file + at Object.toEqual (/Users/greghale/code/baml/integ-tests/typescript/tests/integ-tests.test.ts:580:25)
Integ tests
should work with dynamic client
passed
0.431s
Integ tests
should work with 'onLogEvent'
passed
1.927s
Integ tests
should work with a sync client
passed
0.633s
Integ tests
should raise an error when appropriate
passed
0.852s
Integ tests
should raise a BAMLValidationError
passed
0.583s
Integ tests
should reset environment variables correctly
passed
0.807s
Integ tests
should use aliases when serializing input objects - classes
passed
0.913s
Integ tests
should use aliases when serializing, but still have original keys in jinja
passed
0.991s
Integ tests
should use aliases when serializing input objects - enums
passed
0.477s
Integ tests
should use aliases when serializing input objects - lists
passed
0.396s
Integ tests
constraints: should handle checks in return types
passed
1.123s
Integ tests
constraints: should handle checks in returned unions
passed
0.906s
Integ tests
constraints: should handle block-level checks
passed
0.732s
Integ tests
constraints: should handle nested-block-level checks
passed
0.666s
\ 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 f3ec985ca..a4afce68a 100644 --- a/integ-tests/typescript/tests/integ-tests.test.ts +++ b/integ-tests/typescript/tests/integ-tests.test.ts @@ -742,6 +742,17 @@ describe('Integ tests', () => { expect(res.primary.value).toBe('111-222-3333') expect(res.secondary?.value).toBe('robert@boundaryml.com') }) + + it('constraints: should handle block-level checks', async () => { + const res = await b.MakeBlockConstraint() + expect(res.checks.cross_field.status).toBe('failed') + }) + + it('constraints: should handle nested-block-level checks', async () => { + const res = await b.MakeNestedBlockConstraint() + console.log(JSON.stringify(res)); + expect(res.nbc.checks.cross_field.status).toBe('succeeded') + }) }) interface MyInterface {