feat: Add support for Chinese AI providers (GLM, Qwen, Kimi, MiniMax, Qiniu)

* feat: Add support for Chinese AI providers (GLM, Qwen, Kimi, MiniMax, Qiniu)

- Add minimax, glm, qwen, qiniu, kimi to ProviderName type
- Add provider configurations to PROVIDER_INFO with default base URLs
- Add suggested models for MiniMax in SUGGESTED_MODELS
- Add minimax/glm/qwen/qiniu/kimi cases to getAIModel using OpenAI-compatible SDK
- Update ALLOWED_CLIENT_PROVIDERS and error messages
- Add environment variable examples to env.example

Fixes: MiniMax API compatibility issue (invalid chat setting 2013)

* fix: Add missing providers to PROVIDER_ENV_VARS type

* fix: Handle null case in PROVIDER_ENV_VARS for new providers

* fix: Add minimax/glm/qwen/kimi/qiniu support to validate-model API

- Add getDefaultBaseUrl helper function
- Add validation cases for new providers in validate-model route

* fix: Add new providers to buildProviderOptions switch case

* fix: Merge multiple system messages into one for minimax/glm/qwen/kimi/qiniu

MiniMax API doesn't support multiple system messages.
This fix combines them into a single message for Chinese providers.

* fix: Handle null provider in system message check

* debug: Add logging for allMessages count

* fix: Use effective provider (including env var fallback) for isSingleSystemProvider check

* fix: apply biome formatting (line-wrapping)

* docs: add Chinese AI providers documentation (MiniMax, GLM, Qwen, Kimi, Qiniu)

- Add i18n translations for new providers in all language dictionaries
- Add provider configuration documentation in en/cn/ja docs

* fix: 改进 PR #722 的代码审查反馈

1. 删除重复的 getDefaultBaseUrl 函数,改用 model-config.ts 的 PROVIDER_INFO
2. validate-model 路由改用 AI SDK 的 createOpenAI + generateText
3. 修复 resolveBaseURL 回退逻辑,传入 PROVIDER_INFO 的 defaultBaseUrl
4. 删除无用的 .bak 备份文件

Co-authored-by: Shinyi <shinyi@openclaw.ai>

* fix: 修正中国 AI provider 端点配置

- qiniu: api.qiniucdn.com → api.qnaigc.com
- qwen: dashscope.aliyun.com → dashscope.aliyuncs.com
- 更新 env.example 文档链接

Co-authored-by: Shinyi <shinyi@openclaw.ai>

* feat: MiniMax 使用 Anthropic 兼容 API

- MiniMax 改用 createAnthropic (而非 createOpenAI)
- 支持 api.minimax.io/anthropic 和 api.minimaxi.com/anthropic
- 合并多个 system 消息为单个 (MiniMax/GLM/Qwen/Kimi/Qiniu)
- 更新默认模型为 MiniMax-M2.5 系列
- 支持 MINIMAX_BASE_URL 环境变量配置

Co-authored-by: Shinyi <shinyi@openclaw.ai>

* docs: 更新 MiniMax 文档

- 添加 Anthropic 兼容 API 说明
- 更新默认模型为 MiniMax-M2.5
- 添加国际版/中国大陆版配置示例
- 更新 env.example 注释

Co-authored-by: Shinyi <shinyi@openclaw.ai>

* fix: 完善 MiniMax 双端点支持及问题修复

- 支持 MiniMax Anthropic 兼容端点和 OpenAI 兼容端点自动切换
- 修正默认端点为 api.minimaxi.com (中国大陆可用)
- 修复端点路径缺少 /v1 的问题
- 添加前端 MiniMax logo 映射
- 移除调试日志
- 修正 env.example 默认配置

* chore: clean backup artifacts and align biome formatting

* fix: resolve effectiveProvider bug, deduplicate MiniMax URL logic, fix docs

- Fix critical bug: effectiveProvider was empty during auto-detection,
  causing multi-system-message to be sent to MiniMax (which rejects it).
  Now uses resolved provider from getAIModel instead of re-deriving it.
- Extract normalizeMiniMaxBaseURL() shared helper to eliminate duplication
  between ai-providers.ts and validate-model/route.ts
- Add guard for undefined MiniMax baseURL to prevent hitting api.anthropic.com
- Fix docs: mark China mainland URL as default (matches code behavior)
- Restructure minimax/glm/qwen/kimi/qiniu validation to use shared pattern

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: document MiniMax dual API formats in docs and UI

