feat: add aliyun qwen backup provider
This commit is contained in:
@@ -7,6 +7,7 @@ import { NextRequest } from "next/server";
|
||||
|
||||
let runtimeRoot = "";
|
||||
let openAiOnboardRoute: (typeof import("../src/app/api/v1/accounts/onboard/openai-api/route"))["POST"];
|
||||
let aliyunQwenOnboardRoute: (typeof import("../src/app/api/v1/accounts/onboard/aliyun-qwen/route"))["POST"];
|
||||
let masterNodeOnboardRoute: (typeof import("../src/app/api/v1/accounts/onboard/master-node/route"))["POST"];
|
||||
let createAuthSession: (typeof import("../src/lib/boss-data"))["createAuthSession"];
|
||||
let readState: (typeof import("../src/lib/boss-data"))["readState"];
|
||||
@@ -19,14 +20,16 @@ async function setup() {
|
||||
process.env.BOSS_RUNTIME_ROOT = runtimeRoot;
|
||||
process.env.BOSS_STATE_FILE = path.join(runtimeRoot, "boss-state.json");
|
||||
|
||||
const [openAiModule, masterNodeModule, data, auth] = await Promise.all([
|
||||
const [openAiModule, aliyunModule, masterNodeModule, data, auth] = await Promise.all([
|
||||
import("../src/app/api/v1/accounts/onboard/openai-api/route.ts"),
|
||||
import("../src/app/api/v1/accounts/onboard/aliyun-qwen/route.ts"),
|
||||
import("../src/app/api/v1/accounts/onboard/master-node/route.ts"),
|
||||
import("../src/lib/boss-data.ts"),
|
||||
import("../src/lib/boss-auth.ts"),
|
||||
]);
|
||||
|
||||
openAiOnboardRoute = openAiModule.POST;
|
||||
aliyunQwenOnboardRoute = aliyunModule.POST;
|
||||
masterNodeOnboardRoute = masterNodeModule.POST;
|
||||
createAuthSession = data.createAuthSession;
|
||||
readState = data.readState;
|
||||
@@ -188,3 +191,61 @@ test("POST /api/v1/accounts/onboard/openai-api returns a clear network guidance
|
||||
globalThis.fetch = originalFetch;
|
||||
}
|
||||
});
|
||||
|
||||
test("POST /api/v1/accounts/onboard/aliyun-qwen creates a backup aliyun qwen account", async () => {
|
||||
await setup();
|
||||
|
||||
const originalFetch = globalThis.fetch;
|
||||
globalThis.fetch = (async (input, init) => {
|
||||
if (typeof input === "string" && input === "https://dashscope.aliyuncs.com/compatible-mode/v1/responses") {
|
||||
assert.equal(init?.method, "POST");
|
||||
return new Response(JSON.stringify({ output_text: "连接正常" }), {
|
||||
status: 200,
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
"x-request-id": "req-onboard-aliyun",
|
||||
},
|
||||
});
|
||||
}
|
||||
throw new Error(`unexpected fetch: ${String(input)}`);
|
||||
}) as typeof fetch;
|
||||
|
||||
try {
|
||||
const response = await aliyunQwenOnboardRoute(
|
||||
await createAuthedJsonRequest("http://127.0.0.1:3000/api/v1/accounts/onboard/aliyun-qwen", {
|
||||
label: "阿里备用",
|
||||
displayName: "阿里百炼备用账号",
|
||||
accountIdentifier: "dashscope-demo",
|
||||
model: "qwen3.5-plus",
|
||||
apiKey: "sk-aliyun-demo-123456",
|
||||
}),
|
||||
);
|
||||
|
||||
assert.equal(response.status, 200);
|
||||
const payload = (await response.json()) as {
|
||||
ok: boolean;
|
||||
accountId: string;
|
||||
message: string;
|
||||
account: { provider: string; role: string; model: string; isActive: boolean };
|
||||
};
|
||||
|
||||
assert.equal(payload.ok, true);
|
||||
assert.equal(payload.accountId, "aliyun-qwen-backup");
|
||||
assert.equal(payload.account.provider, "aliyun_qwen_api");
|
||||
assert.equal(payload.account.role, "backup");
|
||||
assert.equal(payload.account.model, "qwen3.5-plus");
|
||||
assert.equal(payload.account.isActive, false);
|
||||
assert.match(payload.message, /备用/);
|
||||
assert.match(payload.message, /阿里|百炼/);
|
||||
|
||||
const state = await readState();
|
||||
const account = state.aiAccounts.find((item) => item.accountId === "aliyun-qwen-backup");
|
||||
assert.ok(account, "expected aliyun backup account to be created");
|
||||
assert.equal(account?.provider, "aliyun_qwen_api");
|
||||
assert.equal(account?.role, "backup");
|
||||
assert.equal(account?.model, "qwen3.5-plus");
|
||||
assert.equal(account?.apiKey, "sk-aliyun-demo-123456");
|
||||
} finally {
|
||||
globalThis.fetch = originalFetch;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -9,6 +9,7 @@ let runtimeRoot = "";
|
||||
let validateAiAccountConnection: (typeof import("../src/lib/boss-master-agent"))["validateAiAccountConnection"];
|
||||
let readState: (typeof import("../src/lib/boss-data"))["readState"];
|
||||
let deviceHeartbeatRoute: (typeof import("../src/app/api/device-heartbeat/route"))["POST"];
|
||||
let saveAiAccount: (typeof import("../src/lib/boss-data"))["saveAiAccount"];
|
||||
|
||||
async function setup() {
|
||||
if (runtimeRoot) return;
|
||||
@@ -25,6 +26,7 @@ async function setup() {
|
||||
|
||||
validateAiAccountConnection = masterAgent.validateAiAccountConnection;
|
||||
readState = data.readState;
|
||||
saveAiAccount = data.saveAiAccount;
|
||||
deviceHeartbeatRoute = heartbeatModule.POST;
|
||||
}
|
||||
|
||||
@@ -77,3 +79,42 @@ test("validateAiAccountConnection reports degraded when the bound master node de
|
||||
assert.equal(result.status, "degraded");
|
||||
assert.match(result.message, /当前不在线|不在线/);
|
||||
});
|
||||
|
||||
test("validateAiAccountConnection probes aliyun qwen backup accounts through the compatible endpoint", async () => {
|
||||
await setup();
|
||||
|
||||
await saveAiAccount({
|
||||
accountId: "aliyun-qwen-backup",
|
||||
label: "阿里备用",
|
||||
role: "backup",
|
||||
provider: "aliyun_qwen_api",
|
||||
displayName: "阿里百炼备用账号",
|
||||
accountIdentifier: "dashscope-demo",
|
||||
model: "qwen3.5-plus",
|
||||
apiKey: "sk-aliyun-demo-123456",
|
||||
enabled: true,
|
||||
});
|
||||
|
||||
const originalFetch = globalThis.fetch;
|
||||
globalThis.fetch = (async (input) => {
|
||||
if (typeof input === "string" && input === "https://dashscope.aliyuncs.com/compatible-mode/v1/responses") {
|
||||
return new Response(JSON.stringify({ output_text: "连接正常" }), {
|
||||
status: 200,
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
"x-request-id": "req-aliyun-validate",
|
||||
},
|
||||
});
|
||||
}
|
||||
throw new Error(`unexpected fetch: ${String(input)}`);
|
||||
}) as typeof fetch;
|
||||
|
||||
try {
|
||||
const result = await validateAiAccountConnection("aliyun-qwen-backup");
|
||||
assert.equal(result.ok, true);
|
||||
assert.equal(result.status, "ready");
|
||||
assert.match(result.message, /连接正常/);
|
||||
} finally {
|
||||
globalThis.fetch = originalFetch;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -100,3 +100,72 @@ test("replyToMasterAgentUserMessage falls back to a runnable OpenAI API account
|
||||
globalThis.fetch = originalFetch;
|
||||
}
|
||||
});
|
||||
|
||||
test("replyToMasterAgentUserMessage falls back to a runnable aliyun qwen backup account when the master node is offline", async () => {
|
||||
await setup();
|
||||
|
||||
await saveAiAccount({
|
||||
accountId: "master-codex-primary",
|
||||
label: "主 GPT",
|
||||
role: "primary",
|
||||
provider: "master_codex_node",
|
||||
displayName: "Mac 上的 Master Codex Node",
|
||||
nodeId: "offline-node",
|
||||
nodeLabel: "离线节点",
|
||||
model: "gpt-5.4",
|
||||
enabled: true,
|
||||
setActive: true,
|
||||
loginStatusNote: "通过绑定的 Master Codex Node 对话。",
|
||||
});
|
||||
|
||||
await saveAiAccount({
|
||||
accountId: "aliyun-qwen-backup",
|
||||
label: "阿里备用",
|
||||
role: "backup",
|
||||
provider: "aliyun_qwen_api",
|
||||
displayName: "阿里百炼备用账号",
|
||||
accountIdentifier: "dashscope-demo",
|
||||
model: "qwen3.5-plus",
|
||||
apiKey: "sk-aliyun-demo-123456",
|
||||
enabled: true,
|
||||
setActive: false,
|
||||
loginStatusNote: "阿里百炼 Qwen 备用账号。",
|
||||
});
|
||||
|
||||
const originalFetch = globalThis.fetch;
|
||||
globalThis.fetch = (async (input) => {
|
||||
if (typeof input === "string" && input === "https://dashscope.aliyuncs.com/compatible-mode/v1/responses") {
|
||||
return new Response(JSON.stringify({ output_text: "阿里备用链路正常。" }), {
|
||||
status: 200,
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
"x-request-id": "req-master-aliyun-fallback",
|
||||
},
|
||||
});
|
||||
}
|
||||
throw new Error(`unexpected fetch: ${String(input)}`);
|
||||
}) as typeof fetch;
|
||||
|
||||
try {
|
||||
const result = await replyToMasterAgentUserMessage({
|
||||
requestMessageId: "msg-master-aliyun-fallback",
|
||||
requestText: "请只回复:阿里备用链路正常。",
|
||||
requestedBy: "Boss 超级管理员",
|
||||
requestedByAccount: "17600003315",
|
||||
});
|
||||
|
||||
assert.equal(result.ok, true);
|
||||
assert.equal(result.accountId, "aliyun-qwen-backup");
|
||||
assert.equal(result.requestId, "req-master-aliyun-fallback");
|
||||
|
||||
const state = await readState();
|
||||
const masterProject = state.projects.find((project) => project.id === "master-agent");
|
||||
const reply = masterProject?.messages.at(-1);
|
||||
assert.ok(reply, "expected a master-agent reply to be appended");
|
||||
assert.equal(reply?.sender, "master");
|
||||
assert.equal(reply?.senderLabel, "主 Agent · 阿里备用");
|
||||
assert.match(reply?.body ?? "", /阿里备用链路正常/);
|
||||
} finally {
|
||||
globalThis.fetch = originalFetch;
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user