diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/constant/plugin/config/AiStatisticsConfig.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/constant/plugin/config/AiStatisticsConfig.java new file mode 100644 index 00000000..ad151703 --- /dev/null +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/constant/plugin/config/AiStatisticsConfig.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2022-2025 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.higress.sdk.constant.plugin.config; + +import java.util.HashMap; +import java.util.Map; + +public class AiStatisticsConfig { + + public static final String ATTRIBUTES = "attributes"; + + public static final String KEY = "key"; + public static final String VALUE_SOURCE = "value_source"; + public static final String VALUE = "value"; + public static final String RULE = "rule"; + public static final String APPLY_TO_LOG = "apply_to_log"; + public static final String APPLY_TO_SPAN = "apply_to_span"; + + public static class ValueSource { + + public static final String FIXED_VALUE = "fixed_value"; + public static final String REQUEST_HEADER = "request_header"; + public static final String REQUEST_BODY = "request_body"; + public static final String RESPONSE_HEADER = "response_header"; + public static final String RESPONSE_BODY = "response_body"; + public static final String RESPONSE_STREAMING_BODY = "response_streaming_body"; + } + + public static class Rule { + + public static final String FIRST = "first"; + public static final String REPLACE = "replace"; + public static final String APPEND = "append"; + } + + public static Map buildAttribute(String key, String valueSource, String value, String rule, + Boolean applyToLog, Boolean applyToSpan) { + Map attribute = new HashMap<>(); + if (key != null) { + key = key.trim(); + attribute.put(KEY, key); + } + if (valueSource != null) { + attribute.put(VALUE_SOURCE, valueSource); + } + if (value != null) { + attribute.put(VALUE, value); + } + if (rule != null) { + attribute.put(RULE, rule); + } + if (applyToLog != null) { + attribute.put(APPLY_TO_LOG, applyToLog); + } + if (applyToSpan != null) { + attribute.put(APPLY_TO_SPAN, applyToSpan); + } + return attribute; + } +} diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/ai/AiRouteServiceImpl.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/ai/AiRouteServiceImpl.java index 25887949..6fabaceb 100644 --- a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/ai/AiRouteServiceImpl.java +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/ai/AiRouteServiceImpl.java @@ -20,6 +20,7 @@ import java.util.Map; import java.util.Optional; +import com.alibaba.higress.sdk.constant.plugin.config.AiStatisticsConfig; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.MapUtils; import org.apache.commons.io.IOUtils; @@ -215,6 +216,7 @@ private void writeAiRouteResources(AiRoute aiRoute) { writeAuthConfigResources(routeName, aiRoute.getAuthConfig()); writeModelRouteResources(aiRoute.getModelPredicates()); writeModelMappingResources(routeName, aiRoute.getUpstreams()); + writeAiStatisticsResources(routeName); } private void writeAiRouteFallbackResources(AiRoute aiRoute) { @@ -273,6 +275,7 @@ private void writeAiRouteFallbackResources(AiRoute aiRoute) { writeAuthConfigResources(fallbackRouteName, aiRoute.getAuthConfig()); writeModelMappingResources(fallbackRouteName, fallbackUpStreams); + writeAiStatisticsResources(fallbackRouteName); } private void writeAuthConfigResources(String routeName, AiRouteAuthConfig authConfig) { @@ -347,6 +350,32 @@ private void writeModelMappingResources(String routeName, List upstr } } + private void writeAiStatisticsResources(String routeName) { + WasmPluginInstance existedInstance = wasmPluginInstanceService.query(WasmPluginInstanceScope.ROUTE, routeName, + BuiltInPluginName.AI_STATISTICS, false); + if (existedInstance != null) { + return; + } + + WasmPluginInstance instance = wasmPluginInstanceService.createEmptyInstance(BuiltInPluginName.AI_STATISTICS); + instance.setTarget(WasmPluginInstanceScope.ROUTE, routeName); + instance.setEnabled(true); + instance.setInternal(false); + + Map questionAttribute = AiStatisticsConfig.buildAttribute("question", + AiStatisticsConfig.ValueSource.REQUEST_BODY, "messages.@reverse.0.content", null, true, null); + Map streamingAnswerAttribute = + AiStatisticsConfig.buildAttribute("answer", AiStatisticsConfig.ValueSource.RESPONSE_STREAMING_BODY, + "choices.0.delta.content", AiStatisticsConfig.Rule.APPEND, true, null); + Map nonStreamingAnswerAttribute = AiStatisticsConfig.buildAttribute("answer", + AiStatisticsConfig.ValueSource.RESPONSE_BODY, "choices.0.message.content", null, true, null); + List> attributes = + List.of(questionAttribute, streamingAnswerAttribute, nonStreamingAnswerAttribute); + instance.setConfigurations(Map.of(AiStatisticsConfig.ATTRIBUTES, attributes)); + + wasmPluginInstanceService.addOrUpdate(instance); + } + private Route buildRoute(String routeName, AiRoute aiRoute) { Route route = new Route(); route.setName(routeName);