Files
Jyotisha/frontend/tests/consult-session-model-fallback.test.ts
T
jesse-uxandCursor 6a761bc51e fix(synastry): show timeline spans and keep old chats on the default model
English reports hid the analysis fold, timeline titles left out the month span, and deleting a pinned model stopped the next consultation turn.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-21 16:28:04 +08:00

106 lines
4.4 KiB
TypeScript

import assert from "node:assert/strict";
import test from "node:test";
import { reconcileConsultSessionModel } from "../src/lib/consult/session-model-fallback.ts";
import { loadConsultTurnHead } from "../src/lib/consult/turn-prep.ts";
import type { ResolvedLanguageModel } from "../src/mastra/model.ts";
function model(id: string, version: number): ResolvedLanguageModel {
return {
id,
label: id,
description: "",
creditCost: 1,
isDefault: id === "default-model",
mode: "openai",
model: { providerId: "openai", modelId: id, apiKey: "sk-test" },
configVersion: version,
};
}
test("a session pinned to a deleted version continues on the default model", async () => {
const writes: Array<string | null> = [];
const decision = await reconcileConsultSessionModel({
session: { id: "session-1", model_id: "retired-model", model_config_version: 4 },
requestedModelId: "retired-model",
resolveBound: async (modelId) => modelId === "retired-model" ? null : model(modelId, 1),
loadCatalog: async () => ({
models: [model("default-model", 2)],
publicModels: [],
defaultModelId: "default-model",
issues: [],
}),
persistModelId: async (_sessionId, modelId) => {
writes.push(modelId);
return true;
},
});
assert.equal(decision.kind, "ready");
if (decision.kind !== "ready") return;
assert.equal(decision.model.id, "default-model");
assert.equal(decision.session.model_id, "default-model");
assert.equal(decision.session.model_config_version, 2);
assert.deepEqual(writes, ["default-model"]);
});
test("the next turn still uses the default when the client sends the deleted model id", async () => {
const decision = await reconcileConsultSessionModel({
session: { id: "session-1", model_id: "default-model", model_config_version: 2 },
requestedModelId: "retired-model",
resolveBound: async (modelId, version) => {
if (modelId === "retired-model") return null;
if (modelId === "default-model" && version === 2) return model("default-model", 2);
if (modelId === "default-model" && version == null) return model("default-model", 2);
return null;
},
loadCatalog: async () => ({ models: [], publicModels: [], defaultModelId: null, issues: [] }),
persistModelId: async () => {
throw new Error("persist should not run");
},
});
assert.equal(decision.kind, "ready");
if (decision.kind !== "ready") return;
assert.equal(decision.model.id, "default-model");
});
test("a live different model is still refused as a model change", async () => {
const decision = await reconcileConsultSessionModel({
session: { id: "session-1", model_id: "default-model", model_config_version: 2 },
requestedModelId: "other-model",
resolveBound: async (modelId) => model(modelId, modelId === "other-model" ? 1 : 2),
loadCatalog: async () => ({ models: [], publicModels: [], defaultModelId: null, issues: [] }),
persistModelId: async () => false,
});
assert.equal(decision.kind, "changed");
});
test("a deleted version with no default model still reports the model as unavailable", async () => {
const head = await loadConsultTurnHead({
parallel: false,
getUser: async () => ({ data: { user: { id: "user-1" } }, error: null }),
readPendingDeletion: async () => null,
readBody: async () => ({ success: true as const, data: { sessionId: "session-1", modelId: "retired-model" } }),
readSession: async () => ({
data: { id: "session-1", model_id: "retired-model", model_config_version: 4, session_type: "consultation" },
error: null,
}),
resolveModel: async (): Promise<ResolvedLanguageModel | null> => null,
reconcileModel: async (chatSession, requestedModelId) => {
const decision = await reconcileConsultSessionModel({
session: chatSession,
requestedModelId,
resolveBound: async () => null,
loadCatalog: async () => ({ models: [], publicModels: [], defaultModelId: null, issues: ["default_model_unavailable"] }),
persistModelId: async () => false,
});
if (decision.kind !== "ready") return decision;
return { kind: "ready" as const, chatSession: decision.session, sessionModel: decision.model };
},
});
assert.equal(head.ok, false);
if (head.ok) return;
assert.equal(head.response.status, 503);
const body = await head.response.json() as { error?: string };
assert.equal(body.error, "模型暂不可用");
});