Skip to content

Commit

Permalink
improve error handling when submitting logs to api
Browse files Browse the repository at this point in the history
  • Loading branch information
aaronvg committed Jul 4, 2024
1 parent d1ebbc0 commit d711dc5
Show file tree
Hide file tree
Showing 18 changed files with 287 additions and 8 deletions.
14 changes: 12 additions & 2 deletions engine/baml-runtime/src/tracing/api_wrapper/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,18 @@ impl CompleteAPIConfig {
return Err(anyhow::anyhow!("Failed to fetch: {url}"));
};
let status = res.status();
match res.json::<T>().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::<T>(&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
)),
}
Expand All @@ -130,6 +138,8 @@ impl CompleteAPIConfig {
struct LogResponse {
#[allow(dead_code)]
status: Option<String>,
#[allow(dead_code)]
message: Option<String>,
}

impl BoundaryAPI for CompleteAPIConfig {
Expand Down
2 changes: 0 additions & 2 deletions engine/baml-runtime/src/tracing/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand All @@ -645,7 +644,6 @@ impl From<&RenderedPrompt> for Template {
BamlMediaType::Audio => {
ContentPart::UrlAudio(data.url.clone())
}
_ => panic!("Unsupported media type"),
}
}
},
Expand Down
2 changes: 1 addition & 1 deletion engine/baml-runtime/src/tracing/threaded_tracer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ impl DeliveryThread {
);
}
Err(e) => {
log::warn!("Unable to emit BAML logs: {}", e);
log::warn!("Unable to emit BAML logs: {:#?}", e);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class DummyOutput {
nonce string
nonce2 string
@@dynamic
}

function DummyOutputFunction(input: string) -> DummyOutput {
client GPT35
prompt #"
Say "hello there".
"#
}
54 changes: 54 additions & 0 deletions integ-tests/python/baml_client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions integ-tests/python/baml_client/inlinedbaml.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
7 changes: 7 additions & 0 deletions integ-tests/python/baml_client/partial_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
47 changes: 46 additions & 1 deletion integ-tests/python/baml_client/type_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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")
Expand Down
7 changes: 7 additions & 0 deletions integ-tests/python/baml_client/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
40 changes: 40 additions & 0 deletions integ-tests/python/tests/test_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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())
41 changes: 41 additions & 0 deletions integ-tests/ruby/baml_client/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions integ-tests/ruby/baml_client/inlined.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
6 changes: 6 additions & 0 deletions integ-tests/ruby/baml_client/partial-types.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions integ-tests/ruby/baml_client/types.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Loading

0 comments on commit d711dc5

Please sign in to comment.