diff --git a/open-sse/config/constants.js b/open-sse/config/constants.js index 39b2e32..eced434 100644 --- a/open-sse/config/constants.js +++ b/open-sse/config/constants.js @@ -223,6 +223,11 @@ export const PROVIDERS = { "Anthropic-Beta": "claude-code-20250219,interleaved-thinking-2025-05-14" } }, + alicode: { + baseUrl: "https://coding.dashscope.aliyuncs.com/v1/chat/completions", + format: "openai", + headers: {} + }, github: { baseUrl: "https://api.githubcopilot.com/chat/completions", // GitHub Copilot API endpoint for chat responsesUrl: "https://api.githubcopilot.com/responses", diff --git a/open-sse/config/providerModels.js b/open-sse/config/providerModels.js index 1c17553..9491178 100644 --- a/open-sse/config/providerModels.js +++ b/open-sse/config/providerModels.js @@ -186,6 +186,16 @@ export const PROVIDER_MODELS = { { id: "MiniMax-M2.5", name: "MiniMax M2.5" }, { id: "MiniMax-M2.1", name: "MiniMax M2.1" }, ], + alicode: [ + { id: "qwen3.5-plus", name: "Qwen3.5 Plus" }, + { id: "kimi-k2.5", name: "Kimi K2.5" }, + { id: "glm-5", name: "GLM 5" }, + { id: "MiniMax-M2.5", name: "MiniMax M2.5" }, + { id: "qwen3-max-2026-01-23", name: "Qwen3 Max" }, + { id: "qwen3-coder-next", name: "Qwen3 Coder Next" }, + { id: "qwen3-coder-plus", name: "Qwen3 Coder Plus" }, + { id: "glm-4.7", name: "GLM 4.7" }, + ], deepseek: [ { id: "deepseek-chat", name: "DeepSeek V3.2 Chat" }, { id: "deepseek-reasoner", name: "DeepSeek V3.2 Reasoner" }, @@ -324,6 +334,7 @@ export const PROVIDER_ID_TO_ALIAS = { kimi: "kimi", minimax: "minimax", "minimax-cn": "minimax-cn", + alicode: "alicode", deepseek: "deepseek", groq: "groq", xai: "xai", diff --git a/src/app/api/providers/[id]/models/route.js b/src/app/api/providers/[id]/models/route.js index 3b92d7a..a3c4bcb 100644 --- a/src/app/api/providers/[id]/models/route.js +++ b/src/app/api/providers/[id]/models/route.js @@ -120,6 +120,15 @@ const PROVIDER_MODELS_CONFIG = { parseResponse: (data) => data.data || [] }, + alicode: { + url: "https://coding.dashscope.aliyuncs.com/v1/models", + method: "GET", + headers: { "Content-Type": "application/json" }, + authHeader: "Authorization", + authPrefix: "Bearer ", + parseResponse: (data) => data.data || [] + }, + // OpenAI-compatible API key providers deepseek: createOpenAIModelsConfig("https://api.deepseek.com/models"), groq: createOpenAIModelsConfig("https://api.groq.com/openai/v1/models"), diff --git a/src/app/api/providers/[id]/test/testUtils.js b/src/app/api/providers/[id]/test/testUtils.js index 4049426..ce934ae 100644 --- a/src/app/api/providers/[id]/test/testUtils.js +++ b/src/app/api/providers/[id]/test/testUtils.js @@ -1,5 +1,6 @@ import { getProviderConnectionById, updateProviderConnection } from "@/lib/localDb"; import { isOpenAICompatibleProvider, isAnthropicCompatibleProvider } from "@/shared/constants/providers"; +import { getDefaultModel } from "open-sse/config/providerModels.js"; import { GEMINI_CONFIG, ANTIGRAVITY_CONFIG, @@ -308,6 +309,16 @@ async function testApiKeyConnection(connection) { const valid = res.status !== 401 && res.status !== 403; return { valid, error: valid ? null : "Invalid API key" }; } + case "alicode": { + // Aliyun Coding Plan uses OpenAI-compatible API + const res = await fetch("https://coding.dashscope.aliyuncs.com/v1/chat/completions", { + method: "POST", + headers: { "Authorization": `Bearer ${connection.apiKey}`, "content-type": "application/json" }, + body: JSON.stringify({ model: getDefaultModel("alicode"), max_tokens: 1, messages: [{ role: "user", content: "test" }] }), + }); + const valid = res.status !== 401 && res.status !== 403; + return { valid, error: valid ? null : "Invalid API key" }; + } case "deepseek": { const res = await fetch("https://api.deepseek.com/models", { headers: { Authorization: `Bearer ${connection.apiKey}` } }); return { valid: res.ok, error: res.ok ? null : "Invalid API key" }; diff --git a/src/app/api/providers/validate/route.js b/src/app/api/providers/validate/route.js index 26c083d..2934953 100644 --- a/src/app/api/providers/validate/route.js +++ b/src/app/api/providers/validate/route.js @@ -1,6 +1,7 @@ import { NextResponse } from "next/server"; import { getProviderNodeById } from "@/models"; import { isOpenAICompatibleProvider, isAnthropicCompatibleProvider } from "@/shared/constants/providers"; +import { getDefaultModel } from "open-sse/config/providerModels.js"; // POST /api/providers/validate - Validate API key with provider export async function POST(request) { @@ -102,17 +103,20 @@ export async function POST(request) { case "glm-cn": case "kimi": case "minimax": - case "minimax-cn": { + case "minimax-cn": + case "alicode": { const claudeBaseUrls = { glm: "https://api.z.ai/api/anthropic/v1/messages", "glm-cn": "https://open.bigmodel.cn/api/coding/paas/v4/chat/completions", kimi: "https://api.kimi.com/coding/v1/messages", minimax: "https://api.minimax.io/anthropic/v1/messages", "minimax-cn": "https://api.minimaxi.com/anthropic/v1/messages", + alicode: "https://coding.dashscope.aliyuncs.com/v1/chat/completions", }; - // glm-cn uses OpenAI format - if (provider === "glm-cn") { + // glm-cn and alicode use OpenAI format + if (provider === "glm-cn" || provider === "alicode") { + const testModel = getDefaultModel(provider); const glmCnRes = await fetch(claudeBaseUrls[provider], { method: "POST", headers: { @@ -120,7 +124,7 @@ export async function POST(request) { "content-type": "application/json", }, body: JSON.stringify({ - model: "glm-4.7", + model: testModel, max_tokens: 1, messages: [{ role: "user", content: "test" }], }), diff --git a/src/shared/constants/config.js b/src/shared/constants/config.js index a0bcdb4..adad400 100644 --- a/src/shared/constants/config.js +++ b/src/shared/constants/config.js @@ -37,6 +37,7 @@ export const PROVIDER_ENDPOINTS = { kimi: "https://api.kimi.com/coding/v1/messages", minimax: "https://api.minimax.io/anthropic/v1/messages", "minimax-cn": "https://api.minimaxi.com/anthropic/v1/messages", + alicode: "https://coding.dashscope.aliyuncs.com/v1/chat/completions", openai: "https://api.openai.com/v1/chat/completions", anthropic: "https://api.anthropic.com/v1/messages", gemini: "https://generativelanguage.googleapis.com/v1beta/models", diff --git a/src/shared/constants/providers.js b/src/shared/constants/providers.js index 4e780f6..8a64517 100644 --- a/src/shared/constants/providers.js +++ b/src/shared/constants/providers.js @@ -27,6 +27,7 @@ export const APIKEY_PROVIDERS = { kimi: { id: "kimi", alias: "kimi", name: "Kimi", icon: "psychology", color: "#1E3A8A", textIcon: "KM", website: "https://kimi.moonshot.cn" }, minimax: { id: "minimax", alias: "minimax", name: "Minimax Coding", icon: "memory", color: "#7C3AED", textIcon: "MM", website: "https://www.minimaxi.com" }, "minimax-cn": { id: "minimax-cn", alias: "minimax-cn", name: "Minimax (China)", icon: "memory", color: "#DC2626", textIcon: "MC", website: "https://www.minimaxi.com" }, + alicode: { id: "alicode", alias: "alicode", name: "Aliyun Bailian Coding Plan", icon: "cloud", color: "#FF6A00", textIcon: "ALi" }, openai: { id: "openai", alias: "openai", name: "OpenAI", icon: "auto_awesome", color: "#10A37F", textIcon: "OA", website: "https://platform.openai.com" }, anthropic: { id: "anthropic", alias: "anthropic", name: "Anthropic", icon: "smart_toy", color: "#D97757", textIcon: "AN", website: "https://console.anthropic.com" }, gemini: { id: "gemini", alias: "gemini", name: "Gemini", icon: "diamond", color: "#4285F4", textIcon: "GE", website: "https://ai.google.dev" },