Files
next-ai-draw-io/lib/types/model-config.ts
CharlesJay01 4984be82a1 feat: add MiMo (Xiaomi) as AI provider (#887)
* feat: add MiMo (Xiaomi) as AI provider

* fix: correct MiMo default base URL, suggested models, and reasoning support

- Default base URL was the China Token Plan endpoint (tp- keys only);
  switch to https://api.xiaomimimo.com/v1 which works with standard
  pay-as-you-go sk- keys. Token Plan users can override in settings.
- Replace deprecated mimo-v2-flash suggestion with mimo-v2.5
  (v2 series was deprecated on 2026-06-30).
- Use createDeepSeek instead of createOpenAI so reasoning_content is
  passed back during multi-turn tool calls (MiMo returns 400 without
  it), matching the existing Kimi implementation.
- Add mimo to SINGLE_SYSTEM_PROVIDERS so system messages are merged.
- Fold validate-model case into the shared OpenAI-compatible group.
- Drop the Bot icon special case; models.dev serves a real xiaomi logo
  via PROVIDER_LOGO_MAP.
- Document MIMO_API_KEY/MIMO_BASE_URL in env.example and
  docs/{en,cn,ja}/ai-providers.md.

* feat: show base URL hint for MiMo in provider settings

MiMo has two endpoints tied to key type: pay-as-you-go keys (sk-...)
use the default api.xiaomimimo.com/v1, while Token Plan keys (tp-...)
require token-plan-cn.xiaomimimo.com/v1. Surface this under the Base
URL field like the existing MiniMax hint, in all four locales.

---------

Co-authored-by: mapengfei <mapengfei@srsj.com>
Co-authored-by: dayuan.jiang <jdy.toh@gmail.com>
2026-07-12 09:10:12 +09:00

525 lines
15 KiB
TypeScript

// Types for multi-provider model configuration
export type ProviderName =
| "openai"
| "anthropic"
| "google"
| "vertexai"
| "azure"
| "bedrock"
| "ollama"
| "openrouter"
| "aihubmix"
| "deepseek"
| "siliconflow"
| "sglang"
| "gateway"
| "edgeone"
| "doubao"
| "modelscope"
| "glm"
| "qwen"
| "qiniu"
| "kimi"
| "minimax"
| "novita"
| "mimo"
// Individual model configuration
export interface ModelConfig {
id: string // UUID for this model
modelId: string // e.g., "gpt-4o", "claude-sonnet-4-5"
validated?: boolean // Has this model been validated
validationError?: string // Error message if validation failed
}
// Provider configuration
export interface ProviderConfig {
id: string // UUID for this provider config
provider: ProviderName
name?: string // Custom display name (e.g., "OpenAI Production")
apiKey: string
baseUrl?: string
// AWS Bedrock specific fields
awsAccessKeyId?: string
awsSecretAccessKey?: string
awsRegion?: string
awsSessionToken?: string // Optional, for temporary credentials
// Vertex AI specific fields
vertexApiKey?: string // Express Mode API key
models: ModelConfig[]
validated?: boolean // Has API key been validated
}
// The complete multi-model configuration
export interface MultiModelConfig {
version: 1
providers: ProviderConfig[]
selectedModelId?: string // Currently selected model's UUID
showUnvalidatedModels?: boolean // Show models that haven't been validated
}
// Flattened model for dropdown display
export interface FlattenedModel {
id: string // Model config UUID or synthetic server ID (e.g., "server:provider:modelId")
modelId: string // Actual model ID
provider: ProviderName
providerLabel: string // Provider display name
apiKey: string
baseUrl?: string
// AWS Bedrock specific fields
awsAccessKeyId?: string
awsSecretAccessKey?: string
awsRegion?: string
awsSessionToken?: string
// Vertex AI specific fields
vertexApiKey?: string // Express Mode API key
validated?: boolean // Has this model been validated
// Source of this model config: user-defined (client) or server-defined
source?: "user" | "server"
// Whether this model is the server default (matches AI_MODEL env var)
isDefault?: boolean
// Custom env var name(s) for server models
// Can be a single string or array of strings for load balancing
apiKeyEnv?: string | string[]
baseUrlEnv?: string
}
// Providers whose server credentials live in fixed env vars
// (AWS_ACCESS_KEY_ID, GOOGLE_VERTEX_API_KEY, OLLAMA_API_KEY) with no
// apiKeyEnv redirection support — their credentials are global
export const FIXED_CRED_PROVIDERS: ProviderName[] = [
"bedrock",
"vertexai",
"ollama",
]
// 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",
aihubmix: "aihubmix",
deepseek: "deepseek",
siliconflow: "siliconflow",
sglang: "openai", // SGLang is OpenAI-compatible
gateway: "vercel",
edgeone: "tencent-cloud",
vertexai: "google",
doubao: "bytedance",
modelscope: "modelscope",
minimax: "minimax",
novita: "novita",
mimo: "xiaomi",
}
// Provider metadata
export const PROVIDER_INFO: Record<
ProviderName,
{ label: string; defaultBaseUrl?: string }
> = {
openai: {
label: "OpenAI",
defaultBaseUrl: "https://api.openai.com/v1",
},
anthropic: {
label: "Anthropic",
defaultBaseUrl: "https://api.anthropic.com/v1",
},
google: {
label: "Google",
defaultBaseUrl: "https://generativelanguage.googleapis.com/v1beta",
},
vertexai: { label: "Google Vertex AI" },
azure: {
label: "Azure OpenAI",
defaultBaseUrl: "https://your-resource.openai.azure.com/openai",
},
bedrock: { label: "Amazon Bedrock" },
ollama: {
label: "Ollama",
defaultBaseUrl: "https://ollama.com/api",
},
openrouter: {
label: "OpenRouter",
defaultBaseUrl: "https://openrouter.ai/api/v1",
},
aihubmix: {
label: "AIHubMix",
defaultBaseUrl: "https://aihubmix.com/v1",
},
deepseek: {
label: "DeepSeek",
defaultBaseUrl: "https://api.deepseek.com/v1",
},
siliconflow: {
label: "SiliconFlow",
defaultBaseUrl: "https://api.siliconflow.cn/v1",
},
sglang: {
label: "SGLang",
defaultBaseUrl: "http://127.0.0.1:8000/v1",
},
gateway: {
label: "AI Gateway",
defaultBaseUrl: "https://ai-gateway.vercel.sh/v1/ai",
},
edgeone: { label: "EdgeOne Pages" },
doubao: {
label: "Doubao (ByteDance)",
defaultBaseUrl: "https://ark.cn-beijing.volces.com/api/v3",
},
modelscope: {
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",
},
novita: {
label: "Novita AI",
defaultBaseUrl: "https://api.novita.ai/openai",
},
mimo: {
label: "MiMo (Xiaomi)",
defaultBaseUrl: "https://api.xiaomimimo.com/v1",
},
}
// Suggested models per provider for quick add
export const SUGGESTED_MODELS: Partial<Record<ProviderName, string[]>> = {
openai: [
"gpt-5.5-pro",
"gpt-5.5",
"gpt-5.4-pro",
"gpt-5.4",
"gpt-5.4-mini",
"gpt-5.4-nano",
"gpt-5-codex-mini",
"gpt-4.1",
"gpt-4.1-mini",
"gpt-4o",
"gpt-4o-mini",
],
anthropic: [
// Claude 4.8 / 4.7 / 4.6 series (latest, dateless pinned IDs)
"claude-opus-4-8",
"claude-sonnet-4-6",
"claude-haiku-4-5",
"claude-opus-4-7",
"claude-opus-4-6",
// Claude 4.5 series
"claude-sonnet-4-5-20250929",
"claude-opus-4-5-20251101",
// Claude 3.7 series
"claude-3-7-sonnet-20250219",
// Claude 3.5 series
"claude-3-5-sonnet-20241022",
"claude-3-5-haiku-20241022",
],
google: [
// Gemini 3 series
"gemini-3.1-pro",
"gemini-3.5-flash",
"gemini-3-flash",
"gemini-3.1-flash-lite",
// Gemini 2.5 series
"gemini-2.5-pro",
"gemini-2.5-flash",
"gemini-2.5-flash-lite",
],
vertexai: [
// Gemini 3 series
"gemini-3.1-pro-preview",
"gemini-3.5-flash",
"gemini-3-flash-preview",
"gemini-3.1-flash-lite",
// Gemini 2.5 series
"gemini-2.5-pro",
"gemini-2.5-flash",
"gemini-2.5-flash-lite",
],
azure: [
"gpt-5.5",
"gpt-5.4",
"gpt-5.1",
"gpt-5",
"gpt-5-mini",
"gpt-4.1",
"gpt-4o",
"gpt-4o-mini",
"o3",
"o4-mini",
],
bedrock: [
// Anthropic Claude
"anthropic.claude-opus-4-8",
"anthropic.claude-opus-4-7",
"anthropic.claude-sonnet-4-6",
"anthropic.claude-opus-4-6-v1",
"anthropic.claude-opus-4-5-20251101-v1:0",
"anthropic.claude-sonnet-4-5-20250929-v1:0",
"anthropic.claude-haiku-4-5-20251001-v1:0",
"anthropic.claude-opus-4-1-20250805-v1:0",
"anthropic.claude-opus-4-20250514-v1:0",
"anthropic.claude-sonnet-4-20250514-v1:0",
"anthropic.claude-3-5-haiku-20241022-v1:0",
// Amazon Nova
"amazon.nova-2-lite-v1:0",
"amazon.nova-premier-v1:0",
"amazon.nova-pro-v1:0",
"amazon.nova-lite-v1:0",
"amazon.nova-micro-v1:0",
// Meta Llama
"meta.llama4-maverick-17b-instruct-v1:0",
"meta.llama4-scout-17b-instruct-v1:0",
"meta.llama3-3-70b-instruct-v1:0",
// Mistral
"mistral.mistral-large-3-675b-instruct",
"mistral.pixtral-large-2502-v1:0",
],
openrouter: [
// Anthropic
"anthropic/claude-opus-4.8",
"anthropic/claude-sonnet-4.6",
"anthropic/claude-haiku-4.5",
// OpenAI
"openai/gpt-5.5",
"openai/gpt-5.4",
"openai/gpt-5.4-mini",
"openai/gpt-4o-mini",
// Google
"google/gemini-3.1-pro-preview",
"google/gemini-3.5-flash",
"google/gemini-2.5-flash-lite",
// xAI
"x-ai/grok-4.3",
// Meta Llama
"meta-llama/llama-4-maverick",
"meta-llama/llama-4-scout",
"meta-llama/llama-3.3-70b-instruct",
// DeepSeek
"deepseek/deepseek-v4-pro",
"deepseek/deepseek-v3.2",
// Qwen
"qwen/qwen3.7-max",
"qwen/qwen3-coder",
// MiniMax
"minimax/minimax-m3",
],
aihubmix: [
// Fallback list. The settings UI loads the live model list from AIHubMix when available.
// Anthropic Claude
"claude-fable-5",
"claude-opus-4-8",
"claude-sonnet-4-6",
// OpenAI
"gpt-5.5",
"gpt-5.5-pro",
"gpt-5.4",
// Google Gemini
"gemini-3.5-flash",
"gemini-3.1-pro-preview",
"gemini-3-flash-preview",
// DeepSeek
"deepseek-v4-pro",
"deepseek-v4-flash",
// Qwen
"qwen3.7-max",
"qwen3-coder-next",
// Z.ai
"glm-5.1",
// Moonshot AI
"kimi-k2.6",
// MiniMax
"minimax-m3",
// xAI
"grok-4.3",
// Baidu
"ernie-5.1",
// Mistral
"mistral-large-3",
// Meta
"llama-4-maverick",
],
deepseek: [
"deepseek-v4-pro",
"deepseek-v4-flash",
"deepseek-chat",
"deepseek-reasoner",
],
siliconflow: [
// DeepSeek
"deepseek-ai/DeepSeek-V4-Pro",
"deepseek-ai/DeepSeek-V4-Flash",
"deepseek-ai/DeepSeek-V3.2",
// MiniMax
"MiniMaxAI/MiniMax-M3",
// Moonshot
"moonshotai/Kimi-K2.6",
// Z.ai
"zai-org/GLM-5",
// Qwen
"Qwen/Qwen3.6-35B-A3B",
"Qwen/Qwen3-Coder-480B-A35B-Instruct",
"Qwen/Qwen3-30B-A3B-Instruct-2507",
"Qwen/Qwen3-VL-32B-Instruct",
// OpenAI open-weights
"openai/gpt-oss-120b",
],
sglang: [
// SGLang is OpenAI-compatible, models depend on deployment
"default",
],
gateway: [
"openai/gpt-5.5",
"anthropic/claude-opus-4.7",
"google/gemini-3.1-pro-preview",
"xai/grok-4.3",
"anthropic/claude-sonnet-4.6",
"anthropic/claude-haiku-4.5",
"openai/gpt-5.4-mini",
],
edgeone: ["@tx/deepseek-ai/deepseek-v32"],
doubao: [
// ByteDance Doubao models (Volcengine Ark IDs use dash form)
"doubao-seed-2-0-pro-260215",
"doubao-seed-2-0-lite-260428",
"doubao-seed-2-0-mini-260428",
"doubao-seed-1-8-251228",
"doubao-seed-1-6-251015",
"doubao-seed-1-6-flash-250828",
"doubao-seed-1-6-vision-250815",
"doubao-1-5-pro-32k-250115",
"doubao-1-5-lite-32k-250115",
],
modelscope: [
// DeepSeek
"deepseek-ai/DeepSeek-V4-Pro",
"deepseek-ai/DeepSeek-V3.2",
"deepseek-ai/DeepSeek-R1-0528",
"deepseek-ai/DeepSeek-R1",
// Qwen
"Qwen/Qwen3-235B-A22B-Instruct-2507",
"Qwen/Qwen3-VL-235B-A22B-Instruct",
"Qwen/Qwen3-Coder-30B-A3B-Instruct",
"Qwen/Qwen3-32B",
"Qwen/Qwen2.5-72B-Instruct",
],
minimax: [
// MiniMax models (Anthropic-compatible API)
"MiniMax-M3",
"MiniMax-M2.7",
"MiniMax-M2.7-highspeed",
"MiniMax-M2.5",
],
novita: [
// Novita AI models (OpenAI-compatible API)
"minimax/minimax-m3",
"deepseek/deepseek-v4-pro",
"zai-org/glm-5.1",
"moonshotai/kimi-k2.6",
"deepseek/deepseek-v4-flash",
],
mimo: ["mimo-v2.5-pro", "mimo-v2.5"],
}
// Helper to generate UUID
export function generateId(): string {
return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
}
// Create empty config
export function createEmptyConfig(): MultiModelConfig {
return {
version: 1,
providers: [],
selectedModelId: undefined,
}
}
// Create new provider config
export function createProviderConfig(provider: ProviderName): ProviderConfig {
return {
id: generateId(),
provider,
apiKey: "",
baseUrl: PROVIDER_INFO[provider].defaultBaseUrl,
models: [],
validated: false,
}
}
// Create new model config
export function createModelConfig(modelId: string): ModelConfig {
return {
id: generateId(),
modelId,
}
}
// Get all models as flattened list for dropdown (user-defined only)
export function flattenModels(config: MultiModelConfig): FlattenedModel[] {
const models: FlattenedModel[] = []
for (const provider of config.providers) {
// Use custom name if provided, otherwise use default provider label
const providerLabel =
provider.name || PROVIDER_INFO[provider.provider].label
for (const model of provider.models) {
models.push({
id: model.id,
modelId: model.modelId,
provider: provider.provider,
providerLabel,
apiKey: provider.apiKey,
baseUrl: provider.baseUrl,
// AWS Bedrock fields
awsAccessKeyId: provider.awsAccessKeyId,
awsSecretAccessKey: provider.awsSecretAccessKey,
awsRegion: provider.awsRegion,
awsSessionToken: provider.awsSessionToken,
// Vertex AI fields
vertexApiKey: provider.vertexApiKey,
validated: model.validated,
source: "user",
isDefault: false,
})
}
}
return models
}
// Find model by ID
export function findModelById(
config: MultiModelConfig,
modelId: string,
): FlattenedModel | undefined {
return flattenModels(config).find((m) => m.id === modelId)
}