Просмотр исходного кода

feat(core): 添加 endpoint 分组字段

fanghanjun 6 месяцев назад
Родитель
Сommit
e9feb91f15
43 измененных файлов с 84 добавлено и 1 удалено
  1. 34 0
      packages/core/src/__tests__/providers-group.test.ts
  2. 1 0
      packages/core/src/llm/providers/endpoints/ai360.ts
  3. 1 0
      packages/core/src/llm/providers/endpoints/anthropic.ts
  4. 1 0
      packages/core/src/llm/providers/endpoints/astronCodingPlan.ts
  5. 1 0
      packages/core/src/llm/providers/endpoints/baichuan.ts
  6. 1 0
      packages/core/src/llm/providers/endpoints/bailian.ts
  7. 1 0
      packages/core/src/llm/providers/endpoints/bailianCodingPlan.ts
  8. 1 0
      packages/core/src/llm/providers/endpoints/deepseek.ts
  9. 1 0
      packages/core/src/llm/providers/endpoints/giteeai.ts
  10. 1 0
      packages/core/src/llm/providers/endpoints/githubCopilot.ts
  11. 1 0
      packages/core/src/llm/providers/endpoints/glmCodingPlan.ts
  12. 1 0
      packages/core/src/llm/providers/endpoints/google.ts
  13. 1 0
      packages/core/src/llm/providers/endpoints/hunyuan.ts
  14. 1 0
      packages/core/src/llm/providers/endpoints/infiniai.ts
  15. 1 0
      packages/core/src/llm/providers/endpoints/internlm.ts
  16. 1 0
      packages/core/src/llm/providers/endpoints/kimiCodingPlan.ts
  17. 1 0
      packages/core/src/llm/providers/endpoints/longcat.ts
  18. 1 0
      packages/core/src/llm/providers/endpoints/minimax.ts
  19. 1 0
      packages/core/src/llm/providers/endpoints/minimaxCodingPlan.ts
  20. 1 0
      packages/core/src/llm/providers/endpoints/mistral.ts
  21. 1 0
      packages/core/src/llm/providers/endpoints/modelscope.ts
  22. 1 0
      packages/core/src/llm/providers/endpoints/moonshot.ts
  23. 1 0
      packages/core/src/llm/providers/endpoints/newapi.ts
  24. 1 0
      packages/core/src/llm/providers/endpoints/ollama.ts
  25. 1 0
      packages/core/src/llm/providers/endpoints/openai.ts
  26. 1 0
      packages/core/src/llm/providers/endpoints/opencodeCodingPlan.ts
  27. 1 0
      packages/core/src/llm/providers/endpoints/openrouter.ts
  28. 1 0
      packages/core/src/llm/providers/endpoints/ppio.ts
  29. 1 0
      packages/core/src/llm/providers/endpoints/qiniu.ts
  30. 1 0
      packages/core/src/llm/providers/endpoints/sensenova.ts
  31. 1 0
      packages/core/src/llm/providers/endpoints/siliconcloud.ts
  32. 1 0
      packages/core/src/llm/providers/endpoints/spark.ts
  33. 1 0
      packages/core/src/llm/providers/endpoints/stepfun.ts
  34. 1 0
      packages/core/src/llm/providers/endpoints/tencentcloud.ts
  35. 1 0
      packages/core/src/llm/providers/endpoints/volcengine.ts
  36. 1 0
      packages/core/src/llm/providers/endpoints/volcengineCodingPlan.ts
  37. 1 0
      packages/core/src/llm/providers/endpoints/wenxin.ts
  38. 1 0
      packages/core/src/llm/providers/endpoints/xai.ts
  39. 1 0
      packages/core/src/llm/providers/endpoints/xiaomimimo.ts
  40. 1 0
      packages/core/src/llm/providers/endpoints/zeroone.ts
  41. 1 0
      packages/core/src/llm/providers/endpoints/zhipu.ts
  42. 1 1
      packages/core/src/llm/providers/index.ts
  43. 9 0
      packages/core/src/llm/providers/types.ts

+ 34 - 0
packages/core/src/__tests__/providers-group.test.ts

