@@ -486,7 +486,7 @@ class ToolCallPart:
486
486
tool_name : str
487
487
"""The name of the tool to call."""
488
488
489
- args : str | dict [str , Any ]
489
+ args : str | dict [str , Any ] | None = None
490
490
"""The arguments to pass to the tool.
491
491
492
492
This is stored either as a JSON string or a Python dictionary depending on how data was received.
@@ -506,10 +506,10 @@ def args_as_dict(self) -> dict[str, Any]:
506
506
507
507
This is just for convenience with models that require dicts as input.
508
508
"""
509
+ if not self .args :
510
+ return {}
509
511
if isinstance (self .args , dict ):
510
512
return self .args
511
- if isinstance (self .args , str ) and not self .args :
512
- return {}
513
513
args = pydantic_core .from_json (self .args )
514
514
assert isinstance (args , dict ), 'args should be a dict'
515
515
return cast (dict [str , Any ], args )
@@ -519,6 +519,8 @@ def args_as_json_str(self) -> str:
519
519
520
520
This is just for convenience with models that require JSON strings as input.
521
521
"""
522
+ if not self .args :
523
+ return '{}'
522
524
if isinstance (self .args , str ):
523
525
return self .args
524
526
return pydantic_core .to_json (self .args ).decode ()
@@ -666,9 +668,9 @@ def as_part(self) -> ToolCallPart | None:
666
668
"""Convert this delta to a fully formed `ToolCallPart` if possible, otherwise return `None`.
667
669
668
670
Returns:
669
- A `ToolCallPart` if both `tool_name_delta` and `args_delta` are set, otherwise `None`.
671
+ A `ToolCallPart` if `tool_name_delta` is set, otherwise `None`.
670
672
"""
671
- if self .tool_name_delta is None or self . args_delta is None :
673
+ if self .tool_name_delta is None :
672
674
return None
673
675
674
676
return ToolCallPart (self .tool_name_delta , self .args_delta , self .tool_call_id or _generate_tool_call_id ())
@@ -728,7 +730,7 @@ def _apply_to_delta(self, delta: ToolCallPartDelta) -> ToolCallPart | ToolCallPa
728
730
delta = replace (delta , tool_call_id = self .tool_call_id )
729
731
730
732
# If we now have enough data to create a full ToolCallPart, do so
731
- if delta .tool_name_delta is not None and delta . args_delta is not None :
733
+ if delta .tool_name_delta is not None :
732
734
return ToolCallPart (delta .tool_name_delta , delta .args_delta , delta .tool_call_id or _generate_tool_call_id ())
733
735
734
736
return delta
@@ -741,12 +743,12 @@ def _apply_to_part(self, part: ToolCallPart) -> ToolCallPart:
741
743
part = replace (part , tool_name = tool_name )
742
744
743
745
if isinstance (self .args_delta , str ):
744
- if not isinstance (part .args , str ):
746
+ if isinstance (part .args , dict ):
745
747
raise UnexpectedModelBehavior (f'Cannot apply JSON deltas to non-JSON tool arguments ({ part = } , { self = } )' )
746
- updated_json = part .args + self .args_delta
748
+ updated_json = ( part .args or '' ) + self .args_delta
747
749
part = replace (part , args = updated_json )
748
750
elif isinstance (self .args_delta , dict ):
749
- if not isinstance (part .args , dict ):
751
+ if isinstance (part .args , str ):
750
752
raise UnexpectedModelBehavior (f'Cannot apply dict deltas to non-dict tool arguments ({ part = } , { self = } )' )
751
753
updated_dict = {** (part .args or {}), ** self .args_delta }
752
754
part = replace (part , args = updated_dict )
0 commit comments