@@ -719,12 +719,26 @@ private IJsonSchemaValidationProperties findMatchingOneOf(Object param, CodegenM
719719 return bestOneOf ;
720720 }
721721
722- for (CodegenProperty prop : model .getComposedSchemas ().getOneOf ()) {
723- // find the correct list
724- if (param instanceof List && prop .getIsArray ()) {
725- return prop ;
722+ if (param instanceof List ) {
723+ List <CodegenProperty > arrayVariants = model .getComposedSchemas ().getOneOf ().stream ().filter (CodegenProperty ::getIsArray ).toList ();
724+
725+ if (arrayVariants .size () == 1 ) {
726+ // Single array variant - return it directly (existing behavior)
727+ return arrayVariants .get (0 );
726728 }
727729
730+ if (arrayVariants .size () > 1 ) {
731+ // Multiple array variants - inspect first element to disambiguate
732+ List <?> list = (List <?>) param ;
733+ if (!list .isEmpty () && list .get (0 ) instanceof Map ) {
734+ return findBestArrayVariant (arrayVariants , (Map <String , Object >) list .get (0 ));
735+ }
736+ // Empty list or non-Map elements - fall back to first
737+ return arrayVariants .get (0 );
738+ }
739+ }
740+
741+ for (CodegenProperty prop : model .getComposedSchemas ().getOneOf ()) {
728742 // find the correct enum
729743 if (param instanceof String && prop .getIsEnumOrRef () && couldMatchEnum (param , prop )) {
730744 return prop ;
@@ -765,6 +779,88 @@ private IJsonSchemaValidationProperties findMatchingOneOf(Object param, CodegenM
765779 return maybeMatch ;
766780 }
767781
782+ /**
783+ * When a oneOf has multiple array variants, score each variant by checking how well its items
784+ * type matches a sample element from the data. Uses the same required-field and property-counting
785+ * heuristic as the Map-based oneOf resolution.
786+ */
787+ @ SuppressWarnings ("unchecked" )
788+ private CodegenProperty findBestArrayVariant (List <CodegenProperty > arrayVariants , Map <String , Object > sampleElement ) {
789+ CodegenProperty bestVariant = arrayVariants .get (0 );
790+ int bestScore = -1 ;
791+
792+ for (CodegenProperty variant : arrayVariants ) {
793+ CodegenProperty items = variant .getItems ();
794+ if (items == null ) continue ;
795+
796+ String itemTypeName = items .getDataType ();
797+ if (itemTypeName == null ) continue ;
798+
799+ CodegenModel itemModel = models .get (itemTypeName );
800+ if (itemModel == null ) {
801+ // Try lowercase first char
802+ String lower = itemTypeName .substring (0 , 1 ).toLowerCase () + itemTypeName .substring (1 );
803+ itemModel = models .get (lower );
804+ }
805+ if (itemModel == null ) continue ;
806+
807+ int score ;
808+ if (!itemModel .oneOf .isEmpty () && itemModel .interfaceModels != null ) {
809+ // Items type is itself a oneOf (e.g. MessageV4 = UserMessageV4 | AssistantMessageV4)
810+ // Find best sub-match score
811+ score = scoreOneOfModelMatch (sampleElement , itemModel );
812+ } else {
813+ // Items type is a direct model
814+ score = scoreDirectModelMatch (sampleElement , itemModel );
815+ }
816+
817+ if (score > bestScore ) {
818+ bestScore = score ;
819+ bestVariant = variant ;
820+ }
821+ }
822+
823+ return bestVariant ;
824+ }
825+
826+ /**
827+ * Score how well a Map matches against the best sub-variant of a oneOf model. Returns -1 if no
828+ * sub-variant matches (all have missing required fields).
829+ */
830+ private int scoreOneOfModelMatch (Map <String , Object > data , CodegenModel oneOfModel ) {
831+ int bestScore = -1 ;
832+ for (CodegenModel subVariant : oneOfModel .interfaceModels ) {
833+ if (subVariant .vars .isEmpty ()) continue ;
834+ int score = scoreDirectModelMatch (data , subVariant );
835+ if (score > bestScore ) {
836+ bestScore = score ;
837+ }
838+ }
839+ return bestScore ;
840+ }
841+
842+ /**
843+ * Score how well a Map matches a direct model: -1 if required fields are missing, otherwise the
844+ * count of common properties.
845+ */
846+ private int scoreDirectModelMatch (Map <String , Object > data , CodegenModel model ) {
847+ // If any required property is missing, this model doesn't match
848+ for (CodegenProperty prop : model .requiredVars ) {
849+ if (!data .containsKey (prop .baseName )) {
850+ return -1 ;
851+ }
852+ }
853+ int commonCount = 0 ;
854+ for (String key : data .keySet ()) {
855+ for (CodegenProperty prop : model .vars ) {
856+ if (key .equals (prop .baseName ) && couldMatchEnum (data .get (key ), prop )) {
857+ commonCount ++;
858+ }
859+ }
860+ }
861+ return commonCount ;
862+ }
863+
768864 // If the model is an enum and contains a valid list of allowed values,
769865 // it will check that 'value' is in the list.
770866 // Otherwise return true by default to avoid false negative.
0 commit comments