$sub ) { if ( is_array( $sub ) ) { $schema[ $map_key ][ $name ] = openstation_ai_normalize_response_schema( $sub ); } } } } if ( isset( $schema['items'] ) && is_array( $schema['items'] ) ) { $items = $schema['items']; $is_list = array_keys( $items ) === range( 0, count( $items ) - 1 ); if ( $is_list && array() !== $items ) { // Tuple form — a list of schemas. foreach ( $items as $i => $sub ) { if ( is_array( $sub ) ) { $items[ $i ] = openstation_ai_normalize_response_schema( $sub ); } } $schema['items'] = $items; } else { $schema['items'] = openstation_ai_normalize_response_schema( $items ); } } foreach ( array( 'oneOf', 'allOf', 'anyOf', 'prefixItems' ) as $list_key ) { if ( isset( $schema[ $list_key ] ) && is_array( $schema[ $list_key ] ) ) { foreach ( $schema[ $list_key ] as $i => $sub ) { if ( is_array( $sub ) ) { $schema[ $list_key ][ $i ] = openstation_ai_normalize_response_schema( $sub ); } } } } if ( isset( $schema['not'] ) && is_array( $schema['not'] ) ) { $schema['not'] = openstation_ai_normalize_response_schema( $schema['not'] ); } return $schema; } /** * Whether a (sub)schema describes an object, and so needs the strict key. * * Covers the three ways a schema says "object": the literal type, a type * union that includes it (`array( 'object', 'null' )` — how a nullable * branch is usually written), and the untyped-but-shaped form that declares * `properties` with no `type` at all. * * @param array $schema A structured-output (sub)schema. * @return bool */ function openstation_ai_schema_is_object_node( array $schema ) { if ( isset( $schema['type'] ) ) { $types = is_array( $schema['type'] ) ? $schema['type'] : array( $schema['type'] ); return in_array( 'object', $types, true ); } return isset( $schema['properties'] ) || isset( $schema['patternProperties'] ); }