feat: add aliyun qwen backup provider

This commit is contained in:
kris
2026-04-01 05:33:35 +08:00
parent ba01ae5393
commit 60d69eb222
15 changed files with 708 additions and 87 deletions

View File

@@ -127,7 +127,7 @@ export type LoginMethod = "password" | "code";
export type OtaUpdateStatus = "available" | "scheduled" | "applied" | "skipped";
export type OtaLogStatus = "checked" | "applied" | "skipped";
export type AppLogLevel = "info" | "warn" | "error";
export type AiProvider = "master_codex_node" | "openai_api";
export type AiProvider = "master_codex_node" | "openai_api" | "aliyun_qwen_api";
export type AiAccountRole = "primary" | "backup" | "api_fallback";
export type AiAccountStatus = "ready" | "needs_login" | "needs_api_key" | "degraded" | "disabled";
export type MasterAgentTaskStatus = "queued" | "running" | "completed" | "failed";
@@ -2254,6 +2254,8 @@ export function aiProviderLabel(provider: AiProvider) {
return "Master Codex Node / ChatGPT Plus 节点";
case "openai_api":
return "OpenAI API";
case "aliyun_qwen_api":
return "阿里百炼 Qwen";
default:
return provider;
}
@@ -2283,9 +2285,13 @@ function maskApiKey(value?: string) {
return `${trimmed.slice(0, 4)}...${trimmed.slice(-4)}`;
}
function isApiKeyProvider(provider: AiProvider) {
return provider === "openai_api" || provider === "aliyun_qwen_api";
}
function deriveAiAccountStatus(account: AiAccount): AiAccountStatus {
if (!account.enabled) return "disabled";
if (account.provider === "openai_api") {
if (isApiKeyProvider(account.provider)) {
if (!account.apiKey?.trim()) return "needs_api_key";
return account.status === "disabled" ? "ready" : account.status;
}
@@ -2458,7 +2464,7 @@ export function getMasterIdentitySummaryFromState(state: BossState): MasterIdent
status: "needs_api_key",
statusLabel: aiStatusLabel("needs_api_key"),
canGenerate: false,
note: "请到“我的 > AI 账号”至少配置一个可用的 Master Codex NodeOpenAI API 账号。",
note: "请到“我的 > AI 账号”至少配置一个可用的 Master Codex NodeOpenAI API 或阿里百炼 Qwen 账号。",
};
}
@@ -4662,6 +4668,12 @@ export async function saveAiAccount(payload: {
existing?.accountId ??
payload.accountId?.trim() ??
`ai-${slugify(`${payload.label}-${payload.displayName}`)}`;
const defaultModel =
payload.provider === "aliyun_qwen_api"
? "qwen3.5-plus"
: payload.provider === "openai_api"
? "gpt-5.4"
: undefined;
const next: AiAccount = normalizeAiAccount({
accountId,
label: payload.label.trim() || aiRoleLabel(payload.role),
@@ -4671,21 +4683,21 @@ export async function saveAiAccount(payload: {
accountIdentifier: payload.accountIdentifier?.trim() || undefined,
nodeId: payload.nodeId?.trim() || undefined,
nodeLabel: payload.nodeLabel?.trim() || undefined,
model: payload.model?.trim() || (payload.provider === "openai_api" ? "gpt-5.4" : undefined),
model: payload.model?.trim() || defaultModel,
apiKey:
payload.provider === "openai_api"
isApiKeyProvider(payload.provider)
? payload.apiKey?.trim()
? payload.apiKey.trim()
: existing?.apiKey
: undefined,
apiKeyMasked:
payload.provider === "openai_api"
isApiKeyProvider(payload.provider)
? maskApiKey(payload.apiKey?.trim() || existing?.apiKey)
: undefined,
enabled: payload.enabled ?? existing?.enabled ?? true,
isActive: existing?.isActive ?? false,
status:
payload.provider === "openai_api"
isApiKeyProvider(payload.provider)
? payload.apiKey?.trim() || existing?.apiKey
? existing?.status === "degraded"
? "degraded"