Skip to content

Commit 9a224b1

Browse files
Add usage stats for semantic_text fields
This change enhances usage reporting for the inference plugin to account for usage of `semantic_text` fields. Usage is bucketed by service and task_type. For each bucket this adds a `semantic_text` object that contains: - `field_count`: the number of `semantic_text` fields that use an inference endpoint of that service/task_type. - `indices_count`: the number of indices that contain at least one `semantic_field` referencing an inference endpoint of that service/task_type. - `inference_id_count`: the number of distinct inference endpoints of that service/task_type used by `semantic_text` fields. In addition, this change adds two new kinds of buckets that facalitate getting aggregate usage information: - `_all` buckets are added by `task_type`. These allow summation of usage info for all inference endpoints of a particular `task_type`. - default model buckets are added. Those contain usage info for models that are included by default.
1 parent a017664 commit 9a224b1

File tree

9 files changed

+718
-61
lines changed

9 files changed

+718
-61
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
9169000
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
security_stats_endpoint,9168000
1+
inference_telemetry_added_semantic_text_stats,9169000

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public int hashCode() {
6666
return Objects.hashCode(modelStats);
6767
}
6868

69-
Collection<ModelStats> modelStats() {
69+
public Collection<ModelStats> modelStats() {
7070
return modelStats;
7171
}
7272
}

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/inference/usage/ModelStats.java

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

88
package org.elasticsearch.xpack.core.inference.usage;
99

10+
import org.elasticsearch.TransportVersion;
1011
import org.elasticsearch.common.io.stream.StreamInput;
1112
import org.elasticsearch.common.io.stream.StreamOutput;
1213
import org.elasticsearch.common.io.stream.Writeable;
@@ -19,28 +20,39 @@
1920

2021
public class ModelStats implements ToXContentObject, Writeable {
2122

23+
static final TransportVersion INFERENCE_TELEMETRY_ADDED_SEMANTIC_TEXT_STATS = TransportVersion.fromName(
24+
"inference_telemetry_added_semantic_text_stats"
25+
);
26+
2227
private final String service;
2328
private final TaskType taskType;
2429
private long count;
30+
private final SemanticTextStats semanticTextStats;
2531

2632
public ModelStats(String service, TaskType taskType) {
27-
this(service, taskType, 0L);
33+
this(service, taskType, 0L, new SemanticTextStats());
2834
}
2935

3036
public ModelStats(String service, TaskType taskType, long count) {
37+
this(service, taskType, count, new SemanticTextStats());
38+
}
39+
40+
public ModelStats(String service, TaskType taskType, long count, SemanticTextStats semanticTextStats) {
3141
this.service = service;
3242
this.taskType = taskType;
3343
this.count = count;
34-
}
35-
36-
public ModelStats(ModelStats stats) {
37-
this(stats.service, stats.taskType, stats.count);
44+
this.semanticTextStats = Objects.requireNonNull(semanticTextStats);
3845
}
3946

4047
public ModelStats(StreamInput in) throws IOException {
4148
this.service = in.readString();
4249
this.taskType = in.readEnum(TaskType.class);
4350
this.count = in.readLong();
51+
if (in.getTransportVersion().supports(INFERENCE_TELEMETRY_ADDED_SEMANTIC_TEXT_STATS)) {
52+
this.semanticTextStats = new SemanticTextStats(in);
53+
} else {
54+
semanticTextStats = new SemanticTextStats();
55+
}
4456
}
4557

4658
public void add() {
@@ -59,6 +71,10 @@ public long count() {
5971
return count;
6072
}
6173

74+
public SemanticTextStats semanticTextStats() {
75+
return semanticTextStats;
76+
}
77+
6278
@Override
6379
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
6480
builder.startObject();
@@ -71,25 +87,34 @@ public void addXContentFragment(XContentBuilder builder, Params params) throws I
7187
builder.field("service", service);
7288
builder.field("task_type", taskType.name());
7389
builder.field("count", count);
90+
if (semanticTextStats.isEmpty() == false) {
91+
builder.field("semantic_text", semanticTextStats);
92+
}
7493
}
7594

7695
@Override
7796
public void writeTo(StreamOutput out) throws IOException {
7897
out.writeString(service);
7998
out.writeEnum(taskType);
8099
out.writeLong(count);
100+
if (out.getTransportVersion().supports(INFERENCE_TELEMETRY_ADDED_SEMANTIC_TEXT_STATS)) {
101+
semanticTextStats.writeTo(out);
102+
}
81103
}
82104

83105
@Override
84106
public boolean equals(Object o) {
85107
if (this == o) return true;
86108
if (o == null || getClass() != o.getClass()) return false;
87109
ModelStats that = (ModelStats) o;
88-
return count == that.count && Objects.equals(service, that.service) && taskType == that.taskType;
110+
return count == that.count
111+
&& Objects.equals(service, that.service)
112+
&& taskType == that.taskType
113+
&& Objects.equals(semanticTextStats, that.semanticTextStats);
89114
}
90115

91116
@Override
92117
public int hashCode() {
93-
return Objects.hash(service, taskType, count);
118+
return Objects.hash(service, taskType, count, semanticTextStats);
94119
}
95120
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
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.usage;
9+
10+
import org.elasticsearch.common.io.stream.StreamInput;
11+
import org.elasticsearch.common.io.stream.StreamOutput;
12+
import org.elasticsearch.common.io.stream.Writeable;
13+
import org.elasticsearch.xcontent.ToXContentObject;
14+
import org.elasticsearch.xcontent.XContentBuilder;
15+
16+
import java.io.IOException;
17+
import java.util.Objects;
18+
19+
public class SemanticTextStats implements ToXContentObject, Writeable {
20+
21+
private static final String FIELD_COUNT = "field_count";
22+
private static final String INDICES_COUNT = "indices_count";
23+
private static final String INFERENCE_ID_COUNT = "inference_id_count";
24+
25+
private long fieldCount;
26+
private long indicesCount;
27+
private long inferenceIdCount;
28+
29+
public SemanticTextStats() {}
30+
31+
public SemanticTextStats(long fieldCount, long indicesCount, long inferenceIdCount) {
32+
this.fieldCount = fieldCount;
33+
this.indicesCount = indicesCount;
34+
this.inferenceIdCount = inferenceIdCount;
35+
}
36+
37+
public SemanticTextStats(StreamInput in) throws IOException {
38+
fieldCount = in.readVLong();
39+
indicesCount = in.readVLong();
40+
inferenceIdCount = in.readVLong();
41+
}
42+
43+
@Override
44+
public void writeTo(StreamOutput out) throws IOException {
45+
out.writeVLong(fieldCount);
46+
out.writeVLong(indicesCount);
47+
out.writeVLong(inferenceIdCount);
48+
}
49+
50+
@Override
51+
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
52+
builder.startObject();
53+
builder.field(FIELD_COUNT, fieldCount);
54+
builder.field(INDICES_COUNT, indicesCount);
55+
builder.field(INFERENCE_ID_COUNT, inferenceIdCount);
56+
builder.endObject();
57+
return builder;
58+
}
59+
60+
@Override
61+
public boolean equals(Object o) {
62+
if (this == o) return true;
63+
if (o == null || getClass() != o.getClass()) return false;
64+
SemanticTextStats that = (SemanticTextStats) o;
65+
return fieldCount == that.fieldCount && indicesCount == that.indicesCount && inferenceIdCount == that.inferenceIdCount;
66+
}
67+
68+
@Override
69+
public int hashCode() {
70+
return Objects.hash(fieldCount, indicesCount, inferenceIdCount);
71+
}
72+
73+
public long getFieldCount() {
74+
return fieldCount;
75+
}
76+
77+
public long getIndicesCount() {
78+
return indicesCount;
79+
}
80+
81+
public long getInferenceIdCount() {
82+
return inferenceIdCount;
83+
}
84+
85+
public void addFieldCount(long fieldCount) {
86+
this.fieldCount += fieldCount;
87+
}
88+
89+
public void incIndicesCount() {
90+
this.indicesCount++;
91+
}
92+
93+
public void setInferenceIdCount(long inferenceIdCount) {
94+
this.inferenceIdCount = inferenceIdCount;
95+
}
96+
97+
public boolean isEmpty() {
98+
return fieldCount == 0 && indicesCount == 0 && inferenceIdCount == 0;
99+
}
100+
}

x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/inference/usage/ModelStatsTests.java

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,17 @@
77

88
package org.elasticsearch.xpack.core.inference.usage;
99

10+
import org.elasticsearch.TransportVersion;
1011
import org.elasticsearch.common.io.stream.Writeable;
1112
import org.elasticsearch.inference.TaskType;
12-
import org.elasticsearch.test.AbstractWireSerializingTestCase;
1313
import org.elasticsearch.test.ESTestCase;
14+
import org.elasticsearch.xpack.core.ml.AbstractBWCWireSerializationTestCase;
1415

1516
import java.io.IOException;
1617

1718
import static org.hamcrest.Matchers.equalTo;
1819

19-
public class ModelStatsTests extends AbstractWireSerializingTestCase<ModelStats> {
20+
public class ModelStatsTests extends AbstractBWCWireSerializationTestCase<ModelStats> {
2021

2122
@Override
2223
protected Writeable.Reader<ModelStats> instanceReader() {
@@ -33,10 +34,22 @@ protected ModelStats mutateInstance(ModelStats modelStats) throws IOException {
3334
String service = modelStats.service();
3435
TaskType taskType = modelStats.taskType();
3536
long count = modelStats.count();
36-
return switch (randomInt(2)) {
37-
case 0 -> new ModelStats(randomValueOtherThan(service, ESTestCase::randomIdentifier), taskType, count);
38-
case 1 -> new ModelStats(service, randomValueOtherThan(taskType, () -> randomFrom(TaskType.values())), count);
39-
case 2 -> new ModelStats(service, taskType, randomValueOtherThan(count, ESTestCase::randomLong));
37+
SemanticTextStats semanticTextStats = modelStats.semanticTextStats();
38+
return switch (randomInt(3)) {
39+
case 0 -> new ModelStats(randomValueOtherThan(service, ESTestCase::randomIdentifier), taskType, count, semanticTextStats);
40+
case 1 -> new ModelStats(
41+
service,
42+
randomValueOtherThan(taskType, () -> randomFrom(TaskType.values())),
43+
count,
44+
semanticTextStats
45+
);
46+
case 2 -> new ModelStats(service, taskType, randomValueOtherThan(count, ESTestCase::randomLong), semanticTextStats);
47+
case 3 -> new ModelStats(
48+
service,
49+
taskType,
50+
count,
51+
randomValueOtherThan(semanticTextStats, SemanticTextStatsTests::createRandomInstance)
52+
);
4053
default -> throw new IllegalArgumentException();
4154
};
4255
}
@@ -56,6 +69,19 @@ public void testAdd() {
5669
}
5770

5871
public static ModelStats createRandomInstance() {
59-
return new ModelStats(randomIdentifier(), randomFrom(TaskType.values()), randomLong());
72+
return new ModelStats(
73+
randomIdentifier(),
74+
randomFrom(TaskType.values()),
75+
randomLong(),
76+
SemanticTextStatsTests.createRandomInstance()
77+
);
78+
}
79+
80+
@Override
81+
protected ModelStats mutateInstanceForVersion(ModelStats instance, TransportVersion version) {
82+
if (version.supports(ModelStats.INFERENCE_TELEMETRY_ADDED_SEMANTIC_TEXT_STATS) == false) {
83+
return new ModelStats(instance.service(), instance.taskType(), instance.count(), new SemanticTextStats());
84+
}
85+
return instance;
6086
}
6187
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
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.usage;
9+
10+
import org.elasticsearch.common.io.stream.Writeable;
11+
import org.elasticsearch.test.AbstractWireSerializingTestCase;
12+
import org.elasticsearch.test.ESTestCase;
13+
14+
import java.io.IOException;
15+
16+
import static org.hamcrest.Matchers.equalTo;
17+
import static org.hamcrest.Matchers.is;
18+
19+
public class SemanticTextStatsTests extends AbstractWireSerializingTestCase<SemanticTextStats> {
20+
21+
@Override
22+
protected Writeable.Reader<SemanticTextStats> instanceReader() {
23+
return SemanticTextStats::new;
24+
}
25+
26+
@Override
27+
protected SemanticTextStats createTestInstance() {
28+
return createRandomInstance();
29+
}
30+
31+
static SemanticTextStats createRandomInstance() {
32+
return new SemanticTextStats(randomNonNegativeLong(), randomNonNegativeLong(), randomNonNegativeLong());
33+
}
34+
35+
@Override
36+
protected SemanticTextStats mutateInstance(SemanticTextStats instance) throws IOException {
37+
return switch (randomInt(2)) {
38+
case 0 -> new SemanticTextStats(
39+
randomValueOtherThan(instance.getFieldCount(), ESTestCase::randomNonNegativeLong),
40+
instance.getIndicesCount(),
41+
instance.getInferenceIdCount()
42+
);
43+
case 1 -> new SemanticTextStats(
44+
instance.getFieldCount(),
45+
randomValueOtherThan(instance.getIndicesCount(), ESTestCase::randomNonNegativeLong),
46+
instance.getInferenceIdCount()
47+
);
48+
case 2 -> new SemanticTextStats(
49+
instance.getFieldCount(),
50+
instance.getIndicesCount(),
51+
randomValueOtherThan(instance.getInferenceIdCount(), ESTestCase::randomNonNegativeLong)
52+
);
53+
default -> throw new IllegalArgumentException();
54+
};
55+
}
56+
57+
public void testDefaultConstructor() {
58+
var stats = new SemanticTextStats();
59+
assertThat(stats.getFieldCount(), equalTo(0L));
60+
assertThat(stats.getIndicesCount(), equalTo(0L));
61+
assertThat(stats.getInferenceIdCount(), equalTo(0L));
62+
}
63+
64+
public void testAddFieldCount() {
65+
var stats = new SemanticTextStats();
66+
stats.addFieldCount(10L);
67+
assertThat(stats.getFieldCount(), equalTo(10L));
68+
stats.addFieldCount(32L);
69+
assertThat(stats.getFieldCount(), equalTo(42L));
70+
}
71+
72+
public void testIsEmpty() {
73+
assertThat(new SemanticTextStats().isEmpty(), is(true));
74+
assertThat(new SemanticTextStats(randomLongBetween(1, Long.MAX_VALUE), 0, 0).isEmpty(), is(false));
75+
assertThat(new SemanticTextStats(0, randomLongBetween(1, Long.MAX_VALUE), 0).isEmpty(), is(false));
76+
assertThat(new SemanticTextStats(0, 0, randomLongBetween(1, Long.MAX_VALUE)).isEmpty(), is(false));
77+
}
78+
}

0 commit comments

Comments
 (0)