@@ -0,0 +1,34 @@
+import { describe, expect, it } from "vitest";
+import { getAllEndpoints } from "../llm/providers/index.js";
+
+describe("InkosEndpoint.group", () => {
+  it("每个非 custom endpoint 都必须声明 group 字段", () => {
+    const missing = getAllEndpoints().filter((ep) => ep.id !== "custom" && !ep.group);
+    expect(missing, `missing group: ${missing.map((e) => e.id).join(", ")}`).toHaveLength(0);
+  });
+
+  it("每个 group 的 endpoint 数量匹配分组清单", () => {
+    const all = getAllEndpoints();
+    const byGroup = (g: string) => all.filter((ep) => ep.group === g).map((e) => e.id).sort();
+
+    expect(byGroup("overseas")).toEqual(["anthropic", "google", "mistral", "openai", "xai"].sort());
+    expect(byGroup("china")).toEqual([
+      "ai360", "baichuan", "bailian", "deepseek", "hunyuan", "internlm", "longcat",
+      "minimax", "moonshot", "sensenova", "spark", "stepfun", "tencentcloud",
+      "volcengine", "wenxin", "xiaomimimo", "zeroone", "zhipu",
+    ].sort());
+    expect(byGroup("aggregator")).toEqual([
+      "giteeai", "infiniai", "modelscope", "newapi", "openrouter", "ppio", "qiniu", "siliconcloud",
+    ].sort());
+    expect(byGroup("local")).toEqual(["githubCopilot", "ollama"].sort());
+    expect(byGroup("codingPlan")).toEqual([
+      "astronCodingPlan", "bailianCodingPlan", "glmCodingPlan", "kimiCodingPlan",
+      "minimaxCodingPlan", "opencodeCodingPlan", "volcengineCodingPlan",
+    ].sort());
+  });
+
+  it("custom endpoint 不参与分组计数", () => {
+    const custom = getAllEndpoints().find((ep) => ep.id === "custom");
+    expect(custom).toBeDefined();
+  });
+});

+ 1 - 0
packages/core/src/llm/providers/endpoints/ai360.ts

