feat(backend): Ollama embeddings preflight on session create/resume (503 hard-fail)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-29 19:37:39 +02:00
co-authored by Claude Sonnet 4.6
parent a0af089d00
commit 90a26dafce
5 changed files with 59 additions and 3 deletions
+47 -1
View File
@@ -9,7 +9,7 @@ const SCRIPT = path.resolve("../harness/tests/fake_pi/scripts/f1_disambiguation.
function mutApp(thtRunner: any) {
return buildApp(loadConfig({ THT_HARNESS_DIR: "../harness" }), {
thtRunner,
thtRunner: { ollamaEnsure: async () => ({ ok: true }), ...thtRunner },
getSettings: () => ({ workspace: "w" }) as any,
spawnFn: () => nodeSpawn("node", [FAKE, SCRIPT]) as any,
});
@@ -20,6 +20,7 @@ test("POST /sessions usa i settings (workspace/provider/model/thinking) e crea+a
let spawnArg: any;
const app = buildApp(loadConfig({ THT_HARNESS_DIR: "../harness" }), {
thtRunner: {
ollamaEnsure: async () => ({ ok: true }),
sessionNew: async (o: any) => { sessionNewArg = o; return { id: "s1" }; },
sessionList: async () => [{ id: "s1" }],
} as any,
@@ -40,6 +41,7 @@ test("POST /sessions usa i settings (workspace/provider/model/thinking) e crea+a
test("POST /sessions/:id/response inoltra al bridge (no error)", async () => {
const app = buildApp(loadConfig({ THT_HARNESS_DIR: "../harness" }), {
thtRunner: {
ollamaEnsure: async () => ({ ok: true }),
sessionNew: async () => ({ id: "s1" }),
sessionList: async () => [],
} as any,
@@ -118,3 +120,47 @@ test("POST resume on an archived session is refused with 409", async () => {
const res = await app.inject({ method: "POST", url: "/sessions/s1/resume" });
expect(res.statusCode).toBe(409);
});
test("POST /sessions refuses with 503 when ollamaEnsure fails (no session created)", async () => {
let createdCalled = false;
const app = buildApp(loadConfig({ THT_HARNESS_DIR: "../harness" }), {
thtRunner: {
ollamaEnsure: async () => ({ ok: false, stage: "model", error: "modello non installato" }),
sessionNew: async () => { createdCalled = true; return { id: "s1" }; },
} as any,
getSettings: () => ({ workspace: "psd" }) as any,
spawnFn: () => nodeSpawn("node", [FAKE, SCRIPT]) as any,
});
const res = await app.inject({ method: "POST", url: "/sessions", payload: { question: "q" } });
expect(res.statusCode).toBe(503);
expect(res.json().error).toContain("non installato");
expect(createdCalled).toBe(false);
});
test("POST /sessions proceeds when ollamaEnsure succeeds", async () => {
let ensureWs: string | undefined;
const app = buildApp(loadConfig({ THT_HARNESS_DIR: "../harness" }), {
thtRunner: {
ollamaEnsure: async (ws: string) => { ensureWs = ws; return { ok: true }; },
sessionNew: async () => ({ id: "s1" }),
} as any,
getSettings: () => ({ workspace: "psd" }) as any,
spawnFn: () => nodeSpawn("node", [FAKE, SCRIPT]) as any,
});
const res = await app.inject({ method: "POST", url: "/sessions", payload: { question: "q" } });
expect(res.json()).toEqual({ id: "s1" });
expect(ensureWs).toBe("psd");
});
test("POST /sessions/:id/resume refuses with 503 when ollamaEnsure fails", async () => {
const app = buildApp(loadConfig({ THT_HARNESS_DIR: "../harness" }), {
thtRunner: {
ollamaEnsure: async () => ({ ok: false, error: "Ollama down" }),
sessionShow: async () => ({ status: "open", archived: false }),
} as any,
getSettings: () => ({ workspace: "psd" }) as any,
spawnFn: () => nodeSpawn("node", [FAKE, SCRIPT]) as any,
});
const res = await app.inject({ method: "POST", url: "/sessions/s1/resume" });
expect(res.statusCode).toBe(503);
});