Skip to content

Commit 90d2320

Browse files
Adding list entry index to error message and field names
1 parent fa1dd88 commit 90d2320

File tree

8 files changed

+261
-16
lines changed

8 files changed

+261
-16
lines changed

x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/custom/response/BaseCustomResponseParser.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,12 @@ static <T> List<T> castList(List<?> items, BiFunction<Object, String, T> convert
119119
validateNonNull(items, fieldName);
120120

121121
List<T> resultList = new ArrayList<>();
122-
for (var obj : items) {
123-
resultList.add(converter.apply(obj, fieldName));
122+
for (int i = 0; i < items.size(); i++) {
123+
try {
124+
resultList.add(converter.apply(items.get(i), fieldName));
125+
} catch (Exception e) {
126+
throw new IllegalStateException(Strings.format("Failed to parse list entry [%d], error: %s", i, e.getMessage()), e);
127+
}
124128
}
125129

126130
return resultList;

x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/custom/response/RerankResponseParser.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,8 @@ public String getWriteableName() {
111111
@Override
112112
public RankedDocsResults transform(Map<String, Object> map) {
113113
var scores = extractScores(map);
114-
List<Integer> indices = extractIndices(map);
115-
List<String> documents = extractDocuments(map);
114+
var indices = extractIndices(map);
115+
var documents = extractDocuments(map);
116116

117117
if (indices != null && indices.size() != scores.size()) {
118118
throw new IllegalStateException(

x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/custom/response/TextEmbeddingResponseParser.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
package org.elasticsearch.xpack.inference.services.custom.response;
99

10+
import org.elasticsearch.common.Strings;
1011
import org.elasticsearch.common.ValidationException;
1112
import org.elasticsearch.common.io.stream.StreamInput;
1213
import org.elasticsearch.common.io.stream.StreamOutput;
@@ -85,9 +86,17 @@ protected TextEmbeddingFloatResults transform(Map<String, Object> map) {
8586

8687
var embeddings = new ArrayList<TextEmbeddingFloatResults.Embedding>(mapResultsList.size());
8788

88-
for (var entry : mapResultsList) {
89-
var embeddingsAsListFloats = convertToListOfFloats(entry, extractedResult.getArrayFieldName(1));
90-
embeddings.add(TextEmbeddingFloatResults.Embedding.of(embeddingsAsListFloats));
89+
for (int i = 0; i < mapResultsList.size(); i++) {
90+
try {
91+
var entry = mapResultsList.get(i);
92+
var embeddingsAsListFloats = convertToListOfFloats(entry, extractedResult.getArrayFieldName(1));
93+
embeddings.add(TextEmbeddingFloatResults.Embedding.of(embeddingsAsListFloats));
94+
} catch (Exception e) {
95+
throw new IllegalArgumentException(
96+
Strings.format("Failed to parse text embedding entry [%d], error: %s", i, e.getMessage()),
97+
e
98+
);
99+
}
91100
}
92101

93102
return new TextEmbeddingFloatResults(embeddings);

x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/services/custom/response/BaseCustomResponseParserTests.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,11 @@ public void testValidateList_ReturnsList() {
4141
public void testConvertToListOfFloats_ThrowsException_WhenAnItemInTheListIsNotANumber() {
4242
var list = List.of(1, "hello");
4343

44-
var exception = expectThrows(IllegalArgumentException.class, () -> convertToListOfFloats(list, "field"));
45-
assertThat(exception.getMessage(), is("Unable to convert field [field] of type [String] to Number"));
44+
var exception = expectThrows(IllegalStateException.class, () -> convertToListOfFloats(list, "field"));
45+
assertThat(
46+
exception.getMessage(),
47+
is("Failed to parse list entry [1], error: Unable to convert field [field] of type [String] to Number")
48+
);
4649
}
4750

4851
public void testConvertToListOfFloats_ReturnsList() {
@@ -60,11 +63,11 @@ public void testCastList() {
6063
public void testCastList_ThrowsException() {
6164
var list = List.of("abc");
6265

63-
var exception = expectThrows(IllegalArgumentException.class, () -> castList(list, (obj, fieldName) -> {
66+
var exception = expectThrows(IllegalStateException.class, () -> castList(list, (obj, fieldName) -> {
6467
throw new IllegalArgumentException("failed");
6568
}, "field"));
6669

67-
assertThat(exception.getMessage(), is("failed"));
70+
assertThat(exception.getMessage(), is("Failed to parse list entry [0], error: failed"));
6871
}
6972

7073
public void testValidateMap() {

x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/services/custom/response/CompletionResponseParserTests.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,32 @@ public void testParse_ThrowsException_WhenExtractedField_IsNotAList() {
230230
);
231231
}
232232

233+
public void testParse_ThrowsException_WhenExtractedField_IsNotListOfStrings() {
234+
String responseJson = """
235+
{
236+
"request_id": "450fcb80-f796-****-8d69-e1e86d29aa9f",
237+
"latency": 564.903929,
238+
"result": ["string", true],
239+
"usage": {
240+
"output_tokens": 6320,
241+
"input_tokens": 35,
242+
"total_tokens": 6355
243+
}
244+
}
245+
""";
246+
247+
var parser = new CompletionResponseParser("$.result");
248+
var exception = expectThrows(
249+
IllegalStateException.class,
250+
() -> parser.parse(new HttpResult(mock(HttpResponse.class), responseJson.getBytes(StandardCharsets.UTF_8)))
251+
);
252+
253+
assertThat(
254+
exception.getMessage(),
255+
is("Failed to parse list entry [1], error: Unable to convert field [$.result] of type [Boolean] to [String]")
256+
);
257+
}
258+
233259
public void testParse_ThrowsException_WhenExtractedField_IsNotAListOrString() {
234260
String responseJson = """
235261
{

x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/services/custom/response/RerankResponseParserTests.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,10 @@ public void testParse_ThrowsException_WhenIndex_IsInvalid() {
198198

199199
assertThat(
200200
exception.getMessage(),
201-
is("Failed to parse rerank indices, error: Unable to convert field [result.scores] of type [String] to Number")
201+
is(
202+
"Failed to parse rerank indices, error: Failed to parse list entry [0], "
203+
+ "error: Unable to convert field [result.scores] of type [String] to Number"
204+
)
202205
);
203206
}
204207

@@ -234,7 +237,10 @@ public void testParse_ThrowsException_WhenScore_IsInvalid() {
234237

235238
assertThat(
236239
exception.getMessage(),
237-
is("Failed to parse rerank scores, error: Unable to convert field [result.scores] of type [Boolean] to Number")
240+
is(
241+
"Failed to parse rerank scores, error: Failed to parse list entry [0], "
242+
+ "error: Unable to convert field [result.scores] of type [Boolean] to Number"
243+
)
238244
);
239245
}
240246

@@ -272,7 +278,10 @@ public void testParse_ThrowsException_WhenDocument_IsInvalid() {
272278

273279
assertThat(
274280
exception.getMessage(),
275-
is("Failed to parse rerank documents, error: Unable to convert field [result.scores] of type [Integer] to String")
281+
is(
282+
"Failed to parse rerank documents, error: Failed to parse list entry [0], error: "
283+
+ "Unable to convert field [result.scores] of type [Integer] to [String]"
284+
)
276285
);
277286
}
278287

x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/services/custom/response/SparseEmbeddingResponseParserTests.java

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,150 @@ public void testParse() throws IOException {
143143
)
144144
)
145145
);
146+
}
147+
148+
public void testParse_ThrowsException_WhenTheTokenField_IsNotAnArray() {
149+
String responseJson = """
150+
{
151+
"request_id": "75C50B5B-E79E-4930-****-F48DBB392231",
152+
"latency": 22,
153+
"usage": {
154+
"token_count": 11
155+
},
156+
"result": {
157+
"sparse_embeddings": [
158+
{
159+
"index": 0,
160+
"tokenId": 6,
161+
"weight": [0.101]
162+
}
163+
]
164+
}
165+
}
166+
""";
167+
168+
var parser = new SparseEmbeddingResponseParser("$.result.sparse_embeddings[*].tokenId", "$.result.sparse_embeddings[*].weight");
169+
170+
var exception = expectThrows(
171+
IllegalStateException.class,
172+
() -> parser.parse(new HttpResult(mock(HttpResponse.class), responseJson.getBytes(StandardCharsets.UTF_8)))
173+
);
174+
175+
assertThat(
176+
exception.getMessage(),
177+
is(
178+
"Failed to parse sparse embedding entry [0], error: Extracted field [result.sparse_embeddings.tokenId] "
179+
+ "is an invalid type, expected a list but received [Integer]"
180+
)
181+
);
182+
}
146183

184+
public void testParse_ThrowsException_WhenTheTokenArraySize_AndWeightArraySize_AreDifferent() {
185+
String responseJson = """
186+
{
187+
"request_id": "75C50B5B-E79E-4930-****-F48DBB392231",
188+
"latency": 22,
189+
"usage": {
190+
"token_count": 11
191+
},
192+
"result": {
193+
"sparse_embeddings": [
194+
{
195+
"index": 0,
196+
"tokenId": [6, 7],
197+
"weight": [0.101]
198+
}
199+
]
200+
}
201+
}
202+
""";
203+
204+
var parser = new SparseEmbeddingResponseParser("$.result.sparse_embeddings[*].tokenId", "$.result.sparse_embeddings[*].weight");
205+
206+
var exception = expectThrows(
207+
IllegalStateException.class,
208+
() -> parser.parse(new HttpResult(mock(HttpResponse.class), responseJson.getBytes(StandardCharsets.UTF_8)))
209+
);
210+
211+
assertThat(
212+
exception.getMessage(),
213+
is(
214+
"Failed to parse sparse embedding entry [0], error: The extracted tokens list is size [2] "
215+
+ "but the weights list is size [1]. The list sizes must be equal."
216+
)
217+
);
218+
}
219+
220+
public void testParse_ThrowsException_WhenTheWeightValue_IsNotAFloat() {
221+
String responseJson = """
222+
{
223+
"request_id": "75C50B5B-E79E-4930-****-F48DBB392231",
224+
"latency": 22,
225+
"usage": {
226+
"token_count": 11
227+
},
228+
"result": {
229+
"sparse_embeddings": [
230+
{
231+
"index": 0,
232+
"tokenId": [6],
233+
"weight": [true]
234+
}
235+
]
236+
}
237+
}
238+
""";
239+
240+
var parser = new SparseEmbeddingResponseParser("$.result.sparse_embeddings[*].tokenId", "$.result.sparse_embeddings[*].weight");
241+
242+
var exception = expectThrows(
243+
IllegalStateException.class,
244+
() -> parser.parse(new HttpResult(mock(HttpResponse.class), responseJson.getBytes(StandardCharsets.UTF_8)))
245+
);
246+
247+
assertThat(
248+
exception.getMessage(),
249+
is(
250+
"Failed to parse sparse embedding entry [0], error: Failed to parse weight item: "
251+
+ "[0] of array, error: Unable to convert field [result.sparse_embeddings.weight] of type [Boolean] to Number"
252+
)
253+
);
254+
}
255+
256+
public void testParse_ThrowsException_WhenTheWeightField_IsNotAnArray() {
257+
String responseJson = """
258+
{
259+
"request_id": "75C50B5B-E79E-4930-****-F48DBB392231",
260+
"latency": 22,
261+
"usage": {
262+
"token_count": 11
263+
},
264+
"result": {
265+
"sparse_embeddings": [
266+
{
267+
"index": 0,
268+
"tokenId": [6],
269+
"weight": 0.101
270+
}
271+
]
272+
}
273+
}
274+
""";
275+
276+
var parser = new SparseEmbeddingResponseParser("$.result.sparse_embeddings[*].tokenId", "$.result.sparse_embeddings[*].weight");
277+
278+
var exception = expectThrows(
279+
IllegalStateException.class,
280+
() -> parser.parse(new HttpResult(mock(HttpResponse.class), responseJson.getBytes(StandardCharsets.UTF_8)))
281+
);
282+
283+
assertThat(
284+
exception.getMessage(),
285+
is(
286+
"Failed to parse sparse embedding entry [0], error: Extracted field [result.sparse_embeddings.weight] "
287+
+ "is an invalid type, expected a list but received [Double]"
288+
)
289+
);
147290
}
148291

149292
public void testParse_ThrowsException_WhenExtractedField_IsNotFormattedCorrectly() {

x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/services/custom/response/TextEmbeddingResponseParserTests.java

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,52 @@ public void testParse_MultipleEmbeddings() throws IOException {
162162
);
163163
}
164164

165-
public void testParse_ThrowsException_WhenExtractedField_IsNotAList() throws IOException {
165+
public void testParse_ThrowsException_WhenExtractedField_IsNotAListOfFloats() {
166+
String responseJson = """
167+
{
168+
"object": "list",
169+
"data": [
170+
{
171+
"object": "embedding",
172+
"index": 0,
173+
"embedding": [
174+
1,
175+
-0.015288644
176+
]
177+
},
178+
{
179+
"object": "embedding",
180+
"index": 0,
181+
"embedding": [
182+
true,
183+
-0.015288644
184+
]
185+
}
186+
],
187+
"model": "text-embedding-ada-002-v2",
188+
"usage": {
189+
"prompt_tokens": 8,
190+
"total_tokens": 8
191+
}
192+
}
193+
""";
194+
195+
var parser = new TextEmbeddingResponseParser("$.data[*].embedding");
196+
var exception = expectThrows(
197+
IllegalArgumentException.class,
198+
() -> parser.parse(new HttpResult(mock(HttpResponse.class), responseJson.getBytes(StandardCharsets.UTF_8)))
199+
);
200+
201+
assertThat(
202+
exception.getMessage(),
203+
is(
204+
"Failed to parse text embedding entry [1], error: Failed to parse list entry [0], error:"
205+
+ " Unable to convert field [data.embedding] of type [Boolean] to Number"
206+
)
207+
);
208+
}
209+
210+
public void testParse_ThrowsException_WhenExtractedField_IsNotAList() {
166211
String responseJson = """
167212
{
168213
"object": "list",
@@ -187,7 +232,13 @@ public void testParse_ThrowsException_WhenExtractedField_IsNotAList() throws IOE
187232
() -> parser.parse(new HttpResult(mock(HttpResponse.class), responseJson.getBytes(StandardCharsets.UTF_8)))
188233
);
189234

190-
assertThat(exception.getMessage(), is("Extracted field is an invalid type, expected a list but received [Integer]"));
235+
assertThat(
236+
exception.getMessage(),
237+
is(
238+
"Failed to parse text embedding entry [0], error: Extracted field [data.embedding] "
239+
+ "is an invalid type, expected a list but received [Integer]"
240+
)
241+
);
191242
}
192243

193244
@Override

0 commit comments

Comments
 (0)