Skip to content

Commit

Permalink
feat: Enhance AI console functions
Browse files Browse the repository at this point in the history
1. Support more LLM providers.
2. Move model mapping from LlmProvider to AiRoute.
3. Update the model predicates in AiRoute.
  • Loading branch information
CH3CHO committed Nov 25, 2024
1 parent e8d1c22 commit 2b7ad3b
Show file tree
Hide file tree
Showing 46 changed files with 1,401 additions and 554 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build-and-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
fetch-depth: 1

- name: Build Higress Console Package
run: mvn clean package -f ./backend/pom.xml
run: mvn clean package -f ./backend/pom.xml -Dpmd.language=en

- name: Upload Higress Console Package
uses: actions/upload-artifact@v3
Expand Down
2 changes: 1 addition & 1 deletion backend/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ fi
if [ -n "$DEV" ]; then
BUILD_ARGS="$BUILD_ARGS -Dapp.build.dev=$DEV"
fi
./mvnw clean package -Dmaven.test.skip=true $BUILD_ARGS
./mvnw clean package -Dmaven.test.skip=true -Dpmd.language=en $BUILD_ARGS
docker build -t higress-console:0.0.1 -f Dockerfile .
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,8 @@ private ResponseEntity<Response<WasmPluginInstance>> queryInstance(WasmPluginIns
String name) {
WasmPluginInstance instance = wasmPluginInstanceService.query(scope, target, name, false);
if (instance == null) {
instance = WasmPluginInstance.builder().scope(scope).target(target).pluginName(name).internal(false)
.enabled(false).build();
instance = WasmPluginInstance.builder().pluginName(name).internal(false).enabled(false).build();
instance.setTarget(scope, target);
}
return ControllerUtil.buildResponseEntity(instance);
}
Expand All @@ -234,8 +234,7 @@ private ResponseEntity<Response<WasmPluginInstance>> addOrUpdateInstance(WasmPlu
if (plugin == null) {
throw new ValidationException("Unsupported plugin: " + name);
}
instance.setScope(scope);
instance.setTarget(target);
instance.setTarget(scope, target);
instance = wasmPluginInstanceService.addOrUpdate(instance);
return ControllerUtil.buildResponseEntity(instance);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,4 @@ public class HigressConstants {
public static final String INTERNAL_RESOURCE_NAME_SUFFIX = ".internal";
public static final String FALLBACK_ROUTE_NAME_SUFFIX = ".fallback";
public static final String FALLBACK_FROM_HEADER = "x-higress-fallback-from";
public static final String MODEL_ROUTER_HEADER = "x-higress-llm-provider";
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ public class Separators {

public static final String ASTERISK = "*";

public static final String DOT = ".";

public static final String COMMA = ",";

public static final String EQUALS_SIGN = "=";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* 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;
package com.alibaba.higress.sdk.constant.plugin;

public final class BuiltInPluginName {

Expand All @@ -29,6 +29,7 @@ public final class BuiltInPluginName {
public static final String AI_QUOTA = "ai-quota";
public static final String AI_AGENT = "ai-agent";
public static final String MODEL_ROUTER = "model-router";
public static final String MODEL_MAPPER = "model-mapper";

// Auth
public static final String BASIC_AUTH = "basic-auth";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2022-2024 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;

public class AiProxyConfig {

public static final String ACTIVE_PROVIDER_ID = "activeProviderId";

public static final String PROVIDERS = "providers";
public static final String PROVIDER_ID = "id";
public static final String PROVIDER_TYPE = "type";
public static final String PROVIDER_API_TOKENS = "apiTokens";

public static final String PROTOCOL = "protocol";

public static final String FAILOVER = "failover";
public static final String FAILOVER_ENABLED = "enabled";
public static final String FAILOVER_FAILURE_THRESHOLD = "failureThreshold";
public static final String FAILOVER_SUCCESS_THRESHOLD = "successThreshold";
public static final String FAILOVER_HEALTH_CHECK_INTERVAL = "healthCheckInterval";
public static final String FAILOVER_HEALTH_CHECK_TIMEOUT = "healthCheckTimeout";
public static final String FAILOVER_HEALTH_CHECK_MODEL = "healthCheckModel";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright (c) 2022-2024 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;

public class KeyAuthConfig {

public static final String CONSUMERS = "consumers";
public static final String CONSUMER_NAME = "name";
public static final String CONSUMER_CREDENTIAL = "credential";

public static final String KEYS = "keys";
public static final String IN_HEADER = "in_header";
public static final String IN_QUERY = "in_query";

public static final String ALLOW = "allow";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright (c) 2022-2024 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;

public class ModelMapperConfig {

public static final String MODEL_MAPPING = "modelMapping";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright (c) 2022-2024 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;

public class ModelRouterConfig {

public static final String MODEL_TO_HEADER = "modelToHeader";
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@
*/
package com.alibaba.higress.sdk.model;

import java.util.HashMap;
import java.util.Map;

import org.apache.commons.collections4.MapUtils;

import com.alibaba.higress.sdk.util.MapUtil;

import io.swagger.annotations.ApiModel;
import lombok.AllArgsConstructor;
import lombok.Builder;
Expand All @@ -29,10 +34,14 @@ public class WasmPluginInstance implements VersionedDto {

private String version;

@Deprecated
private WasmPluginInstanceScope scope;

@Deprecated
private String target;

private Map<WasmPluginInstanceScope, String> targets;

private String pluginName;

private String pluginVersion;
Expand All @@ -48,4 +57,45 @@ public class WasmPluginInstance implements VersionedDto {
public boolean isInternal() {
return Boolean.TRUE.equals(internal);
}

public void syncDeprecatedFields() {
if (scope == null && MapUtils.isEmpty(targets)) {
return;
}
if (scope != null) {
targets = MapUtil.of(scope, target);
} else if (targets.size() == 1) {
Map.Entry<WasmPluginInstanceScope, String> entry = targets.entrySet().iterator().next();
scope = entry.getKey();
target = entry.getValue();
} else {
// We don't know which one to choose, so skip syncing.
}
}

public boolean hasScopedTarget(WasmPluginInstanceScope scope) {
return targets != null && targets.containsKey(scope);
}

public void setGlobalTarget() {
setTarget(WasmPluginInstanceScope.GLOBAL, null);
}

public void setTarget(WasmPluginInstanceScope scope, String target) {
if (targets == null) {
targets = new HashMap<>();
} else {
targets.clear();
}
targets.put(scope, target);
syncDeprecatedFields();
}

public void putTarget(WasmPluginInstanceScope scope, String target) {
if (targets == null) {
targets = new HashMap<>();
}
targets.put(scope, target);
syncDeprecatedFields();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
package com.alibaba.higress.sdk.model;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

Expand All @@ -39,6 +40,8 @@ public enum WasmPluginInstanceScope {
*/
SERVICE("service", 1000);

public static final List<WasmPluginInstanceScope> NON_GLOBAL_SCOPES = Arrays.asList(DOMAIN, ROUTE, SERVICE);

private static final Map<String, WasmPluginInstanceScope> ID_SCOPE_MAPPING = Arrays
.stream(WasmPluginInstanceScope.values()).collect(Collectors.toMap(WasmPluginInstanceScope::getId, s -> s));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,20 @@
*/
package com.alibaba.higress.sdk.model.ai;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import com.alibaba.higress.sdk.exception.ValidationException;
import com.alibaba.higress.sdk.model.route.RoutePredicate;
import com.alibaba.higress.sdk.model.route.RoutePredicateTypeEnum;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AiModelPredicate {

private Boolean enabled;
private String prefix;
public class AiModelPredicate extends RoutePredicate {

public void validate() {
super.validate();
RoutePredicateTypeEnum predicateType = RoutePredicateTypeEnum.fromName(this.getMatchType());
if (predicateType == null) {
throw new ValidationException("Unknown matchType: " + this.getMatchType());
}
if (predicateType == RoutePredicateTypeEnum.REGULAR) {
throw new ValidationException("AiModelPredicate does not support regular expression matchType");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,26 +36,26 @@ public class AiRoute {
private String version;
private List<String> domains;
private List<AiUpstream> upstreams;
private AiModelPredicate modelPredicate;
private List<AiModelPredicate> modelPredicates;
private AiRouteAuthConfig authConfig;
private AiRouteFallbackConfig fallbackConfig;

public void validate() {
if (StringUtils.isBlank(name)) {
throw new ValidationException("name cannot be blank.");
}
if (CollectionUtils.isEmpty(upstreams)){
if (CollectionUtils.isEmpty(upstreams)) {
throw new ValidationException("upstreams cannot be empty.");
}
upstreams.forEach(AiUpstream::validate);
if (modelPredicate != null){
modelPredicate.validate();
}
if (authConfig != null){
if (authConfig != null) {
authConfig.validate();
}
if (fallbackConfig != null){
if (fallbackConfig != null) {
fallbackConfig.validate();
}
if (CollectionUtils.isNotEmpty(modelPredicates)) {
modelPredicates.forEach(AiModelPredicate::validate);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
*/
package com.alibaba.higress.sdk.model.ai;

import java.util.Map;

import org.apache.commons.lang3.StringUtils;

import com.alibaba.higress.sdk.exception.ValidationException;
Expand All @@ -31,6 +33,7 @@ public class AiUpstream {

private String provider;
private Integer weight;
private Map<String, String> modelMapping;

public void validate() {
if (StringUtils.isEmpty(provider)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ public class LlmProvider {
private String name;
private String type;
private String protocol;
private Map<String, String> modelMapping;
private List<String> tokens;
private TokenFailoverConfig tokenFailoverConfig;
private Map<String, Object> rawConfigs;

public void validate(boolean forUpdate) {
if (StringUtils.isBlank(name)) {
Expand Down
Loading

0 comments on commit 2b7ad3b

Please sign in to comment.