@@ -57,3 +57,41 @@ def test_unified_engine_adapt_routes_by_provider() -> None:
5757
5858def test_adapt_none_returns_none () -> None :
5959 assert UnifiedAIEngine ._adapt_structured_schema_for_llm_provider (None , "openai" ) is None
60+
61+
62+ def _first_property_key_of_object_schema (obj : dict ) -> str | None :
63+ props = obj .get ("properties" )
64+ if not isinstance (props , dict ) or not props :
65+ return None
66+ return next (iter (props .keys ()))
67+
68+
69+ def test_openai_blocks_anyof_branches_have_distinct_first_keys () -> None :
70+ """OpenAI rejects anyOf when each object branch shares the same first key."""
71+ full = EmailInsightResponse .model_json_schema (by_alias = True )
72+ prepared = _prepare_like_engine (full , "EmailInsightResponse" )
73+ adapted = adapt_json_schema_for_openai_structured_output (prepared )
74+
75+ blocks = adapted .get ("properties" , {}).get ("blocks" )
76+ assert isinstance (blocks , dict )
77+ items = blocks .get ("items" )
78+ assert isinstance (items , dict )
79+ variants = items .get ("anyOf" ) or items .get ("oneOf" )
80+ assert isinstance (variants , list ) and len (variants ) == 3
81+
82+ first_keys : list [str ] = []
83+ for branch in variants :
84+ assert isinstance (branch , dict )
85+ if "$ref" in branch :
86+ ref = branch ["$ref" ]
87+ assert ref .startswith ("#/$defs/" )
88+ def_name = ref .removeprefix ("#/$defs/" )
89+ defn = adapted .get ("$defs" , {}).get (def_name )
90+ assert isinstance (defn , dict )
91+ key = _first_property_key_of_object_schema (defn )
92+ else :
93+ key = _first_property_key_of_object_schema (branch )
94+ assert key is not None
95+ first_keys .append (key )
96+
97+ assert len (set (first_keys )) == len (first_keys ), f"duplicate first keys: { first_keys } "
0 commit comments