99
1010package org .elasticsearch .xpack .inference .external .response .jinaai ;
1111
12+ import org .elasticsearch .common .Strings ;
1213import org .elasticsearch .common .xcontent .LoggingDeprecationHandler ;
14+ import org .elasticsearch .core .CheckedFunction ;
15+ import org .elasticsearch .inference .InferenceServiceResults ;
1316import org .elasticsearch .xcontent .XContentFactory ;
1417import org .elasticsearch .xcontent .XContentParser ;
1518import org .elasticsearch .xcontent .XContentParserConfiguration ;
1619import org .elasticsearch .xcontent .XContentType ;
20+ import org .elasticsearch .xpack .core .inference .results .InferenceByteEmbedding ;
21+ import org .elasticsearch .xpack .core .inference .results .InferenceTextEmbeddingBitResults ;
1722import org .elasticsearch .xpack .core .inference .results .InferenceTextEmbeddingFloatResults ;
1823import org .elasticsearch .xpack .inference .external .http .HttpResult ;
1924import org .elasticsearch .xpack .inference .external .request .Request ;
25+ import org .elasticsearch .xpack .inference .external .request .jinaai .JinaAIEmbeddingsRequest ;
2026import org .elasticsearch .xpack .inference .external .response .XContentUtils ;
27+ import org .elasticsearch .xpack .inference .services .jinaai .embeddings .JinaAIEmbeddingType ;
2128
2229import java .io .IOException ;
30+ import java .util .Arrays ;
2331import java .util .List ;
32+ import java .util .Map ;
2433
2534import static org .elasticsearch .common .xcontent .XContentParserUtils .ensureExpectedToken ;
2635import static org .elasticsearch .common .xcontent .XContentParserUtils .parseList ;
2736import static org .elasticsearch .xpack .inference .external .response .XContentUtils .consumeUntilObjectEnd ;
2837import static org .elasticsearch .xpack .inference .external .response .XContentUtils .moveToFirstToken ;
2938import static org .elasticsearch .xpack .inference .external .response .XContentUtils .positionParserAtTokenAfterField ;
39+ import static org .elasticsearch .xpack .inference .services .jinaai .embeddings .JinaAIEmbeddingType .toLowerCase ;
3040
3141public class JinaAIEmbeddingsResponseEntity {
3242 private static final String FAILED_TO_FIND_FIELD_TEMPLATE = "Failed to find required field [%s] in JinaAI embeddings response" ;
3343
44+ private static final Map <String , CheckedFunction <XContentParser , InferenceServiceResults , IOException >> EMBEDDING_PARSERS = Map .of (
45+ toLowerCase (JinaAIEmbeddingType .FLOAT ),
46+ JinaAIEmbeddingsResponseEntity ::parseFloatDataObject ,
47+ toLowerCase (JinaAIEmbeddingType .BIT ),
48+ JinaAIEmbeddingsResponseEntity ::parseBitDataObject ,
49+ toLowerCase (JinaAIEmbeddingType .BINARY ),
50+ JinaAIEmbeddingsResponseEntity ::parseBitDataObject
51+ );
52+ private static final String VALID_EMBEDDING_TYPES_STRING = supportedEmbeddingTypes ();
53+
54+ private static String supportedEmbeddingTypes () {
55+ var validTypes = EMBEDDING_PARSERS .keySet ().toArray (String []::new );
56+ Arrays .sort (validTypes );
57+ return String .join (", " , validTypes );
58+ }
59+
3460 /**
3561 * Parses the JinaAI json response.
3662 * For a request like:
@@ -73,8 +99,21 @@ public class JinaAIEmbeddingsResponseEntity {
7399 * </code>
74100 * </pre>
75101 */
76- public static InferenceTextEmbeddingFloatResults fromResponse (Request request , HttpResult response ) throws IOException {
102+ public static InferenceServiceResults fromResponse (Request request , HttpResult response ) throws IOException {
103+ // embeddings type is not specified anywhere in the response so grab it from the request
104+ JinaAIEmbeddingsRequest embeddingsRequest = (JinaAIEmbeddingsRequest ) request ;
105+ var embeddingType = embeddingsRequest .getEmbeddingType ().toString ();
77106 var parserConfig = XContentParserConfiguration .EMPTY .withDeprecationHandler (LoggingDeprecationHandler .INSTANCE );
107+ var embeddingValueParser = EMBEDDING_PARSERS .get (embeddingType );
108+
109+ if (embeddingValueParser == null ) {
110+ throw new IllegalStateException (
111+ Strings .format (
112+ "Failed to find a supported embedding type for in the Jina AI embeddings response. Supported types are [%s]" ,
113+ VALID_EMBEDDING_TYPES_STRING
114+ )
115+ );
116+ }
78117
79118 try (XContentParser jsonParser = XContentFactory .xContent (XContentType .JSON ).createParser (parserConfig , response .body ())) {
80119 moveToFirstToken (jsonParser );
@@ -84,27 +123,64 @@ public static InferenceTextEmbeddingFloatResults fromResponse(Request request, H
84123
85124 positionParserAtTokenAfterField (jsonParser , "data" , FAILED_TO_FIND_FIELD_TEMPLATE );
86125
87- List <InferenceTextEmbeddingFloatResults .InferenceFloatEmbedding > embeddingList = parseList (
88- jsonParser ,
89- JinaAIEmbeddingsResponseEntity ::parseEmbeddingObject
90- );
91-
92- return new InferenceTextEmbeddingFloatResults (embeddingList );
126+ return embeddingValueParser .apply (jsonParser );
93127 }
94128 }
95129
96- private static InferenceTextEmbeddingFloatResults .InferenceFloatEmbedding parseEmbeddingObject (XContentParser parser )
130+ private static InferenceTextEmbeddingFloatResults parseFloatDataObject (XContentParser jsonParser ) throws IOException {
131+ List <InferenceTextEmbeddingFloatResults .InferenceFloatEmbedding > embeddingList = parseList (
132+ jsonParser ,
133+ JinaAIEmbeddingsResponseEntity ::parseFloatEmbeddingObject
134+ );
135+
136+ return new InferenceTextEmbeddingFloatResults (embeddingList );
137+ }
138+
139+ private static InferenceTextEmbeddingFloatResults .InferenceFloatEmbedding parseFloatEmbeddingObject (XContentParser parser )
97140 throws IOException {
98141 ensureExpectedToken (XContentParser .Token .START_OBJECT , parser .currentToken (), parser );
99142
100143 positionParserAtTokenAfterField (parser , "embedding" , FAILED_TO_FIND_FIELD_TEMPLATE );
101144
102- List < Float > embeddingValuesList = parseList (parser , XContentUtils ::parseFloat );
145+ var embeddingValuesList = parseList (parser , XContentUtils ::parseFloat );
103146 // parse and discard the rest of the object
104147 consumeUntilObjectEnd (parser );
105148
106149 return InferenceTextEmbeddingFloatResults .InferenceFloatEmbedding .of (embeddingValuesList );
107150 }
108151
152+ private static InferenceTextEmbeddingBitResults parseBitDataObject (XContentParser jsonParser ) throws IOException {
153+ List <InferenceByteEmbedding > embeddingList = parseList (jsonParser , JinaAIEmbeddingsResponseEntity ::parseBitEmbeddingObject );
154+
155+ return new InferenceTextEmbeddingBitResults (embeddingList );
156+ }
157+
158+ private static InferenceByteEmbedding parseBitEmbeddingObject (XContentParser parser ) throws IOException {
159+ ensureExpectedToken (XContentParser .Token .START_OBJECT , parser .currentToken (), parser );
160+
161+ positionParserAtTokenAfterField (parser , "embedding" , FAILED_TO_FIND_FIELD_TEMPLATE );
162+
163+ var embeddingList = parseList (parser , JinaAIEmbeddingsResponseEntity ::parseEmbeddingInt8Entry );
164+ // parse and discard the rest of the object
165+ consumeUntilObjectEnd (parser );
166+
167+ return InferenceByteEmbedding .of (embeddingList );
168+ }
169+
170+ private static Byte parseEmbeddingInt8Entry (XContentParser parser ) throws IOException {
171+ XContentParser .Token token = parser .currentToken ();
172+ ensureExpectedToken (XContentParser .Token .VALUE_NUMBER , token , parser );
173+ var parsedByte = parser .shortValue ();
174+ checkByteBounds (parsedByte );
175+
176+ return (byte ) parsedByte ;
177+ }
178+
179+ private static void checkByteBounds (short value ) {
180+ if (value < Byte .MIN_VALUE || value > Byte .MAX_VALUE ) {
181+ throw new IllegalArgumentException ("Value [" + value + "] is out of range for a byte" );
182+ }
183+ }
184+
109185 private JinaAIEmbeddingsResponseEntity () {}
110186}
0 commit comments