Skip to content

Commit

Permalink
feat: Support more LLM providers on the frontend (#412)
Browse files Browse the repository at this point in the history
  • Loading branch information
CH3CHO authored Jan 20, 2025
1 parent 99e4ed4 commit 2753367
Show file tree
Hide file tree
Showing 12 changed files with 389 additions and 123 deletions.
2 changes: 1 addition & 1 deletion backend/console/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<properties>
<nodejs.version>16.19.0</nodejs.version>

<app.build.version>2.0.0</app.build.version>
<app.build.version>2.1.0</app.build.version>
<app.build.dev>true</app.build.dev>
</properties>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ public class FrontEndI18nResourceChecker {
private static final String I18N_RESOURCE_FILE_NAME = "translation.json";
private static final List<String> TS_FILE_EXTENSIONS = Arrays.asList(".ts", ".tsx");

private static final Set<String> IMPLICITLY_USED_RESOURCE_KEYS = Set.of("init.title", "login.title", "aiRoute.edit",
"tlsCertificate.editTlsCertificate", "serviceSource.editServiceSource", "llmProvider.edit",
private static final Set<String> IMPLICITLY_USED_RESOURCE_KEYS = Set.of("index.title", "init.title", "login.title",
"aiRoute.edit", "tlsCertificate.editTlsCertificate", "serviceSource.editServiceSource", "llmProvider.edit",
"plugins.editPlugin", "route.editRoute", "domain.editDomain", "consumer.edit");
private static final List<String> IMPLICITLY_USED_RESOURCE_KEY_PREFIXES =
Arrays.asList("menu.", "request.error.", "serviceSource.types.", "llmProvider.providerTypes.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,6 @@ private LlmProviderType() {
public static final String DOUBAO = "doubao";

public static final String COZE = "coze";

public static final String TOGETHER_AI = "together-ai";
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import com.alibaba.higress.sdk.constant.CommonKey;
import com.alibaba.higress.sdk.constant.HigressConstants;
import com.alibaba.higress.sdk.constant.Separators;
import com.alibaba.higress.sdk.exception.ValidationException;
import com.alibaba.higress.sdk.model.ServiceSource;
import com.alibaba.higress.sdk.model.ai.LlmProvider;
import com.alibaba.higress.sdk.model.ai.LlmProviderProtocol;
Expand Down Expand Up @@ -166,6 +167,21 @@ public UpstreamService buildUpstreamService(String providerName, Map<String, Obj

protected abstract String getServiceProtocol(Map<String, Object> providerConfig);

protected static int getIntConfig(Map<String, Object> providerConfig, String key) {
Object serverPortObj = providerConfig.get(key);
if (serverPortObj instanceof Integer) {
return (Integer)serverPortObj;
}
if (serverPortObj instanceof String serverPortStr) {
try {
return Integer.parseInt(serverPortStr);
} catch (NumberFormatException e) {
throw new ValidationException(key + " must be a number.");
}
}
throw new ValidationException(key + " must be a number.");
}

protected static String generateServiceProviderName(String llmProviderName) {
return CommonKey.LLM_SERVICE_NAME_PREFIX + llmProviderName + HigressConstants.INTERNAL_RESOURCE_NAME_SUFFIX;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import java.net.URI;
import java.net.URISyntaxException;
import java.util.Locale;
import java.util.Map;

import org.apache.commons.collections4.MapUtils;
Expand All @@ -34,20 +35,17 @@ public String getType() {

@Override
public void validateConfig(Map<String, Object> configurations) {
if (MapUtils.isEmpty(configurations)){
if (MapUtils.isEmpty(configurations)) {
throw new ValidationException("Missing Azure specific configurations.");
}
Object serviceUrlObj = configurations.get(SERVICE_URL_KEY);
if (!(serviceUrlObj instanceof String serviceUrl)){
throw new ValidationException(SERVICE_URL_KEY + " must be a string.");
}
if (StringUtils.isEmpty(serviceUrl)) {
throw new ValidationException(SERVICE_URL_KEY + " cannot be empty.");
URI uri = getServiceUri(configurations);
String scheme = uri.getScheme();
if (StringUtils.isEmpty(scheme)) {
throw new ValidationException("Azure service URL must have a scheme.");
}
try {
new URI(serviceUrl);
} catch (URISyntaxException e) {
throw new ValidationException(SERVICE_URL_KEY + " is not a valid URL.", e);
scheme = scheme.toLowerCase(Locale.ROOT);
if (!scheme.equals(V1McpBridge.PROTOCOL_HTTP) && !scheme.equals(V1McpBridge.PROTOCOL_HTTPS)) {
throw new ValidationException("Azure service URL must have a valid scheme.");
}
}

Expand All @@ -58,28 +56,52 @@ protected String getServiceRegistryType(Map<String, Object> providerConfig) {

@Override
protected String getServiceDomain(Map<String, Object> providerConfig) {
if (MapUtils.isEmpty(providerConfig)){
return "";
}
String serviceUrl = (String)providerConfig.get(SERVICE_URL_KEY);
if (StringUtils.isEmpty(serviceUrl)) {
return "";
}
try {
URI uri = new URI(serviceUrl);
return uri.getHost();
} catch (URISyntaxException e) {
return null;
}
URI uri = getServiceUri(providerConfig);
return uri.getHost();
}

@Override
protected int getServicePort(Map<String, Object> providerConfig) {
return 443;
URI uri = getServiceUri(providerConfig);
String scheme = uri.getScheme();
if (scheme == null) {
return 80;
}
return switch (scheme.toLowerCase(Locale.ROOT)) {
case V1McpBridge.PROTOCOL_HTTP -> 80;
case V1McpBridge.PROTOCOL_HTTPS -> 443;
default -> 80;
};
}

@Override
protected String getServiceProtocol(Map<String, Object> providerConfig) {
return V1McpBridge.PROTOCOL_HTTPS;
URI uri = getServiceUri(providerConfig);
String scheme = uri.getScheme();
if (scheme == null) {
return V1McpBridge.PROTOCOL_HTTP;
}
return switch (scheme.toLowerCase(Locale.ROOT)) {
case V1McpBridge.PROTOCOL_HTTP, V1McpBridge.PROTOCOL_HTTPS -> scheme;
default -> V1McpBridge.PROTOCOL_HTTP;
};
}

private static URI getServiceUri(Map<String, Object> providerConfig) {
if (MapUtils.isEmpty(providerConfig)) {
throw new ValidationException("Missing Azure specific configurations.");
}
Object serviceUrlObj = providerConfig.get(SERVICE_URL_KEY);
if (!(serviceUrlObj instanceof String serviceUrl)) {
throw new ValidationException(SERVICE_URL_KEY + " must be a string.");
}
if (StringUtils.isEmpty(serviceUrl)) {
throw new ValidationException(SERVICE_URL_KEY + " cannot be empty.");
}
try {
return new URI(serviceUrl);
} catch (URISyntaxException e) {
throw new ValidationException(SERVICE_URL_KEY + " is not a valid URL.", e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,7 @@ public void validateConfig(Map<String, Object> configurations) {
if (StringUtils.isEmpty(serverHost)) {
throw new ValidationException(SERVER_HOST_KEY + " cannot be empty.");
}
Object serverPortObj = configurations.get(SERVER_PORT_KEY);
if (!(serverPortObj instanceof Integer serverPort)) {
throw new ValidationException(SERVER_PORT_KEY + " must be a number.");
}
int serverPort = getIntConfig(configurations, SERVER_PORT_KEY);
if (!ValidateUtil.checkPort(serverPort)) {
throw new ValidationException(SERVER_PORT_KEY + " must be a valid port number.");
}
Expand Down
39 changes: 36 additions & 3 deletions frontend/src/locales/en-US/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,30 @@
"providerTypes": {
"openai": "OpenAI",
"qwen": "Tongyi Qianwen",
"moonshot": "Moonshot"
"moonshot": "Moonshot",
"ai360": "360 Zhinao|",
"azure": "Azure OpenAI",
"baichuan": "Baichuan AI",
"baidu": "ERNIE Bot",
"claude": "Anthropic Claude",
"cloudflare": "Cloudflare Workers AI",
"cohere": "Cohere",
"coze": "Coze",
"deepl": "DeepL",
"deepseek": "DeepSeek",
"doubao": "Doubao",
"gemini": "Google Gemini",
"github": "GitHub Models",
"groq": "Groq",
"hunyuan": "Tencent Hunyuan",
"minimax": "MiniMax",
"mistral": "Mistral",
"ollama": "Ollama",
"together-ai": "Together AI",
"stepfun": "Stepfun",
"spark": "iFlyTek Spark",
"yi": "01.AI",
"zhipuai": "Zhipu AI"
},
"providerForm": {
"label": {
Expand All @@ -201,7 +224,10 @@
"healthCheckTimeout": "Health Check Request Timeout",
"protocol": "Request Procotol",
"serviceName": "Service Name",
"successThreshold": "Min Consecuitive Sucesses to Mark Up a Token"
"successThreshold": "Min Consecuitive Sucesses to Mark Up a Token",
"azureServiceUrl": "Azure Service URL",
"ollamaServerHost": "Ollama Service Host",
"ollamaServerPort": "Ollama Service Port"
},
"rules": {
"tokenRequired": "Please input auth token",
Expand All @@ -212,7 +238,14 @@
"healthCheckIntervalRequired": "Please input health check request interval",
"healthCheckModelRequired": "Please input health check request LLM model",
"protocol": "Please select a request protocol",
"successThresholdRequired": "Please input min consecuitive sucesses to mark up a token"
"successThresholdRequired": "Please input min consecuitive sucesses to mark up a token",
"azureServiceUrlRequired": "Please input Azure service URL",
"ollamaServerHostRequired": "Please input Ollama service host",
"ollamaServerPortRequired": "Please input Ollama service port"
},
"placeholder": {
"azureServiceUrlPlaceholder": "It shall contain \"/chat/completions\" in the path and \"api-version\" in the query string",
"ollamaServerHostPlaceholder": "Please input a hostname, domain name or IP address"
}
},
"create": "Create AI Service Provider",
Expand Down
39 changes: 36 additions & 3 deletions frontend/src/locales/zh-CN/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,30 @@
"providerTypes": {
"openai": "OpenAI",
"qwen": "通义千问",
"moonshot": "月之暗面"
"moonshot": "月之暗面",
"ai360": "360智脑",
"azure": "Azure OpenAI",
"baichuan": "百川智能",
"baidu": "文心一言",
"claude": "Anthropic Claude",
"cloudflare": "Cloudflare Workers AI",
"cohere": "Cohere",
"coze": "扣子",
"deepl": "DeepL",
"deepseek": "DeepSeek",
"doubao": "豆包",
"gemini": "Google Gemini",
"github": "GitHub模型",
"groq": "Groq",
"hunyuan": "混元",
"minimax": "MiniMax",
"mistral": "Mistral",
"ollama": "Ollama",
"together-ai": "Together AI",
"stepfun": "阶跃星辰",
"spark": "星火",
"yi": "零一万物",
"zhipuai": "智谱AI"
},
"providerForm": {
"label": {
Expand All @@ -200,7 +223,10 @@
"healthCheckTimeout": "健康检测请求超时时间",
"protocol": "协议",
"serviceName": "服务名称",
"successThreshold": "令牌可用时需满足的最小连续健康检测成功次数"
"successThreshold": "令牌可用时需满足的最小连续健康检测成功次数",
"azureServiceUrl": "Azure 服务 URL",
"ollamaServerHost": "Ollama 服务主机名",
"ollamaServerPort": "Ollama 服务端口"
},
"rules": {
"tokenRequired": "请输入凭证",
Expand All @@ -211,7 +237,14 @@
"healthCheckIntervalRequired": "请输入健康检测请求发起间隔",
"healthCheckModelRequired": "请输入健康检测请求使用的模型名称",
"protocol": "请选择请求协议",
"successThresholdRequired": "请输入最小连续健康检测成功次数"
"successThresholdRequired": "请输入最小连续健康检测成功次数",
"azureServiceUrlRequired": "请输入 Azure 服务 URL",
"ollamaServerHostRequired": "请输入 Ollama 服务主机名",
"ollamaServerPortRequired": "请输入 Ollama 服务端口"
},
"placeholder": {
"azureServiceUrlPlaceholder": "需包含“/chat/completions”路径和“api-version”查询参数",
"ollamaServerHostPlaceholder": "请填写机器名、域名或 IP 地址"
}
},
"create": "创建AI服务提供者",
Expand Down
Loading

0 comments on commit 2753367

Please sign in to comment.