Files
HealthLog/src/lib/ai/__tests__/json-extract.test.ts
T
Marc-André Bombeck a463206774 fix(ai): stop prefilling the assistant turn on Anthropic JSON calls
The Anthropic client steered JSON replies by appending an assistant turn
containing an opening brace and gluing the brace back onto the reply.
Current Claude models (the 4.6 / 4.7 / 4.8 family, Opus 5, Sonnet 5,
Fable 5 and 5.1) reject an assistant prefill with HTTP 400, so every
JSON surface on the provider (briefing, status cards, document and lab
extraction) failed for a self-hoster on a current model while plain chat
kept working.

Drop the prefill on every model. The request now carries a plain
instruction on the last user turn to reply with a single JSON object and
nothing else, and the reply is narrowed to the outermost object body via
a shared extractJsonObject helper, so a fenced or prefixed answer still
parses. The insights stripJsonFences helper delegates to the same
function rather than keeping a second copy. The tool-use path is
unchanged.

Reported independently by @sweidinger.
2026-09-02 10:28:05 +02:00

36 lines
1.1 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { extractJsonObject } from "../json-extract";
describe("extractJsonObject", () => {
it("returns a plain object untouched", () => {
expect(extractJsonObject('{"summary":"ok"}')).toBe('{"summary":"ok"}');
});
it("unwraps a ```json fence", () => {
expect(extractJsonObject('```json\n{"summary":"ok"}\n```')).toBe(
'{"summary":"ok"}',
);
});
it("unwraps a bare ``` fence", () => {
expect(extractJsonObject('```\n{"summary":"ok"}\n```')).toBe(
'{"summary":"ok"}',
);
});
it("trims a leading sentence and a trailing remark around the object", () => {
expect(
extractJsonObject('Here is the result:\n{"summary":"ok"}\nDone.'),
).toBe('{"summary":"ok"}');
});
it("keeps nested braces inside the outermost span", () => {
const nested = '{"a":{"b":[{"c":1}]},"d":"}"}';
expect(extractJsonObject(`Sure!\n${nested}`)).toBe(nested);
});
it("returns the trimmed input when there is no object body", () => {
expect(extractJsonObject(" plain prose ")).toBe("plain prose");
});
});