@debriefer/ai 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/ai-defaults.test.d.ts +2 -0
- package/dist/__tests__/ai-defaults.test.d.ts.map +1 -0
- package/dist/__tests__/ai-defaults.test.js +369 -0
- package/dist/__tests__/ai-defaults.test.js.map +1 -0
- package/dist/__tests__/synthesizer.test.d.ts +2 -0
- package/dist/__tests__/synthesizer.test.d.ts.map +1 -0
- package/dist/__tests__/synthesizer.test.js +147 -0
- package/dist/__tests__/synthesizer.test.js.map +1 -0
- package/dist/ai-client.d.ts +52 -0
- package/dist/ai-client.d.ts.map +1 -0
- package/dist/ai-client.js +40 -0
- package/dist/ai-client.js.map +1 -0
- package/dist/confidence.d.ts +22 -0
- package/dist/confidence.d.ts.map +1 -0
- package/dist/confidence.js +53 -0
- package/dist/confidence.js.map +1 -0
- package/dist/index.d.ts +79 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +79 -0
- package/dist/index.js.map +1 -0
- package/dist/link-selector.d.ts +23 -0
- package/dist/link-selector.d.ts.map +1 -0
- package/dist/link-selector.js +67 -0
- package/dist/link-selector.js.map +1 -0
- package/dist/person-validator.d.ts +21 -0
- package/dist/person-validator.d.ts.map +1 -0
- package/dist/person-validator.js +63 -0
- package/dist/person-validator.js.map +1 -0
- package/dist/section-filter.d.ts +22 -0
- package/dist/section-filter.d.ts.map +1 -0
- package/dist/section-filter.js +54 -0
- package/dist/section-filter.js.map +1 -0
- package/dist/synthesizer.d.ts +91 -0
- package/dist/synthesizer.d.ts.map +1 -0
- package/dist/synthesizer.js +127 -0
- package/dist/synthesizer.js.map +1 -0
- package/package.json +54 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-defaults.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/ai-defaults.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
import { createAISectionFilter } from "../section-filter.js";
|
|
3
|
+
import { createAIConfidenceScorer } from "../confidence.js";
|
|
4
|
+
import { createAILinkSelector } from "../link-selector.js";
|
|
5
|
+
import { createAIPersonValidator } from "../person-validator.js";
|
|
6
|
+
import { createAIDefaults } from "../index.js";
|
|
7
|
+
// ============================================================================
|
|
8
|
+
// Mock AI Client
|
|
9
|
+
// ============================================================================
|
|
10
|
+
function mockClient(response) {
|
|
11
|
+
return {
|
|
12
|
+
complete: vi.fn().mockResolvedValue({
|
|
13
|
+
text: response,
|
|
14
|
+
usage: { inputTokens: 100, outputTokens: 50 },
|
|
15
|
+
}),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function failingClient(error) {
|
|
19
|
+
return {
|
|
20
|
+
complete: vi.fn().mockRejectedValue(new Error(error)),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function mockTelemetry() {
|
|
24
|
+
return {
|
|
25
|
+
recordEvent: vi.fn(),
|
|
26
|
+
startSpan: vi.fn().mockReturnValue({ end: vi.fn(), setAttributes: vi.fn() }),
|
|
27
|
+
recordError: vi.fn(),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const testSubject = {
|
|
31
|
+
id: 1,
|
|
32
|
+
name: "Marie Curie",
|
|
33
|
+
context: { birthYear: "1867", deathday: "1934-07-04", occupation: "physicist" },
|
|
34
|
+
};
|
|
35
|
+
const testSections = [
|
|
36
|
+
{ index: 0, title: "Introduction", depth: 0 },
|
|
37
|
+
{ index: 1, title: "Early life", depth: 0 },
|
|
38
|
+
{ index: 2, title: "Scientific career", depth: 0 },
|
|
39
|
+
{ index: 3, title: "Nobel Prizes", depth: 0 },
|
|
40
|
+
{ index: 4, title: "Death", depth: 0 },
|
|
41
|
+
{ index: 5, title: "Legacy", depth: 0 },
|
|
42
|
+
];
|
|
43
|
+
const testSearchResults = [
|
|
44
|
+
{ url: "https://example.com/a", title: "Result A", snippet: "About Marie" },
|
|
45
|
+
{ url: "https://example.com/b", title: "Result B", snippet: "Unrelated page" },
|
|
46
|
+
{ url: "https://example.com/c", title: "Result C", snippet: "Curie biography" },
|
|
47
|
+
];
|
|
48
|
+
// ============================================================================
|
|
49
|
+
// Section Filter
|
|
50
|
+
// ============================================================================
|
|
51
|
+
describe("createAISectionFilter", () => {
|
|
52
|
+
it("returns sections selected by AI", async () => {
|
|
53
|
+
const client = mockClient("[0, 3, 4]");
|
|
54
|
+
const filter = createAISectionFilter({
|
|
55
|
+
client,
|
|
56
|
+
researchGoal: "Find death info",
|
|
57
|
+
fallbackToHeuristics: true,
|
|
58
|
+
});
|
|
59
|
+
const result = await filter(testSections, "Full article text...");
|
|
60
|
+
expect(result).toHaveLength(3);
|
|
61
|
+
expect(result.map((s) => s.index)).toEqual([0, 3, 4]);
|
|
62
|
+
expect(client.complete).toHaveBeenCalledOnce();
|
|
63
|
+
});
|
|
64
|
+
it("falls back to all sections on AI failure when fallback enabled", async () => {
|
|
65
|
+
const client = failingClient("API error");
|
|
66
|
+
const telemetry = mockTelemetry();
|
|
67
|
+
const filter = createAISectionFilter({
|
|
68
|
+
client,
|
|
69
|
+
fallbackToHeuristics: true,
|
|
70
|
+
telemetry,
|
|
71
|
+
});
|
|
72
|
+
const result = await filter(testSections, "Full article text...");
|
|
73
|
+
expect(result).toEqual(testSections);
|
|
74
|
+
expect(telemetry.recordEvent).toHaveBeenCalledWith("ai.call_failed", expect.objectContaining({
|
|
75
|
+
callback: "sectionFilter",
|
|
76
|
+
fallback: true,
|
|
77
|
+
}));
|
|
78
|
+
});
|
|
79
|
+
it("returns empty on AI failure when fallback disabled", async () => {
|
|
80
|
+
const client = failingClient("API error");
|
|
81
|
+
const filter = createAISectionFilter({
|
|
82
|
+
client,
|
|
83
|
+
fallbackToHeuristics: false,
|
|
84
|
+
});
|
|
85
|
+
const result = await filter(testSections, "Full article text...");
|
|
86
|
+
expect(result).toEqual([]);
|
|
87
|
+
});
|
|
88
|
+
it("returns all sections when AI returns invalid JSON", async () => {
|
|
89
|
+
const client = mockClient("not valid json");
|
|
90
|
+
const filter = createAISectionFilter({
|
|
91
|
+
client,
|
|
92
|
+
fallbackToHeuristics: true,
|
|
93
|
+
});
|
|
94
|
+
const result = await filter(testSections, "Full article text...");
|
|
95
|
+
expect(result).toEqual(testSections);
|
|
96
|
+
});
|
|
97
|
+
it("returns all sections when AI returns empty array", async () => {
|
|
98
|
+
const client = mockClient("[]");
|
|
99
|
+
const filter = createAISectionFilter({
|
|
100
|
+
client,
|
|
101
|
+
fallbackToHeuristics: true,
|
|
102
|
+
});
|
|
103
|
+
const result = await filter(testSections, "Full article text...");
|
|
104
|
+
expect(result).toEqual(testSections);
|
|
105
|
+
});
|
|
106
|
+
it("returns empty array for empty input", async () => {
|
|
107
|
+
const client = mockClient("[0]");
|
|
108
|
+
const filter = createAISectionFilter({
|
|
109
|
+
client,
|
|
110
|
+
fallbackToHeuristics: true,
|
|
111
|
+
});
|
|
112
|
+
const result = await filter([], "");
|
|
113
|
+
expect(result).toEqual([]);
|
|
114
|
+
expect(client.complete).not.toHaveBeenCalled();
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
// ============================================================================
|
|
118
|
+
// Confidence Scorer
|
|
119
|
+
// ============================================================================
|
|
120
|
+
describe("createAIConfidenceScorer", () => {
|
|
121
|
+
it("returns AI-assessed confidence score", async () => {
|
|
122
|
+
const client = mockClient("0.85");
|
|
123
|
+
const scorer = createAIConfidenceScorer({
|
|
124
|
+
client,
|
|
125
|
+
researchGoal: "Find death info",
|
|
126
|
+
fallbackToHeuristics: true,
|
|
127
|
+
});
|
|
128
|
+
const result = await scorer("Marie Curie died in 1934.", testSubject);
|
|
129
|
+
expect(result).toBe(0.85);
|
|
130
|
+
expect(client.complete).toHaveBeenCalledOnce();
|
|
131
|
+
});
|
|
132
|
+
it("returns 0 for empty text", async () => {
|
|
133
|
+
const client = mockClient("0.85");
|
|
134
|
+
const scorer = createAIConfidenceScorer({
|
|
135
|
+
client,
|
|
136
|
+
fallbackToHeuristics: true,
|
|
137
|
+
});
|
|
138
|
+
const result = await scorer("", testSubject);
|
|
139
|
+
expect(result).toBe(0);
|
|
140
|
+
expect(client.complete).not.toHaveBeenCalled();
|
|
141
|
+
});
|
|
142
|
+
it("falls back to 0.5 on AI failure when fallback enabled", async () => {
|
|
143
|
+
const client = failingClient("Rate limited");
|
|
144
|
+
const telemetry = mockTelemetry();
|
|
145
|
+
const scorer = createAIConfidenceScorer({
|
|
146
|
+
client,
|
|
147
|
+
fallbackToHeuristics: true,
|
|
148
|
+
telemetry,
|
|
149
|
+
});
|
|
150
|
+
const result = await scorer("Some text", testSubject);
|
|
151
|
+
expect(result).toBe(0.5);
|
|
152
|
+
expect(telemetry.recordEvent).toHaveBeenCalledWith("ai.call_failed", expect.objectContaining({
|
|
153
|
+
callback: "confidenceScorer",
|
|
154
|
+
}));
|
|
155
|
+
});
|
|
156
|
+
it("returns 0 on AI failure when fallback disabled", async () => {
|
|
157
|
+
const client = failingClient("Rate limited");
|
|
158
|
+
const scorer = createAIConfidenceScorer({
|
|
159
|
+
client,
|
|
160
|
+
fallbackToHeuristics: false,
|
|
161
|
+
});
|
|
162
|
+
const result = await scorer("Some text", testSubject);
|
|
163
|
+
expect(result).toBe(0);
|
|
164
|
+
});
|
|
165
|
+
it("falls back on invalid score (out of range)", async () => {
|
|
166
|
+
const client = mockClient("1.5");
|
|
167
|
+
const scorer = createAIConfidenceScorer({
|
|
168
|
+
client,
|
|
169
|
+
fallbackToHeuristics: true,
|
|
170
|
+
});
|
|
171
|
+
const result = await scorer("Some text", testSubject);
|
|
172
|
+
expect(result).toBe(0.5); // fallback
|
|
173
|
+
});
|
|
174
|
+
it("falls back on non-numeric response", async () => {
|
|
175
|
+
const client = mockClient("very relevant");
|
|
176
|
+
const scorer = createAIConfidenceScorer({
|
|
177
|
+
client,
|
|
178
|
+
fallbackToHeuristics: true,
|
|
179
|
+
});
|
|
180
|
+
const result = await scorer("Some text", testSubject);
|
|
181
|
+
expect(result).toBe(0.5); // fallback
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
// ============================================================================
|
|
185
|
+
// Link Selector
|
|
186
|
+
// ============================================================================
|
|
187
|
+
describe("createAILinkSelector", () => {
|
|
188
|
+
it("reorders results based on AI ranking", async () => {
|
|
189
|
+
const client = mockClient("[2, 0, 1]");
|
|
190
|
+
const selector = createAILinkSelector({
|
|
191
|
+
client,
|
|
192
|
+
researchGoal: "Find death info",
|
|
193
|
+
fallbackToHeuristics: true,
|
|
194
|
+
});
|
|
195
|
+
const result = await selector(testSearchResults, testSubject);
|
|
196
|
+
expect(result).toHaveLength(3);
|
|
197
|
+
expect(result[0].url).toBe("https://example.com/c");
|
|
198
|
+
expect(result[1].url).toBe("https://example.com/a");
|
|
199
|
+
expect(result[2].url).toBe("https://example.com/b");
|
|
200
|
+
});
|
|
201
|
+
it("returns empty array for empty input", async () => {
|
|
202
|
+
const client = mockClient("[]");
|
|
203
|
+
const selector = createAILinkSelector({
|
|
204
|
+
client,
|
|
205
|
+
fallbackToHeuristics: true,
|
|
206
|
+
});
|
|
207
|
+
const result = await selector([], testSubject);
|
|
208
|
+
expect(result).toEqual([]);
|
|
209
|
+
expect(client.complete).not.toHaveBeenCalled();
|
|
210
|
+
});
|
|
211
|
+
it("falls back to original order on AI failure when fallback enabled", async () => {
|
|
212
|
+
const client = failingClient("API error");
|
|
213
|
+
const selector = createAILinkSelector({
|
|
214
|
+
client,
|
|
215
|
+
fallbackToHeuristics: true,
|
|
216
|
+
});
|
|
217
|
+
const result = await selector(testSearchResults, testSubject);
|
|
218
|
+
expect(result).toEqual(testSearchResults);
|
|
219
|
+
});
|
|
220
|
+
it("returns empty on AI failure when fallback disabled", async () => {
|
|
221
|
+
const client = failingClient("API error");
|
|
222
|
+
const selector = createAILinkSelector({
|
|
223
|
+
client,
|
|
224
|
+
fallbackToHeuristics: false,
|
|
225
|
+
});
|
|
226
|
+
const result = await selector(testSearchResults, testSubject);
|
|
227
|
+
expect(result).toEqual([]);
|
|
228
|
+
});
|
|
229
|
+
it("appends unmentioned results at the end", async () => {
|
|
230
|
+
// AI only mentions index 2, remaining should be appended
|
|
231
|
+
const client = mockClient("[2]");
|
|
232
|
+
const selector = createAILinkSelector({
|
|
233
|
+
client,
|
|
234
|
+
fallbackToHeuristics: true,
|
|
235
|
+
});
|
|
236
|
+
const result = await selector(testSearchResults, testSubject);
|
|
237
|
+
expect(result).toHaveLength(3);
|
|
238
|
+
expect(result[0].url).toBe("https://example.com/c"); // AI picked
|
|
239
|
+
expect(result[1].url).toBe("https://example.com/a"); // appended
|
|
240
|
+
expect(result[2].url).toBe("https://example.com/b"); // appended
|
|
241
|
+
});
|
|
242
|
+
it("deduplicates repeated indices from AI", async () => {
|
|
243
|
+
const client = mockClient("[0, 0, 1, 1, 2]");
|
|
244
|
+
const selector = createAILinkSelector({
|
|
245
|
+
client,
|
|
246
|
+
fallbackToHeuristics: true,
|
|
247
|
+
});
|
|
248
|
+
const result = await selector(testSearchResults, testSubject);
|
|
249
|
+
expect(result).toHaveLength(3);
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
// ============================================================================
|
|
253
|
+
// Person Validator
|
|
254
|
+
// ============================================================================
|
|
255
|
+
describe("createAIPersonValidator", () => {
|
|
256
|
+
it("returns true when AI confirms person match", async () => {
|
|
257
|
+
const client = mockClient("true");
|
|
258
|
+
const validator = createAIPersonValidator({
|
|
259
|
+
client,
|
|
260
|
+
fallbackToHeuristics: true,
|
|
261
|
+
});
|
|
262
|
+
const result = await validator("Marie Curie was a physicist born in Warsaw.", testSubject);
|
|
263
|
+
expect(result).toBe(true);
|
|
264
|
+
expect(client.complete).toHaveBeenCalledOnce();
|
|
265
|
+
});
|
|
266
|
+
it("returns false when AI denies person match", async () => {
|
|
267
|
+
const client = mockClient("false");
|
|
268
|
+
const validator = createAIPersonValidator({
|
|
269
|
+
client,
|
|
270
|
+
fallbackToHeuristics: true,
|
|
271
|
+
});
|
|
272
|
+
const result = await validator("Pierre Curie was born in Paris.", testSubject);
|
|
273
|
+
expect(result).toBe(false);
|
|
274
|
+
});
|
|
275
|
+
it("returns false for empty article text", async () => {
|
|
276
|
+
const client = mockClient("true");
|
|
277
|
+
const validator = createAIPersonValidator({
|
|
278
|
+
client,
|
|
279
|
+
fallbackToHeuristics: true,
|
|
280
|
+
});
|
|
281
|
+
const result = await validator("", testSubject);
|
|
282
|
+
expect(result).toBe(false);
|
|
283
|
+
expect(client.complete).not.toHaveBeenCalled();
|
|
284
|
+
});
|
|
285
|
+
it("falls back to true on AI failure when fallback enabled", async () => {
|
|
286
|
+
const client = failingClient("Network error");
|
|
287
|
+
const validator = createAIPersonValidator({
|
|
288
|
+
client,
|
|
289
|
+
fallbackToHeuristics: true,
|
|
290
|
+
});
|
|
291
|
+
const result = await validator("Some article text", testSubject);
|
|
292
|
+
expect(result).toBe(true);
|
|
293
|
+
});
|
|
294
|
+
it("returns false on AI failure when fallback disabled", async () => {
|
|
295
|
+
const client = failingClient("Network error");
|
|
296
|
+
const validator = createAIPersonValidator({
|
|
297
|
+
client,
|
|
298
|
+
fallbackToHeuristics: false,
|
|
299
|
+
});
|
|
300
|
+
const result = await validator("Some article text", testSubject);
|
|
301
|
+
expect(result).toBe(false);
|
|
302
|
+
});
|
|
303
|
+
it("includes context hints in the prompt", async () => {
|
|
304
|
+
const client = mockClient("true");
|
|
305
|
+
const validator = createAIPersonValidator({
|
|
306
|
+
client,
|
|
307
|
+
fallbackToHeuristics: true,
|
|
308
|
+
});
|
|
309
|
+
await validator("Some text", testSubject);
|
|
310
|
+
const call = client.complete.mock.calls[0][0];
|
|
311
|
+
expect(call.user).toContain("Born: 1867");
|
|
312
|
+
expect(call.user).toContain("Died: 1934-07-04");
|
|
313
|
+
expect(call.user).toContain("Occupation: physicist");
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
// ============================================================================
|
|
317
|
+
// createAIDefaults
|
|
318
|
+
// ============================================================================
|
|
319
|
+
describe("createAIDefaults", () => {
|
|
320
|
+
const originalEnv = process.env.ANTHROPIC_API_KEY;
|
|
321
|
+
beforeEach(() => {
|
|
322
|
+
if (originalEnv === undefined) {
|
|
323
|
+
delete process.env.ANTHROPIC_API_KEY;
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
process.env.ANTHROPIC_API_KEY = originalEnv;
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
it("creates all four callbacks with a custom client", () => {
|
|
330
|
+
const client = mockClient("test");
|
|
331
|
+
const ai = createAIDefaults({ client });
|
|
332
|
+
expect(ai.sectionFilter).toBeTypeOf("function");
|
|
333
|
+
expect(ai.confidenceScorer).toBeTypeOf("function");
|
|
334
|
+
expect(ai.linkSelector).toBeTypeOf("function");
|
|
335
|
+
expect(ai.personValidator).toBeTypeOf("function");
|
|
336
|
+
expect(ai.isAvailable).toBe(true);
|
|
337
|
+
});
|
|
338
|
+
it("reports unavailable when no API key and no custom client", () => {
|
|
339
|
+
delete process.env.ANTHROPIC_API_KEY;
|
|
340
|
+
const telemetry = mockTelemetry();
|
|
341
|
+
const ai = createAIDefaults({ telemetry });
|
|
342
|
+
expect(ai.isAvailable).toBe(false);
|
|
343
|
+
expect(telemetry.recordEvent).toHaveBeenCalledWith("ai.unavailable", expect.objectContaining({ reason: expect.stringContaining("ANTHROPIC_API_KEY not set") }));
|
|
344
|
+
});
|
|
345
|
+
it("provides passthrough callbacks when unavailable", async () => {
|
|
346
|
+
delete process.env.ANTHROPIC_API_KEY;
|
|
347
|
+
const ai = createAIDefaults();
|
|
348
|
+
const sections = await ai.sectionFilter(testSections, "text");
|
|
349
|
+
expect(sections).toEqual(testSections);
|
|
350
|
+
const confidence = await ai.confidenceScorer("text", testSubject);
|
|
351
|
+
expect(confidence).toBe(0.5);
|
|
352
|
+
const links = await ai.linkSelector(testSearchResults, testSubject);
|
|
353
|
+
expect(links).toEqual(testSearchResults);
|
|
354
|
+
const valid = await ai.personValidator("text", testSubject);
|
|
355
|
+
expect(valid).toBe(true);
|
|
356
|
+
vi.restoreAllMocks();
|
|
357
|
+
});
|
|
358
|
+
it("reports available when API key is set", () => {
|
|
359
|
+
process.env.ANTHROPIC_API_KEY = "test-key";
|
|
360
|
+
const ai = createAIDefaults();
|
|
361
|
+
expect(ai.isAvailable).toBe(true);
|
|
362
|
+
});
|
|
363
|
+
it("reports available when explicit apiKey provided", () => {
|
|
364
|
+
delete process.env.ANTHROPIC_API_KEY;
|
|
365
|
+
const ai = createAIDefaults({ apiKey: "explicit-key" });
|
|
366
|
+
expect(ai.isAvailable).toBe(true);
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
//# sourceMappingURL=ai-defaults.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-defaults.test.js","sourceRoot":"","sources":["../../src/__tests__/ai-defaults.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"synthesizer.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/synthesizer.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
import { ReliabilityTier } from "@debriefer/core";
|
|
3
|
+
vi.mock("@anthropic-ai/sdk", () => {
|
|
4
|
+
return {
|
|
5
|
+
default: class MockAnthropic {
|
|
6
|
+
messages = {
|
|
7
|
+
create: vi.fn().mockResolvedValue({
|
|
8
|
+
content: [{ type: "text", text: '```json\n{"result": "test"}\n```' }],
|
|
9
|
+
usage: { input_tokens: 1000, output_tokens: 500 },
|
|
10
|
+
}),
|
|
11
|
+
};
|
|
12
|
+
constructor() { }
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
});
|
|
16
|
+
import { ClaudeSynthesizer } from "../synthesizer.js";
|
|
17
|
+
function makeFinding(overrides = {}) {
|
|
18
|
+
return {
|
|
19
|
+
text: "Some finding text",
|
|
20
|
+
confidence: 0.8,
|
|
21
|
+
costUsd: 0,
|
|
22
|
+
sourceType: "wikipedia",
|
|
23
|
+
sourceName: "Wikipedia",
|
|
24
|
+
reliabilityTier: ReliabilityTier.SECONDARY_COMPILATION,
|
|
25
|
+
reliabilityScore: 0.85,
|
|
26
|
+
...overrides,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
// Identity parser for tests where we just want the raw JSON back
|
|
30
|
+
const identityParser = (raw) => raw;
|
|
31
|
+
const testSubject = {
|
|
32
|
+
id: 1,
|
|
33
|
+
name: "John Wayne",
|
|
34
|
+
context: { deathday: "1979-06-11" },
|
|
35
|
+
};
|
|
36
|
+
describe("ClaudeSynthesizer", () => {
|
|
37
|
+
let promptBuilderSpy;
|
|
38
|
+
beforeEach(() => {
|
|
39
|
+
vi.clearAllMocks();
|
|
40
|
+
promptBuilderSpy = vi.fn().mockReturnValue({
|
|
41
|
+
system: "You are a test system",
|
|
42
|
+
user: "Test user prompt",
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
it("calls Anthropic with correct model and max_tokens", async () => {
|
|
46
|
+
const synthesizer = new ClaudeSynthesizer({
|
|
47
|
+
promptBuilder: promptBuilderSpy,
|
|
48
|
+
responseParser: identityParser,
|
|
49
|
+
defaultModel: "claude-haiku-3-20250101",
|
|
50
|
+
defaultMaxTokens: 2048,
|
|
51
|
+
});
|
|
52
|
+
await synthesizer.synthesize(testSubject, [makeFinding()]);
|
|
53
|
+
const client = synthesizer.client;
|
|
54
|
+
expect(client.messages.create).toHaveBeenCalledWith(expect.objectContaining({
|
|
55
|
+
model: "claude-haiku-3-20250101",
|
|
56
|
+
max_tokens: 2048,
|
|
57
|
+
system: "You are a test system",
|
|
58
|
+
messages: [{ role: "user", content: "Test user prompt" }],
|
|
59
|
+
}));
|
|
60
|
+
});
|
|
61
|
+
it("sorts findings by reliability score before passing to promptBuilder", async () => {
|
|
62
|
+
const findings = [
|
|
63
|
+
makeFinding({
|
|
64
|
+
sourceType: "find_a_grave",
|
|
65
|
+
reliabilityScore: 0.35,
|
|
66
|
+
text: "Low reliability",
|
|
67
|
+
}),
|
|
68
|
+
makeFinding({
|
|
69
|
+
sourceType: "ap_news",
|
|
70
|
+
reliabilityScore: 0.95,
|
|
71
|
+
text: "High reliability",
|
|
72
|
+
}),
|
|
73
|
+
makeFinding({
|
|
74
|
+
sourceType: "wikipedia",
|
|
75
|
+
reliabilityScore: 0.85,
|
|
76
|
+
text: "Medium reliability",
|
|
77
|
+
}),
|
|
78
|
+
];
|
|
79
|
+
const synthesizer = new ClaudeSynthesizer({
|
|
80
|
+
promptBuilder: promptBuilderSpy,
|
|
81
|
+
responseParser: identityParser,
|
|
82
|
+
});
|
|
83
|
+
await synthesizer.synthesize(testSubject, findings);
|
|
84
|
+
const passedFindings = promptBuilderSpy.mock.calls[0][1];
|
|
85
|
+
expect(passedFindings[0].reliabilityScore).toBe(0.95);
|
|
86
|
+
expect(passedFindings[1].reliabilityScore).toBe(0.85);
|
|
87
|
+
expect(passedFindings[2].reliabilityScore).toBe(0.35);
|
|
88
|
+
});
|
|
89
|
+
it("parses JSON response through responseParser", async () => {
|
|
90
|
+
const synthesizer = new ClaudeSynthesizer({
|
|
91
|
+
promptBuilder: promptBuilderSpy,
|
|
92
|
+
responseParser: identityParser,
|
|
93
|
+
});
|
|
94
|
+
const result = await synthesizer.synthesize(testSubject, [makeFinding()]);
|
|
95
|
+
expect(result.data).toEqual({ result: "test" });
|
|
96
|
+
});
|
|
97
|
+
it("calculates cost from token counts", async () => {
|
|
98
|
+
const synthesizer = new ClaudeSynthesizer({
|
|
99
|
+
promptBuilder: promptBuilderSpy,
|
|
100
|
+
responseParser: identityParser,
|
|
101
|
+
// Default model is Sonnet: input $3/M, output $15/M
|
|
102
|
+
});
|
|
103
|
+
const result = await synthesizer.synthesize(testSubject, [makeFinding()]);
|
|
104
|
+
// 1000 input tokens * $3/M + 500 output tokens * $15/M
|
|
105
|
+
const expectedCost = (1000 * 3) / 1_000_000 + (500 * 15) / 1_000_000;
|
|
106
|
+
expect(result.costUsd).toBeCloseTo(expectedCost);
|
|
107
|
+
expect(result.inputTokens).toBe(1000);
|
|
108
|
+
expect(result.outputTokens).toBe(500);
|
|
109
|
+
});
|
|
110
|
+
it("uses custom responseParser for validation", async () => {
|
|
111
|
+
const synthesizer = new ClaudeSynthesizer({
|
|
112
|
+
promptBuilder: promptBuilderSpy,
|
|
113
|
+
responseParser: (raw) => {
|
|
114
|
+
const obj = raw;
|
|
115
|
+
return { transformed: `parsed-${obj.result}` };
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
const result = await synthesizer.synthesize(testSubject, [makeFinding()]);
|
|
119
|
+
expect(result.data).toEqual({ transformed: "parsed-test" });
|
|
120
|
+
});
|
|
121
|
+
it("uses default model when none specified", async () => {
|
|
122
|
+
const synthesizer = new ClaudeSynthesizer({
|
|
123
|
+
promptBuilder: promptBuilderSpy,
|
|
124
|
+
responseParser: identityParser,
|
|
125
|
+
});
|
|
126
|
+
await synthesizer.synthesize(testSubject, [makeFinding()]);
|
|
127
|
+
const client = synthesizer.client;
|
|
128
|
+
expect(client.messages.create).toHaveBeenCalledWith(expect.objectContaining({
|
|
129
|
+
model: "claude-sonnet-4-20250514",
|
|
130
|
+
}));
|
|
131
|
+
});
|
|
132
|
+
it("uses options.model override", async () => {
|
|
133
|
+
const synthesizer = new ClaudeSynthesizer({
|
|
134
|
+
promptBuilder: promptBuilderSpy,
|
|
135
|
+
responseParser: identityParser,
|
|
136
|
+
defaultModel: "claude-sonnet-4-20250514",
|
|
137
|
+
});
|
|
138
|
+
await synthesizer.synthesize(testSubject, [makeFinding()], {
|
|
139
|
+
model: "claude-opus-4-5-20251101",
|
|
140
|
+
});
|
|
141
|
+
const client = synthesizer.client;
|
|
142
|
+
expect(client.messages.create).toHaveBeenCalledWith(expect.objectContaining({
|
|
143
|
+
model: "claude-opus-4-5-20251101",
|
|
144
|
+
}));
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
//# sourceMappingURL=synthesizer.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"synthesizer.test.js","sourceRoot":"","sources":["../../src/__tests__/synthesizer.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AIClient abstraction for making AI inference calls.
|
|
3
|
+
*
|
|
4
|
+
* The default implementation uses Anthropic's Claude API via @anthropic-ai/sdk.
|
|
5
|
+
* Consumers can provide their own AIClient for non-Anthropic providers (OpenAI,
|
|
6
|
+
* Gemini, local models, etc.).
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* A single message completion request.
|
|
10
|
+
*/
|
|
11
|
+
export interface AICompletionRequest {
|
|
12
|
+
/** System prompt providing context and instructions */
|
|
13
|
+
system: string;
|
|
14
|
+
/** User message to respond to */
|
|
15
|
+
user: string;
|
|
16
|
+
/** Maximum tokens in the response */
|
|
17
|
+
maxTokens?: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Response from an AI completion call.
|
|
21
|
+
*/
|
|
22
|
+
export interface AICompletionResponse {
|
|
23
|
+
/** The text response from the model */
|
|
24
|
+
text: string;
|
|
25
|
+
/** Token usage for cost tracking */
|
|
26
|
+
usage: {
|
|
27
|
+
inputTokens: number;
|
|
28
|
+
outputTokens: number;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Interface for AI inference providers. Abstracts the API call so
|
|
33
|
+
* consumers can swap Claude for OpenAI, Gemini, local models, etc.
|
|
34
|
+
*/
|
|
35
|
+
export interface AIClient {
|
|
36
|
+
/** Make a completion call and return the text response. */
|
|
37
|
+
complete(request: AICompletionRequest): Promise<AICompletionResponse>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Default AIClient implementation using Anthropic's Claude API.
|
|
41
|
+
* Uses Haiku 4.5 by default for cost-effective AI callbacks.
|
|
42
|
+
*/
|
|
43
|
+
export declare class HaikuClient implements AIClient {
|
|
44
|
+
private client;
|
|
45
|
+
private model;
|
|
46
|
+
constructor(options?: {
|
|
47
|
+
apiKey?: string;
|
|
48
|
+
model?: string;
|
|
49
|
+
});
|
|
50
|
+
complete(request: AICompletionRequest): Promise<AICompletionResponse>;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=ai-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-client.d.ts","sourceRoot":"","sources":["../src/ai-client.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,uDAAuD;IACvD,MAAM,EAAE,MAAM,CAAA;IACd,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAA;IACZ,oCAAoC;IACpC,KAAK,EAAE;QACL,WAAW,EAAE,MAAM,CAAA;QACnB,YAAY,EAAE,MAAM,CAAA;KACrB,CAAA;CACF;AAED;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,2DAA2D;IAC3D,QAAQ,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAA;CACtE;AAED;;;GAGG;AACH,qBAAa,WAAY,YAAW,QAAQ;IAC1C,OAAO,CAAC,MAAM,CAAW;IACzB,OAAO,CAAC,KAAK,CAAQ;gBAET,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAO;IAKvD,QAAQ,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,oBAAoB,CAAC;CAqB5E"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AIClient abstraction for making AI inference calls.
|
|
3
|
+
*
|
|
4
|
+
* The default implementation uses Anthropic's Claude API via @anthropic-ai/sdk.
|
|
5
|
+
* Consumers can provide their own AIClient for non-Anthropic providers (OpenAI,
|
|
6
|
+
* Gemini, local models, etc.).
|
|
7
|
+
*/
|
|
8
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
9
|
+
/**
|
|
10
|
+
* Default AIClient implementation using Anthropic's Claude API.
|
|
11
|
+
* Uses Haiku 4.5 by default for cost-effective AI callbacks.
|
|
12
|
+
*/
|
|
13
|
+
export class HaikuClient {
|
|
14
|
+
client;
|
|
15
|
+
model;
|
|
16
|
+
constructor(options = {}) {
|
|
17
|
+
this.client = new Anthropic({ apiKey: options.apiKey });
|
|
18
|
+
this.model = options.model ?? "claude-haiku-4-5-20251001";
|
|
19
|
+
}
|
|
20
|
+
async complete(request) {
|
|
21
|
+
const response = await this.client.messages.create({
|
|
22
|
+
model: this.model,
|
|
23
|
+
max_tokens: request.maxTokens ?? 1024,
|
|
24
|
+
system: request.system,
|
|
25
|
+
messages: [{ role: "user", content: request.user }],
|
|
26
|
+
});
|
|
27
|
+
const text = response.content
|
|
28
|
+
.filter((block) => block.type === "text")
|
|
29
|
+
.map((block) => block.text)
|
|
30
|
+
.join("");
|
|
31
|
+
return {
|
|
32
|
+
text,
|
|
33
|
+
usage: {
|
|
34
|
+
inputTokens: response.usage.input_tokens,
|
|
35
|
+
outputTokens: response.usage.output_tokens,
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=ai-client.js.map
|