fix: harden multi-model chat selection

This commit is contained in:
Jesse_Chen
2026-07-17 14:09:57 +08:00
parent 4968c961f7
commit 6c02f27d1c
13 changed files with 338 additions and 91 deletions
@@ -0,0 +1,36 @@
import assert from "node:assert/strict";
import test from "node:test";
import { reserveConsultationModel } from "../src/lib/consultation-model-selection.ts";
test("rejects an unknown model before credit reservation", async () => {
let reservationCalls = 0;
const result = await reserveConsultationModel(
"removed-model",
() => null,
async () => {
reservationCalls += 1;
return { success: true };
},
);
assert.deepEqual(result, { status: "unavailable" });
assert.equal(reservationCalls, 0);
});
test("keeps the resolved agent model and ledger model id together", async () => {
const model = { id: "deepseek-pro", model: "deepseek-v4-pro" };
const result = await reserveConsultationModel(
"deepseek-pro",
(modelId) => modelId === model.id ? model : null,
async () => ({ success: true, credits: 4 }),
);
assert.deepEqual(result, {
status: "reserved",
model,
usageModelId: "deepseek-pro",
reservation: { success: true, credits: 4 },
});
});
+5 -1
View File
@@ -50,7 +50,11 @@ test("resolves configured models while returning sanitized public metadata", ()
});
assert.equal(JSON.stringify(catalog.publicModels).includes("secret"), false);
assert.equal(JSON.stringify(catalog.publicModels).includes("baseURL"), false);
assert.equal(catalog.models[1]?.model, "openai/gpt-5-mini");
assert.deepEqual(catalog.models[1]?.model, {
providerId: "openai",
modelId: "gpt-5-mini",
apiKey: "openai-secret",
});
});
test("excludes an invalid catalog entry without leaking its secret", () => {
@@ -0,0 +1,60 @@
import assert from "node:assert/strict";
import test from "node:test";
import {
SessionModelPersistenceQueue,
persistSessionModelSelection,
} from "../src/lib/session-model-persistence.ts";
test("persists only model_id for the owned session", async () => {
const writes: unknown[] = [];
await persistSessionModelSelection(async (write) => {
writes.push(write);
return { found: true, error: null };
}, "user-1", "session-1", "gpt-mini");
assert.deepEqual(writes, [{
values: { model_id: "gpt-mini" },
sessionId: "session-1",
userId: "user-1",
}]);
});
test("serializes model writes for the same session", async () => {
const queue = new SessionModelPersistenceQueue();
const calls: string[] = [];
let releaseFirst = () => {};
const firstGate = new Promise<void>((resolve) => { releaseFirst = resolve; });
const first = queue.enqueue("session-1", async () => {
calls.push("first");
await firstGate;
});
const second = queue.enqueue("session-1", async () => {
calls.push("second");
});
await new Promise<void>((resolve) => setImmediate(resolve));
assert.deepEqual(calls, ["first"]);
releaseFirst();
await Promise.all([first, second]);
assert.deepEqual(calls, ["first", "second"]);
});
test("continues with the latest model write after an earlier sync fails", async () => {
const queue = new SessionModelPersistenceQueue();
const calls: string[] = [];
const failed = queue.enqueue("session-1", async () => {
calls.push("failed");
throw new Error("offline");
});
const latest = queue.enqueue("session-1", async () => {
calls.push("latest");
});
const results = await Promise.allSettled([failed, latest]);
assert.equal(results[0]?.status, "rejected");
assert.equal(results[1]?.status, "fulfilled");
assert.deepEqual(calls, ["failed", "latest"]);
});