Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve error handling when submitting logs to api #754

Merged
merged 1 commit into from
Jul 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading