From e29c3a90818cc85372724eb9d5bce8a67b550775 Mon Sep 17 00:00:00 2001
From: peachisai <2581009893@qq.com>
Date: Mon, 16 Mar 2026 21:39:21 +0800
Subject: [PATCH 1/4] Support Virtual-GenAI monitoring
---
.github/workflows/skywalking.yaml | 2 +
docs/en/setup/service-agent/virtual-genai.md | 16 +
oap-server/analyzer/agent-analyzer/pom.xml | 5 +
.../VirtualServiceAnalysisListener.java | 21 +-
.../vservice/VirtualGenAIProcessor.java | 101 ++++++
oap-server/analyzer/genAI-analyzer/pom.xml | 37 ++
.../analyzer/GenAIAnalyzerModuleProvider.java | 94 +++++
.../meter/analyzer/config/GenAIConfig.java | 50 +++
.../analyzer/config/GenAIConfigLoader.java | 110 ++++++
.../meter/analyzer/config/GenAIOALDefine.java | 33 ++
.../meter/analyzer/config/GenAITagKey.java | 28 ++
.../matcher/GenAIProviderPrefixMatcher.java | 115 ++++++
.../analyzer/module/GenAIAnalyzerModule.java | 38 ++
.../analyzer/service/GenAIMeterAnalyzer.java | 130 +++++++
.../service/GenAIModelAccessDispatcher.java | 37 ++
.../service/IGenAIMeterAnalyzerService.java | 30 ++
...ing.oap.server.library.module.ModuleDefine | 19 +
...g.oap.server.library.module.ModuleProvider | 18 +
oap-server/analyzer/pom.xml | 1 +
.../skywalking/oal/rt/grammar/OALLexer.g4 | 2 +
.../skywalking/oal/rt/grammar/OALParser.g4 | 3 +-
.../generator/RuntimeOALGenerationTest.java | 6 +
.../ui/template/UITemplateInitializer.java | 1 +
.../core/source/DefaultScopeDefine.java | 2 +
.../oap/server/core/source/GenAIMetrics.java | 45 +++
.../server/core/source/GenAIModelAccess.java | 71 ++++
.../core/source/GenAIProviderAccess.java | 62 ++++
oap-server/server-starter/pom.xml | 1 +
.../src/main/resources/application.yml | 4 +
.../src/main/resources/gen-ai-config.yml | 95 +++++
.../src/main/resources/oal/virtual-gen-ai.oal | 45 +++
.../ui-initialized-templates/menu.yaml | 10 +
.../rocketmq/rocketmq-root.json | 2 +-
.../virtual_genai/virtual-genai-model.json | 326 ++++++++++++++++++
.../virtual_genai/virtual-genai-provider.json | 280 +++++++++++++++
.../virtual_genai/virtual-genai-root.json | 57 +++
.../cases/storage/expected/config-dump.yml | 1 +
.../cases/virtual-genai/Dockerfile.provider | 41 +++
.../cases/virtual-genai/docker-compose.yml | 69 ++++
test/e2e-v2/cases/virtual-genai/e2e.yaml | 44 +++
.../cases/virtual-genai/expected/instance.yml | 22 ++
.../expected/metrics-has-value-label.yml | 38 ++
.../expected/metrics-has-value.yml | 34 ++
.../cases/virtual-genai/expected/service.yml | 24 ++
.../cases/virtual-genai/virtual-genai.yaml | 65 ++++
.../e2e-service-provider/pom.xml | 6 +
.../e2e/controller/LLMMockController.java | 107 ++++++
test/e2e-v2/script/env | 2 +-
48 files changed, 2341 insertions(+), 9 deletions(-)
create mode 100644 docs/en/setup/service-agent/virtual-genai.md
create mode 100644 oap-server/analyzer/agent-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/provider/trace/parser/listener/vservice/VirtualGenAIProcessor.java
create mode 100644 oap-server/analyzer/genAI-analyzer/pom.xml
create mode 100644 oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/GenAIAnalyzerModuleProvider.java
create mode 100644 oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/config/GenAIConfig.java
create mode 100644 oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/config/GenAIConfigLoader.java
create mode 100644 oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/config/GenAIOALDefine.java
create mode 100644 oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/config/GenAITagKey.java
create mode 100644 oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/matcher/GenAIProviderPrefixMatcher.java
create mode 100644 oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/module/GenAIAnalyzerModule.java
create mode 100644 oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/service/GenAIMeterAnalyzer.java
create mode 100644 oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/service/GenAIModelAccessDispatcher.java
create mode 100644 oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/service/IGenAIMeterAnalyzerService.java
create mode 100644 oap-server/analyzer/genAI-analyzer/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleDefine
create mode 100644 oap-server/analyzer/genAI-analyzer/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider
create mode 100644 oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/source/GenAIMetrics.java
create mode 100644 oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/source/GenAIModelAccess.java
create mode 100644 oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/source/GenAIProviderAccess.java
create mode 100644 oap-server/server-starter/src/main/resources/gen-ai-config.yml
create mode 100644 oap-server/server-starter/src/main/resources/oal/virtual-gen-ai.oal
create mode 100644 oap-server/server-starter/src/main/resources/ui-initialized-templates/virtual_genai/virtual-genai-model.json
create mode 100644 oap-server/server-starter/src/main/resources/ui-initialized-templates/virtual_genai/virtual-genai-provider.json
create mode 100644 oap-server/server-starter/src/main/resources/ui-initialized-templates/virtual_genai/virtual-genai-root.json
create mode 100644 test/e2e-v2/cases/virtual-genai/Dockerfile.provider
create mode 100644 test/e2e-v2/cases/virtual-genai/docker-compose.yml
create mode 100644 test/e2e-v2/cases/virtual-genai/e2e.yaml
create mode 100644 test/e2e-v2/cases/virtual-genai/expected/instance.yml
create mode 100644 test/e2e-v2/cases/virtual-genai/expected/metrics-has-value-label.yml
create mode 100644 test/e2e-v2/cases/virtual-genai/expected/metrics-has-value.yml
create mode 100644 test/e2e-v2/cases/virtual-genai/expected/service.yml
create mode 100644 test/e2e-v2/cases/virtual-genai/virtual-genai.yaml
create mode 100644 test/e2e-v2/java-test-service/e2e-service-provider/src/main/java/org/apache/skywalking/e2e/controller/LLMMockController.java
diff --git a/.github/workflows/skywalking.yaml b/.github/workflows/skywalking.yaml
index 3e4158e9699a..a663789297d3 100644
--- a/.github/workflows/skywalking.yaml
+++ b/.github/workflows/skywalking.yaml
@@ -627,6 +627,8 @@ jobs:
config: test/e2e-v2/cases/zipkin/kafka/e2e.yaml
- name: Zipkin BanyanDB
config: test/e2e-v2/cases/zipkin/banyandb/e2e.yaml
+ - name: Virtual-genai
+ config: test/e2e-v2/cases/virtual-genai/e2e.yaml
- name: Nginx
config: test/e2e-v2/cases/nginx/e2e.yaml
diff --git a/docs/en/setup/service-agent/virtual-genai.md b/docs/en/setup/service-agent/virtual-genai.md
new file mode 100644
index 000000000000..1c9feb68dbdc
--- /dev/null
+++ b/docs/en/setup/service-agent/virtual-genai.md
@@ -0,0 +1,16 @@
+# Virtual GenAI
+
+Virtual cache represent the Generative AI service nodes detected by [server agents' plugins](server-agents.md). The performance
+metrics of the GenAI operations are also from the GenAI client-side perspective.
+
+For example, an Spring-ai plugin in the Java agent could detect the latency of a chat completion request.
+As a result, SkyWalking would show traffic, latency, success rate, and token usage (input/output) powered by backend analysis capabilities in this dashboard.
+
+The GenAI operation span should have
+- It is an **Exit** span
+- **Span's layer == GENAI**
+- Tag key = `gen_ai.provider.name`, value = The Generative AI provider, e.g. openai, anthropic, ollama
+- Tag key = `gen_ai.response.model`, value = The name of the GenAI model a response is being made to, e.g. gpt-4o, claude-3-5-sonnet
+- Tag key = `gen_ai.usage.input_tokens`, value = The number of tokens used in the GenAI input (prompt)
+- Tag key = `gen_ai.usage.output_tokens`, value = The number of tokens used in the GenAI response (completion)
+- If the GenAI service is a remote API (e.g. OpenAI), the span's peer would be the network address (IP or domain) of the GenAI server.
diff --git a/oap-server/analyzer/agent-analyzer/pom.xml b/oap-server/analyzer/agent-analyzer/pom.xml
index 5a281800589b..391d0765035e 100644
--- a/oap-server/analyzer/agent-analyzer/pom.xml
+++ b/oap-server/analyzer/agent-analyzer/pom.xml
@@ -43,6 +43,11 @@
meter-analyzer
${project.version}
+
+ org.apache.skywalking
+ genAI-analyzer
+ ${project.version}
+
org.apache.skywalking
server-testing
diff --git a/oap-server/analyzer/agent-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/provider/trace/parser/listener/VirtualServiceAnalysisListener.java b/oap-server/analyzer/agent-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/provider/trace/parser/listener/VirtualServiceAnalysisListener.java
index 95c0ac47fccb..90e15c41ec76 100644
--- a/oap-server/analyzer/agent-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/provider/trace/parser/listener/VirtualServiceAnalysisListener.java
+++ b/oap-server/analyzer/agent-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/provider/trace/parser/listener/VirtualServiceAnalysisListener.java
@@ -20,12 +20,16 @@
import java.util.Arrays;
import java.util.List;
+
import lombok.RequiredArgsConstructor;
import org.apache.skywalking.apm.network.language.agent.v3.SegmentObject;
import org.apache.skywalking.apm.network.language.agent.v3.SpanObject;
+import org.apache.skywalking.oap.meter.analyzer.module.GenAIAnalyzerModule;
+import org.apache.skywalking.oap.meter.analyzer.service.IGenAIMeterAnalyzerService;
import org.apache.skywalking.oap.server.analyzer.provider.AnalyzerModuleConfig;
import org.apache.skywalking.oap.server.analyzer.provider.trace.parser.listener.vservice.VirtualCacheProcessor;
import org.apache.skywalking.oap.server.analyzer.provider.trace.parser.listener.vservice.VirtualDatabaseProcessor;
+import org.apache.skywalking.oap.server.analyzer.provider.trace.parser.listener.vservice.VirtualGenAIProcessor;
import org.apache.skywalking.oap.server.analyzer.provider.trace.parser.listener.vservice.VirtualMQProcessor;
import org.apache.skywalking.oap.server.analyzer.provider.trace.parser.listener.vservice.VirtualServiceProcessor;
import org.apache.skywalking.oap.server.core.CoreModule;
@@ -71,23 +75,28 @@ public void parseEntry(final SpanObject span, final SegmentObject segmentObject)
public static class Factory implements AnalysisListenerFactory {
private final SourceReceiver sourceReceiver;
private final NamingControl namingControl;
+ private final IGenAIMeterAnalyzerService genAIMeterAnalyzerService;
public Factory(ModuleManager moduleManager) {
this.sourceReceiver = moduleManager.find(CoreModule.NAME).provider().getService(SourceReceiver.class);
this.namingControl = moduleManager.find(CoreModule.NAME)
.provider()
.getService(NamingControl.class);
+ this.genAIMeterAnalyzerService = moduleManager.find(GenAIAnalyzerModule.NAME)
+ .provider()
+ .getService(IGenAIMeterAnalyzerService.class);
}
@Override
public AnalysisListener create(ModuleManager moduleManager, AnalyzerModuleConfig config) {
return new VirtualServiceAnalysisListener(
- sourceReceiver,
- Arrays.asList(
- new VirtualCacheProcessor(namingControl, config),
- new VirtualDatabaseProcessor(namingControl, config),
- new VirtualMQProcessor(namingControl)
- )
+ sourceReceiver,
+ Arrays.asList(
+ new VirtualCacheProcessor(namingControl, config),
+ new VirtualDatabaseProcessor(namingControl, config),
+ new VirtualMQProcessor(namingControl),
+ new VirtualGenAIProcessor(genAIMeterAnalyzerService)
+ )
);
}
}
diff --git a/oap-server/analyzer/agent-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/provider/trace/parser/listener/vservice/VirtualGenAIProcessor.java b/oap-server/analyzer/agent-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/provider/trace/parser/listener/vservice/VirtualGenAIProcessor.java
new file mode 100644
index 000000000000..1b1c3e7eb423
--- /dev/null
+++ b/oap-server/analyzer/agent-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/provider/trace/parser/listener/vservice/VirtualGenAIProcessor.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.skywalking.oap.server.analyzer.provider.trace.parser.listener.vservice;
+
+import lombok.RequiredArgsConstructor;
+import org.apache.skywalking.apm.network.language.agent.v3.SegmentObject;
+import org.apache.skywalking.apm.network.language.agent.v3.SpanLayer;
+import org.apache.skywalking.apm.network.language.agent.v3.SpanObject;
+import org.apache.skywalking.oap.meter.analyzer.service.IGenAIMeterAnalyzerService;
+import org.apache.skywalking.oap.server.core.analysis.Layer;
+import org.apache.skywalking.oap.server.core.source.GenAIMetrics;
+import org.apache.skywalking.oap.server.core.source.GenAIModelAccess;
+import org.apache.skywalking.oap.server.core.source.GenAIProviderAccess;
+import org.apache.skywalking.oap.server.core.source.ServiceMeta;
+import org.apache.skywalking.oap.server.core.source.Source;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+@RequiredArgsConstructor
+public class VirtualGenAIProcessor implements VirtualServiceProcessor {
+
+ private final IGenAIMeterAnalyzerService meterAnalyzerService;
+
+ private List recordList = new ArrayList<>();
+
+ @Override
+ public void prepareVSIfNecessary(SpanObject span, SegmentObject segmentObject) {
+ if (span.getSpanLayer() != SpanLayer.GenAI) {
+ return;
+ }
+
+ GenAIMetrics metrics = meterAnalyzerService.extractMetricsFromSWSpan(span, segmentObject);
+ if (metrics == null) {
+ return;
+ }
+
+ recordList.add(toServiceMeta(metrics));
+ recordList.add(toProviderAccess(metrics));
+ recordList.add(toModelAccess(metrics));
+ }
+
+ private ServiceMeta toServiceMeta(GenAIMetrics metrics) {
+ ServiceMeta service = new ServiceMeta();
+ service.setName(metrics.getProviderName());
+ service.setLayer(Layer.VIRTUAL_GENAI);
+ service.setTimeBucket(metrics.getTimeBucket());
+ return service;
+ }
+
+ private GenAIProviderAccess toProviderAccess(GenAIMetrics metrics) {
+ GenAIProviderAccess source = new GenAIProviderAccess();
+ source.setName(metrics.getProviderName());
+ source.setInputTokens(metrics.getInputTokens());
+ source.setOutputTokens(metrics.getOutputTokens());
+ source.setTotalCost(metrics.getTotalCost());
+ source.setLatency(metrics.getLatency());
+ source.setStatus(metrics.isStatus());
+ source.setTimeBucket(metrics.getTimeBucket());
+ return source;
+ }
+
+ private GenAIModelAccess toModelAccess(GenAIMetrics metrics) {
+ GenAIModelAccess source = new GenAIModelAccess();
+ source.setServiceName(metrics.getProviderName());
+ source.setModelName(metrics.getModelName());
+ source.setInputTokens(metrics.getInputTokens());
+ source.setOutputTokens(metrics.getOutputTokens());
+ source.setTotalCost(metrics.getTotalCost());
+ source.setTimeToFirstToken(metrics.getTimeToFirstToken());
+ source.setLatency(metrics.getLatency());
+ source.setStatus(metrics.isStatus());
+ source.setTimeBucket(metrics.getTimeBucket());
+ return source;
+ }
+
+ @Override
+ public void emitTo(Consumer consumer) {
+ for (Source source : recordList) {
+ if (source != null) {
+ consumer.accept(source);
+ }
+ }
+ }
+}
diff --git a/oap-server/analyzer/genAI-analyzer/pom.xml b/oap-server/analyzer/genAI-analyzer/pom.xml
new file mode 100644
index 000000000000..52ee1bddf325
--- /dev/null
+++ b/oap-server/analyzer/genAI-analyzer/pom.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+ analyzer
+ org.apache.skywalking
+ ${revision}
+
+ 4.0.0
+
+ genAI-analyzer
+
+
+
+ org.apache.skywalking
+ server-core
+ ${project.version}
+
+
+
diff --git a/oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/GenAIAnalyzerModuleProvider.java b/oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/GenAIAnalyzerModuleProvider.java
new file mode 100644
index 000000000000..fa6b2a2798fa
--- /dev/null
+++ b/oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/GenAIAnalyzerModuleProvider.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oap.meter.analyzer;
+
+import org.apache.skywalking.oap.meter.analyzer.config.GenAIConfig;
+import org.apache.skywalking.oap.meter.analyzer.config.GenAIConfigLoader;
+import org.apache.skywalking.oap.meter.analyzer.config.GenAIOALDefine;
+import org.apache.skywalking.oap.meter.analyzer.matcher.GenAIProviderPrefixMatcher;
+import org.apache.skywalking.oap.meter.analyzer.module.GenAIAnalyzerModule;
+import org.apache.skywalking.oap.meter.analyzer.service.GenAIMeterAnalyzer;
+import org.apache.skywalking.oap.meter.analyzer.service.IGenAIMeterAnalyzerService;
+import org.apache.skywalking.oap.server.core.CoreModule;
+import org.apache.skywalking.oap.server.core.oal.rt.OALEngineLoaderService;
+import org.apache.skywalking.oap.server.library.module.ModuleConfig;
+import org.apache.skywalking.oap.server.library.module.ModuleDefine;
+import org.apache.skywalking.oap.server.library.module.ModuleProvider;
+import org.apache.skywalking.oap.server.library.module.ModuleStartException;
+import org.apache.skywalking.oap.server.library.module.ServiceNotProvidedException;
+import org.yaml.snakeyaml.Yaml;
+
+public class GenAIAnalyzerModuleProvider extends ModuleProvider {
+
+ private GenAIConfig config;
+
+ @Override
+ public String name() {
+ return "default";
+ }
+
+ @Override
+ public Class extends ModuleDefine> module() {
+ return GenAIAnalyzerModule.class;
+ }
+
+ @Override
+ public ConfigCreator extends ModuleConfig> newConfigCreator() {
+ return new ConfigCreator() {
+ @Override
+ public Class type() {
+ return GenAIConfig.class;
+ }
+
+ @Override
+ public void onInitialized(final GenAIConfig initialized) {
+ config = initialized;
+ }
+ };
+ }
+
+ @Override
+ public void prepare() throws ServiceNotProvidedException, ModuleStartException {
+ GenAIConfigLoader loader = new GenAIConfigLoader(config, new Yaml());
+ config = loader.loadConfig();
+ GenAIProviderPrefixMatcher matcher = GenAIProviderPrefixMatcher.build(config);
+ this.registerServiceImplementation(
+ IGenAIMeterAnalyzerService.class,
+ new GenAIMeterAnalyzer(matcher)
+ );
+ }
+
+ @Override
+ public void start() throws ServiceNotProvidedException, ModuleStartException {
+ getManager().find(CoreModule.NAME)
+ .provider()
+ .getService(OALEngineLoaderService.class)
+ .load(GenAIOALDefine.INSTANCE);
+ }
+
+ @Override
+ public void notifyAfterCompleted() throws ServiceNotProvidedException, ModuleStartException {
+
+ }
+
+ @Override
+ public String[] requiredModules() {
+ return new String[0];
+ }
+}
diff --git a/oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/config/GenAIConfig.java b/oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/config/GenAIConfig.java
new file mode 100644
index 000000000000..41682ca97637
--- /dev/null
+++ b/oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/config/GenAIConfig.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oap.meter.analyzer.config;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.skywalking.oap.server.library.module.ModuleConfig;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class GenAIConfig extends ModuleConfig {
+
+ @Getter
+ @Setter
+ private List providers = new ArrayList<>();
+
+ @Getter
+ @Setter
+ public static class Provider {
+ private String provider;
+ private String baseUrl;
+ private List prefixMatch = new ArrayList<>();
+ private List models = new ArrayList<>();
+ }
+
+ @Getter
+ @Setter
+ public static class Model {
+ private String name;
+ private double inputCostPerM;
+ private double outputCostPerM;
+ }
+}
diff --git a/oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/config/GenAIConfigLoader.java b/oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/config/GenAIConfigLoader.java
new file mode 100644
index 000000000000..4fcede0b3ed7
--- /dev/null
+++ b/oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/config/GenAIConfigLoader.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oap.meter.analyzer.config;
+
+import org.apache.skywalking.oap.server.library.module.ModuleStartException;
+import org.apache.skywalking.oap.server.library.util.ResourceUtils;
+import org.apache.skywalking.oap.server.library.util.StringUtil;
+import org.yaml.snakeyaml.Yaml;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.Reader;
+import java.util.List;
+import java.util.Map;
+
+public class GenAIConfigLoader {
+
+ private final GenAIConfig config;
+
+ public GenAIConfigLoader(GenAIConfig config, Yaml yaml) {
+ this.config = config;
+ }
+
+ public GenAIConfig loadConfig() throws ModuleStartException {
+ Map>> configMap;
+ try (Reader applicationReader = ResourceUtils.read("gen-ai-config.yml")) {
+ Yaml yaml = new Yaml();
+ configMap = yaml.loadAs(applicationReader, Map.class);
+ } catch (FileNotFoundException e) {
+ throw new ModuleStartException(
+ "Cannot find the GenAI configuration file [gen-ai-config.yml].", e);
+ } catch (IOException e) {
+ throw new ModuleStartException(
+ "Failed to read the GenAI configuration file [gen-ai-config.yml].", e);
+ }
+
+ if (configMap == null || !configMap.containsKey("providers")) {
+ return config;
+ }
+
+ List
+
+
+ com.alibaba
+ fastjson
+ 1.2.83
+
diff --git a/test/e2e-v2/java-test-service/e2e-service-provider/src/main/java/org/apache/skywalking/e2e/controller/LLMMockController.java b/test/e2e-v2/java-test-service/e2e-service-provider/src/main/java/org/apache/skywalking/e2e/controller/LLMMockController.java
new file mode 100644
index 000000000000..24baa68d0088
--- /dev/null
+++ b/test/e2e-v2/java-test-service/e2e-service-provider/src/main/java/org/apache/skywalking/e2e/controller/LLMMockController.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.e2e.controller;
+
+import com.alibaba.fastjson.JSONObject;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.PrintWriter;
+import java.time.Instant;
+
+@RestController
+@RequestMapping("/llm")
+public class LLMMockController {
+ @PostMapping("/v1/chat/completions")
+ public Object completions(@RequestBody JSONObject request, HttpServletResponse response) throws Exception {
+
+ response.setContentType("text/event-stream");
+ response.setCharacterEncoding("UTF-8");
+ response.setHeader("Cache-Control", "no-cache");
+ response.setHeader("Connection", "keep-alive");
+
+ String id = "chatcmpl-simple-mock-001";
+ long created = Instant.now().getEpochSecond();
+ String model = "gpt-4.1-mini-2025-04-14";
+
+ try (PrintWriter writer = response.getWriter()) {
+ Thread.sleep(1000);
+ writeStreamChunk(writer, id, created, model, "{\"role\":\"assistant\"}", "null");
+
+ String fullContent = "Why did the scarecrow win an award? Because he was outstanding in his field!";
+ String[] words = fullContent.split(" ");
+
+ for (int i = 0; i < words.length; i++) {
+ String chunk = words[i] + (i == words.length - 1 ? "" : " ");
+ Thread.sleep(50);
+ writeStreamChunk(writer, id, created, model, "{\"content\":\"" + chunk + "\"}", "null");
+ }
+
+ writeStreamChunk(writer, id, created, model, "{}", "\"stop\"");
+
+ writer.write("data: [DONE]\n\n");
+ writer.flush();
+
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ return null;
+ }
+
+ private void writeStreamChunk(PrintWriter writer, String id, long created, String model, String delta, String finishReason) {
+ String json = "{"
+ + "\"id\": \"%s\","
+ + "\"object\": \"chat.completion.chunk\","
+ + "\"created\": %d,"
+ + "\"model\": \"%s\","
+ + "\"system_fingerprint\": null,"
+ + "\"choices\": ["
+ + "{"
+ + "\"index\": 0,"
+ + "\"delta\": %s,"
+ + "\"finish_reason\": %s"
+ + "}"
+ + "],"
+ + "\"usage\": {"
+ + "\"completion_tokens\": 17,"
+ + "\"completion_tokens_details\": {"
+ + "\"accepted_prediction_tokens\": 0,"
+ + "\"audio_tokens\": 0,"
+ + "\"reasoning_tokens\": 0,"
+ + "\"rejected_prediction_tokens\": 0"
+ + "},"
+ + "\"prompt_tokens\": 52,"
+ + "\"prompt_tokens_details\": {"
+ + "\"audio_tokens\": 0,"
+ + "\"cached_tokens\": 0"
+ + "},"
+ + "\"total_tokens\": 69"
+ + "}"
+ + "}";
+
+ String formattedJson = String.format(json, id, created, model, delta, finishReason);
+
+ String cleanJson = formattedJson.replace("\n", "").replace("\r", "");
+ writer.write("data: " + cleanJson + "\n\n");
+ writer.flush();
+ }
+}
diff --git a/test/e2e-v2/script/env b/test/e2e-v2/script/env
index 96f105986f51..9d7ff36f3dc0 100644
--- a/test/e2e-v2/script/env
+++ b/test/e2e-v2/script/env
@@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-SW_AGENT_JAVA_COMMIT=2a61027e5eb74ed1258c764ae2ffeabd499416a6
+SW_AGENT_JAVA_COMMIT=2f1d9e94d6d1ac22d92f4e9c6905901fe646ffdf
SW_AGENT_SATELLITE_COMMIT=ea27a3f4e126a24775fe12e2aa2695bcb23d99c3
SW_AGENT_NGINX_LUA_COMMIT=c3cee4841798a147d83b96a10914d4ac0e11d0aa
SW_AGENT_NODEJS_COMMIT=4f9a91dad3dfd8cfe5ba8f7bd06b39e11eb5e65e
From 3642ce354579e5161bd97dab829e226443ae189c Mon Sep 17 00:00:00 2001
From: peachisai <2581009893@qq.com>
Date: Mon, 16 Mar 2026 21:55:52 +0800
Subject: [PATCH 2/4] fix changes
---
docs/en/changes/changes.md | 1 +
.../server-starter/src/main/resources/gen-ai-config.yml | 4 ----
2 files changed, 1 insertion(+), 4 deletions(-)
diff --git a/docs/en/changes/changes.md b/docs/en/changes/changes.md
index a8465dc26a29..77dae2645187 100644
--- a/docs/en/changes/changes.md
+++ b/docs/en/changes/changes.md
@@ -166,6 +166,7 @@
* Update hierarchy rule documentation: `auto-matching-rules` in `hierarchy-definition.yml` no longer use Groovy scripts. Rules now use a dedicated expression grammar supporting property access, String methods, if/else, comparisons, and logical operators. All shipped rules are fully compatible.
* Activate `otlp-traces` handler in `receiver-otel` by default.
* Update Istio E2E test versions: remove EOL 1.20.0, add 1.25.0–1.29.0 for ALS/Metrics/Ambient tests. Update Rover with Istio Process test from 1.15.0 to 1.28.0 with Kubernetes 1.28.
+* Support Virtual-GenAI monitoring.
#### UI
* Fix the missing icon in new native trace view.
diff --git a/oap-server/server-starter/src/main/resources/gen-ai-config.yml b/oap-server/server-starter/src/main/resources/gen-ai-config.yml
index c95667dc1e64..5d6ad7291847 100644
--- a/oap-server/server-starter/src/main/resources/gen-ai-config.yml
+++ b/oap-server/server-starter/src/main/resources/gen-ai-config.yml
@@ -41,10 +41,6 @@ providers:
- provider: anthropic
prefix-match:
- claude
- models:
- - name: claude-3-7-sonnet-20250219-thinking
- input-cost-per-m: 100
- output-cost-per-m: 100
- provider: gemini
prefix-match:
From d2c21655d7cc5c0485b30f3ad735ae572e640858 Mon Sep 17 00:00:00 2001
From: peachisai <2581009893@qq.com>
Date: Fri, 20 Mar 2026 12:33:34 +0800
Subject: [PATCH 3/4] fix some issues
---
.github/workflows/skywalking.yaml | 2 +-
apm-dist/src/main/assembly/binary.xml | 1 +
docs/en/setup/service-agent/virtual-genai.md | 3 +-
docs/menu.yml | 4 +
oap-server/analyzer/agent-analyzer/pom.xml | 2 +-
.../VirtualServiceAnalysisListener.java | 6 +-
.../vservice/VirtualGenAIProcessor.java | 13 +-
.../pom.xml | 2 +-
.../genai}/GenAIAnalyzerModuleProvider.java | 23 +--
.../analyzer/genai}/config/GenAIConfig.java | 2 +-
.../genai}/config/GenAIConfigLoader.java | 4 +-
.../genai}/config/GenAIOALDefine.java | 2 +-
.../analyzer/genai}/config/GenAITagKey.java | 4 +-
.../matcher/GenAIProviderPrefixMatcher.java | 4 +-
.../genai}/module/GenAIAnalyzerModule.java | 6 +-
.../genai}/service/GenAIMeterAnalyzer.java | 10 +-
.../service/GenAIModelAccessDispatcher.java | 2 +-
.../service/IGenAIMeterAnalyzerService.java | 2 +-
...ing.oap.server.library.module.ModuleDefine | 2 +-
...g.oap.server.library.module.ModuleProvider | 2 +-
oap-server/analyzer/pom.xml | 2 +-
oap-server/server-starter/pom.xml | 2 +-
.../src/main/resources/application.yml | 2 +-
.../src/main/resources/gen-ai-config.yml | 2 +-
.../src/main/resources/oal/virtual-gen-ai.oal | 16 +-
.../ui-initialized-templates/menu.yaml | 2 +-
.../virtual_genai/virtual-genai-model.json | 155 +++++++++++++-----
.../virtual_genai/virtual-genai-provider.json | 83 ++++++++--
.../virtual_genai/virtual-genai-root.json | 1 +
skywalking-ui | 2 +-
.../cases/storage/expected/config-dump.yml | 2 +-
.../cases/virtual-genai/Dockerfile.provider | 5 +-
.../cases/virtual-genai/docker-compose.yml | 2 +-
test/e2e-v2/cases/virtual-genai/e2e.yaml | 1 +
.../cases/virtual-genai/expected/instance.yml | 4 +-
.../expected/metrics-has-value-label.yml | 3 +-
.../expected/metrics-has-value.yml | 2 +-
.../cases/virtual-genai/expected/service.yml | 2 +-
.../cases/virtual-genai/virtual-genai.yaml | 33 ++--
.../e2e-service-provider/pom.xml | 5 -
.../e2e/controller/LLMMockController.java | 4 +-
test/e2e-v2/script/env | 2 +-
42 files changed, 295 insertions(+), 133 deletions(-)
rename oap-server/analyzer/{genAI-analyzer => gen-ai-analyzer}/pom.xml (97%)
rename oap-server/analyzer/{genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer => gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai}/GenAIAnalyzerModuleProvider.java (82%)
rename oap-server/analyzer/{genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer => gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai}/config/GenAIConfig.java (96%)
rename oap-server/analyzer/{genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer => gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai}/config/GenAIConfigLoader.java (97%)
rename oap-server/analyzer/{genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer => gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai}/config/GenAIOALDefine.java (95%)
rename oap-server/analyzer/{genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer => gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai}/config/GenAITagKey.java (88%)
rename oap-server/analyzer/{genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer => gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai}/matcher/GenAIProviderPrefixMatcher.java (96%)
rename oap-server/analyzer/{genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer => gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai}/module/GenAIAnalyzerModule.java (86%)
rename oap-server/analyzer/{genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer => gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai}/service/GenAIMeterAnalyzer.java (94%)
rename oap-server/analyzer/{genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer => gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai}/service/GenAIModelAccessDispatcher.java (96%)
rename oap-server/analyzer/{genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer => gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai}/service/IGenAIMeterAnalyzerService.java (95%)
rename oap-server/analyzer/{genAI-analyzer => gen-ai-analyzer}/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleDefine (92%)
rename oap-server/analyzer/{genAI-analyzer => gen-ai-analyzer}/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider (91%)
diff --git a/.github/workflows/skywalking.yaml b/.github/workflows/skywalking.yaml
index a663789297d3..b5a88ec4a698 100644
--- a/.github/workflows/skywalking.yaml
+++ b/.github/workflows/skywalking.yaml
@@ -627,7 +627,7 @@ jobs:
config: test/e2e-v2/cases/zipkin/kafka/e2e.yaml
- name: Zipkin BanyanDB
config: test/e2e-v2/cases/zipkin/banyandb/e2e.yaml
- - name: Virtual-genai
+ - name: Virtual GenAI
config: test/e2e-v2/cases/virtual-genai/e2e.yaml
- name: Nginx
diff --git a/apm-dist/src/main/assembly/binary.xml b/apm-dist/src/main/assembly/binary.xml
index f6dbf452d329..81f7b2a281c4 100644
--- a/apm-dist/src/main/assembly/binary.xml
+++ b/apm-dist/src/main/assembly/binary.xml
@@ -75,6 +75,7 @@
log-mal-rules/**
telegraf-rules/*
cilium-rules/*
+ gen-ai-config.yml
config
diff --git a/docs/en/setup/service-agent/virtual-genai.md b/docs/en/setup/service-agent/virtual-genai.md
index 1c9feb68dbdc..ce91ecc2c045 100644
--- a/docs/en/setup/service-agent/virtual-genai.md
+++ b/docs/en/setup/service-agent/virtual-genai.md
@@ -1,6 +1,6 @@
# Virtual GenAI
-Virtual cache represent the Generative AI service nodes detected by [server agents' plugins](server-agents.md). The performance
+Virtual GenAI represent the Generative AI service nodes detected by [server agents' plugins](server-agents.md). The performance
metrics of the GenAI operations are also from the GenAI client-side perspective.
For example, an Spring-ai plugin in the Java agent could detect the latency of a chat completion request.
@@ -13,4 +13,5 @@ The GenAI operation span should have
- Tag key = `gen_ai.response.model`, value = The name of the GenAI model a response is being made to, e.g. gpt-4o, claude-3-5-sonnet
- Tag key = `gen_ai.usage.input_tokens`, value = The number of tokens used in the GenAI input (prompt)
- Tag key = `gen_ai.usage.output_tokens`, value = The number of tokens used in the GenAI response (completion)
+- Tag key = `gen_ai.server.time_to_first_token`, value = The duration in milliseconds from the start of the request until the first token is received. Note: This metric is only available for streaming requests.
- If the GenAI service is a remote API (e.g. OpenAI), the span's peer would be the network address (IP or domain) of the GenAI server.
diff --git a/docs/menu.yml b/docs/menu.yml
index 4558a252eac7..e347015d95bc 100644
--- a/docs/menu.yml
+++ b/docs/menu.yml
@@ -156,6 +156,10 @@ catalog:
path: "/en/setup/backend/dashboards-so11y-java-agent"
- name: "SkyWalking Go Agent self telemetry"
path: "/en/setup/backend/dashboards-so11y-go-agent"
+ - name: "GenAI"
+ catalog:
+ - name: "Virtual Genai"
+ path: "/en/setup/service-agent/virtual-genai"
- name: "Configuration Vocabulary"
path: "/en/setup/backend/configuration-vocabulary"
- name: "Advanced Setup"
diff --git a/oap-server/analyzer/agent-analyzer/pom.xml b/oap-server/analyzer/agent-analyzer/pom.xml
index 391d0765035e..1fe05db26720 100644
--- a/oap-server/analyzer/agent-analyzer/pom.xml
+++ b/oap-server/analyzer/agent-analyzer/pom.xml
@@ -45,7 +45,7 @@
org.apache.skywalking
- genAI-analyzer
+ gen-ai-analyzer
${project.version}
diff --git a/oap-server/analyzer/agent-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/provider/trace/parser/listener/VirtualServiceAnalysisListener.java b/oap-server/analyzer/agent-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/provider/trace/parser/listener/VirtualServiceAnalysisListener.java
index 90e15c41ec76..861f699ce830 100644
--- a/oap-server/analyzer/agent-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/provider/trace/parser/listener/VirtualServiceAnalysisListener.java
+++ b/oap-server/analyzer/agent-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/provider/trace/parser/listener/VirtualServiceAnalysisListener.java
@@ -24,8 +24,8 @@
import lombok.RequiredArgsConstructor;
import org.apache.skywalking.apm.network.language.agent.v3.SegmentObject;
import org.apache.skywalking.apm.network.language.agent.v3.SpanObject;
-import org.apache.skywalking.oap.meter.analyzer.module.GenAIAnalyzerModule;
-import org.apache.skywalking.oap.meter.analyzer.service.IGenAIMeterAnalyzerService;
+import org.apache.skywalking.oap.analyzer.genai.module.GenAIAnalyzerModule;
+import org.apache.skywalking.oap.analyzer.genai.service.IGenAIMeterAnalyzerService;
import org.apache.skywalking.oap.server.analyzer.provider.AnalyzerModuleConfig;
import org.apache.skywalking.oap.server.analyzer.provider.trace.parser.listener.vservice.VirtualCacheProcessor;
import org.apache.skywalking.oap.server.analyzer.provider.trace.parser.listener.vservice.VirtualDatabaseProcessor;
@@ -95,7 +95,7 @@ public AnalysisListener create(ModuleManager moduleManager, AnalyzerModuleConfig
new VirtualCacheProcessor(namingControl, config),
new VirtualDatabaseProcessor(namingControl, config),
new VirtualMQProcessor(namingControl),
- new VirtualGenAIProcessor(genAIMeterAnalyzerService)
+ new VirtualGenAIProcessor(namingControl, genAIMeterAnalyzerService)
)
);
}
diff --git a/oap-server/analyzer/agent-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/provider/trace/parser/listener/vservice/VirtualGenAIProcessor.java b/oap-server/analyzer/agent-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/provider/trace/parser/listener/vservice/VirtualGenAIProcessor.java
index 1b1c3e7eb423..e5e8c4af33a5 100644
--- a/oap-server/analyzer/agent-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/provider/trace/parser/listener/vservice/VirtualGenAIProcessor.java
+++ b/oap-server/analyzer/agent-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/provider/trace/parser/listener/vservice/VirtualGenAIProcessor.java
@@ -21,8 +21,9 @@
import org.apache.skywalking.apm.network.language.agent.v3.SegmentObject;
import org.apache.skywalking.apm.network.language.agent.v3.SpanLayer;
import org.apache.skywalking.apm.network.language.agent.v3.SpanObject;
-import org.apache.skywalking.oap.meter.analyzer.service.IGenAIMeterAnalyzerService;
+import org.apache.skywalking.oap.analyzer.genai.service.IGenAIMeterAnalyzerService;
import org.apache.skywalking.oap.server.core.analysis.Layer;
+import org.apache.skywalking.oap.server.core.config.NamingControl;
import org.apache.skywalking.oap.server.core.source.GenAIMetrics;
import org.apache.skywalking.oap.server.core.source.GenAIModelAccess;
import org.apache.skywalking.oap.server.core.source.GenAIProviderAccess;
@@ -36,9 +37,11 @@
@RequiredArgsConstructor
public class VirtualGenAIProcessor implements VirtualServiceProcessor {
+ private final NamingControl namingControl;
+
private final IGenAIMeterAnalyzerService meterAnalyzerService;
- private List recordList = new ArrayList<>();
+ private final List recordList = new ArrayList<>();
@Override
public void prepareVSIfNecessary(SpanObject span, SegmentObject segmentObject) {
@@ -66,7 +69,7 @@ private ServiceMeta toServiceMeta(GenAIMetrics metrics) {
private GenAIProviderAccess toProviderAccess(GenAIMetrics metrics) {
GenAIProviderAccess source = new GenAIProviderAccess();
- source.setName(metrics.getProviderName());
+ source.setName(namingControl.formatServiceName(metrics.getProviderName()));
source.setInputTokens(metrics.getInputTokens());
source.setOutputTokens(metrics.getOutputTokens());
source.setTotalCost(metrics.getTotalCost());
@@ -78,8 +81,8 @@ private GenAIProviderAccess toProviderAccess(GenAIMetrics metrics) {
private GenAIModelAccess toModelAccess(GenAIMetrics metrics) {
GenAIModelAccess source = new GenAIModelAccess();
- source.setServiceName(metrics.getProviderName());
- source.setModelName(metrics.getModelName());
+ source.setServiceName(namingControl.formatServiceName(metrics.getProviderName()));
+ source.setModelName(namingControl.formatInstanceName(metrics.getModelName()));
source.setInputTokens(metrics.getInputTokens());
source.setOutputTokens(metrics.getOutputTokens());
source.setTotalCost(metrics.getTotalCost());
diff --git a/oap-server/analyzer/genAI-analyzer/pom.xml b/oap-server/analyzer/gen-ai-analyzer/pom.xml
similarity index 97%
rename from oap-server/analyzer/genAI-analyzer/pom.xml
rename to oap-server/analyzer/gen-ai-analyzer/pom.xml
index 52ee1bddf325..be35fa818e9e 100644
--- a/oap-server/analyzer/genAI-analyzer/pom.xml
+++ b/oap-server/analyzer/gen-ai-analyzer/pom.xml
@@ -25,7 +25,7 @@
4.0.0
- genAI-analyzer
+ gen-ai-analyzer
diff --git a/oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/GenAIAnalyzerModuleProvider.java b/oap-server/analyzer/gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai/GenAIAnalyzerModuleProvider.java
similarity index 82%
rename from oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/GenAIAnalyzerModuleProvider.java
rename to oap-server/analyzer/gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai/GenAIAnalyzerModuleProvider.java
index fa6b2a2798fa..6248a9b1b329 100644
--- a/oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/GenAIAnalyzerModuleProvider.java
+++ b/oap-server/analyzer/gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai/GenAIAnalyzerModuleProvider.java
@@ -16,15 +16,15 @@
*
*/
-package org.apache.skywalking.oap.meter.analyzer;
+package org.apache.skywalking.oap.analyzer.genai;
-import org.apache.skywalking.oap.meter.analyzer.config.GenAIConfig;
-import org.apache.skywalking.oap.meter.analyzer.config.GenAIConfigLoader;
-import org.apache.skywalking.oap.meter.analyzer.config.GenAIOALDefine;
-import org.apache.skywalking.oap.meter.analyzer.matcher.GenAIProviderPrefixMatcher;
-import org.apache.skywalking.oap.meter.analyzer.module.GenAIAnalyzerModule;
-import org.apache.skywalking.oap.meter.analyzer.service.GenAIMeterAnalyzer;
-import org.apache.skywalking.oap.meter.analyzer.service.IGenAIMeterAnalyzerService;
+import org.apache.skywalking.oap.analyzer.genai.config.GenAIConfig;
+import org.apache.skywalking.oap.analyzer.genai.config.GenAIConfigLoader;
+import org.apache.skywalking.oap.analyzer.genai.config.GenAIOALDefine;
+import org.apache.skywalking.oap.analyzer.genai.matcher.GenAIProviderPrefixMatcher;
+import org.apache.skywalking.oap.analyzer.genai.module.GenAIAnalyzerModule;
+import org.apache.skywalking.oap.analyzer.genai.service.GenAIMeterAnalyzer;
+import org.apache.skywalking.oap.analyzer.genai.service.IGenAIMeterAnalyzerService;
import org.apache.skywalking.oap.server.core.CoreModule;
import org.apache.skywalking.oap.server.core.oal.rt.OALEngineLoaderService;
import org.apache.skywalking.oap.server.library.module.ModuleConfig;
@@ -32,7 +32,6 @@
import org.apache.skywalking.oap.server.library.module.ModuleProvider;
import org.apache.skywalking.oap.server.library.module.ModuleStartException;
import org.apache.skywalking.oap.server.library.module.ServiceNotProvidedException;
-import org.yaml.snakeyaml.Yaml;
public class GenAIAnalyzerModuleProvider extends ModuleProvider {
@@ -65,7 +64,7 @@ public void onInitialized(final GenAIConfig initialized) {
@Override
public void prepare() throws ServiceNotProvidedException, ModuleStartException {
- GenAIConfigLoader loader = new GenAIConfigLoader(config, new Yaml());
+ GenAIConfigLoader loader = new GenAIConfigLoader(config);
config = loader.loadConfig();
GenAIProviderPrefixMatcher matcher = GenAIProviderPrefixMatcher.build(config);
this.registerServiceImplementation(
@@ -89,6 +88,8 @@ public void notifyAfterCompleted() throws ServiceNotProvidedException, ModuleSta
@Override
public String[] requiredModules() {
- return new String[0];
+ return new String[] {
+ CoreModule.NAME
+ };
}
}
diff --git a/oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/config/GenAIConfig.java b/oap-server/analyzer/gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai/config/GenAIConfig.java
similarity index 96%
rename from oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/config/GenAIConfig.java
rename to oap-server/analyzer/gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai/config/GenAIConfig.java
index 41682ca97637..46d83174c586 100644
--- a/oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/config/GenAIConfig.java
+++ b/oap-server/analyzer/gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai/config/GenAIConfig.java
@@ -16,7 +16,7 @@
*
*/
-package org.apache.skywalking.oap.meter.analyzer.config;
+package org.apache.skywalking.oap.analyzer.genai.config;
import lombok.Getter;
import lombok.Setter;
diff --git a/oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/config/GenAIConfigLoader.java b/oap-server/analyzer/gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai/config/GenAIConfigLoader.java
similarity index 97%
rename from oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/config/GenAIConfigLoader.java
rename to oap-server/analyzer/gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai/config/GenAIConfigLoader.java
index 4fcede0b3ed7..ea16c7cc7112 100644
--- a/oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/config/GenAIConfigLoader.java
+++ b/oap-server/analyzer/gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai/config/GenAIConfigLoader.java
@@ -16,7 +16,7 @@
*
*/
-package org.apache.skywalking.oap.meter.analyzer.config;
+package org.apache.skywalking.oap.analyzer.genai.config;
import org.apache.skywalking.oap.server.library.module.ModuleStartException;
import org.apache.skywalking.oap.server.library.util.ResourceUtils;
@@ -33,7 +33,7 @@ public class GenAIConfigLoader {
private final GenAIConfig config;
- public GenAIConfigLoader(GenAIConfig config, Yaml yaml) {
+ public GenAIConfigLoader(GenAIConfig config) {
this.config = config;
}
diff --git a/oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/config/GenAIOALDefine.java b/oap-server/analyzer/gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai/config/GenAIOALDefine.java
similarity index 95%
rename from oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/config/GenAIOALDefine.java
rename to oap-server/analyzer/gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai/config/GenAIOALDefine.java
index 97c399def037..2509b3772581 100644
--- a/oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/config/GenAIOALDefine.java
+++ b/oap-server/analyzer/gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai/config/GenAIOALDefine.java
@@ -16,7 +16,7 @@
*
*/
-package org.apache.skywalking.oap.meter.analyzer.config;
+package org.apache.skywalking.oap.analyzer.genai.config;
import org.apache.skywalking.oap.server.core.oal.rt.OALDefine;
diff --git a/oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/config/GenAITagKey.java b/oap-server/analyzer/gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai/config/GenAITagKey.java
similarity index 88%
rename from oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/config/GenAITagKey.java
rename to oap-server/analyzer/gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai/config/GenAITagKey.java
index 456b36e7275f..df33c32262f4 100644
--- a/oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/config/GenAITagKey.java
+++ b/oap-server/analyzer/gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai/config/GenAITagKey.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package org.apache.skywalking.oap.meter.analyzer.config;
+package org.apache.skywalking.oap.analyzer.genai.config;
public class GenAITagKey {
@@ -24,5 +24,5 @@ public class GenAITagKey {
public static final String RESPONSE_MODEL = "gen_ai.response.model";
public static final String INPUT_TOKENS = "gen_ai.usage.input_tokens";
public static final String OUTPUT_TOKENS = "gen_ai.usage.output_tokens";
- public static final String STREAM_TTFT = "gen_ai.stream.ttfr";
+ public static final String SERVER_TIME_TO_FIRST_TOKEN = "gen_ai.server.time_to_first_token";
}
diff --git a/oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/matcher/GenAIProviderPrefixMatcher.java b/oap-server/analyzer/gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai/matcher/GenAIProviderPrefixMatcher.java
similarity index 96%
rename from oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/matcher/GenAIProviderPrefixMatcher.java
rename to oap-server/analyzer/gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai/matcher/GenAIProviderPrefixMatcher.java
index a0802f41d396..6bbd71d390bb 100644
--- a/oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/matcher/GenAIProviderPrefixMatcher.java
+++ b/oap-server/analyzer/gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai/matcher/GenAIProviderPrefixMatcher.java
@@ -16,9 +16,9 @@
*
*/
-package org.apache.skywalking.oap.meter.analyzer.matcher;
+package org.apache.skywalking.oap.analyzer.genai.matcher;
-import org.apache.skywalking.oap.meter.analyzer.config.GenAIConfig;
+import org.apache.skywalking.oap.analyzer.genai.config.GenAIConfig;
import java.util.HashMap;
import java.util.List;
diff --git a/oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/module/GenAIAnalyzerModule.java b/oap-server/analyzer/gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai/module/GenAIAnalyzerModule.java
similarity index 86%
rename from oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/module/GenAIAnalyzerModule.java
rename to oap-server/analyzer/gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai/module/GenAIAnalyzerModule.java
index 0945b7f89d95..833cb30e8e5b 100644
--- a/oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/module/GenAIAnalyzerModule.java
+++ b/oap-server/analyzer/gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai/module/GenAIAnalyzerModule.java
@@ -16,14 +16,14 @@
*
*/
-package org.apache.skywalking.oap.meter.analyzer.module;
+package org.apache.skywalking.oap.analyzer.genai.module;
-import org.apache.skywalking.oap.meter.analyzer.service.IGenAIMeterAnalyzerService;
+import org.apache.skywalking.oap.analyzer.genai.service.IGenAIMeterAnalyzerService;
import org.apache.skywalking.oap.server.library.module.ModuleDefine;
public class GenAIAnalyzerModule extends ModuleDefine {
- public static final String NAME = "genAI-analyzer";
+ public static final String NAME = "gen-ai-analyzer";
public GenAIAnalyzerModule() {
super(NAME);
diff --git a/oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/service/GenAIMeterAnalyzer.java b/oap-server/analyzer/gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai/service/GenAIMeterAnalyzer.java
similarity index 94%
rename from oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/service/GenAIMeterAnalyzer.java
rename to oap-server/analyzer/gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai/service/GenAIMeterAnalyzer.java
index 1e67f1c11053..735acac69dd1 100644
--- a/oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/service/GenAIMeterAnalyzer.java
+++ b/oap-server/analyzer/gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai/service/GenAIMeterAnalyzer.java
@@ -15,14 +15,14 @@
* limitations under the License.
*/
-package org.apache.skywalking.oap.meter.analyzer.service;
+package org.apache.skywalking.oap.analyzer.genai.service;
import org.apache.skywalking.apm.network.common.v3.KeyStringValuePair;
import org.apache.skywalking.apm.network.language.agent.v3.SegmentObject;
import org.apache.skywalking.apm.network.language.agent.v3.SpanObject;
-import org.apache.skywalking.oap.meter.analyzer.config.GenAIConfig;
-import org.apache.skywalking.oap.meter.analyzer.config.GenAITagKey;
-import org.apache.skywalking.oap.meter.analyzer.matcher.GenAIProviderPrefixMatcher;
+import org.apache.skywalking.oap.analyzer.genai.config.GenAIConfig;
+import org.apache.skywalking.oap.analyzer.genai.config.GenAITagKey;
+import org.apache.skywalking.oap.analyzer.genai.matcher.GenAIProviderPrefixMatcher;
import org.apache.skywalking.oap.server.core.analysis.IDManager;
import org.apache.skywalking.oap.server.core.analysis.Layer;
import org.apache.skywalking.oap.server.core.analysis.TimeBucket;
@@ -93,7 +93,7 @@ public GenAIMetrics extractMetricsFromSWSpan(SpanObject span, SegmentObject segm
metrics.setInputTokens(inputTokens);
metrics.setOutputTokens(outputTokens);
- metrics.setTimeToFirstToken(parseSafeInt(tags.get(GenAITagKey.STREAM_TTFT)));
+ metrics.setTimeToFirstToken(parseSafeInt(tags.get(GenAITagKey.SERVER_TIME_TO_FIRST_TOKEN)));
metrics.setTotalCost(totalCost);
long latency = span.getEndTime() - span.getStartTime();
diff --git a/oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/service/GenAIModelAccessDispatcher.java b/oap-server/analyzer/gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai/service/GenAIModelAccessDispatcher.java
similarity index 96%
rename from oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/service/GenAIModelAccessDispatcher.java
rename to oap-server/analyzer/gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai/service/GenAIModelAccessDispatcher.java
index 9c0fa069b8a2..d5b0337a1d93 100644
--- a/oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/service/GenAIModelAccessDispatcher.java
+++ b/oap-server/analyzer/gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai/service/GenAIModelAccessDispatcher.java
@@ -16,7 +16,7 @@
*
*/
-package org.apache.skywalking.oap.meter.analyzer.service;
+package org.apache.skywalking.oap.analyzer.genai.service;
import org.apache.skywalking.oap.server.core.analysis.SourceDispatcher;
import org.apache.skywalking.oap.server.core.analysis.manual.instance.InstanceTraffic;
diff --git a/oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/service/IGenAIMeterAnalyzerService.java b/oap-server/analyzer/gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai/service/IGenAIMeterAnalyzerService.java
similarity index 95%
rename from oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/service/IGenAIMeterAnalyzerService.java
rename to oap-server/analyzer/gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai/service/IGenAIMeterAnalyzerService.java
index 6b00eeb024da..efbf2192b030 100644
--- a/oap-server/analyzer/genAI-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/service/IGenAIMeterAnalyzerService.java
+++ b/oap-server/analyzer/gen-ai-analyzer/src/main/java/org/apache/skywalking/oap/analyzer/genai/service/IGenAIMeterAnalyzerService.java
@@ -16,7 +16,7 @@
*
*/
-package org.apache.skywalking.oap.meter.analyzer.service;
+package org.apache.skywalking.oap.analyzer.genai.service;
import org.apache.skywalking.apm.network.language.agent.v3.SegmentObject;
import org.apache.skywalking.apm.network.language.agent.v3.SpanObject;
diff --git a/oap-server/analyzer/genAI-analyzer/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleDefine b/oap-server/analyzer/gen-ai-analyzer/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleDefine
similarity index 92%
rename from oap-server/analyzer/genAI-analyzer/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleDefine
rename to oap-server/analyzer/gen-ai-analyzer/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleDefine
index c648cced34ee..ea7d0042b839 100644
--- a/oap-server/analyzer/genAI-analyzer/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleDefine
+++ b/oap-server/analyzer/gen-ai-analyzer/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleDefine
@@ -16,4 +16,4 @@
#
#
-org.apache.skywalking.oap.meter.analyzer.module.GenAIAnalyzerModule
\ No newline at end of file
+org.apache.skywalking.oap.analyzer.genai.module.GenAIAnalyzerModule
diff --git a/oap-server/analyzer/genAI-analyzer/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider b/oap-server/analyzer/gen-ai-analyzer/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider
similarity index 91%
rename from oap-server/analyzer/genAI-analyzer/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider
rename to oap-server/analyzer/gen-ai-analyzer/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider
index 1eef579246fa..b0256f986574 100644
--- a/oap-server/analyzer/genAI-analyzer/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider
+++ b/oap-server/analyzer/gen-ai-analyzer/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider
@@ -15,4 +15,4 @@
# limitations under the License.
#
-org.apache.skywalking.oap.meter.analyzer.GenAIAnalyzerModuleProvider
\ No newline at end of file
+org.apache.skywalking.oap.analyzer.genai.GenAIAnalyzerModuleProvider
diff --git a/oap-server/analyzer/pom.xml b/oap-server/analyzer/pom.xml
index fa78c963f434..091eea2f35f6 100644
--- a/oap-server/analyzer/pom.xml
+++ b/oap-server/analyzer/pom.xml
@@ -34,7 +34,7 @@
meter-analyzer
log-analyzer
hierarchy
- genAI-analyzer
+ gen-ai-analyzer
diff --git a/oap-server/server-starter/pom.xml b/oap-server/server-starter/pom.xml
index 09b5138c2f62..cc510317a504 100644
--- a/oap-server/server-starter/pom.xml
+++ b/oap-server/server-starter/pom.xml
@@ -346,7 +346,7 @@
log-mal-rules/
telegraf-rules/
cilium-rules/
- gen-ai-settings.yml
+ gen-ai-config.yml
diff --git a/oap-server/server-starter/src/main/resources/application.yml b/oap-server/server-starter/src/main/resources/application.yml
index 2c866302fa9b..7f2223f8fc4a 100644
--- a/oap-server/server-starter/src/main/resources/application.yml
+++ b/oap-server/server-starter/src/main/resources/application.yml
@@ -244,7 +244,7 @@ event-analyzer:
selector: ${SW_EVENT_ANALYZER:default}
default:
-genAI-analyzer:
+gen-ai-analyzer:
selector: ${SW_GENAI_ANALYZER:default}
default:
diff --git a/oap-server/server-starter/src/main/resources/gen-ai-config.yml b/oap-server/server-starter/src/main/resources/gen-ai-config.yml
index 5d6ad7291847..49cba6f37352 100644
--- a/oap-server/server-starter/src/main/resources/gen-ai-config.yml
+++ b/oap-server/server-starter/src/main/resources/gen-ai-config.yml
@@ -88,4 +88,4 @@ providers:
- provider: azure_openai
prefix-match:
- - azure
\ No newline at end of file
+ - azure
diff --git a/oap-server/server-starter/src/main/resources/oal/virtual-gen-ai.oal b/oap-server/server-starter/src/main/resources/oal/virtual-gen-ai.oal
index dd5331c85200..ee07e548bec5 100644
--- a/oap-server/server-starter/src/main/resources/oal/virtual-gen-ai.oal
+++ b/oap-server/server-starter/src/main/resources/oal/virtual-gen-ai.oal
@@ -20,13 +20,16 @@ gen_ai_provider_resp_time = from(GenAIProviderAccess.latency).longAvg();
gen_ai_provider_sla = from(GenAIProviderAccess.*).percent(status == true);
gen_ai_provider_cpm = from(GenAIProviderAccess.*).cpm();
-gen_ai_provider_latency_avg = from(GenAIProviderAccess.latency).longAvg();
-gen_ai_provider_latency_percentile = from(GenAIProviderAccess.latency).percentile(10);
+gen_ai_provider_latency_percentile = from(GenAIProviderAccess.latency).percentile2(10);
gen_ai_provider_input_tokens_sum = from(GenAIProviderAccess.inputTokens).sum();
+gen_ai_provider_input_tokens_avg = from(GenAIProviderAccess.inputTokens).longAvg();
+
gen_ai_provider_output_tokens_sum = from(GenAIProviderAccess.outputTokens).sum();
+gen_ai_provider_output_tokens_avg = from(GenAIProviderAccess.outputTokens).longAvg();
gen_ai_provider_total_cost = from(GenAIProviderAccess.totalCost).sum();
+gen_ai_provider_avg_cost = from(GenAIProviderAccess.totalCost).doubleAvg();
gen_ai_model_call_cpm = from(GenAIModelAccess.*).cpm();
@@ -34,12 +37,15 @@ gen_ai_model_sla = from(GenAIModelAccess.*).percent(status == true);
gen_ai_model_latency_avg = from(GenAIModelAccess.latency).longAvg();
-gen_ai_model_latency_percentile = from(GenAIModelAccess.latency).percentile(10);
+gen_ai_model_latency_percentile = from(GenAIModelAccess.latency).percentile2(10);
gen_ai_model_ttft_avg = from(GenAIModelAccess.timeToFirstToken).filter(timeToFirstToken > 0).longAvg();
-gen_ai_model_ttft_percentile = from(GenAIModelAccess.timeToFirstToken).filter(timeToFirstToken > 0).percentile(10);
+gen_ai_model_ttft_percentile = from(GenAIModelAccess.timeToFirstToken).filter(timeToFirstToken > 0).percentile2(10);
gen_ai_model_input_tokens_sum = from(GenAIModelAccess.inputTokens).sum();
+gen_ai_model_input_tokens_avg = from(GenAIModelAccess.inputTokens).longAvg();
gen_ai_model_output_tokens_sum = from(GenAIModelAccess.outputTokens).sum();
+gen_ai_model_output_tokens_avg = from(GenAIModelAccess.outputTokens).longAvg();
-gen_ai_model_total_cost = from(GenAIModelAccess.totalCost).sum();
\ No newline at end of file
+gen_ai_model_total_cost = from(GenAIModelAccess.totalCost).sum();
+gen_ai_model_avg_cost = from(GenAIModelAccess.totalCost).doubleAvg();
\ No newline at end of file
diff --git a/oap-server/server-starter/src/main/resources/ui-initialized-templates/menu.yaml b/oap-server/server-starter/src/main/resources/ui-initialized-templates/menu.yaml
index 73b11a265eed..2a1c862e8764 100644
--- a/oap-server/server-starter/src/main/resources/ui-initialized-templates/menu.yaml
+++ b/oap-server/server-starter/src/main/resources/ui-initialized-templates/menu.yaml
@@ -281,4 +281,4 @@ menus:
layer: VIRTUAL_GENAI
description: Observe the virtual GenAI providers and models which are conjectured by language agents through various plugins.
documentLink: https://skywalking.apache.org/docs/main/next/en/setup/service-agent/virtual-genai/
- i18nKey: virtual_gen_ai
\ No newline at end of file
+ i18nKey: virtual_gen_ai
diff --git a/oap-server/server-starter/src/main/resources/ui-initialized-templates/virtual_genai/virtual-genai-model.json b/oap-server/server-starter/src/main/resources/ui-initialized-templates/virtual_genai/virtual-genai-model.json
index dbeafb0e38ac..610cf90adea1 100644
--- a/oap-server/server-starter/src/main/resources/ui-initialized-templates/virtual_genai/virtual-genai-model.json
+++ b/oap-server/server-starter/src/main/resources/ui-initialized-templates/virtual_genai/virtual-genai-model.json
@@ -107,9 +107,9 @@
},
{
"x": 12,
- "y": 0,
+ "y": 24,
"w": 12,
- "h": 11,
+ "h": 12,
"i": "1",
"type": "Widget",
"expressions": [
@@ -136,9 +136,9 @@
},
{
"x": 0,
- "y": 11,
+ "y": 24,
"w": 12,
- "h": 11,
+ "h": 12,
"i": "0",
"type": "Widget",
"expressions": [
@@ -165,7 +165,7 @@
},
{
"x": 12,
- "y": 11,
+ "y": 36,
"w": 12,
"h": 11,
"i": "5",
@@ -191,38 +191,9 @@
"id": "0-0-5",
"moved": false
},
- {
- "x": 0,
- "y": 22,
- "w": 12,
- "h": 12,
- "i": "14",
- "type": "Widget",
- "expressions": [
- "gen_ai_model_latency_avg"
- ],
- "graph": {
- "type": "Line",
- "showXAxis": true,
- "showYAxis": true
- },
- "widget": {
- "name": "AvgLatency",
- "title": "Average Latency",
- "tips": "The average latency of model access."
- },
- "metricConfig": [
- {
- "label": "Avg Latency",
- "unit": "ms"
- }
- ],
- "id": "0-0-14",
- "moved": false
- },
{
"x": 12,
- "y": 22,
+ "y": 47,
"w": 12,
"h": 12,
"i": "13",
@@ -244,9 +215,9 @@
},
{
"x": 0,
- "y": 34,
+ "y": 36,
"w": 12,
- "h": 10,
+ "h": 11,
"i": "20",
"type": "Widget",
"widget": {
@@ -278,10 +249,10 @@
]
},
{
- "x": 12,
- "y": 34,
+ "x": 0,
+ "y": 47,
"w": 12,
- "h": 10,
+ "h": 12,
"i": "21",
"type": "Widget",
"widget": {
@@ -307,6 +278,108 @@
"typesOfMQE": [
"TIME_SERIES_VALUES"
]
+ },
+ {
+ "x": 0,
+ "y": 11,
+ "w": 12,
+ "h": 13,
+ "i": "22",
+ "type": "Widget",
+ "widget": {
+ "name": "AvgInputTokens",
+ "title": "Average Input Tokens",
+ "tips": "The average number of input tokens used per model call."
+ },
+ "metricConfig": [
+ {
+ "label": "Avg Input Tokens"
+ }
+ ],
+ "graph": {
+ "type": "Line",
+ "step": false,
+ "smooth": false,
+ "showSymbol": true,
+ "showXAxis": true,
+ "showYAxis": true
+ },
+ "id": "0-0-22",
+ "moved": false,
+ "expressions": [
+ "gen_ai_model_input_tokens_avg"
+ ],
+ "typesOfMQE": [
+ "TIME_SERIES_VALUES"
+ ]
+ },
+ {
+ "x": 12,
+ "y": 11,
+ "w": 12,
+ "h": 13,
+ "i": "23",
+ "type": "Widget",
+ "widget": {
+ "name": "AvgOutputTokens",
+ "title": "Average Output Tokens",
+ "tips": "The average number of output tokens used per model call."
+ },
+ "metricConfig": [
+ {
+ "label": "Avg Output Tokens"
+ }
+ ],
+ "graph": {
+ "type": "Line",
+ "step": false,
+ "smooth": false,
+ "showSymbol": true,
+ "showXAxis": true,
+ "showYAxis": true
+ },
+ "id": "0-0-23",
+ "moved": false,
+ "expressions": [
+ "gen_ai_model_output_tokens_avg"
+ ],
+ "typesOfMQE": [
+ "TIME_SERIES_VALUES"
+ ]
+ },
+ {
+ "x": 12,
+ "y": 0,
+ "w": 12,
+ "h": 11,
+ "i": "24",
+ "type": "Widget",
+ "widget": {
+ "name": "AvgCost",
+ "title": "Average Cost",
+ "tips": "The average cost of model calls."
+ },
+ "metricConfig": [
+ {
+ "label": "Avg Cost"
+ }
+ ],
+ "graph": {
+ "type": "Line",
+ "step": false,
+ "smooth": false,
+ "showSymbol": true,
+ "showXAxis": true,
+ "showYAxis": true
+ },
+ "id": "0-0-24",
+ "moved": false,
+ "expressions": [
+ "gen_ai_model_avg_cost"
+ ],
+ "typesOfMQE": [
+ "TIME_SERIES_VALUES"
+ ]
}
]
}
@@ -323,4 +396,4 @@
"isRoot": false
}
}
-]
\ No newline at end of file
+]
diff --git a/oap-server/server-starter/src/main/resources/ui-initialized-templates/virtual_genai/virtual-genai-provider.json b/oap-server/server-starter/src/main/resources/ui-initialized-templates/virtual_genai/virtual-genai-provider.json
index dca377314f3c..3bbbe6923f9e 100644
--- a/oap-server/server-starter/src/main/resources/ui-initialized-templates/virtual_genai/virtual-genai-provider.json
+++ b/oap-server/server-starter/src/main/resources/ui-initialized-templates/virtual_genai/virtual-genai-provider.json
@@ -136,9 +136,9 @@
},
{
"x": 0,
- "y": 11,
+ "y": 34,
"w": 12,
- "h": 11,
+ "h": 12,
"i": "0",
"type": "Widget",
"expressions": [
@@ -165,9 +165,9 @@
},
{
"x": 12,
- "y": 11,
+ "y": 22,
"w": 12,
- "h": 11,
+ "h": 12,
"i": "5",
"type": "Widget",
"expressions": [
@@ -199,7 +199,7 @@
"i": "14",
"type": "Widget",
"expressions": [
- "gen_ai_provider_latency_avg"
+ "gen_ai_provider_avg_cost"
],
"graph": {
"type": "Line",
@@ -207,9 +207,9 @@
"showYAxis": true
},
"widget": {
- "name": "AvgLatency",
- "title": "Average Latency",
- "tips": "The average latency of model access."
+ "name": "AvgCost",
+ "title": "Average Cost",
+ "tips": "The average cost of provider access."
},
"metricConfig": [
{
@@ -218,11 +218,14 @@
}
],
"id": "0-0-14",
- "moved": false
+ "moved": false,
+ "typesOfMQE": [
+ "UNKNOWN"
+ ]
},
{
"x": 12,
- "y": 22,
+ "y": 34,
"w": 12,
"h": 12,
"i": "13",
@@ -241,6 +244,64 @@
},
"id": "0-0-13",
"moved": false
+ },
+ {
+ "x": 12,
+ "y": 11,
+ "w": 12,
+ "h": 11,
+ "i": "20",
+ "type": "Widget",
+ "widget": {
+ "name": "AvgOutputTokens",
+ "title": "Average Output Tokens",
+ "tips": "The average number of output tokens used per provider access."
+ },
+ "graph": {
+ "type": "Line",
+ "step": false,
+ "smooth": false,
+ "showSymbol": true,
+ "showXAxis": true,
+ "showYAxis": true
+ },
+ "id": "0-0-20",
+ "moved": false,
+ "expressions": [
+ "gen_ai_provider_output_tokens_avg"
+ ],
+ "typesOfMQE": [
+ "TIME_SERIES_VALUES"
+ ]
+ },
+ {
+ "x": 0,
+ "y": 11,
+ "w": 12,
+ "h": 11,
+ "i": "21",
+ "type": "Widget",
+ "widget": {
+ "name": "AvgInputTokens",
+ "title": "Average Input Tokens",
+ "tips": "The average number of input tokens used per provider access."
+ },
+ "graph": {
+ "type": "Line",
+ "step": false,
+ "smooth": false,
+ "showSymbol": true,
+ "showXAxis": true,
+ "showYAxis": true
+ },
+ "id": "0-0-21",
+ "moved": false,
+ "expressions": [
+ "gen_ai_provider_input_tokens_avg"
+ ],
+ "typesOfMQE": [
+ "TIME_SERIES_VALUES"
+ ]
}
]
},
@@ -278,3 +339,5 @@
}
}
]
+
+
diff --git a/oap-server/server-starter/src/main/resources/ui-initialized-templates/virtual_genai/virtual-genai-root.json b/oap-server/server-starter/src/main/resources/ui-initialized-templates/virtual_genai/virtual-genai-root.json
index 21f037425094..b2e76a1168d2 100644
--- a/oap-server/server-starter/src/main/resources/ui-initialized-templates/virtual_genai/virtual-genai-root.json
+++ b/oap-server/server-starter/src/main/resources/ui-initialized-templates/virtual_genai/virtual-genai-root.json
@@ -55,3 +55,4 @@
}
}
]
+
diff --git a/skywalking-ui b/skywalking-ui
index 6be09fb26b24..6538cc401d19 160000
--- a/skywalking-ui
+++ b/skywalking-ui
@@ -1 +1 @@
-Subproject commit 6be09fb26b248814f45224e8fded0b1a5fc7a9cf
+Subproject commit 6538cc401d19f768d8b1e075785d991ce7e4739f
diff --git a/test/e2e-v2/cases/storage/expected/config-dump.yml b/test/e2e-v2/cases/storage/expected/config-dump.yml
index 8c0590d6527c..118fa76f7012 100644
--- a/test/e2e-v2/cases/storage/expected/config-dump.yml
+++ b/test/e2e-v2/cases/storage/expected/config-dump.yml
@@ -38,6 +38,7 @@ core.default.enableDataKeeperExecutor=true
agent-analyzer.default.slowCacheReadThreshold=default:20,redis:10
receiver-ebpf.default.continuousPolicyCacheTimeout=60
receiver-ebpf.default.gRPCSslKeyPath=
+gen-ai-analyzer.provider=default
receiver-browser.provider=default
agent-analyzer.default.segmentStatusAnalysisStrategy=FROM_SPAN_STATUS
envoy-metric.default.maxConcurrentCallsPerConnection=0
@@ -64,7 +65,6 @@ telemetry.provider=prometheus
core.default.trainingPeriodHttpUriRecognitionPattern=60
promql.default.restContextPath=/
core.default.maxHeapMemoryUsagePercent=96
-genAI-analyzer.provider=default
aws-firehose.default.contextPath=/
agent-analyzer.default.slowCacheWriteThreshold=default:20,redis:10
envoy-metric.default.maxMessageSize=0
diff --git a/test/e2e-v2/cases/virtual-genai/Dockerfile.provider b/test/e2e-v2/cases/virtual-genai/Dockerfile.provider
index 256187e6c495..93f3935f944a 100644
--- a/test/e2e-v2/cases/virtual-genai/Dockerfile.provider
+++ b/test/e2e-v2/cases/virtual-genai/Dockerfile.provider
@@ -21,7 +21,9 @@ FROM eclipse-temurin:17-jdk AS builder
RUN apt-get update && apt-get install -y git maven && rm -rf /var/lib/apt/lists/*
WORKDIR /source
-RUN git clone https://github.com/spring-projects/spring-ai-examples.git
+RUN git clone https://github.com/spring-projects/spring-ai-examples.git \
+ && cd spring-ai-examples \
+ && git checkout 2a6088db3d18d5fa6fc208b12adf1172d22f77fd
WORKDIR /source/spring-ai-examples/misc/openai-streaming-response
RUN mvn clean package -DskipTests
@@ -39,3 +41,4 @@ COPY --from=builder /app.jar /services/app.jar
EXPOSE 8080
CMD ["sh", "-c", "java -jar /services/app.jar"]
+
diff --git a/test/e2e-v2/cases/virtual-genai/docker-compose.yml b/test/e2e-v2/cases/virtual-genai/docker-compose.yml
index e1839a1a17c4..da9253f2df33 100644
--- a/test/e2e-v2/cases/virtual-genai/docker-compose.yml
+++ b/test/e2e-v2/cases/virtual-genai/docker-compose.yml
@@ -66,4 +66,4 @@ services:
condition: service_healthy
networks:
- e2e:
\ No newline at end of file
+ e2e:
diff --git a/test/e2e-v2/cases/virtual-genai/e2e.yaml b/test/e2e-v2/cases/virtual-genai/e2e.yaml
index cfd52903aa70..3416deb91cec 100644
--- a/test/e2e-v2/cases/virtual-genai/e2e.yaml
+++ b/test/e2e-v2/cases/virtual-genai/e2e.yaml
@@ -42,3 +42,4 @@ verify:
cases:
- includes:
- ./virtual-genai.yaml
+
diff --git a/test/e2e-v2/cases/virtual-genai/expected/instance.yml b/test/e2e-v2/cases/virtual-genai/expected/instance.yml
index d3ffc6ca30a3..f74704b2b875 100644
--- a/test/e2e-v2/cases/virtual-genai/expected/instance.yml
+++ b/test/e2e-v2/cases/virtual-genai/expected/instance.yml
@@ -15,8 +15,8 @@
{{- contains . }}
- id: {{ notEmpty .id }}
- name: gpt-4.1-mini
+ name: gpt-4.1-mini-2025-04-14
instanceuuid: {{ notEmpty .instanceuuid }}
attributes: []
language: UNKNOWN
-{{- end }}
\ No newline at end of file
+{{- end }}
diff --git a/test/e2e-v2/cases/virtual-genai/expected/metrics-has-value-label.yml b/test/e2e-v2/cases/virtual-genai/expected/metrics-has-value-label.yml
index 1df2937d4846..c983c0e19ba3 100644
--- a/test/e2e-v2/cases/virtual-genai/expected/metrics-has-value-label.yml
+++ b/test/e2e-v2/cases/virtual-genai/expected/metrics-has-value-label.yml
@@ -20,7 +20,7 @@ results:
- metric:
labels:
{{- contains .metric.labels }}
- - key: "_"
+ - key: "p"
value: {{ notEmpty .value }}
{{- end}}
values:
@@ -36,3 +36,4 @@ results:
{{- end}}
{{- end}}
error: null
+
diff --git a/test/e2e-v2/cases/virtual-genai/expected/metrics-has-value.yml b/test/e2e-v2/cases/virtual-genai/expected/metrics-has-value.yml
index f68a07155e03..979b9b25775c 100644
--- a/test/e2e-v2/cases/virtual-genai/expected/metrics-has-value.yml
+++ b/test/e2e-v2/cases/virtual-genai/expected/metrics-has-value.yml
@@ -31,4 +31,4 @@ results:
owner: null
{{- end}}
{{- end}}
-error: null
\ No newline at end of file
+error: null
diff --git a/test/e2e-v2/cases/virtual-genai/expected/service.yml b/test/e2e-v2/cases/virtual-genai/expected/service.yml
index 498ee501899a..5471c7d596d8 100644
--- a/test/e2e-v2/cases/virtual-genai/expected/service.yml
+++ b/test/e2e-v2/cases/virtual-genai/expected/service.yml
@@ -21,4 +21,4 @@
layers:
- VIRTUAL_GENAI
normal: false
-{{- end }}
\ No newline at end of file
+{{- end }}
diff --git a/test/e2e-v2/cases/virtual-genai/virtual-genai.yaml b/test/e2e-v2/cases/virtual-genai/virtual-genai.yaml
index ca5f4dc66706..aacfa76bdd48 100644
--- a/test/e2e-v2/cases/virtual-genai/virtual-genai.yaml
+++ b/test/e2e-v2/cases/virtual-genai/virtual-genai.yaml
@@ -26,39 +26,50 @@ cases:
expected: expected/metrics-has-value.yml
- query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=gen_ai_provider_cpm --service-id=b3BlbmFp.0
expected: expected/metrics-has-value.yml
- - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=gen_ai_provider_latency_avg --service-id=b3BlbmFp.0
- expected: expected/metrics-has-value.yml
- query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=gen_ai_provider_latency_percentile --service-id=b3BlbmFp.0
expected: expected/metrics-has-value-label.yml
- query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=gen_ai_provider_input_tokens_sum --service-id=b3BlbmFp.0
expected: expected/metrics-has-value.yml
+ - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=gen_ai_provider_input_tokens_avg --service-id=b3BlbmFp.0
+ expected: expected/metrics-has-value.yml
- query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=gen_ai_provider_output_tokens_sum --service-id=b3BlbmFp.0
expected: expected/metrics-has-value.yml
+ - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=gen_ai_provider_output_tokens_avg --service-id=b3BlbmFp.0
+ expected: expected/metrics-has-value.yml
- query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=gen_ai_provider_total_cost --service-id=b3BlbmFp.0
expected: expected/metrics-has-value.yml
+ - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=gen_ai_provider_avg_cost --service-id=b3BlbmFp.0
+ expected: expected/metrics-has-value.yml
# instance cases
- query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql instance ls --service-id=b3BlbmFp.0
expected: expected/instance.yml
- - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=gen_ai_model_call_cpm --service-id=b3BlbmFp.0 --instance-name=gpt-4.1-mini
+ - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=gen_ai_model_call_cpm --service-id=b3BlbmFp.0 --instance-name=gpt-4.1-mini-2025-04-14
expected: expected/metrics-has-value.yml
- - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=gen_ai_model_sla --service-id=b3BlbmFp.0 --instance-name=gpt-4.1-mini
+ - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=gen_ai_model_sla --service-id=b3BlbmFp.0 --instance-name=gpt-4.1-mini-2025-04-14
expected: expected/metrics-has-value.yml
- - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=gen_ai_model_latency_avg --service-id=b3BlbmFp.0 --instance-name=gpt-4.1-mini
+ - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=gen_ai_model_latency_avg --service-id=b3BlbmFp.0 --instance-name=gpt-4.1-mini-2025-04-14
expected: expected/metrics-has-value.yml
- - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=gen_ai_model_latency_percentile --service-id=b3BlbmFp.0 --instance-name=gpt-4.1-mini
+ - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=gen_ai_model_latency_percentile --service-id=b3BlbmFp.0 --instance-name=gpt-4.1-mini-2025-04-14
expected: expected/metrics-has-value-label.yml
- - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=gen_ai_model_ttft_avg --service-id=b3BlbmFp.0 --instance-name=gpt-4.1-mini
+ - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=gen_ai_model_ttft_avg --service-id=b3BlbmFp.0 --instance-name=gpt-4.1-mini-2025-04-14
expected: expected/metrics-has-value.yml
- - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=gen_ai_model_ttft_percentile --service-id=b3BlbmFp.0 --instance-name=gpt-4.1-mini
+ - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=gen_ai_model_ttft_percentile --service-id=b3BlbmFp.0 --instance-name=gpt-4.1-mini-2025-04-14
expected: expected/metrics-has-value-label.yml
- - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=gen_ai_model_input_tokens_sum --service-id=b3BlbmFp.0 --instance-name=gpt-4.1-mini
+ - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=gen_ai_model_input_tokens_sum --service-id=b3BlbmFp.0 --instance-name=gpt-4.1-mini-2025-04-14
expected: expected/metrics-has-value.yml
- - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=gen_ai_model_output_tokens_sum --service-id=b3BlbmFp.0 --instance-name=gpt-4.1-mini
+ - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=gen_ai_model_input_tokens_avg --service-id=b3BlbmFp.0 --instance-name=gpt-4.1-mini-2025-04-14
expected: expected/metrics-has-value.yml
- - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=gen_ai_model_total_cost --service-id=b3BlbmFp.0 --instance-name=gpt-4.1-mini
+ - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=gen_ai_model_output_tokens_sum --service-id=b3BlbmFp.0 --instance-name=gpt-4.1-mini-2025-04-14
expected: expected/metrics-has-value.yml
+ - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=gen_ai_model_output_tokens_avg --service-id=b3BlbmFp.0 --instance-name=gpt-4.1-mini-2025-04-14
+ expected: expected/metrics-has-value.yml
+ - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=gen_ai_model_total_cost --service-id=b3BlbmFp.0 --instance-name=gpt-4.1-mini-2025-04-14
+ expected: expected/metrics-has-value.yml
+ - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression=gen_ai_model_avg_cost --service-id=b3BlbmFp.0 --instance-name=gpt-4.1-mini-2025-04-14
+ expected: expected/metrics-has-value.yml
+
diff --git a/test/e2e-v2/java-test-service/e2e-service-provider/pom.xml b/test/e2e-v2/java-test-service/e2e-service-provider/pom.xml
index 048bd92375b0..2de07cc331c9 100644
--- a/test/e2e-v2/java-test-service/e2e-service-provider/pom.xml
+++ b/test/e2e-v2/java-test-service/e2e-service-provider/pom.xml
@@ -112,11 +112,6 @@
23.0
-
- com.alibaba
- fastjson
- 1.2.83
-
diff --git a/test/e2e-v2/java-test-service/e2e-service-provider/src/main/java/org/apache/skywalking/e2e/controller/LLMMockController.java b/test/e2e-v2/java-test-service/e2e-service-provider/src/main/java/org/apache/skywalking/e2e/controller/LLMMockController.java
index 24baa68d0088..1d29883ebdd7 100644
--- a/test/e2e-v2/java-test-service/e2e-service-provider/src/main/java/org/apache/skywalking/e2e/controller/LLMMockController.java
+++ b/test/e2e-v2/java-test-service/e2e-service-provider/src/main/java/org/apache/skywalking/e2e/controller/LLMMockController.java
@@ -18,9 +18,7 @@
package org.apache.skywalking.e2e.controller;
-import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -32,7 +30,7 @@
@RequestMapping("/llm")
public class LLMMockController {
@PostMapping("/v1/chat/completions")
- public Object completions(@RequestBody JSONObject request, HttpServletResponse response) throws Exception {
+ public Object completions(HttpServletResponse response) throws Exception {
response.setContentType("text/event-stream");
response.setCharacterEncoding("UTF-8");
diff --git a/test/e2e-v2/script/env b/test/e2e-v2/script/env
index 9d7ff36f3dc0..f9a89d8849f8 100644
--- a/test/e2e-v2/script/env
+++ b/test/e2e-v2/script/env
@@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-SW_AGENT_JAVA_COMMIT=2f1d9e94d6d1ac22d92f4e9c6905901fe646ffdf
+SW_AGENT_JAVA_COMMIT=ac0df43d7140e726eba9e5e5b1b75cf364c71dff
SW_AGENT_SATELLITE_COMMIT=ea27a3f4e126a24775fe12e2aa2695bcb23d99c3
SW_AGENT_NGINX_LUA_COMMIT=c3cee4841798a147d83b96a10914d4ac0e11d0aa
SW_AGENT_NODEJS_COMMIT=4f9a91dad3dfd8cfe5ba8f7bd06b39e11eb5e65e
From ca9704e17be8868d29f38f766068307b88863008 Mon Sep 17 00:00:00 2001
From: peachisai <2581009893@qq.com>
Date: Fri, 20 Mar 2026 20:57:28 +0800
Subject: [PATCH 4/4] fix
---
.../server-starter/src/main/resources/oal/virtual-gen-ai.oal | 2 +-
skywalking-ui | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/oap-server/server-starter/src/main/resources/oal/virtual-gen-ai.oal b/oap-server/server-starter/src/main/resources/oal/virtual-gen-ai.oal
index ee07e548bec5..d828517b9118 100644
--- a/oap-server/server-starter/src/main/resources/oal/virtual-gen-ai.oal
+++ b/oap-server/server-starter/src/main/resources/oal/virtual-gen-ai.oal
@@ -48,4 +48,4 @@ gen_ai_model_output_tokens_sum = from(GenAIModelAccess.outputTokens).sum();
gen_ai_model_output_tokens_avg = from(GenAIModelAccess.outputTokens).longAvg();
gen_ai_model_total_cost = from(GenAIModelAccess.totalCost).sum();
-gen_ai_model_avg_cost = from(GenAIModelAccess.totalCost).doubleAvg();
\ No newline at end of file
+gen_ai_model_avg_cost = from(GenAIModelAccess.totalCost).doubleAvg();
diff --git a/skywalking-ui b/skywalking-ui
index 6538cc401d19..8b004ef3167c 160000
--- a/skywalking-ui
+++ b/skywalking-ui
@@ -1 +1 @@
-Subproject commit 6538cc401d19f768d8b1e075785d991ce7e4739f
+Subproject commit 8b004ef3167c44d1e4176db0bdeaf41efbad016b