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 .TextEmbeddingBitResults ;
21+ import org .elasticsearch .xpack .core .inference .results .TextEmbeddingByteResults ;
1722import org .elasticsearch .xpack .core .inference .results .TextEmbeddingFloatResults ;
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 TextEmbeddingFloatResults 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,26 +123,66 @@ public static TextEmbeddingFloatResults fromResponse(Request request, HttpResult
84123
85124 positionParserAtTokenAfterField (jsonParser , "data" , FAILED_TO_FIND_FIELD_TEMPLATE );
86125
87- List <TextEmbeddingFloatResults .Embedding > embeddingList = parseList (
88- jsonParser ,
89- JinaAIEmbeddingsResponseEntity ::parseEmbeddingObject
90- );
91-
92- return new TextEmbeddingFloatResults (embeddingList );
126+ return embeddingValueParser .apply (jsonParser );
93127 }
94128 }
95129
96- private static TextEmbeddingFloatResults .Embedding parseEmbeddingObject (XContentParser parser ) throws IOException {
130+ private static InferenceServiceResults parseFloatDataObject (XContentParser jsonParser ) throws IOException {
131+ List <TextEmbeddingFloatResults .Embedding > embeddingList = parseList (
132+ jsonParser ,
133+ JinaAIEmbeddingsResponseEntity ::parseFloatEmbeddingObject
134+ );
135+
136+ return new TextEmbeddingFloatResults (embeddingList );
137+ }
138+
139+ private static TextEmbeddingFloatResults .Embedding parseFloatEmbeddingObject (XContentParser parser ) throws IOException {
97140 ensureExpectedToken (XContentParser .Token .START_OBJECT , parser .currentToken (), parser );
98141
99142 positionParserAtTokenAfterField (parser , "embedding" , FAILED_TO_FIND_FIELD_TEMPLATE );
100143
101- List < Float > embeddingValuesList = parseList (parser , XContentUtils ::parseFloat );
144+ var embeddingValuesList = parseList (parser , XContentUtils ::parseFloat );
102145 // parse and discard the rest of the object
103146 consumeUntilObjectEnd (parser );
104147
105148 return TextEmbeddingFloatResults .Embedding .of (embeddingValuesList );
106149 }
107150
151+ private static InferenceServiceResults parseBitDataObject (XContentParser jsonParser ) throws IOException {
152+ List <TextEmbeddingByteResults .Embedding > embeddingList = parseList (
153+ jsonParser ,
154+ JinaAIEmbeddingsResponseEntity ::parseBitEmbeddingObject
155+ );
156+
157+ return new TextEmbeddingBitResults (embeddingList );
158+ }
159+
160+ private static TextEmbeddingByteResults .Embedding parseBitEmbeddingObject (XContentParser parser ) throws IOException {
161+ ensureExpectedToken (XContentParser .Token .START_OBJECT , parser .currentToken (), parser );
162+
163+ positionParserAtTokenAfterField (parser , "embedding" , FAILED_TO_FIND_FIELD_TEMPLATE );
164+
165+ var embeddingList = parseList (parser , JinaAIEmbeddingsResponseEntity ::parseEmbeddingInt8Entry );
166+ // parse and discard the rest of the object
167+ consumeUntilObjectEnd (parser );
168+
169+ return TextEmbeddingByteResults .Embedding .of (embeddingList );
170+ }
171+
172+ private static Byte parseEmbeddingInt8Entry (XContentParser parser ) throws IOException {
173+ XContentParser .Token token = parser .currentToken ();
174+ ensureExpectedToken (XContentParser .Token .VALUE_NUMBER , token , parser );
175+ var parsedByte = parser .shortValue ();
176+ checkByteBounds (parsedByte );
177+
178+ return (byte ) parsedByte ;
179+ }
180+
181+ private static void checkByteBounds (short value ) {
182+ if (value < Byte .MIN_VALUE || value > Byte .MAX_VALUE ) {
183+ throw new IllegalArgumentException ("Value [" + value + "] is out of range for a byte" );
184+ }
185+ }
186+
108187 private JinaAIEmbeddingsResponseEntity () {}
109188}
0 commit comments