mirror of
https://wget.la/https://github.com/leookun/cursor-byok
synced 2026-10-06 05:12:03 +08:00
Console — provider presets inside the model editor: - Preset chips for five coding-plan providers (Zhipu GLM / Kimi / DeepSeek / Volcengine Ark / MiniMax) rendered at the top of the add/edit model dialog - All five providers expose both an Anthropic-protocol and an OpenAI-protocol endpoint; a click fills the endpoint matching the current model type, and switching the type automatically swaps to the same provider's other endpoint (no stale URL/protocol mismatches) - Switching to a different provider clears the API key; switching protocols within the same provider keeps it - Fills base URL (full-request-URL form for irregular paths), request protocol, claude-cli custom headers for Anthropic endpoints, token limits, remark, and the default model id + display name; known preset model ids merge into the model-name combobox Server — model discovery for sub-path endpoints: - model_discovery_url previously replaced the whole path with /v1/models, which strips the /api/anthropic, /coding, /api/paas/v4 style prefixes every coding-plan provider uses, so discovery always 404'd for them. It now appends to the existing path and normalizes endpoint/version suffixes (/chat/completions, /responses, /messages, /v1, /v3, /v4) - Verified live against Zhipu (10 models) and Kimi (4 models) coding plans; existing tests updated to the append semantics plus new URL cases Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
131 lines
4.8 KiB
TypeScript
131 lines
4.8 KiB
TypeScript
import type { ModelType } from "../api";
|
||
import deepseekIcon from "../assets/provider-icons/deepseek.svg";
|
||
import huoshanIcon from "../assets/provider-icons/huoshan.png";
|
||
import kimiIcon from "../assets/provider-icons/kimi.svg";
|
||
import minimaxIcon from "../assets/provider-icons/minimax.svg";
|
||
import zhipuIcon from "../assets/provider-icons/zhipu.svg";
|
||
import { defaultCustomHeaders } from "./modelDefaults";
|
||
|
||
export interface ModelPresetEntry {
|
||
model_id: string;
|
||
display_name: string;
|
||
context_window_tokens: number | null;
|
||
max_output_tokens: number | null;
|
||
}
|
||
|
||
/** 一个服务商在某种协议(anthropic / openai)下的接入端点 */
|
||
export interface ModelPresetEndpoint {
|
||
baseUrl: string;
|
||
/** true 时 baseUrl 即完整请求 URL;false 时由请求协议追加标准端点路径 */
|
||
useFullUrl: boolean;
|
||
/** openai 协议的请求端点(useFullUrl 为 true 时忽略) */
|
||
openaiEndpoint: string;
|
||
/** 非空时启用自定义 Headers(claude-cli 伪装头) */
|
||
customHeaders: Record<string, string> | null;
|
||
}
|
||
|
||
export interface ModelPreset {
|
||
key: string;
|
||
name: string;
|
||
icon: string;
|
||
keyHint: string;
|
||
/** 五家服务商均同时提供 Anthropic 与 OpenAI 兼容协议 */
|
||
endpoints: { anthropic: ModelPresetEndpoint; openai: ModelPresetEndpoint };
|
||
models: ModelPresetEntry[];
|
||
}
|
||
|
||
const entry = (
|
||
modelId: string,
|
||
displayName: string,
|
||
contextWindowTokens: number | null,
|
||
maxOutputTokens: number | null,
|
||
): ModelPresetEntry => ({
|
||
model_id: modelId,
|
||
display_name: displayName,
|
||
context_window_tokens: contextWindowTokens,
|
||
max_output_tokens: maxOutputTokens,
|
||
});
|
||
|
||
const claudeHeaders = { ...defaultCustomHeaders };
|
||
/** anthropic 协议:填 Base URL,自动追加 /v1/messages */
|
||
const anthropic = (baseUrl: string): ModelPresetEndpoint => ({ baseUrl, useFullUrl: false, openaiEndpoint: "", customHeaders: claudeHeaders });
|
||
/** openai 协议:填 Base URL,自动追加 /v1/chat/completions */
|
||
const openaiChat = (baseUrl: string): ModelPresetEndpoint => ({ baseUrl, useFullUrl: false, openaiEndpoint: "/v1/chat/completions", customHeaders: null });
|
||
/** openai 协议:路径不规则,直接给完整请求 URL */
|
||
const openaiFullUrl = (url: string): ModelPresetEndpoint => ({ baseUrl: url, useFullUrl: true, openaiEndpoint: "/v1/chat/completions", customHeaders: null });
|
||
|
||
export const modelPresets: ModelPreset[] = [
|
||
{
|
||
key: "zhipu",
|
||
name: "智谱 GLM",
|
||
icon: zhipuIcon,
|
||
keyHint: "bigmodel.cn → GLM Coding Plan → API Key(套餐 Key 与普通 Key 不通用)",
|
||
endpoints: {
|
||
anthropic: anthropic("https://open.bigmodel.cn/api/anthropic"),
|
||
openai: openaiFullUrl("https://open.bigmodel.cn/api/coding/paas/v4/chat/completions"),
|
||
},
|
||
models: [
|
||
entry("glm-5.3", "GLM 5.3", 1000000, 65536),
|
||
entry("glm-5.2", "GLM 5.2", 200000, 32768),
|
||
entry("glm-4.7", "GLM 4.7", 200000, 32768),
|
||
],
|
||
},
|
||
{
|
||
key: "kimi",
|
||
name: "Kimi (Moonshot)",
|
||
icon: kimiIcon,
|
||
keyHint: "Kimi Code 编程套餐页获取 API Key(api.kimi.com/coding 端点)",
|
||
endpoints: {
|
||
anthropic: anthropic("https://api.kimi.com/coding"),
|
||
openai: openaiChat("https://api.kimi.com/coding"),
|
||
},
|
||
models: [
|
||
entry("k3", "Kimi K3", 1048576, 65536),
|
||
entry("kimi-for-coding", "K2.7 Coding", 262144, 32768),
|
||
],
|
||
},
|
||
{
|
||
key: "deepseek",
|
||
name: "DeepSeek",
|
||
icon: deepseekIcon,
|
||
keyHint: "platform.deepseek.com → API Keys",
|
||
endpoints: {
|
||
anthropic: anthropic("https://api.deepseek.com/anthropic"),
|
||
openai: openaiChat("https://api.deepseek.com"),
|
||
},
|
||
models: [
|
||
entry("deepseek-v4-pro", "DeepSeek V4 Pro", 1000000, 65536),
|
||
entry("deepseek-v4-flash", "DeepSeek V4 Flash", 1000000, null),
|
||
],
|
||
},
|
||
{
|
||
key: "volcengine",
|
||
name: "火山引擎方舟",
|
||
icon: huoshanIcon,
|
||
keyHint: "火山方舟 Coding Plan(ark-code-latest 路由多款代码模型)",
|
||
endpoints: {
|
||
anthropic: anthropic("https://ark.cn-beijing.volces.com/api/coding"),
|
||
openai: openaiFullUrl("https://ark.cn-beijing.volces.com/api/coding/v3/chat/completions"),
|
||
},
|
||
models: [entry("ark-code-latest", "Ark Code Latest", 256000, 32768)],
|
||
},
|
||
{
|
||
key: "minimax",
|
||
name: "MiniMax",
|
||
icon: minimaxIcon,
|
||
keyHint: "platform.minimaxi.com → 订阅 Coding Plan → API Key",
|
||
endpoints: {
|
||
anthropic: anthropic("https://api.minimaxi.com/anthropic"),
|
||
openai: { baseUrl: "https://api.minimaxi.com", useFullUrl: false, openaiEndpoint: "/v1/responses", customHeaders: null },
|
||
},
|
||
models: [
|
||
entry("MiniMax-M3", "MiniMax M3", 1000000, 65536),
|
||
entry("MiniMax-M2.7", "MiniMax M2.7", 205000, 32768),
|
||
],
|
||
},
|
||
];
|
||
|
||
export const trimTrailingSlash = (url: string) => url.replace(/\/+$/, "");
|
||
|
||
export const presetEndpoint = (preset: ModelPreset, type: ModelType): ModelPresetEndpoint => preset.endpoints[type];
|