Skip to content

Commit ab6e9d8

Browse files
authored
[ML] Include the chunk text offsets in chunked inference response (#118659) (#118775)
# Conflicts: # x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/elastic/ElasticInferenceService.java # x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/huggingface/elser/HuggingFaceElserService.java # x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/services/googleaistudio/GoogleAiStudioServiceTests.java
1 parent 4da564a commit ab6e9d8

File tree

56 files changed

+611
-1413
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+611
-1413
lines changed
Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,27 @@
1212
import org.elasticsearch.common.bytes.BytesReference;
1313
import org.elasticsearch.xcontent.XContent;
1414

15+
import java.io.IOException;
1516
import java.util.Iterator;
1617

17-
public interface ChunkedInferenceServiceResults extends InferenceServiceResults {
18+
public interface ChunkedInference {
1819

1920
/**
2021
* Implementations of this function serialize their embeddings to {@link BytesReference} for storage in semantic text fields.
21-
* The iterator iterates over all the chunks stored in the {@link ChunkedInferenceServiceResults}.
2222
*
2323
* @param xcontent provided by the SemanticTextField
2424
* @return an iterator of the serialized {@link Chunk} which includes the matched text (input) and bytes reference (output/embedding).
2525
*/
26-
Iterator<Chunk> chunksAsMatchedTextAndByteReference(XContent xcontent);
26+
Iterator<Chunk> chunksAsMatchedTextAndByteReference(XContent xcontent) throws IOException;
2727

2828
/**
29-
* A chunk of inference results containing matched text and the bytes reference.
29+
* A chunk of inference results containing matched text, the substring location
30+
* in the original text and the bytes reference.
3031
* @param matchedText
32+
* @param textOffset
3133
* @param bytesReference
3234
*/
33-
record Chunk(String matchedText, BytesReference bytesReference) {}
35+
record Chunk(String matchedText, TextOffset textOffset, BytesReference bytesReference) {}
36+
37+
record TextOffset(int start, int end) {}
3438
}

server/src/main/java/org/elasticsearch/inference/InferenceService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ void chunkedInfer(
127127
Map<String, Object> taskSettings,
128128
InputType inputType,
129129
TimeValue timeout,
130-
ActionListener<List<ChunkedInferenceServiceResults>> listener
130+
ActionListener<List<ChunkedInference>> listener
131131
);
132132

133133
/**
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
package org.elasticsearch.xpack.core.inference.results;
9+
10+
import org.elasticsearch.common.bytes.BytesReference;
11+
import org.elasticsearch.inference.ChunkedInference;
12+
import org.elasticsearch.xcontent.XContent;
13+
import org.elasticsearch.xcontent.XContentBuilder;
14+
15+
import java.io.IOException;
16+
import java.util.ArrayList;
17+
import java.util.Iterator;
18+
import java.util.List;
19+
20+
public record ChunkedInferenceEmbeddingByte(List<ChunkedInferenceEmbeddingByte.ByteEmbeddingChunk> chunks) implements ChunkedInference {
21+
22+
@Override
23+
public Iterator<Chunk> chunksAsMatchedTextAndByteReference(XContent xcontent) throws IOException {
24+
var asChunk = new ArrayList<Chunk>();
25+
for (var chunk : chunks) {
26+
asChunk.add(new Chunk(chunk.matchedText(), chunk.offset(), toBytesReference(xcontent, chunk.embedding())));
27+
}
28+
return asChunk.iterator();
29+
}
30+
31+
/**
32+
* Serialises the {@code value} array, according to the provided {@link XContent}, into a {@link BytesReference}.
33+
*/
34+
private static BytesReference toBytesReference(XContent xContent, byte[] value) throws IOException {
35+
XContentBuilder builder = XContentBuilder.builder(xContent);
36+
builder.startArray();
37+
for (byte v : value) {
38+
builder.value(v);
39+
}
40+
builder.endArray();
41+
return BytesReference.bytes(builder);
42+
}
43+
44+
public record ByteEmbeddingChunk(byte[] embedding, String matchedText, TextOffset offset) {}
45+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
package org.elasticsearch.xpack.core.inference.results;
9+
10+
import org.elasticsearch.common.bytes.BytesReference;
11+
import org.elasticsearch.inference.ChunkedInference;
12+
import org.elasticsearch.xcontent.XContent;
13+
import org.elasticsearch.xcontent.XContentBuilder;
14+
15+
import java.io.IOException;
16+
import java.util.ArrayList;
17+
import java.util.Iterator;
18+
import java.util.List;
19+
20+
public record ChunkedInferenceEmbeddingFloat(List<FloatEmbeddingChunk> chunks) implements ChunkedInference {
21+
22+
@Override
23+
public Iterator<Chunk> chunksAsMatchedTextAndByteReference(XContent xcontent) throws IOException {
24+
var asChunk = new ArrayList<Chunk>();
25+
for (var chunk : chunks) {
26+
asChunk.add(new Chunk(chunk.matchedText(), chunk.offset(), toBytesReference(xcontent, chunk.embedding())));
27+
}
28+
return asChunk.iterator();
29+
}
30+
31+
/**
32+
* Serialises the {@code value} array, according to the provided {@link XContent}, into a {@link BytesReference}.
33+
*/
34+
private static BytesReference toBytesReference(XContent xContent, float[] value) throws IOException {
35+
XContentBuilder b = XContentBuilder.builder(xContent);
36+
b.startArray();
37+
for (float v : value) {
38+
b.value(v);
39+
}
40+
b.endArray();
41+
return BytesReference.bytes(b);
42+
}
43+
44+
public record FloatEmbeddingChunk(float[] embedding, String matchedText, TextOffset offset) {}
45+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
package org.elasticsearch.xpack.core.inference.results;
9+
10+
import org.elasticsearch.common.bytes.BytesReference;
11+
import org.elasticsearch.inference.ChunkedInference;
12+
import org.elasticsearch.xcontent.ToXContent;
13+
import org.elasticsearch.xcontent.XContent;
14+
import org.elasticsearch.xcontent.XContentBuilder;
15+
import org.elasticsearch.xpack.core.ml.search.WeightedToken;
16+
17+
import java.io.IOException;
18+
import java.util.ArrayList;
19+
import java.util.Iterator;
20+
import java.util.List;
21+
22+
import static org.elasticsearch.xpack.core.inference.results.TextEmbeddingUtils.validateInputSizeAgainstEmbeddings;
23+
24+
public record ChunkedInferenceEmbeddingSparse(List<SparseEmbeddingChunk> chunks) implements ChunkedInference {
25+
26+
public static List<ChunkedInference> listOf(List<String> inputs, SparseEmbeddingResults sparseEmbeddingResults) {
27+
validateInputSizeAgainstEmbeddings(inputs, sparseEmbeddingResults.embeddings().size());
28+
29+
var results = new ArrayList<ChunkedInference>(inputs.size());
30+
for (int i = 0; i < inputs.size(); i++) {
31+
results.add(
32+
new ChunkedInferenceEmbeddingSparse(
33+
List.of(
34+
new SparseEmbeddingChunk(
35+
sparseEmbeddingResults.embeddings().get(i).tokens(),
36+
inputs.get(i),
37+
new TextOffset(0, inputs.get(i).length())
38+
)
39+
)
40+
)
41+
);
42+
}
43+
44+
return results;
45+
}
46+
47+
@Override
48+
public Iterator<Chunk> chunksAsMatchedTextAndByteReference(XContent xcontent) throws IOException {
49+
var asChunk = new ArrayList<Chunk>();
50+
for (var chunk : chunks) {
51+
asChunk.add(new Chunk(chunk.matchedText(), chunk.offset(), toBytesReference(xcontent, chunk.weightedTokens())));
52+
}
53+
return asChunk.iterator();
54+
}
55+
56+
private static BytesReference toBytesReference(XContent xContent, List<WeightedToken> tokens) throws IOException {
57+
XContentBuilder b = XContentBuilder.builder(xContent);
58+
b.startObject();
59+
for (var weightedToken : tokens) {
60+
weightedToken.toXContent(b, ToXContent.EMPTY_PARAMS);
61+
}
62+
b.endObject();
63+
return BytesReference.bytes(b);
64+
}
65+
66+
public record SparseEmbeddingChunk(List<WeightedToken> weightedTokens, String matchedText, TextOffset offset) {}
67+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
package org.elasticsearch.xpack.core.inference.results;
9+
10+
import org.elasticsearch.common.bytes.BytesArray;
11+
import org.elasticsearch.inference.ChunkedInference;
12+
import org.elasticsearch.xcontent.XContent;
13+
14+
import java.util.Iterator;
15+
import java.util.stream.Stream;
16+
17+
public record ChunkedInferenceError(Exception exception) implements ChunkedInference {
18+
19+
@Override
20+
public Iterator<Chunk> chunksAsMatchedTextAndByteReference(XContent xcontent) {
21+
return Stream.of(exception).map(e -> new Chunk(e.getMessage(), new TextOffset(0, 0), BytesArray.EMPTY)).iterator();
22+
}
23+
}

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/inference/results/ErrorChunkedInferenceResults.java

Lines changed: 0 additions & 106 deletions
This file was deleted.

0 commit comments

Comments
 (0)