@@ -661,3 +661,120 @@ def greet(name: str = 'World') -> str:
661661 schema = decl .parameters_json_schema
662662 assert schema ['properties' ]['name' ]['default' ] == 'World'
663663 assert 'name' not in schema .get ('required' , [])
664+
665+
666+ # ── Pipe-union (X | Y) tests ──────────────────────────────────────────
667+
668+
669+ def test_pipe_union_optional_list ():
670+ """list[str] | None should parse as ARRAY with nullable=True."""
671+
672+ def func (a : list [str ] | None ):
673+ pass
674+
675+ decl = _automatic_function_calling_util .build_function_declaration (
676+ func = func , variant = GoogleLLMVariant .VERTEX_AI
677+ )
678+ prop = decl .parameters .properties ['a' ]
679+ assert prop .type == types .Type .ARRAY
680+ assert prop .nullable is True
681+
682+
683+ def test_pipe_union_optional_dict ():
684+ """dict[str, int] | None should parse as OBJECT with nullable=True."""
685+
686+ def func (a : dict [str , int ] | None ):
687+ pass
688+
689+ decl = _automatic_function_calling_util .build_function_declaration (
690+ func = func , variant = GoogleLLMVariant .VERTEX_AI
691+ )
692+ prop = decl .parameters .properties ['a' ]
693+ assert prop .type == types .Type .OBJECT
694+ assert prop .nullable is True
695+
696+
697+ def test_pipe_union_optional_list_with_default ():
698+ """list[str] | None = None should parse as ARRAY, nullable, no default."""
699+
700+ def func (a : list [str ] | None = None ):
701+ pass
702+
703+ decl = _automatic_function_calling_util .build_function_declaration (
704+ func = func , variant = GoogleLLMVariant .VERTEX_AI
705+ )
706+ prop = decl .parameters .properties ['a' ]
707+ assert prop .type == types .Type .ARRAY
708+ assert prop .nullable is True
709+
710+
711+ def test_pipe_union_simple_primitives ():
712+ """int | str should produce any_of with two types."""
713+
714+ def func (a : int | str ):
715+ pass
716+
717+ decl = _automatic_function_calling_util .build_function_declaration (
718+ func = func , variant = GoogleLLMVariant .VERTEX_AI
719+ )
720+ prop = decl .parameters .properties ['a' ]
721+ assert prop .any_of is not None
722+ assert len (prop .any_of ) == 2
723+
724+
725+ def test_pipe_union_simple_primitives_with_none ():
726+ """int | str | None should produce any_of + nullable."""
727+
728+ def func (a : int | str | None ):
729+ pass
730+
731+ decl = _automatic_function_calling_util .build_function_declaration (
732+ func = func , variant = GoogleLLMVariant .VERTEX_AI
733+ )
734+ prop = decl .parameters .properties ['a' ]
735+ assert prop .any_of is not None
736+ assert len (prop .any_of ) == 2
737+ assert prop .nullable is True
738+
739+
740+ def test_pipe_union_complex_multi_type ():
741+ """list[str] | dict[str, int] should produce any_of (VERTEX_AI)."""
742+
743+ def func (a : list [str ] | dict [str , int ]):
744+ pass
745+
746+ decl = _automatic_function_calling_util .build_function_declaration (
747+ func = func , variant = GoogleLLMVariant .VERTEX_AI
748+ )
749+ prop = decl .parameters .properties ['a' ]
750+ assert prop .any_of is not None
751+ assert len (prop .any_of ) == 2
752+
753+
754+ def test_pipe_union_complex_falls_back_for_gemini_api ():
755+ """Complex pipe union for GEMINI_API falls back to pydantic schema."""
756+
757+ def func (a : list [str ] | dict [str , int ]):
758+ pass
759+
760+ decl = _automatic_function_calling_util .build_function_declaration (
761+ func = func , variant = GoogleLLMVariant .GEMINI_API
762+ )
763+ # GEMINI_API does not support any_of, so the parser falls back to
764+ # pydantic-based json schema generation.
765+ assert decl .name == 'func'
766+
767+
768+ def test_typing_union_optional_list_still_works ():
769+ """Regression: typing.Union[list[str], None] must still work."""
770+ import typing
771+
772+ def func (a : typing .Union [list [str ], None ]):
773+ pass
774+
775+ decl = _automatic_function_calling_util .build_function_declaration (
776+ func = func , variant = GoogleLLMVariant .VERTEX_AI
777+ )
778+ prop = decl .parameters .properties ['a' ]
779+ assert prop .type == types .Type .ARRAY
780+ assert prop .nullable is True
0 commit comments