|
20 | 20 | import io |
21 | 21 | import itertools |
22 | 22 | import json |
23 | | -from typing import Any, TypeVar |
| 23 | +from typing import Any, TypeVar, Union |
24 | 24 |
|
25 | 25 | from absl import logging |
26 | 26 | from genai_processors import mime_types |
@@ -328,23 +328,66 @@ def from_function_response( |
328 | 328 | cls, |
329 | 329 | *, |
330 | 330 | name: str, |
331 | | - response: dict[str, Any], |
| 331 | + response: Union[dict[str, Any], 'ProcessorContentTypes'], |
332 | 332 | function_call_id: str | None = None, |
333 | 333 | will_continue: bool | None = None, |
334 | 334 | scheduling: genai_types.FunctionResponseScheduling | None = None, |
335 | 335 | **kwargs, |
336 | 336 | ) -> 'ProcessorPart': |
337 | | - """Constructs a ProcessorPart as a function response.""" |
338 | | - part = genai_types.Part( |
339 | | - function_response=genai_types.FunctionResponse( |
340 | | - id=function_call_id, |
341 | | - name=name, |
342 | | - response=response, |
343 | | - will_continue=will_continue, |
344 | | - scheduling=scheduling, |
345 | | - ) |
| 337 | + """Constructs a ProcessorPart as a function response. |
| 338 | +
|
| 339 | + Args: |
| 340 | + name: The name of the invoked function. |
| 341 | + response: The value returned by the function. It can be a |
| 342 | + JSON-serializable object or `ProcessorContentTypes`. If response is a |
| 343 | + JSON-serializable or is text-only it will be stored in the '.result' |
| 344 | + field of the function response. Otherwise it will be stored in `.parts`. |
| 345 | + function_call_id: The ID of the function call this is a response to if |
| 346 | + known. |
| 347 | + will_continue: Whether this is the last response from a generator |
| 348 | + function. |
| 349 | + scheduling: The scheduling policy for the function response. Controls |
| 350 | + whether the response generation will be triggered immediately or the |
| 351 | + function response is just added to the context. |
| 352 | + **kwargs: Additional keyword arguments to pass to the `ProcessorPart` |
| 353 | + constructor. |
| 354 | +
|
| 355 | + Returns: |
| 356 | + A ProcessorPart representing the function response. |
| 357 | + """ |
| 358 | + function_response_args = dict( |
| 359 | + id=function_call_id, |
| 360 | + name=name, |
| 361 | + will_continue=will_continue, |
| 362 | + scheduling=scheduling, |
346 | 363 | ) |
347 | | - return cls(part, **kwargs) |
| 364 | + # For maximal compatibility we first try to interpret response as JSON |
| 365 | + # serializable object. This is what tools in Gemini API return historically. |
| 366 | + try: |
| 367 | + function_response = genai_types.FunctionResponse( |
| 368 | + response={'result': response}, **function_response_args |
| 369 | + ) |
| 370 | + function_response.json() |
| 371 | + except ValueError: |
| 372 | + # Response is not JSON serializable. Then try to construct content. |
| 373 | + response_content = ProcessorContent(response) |
| 374 | + try: |
| 375 | + function_response = genai_types.FunctionResponse( |
| 376 | + response={'result': as_text(response_content, strict=True)}, |
| 377 | + **function_response_args, |
| 378 | + ) |
| 379 | + except ValueError: |
| 380 | + parts = [ |
| 381 | + genai_types.FunctionResponsePart.from_bytes( |
| 382 | + data=part.bytes, mime_type=part.mimetype |
| 383 | + ) |
| 384 | + for part in response_content |
| 385 | + ] |
| 386 | + function_response = genai_types.FunctionResponse( |
| 387 | + parts=parts, **function_response_args |
| 388 | + ) |
| 389 | + |
| 390 | + return cls(genai_types.Part(function_response=function_response), **kwargs) |
348 | 391 |
|
349 | 392 | @classmethod |
350 | 393 | def from_executable_code( |
|
0 commit comments