From d711dc5696bb7f07bb143069572e1c4309d9128e Mon Sep 17 00:00:00 2001 From: Aaron Villalpando Date: Wed, 3 Jul 2024 21:52:43 -0700 Subject: [PATCH] improve error handling when submitting logs to api --- .../src/tracing/api_wrapper/mod.rs | 14 ++++- engine/baml-runtime/src/tracing/mod.rs | 2 - .../src/tracing/threaded_tracer.rs | 2 +- .../functions/output/serialization-error.baml | 12 +++++ integ-tests/python/baml_client/client.py | 54 +++++++++++++++++++ integ-tests/python/baml_client/inlinedbaml.py | 1 + .../python/baml_client/partial_types.py | 7 +++ .../python/baml_client/type_builder.py | 47 +++++++++++++++- integ-tests/python/baml_client/types.py | 7 +++ integ-tests/python/tests/test_functions.py | 40 ++++++++++++++ integ-tests/ruby/baml_client/client.rb | 41 ++++++++++++++ integ-tests/ruby/baml_client/inlined.rb | 1 + integ-tests/ruby/baml_client/partial-types.rb | 6 +++ integ-tests/ruby/baml_client/types.rb | 6 +++ integ-tests/typescript/baml_client/client.ts | 39 +++++++++++++- .../typescript/baml_client/inlinedbaml.ts | 1 + .../typescript/baml_client/type_builder.ts | 8 ++- integ-tests/typescript/baml_client/types.ts | 7 +++ 18 files changed, 287 insertions(+), 8 deletions(-) create mode 100644 integ-tests/baml_src/test-files/functions/output/serialization-error.baml diff --git a/engine/baml-runtime/src/tracing/api_wrapper/mod.rs b/engine/baml-runtime/src/tracing/api_wrapper/mod.rs index 665d4714c..0aad4e0d4 100644 --- a/engine/baml-runtime/src/tracing/api_wrapper/mod.rs +++ b/engine/baml-runtime/src/tracing/api_wrapper/mod.rs @@ -116,10 +116,18 @@ impl CompleteAPIConfig { return Err(anyhow::anyhow!("Failed to fetch: {url}")); }; let status = res.status(); - match res.json::().await { + let body = res.text().await?; + + if !status.is_success() { + return Err(anyhow::anyhow!( + "Failed to submit BAML log: {url}. Status: {status}\nBody: {body}" + )); + } + + match serde_json::from_str::(&body) { Ok(v) => Ok(v), Err(e) => Err(anyhow::anyhow!( - "Failed to parse response: {url}. Status: {status}\n{:?}", + "Failed to parse response: {url}. Status: {status}\nBody: {body} \nError: {:?}", e )), } @@ -130,6 +138,8 @@ impl CompleteAPIConfig { struct LogResponse { #[allow(dead_code)] status: Option, + #[allow(dead_code)] + message: Option, } impl BoundaryAPI for CompleteAPIConfig { diff --git a/engine/baml-runtime/src/tracing/mod.rs b/engine/baml-runtime/src/tracing/mod.rs index f2e33a879..85d5d4e5e 100644 --- a/engine/baml-runtime/src/tracing/mod.rs +++ b/engine/baml-runtime/src/tracing/mod.rs @@ -634,7 +634,6 @@ impl From<&RenderedPrompt> for Template { BamlMediaType::Audio => { ContentPart::B64Audio(data.base64.clone()) } - _ => panic!("Unsupported media type"), } } baml_types::BamlMedia::Url(media_type, data) => { @@ -645,7 +644,6 @@ impl From<&RenderedPrompt> for Template { BamlMediaType::Audio => { ContentPart::UrlAudio(data.url.clone()) } - _ => panic!("Unsupported media type"), } } }, diff --git a/engine/baml-runtime/src/tracing/threaded_tracer.rs b/engine/baml-runtime/src/tracing/threaded_tracer.rs index 2994266c4..f5e723ee7 100644 --- a/engine/baml-runtime/src/tracing/threaded_tracer.rs +++ b/engine/baml-runtime/src/tracing/threaded_tracer.rs @@ -84,7 +84,7 @@ impl DeliveryThread { ); } Err(e) => { - log::warn!("Unable to emit BAML logs: {}", e); + log::warn!("Unable to emit BAML logs: {:#?}", e); } } } diff --git a/integ-tests/baml_src/test-files/functions/output/serialization-error.baml b/integ-tests/baml_src/test-files/functions/output/serialization-error.baml new file mode 100644 index 000000000..1486099de --- /dev/null +++ b/integ-tests/baml_src/test-files/functions/output/serialization-error.baml @@ -0,0 +1,12 @@ +class DummyOutput { + nonce string + nonce2 string + @@dynamic +} + +function DummyOutputFunction(input: string) -> DummyOutput { + client GPT35 + prompt #" + Say "hello there". + "# +} \ No newline at end of file diff --git a/integ-tests/python/baml_client/client.py b/integ-tests/python/baml_client/client.py index c2b05f054..a7d6d886e 100644 --- a/integ-tests/python/baml_client/client.py +++ b/integ-tests/python/baml_client/client.py @@ -231,6 +231,28 @@ async def DescribeImage4( mdl = create_model("DescribeImage4ReturnType", inner=(str, ...)) return coerce(mdl, raw.parsed()) + async def DummyOutputFunction( + self, + input: str, + baml_options: BamlCallOptions = {}, + ) -> types.DummyOutput: + __tb__ = baml_options.get("tb", None) + if __tb__ is not None: + tb = __tb__._tb + else: + tb = None + + raw = await self.__runtime.call_function( + "DummyOutputFunction", + { + "input": input, + }, + self.__ctx_manager.get(), + tb, + ) + mdl = create_model("DummyOutputFunctionReturnType", inner=(types.DummyOutput, ...)) + return coerce(mdl, raw.parsed()) + async def DynamicFunc( self, input: types.DynamicClassOne, @@ -1688,6 +1710,38 @@ def DescribeImage4( tb, ) + def DummyOutputFunction( + self, + input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.BamlStream[partial_types.DummyOutput, types.DummyOutput]: + __tb__ = baml_options.get("tb", None) + if __tb__ is not None: + tb = __tb__._tb + else: + tb = None + + raw = self.__runtime.stream_function( + "DummyOutputFunction", + { + "input": input, + }, + None, + self.__ctx_manager.get(), + tb, + ) + + mdl = create_model("DummyOutputFunctionReturnType", inner=(types.DummyOutput, ...)) + partial_mdl = create_model("DummyOutputFunctionPartialReturnType", inner=(partial_types.DummyOutput, ...)) + + return baml_py.BamlStream[partial_types.DummyOutput, types.DummyOutput]( + raw, + lambda x: coerce(partial_mdl, x), + lambda x: coerce(mdl, x), + self.__ctx_manager.get(), + tb, + ) + def DynamicFunc( self, input: types.DynamicClassOne, diff --git a/integ-tests/python/baml_client/inlinedbaml.py b/integ-tests/python/baml_client/inlinedbaml.py index 1469ef21f..58600252c 100644 --- a/integ-tests/python/baml_client/inlinedbaml.py +++ b/integ-tests/python/baml_client/inlinedbaml.py @@ -55,6 +55,7 @@ "test-files/functions/output/int.baml": " ", "test-files/functions/output/optional-class.baml": "class ClassOptionalOutput {\n prop1 string\n prop2 string\n}\n\nfunction FnClassOptionalOutput(input: string) -> ClassOptionalOutput? {\n client GPT35\n prompt #\"\n Return a json blob for the following input:\n {{input}}\n\n {{ctx.output_format}}\n\n JSON:\n \"#\n}\n\n\nclass Blah {\n prop4 string?\n}\n\nclass ClassOptionalOutput2 {\n prop1 string?\n prop2 string?\n prop3 Blah?\n}\n\nfunction FnClassOptionalOutput2(input: string) -> ClassOptionalOutput2? {\n client GPT35\n prompt #\"\n Return a json blob for the following input:\n {{input}}\n\n {{ctx.output_format}}\n\n JSON:\n \"#\n}\n\ntest FnClassOptionalOutput2 {\n functions [FnClassOptionalOutput2, FnClassOptionalOutput]\n args {\n input \"example input\"\n }\n}\n", "test-files/functions/output/optional.baml": "class OptionalTest_Prop1 {\n omega_a string\n omega_b int\n}\n\nenum OptionalTest_CategoryType {\n Aleph\n Beta\n Gamma\n}\n \nclass OptionalTest_ReturnType {\n omega_1 OptionalTest_Prop1?\n omega_2 string?\n omega_3 (OptionalTest_CategoryType?)[]\n} \n \nfunction OptionalTest_Function(input: string) -> (OptionalTest_ReturnType?)[]\n{ \n client GPT35\n prompt #\"\n Return a JSON blob with this schema: \n {{ctx.output_format}}\n\n JSON:\n \"#\n}\n\ntest OptionalTest_Function {\n functions [OptionalTest_Function]\n args {\n input \"example input\"\n }\n}\n", + "test-files/functions/output/serialization-error.baml": "class DummyOutput {\n nonce string\n nonce2 string\n @@dynamic\n}\n\nfunction DummyOutputFunction(input: string) -> DummyOutput {\n client GPT35\n prompt #\"\n Say \"hello there\".\n \"#\n} ", "test-files/functions/output/string-list.baml": "function FnOutputStringList(input: string) -> string[] {\n client GPT35\n prompt #\"\n Return a list of strings in json format like [\"string1\", \"string2\", \"string3\"].\n\n JSON:\n \"#\n}\n\ntest FnOutputStringList {\n functions [FnOutputStringList]\n args {\n input \"example input\"\n }\n}\n", "test-files/functions/output/unions.baml": "class UnionTest_ReturnType {\n prop1 string | bool\n prop2 (float | bool)[]\n prop3 (bool[] | int[])\n}\n\nfunction UnionTest_Function(input: string | bool) -> UnionTest_ReturnType {\n client GPT35\n prompt #\"\n Return a JSON blob with this schema: \n {{ctx.output_format}}\n\n JSON:\n \"#\n}\n\ntest UnionTest_Function {\n functions [UnionTest_Function]\n args {\n input \"example input\"\n }\n}\n", "test-files/functions/prompts/no-chat-messages.baml": "\n\nfunction PromptTestClaude(input: string) -> string {\n client Claude\n prompt #\"\n Tell me a haiku about {{ input }}\n \"#\n}\n\n\nfunction PromptTestStreaming(input: string) -> string {\n client GPT35\n prompt #\"\n Tell me a short story about {{ input }}\n \"#\n}\n\ntest TestName {\n functions [PromptTestStreaming]\n args {\n input #\"\n hello world\n \"#\n }\n}\n", diff --git a/integ-tests/python/baml_client/partial_types.py b/integ-tests/python/baml_client/partial_types.py index 350ac21ab..c884e36b6 100644 --- a/integ-tests/python/baml_client/partial_types.py +++ b/integ-tests/python/baml_client/partial_types.py @@ -53,6 +53,13 @@ class ClassWithImage(BaseModel): param2: Optional[str] = None fake_image: Optional["FakeImage"] = None +class DummyOutput(BaseModel): + + model_config = ConfigDict(extra='allow') + + nonce: Optional[str] = None + nonce2: Optional[str] = None + class DynInputOutput(BaseModel): model_config = ConfigDict(extra='allow') diff --git a/integ-tests/python/baml_client/type_builder.py b/integ-tests/python/baml_client/type_builder.py index 54d4fb68f..d2467c037 100644 --- a/integ-tests/python/baml_client/type_builder.py +++ b/integ-tests/python/baml_client/type_builder.py @@ -19,13 +19,18 @@ class TypeBuilder(_TypeBuilder): def __init__(self): super().__init__(classes=set( - ["Blah","ClassOptionalOutput","ClassOptionalOutput2","ClassWithImage","DynInputOutput","DynamicClassOne","DynamicClassTwo","DynamicOutput","Education","Email","Event","FakeImage","InnerClass","InnerClass2","NamedArgsSingleClass","OptionalTest_Prop1","OptionalTest_ReturnType","OrderInfo","Person","RaysData","ReceiptInfo","ReceiptItem","Resume","SearchParams","SomeClassNestedDynamic","TestClassAlias","TestClassNested","TestClassWithEnum","TestOutputClass","UnionTest_ReturnType","WithReasoning",] + ["Blah","ClassOptionalOutput","ClassOptionalOutput2","ClassWithImage","DummyOutput","DynInputOutput","DynamicClassOne","DynamicClassTwo","DynamicOutput","Education","Email","Event","FakeImage","InnerClass","InnerClass2","NamedArgsSingleClass","OptionalTest_Prop1","OptionalTest_ReturnType","OrderInfo","Person","RaysData","ReceiptInfo","ReceiptItem","Resume","SearchParams","SomeClassNestedDynamic","TestClassAlias","TestClassNested","TestClassWithEnum","TestOutputClass","UnionTest_ReturnType","WithReasoning",] ), enums=set( ["Category","Category2","Category3","Color","DataType","DynEnumOne","DynEnumTwo","EnumInClass","EnumOutput","Hobby","NamedArgsSingleEnum","NamedArgsSingleEnumList","OptionalTest_CategoryType","OrderStatus","Tag","TestEnum",] )) + @property + def DummyOutput(self) -> "DummyOutputBuilder": + return DummyOutputBuilder(self) + + @property def DynInputOutput(self) -> "DynInputOutputBuilder": return DynInputOutputBuilder(self) @@ -78,6 +83,46 @@ def Hobby(self) -> "HobbyBuilder": return HobbyBuilder(self) +class DummyOutputBuilder: + def __init__(self, tb: _TypeBuilder): + self.__bldr = tb._tb.class_("DummyOutput") + self.__properties = set([ "nonce", "nonce2", ]) + self.__props = DummyOutputProperties(self.__bldr, self.__properties) + + def type(self) -> FieldType: + return self.__bldr.field() + + @property + def props(self) -> "DummyOutputProperties": + return self.__props + + def list_properties(self) -> typing.List[typing.Tuple[str, ClassPropertyBuilder]]: + return [(name, self.__bldr.property(name)) for name in self.__properties] + + def add_property(self, name: str, type: FieldType) -> ClassPropertyBuilder: + if name in self.__properties: + raise ValueError(f"Property {name} already exists.") + return ClassPropertyBuilder(self.__bldr.property(name).type(type)) + +class DummyOutputProperties: + def __init__(self, cls_bldr: ClassBuilder, properties: typing.Set[str]): + self.__bldr = cls_bldr + self.__properties = properties + + + + @property + def nonce(self) -> ClassPropertyBuilder: + return self.__bldr.property("nonce") + + @property + def nonce2(self) -> ClassPropertyBuilder: + return self.__bldr.property("nonce2") + + def __getattr__(self, name: str) -> ClassPropertyBuilder: + if name not in self.__properties: + raise AttributeError(f"Property {name} not found.") + return ClassPropertyBuilder(self.__bldr.property(name)) class DynInputOutputBuilder: def __init__(self, tb: _TypeBuilder): self.__bldr = tb._tb.class_("DynInputOutput") diff --git a/integ-tests/python/baml_client/types.py b/integ-tests/python/baml_client/types.py index 418ecd539..bebfb3c09 100644 --- a/integ-tests/python/baml_client/types.py +++ b/integ-tests/python/baml_client/types.py @@ -144,6 +144,13 @@ class ClassWithImage(BaseModel): param2: str fake_image: "FakeImage" +class DummyOutput(BaseModel): + + model_config = ConfigDict(extra='allow') + + nonce: str + nonce2: str + class DynInputOutput(BaseModel): model_config = ConfigDict(extra='allow') diff --git a/integ-tests/python/tests/test_functions.py b/integ-tests/python/tests/test_functions.py index 180b0a7bb..e32542add 100644 --- a/integ-tests/python/tests/test_functions.py +++ b/integ-tests/python/tests/test_functions.py @@ -591,3 +591,43 @@ async def test_aws_bedrock(): res = await stream.get_final_response() print("streamed final", res) assert len(res) > 0, "Expected non-empty result but got empty." + + +@pytest.mark.asyncio +async def test_serialization_exception(): + with pytest.raises(Exception) as excinfo: + await b.DummyOutputFunction("dummy input") + + print("Exception message: ", excinfo) + assert "Failed to coerce" in str(excinfo) + + +@pytest.mark.asyncio +async def test_stream_serialization_exception(): + with pytest.raises(Exception) as excinfo: + stream = b.stream.DummyOutputFunction("dummy input") + async for msg in stream: + print("streamed ", msg) + + res = await stream.get_final_response() + + print("Exception message: ", excinfo) + assert "Failed to coerce" in str(excinfo) + + +def test_stream2_serialization_exception(): + tb = TypeBuilder() + tb.DummyOutput.add_property("nonce3", tb.string()) + + async def stream_func(): + with pytest.raises(Exception) as excinfo: + stream = b.stream.DummyOutputFunction("dummy input", {"tb": tb}) + async for msg in stream: + print("streamed ", msg) + + res = await stream.get_final_response() + + print("Exception message: ", excinfo) + assert "Failed to coerce" in str(excinfo) + + asyncio.run(stream_func()) diff --git a/integ-tests/ruby/baml_client/client.rb b/integ-tests/ruby/baml_client/client.rb index 9de2dbe2e..08d4fb54b 100644 --- a/integ-tests/ruby/baml_client/client.rb +++ b/integ-tests/ruby/baml_client/client.rb @@ -209,6 +209,26 @@ def DescribeImage4( (raw.parsed_using_types(Baml::Types)) end + sig { + + params( + input: String, + ).returns(Baml::Types::DummyOutput) + + } + def DummyOutputFunction( + input: + ) + raw = @runtime.call_function( + "DummyOutputFunction", + { + "input" => input, + }, + @ctx_manager, + ) + (raw.parsed_using_types(Baml::Types)) + end + sig { params( @@ -1464,6 +1484,27 @@ def DescribeImage4( ) end + sig { + params( + input: String, + ).returns(Baml::BamlStream[Baml::Types::DummyOutput]) + } + def DummyOutputFunction( + input: + ) + raw = @runtime.stream_function( + "DummyOutputFunction", + { + "input" => input, + }, + @ctx_manager, + ) + Baml::BamlStream[Baml::PartialTypes::DummyOutput, Baml::Types::DummyOutput].new( + ffi_stream: raw, + ctx_manager: @ctx_manager + ) + end + sig { params( input: Baml::Types::DynamicClassOne, diff --git a/integ-tests/ruby/baml_client/inlined.rb b/integ-tests/ruby/baml_client/inlined.rb index 4093c9697..cbbe279b5 100644 --- a/integ-tests/ruby/baml_client/inlined.rb +++ b/integ-tests/ruby/baml_client/inlined.rb @@ -55,6 +55,7 @@ module Inlined "test-files/functions/output/int.baml" => " ", "test-files/functions/output/optional-class.baml" => "class ClassOptionalOutput {\n prop1 string\n prop2 string\n}\n\nfunction FnClassOptionalOutput(input: string) -> ClassOptionalOutput? {\n client GPT35\n prompt #\"\n Return a json blob for the following input:\n {{input}}\n\n {{ctx.output_format}}\n\n JSON:\n \"#\n}\n\n\nclass Blah {\n prop4 string?\n}\n\nclass ClassOptionalOutput2 {\n prop1 string?\n prop2 string?\n prop3 Blah?\n}\n\nfunction FnClassOptionalOutput2(input: string) -> ClassOptionalOutput2? {\n client GPT35\n prompt #\"\n Return a json blob for the following input:\n {{input}}\n\n {{ctx.output_format}}\n\n JSON:\n \"#\n}\n\ntest FnClassOptionalOutput2 {\n functions [FnClassOptionalOutput2, FnClassOptionalOutput]\n args {\n input \"example input\"\n }\n}\n", "test-files/functions/output/optional.baml" => "class OptionalTest_Prop1 {\n omega_a string\n omega_b int\n}\n\nenum OptionalTest_CategoryType {\n Aleph\n Beta\n Gamma\n}\n \nclass OptionalTest_ReturnType {\n omega_1 OptionalTest_Prop1?\n omega_2 string?\n omega_3 (OptionalTest_CategoryType?)[]\n} \n \nfunction OptionalTest_Function(input: string) -> (OptionalTest_ReturnType?)[]\n{ \n client GPT35\n prompt #\"\n Return a JSON blob with this schema: \n {{ctx.output_format}}\n\n JSON:\n \"#\n}\n\ntest OptionalTest_Function {\n functions [OptionalTest_Function]\n args {\n input \"example input\"\n }\n}\n", + "test-files/functions/output/serialization-error.baml" => "class DummyOutput {\n nonce string\n nonce2 string\n @@dynamic\n}\n\nfunction DummyOutputFunction(input: string) -> DummyOutput {\n client GPT35\n prompt #\"\n Say \"hello there\".\n \"#\n} ", "test-files/functions/output/string-list.baml" => "function FnOutputStringList(input: string) -> string[] {\n client GPT35\n prompt #\"\n Return a list of strings in json format like [\"string1\", \"string2\", \"string3\"].\n\n JSON:\n \"#\n}\n\ntest FnOutputStringList {\n functions [FnOutputStringList]\n args {\n input \"example input\"\n }\n}\n", "test-files/functions/output/unions.baml" => "class UnionTest_ReturnType {\n prop1 string | bool\n prop2 (float | bool)[]\n prop3 (bool[] | int[])\n}\n\nfunction UnionTest_Function(input: string | bool) -> UnionTest_ReturnType {\n client GPT35\n prompt #\"\n Return a JSON blob with this schema: \n {{ctx.output_format}}\n\n JSON:\n \"#\n}\n\ntest UnionTest_Function {\n functions [UnionTest_Function]\n args {\n input \"example input\"\n }\n}\n", "test-files/functions/prompts/no-chat-messages.baml" => "\n\nfunction PromptTestClaude(input: string) -> string {\n client Claude\n prompt #\"\n Tell me a haiku about {{ input }}\n \"#\n}\n\n\nfunction PromptTestStreaming(input: string) -> string {\n client GPT35\n prompt #\"\n Tell me a short story about {{ input }}\n \"#\n}\n\ntest TestName {\n functions [PromptTestStreaming]\n args {\n input #\"\n hello world\n \"#\n }\n}\n", diff --git a/integ-tests/ruby/baml_client/partial-types.rb b/integ-tests/ruby/baml_client/partial-types.rb index e80d0cfde..6d2773895 100644 --- a/integ-tests/ruby/baml_client/partial-types.rb +++ b/integ-tests/ruby/baml_client/partial-types.rb @@ -25,6 +25,7 @@ class Blah < T::Struct; end class ClassOptionalOutput < T::Struct; end class ClassOptionalOutput2 < T::Struct; end class ClassWithImage < T::Struct; end + class DummyOutput < T::Struct; end class DynInputOutput < T::Struct; end class DynamicClassOne < T::Struct; end class DynamicClassTwo < T::Struct; end @@ -73,6 +74,11 @@ class ClassWithImage < T::Struct const :param2, T.nilable(String) const :fake_image, Baml::PartialTypes::FakeImage end + class DummyOutput < T::Struct + include T::Struct::ActsAsComparable + const :nonce, T.nilable(String) + const :nonce2, T.nilable(String) + end class DynInputOutput < T::Struct include T::Struct::ActsAsComparable const :testKey, T.nilable(String) diff --git a/integ-tests/ruby/baml_client/types.rb b/integ-tests/ruby/baml_client/types.rb index 284546a38..9ee9e3920 100644 --- a/integ-tests/ruby/baml_client/types.rb +++ b/integ-tests/ruby/baml_client/types.rb @@ -135,6 +135,7 @@ class Blah < T::Struct; end class ClassOptionalOutput < T::Struct; end class ClassOptionalOutput2 < T::Struct; end class ClassWithImage < T::Struct; end + class DummyOutput < T::Struct; end class DynInputOutput < T::Struct; end class DynamicClassOne < T::Struct; end class DynamicClassTwo < T::Struct; end @@ -183,6 +184,11 @@ class ClassWithImage < T::Struct const :param2, String const :fake_image, Baml::Types::FakeImage end + class DummyOutput < T::Struct + include T::Struct::ActsAsComparable + const :nonce, String + const :nonce2, String + end class DynInputOutput < T::Struct include T::Struct::ActsAsComparable const :testKey, String diff --git a/integ-tests/typescript/baml_client/client.ts b/integ-tests/typescript/baml_client/client.ts index 4d777d02b..fa7f44534 100644 --- a/integ-tests/typescript/baml_client/client.ts +++ b/integ-tests/typescript/baml_client/client.ts @@ -16,7 +16,7 @@ $ pnpm add @boundaryml/baml // biome-ignore format: autogenerated code /* eslint-disable */ import { BamlRuntime, FunctionResult, BamlCtxManager, BamlStream, Image } from "@boundaryml/baml" -import {Blah, ClassOptionalOutput, ClassOptionalOutput2, ClassWithImage, DynInputOutput, DynamicClassOne, DynamicClassTwo, DynamicOutput, Education, Email, Event, FakeImage, InnerClass, InnerClass2, NamedArgsSingleClass, OptionalTest_Prop1, OptionalTest_ReturnType, OrderInfo, Person, RaysData, ReceiptInfo, ReceiptItem, Resume, SearchParams, SomeClassNestedDynamic, TestClassAlias, TestClassNested, TestClassWithEnum, TestOutputClass, UnionTest_ReturnType, WithReasoning, Category, Category2, Category3, Color, DataType, DynEnumOne, DynEnumTwo, EnumInClass, EnumOutput, Hobby, NamedArgsSingleEnum, NamedArgsSingleEnumList, OptionalTest_CategoryType, OrderStatus, Tag, TestEnum} from "./types" +import {Blah, ClassOptionalOutput, ClassOptionalOutput2, ClassWithImage, DummyOutput, DynInputOutput, DynamicClassOne, DynamicClassTwo, DynamicOutput, Education, Email, Event, FakeImage, InnerClass, InnerClass2, NamedArgsSingleClass, OptionalTest_Prop1, OptionalTest_ReturnType, OrderInfo, Person, RaysData, ReceiptInfo, ReceiptItem, Resume, SearchParams, SomeClassNestedDynamic, TestClassAlias, TestClassNested, TestClassWithEnum, TestOutputClass, UnionTest_ReturnType, WithReasoning, Category, Category2, Category3, Color, DataType, DynEnumOne, DynEnumTwo, EnumInClass, EnumOutput, Hobby, NamedArgsSingleEnum, NamedArgsSingleEnumList, OptionalTest_CategoryType, OrderStatus, Tag, TestEnum} from "./types" import TypeBuilder from "./type_builder" export type RecursivePartialNull = T extends object @@ -161,6 +161,21 @@ export class BamlClient { return raw.parsed() as string } + async DummyOutputFunction( + input: string, + __baml_options__?: { tb?: TypeBuilder } + ): Promise { + const raw = await this.runtime.callFunction( + "DummyOutputFunction", + { + "input": input + }, + this.ctx_manager.cloneContext(), + __baml_options__?.tb?.__tb(), + ) + return raw.parsed() as DummyOutput + } + async DynamicFunc( input: DynamicClassOne, __baml_options__?: { tb?: TypeBuilder } @@ -1153,6 +1168,28 @@ class BamlStreamClient { ) } + DummyOutputFunction( + input: string, + __baml_options__?: { tb?: TypeBuilder } + ): BamlStream, DummyOutput> { + const raw = this.runtime.streamFunction( + "DummyOutputFunction", + { + "input": input + }, + undefined, + this.ctx_manager.cloneContext(), + __baml_options__?.tb?.__tb(), + ) + return new BamlStream, DummyOutput>( + raw, + (a): a is RecursivePartialNull => a, + (a): a is DummyOutput => a, + this.ctx_manager.cloneContext(), + __baml_options__?.tb?.__tb(), + ) + } + DynamicFunc( input: DynamicClassOne, __baml_options__?: { tb?: TypeBuilder } diff --git a/integ-tests/typescript/baml_client/inlinedbaml.ts b/integ-tests/typescript/baml_client/inlinedbaml.ts index ad583ef9a..32d9fb6da 100644 --- a/integ-tests/typescript/baml_client/inlinedbaml.ts +++ b/integ-tests/typescript/baml_client/inlinedbaml.ts @@ -56,6 +56,7 @@ const fileMap = { "test-files/functions/output/int.baml": " ", "test-files/functions/output/optional-class.baml": "class ClassOptionalOutput {\n prop1 string\n prop2 string\n}\n\nfunction FnClassOptionalOutput(input: string) -> ClassOptionalOutput? {\n client GPT35\n prompt #\"\n Return a json blob for the following input:\n {{input}}\n\n {{ctx.output_format}}\n\n JSON:\n \"#\n}\n\n\nclass Blah {\n prop4 string?\n}\n\nclass ClassOptionalOutput2 {\n prop1 string?\n prop2 string?\n prop3 Blah?\n}\n\nfunction FnClassOptionalOutput2(input: string) -> ClassOptionalOutput2? {\n client GPT35\n prompt #\"\n Return a json blob for the following input:\n {{input}}\n\n {{ctx.output_format}}\n\n JSON:\n \"#\n}\n\ntest FnClassOptionalOutput2 {\n functions [FnClassOptionalOutput2, FnClassOptionalOutput]\n args {\n input \"example input\"\n }\n}\n", "test-files/functions/output/optional.baml": "class OptionalTest_Prop1 {\n omega_a string\n omega_b int\n}\n\nenum OptionalTest_CategoryType {\n Aleph\n Beta\n Gamma\n}\n \nclass OptionalTest_ReturnType {\n omega_1 OptionalTest_Prop1?\n omega_2 string?\n omega_3 (OptionalTest_CategoryType?)[]\n} \n \nfunction OptionalTest_Function(input: string) -> (OptionalTest_ReturnType?)[]\n{ \n client GPT35\n prompt #\"\n Return a JSON blob with this schema: \n {{ctx.output_format}}\n\n JSON:\n \"#\n}\n\ntest OptionalTest_Function {\n functions [OptionalTest_Function]\n args {\n input \"example input\"\n }\n}\n", + "test-files/functions/output/serialization-error.baml": "class DummyOutput {\n nonce string\n nonce2 string\n @@dynamic\n}\n\nfunction DummyOutputFunction(input: string) -> DummyOutput {\n client GPT35\n prompt #\"\n Say \"hello there\".\n \"#\n} ", "test-files/functions/output/string-list.baml": "function FnOutputStringList(input: string) -> string[] {\n client GPT35\n prompt #\"\n Return a list of strings in json format like [\"string1\", \"string2\", \"string3\"].\n\n JSON:\n \"#\n}\n\ntest FnOutputStringList {\n functions [FnOutputStringList]\n args {\n input \"example input\"\n }\n}\n", "test-files/functions/output/unions.baml": "class UnionTest_ReturnType {\n prop1 string | bool\n prop2 (float | bool)[]\n prop3 (bool[] | int[])\n}\n\nfunction UnionTest_Function(input: string | bool) -> UnionTest_ReturnType {\n client GPT35\n prompt #\"\n Return a JSON blob with this schema: \n {{ctx.output_format}}\n\n JSON:\n \"#\n}\n\ntest UnionTest_Function {\n functions [UnionTest_Function]\n args {\n input \"example input\"\n }\n}\n", "test-files/functions/prompts/no-chat-messages.baml": "\n\nfunction PromptTestClaude(input: string) -> string {\n client Claude\n prompt #\"\n Tell me a haiku about {{ input }}\n \"#\n}\n\n\nfunction PromptTestStreaming(input: string) -> string {\n client GPT35\n prompt #\"\n Tell me a short story about {{ input }}\n \"#\n}\n\ntest TestName {\n functions [PromptTestStreaming]\n args {\n input #\"\n hello world\n \"#\n }\n}\n", diff --git a/integ-tests/typescript/baml_client/type_builder.ts b/integ-tests/typescript/baml_client/type_builder.ts index 66d694ee4..39f4d8207 100644 --- a/integ-tests/typescript/baml_client/type_builder.ts +++ b/integ-tests/typescript/baml_client/type_builder.ts @@ -21,6 +21,8 @@ import { TypeBuilder as _TypeBuilder, EnumBuilder, ClassBuilder } from '@boundar export default class TypeBuilder { private tb: _TypeBuilder; + DummyOutput: ClassBuilder<'DummyOutput', "nonce" | "nonce2">; + DynInputOutput: ClassBuilder<'DynInputOutput', "testKey">; DynamicClassOne: ClassBuilder<'DynamicClassOne'>; @@ -46,13 +48,17 @@ export default class TypeBuilder { constructor() { this.tb = new _TypeBuilder({ classes: new Set([ - "Blah","ClassOptionalOutput","ClassOptionalOutput2","ClassWithImage","DynInputOutput","DynamicClassOne","DynamicClassTwo","DynamicOutput","Education","Email","Event","FakeImage","InnerClass","InnerClass2","NamedArgsSingleClass","OptionalTest_Prop1","OptionalTest_ReturnType","OrderInfo","Person","RaysData","ReceiptInfo","ReceiptItem","Resume","SearchParams","SomeClassNestedDynamic","TestClassAlias","TestClassNested","TestClassWithEnum","TestOutputClass","UnionTest_ReturnType","WithReasoning", + "Blah","ClassOptionalOutput","ClassOptionalOutput2","ClassWithImage","DummyOutput","DynInputOutput","DynamicClassOne","DynamicClassTwo","DynamicOutput","Education","Email","Event","FakeImage","InnerClass","InnerClass2","NamedArgsSingleClass","OptionalTest_Prop1","OptionalTest_ReturnType","OrderInfo","Person","RaysData","ReceiptInfo","ReceiptItem","Resume","SearchParams","SomeClassNestedDynamic","TestClassAlias","TestClassNested","TestClassWithEnum","TestOutputClass","UnionTest_ReturnType","WithReasoning", ]), enums: new Set([ "Category","Category2","Category3","Color","DataType","DynEnumOne","DynEnumTwo","EnumInClass","EnumOutput","Hobby","NamedArgsSingleEnum","NamedArgsSingleEnumList","OptionalTest_CategoryType","OrderStatus","Tag","TestEnum", ]) }); + this.DummyOutput = this.tb.classBuilder("DummyOutput", [ + "nonce","nonce2", + ]); + this.DynInputOutput = this.tb.classBuilder("DynInputOutput", [ "testKey", ]); diff --git a/integ-tests/typescript/baml_client/types.ts b/integ-tests/typescript/baml_client/types.ts index be0822229..c8fef7184 100644 --- a/integ-tests/typescript/baml_client/types.ts +++ b/integ-tests/typescript/baml_client/types.ts @@ -141,6 +141,13 @@ export interface ClassWithImage { } +export interface DummyOutput { + nonce: string + nonce2: string + + [key: string]: any; +} + export interface DynInputOutput { testKey: string