feat(backend): ThtRunner.ollamaEnsure (parses tht ollama ensure --json)
This commit is contained in:
@@ -26,6 +26,15 @@ export interface SessionDocument {
|
|||||||
content: string;
|
content: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface OllamaEnsureResult {
|
||||||
|
ok: boolean;
|
||||||
|
stage?: string;
|
||||||
|
error?: string;
|
||||||
|
server?: string;
|
||||||
|
model?: string;
|
||||||
|
model_name?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export class ThtRunner {
|
export class ThtRunner {
|
||||||
constructor(private cfg: ThtConfig) {}
|
constructor(private cfg: ThtConfig) {}
|
||||||
|
|
||||||
@@ -129,4 +138,19 @@ export class ThtRunner {
|
|||||||
unarchive(id: string) { return this.ok(["session", "unarchive", id]); }
|
unarchive(id: string) { return this.ok(["session", "unarchive", id]); }
|
||||||
deleteSession(id: string) { return this.ok(["session", "delete", id]); }
|
deleteSession(id: string) { return this.ok(["session", "delete", id]); }
|
||||||
documents(id: string) { return this.json<SessionDocument[]>(["session", "documents", id, "--json"]); }
|
documents(id: string) { return this.json<SessionDocument[]>(["session", "documents", id, "--json"]); }
|
||||||
|
|
||||||
|
async ollamaEnsure(workspace: string, timeoutSec: number): Promise<OllamaEnsureResult> {
|
||||||
|
const { code, stdout, stderr } = await this.run(
|
||||||
|
["ollama", "ensure", "--json", "--timeout", String(timeoutSec)],
|
||||||
|
workspace,
|
||||||
|
);
|
||||||
|
let parsed: Partial<OllamaEnsureResult> = {};
|
||||||
|
try { parsed = JSON.parse(stdout.trim() || "{}"); } catch { /* leave {} */ }
|
||||||
|
if (code === 0) return { ok: true, ...parsed };
|
||||||
|
return {
|
||||||
|
ok: false,
|
||||||
|
stage: parsed.stage,
|
||||||
|
error: parsed.error ?? (stderr.trim() || `tht ollama ensure exit ${code}`),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,3 +114,28 @@ test("documents parses the JSON array", async () => {
|
|||||||
const docs = await r.documents("sid");
|
const docs = await r.documents("sid");
|
||||||
expect(docs[0].key).toBe("question");
|
expect(docs[0].key).toBe("question");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("ollamaEnsure builds argv with --json --timeout and the workspace -c", async () => {
|
||||||
|
let calledArgs: string[] = [];
|
||||||
|
let calledWs: string | undefined;
|
||||||
|
const r = new ThtRunner({ thtBin: "tht", harnessDir: "/nope", configPath: "config/tht.yaml" });
|
||||||
|
r.run = async (args, ws) => { calledArgs = args; calledWs = ws; return { code: 0, stdout: '{"ok":true,"server":"up","model":"warmed","model_name":"m"}', stderr: "" }; };
|
||||||
|
const res = await r.ollamaEnsure("psd", 60);
|
||||||
|
expect(calledArgs).toEqual(["ollama", "ensure", "--json", "--timeout", "60"]);
|
||||||
|
expect(calledWs).toBe("psd");
|
||||||
|
expect(res).toEqual({ ok: true, server: "up", model: "warmed", model_name: "m" });
|
||||||
|
});
|
||||||
|
|
||||||
|
test("ollamaEnsure maps a non-zero exit to ok:false with stage/error from stdout JSON", async () => {
|
||||||
|
const r = new ThtRunner({ thtBin: "tht", harnessDir: "/nope", configPath: "config/tht.yaml" });
|
||||||
|
r.run = async () => ({ code: 1, stdout: '{"ok":false,"stage":"model","error":"missing"}', stderr: "" });
|
||||||
|
expect(await r.ollamaEnsure("psd", 60)).toEqual({ ok: false, stage: "model", error: "missing" });
|
||||||
|
});
|
||||||
|
|
||||||
|
test("ollamaEnsure falls back to stderr when stdout is not JSON on failure", async () => {
|
||||||
|
const r = new ThtRunner({ thtBin: "tht", harnessDir: "/nope", configPath: "config/tht.yaml" });
|
||||||
|
r.run = async () => ({ code: 1, stdout: "", stderr: "boom" });
|
||||||
|
const res = await r.ollamaEnsure("psd", 60);
|
||||||
|
expect(res.ok).toBe(false);
|
||||||
|
expect(res.error).toContain("boom");
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user