- Add hint below Base URL input when MiniMax is selected, explaining
  Anthropic-compatible (/anthropic) vs OpenAI-compatible (/v1) endpoints
- Update all 3 ai-providers docs (en/cn/ja) to list all 4 endpoint options
  (China/International × Anthropic/OpenAI)
- Add i18n translations for the hint in all 4 locales

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: deduplicate PROVIDER_LOGO_MAP, remove unnecessary optional chaining

- Extract PROVIDER_LOGO_MAP to lib/types/model-config.ts (was duplicated
  in model-config-dialog.tsx and model-selector.tsx)
- Remove unnecessary ?. on PROVIDER_INFO.minimax (it's a full Record)

---------

Co-authored-by: msga-oc <msga-oc@gitea.misakiga.top>
Co-authored-by: Shinyi <shinyi@openclaw.ai>
Co-authored-by: dayuan.jiang <jdy.toh@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
misakiga
2026-03-07 17:53:47 +08:00
committed by GitHub
parent c4ba5d4ea8
commit be4bc916fd
15 changed files with 590 additions and 75 deletions

3
.gitignore vendored
View File

@@ -72,3 +72,6 @@ CLAUDE.md
.edgeone
opencode.json
ai-models.json
# local backups
*.bak

View File

@@ -14,6 +14,7 @@ import path from "path"
import { z } from "zod"
import {
getAIModel,
SINGLE_SYSTEM_PROVIDERS,
supportsImageInput,
supportsPromptCaching,
} from "@/lib/ai-providers"
@@ -235,8 +236,13 @@ async function handleChatRequest(req: Request): Promise<Response> {
)
// Get AI model with optional client overrides
const { model, providerOptions, headers, modelId } =
getAIModel(clientOverrides)
const {
model,
providerOptions,
headers,
modelId,
provider: resolvedProvider,
} = getAIModel(clientOverrides)
// Check if model supports prompt caching
const shouldCache = supportsPromptCaching(modelId)
@@ -420,9 +426,34 @@ ${userInputText}
// System messages with multiple cache breakpoints for optimal caching:
// - Breakpoint 1: Static instructions (~1500 tokens) - rarely changes
// - Breakpoint 2: Current XML context - changes per diagram, but constant within a conversation turn
// This allows: if only user message changes, both system caches are reused
// if XML changes, instruction cache is still reused
const systemMessages = [
// Some providers (e.g. MiniMax) don't support multiple system messages
// Merge them into a single system message for compatibility
const isSingleSystemProvider = SINGLE_SYSTEM_PROVIDERS.has(resolvedProvider)
const xmlContext = `${
previousXml
? `Previous diagram XML (before user's last message):
"""xml
${previousXml}
"""
`
: ""
}Current diagram XML (AUTHORITATIVE - the source of truth):
"""xml
${xml || ""}
"""
IMPORTANT: The "Current diagram XML" is the SINGLE SOURCE OF TRUTH for what's on the canvas right now. The user can manually add, delete, or modify shapes directly in draw.io. Always count and describe elements based on the CURRENT XML, not on what you previously generated. If both previous and current XML are shown, compare them to understand what the user changed. When using edit_diagram, COPY search patterns exactly from the CURRENT XML - attribute order matters!`
const systemMessages = isSingleSystemProvider
? [
{
role: "system" as const,
content: `${systemMessage}\n\n${xmlContext}`,
},
]
: [
// Cache breakpoint 1: Instructions (rarely change)
{
role: "system" as const,
@@ -436,7 +467,7 @@ ${userInputText}
// Cache breakpoint 2: Previous and Current diagram XML context
{
role: "system" as const,
content: `${previousXml ? `Previous diagram XML (before user's last message):\n"""xml\n${previousXml}\n"""\n\n` : ""}Current diagram XML (AUTHORITATIVE - the source of truth):\n"""xml\n${xml || ""}\n"""\n\nIMPORTANT: The "Current diagram XML" is the SINGLE SOURCE OF TRUTH for what's on the canvas right now. The user can manually add, delete, or modify shapes directly in draw.io. Always count and describe elements based on the CURRENT XML, not on what you previously generated. If both previous and current XML are shown, compare them to understand what the user changed. When using edit_diagram, COPY search patterns exactly from the CURRENT XML - attribute order matters!`,
content: xmlContext,
...(shouldCache && {
providerOptions: {
bedrock: { cachePoint: { type: "default" } },

View File

@@ -9,7 +9,9 @@ import { createOpenRouter } from "@openrouter/ai-sdk-provider"
import { generateText } from "ai"
import { NextResponse } from "next/server"
import { createOllama } from "ollama-ai-provider-v2"
import { normalizeMiniMaxBaseURL } from "@/lib/ai-providers"
import { allowPrivateUrls, isPrivateUrl } from "@/lib/ssrf-protection"
import { PROVIDER_INFO, type ProviderName } from "@/lib/types/model-config"
export const runtime = "nodejs"
@@ -319,6 +321,58 @@ export async function POST(req: Request) {
}
}
case "minimax": {
const rawUrl =
baseUrl ||
PROVIDER_INFO.minimax?.defaultBaseUrl ||
"https://api.minimaxi.com/anthropic"
const { baseURL: minimaxBaseUrl, isAnthropicCompatible } =
normalizeMiniMaxBaseURL(rawUrl)
if (isAnthropicCompatible) {
const minimax = createAnthropic({
apiKey,
baseURL: minimaxBaseUrl,
})
model = minimax.chat(modelId)
} else {
const minimax = createOpenAI({
apiKey,
baseURL: minimaxBaseUrl,
})
model = minimax.chat(modelId)
}
break
}
// GLM, Qwen, Kimi, Qiniu - OpenAI compatible
case "glm":
case "qwen":
case "kimi":
case "qiniu": {
const baseURL =
baseUrl ||
PROVIDER_INFO[provider as ProviderName]?.defaultBaseUrl ||
""
if (!baseURL) {
return NextResponse.json(
{
valid: false,
error: `No base URL configured for provider: ${provider}`,
},
{ status: 400 },
)
}
const openai = createOpenAI({
apiKey,
baseURL,
})
model = openai.chat(modelId)
break
}
default:
return NextResponse.json(
{ valid: false, error: `Unknown provider: ${provider}` },

View File

@@ -54,7 +54,11 @@ import { useDictionary } from "@/hooks/use-dictionary"
import type { UseModelConfigReturn } from "@/hooks/use-model-config"
import { formatMessage } from "@/lib/i18n/utils"
import type { ProviderConfig, ProviderName } from "@/lib/types/model-config"
import { PROVIDER_INFO, SUGGESTED_MODELS } from "@/lib/types/model-config"
import {
PROVIDER_INFO,
PROVIDER_LOGO_MAP,
SUGGESTED_MODELS,
} from "@/lib/types/model-config"
import { cn } from "@/lib/utils"
interface ModelConfigDialogProps {
@@ -65,24 +69,6 @@ interface ModelConfigDialogProps {
type ValidationStatus = "idle" | "validating" | "success" | "error"
// Map provider names to models.dev logo names
const PROVIDER_LOGO_MAP: Record<string, string> = {
openai: "openai",
anthropic: "anthropic",
google: "google",
azure: "azure",
bedrock: "amazon-bedrock",
openrouter: "openrouter",
deepseek: "deepseek",
siliconflow: "siliconflow",
sglang: "openai", // SGLang is OpenAI-compatible
gateway: "vercel",
edgeone: "tencent-cloud",
vertexai: "google",
doubao: "bytedance",
modelscope: "modelscope",
}
// Provider logo component
function ProviderLogo({
provider,
@@ -1255,6 +1241,16 @@ export function ModelConfigDialog({
}
className="h-9 rounded-xl font-mono text-xs"
/>
{selectedProvider.provider ===
"minimax" && (
<p className="text-xs text-muted-foreground">
{
dict
.modelConfig
.minimaxBaseUrlHint
}
</p>
)}
</div>
</>
)}

View File

@@ -27,7 +27,10 @@ import {
} from "@/components/ai-elements/model-selector"
import { ButtonWithTooltip } from "@/components/button-with-tooltip"
import { useDictionary } from "@/hooks/use-dictionary"
import type { FlattenedModel } from "@/lib/types/model-config"
import {
type FlattenedModel,
PROVIDER_LOGO_MAP,
} from "@/lib/types/model-config"
import { cn } from "@/lib/utils"
interface ModelSelectorProps {
@@ -39,24 +42,6 @@ interface ModelSelectorProps {
showUnvalidatedModels?: boolean
}
// Map our provider names to models.dev logo names
const PROVIDER_LOGO_MAP: Record<string, string> = {
openai: "openai",
anthropic: "anthropic",
google: "google",
azure: "azure",
bedrock: "amazon-bedrock",
openrouter: "openrouter",
deepseek: "deepseek",
siliconflow: "siliconflow",
sglang: "openai", // SGLang is OpenAI-compatible, use OpenAI logo
gateway: "vercel",
edgeone: "tencent-cloud",
vertexai: "google",
doubao: "bytedance",
modelscope: "modelscope",
}
// Group models by providerLabel (handles duplicate providers)
function groupModelsByProvider(
models: FlattenedModel[],

View File

@@ -207,6 +207,85 @@ AI_MODEL=openai/gpt-4o
从 [Vercel AI Gateway 仪表板](https://vercel.com/ai-gateway) 获取您的 API 密钥。
### MiniMax
MiniMax 支持两种 API 格式:
- **Anthropic 兼容**`/anthropic` 端点)— 推荐,支持 interleaved thinking
- **OpenAI 兼容**`/v1` 端点)— 标准 OpenAI 聊天补全格式
```bash
MINIMAX_API_KEY=your_api_key
AI_MODEL=MiniMax-M2.5
```
可选配置:
```bash
# 中国大陆版Anthropic 兼容(默认)
MINIMAX_BASE_URL=https://api.minimaxi.com/anthropic
# 中国大陆版OpenAI 兼容
MINIMAX_BASE_URL=https://api.minimaxi.com/v1
# 国际版Anthropic 兼容
MINIMAX_BASE_URL=https://api.minimax.io/anthropic
# 国际版OpenAI 兼容
MINIMAX_BASE_URL=https://api.minimax.io/v1
```
### GLM (智谱 AI)
```bash
GLM_API_KEY=your_api_key
AI_MODEL=glm-4
```
可选的自定义端点:
```bash
GLM_BASE_URL=https://your-custom-endpoint
```
### Qwen (阿里云通义千问)
```bash
QWEN_API_KEY=your_api_key
AI_MODEL=qwen-turbo
```
可选的自定义端点:
```bash
QWEN_BASE_URL=https://your-custom-endpoint
```
### Kimi (月之暗面 Moonshot AI)
```bash
KIMI_API_KEY=your_api_key
AI_MODEL=kimi-latest
```
可选的自定义端点:
```bash
KIMI_BASE_URL=https://your-custom-endpoint
```
### Qiniu (七牛云)
```bash
QINIU_API_KEY=your_api_key
AI_MODEL=your_model_id
```
可选的自定义端点:
```bash
QINIU_BASE_URL=https://your-custom-endpoint
```
## 自动检测
如果您只配置了**一个**提供商的 API 密钥,系统将自动检测并使用该提供商。无需设置 `AI_PROVIDER`
@@ -214,7 +293,7 @@ AI_MODEL=openai/gpt-4o
如果您配置了**多个** API 密钥,则必须显式设置 `AI_PROVIDER`
```bash
AI_PROVIDER=google # 或openai, anthropic, deepseek, siliconflow, doubao, azure, bedrock, openrouter, ollama, gateway, sglang
AI_PROVIDER=google # 或openai, anthropic, deepseek, siliconflow, doubao, azure, bedrock, openrouter, ollama, gateway, sglang, modelscope, minimax, glm, qwen, kimi, qiniu
```
## 服务端多模型配置

View File

@@ -222,6 +222,85 @@ Model format uses `provider/model` syntax:
Get your API key from the [Vercel AI Gateway dashboard](https://vercel.com/ai-gateway).
### MiniMax
MiniMax supports two API formats:
- **Anthropic-compatible** (`/anthropic` endpoint) — recommended, supports interleaved thinking
- **OpenAI-compatible** (`/v1` endpoint) — standard OpenAI chat completions format
```bash
MINIMAX_API_KEY=your_api_key
AI_MODEL=MiniMax-M2.5
```
Optional configuration:
```bash
# China mainland, Anthropic-compatible (default)
MINIMAX_BASE_URL=https://api.minimaxi.com/anthropic
# China mainland, OpenAI-compatible
MINIMAX_BASE_URL=https://api.minimaxi.com/v1
# International, Anthropic-compatible
MINIMAX_BASE_URL=https://api.minimax.io/anthropic
# International, OpenAI-compatible
MINIMAX_BASE_URL=https://api.minimax.io/v1
```
### GLM (Zhipu AI)
```bash
GLM_API_KEY=your_api_key
AI_MODEL=glm-4
```
Optional custom endpoint:
```bash
GLM_BASE_URL=https://your-custom-endpoint
```
### Qwen (Alibaba Cloud)
```bash
QWEN_API_KEY=your_api_key
AI_MODEL=qwen-turbo
```
Optional custom endpoint:
```bash
QWEN_BASE_URL=https://your-custom-endpoint
```
### Kimi (Moonshot AI)
```bash
KIMI_API_KEY=your_api_key
AI_MODEL=kimi-latest
```
Optional custom endpoint:
```bash
KIMI_BASE_URL=https://your-custom-endpoint
```
### Qiniu (Qiniu Cloud)
```bash
QINIU_API_KEY=your_api_key
AI_MODEL=your_model_id
```
Optional custom endpoint:
```bash
QINIU_BASE_URL=https://your-custom-endpoint
```
## Auto-Detection
If you only configure **one** provider's API key, the system will automatically detect and use that provider. No need to set `AI_PROVIDER`.
@@ -229,7 +308,7 @@ If you only configure **one** provider's API key, the system will automatically
If you configure **multiple** API keys, you must explicitly set `AI_PROVIDER`:
```bash
AI_PROVIDER=google # or: openai, anthropic, deepseek, siliconflow, doubao, azure, bedrock, openrouter, ollama, gateway, sglang, modelscope
AI_PROVIDER=google # or: openai, anthropic, deepseek, siliconflow, doubao, azure, bedrock, openrouter, ollama, gateway, sglang, modelscope, minimax, glm, qwen, kimi, qiniu
```
## Server-Side Multi-Model Configuration

View File

@@ -207,6 +207,85 @@ AI_MODEL=openai/gpt-4o
[Vercel AI Gateway ダッシュボード](https://vercel.com/ai-gateway)から API キーを取得してください。
### MiniMax
MiniMax は 2 つの API 形式をサポートしています:
- **Anthropic 互換**`/anthropic` エンドポイント)— 推奨、インターリーブ思考をサポート
- **OpenAI 互換**`/v1` エンドポイント)— 標準 OpenAI チャット補完形式
```bash
MINIMAX_API_KEY=your_api_key
AI_MODEL=MiniMax-M2.5
```
オプション設定:
```bash
# 中国大陸版、Anthropic 互換(デフォルト)
MINIMAX_BASE_URL=https://api.minimaxi.com/anthropic
# 中国大陸版、OpenAI 互換
MINIMAX_BASE_URL=https://api.minimaxi.com/v1
# 国際版、Anthropic 互換
MINIMAX_BASE_URL=https://api.minimax.io/anthropic
# 国際版、OpenAI 互換
MINIMAX_BASE_URL=https://api.minimax.io/v1
```
### GLM (Zhipu AI)
```bash
GLM_API_KEY=your_api_key
AI_MODEL=glm-4
```
オプションのカスタムエンドポイント:
```bash
GLM_BASE_URL=https://your-custom-endpoint
```
### Qwen (Alibaba Cloud)
```bash
QWEN_API_KEY=your_api_key
AI_MODEL=qwen-turbo
```
オプションのカスタムエンドポイント:
```bash
QWEN_BASE_URL=https://your-custom-endpoint
```
### Kimi (Moonshot AI)
```bash
KIMI_API_KEY=your_api_key
AI_MODEL=kimi-latest
```
オプションのカスタムエンドポイント:
```bash
KIMI_BASE_URL=https://your-custom-endpoint
```
### Qiniu (Qiniu Cloud)
```bash
QINIU_API_KEY=your_api_key
AI_MODEL=your_model_id
```
オプションのカスタムエンドポイント:
```bash
QINIU_BASE_URL=https://your-custom-endpoint
```
## 自動検出
**1つ**のプロバイダーの API キーのみを設定した場合、システムはそのプロバイダーを自動的に検出して使用します。`AI_PROVIDER` を設定する必要はありません。
@@ -214,7 +293,7 @@ AI_MODEL=openai/gpt-4o
**複数**の API キーを設定する場合は、`AI_PROVIDER` を明示的に設定する必要があります:
```bash
AI_PROVIDER=google # または: openai, anthropic, deepseek, siliconflow, doubao, azure, bedrock, openrouter, ollama, gateway, sglang
AI_PROVIDER=google # または: openai, anthropic, deepseek, siliconflow, doubao, azure, bedrock, openrouter, ollama, gateway, sglang, modelscope, minimax, glm, qwen, kimi, qiniu
```
## サーバーサイドマルチモデル設定

View File

@@ -142,3 +142,28 @@ AI_MODEL=global.anthropic.claude-sonnet-4-5-20250929-v1:0
# This switch allows self-hosted users to provide custom messages in response to a 429 code,
# in messageTokenSelfHosted, messageApiSelfHosted, and tipSelfHosted translation strings.
# NEXT_PUBLIC_SELFHOSTED=true
# Minimax Configuration (Optional)
# Get your API key from: https://platform.minimaxi.com/docs/guides/models-intro
# MINIMAX_API_KEY=your_minimax_api_key
# MINIMAX_BASE_URL=https://api.minimaxi.com/anthropic # Optional, default (China mainland)
# GLM Configuration (Optional)
# Get your API key from: https://open.bigmodel.cn/dev/api
# GLM_API_KEY=your_glm_api_key
# GLM_BASE_URL=https://open.bigmodel.cn/api/paas/v4 # Optional, default
# Qwen Configuration (Optional)
# Get your API key from: https://www.aliyun.com/product/bailian
# QWEN_API_KEY=your_qwen_api_key
# QWEN_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1 # Optional, default
# Kimi Configuration (Optional)
# Get your API key from: https://platform.moonshot.cn/
# KIMI_API_KEY=your_kimi_api_key
# KIMI_BASE_URL=https://api.moonshot.cn/v1 # Optional, default
# Qiniu Configuration (Optional)
# Get your API key from: https://www.qiniu.com/ai/models
# QINIU_API_KEY=your_qiniu_api_key
# QINIU_BASE_URL=https://api.qnaigc.com/v1 # Optional, default

View File

@@ -9,7 +9,7 @@ import { createOpenAI, openai } from "@ai-sdk/openai"
import { fromNodeProviderChain } from "@aws-sdk/credential-providers"
import { createOpenRouter } from "@openrouter/ai-sdk-provider"
import { createOllama, ollama } from "ollama-ai-provider-v2"
import type { ProviderName } from "@/lib/types/model-config"
import { PROVIDER_INFO, type ProviderName } from "@/lib/types/model-config"
export type { ProviderName }
@@ -18,6 +18,42 @@ interface ModelConfig {
providerOptions?: any
headers?: Record<string, string>
modelId: string
provider: ProviderName
}
// Providers that only support a single system message
export const SINGLE_SYSTEM_PROVIDERS = new Set<ProviderName>([
"minimax",
"glm",
"qwen",
"kimi",
"qiniu",
])
/**
* Normalize MiniMax base URL for AI SDK compatibility.
* MiniMax supports Anthropic-compatible and OpenAI-compatible endpoints.
*/
export function normalizeMiniMaxBaseURL(rawUrl: string): {
baseURL: string
isAnthropicCompatible: boolean
} {
const isAnthropicCompatible = rawUrl.includes("/anthropic")
let baseURL = rawUrl.replace(/\/$/, "")
if (isAnthropicCompatible) {
if (!baseURL.endsWith("/anthropic/v1")) {
if (baseURL.endsWith("/anthropic")) {
baseURL = `${baseURL}/v1`
} else {
baseURL = `${baseURL}/anthropic/v1`
}
}
} else {
if (!baseURL.endsWith("/v1")) {
baseURL = `${baseURL}/v1`
}
}
return { baseURL, isAnthropicCompatible }
}
export interface ClientOverrides {
@@ -57,6 +93,11 @@ const ALLOWED_CLIENT_PROVIDERS: ProviderName[] = [
"ollama",
"doubao",
"modelscope",
"glm",
"qwen",
"qiniu",
"kimi",
"minimax",
]
// Bedrock provider options for Anthropic beta features
@@ -474,7 +515,12 @@ function buildProviderOptions(
case "sglang":
case "gateway":
case "modelscope":
case "doubao": {
case "doubao":
case "minimax":
case "glm":
case "qwen":
case "kimi":
case "qiniu": {
// These providers don't have reasoning configs in AI SDK yet
// Gateway passes through to underlying providers which handle their own configs
break
@@ -504,6 +550,11 @@ const PROVIDER_ENV_VARS: Record<ProviderName, string | null> = {
edgeone: null, // No credentials needed - uses EdgeOne Edge AI
doubao: "DOUBAO_API_KEY",
modelscope: "MODELSCOPE_API_KEY",
glm: "GLM_API_KEY",
qwen: "QWEN_API_KEY",
qiniu: "QINIU_API_KEY",
kimi: "KIMI_API_KEY",
minimax: "MINIMAX_API_KEY",
}
/**
@@ -1172,9 +1223,69 @@ export function getAIModel(overrides?: ClientOverrides): ModelConfig {
break
}
case "minimax": {
const apiKey = resolveApiKey(overrides, "MINIMAX_API_KEY")
const serverBaseUrl = resolveBaseUrlEnv(
overrides,
"MINIMAX_BASE_URL",
)
const rawBaseURL = resolveBaseURL(
overrides?.apiKey,
overrides?.baseUrl,
serverBaseUrl,
PROVIDER_INFO.minimax.defaultBaseUrl,
)
if (!rawBaseURL) {
throw new Error(
"MiniMax base URL could not be resolved. Set MINIMAX_BASE_URL or configure a base URL in settings.",
)
}
const { baseURL, isAnthropicCompatible } =
normalizeMiniMaxBaseURL(rawBaseURL)
if (isAnthropicCompatible) {
const minimax = createAnthropic({ apiKey, baseURL })
model = minimax.chat(modelId)
} else {
const minimax = createOpenAI({ apiKey, baseURL })
model = minimax.chat(modelId)
}
break
}
case "glm":
case "qwen":
case "qiniu":
case "kimi": {
const envVar = PROVIDER_ENV_VARS[provider]
if (!envVar) {
throw new Error(
`API key environment variable not defined for provider: ${provider}`,
)
}
const apiKey = resolveApiKey(overrides, envVar)
const baseURL = resolveBaseURL(
overrides?.apiKey,
overrides?.baseUrl,
resolveBaseUrlEnv(
overrides,
`${provider.toUpperCase()}_BASE_URL`,
),
PROVIDER_INFO[provider]?.defaultBaseUrl,
)
const customProvider = createOpenAI({
apiKey,
baseURL,
})
model = customProvider.chat(modelId)
break
}
default:
throw new Error(
`Unknown AI provider: ${provider}. Supported providers: bedrock, openai, anthropic, google, azure, ollama, openrouter, deepseek, siliconflow, sglang, gateway, edgeone, doubao, modelscope`,
`Unknown AI provider: ${provider}. Supported providers: bedrock, openai, anthropic, google, azure, ollama, openrouter, deepseek, siliconflow, sglang, gateway, edgeone, doubao, modelscope, glm, qwen, qiniu, kimi, minimax`,
)
}
@@ -1183,7 +1294,7 @@ export function getAIModel(overrides?: ClientOverrides): ModelConfig {
providerOptions = customProviderOptions
}
return { model, providerOptions, headers, modelId }
return { model, providerOptions, headers, modelId, provider }
}
/**

View File

@@ -29,7 +29,12 @@
"openrouter": "OpenRouter",
"deepseek": "DeepSeek",
"siliconflow": "SiliconFlow",
"modelscope": "ModelScope"
"modelscope": "ModelScope",
"minimax": "MiniMax",
"glm": "GLM",
"qwen": "Qwen",
"kimi": "Kimi",
"qiniu": "Qiniu"
},
"chat": {
"placeholder": "Describe your diagram or upload a file...",
@@ -309,6 +314,7 @@
"optional": "(optional)",
"baseUrlWithExample": "Base URL (optional, e.g. {example})",
"customEndpoint": "Custom endpoint URL",
"minimaxBaseUrlHint": "Use /anthropic for Anthropic-compatible API (recommended), or /v1 for OpenAI-compatible API",
"models": "Models",
"customModelId": "Custom model ID...",
"allAdded": "All added",

View File

@@ -29,7 +29,12 @@
"openrouter": "OpenRouter",
"deepseek": "DeepSeek",
"siliconflow": "SiliconFlow",
"modelscope": "ModelScope"
"modelscope": "ModelScope",
"minimax": "MiniMax",
"glm": "GLM",
"qwen": "Qwen",
"kimi": "Kimi",
"qiniu": "Qiniu"
},
"chat": {
"placeholder": "ダイアグラムを説明するか、ファイルをアップロード...",
@@ -309,6 +314,7 @@
"optional": "(オプション)",
"baseUrlWithExample": "ベース URLオプション、例: {example}",
"customEndpoint": "カスタムエンドポイント URL",
"minimaxBaseUrlHint": "/anthropic で Anthropic 互換 API推奨、または /v1 で OpenAI 互換 API を使用",
"models": "モデル",
"customModelId": "カスタムモデル ID...",
"allAdded": "すべて追加済み",

View File

@@ -29,7 +29,12 @@
"openrouter": "OpenRouter",
"deepseek": "DeepSeek",
"siliconflow": "SiliconFlow",
"modelscope": "ModelScope"
"modelscope": "ModelScope",
"minimax": "MiniMax",
"glm": "GLM",
"qwen": "Qwen",
"kimi": "Kimi",
"qiniu": "Qiniu"
},
"chat": {
"placeholder": "描述您的圖表或上傳檔案...",
@@ -309,6 +314,7 @@
"optional": "(可選)",
"baseUrlWithExample": "基礎 URL可選例如 {example}",
"customEndpoint": "自訂端點 URL",
"minimaxBaseUrlHint": "使用 /anthropic 端點為 Anthropic 相容 API推薦或使用 /v1 端點為 OpenAI 相容 API",
"models": "模型",
"customModelId": "自訂模型 ID...",
"allAdded": "已全部新增",

View File

@@ -29,7 +29,12 @@
"openrouter": "OpenRouter",
"deepseek": "DeepSeek",
"siliconflow": "SiliconFlow",
"modelscope": "ModelScope"
"modelscope": "ModelScope",
"minimax": "MiniMax",
"glm": "GLM",
"qwen": "Qwen",
"kimi": "Kimi",
"qiniu": "Qiniu"
},
"chat": {
"placeholder": "描述您的图表或上传文件...",
@@ -309,6 +314,7 @@
"optional": "(可选)",
"baseUrlWithExample": "基础 URL可选例如 {example}",
"customEndpoint": "自定义端点 URL",
"minimaxBaseUrlHint": "使用 /anthropic 端点为 Anthropic 兼容 API推荐或使用 /v1 端点为 OpenAI 兼容 API",
"models": "模型",
"customModelId": "自定义模型 ID...",
"allAdded": "已全部添加",

View File

@@ -16,6 +16,11 @@ export type ProviderName =
| "edgeone"
| "doubao"
| "modelscope"
| "glm"
| "qwen"
| "qiniu"
| "kimi"
| "minimax"
// Individual model configuration
export interface ModelConfig {
@@ -79,6 +84,25 @@ export interface FlattenedModel {
baseUrlEnv?: string
}
// Map provider names to models.dev logo names
export const PROVIDER_LOGO_MAP: Record<string, string> = {
openai: "openai",
anthropic: "anthropic",
google: "google",
azure: "azure",
bedrock: "amazon-bedrock",
openrouter: "openrouter",
deepseek: "deepseek",
siliconflow: "siliconflow",
sglang: "openai", // SGLang is OpenAI-compatible
gateway: "vercel",
edgeone: "tencent-cloud",
vertexai: "google",
doubao: "bytedance",
modelscope: "modelscope",
minimax: "minimax",
}
// Provider metadata
export const PROVIDER_INFO: Record<
ProviderName,
@@ -135,6 +159,26 @@ export const PROVIDER_INFO: Record<
label: "ModelScope",
defaultBaseUrl: "https://api-inference.modelscope.cn/v1",
},
glm: {
label: "GLM (Zhipu)",
defaultBaseUrl: "https://open.bigmodel.cn/api/paas/v4",
},
qwen: {
label: "Qwen (Alibaba)",
defaultBaseUrl: "https://dashscope.aliyuncs.com/compatible-mode/v1",
},
qiniu: {
label: "Qiniu",
defaultBaseUrl: "https://api.qnaigc.com/v1",
},
kimi: {
label: "Kimi (Moonshot)",
defaultBaseUrl: "https://api.moonshot.cn/v1",
},
minimax: {
label: "MiniMax",
defaultBaseUrl: "https://api.minimaxi.com/anthropic",
},
}
// Suggested models per provider for quick add
@@ -299,6 +343,11 @@ export const SUGGESTED_MODELS: Partial<Record<ProviderName, string[]>> = {
"deepseek-ai/DeepSeek-R1-0528",
"deepseek-ai/DeepSeek-V3.2",
],
minimax: [
// MiniMax models (Anthropic-compatible API)
"MiniMax-M2.5",
"MiniMax-M2.5-highspeed",
],
}
// Helper to generate UUID