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 .BINARY ),
48+ JinaAIEmbeddingsResponseEntity ::parseBitDataObject
49+ );
50+ private static final String VALID_EMBEDDING_TYPES_STRING = supportedEmbeddingTypes ();
51+
52+ private static String supportedEmbeddingTypes () {
53+ var validTypes = EMBEDDING_PARSERS .keySet ().toArray (String []::new );
54+ Arrays .sort (validTypes );
55+ return String .join (", " , validTypes );
56+ }
57+
3458 /**
3559 * Parses the JinaAI json response.
3660 * For a request like:
@@ -73,8 +97,21 @@ public class JinaAIEmbeddingsResponseEntity {
7397 * </code>
7498 * </pre>
7599 */
76- public static InferenceTextEmbeddingFloatResults fromResponse (Request request , HttpResult response ) throws IOException {
100+ public static InferenceServiceResults fromResponse (Request request , HttpResult response ) throws IOException {
101+ // embeddings type is not specified anywhere in the response so grab it from the request
102+ JinaAIEmbeddingsRequest embeddingsRequest = (JinaAIEmbeddingsRequest ) request ;
103+ var embeddingType = embeddingsRequest .getEmbeddingType ().toString ();
77104 var parserConfig = XContentParserConfiguration .EMPTY .withDeprecationHandler (LoggingDeprecationHandler .INSTANCE );
105+ var embeddingValueParser = EMBEDDING_PARSERS .get (embeddingType );
106+
107+ if (embeddingValueParser == null ) {
108+ throw new IllegalStateException (
109+ Strings .format (
110+ "Failed to find a supported embedding type for in the Jina AI embeddings response. Supported types are [%s]" ,
111+ VALID_EMBEDDING_TYPES_STRING
112+ )
113+ );
114+ }
78115
79116 try (XContentParser jsonParser = XContentFactory .xContent (XContentType .JSON ).createParser (parserConfig , response .body ())) {
80117 moveToFirstToken (jsonParser );
@@ -84,27 +121,64 @@ public static InferenceTextEmbeddingFloatResults fromResponse(Request request, H
84121
85122 positionParserAtTokenAfterField (jsonParser , "data" , FAILED_TO_FIND_FIELD_TEMPLATE );
86123
87- List <InferenceTextEmbeddingFloatResults .InferenceFloatEmbedding > embeddingList = parseList (
88- jsonParser ,
89- JinaAIEmbeddingsResponseEntity ::parseEmbeddingObject
90- );
91-
92- return new InferenceTextEmbeddingFloatResults (embeddingList );
124+ return embeddingValueParser .apply (jsonParser );
93125 }
94126 }
95127
96- private static InferenceTextEmbeddingFloatResults .InferenceFloatEmbedding parseEmbeddingObject (XContentParser parser )
128+ private static InferenceTextEmbeddingFloatResults parseFloatDataObject (XContentParser jsonParser ) throws IOException {
129+ List <InferenceTextEmbeddingFloatResults .InferenceFloatEmbedding > embeddingList = parseList (
130+ jsonParser ,
131+ JinaAIEmbeddingsResponseEntity ::parseFloatEmbeddingObject
132+ );
133+
134+ return new InferenceTextEmbeddingFloatResults (embeddingList );
135+ }
136+
137+ private static InferenceTextEmbeddingFloatResults .InferenceFloatEmbedding parseFloatEmbeddingObject (XContentParser parser )
97138 throws IOException {
98139 ensureExpectedToken (XContentParser .Token .START_OBJECT , parser .currentToken (), parser );
99140
100141 positionParserAtTokenAfterField (parser , "embedding" , FAILED_TO_FIND_FIELD_TEMPLATE );
101142
102- List < Float > embeddingValuesList = parseList (parser , XContentUtils ::parseFloat );
143+ var embeddingValuesList = parseList (parser , XContentUtils ::parseFloat );
103144 // parse and discard the rest of the object
104145 consumeUntilObjectEnd (parser );
105146
106147 return InferenceTextEmbeddingFloatResults .InferenceFloatEmbedding .of (embeddingValuesList );
107148 }
108149
150+ private static InferenceTextEmbeddingBitResults parseBitDataObject (XContentParser jsonParser ) throws IOException {
151+ List <InferenceByteEmbedding > embeddingList = parseList (jsonParser , JinaAIEmbeddingsResponseEntity ::parseBitEmbeddingObject );
152+
153+ return new InferenceTextEmbeddingBitResults (embeddingList );
154+ }
155+
156+ private static InferenceByteEmbedding parseBitEmbeddingObject (XContentParser parser ) throws IOException {
157+ ensureExpectedToken (XContentParser .Token .START_OBJECT , parser .currentToken (), parser );
158+
159+ positionParserAtTokenAfterField (parser , "embedding" , FAILED_TO_FIND_FIELD_TEMPLATE );
160+
161+ var embeddingList = parseList (parser , JinaAIEmbeddingsResponseEntity ::parseEmbeddingInt8Entry );
162+ // parse and discard the rest of the object
163+ consumeUntilObjectEnd (parser );
164+
165+ return InferenceByteEmbedding .of (embeddingList );
166+ }
167+
168+ private static Byte parseEmbeddingInt8Entry (XContentParser parser ) throws IOException {
169+ XContentParser .Token token = parser .currentToken ();
170+ ensureExpectedToken (XContentParser .Token .VALUE_NUMBER , token , parser );
171+ var parsedByte = parser .shortValue ();
172+ checkByteBounds (parsedByte );
173+
174+ return (byte ) parsedByte ;
175+ }
176+
177+ private static void checkByteBounds (short value ) {
178+ if (value < Byte .MIN_VALUE || value > Byte .MAX_VALUE ) {
179+ throw new IllegalArgumentException ("Value [" + value + "] is out of range for a byte" );
180+ }
181+ }
182+
109183 private JinaAIEmbeddingsResponseEntity () {}
110184}
0 commit comments