@@ -10,6 +10,7 @@ import type { InkosEndpoint } from "../types.js";
 export const AI360: InkosEndpoint = {
   id: "ai360",
   label: "360 智脑",
+  group: "china",
   api: "openai-completions",
   baseUrl: "https://api.360.cn/v1",
   checkModel: "360gpt2-pro",

+ 1 - 0
packages/core/src/llm/providers/endpoints/anthropic.ts

@@ -12,6 +12,7 @@ import type { InkosEndpoint } from "../types.js";
 export const ANTHROPIC: InkosEndpoint = {
   id: "anthropic",
   label: "Anthropic",
+  group: "overseas",
   api: "anthropic-messages",
   baseUrl: "https://api.anthropic.com",
   checkModel: "claude-haiku-4-5-20251001",

+ 1 - 0
packages/core/src/llm/providers/endpoints/astronCodingPlan.ts

@@ -16,6 +16,7 @@ import type { InkosEndpoint } from "../types.js";
 export const ASTRON_CODING_PLAN: InkosEndpoint = {
   id: "astronCodingPlan",
   label: "讯飞星辰 Astron Coding Plan",
+  group: "codingPlan",
   api: "anthropic-messages",
   baseUrl: "https://maas-coding-api.cn-huabei-1.xf-yun.com/anthropic",
   modelsBaseUrl: "https://maas-coding-api.cn-huabei-1.xf-yun.com/v2",

+ 1 - 0
packages/core/src/llm/providers/endpoints/baichuan.ts

@@ -10,6 +10,7 @@ import type { InkosEndpoint } from "../types.js";
 export const BAICHUAN: InkosEndpoint = {
   id: "baichuan",
   label: "百川智能",
+  group: "china",
   api: "openai-completions",
   baseUrl: "https://api.baichuan-ai.com/v1",
   checkModel: "Baichuan4",

+ 1 - 0
packages/core/src/llm/providers/endpoints/bailian.ts

@@ -25,6 +25,7 @@ import type { InkosEndpoint } from "../types.js";
 export const BAILIAN: InkosEndpoint = {
   id: "bailian",
   label: "百炼 (通义千问)",
+  group: "china",
   api: "anthropic-messages",
   baseUrl: "https://dashscope.aliyuncs.com/apps/anthropic",
   checkModel: "qwen-turbo",

+ 1 - 0
packages/core/src/llm/providers/endpoints/bailianCodingPlan.ts

@@ -10,6 +10,7 @@ import type { InkosEndpoint } from "../types.js";
 export const BAILIAN_CODING_PLAN: InkosEndpoint = {
   id: "bailianCodingPlan",
   label: "百炼 Coding Plan",
+  group: "codingPlan",
   api: "anthropic-messages",
   baseUrl: "https://dashscope.aliyuncs.com/apps/anthropic",
   checkModel: "qwen-max",

+ 1 - 0
packages/core/src/llm/providers/endpoints/deepseek.ts

@@ -15,6 +15,7 @@ import type { InkosEndpoint } from "../types.js";
 export const DEEPSEEK: InkosEndpoint = {
   id: "deepseek",
   label: "DeepSeek",
+  group: "china",
   api: "openai-completions",
   baseUrl: "https://api.deepseek.com",
   checkModel: "deepseek-chat",

+ 1 - 0
packages/core/src/llm/providers/endpoints/giteeai.ts

@@ -10,6 +10,7 @@ import type { InkosEndpoint } from "../types.js";
 export const GITEEAI: InkosEndpoint = {
   id: "giteeai",
   label: "Gitee AI",
+  group: "aggregator",
   api: "openai-completions",
   baseUrl: "https://ai.gitee.com/v1",
   checkModel: "Qwen2.5-72B-Instruct",

+ 1 - 0
packages/core/src/llm/providers/endpoints/githubCopilot.ts

@@ -10,6 +10,7 @@ import type { InkosEndpoint } from "../types.js";
 export const GITHUB_COPILOT: InkosEndpoint = {
   id: "githubCopilot",
   label: "GitHub Copilot",
+  group: "local",
   api: "openai-responses",
   baseUrl: "https://api.githubcopilot.com",
   checkModel: "gpt-4o",

+ 1 - 0
packages/core/src/llm/providers/endpoints/glmCodingPlan.ts

@@ -9,6 +9,7 @@ import type { InkosEndpoint } from "../types.js";
 export const GLM_CODING_PLAN: InkosEndpoint = {
   id: "glmCodingPlan",
   label: "GLM Coding Plan",
+  group: "codingPlan",
   api: "anthropic-messages",
   baseUrl: "https://api.z.ai/api/anthropic",
   checkModel: "glm-5.1",

+ 1 - 0
packages/core/src/llm/providers/endpoints/google.ts

@@ -12,6 +12,7 @@ import type { InkosEndpoint } from "../types.js";
 export const GOOGLE: InkosEndpoint = {
   id: "google",
   label: "Google Gemini",
+  group: "overseas",
   api: "openai-completions",
   baseUrl: "https://generativelanguage.googleapis.com/v1beta/openai",
   checkModel: "gemini-2.5-flash",

+ 1 - 0
packages/core/src/llm/providers/endpoints/hunyuan.ts

@@ -10,6 +10,7 @@ import type { InkosEndpoint } from "../types.js";
 export const HUNYUAN: InkosEndpoint = {
   id: "hunyuan",
   label: "腾讯混元",
+  group: "china",
   api: "openai-completions",
   baseUrl: "https://api.hunyuan.cloud.tencent.com/v1",
   checkModel: "hunyuan-lite",

+ 1 - 0
packages/core/src/llm/providers/endpoints/infiniai.ts

@@ -10,6 +10,7 @@ import type { InkosEndpoint } from "../types.js";
 export const INFINIAI: InkosEndpoint = {
   id: "infiniai",
   label: "无问芯穹 InfiniAI",
+  group: "aggregator",
   api: "openai-completions",
   baseUrl: "https://cloud.infini-ai.com/maas/v1",
   checkModel: "qwen2.5-72b-instruct",

+ 1 - 0
packages/core/src/llm/providers/endpoints/internlm.ts

@@ -10,6 +10,7 @@ import type { InkosEndpoint } from "../types.js";
 export const INTERNLM: InkosEndpoint = {
   id: "internlm",
   label: "书生浦语 (InternLM)",
+  group: "china",
   api: "openai-completions",
   baseUrl: "https://chat.intern-ai.org.cn/api/v1",
   checkModel: "internlm2.5-latest",

+ 1 - 0
packages/core/src/llm/providers/endpoints/kimiCodingPlan.ts

@@ -9,6 +9,7 @@ import type { InkosEndpoint } from "../types.js";
 export const KIMI_CODING_PLAN: InkosEndpoint = {
   id: "kimiCodingPlan",
   label: "Kimi Coding Plan",
+  group: "codingPlan",
   api: "anthropic-messages",
   baseUrl: "https://api.moonshot.cn/anthropic",
   checkModel: "kimi-k2.5",

+ 1 - 0
packages/core/src/llm/providers/endpoints/longcat.ts

@@ -9,6 +9,7 @@ import type { InkosEndpoint } from "../types.js";
 export const LONGCAT: InkosEndpoint = {
   id: "longcat",
   label: "美团 LongCat",
+  group: "china",
   api: "openai-completions",
   baseUrl: "https://api.longcat.chat/openai/v1",
   checkModel: "LongCat-Flash-Chat",

+ 1 - 0
packages/core/src/llm/providers/endpoints/minimax.ts

@@ -15,6 +15,7 @@ import type { InkosEndpoint } from "../types.js";
 export const MINIMAX: InkosEndpoint = {
   id: "minimax",
   label: "MiniMax",
+  group: "china",
   api: "anthropic-messages",
   baseUrl: "https://api.minimaxi.com/anthropic",
   checkModel: "MiniMax-M2.7",

+ 1 - 0
packages/core/src/llm/providers/endpoints/minimaxCodingPlan.ts

@@ -9,6 +9,7 @@ import type { InkosEndpoint } from "../types.js";
 export const MINIMAX_CODING_PLAN: InkosEndpoint = {
   id: "minimaxCodingPlan",
   label: "MiniMax Coding Plan",
+  group: "codingPlan",
   api: "anthropic-messages",
   baseUrl: "https://api.minimaxi.com/anthropic",
   checkModel: "MiniMax-M2.7",

+ 1 - 0
packages/core/src/llm/providers/endpoints/mistral.ts

@@ -11,6 +11,7 @@ import type { InkosEndpoint } from "../types.js";
 export const MISTRAL: InkosEndpoint = {
   id: "mistral",
   label: "Mistral AI",
+  group: "overseas",
   api: "openai-completions",
   baseUrl: "https://api.mistral.ai/v1",
   checkModel: "mistral-small-latest",

+ 1 - 0
packages/core/src/llm/providers/endpoints/modelscope.ts

@@ -10,6 +10,7 @@ import type { InkosEndpoint } from "../types.js";
 export const MODELSCOPE: InkosEndpoint = {
   id: "modelscope",
   label: "魔搭社区 ModelScope",
+  group: "aggregator",
   api: "openai-completions",
   baseUrl: "https://api-inference.modelscope.cn/v1",
   checkModel: "Qwen/Qwen2.5-72B-Instruct",

+ 1 - 0
packages/core/src/llm/providers/endpoints/moonshot.ts

@@ -12,6 +12,7 @@ import type { InkosEndpoint } from "../types.js";
 export const MOONSHOT: InkosEndpoint = {
   id: "moonshot",
   label: "Moonshot (Kimi)",
+  group: "china",
   api: "openai-completions",
   baseUrl: "https://api.moonshot.cn/v1",
   checkModel: "moonshot-v1-8k",

+ 1 - 0
packages/core/src/llm/providers/endpoints/newapi.ts

@@ -14,6 +14,7 @@ import type { InkosEndpoint } from "../types.js";
 export const NEWAPI: InkosEndpoint = {
   id: "newapi",
   label: "New API (中转网关)",
+  group: "aggregator",
   api: "openai-completions",
   baseUrl: "",
   models: [],

+ 1 - 0
packages/core/src/llm/providers/endpoints/ollama.ts

@@ -12,6 +12,7 @@ import type { InkosEndpoint } from "../types.js";
 export const OLLAMA: InkosEndpoint = {
   id: "ollama",
   label: "Ollama (本地)",
+  group: "local",
   api: "openai-completions",
   baseUrl: "http://localhost:11434/v1",
   checkModel: "llama3.2:3b",

+ 1 - 0
packages/core/src/llm/providers/endpoints/openai.ts

@@ -12,6 +12,7 @@ import type { InkosEndpoint } from "../types.js";
 export const OPENAI: InkosEndpoint = {
   id: "openai",
   label: "OpenAI",
+  group: "overseas",
   api: "openai-responses",
   baseUrl: "https://api.openai.com/v1",
   checkModel: "gpt-4o-mini",

+ 1 - 0
packages/core/src/llm/providers/endpoints/opencodeCodingPlan.ts

@@ -9,6 +9,7 @@ import type { InkosEndpoint } from "../types.js";
 export const OPENCODE_CODING_PLAN: InkosEndpoint = {
   id: "opencodeCodingPlan",
   label: "OpenCode Coding Plan",
+  group: "codingPlan",
   api: "anthropic-messages",
   baseUrl: "https://opencode.ai/api/anthropic",
   checkModel: "glm-5.1",

+ 1 - 0
packages/core/src/llm/providers/endpoints/openrouter.ts

@@ -15,6 +15,7 @@ import type { InkosEndpoint } from "../types.js";
 export const OPENROUTER: InkosEndpoint = {
   id: "openrouter",
   label: "OpenRouter",
+  group: "aggregator",
   api: "openai-responses",
   baseUrl: "https://openrouter.ai/api/v1",
   checkModel: "google/gemma-2-9b-it:free",

+ 1 - 0
packages/core/src/llm/providers/endpoints/ppio.ts

@@ -15,6 +15,7 @@ import type { InkosEndpoint } from "../types.js";
 export const PPIO: InkosEndpoint = {
   id: "ppio",
   label: "PPIO",
+  group: "aggregator",
   api: "openai-completions",
   baseUrl: "https://api.ppinfra.com/v3/openai",
   checkModel: "deepseek/deepseek-v3.2",

+ 1 - 0
packages/core/src/llm/providers/endpoints/qiniu.ts

@@ -10,6 +10,7 @@ import type { InkosEndpoint } from "../types.js";
 export const QINIU: InkosEndpoint = {
   id: "qiniu",
   label: "七牛云 AI",
+  group: "aggregator",
   api: "openai-completions",
   baseUrl: "https://api.qnaigc.com/v1",
   checkModel: "deepseek-v3",

+ 1 - 0
packages/core/src/llm/providers/endpoints/sensenova.ts

@@ -10,6 +10,7 @@ import type { InkosEndpoint } from "../types.js";
 export const SENSENOVA: InkosEndpoint = {
   id: "sensenova",
   label: "商汤日日新",
+  group: "china",
   api: "openai-completions",
   baseUrl: "https://api.sensenova.cn/compatible-mode/v1",
   checkModel: "SenseChat-Turbo",

+ 1 - 0
packages/core/src/llm/providers/endpoints/siliconcloud.ts

@@ -14,6 +14,7 @@ import type { InkosEndpoint } from "../types.js";
 export const SILICONCLOUD: InkosEndpoint = {
   id: "siliconcloud",
   label: "硅基流动",
+  group: "aggregator",
   api: "openai-completions",
   baseUrl: "https://api.siliconflow.cn/v1",
   checkModel: "Qwen/Qwen2.5-7B-Instruct",

+ 1 - 0
packages/core/src/llm/providers/endpoints/spark.ts

@@ -14,6 +14,7 @@ import type { InkosEndpoint } from "../types.js";
 export const SPARK: InkosEndpoint = {
   id: "spark",
   label: "讯飞星火",
+  group: "china",
   api: "openai-completions",
   baseUrl: "https://spark-api-open.xf-yun.com/v1",
   checkModel: "lite",

+ 1 - 0
packages/core/src/llm/providers/endpoints/stepfun.ts

@@ -10,6 +10,7 @@ import type { InkosEndpoint } from "../types.js";
 export const STEPFUN: InkosEndpoint = {
   id: "stepfun",
   label: "阶跃星辰",
+  group: "china",
   api: "openai-completions",
   baseUrl: "https://api.stepfun.com/v1",
   checkModel: "step-1-8k",

+ 1 - 0
packages/core/src/llm/providers/endpoints/tencentcloud.ts

@@ -10,6 +10,7 @@ import type { InkosEndpoint } from "../types.js";
 export const TENCENTCLOUD: InkosEndpoint = {
   id: "tencentcloud",
   label: "腾讯云 (lkeap)",
+  group: "china",
   api: "openai-completions",
   baseUrl: "https://api.lkeap.cloud.tencent.com/v1",
   checkModel: "deepseek-v3",

+ 1 - 0
packages/core/src/llm/providers/endpoints/volcengine.ts

@@ -10,6 +10,7 @@ import type { InkosEndpoint } from "../types.js";
 export const VOLCENGINE: InkosEndpoint = {
   id: "volcengine",
   label: "火山引擎 (豆包)",
+  group: "china",
   api: "openai-completions",
   baseUrl: "https://ark.cn-beijing.volces.com/api/v3",
   checkModel: "doubao-lite-32k",

+ 1 - 0
packages/core/src/llm/providers/endpoints/volcengineCodingPlan.ts

@@ -19,6 +19,7 @@ import type { InkosEndpoint } from "../types.js";
 export const VOLCENGINE_CODING_PLAN: InkosEndpoint = {
   id: "volcengineCodingPlan",
   label: "火山 Coding Plan",
+  group: "codingPlan",
   api: "anthropic-messages",
   baseUrl: "https://ark.cn-beijing.volces.com/api/coding",
   checkModel: "doubao-seed-2.0-code",

+ 1 - 0
packages/core/src/llm/providers/endpoints/wenxin.ts

@@ -10,6 +10,7 @@ import type { InkosEndpoint } from "../types.js";
 export const WENXIN: InkosEndpoint = {
   id: "wenxin",
   label: "文心一言 (千帆)",
+  group: "china",
   api: "openai-completions",
   baseUrl: "https://qianfan.baidubce.com/v2",
   checkModel: "ernie-3.5-8k",

+ 1 - 0
packages/core/src/llm/providers/endpoints/xai.ts

@@ -11,6 +11,7 @@ import type { InkosEndpoint } from "../types.js";
 export const XAI: InkosEndpoint = {
   id: "xai",
   label: "xAI (Grok)",
+  group: "overseas",
   api: "openai-completions",
   baseUrl: "https://api.x.ai/v1",
   checkModel: "grok-2-1212",

+ 1 - 0
packages/core/src/llm/providers/endpoints/xiaomimimo.ts

@@ -12,6 +12,7 @@ import type { InkosEndpoint } from "../types.js";
 export const XIAOMI_MIMO: InkosEndpoint = {
   id: "xiaomimimo",
   label: "小米 MiMo",
+  group: "china",
   api: "openai-completions",
   baseUrl: "https://api-ai.xiaomi.com/v1",
   temperatureRange: [0, 2],

+ 1 - 0
packages/core/src/llm/providers/endpoints/zeroone.ts

@@ -10,6 +10,7 @@ import type { InkosEndpoint } from "../types.js";
 export const ZEROONE: InkosEndpoint = {
   id: "zeroone",
   label: "零一万物 (01.AI)",
+  group: "china",
   api: "openai-completions",
   baseUrl: "https://api.lingyiwanwu.com/v1",
   checkModel: "yi-lightning",

+ 1 - 0
packages/core/src/llm/providers/endpoints/zhipu.ts

@@ -12,6 +12,7 @@ import type { InkosEndpoint } from "../types.js";
 export const ZHIPU: InkosEndpoint = {
   id: "zhipu",
   label: "智谱 GLM",
+  group: "china",
   api: "openai-completions",
   baseUrl: "https://open.bigmodel.cn/api/paas/v4",
   checkModel: "glm-4.7-flash",

+ 1 - 1
packages/core/src/llm/providers/index.ts

@@ -46,7 +46,7 @@ import { VOLCENGINE_CODING_PLAN } from "./endpoints/volcengineCodingPlan.js";
 import { OPENCODE_CODING_PLAN } from "./endpoints/opencodeCodingPlan.js";
 import { ASTRON_CODING_PLAN } from "./endpoints/astronCodingPlan.js";
 
-export type { InkosEndpoint, InkosModel, ApiProtocol } from "./types.js";
+export type { InkosEndpoint, InkosModel, ApiProtocol, EndpointGroup } from "./types.js";
 
 /**
  * 所有已注册 provider 的扁平列表。顺序定义了 lookup Layer 2 的遍历顺序,

+ 9 - 0
packages/core/src/llm/providers/types.ts

@@ -11,6 +11,13 @@ export type ApiProtocol =
   | "openai-responses"
   | "anthropic-messages";
 
+export type EndpointGroup =
+  | "overseas"
+  | "china"
+  | "aggregator"
+  | "local"
+  | "codingPlan";
+
 export interface InkosModel {
   /** API 请求体里实际用的 model id(可能带斜线如 'deepseek/deepseek-v3')。UI 也直接用 id 显示 */
   readonly id: string;
@@ -35,6 +42,8 @@ export interface InkosModel {
 export interface InkosEndpoint {
   readonly id: string;
   readonly label: string;
+  /** UI 分组。custom 不参与分组,其他 endpoint 必填。 */
+  readonly group?: EndpointGroup;
 
   readonly api: ApiProtocol;
   readonly baseUrl: string;