File size: 2,453 Bytes
ff49634 df94b74 ff49634 df94b74 ff49634 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | import { describe, expect, it } from "bun:test";
import {
extractContentFromCompletionBody,
isLikelyTruncatedJson,
parseAiJsonResponse,
stripReasoningBlocks,
} from "../parse-response";
describe("stripReasoningBlocks", () => {
it("removes closed redacted_thinking blocks", () => {
const input = "<think>plan</think>\n{\"ok\":true}";
expect(stripReasoningBlocks(input)).toBe('{"ok":true}');
});
it("removes think blocks", () => {
const input = ["<", "think", ">planning</", "think", ">\n", '{"ok":true}'].join("");
expect(stripReasoningBlocks(input)).toBe('{"ok":true}');
});
it("removes unclosed thinking at start", () => {
const input = "<think>still streaming...";
expect(stripReasoningBlocks(input)).toBe("");
});
it("does not treat XML-like tags in JSON as reasoning", () => {
const input = '{"questionText":"<b>highlight</b>"}';
expect(stripReasoningBlocks(input)).toBe(input);
});
});
describe("parseAiJsonResponse", () => {
it("parses JSON after reasoning preamble", () => {
const parsed = parseAiJsonResponse(
"<think>planning</think>\n```json\n{\"questions\":[]}\n```",
);
expect(parsed).toEqual({ questions: [] });
});
it("extracts JSON object embedded in prose", () => {
const parsed = parseAiJsonResponse(
"Here is the result:\n{\"questions\":[{\"format\":\"multiple_choice\"}]}\nThanks!",
);
expect(parsed).toEqual({ questions: [{ format: "multiple_choice" }] });
});
it("throws on empty content after stripping", () => {
expect(() => parseAiJsonResponse("<think>only thinking")).toThrow(
"Empty response from AI",
);
});
it("detects truncated JSON and throws a clear truncation error", () => {
const truncated =
'{"title":"The Global Rise of Coffee Culture","passage":"Coffee has a rich history in the highl';
expect(isLikelyTruncatedJson(truncated)).toBe(true);
expect(() => parseAiJsonResponse(truncated)).toThrow("JSON response was truncated");
});
});
describe("extractContentFromCompletionBody", () => {
it("reads non-stream chat completion JSON", () => {
const body = JSON.stringify({
choices: [{ message: { content: '{"questions":[]}' } }],
});
expect(extractContentFromCompletionBody(body)).toBe('{"questions":[]}');
});
it("returns null for non-JSON bodies", () => {
expect(extractContentFromCompletionBody("<html>error</html>")).toBeNull();
});
});
|