feat(backend): ThtRunner.ollamaEnsure (parses tht ollama ensure --json)
This commit is contained in:
@@ -26,6 +26,15 @@ export interface SessionDocument {
|
||||
content: string;
|
||||
}
|
||||
|
||||
export interface OllamaEnsureResult {
|
||||
ok: boolean;
|
||||
stage?: string;
|
||||
error?: string;
|
||||
server?: string;
|
||||
model?: string;
|
||||
model_name?: string;
|
||||
}
|
||||
|
||||
export class ThtRunner {
|
||||
constructor(private cfg: ThtConfig) {}
|
||||
|
||||
@@ -129,4 +138,19 @@ export class ThtRunner {
|
||||
unarchive(id: string) { return this.ok(["session", "unarchive", id]); }
|
||||
deleteSession(id: string) { return this.ok(["session", "delete", id]); }
|
||||
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");
|
||||
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