-
Notifications
You must be signed in to change notification settings - Fork 359
Description
Bug
When using dict[str, str | None] (or any dict type) as the return type of a by llm(tools=([...])) function, Pydantic validation fails intermittently with:
1 validation error for dict[str,nullable[str]]
schema_dict_wrapper
Input should be a valid string [type=string_type, input_value=[{'key': 'assets', 'value...d: BDO, Balance: 500$'}], input_type=list]
Root Cause
_convert_dict_to_schema wraps dict types into a schema_dict_wrapper array format for the LLM API. The LLM returns the dict in this wrapped format.
The response parsing path (json_to_instance in schema.impl.jac) correctly calls _decode_dict to unwrap it before Pydantic validation. However, Tool.parse_arguments in types.impl/tool.impl.jac calls TypeAdapter(arg_type).validate_python(arg_json) directly on the wrapped JSON without first calling _decode_dict.
This affects the finish_tool in ReAct flows — when the LLM calls finish_tool(final_output={"schema_dict_wrapper": [...]}), parse_arguments tries to validate the wrapped format against dict[str, str | None], and Pydantic rejects the array value as not being a string.
Suggested Fix
In byllm/types.impl/tool.impl.jac, Tool.parse_arguments:
impl Tool.parse_arguments(args_json: dict) -> dict {
args = {};
annotations: dict = {};
try {
annotations = self.func.__annotations__;
} except AttributeError {
annotations = get_type_hints(self.func);
}
for (arg_name, arg_json) in args_json.items() {
if arg_type := annotations.get(arg_name) {
if isinstance(arg_json, dict) {
arg_json = _decode_dict(arg_json); # <-- decode before validation
}
args[arg_name] = TypeAdapter(arg_type).validate_python(arg_json);
}
}
return args;
}And add _decode_dict to the import in byllm/types.jac:
import from .schema { tool_to_schema, _decode_dict }Workaround
Use an obj instead of dict for the return type:
obj AssistantResponse {
has message: str,
html: str | None = None;
}
def process_request(query: str, history: str) -> AssistantResponse by llm(tools=([...]));