@@ -535,3 +535,120 @@ def my_tool(param: Any = 'default_string') -> str:
535535 assert declaration .parameters .properties ['param' ].default == 'default_string'
536536 # Any type maps to None (no type) in schema
537537 assert declaration .parameters .properties ['param' ].type is None
538+
539+
540+ class _UnserializableReturn :
541+ """A plain class that has no genai/JSON schema representation."""
542+
543+
544+ def test_from_function_with_options_unserializable_return_vertex_degrades_gracefully ():
545+ """VERTEX_AI omits the response schema instead of raising when it can't be derived."""
546+
547+ def test_function (param : str ) -> _UnserializableReturn :
548+ """A function whose return type cannot be turned into a schema."""
549+ return _UnserializableReturn ()
550+
551+ declaration = _automatic_function_calling_util .from_function_with_options (
552+ test_function , GoogleLLMVariant .VERTEX_AI
553+ )
554+
555+ assert declaration .name == 'test_function'
556+ # Parameters are still populated; only the return schema is dropped.
557+ assert declaration .parameters .type == 'OBJECT'
558+ assert declaration .parameters .properties ['param' ].type == 'STRING'
559+ assert declaration .response is None
560+
561+
562+ def test_from_function_with_options_logs_warning_on_return_schema_failure (
563+ caplog ,
564+ ):
565+ """A warning naming the function is emitted when the return schema is dropped."""
566+
567+ def test_function (param : str ) -> _UnserializableReturn :
568+ """A function whose return type cannot be turned into a schema."""
569+ return _UnserializableReturn ()
570+
571+ with caplog .at_level (
572+ 'WARNING' ,
573+ logger = 'google_adk.google.adk.tools._automatic_function_calling_util' ,
574+ ):
575+ _automatic_function_calling_util .from_function_with_options (
576+ test_function , GoogleLLMVariant .VERTEX_AI
577+ )
578+
579+ warnings = [r for r in caplog .records if r .levelname == 'WARNING' ]
580+ assert len (warnings ) == 1
581+ assert 'test_function' in warnings [0 ].getMessage ()
582+
583+
584+ def test_from_function_with_options_valid_pydantic_return_still_gets_schema_vertex ():
585+ """A serializable pydantic return type keeps producing a response schema."""
586+
587+ class MyModel (pydantic .BaseModel ):
588+ result : str
589+
590+ def test_function (param : str ) -> MyModel :
591+ """A function that returns a valid pydantic model."""
592+ return MyModel (result = param )
593+
594+ declaration = _automatic_function_calling_util .from_function_with_options (
595+ test_function , GoogleLLMVariant .VERTEX_AI
596+ )
597+
598+ assert declaration .name == 'test_function'
599+ assert declaration .response is not None
600+ assert declaration .response .type == types .Type .OBJECT
601+
602+
603+ def test_from_function_with_options_non_value_error_return_degrades_gracefully (
604+ monkeypatch ,
605+ ):
606+ """A non-ValueError from schema parsing is caught (not propagated) and degrades."""
607+
608+ parse_util = _automatic_function_calling_util ._function_parameter_parse_util
609+ original_parse = parse_util ._parse_schema_from_parameter
610+
611+ def _raise_type_error_for_return (variant , param , func_name ):
612+ # Only the return-schema parse should raise; leave parameter parsing intact.
613+ if param .name == 'return_value' :
614+ raise TypeError ('simulated non-ValueError from schema parsing' )
615+ return original_parse (variant , param , func_name )
616+
617+ monkeypatch .setattr (
618+ parse_util ,
619+ '_parse_schema_from_parameter' ,
620+ _raise_type_error_for_return ,
621+ )
622+
623+ def test_function (param : str ) -> _UnserializableReturn :
624+ """A function whose return schema parsing raises a non-ValueError."""
625+ return _UnserializableReturn ()
626+
627+ declaration = _automatic_function_calling_util .from_function_with_options (
628+ test_function , GoogleLLMVariant .VERTEX_AI
629+ )
630+
631+ assert declaration .name == 'test_function'
632+ assert declaration .response is None
633+
634+
635+ def test_from_function_with_options_warning_includes_original_error (caplog ):
636+ """The warning names both the fallback and the original parsing error."""
637+
638+ def test_function (param : str ) -> _UnserializableReturn :
639+ """A function whose return type cannot be turned into a schema."""
640+ return _UnserializableReturn ()
641+
642+ with caplog .at_level (
643+ 'WARNING' ,
644+ logger = 'google_adk.google.adk.tools._automatic_function_calling_util' ,
645+ ):
646+ _automatic_function_calling_util .from_function_with_options (
647+ test_function , GoogleLLMVariant .VERTEX_AI
648+ )
649+
650+ warnings = [r for r in caplog .records if r .levelname == 'WARNING' ]
651+ assert len (warnings ) == 1
652+ message = warnings [0 ].getMessage ()
653+ assert 'Fallback error:' in message
654+ assert 'Original error:' in message
0 commit comments