causantic 0.9.3 → 0.10.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/README.md +70 -56
- package/dist/cli/skill-templates.d.ts.map +1 -1
- package/dist/cli/skill-templates.js +23 -18
- package/dist/cli/skill-templates.js.map +1 -1
- package/dist/clusters/cluster-manager.d.ts +16 -0
- package/dist/clusters/cluster-manager.d.ts.map +1 -1
- package/dist/clusters/cluster-manager.js +119 -1
- package/dist/clusters/cluster-manager.js.map +1 -1
- package/dist/config/loader.d.ts +16 -0
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +51 -0
- package/dist/config/loader.js.map +1 -1
- package/dist/config/memory-config.d.ts +26 -0
- package/dist/config/memory-config.d.ts.map +1 -1
- package/dist/config/memory-config.js +22 -0
- package/dist/config/memory-config.js.map +1 -1
- package/dist/eval/experiments/embedding-model-comparison/run-experiment.d.ts +20 -0
- package/dist/eval/experiments/embedding-model-comparison/run-experiment.d.ts.map +1 -0
- package/dist/eval/experiments/embedding-model-comparison/run-experiment.js +289 -0
- package/dist/eval/experiments/embedding-model-comparison/run-experiment.js.map +1 -0
- package/dist/eval/experiments/index-differentiation/alignment-analysis.d.ts +53 -0
- package/dist/eval/experiments/index-differentiation/alignment-analysis.d.ts.map +1 -0
- package/dist/eval/experiments/index-differentiation/alignment-analysis.js +91 -0
- package/dist/eval/experiments/index-differentiation/alignment-analysis.js.map +1 -0
- package/dist/eval/experiments/index-differentiation/discrimination-test.d.ts +24 -0
- package/dist/eval/experiments/index-differentiation/discrimination-test.d.ts.map +1 -0
- package/dist/eval/experiments/index-differentiation/discrimination-test.js +79 -0
- package/dist/eval/experiments/index-differentiation/discrimination-test.js.map +1 -0
- package/dist/eval/experiments/index-differentiation/index.d.ts +11 -0
- package/dist/eval/experiments/index-differentiation/index.d.ts.map +1 -0
- package/dist/eval/experiments/index-differentiation/index.js +8 -0
- package/dist/eval/experiments/index-differentiation/index.js.map +1 -0
- package/dist/eval/experiments/index-differentiation/refinement-test.d.ts +32 -0
- package/dist/eval/experiments/index-differentiation/refinement-test.d.ts.map +1 -0
- package/dist/eval/experiments/index-differentiation/refinement-test.js +203 -0
- package/dist/eval/experiments/index-differentiation/refinement-test.js.map +1 -0
- package/dist/eval/experiments/index-differentiation/run-experiment.d.ts +20 -0
- package/dist/eval/experiments/index-differentiation/run-experiment.d.ts.map +1 -0
- package/dist/eval/experiments/index-differentiation/run-experiment.js +338 -0
- package/dist/eval/experiments/index-differentiation/run-experiment.js.map +1 -0
- package/dist/eval/experiments/index-differentiation/similarity-analysis.d.ts +31 -0
- package/dist/eval/experiments/index-differentiation/similarity-analysis.d.ts.map +1 -0
- package/dist/eval/experiments/index-differentiation/similarity-analysis.js +60 -0
- package/dist/eval/experiments/index-differentiation/similarity-analysis.js.map +1 -0
- package/dist/eval/experiments/index-differentiation/types.d.ts +114 -0
- package/dist/eval/experiments/index-differentiation/types.d.ts.map +1 -0
- package/dist/eval/experiments/index-differentiation/types.js +8 -0
- package/dist/eval/experiments/index-differentiation/types.js.map +1 -0
- package/dist/eval/experiments/index-vs-chunk/jeopardy-experiment.d.ts +19 -0
- package/dist/eval/experiments/index-vs-chunk/jeopardy-experiment.d.ts.map +1 -0
- package/dist/eval/experiments/index-vs-chunk/jeopardy-experiment.js +328 -0
- package/dist/eval/experiments/index-vs-chunk/jeopardy-experiment.js.map +1 -0
- package/dist/eval/experiments/index-vs-chunk/jeopardy-generator.d.ts +27 -0
- package/dist/eval/experiments/index-vs-chunk/jeopardy-generator.d.ts.map +1 -0
- package/dist/eval/experiments/index-vs-chunk/jeopardy-generator.js +154 -0
- package/dist/eval/experiments/index-vs-chunk/jeopardy-generator.js.map +1 -0
- package/dist/eval/experiments/index-vs-chunk/query-generator.d.ts +23 -0
- package/dist/eval/experiments/index-vs-chunk/query-generator.d.ts.map +1 -0
- package/dist/eval/experiments/index-vs-chunk/query-generator.js +113 -0
- package/dist/eval/experiments/index-vs-chunk/query-generator.js.map +1 -0
- package/dist/eval/experiments/index-vs-chunk/run-experiment.d.ts +17 -0
- package/dist/eval/experiments/index-vs-chunk/run-experiment.d.ts.map +1 -0
- package/dist/eval/experiments/index-vs-chunk/run-experiment.js +341 -0
- package/dist/eval/experiments/index-vs-chunk/run-experiment.js.map +1 -0
- package/dist/eval/experiments/index-vs-chunk/types.d.ts +71 -0
- package/dist/eval/experiments/index-vs-chunk/types.d.ts.map +1 -0
- package/dist/eval/experiments/index-vs-chunk/types.js +8 -0
- package/dist/eval/experiments/index-vs-chunk/types.js.map +1 -0
- package/dist/eval/experiments/pipeline-dropout/run-experiment.d.ts +18 -0
- package/dist/eval/experiments/pipeline-dropout/run-experiment.d.ts.map +1 -0
- package/dist/eval/experiments/pipeline-dropout/run-experiment.js +347 -0
- package/dist/eval/experiments/pipeline-dropout/run-experiment.js.map +1 -0
- package/dist/eval/experiments/rescorer-ceiling/analyze-misses.d.ts +17 -0
- package/dist/eval/experiments/rescorer-ceiling/analyze-misses.d.ts.map +1 -0
- package/dist/eval/experiments/rescorer-ceiling/analyze-misses.js +247 -0
- package/dist/eval/experiments/rescorer-ceiling/analyze-misses.js.map +1 -0
- package/dist/eval/experiments/rescorer-ceiling/benchmark-rescorers.d.ts +18 -0
- package/dist/eval/experiments/rescorer-ceiling/benchmark-rescorers.d.ts.map +1 -0
- package/dist/eval/experiments/rescorer-ceiling/benchmark-rescorers.js +443 -0
- package/dist/eval/experiments/rescorer-ceiling/benchmark-rescorers.js.map +1 -0
- package/dist/eval/experiments/rescorer-ceiling/run-experiment.d.ts +16 -0
- package/dist/eval/experiments/rescorer-ceiling/run-experiment.d.ts.map +1 -0
- package/dist/eval/experiments/rescorer-ceiling/run-experiment.js +226 -0
- package/dist/eval/experiments/rescorer-ceiling/run-experiment.js.map +1 -0
- package/dist/index-entries/index-generator.d.ts +74 -0
- package/dist/index-entries/index-generator.d.ts.map +1 -0
- package/dist/index-entries/index-generator.js +323 -0
- package/dist/index-entries/index-generator.js.map +1 -0
- package/dist/index-entries/index-refresher.d.ts +54 -0
- package/dist/index-entries/index-refresher.d.ts.map +1 -0
- package/dist/index-entries/index-refresher.js +203 -0
- package/dist/index-entries/index-refresher.js.map +1 -0
- package/dist/index-entries/index.d.ts +6 -0
- package/dist/index-entries/index.d.ts.map +1 -0
- package/dist/index-entries/index.js +6 -0
- package/dist/index-entries/index.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/ingest/index-entry-hook.d.ts +15 -0
- package/dist/ingest/index-entry-hook.d.ts.map +1 -0
- package/dist/ingest/index-entry-hook.js +84 -0
- package/dist/ingest/index-entry-hook.js.map +1 -0
- package/dist/ingest/ingest-session.d.ts.map +1 -1
- package/dist/ingest/ingest-session.js +72 -18
- package/dist/ingest/ingest-session.js.map +1 -1
- package/dist/ingest/session-state.d.ts +49 -0
- package/dist/ingest/session-state.d.ts.map +1 -0
- package/dist/ingest/session-state.js +158 -0
- package/dist/ingest/session-state.js.map +1 -0
- package/dist/maintenance/scheduler.d.ts.map +1 -1
- package/dist/maintenance/scheduler.js +25 -0
- package/dist/maintenance/scheduler.js.map +1 -1
- package/dist/maintenance/tasks/backfill-index.d.ts +27 -0
- package/dist/maintenance/tasks/backfill-index.d.ts.map +1 -0
- package/dist/maintenance/tasks/backfill-index.js +44 -0
- package/dist/maintenance/tasks/backfill-index.js.map +1 -0
- package/dist/mcp/tools.d.ts +4 -0
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +115 -7
- package/dist/mcp/tools.js.map +1 -1
- package/dist/models/embedder.js +2 -2
- package/dist/models/embedder.js.map +1 -1
- package/dist/models/model-registry.d.ts +2 -0
- package/dist/models/model-registry.d.ts.map +1 -1
- package/dist/models/model-registry.js +15 -0
- package/dist/models/model-registry.js.map +1 -1
- package/dist/repomap/cache.d.ts +58 -0
- package/dist/repomap/cache.d.ts.map +1 -0
- package/dist/repomap/cache.js +101 -0
- package/dist/repomap/cache.js.map +1 -0
- package/dist/repomap/graph.d.ts +54 -0
- package/dist/repomap/graph.d.ts.map +1 -0
- package/dist/repomap/graph.js +113 -0
- package/dist/repomap/graph.js.map +1 -0
- package/dist/repomap/index.d.ts +83 -0
- package/dist/repomap/index.d.ts.map +1 -0
- package/dist/repomap/index.js +99 -0
- package/dist/repomap/index.js.map +1 -0
- package/dist/repomap/parser.d.ts +43 -0
- package/dist/repomap/parser.d.ts.map +1 -0
- package/dist/repomap/parser.js +994 -0
- package/dist/repomap/parser.js.map +1 -0
- package/dist/repomap/regex-parser.d.ts +24 -0
- package/dist/repomap/regex-parser.d.ts.map +1 -0
- package/dist/repomap/regex-parser.js +190 -0
- package/dist/repomap/regex-parser.js.map +1 -0
- package/dist/repomap/renderer.d.ts +40 -0
- package/dist/repomap/renderer.d.ts.map +1 -0
- package/dist/repomap/renderer.js +163 -0
- package/dist/repomap/renderer.js.map +1 -0
- package/dist/repomap/scanner.d.ts +32 -0
- package/dist/repomap/scanner.d.ts.map +1 -0
- package/dist/repomap/scanner.js +171 -0
- package/dist/repomap/scanner.js.map +1 -0
- package/dist/retrieval/chain-assembler.d.ts.map +1 -1
- package/dist/retrieval/chain-assembler.js +22 -3
- package/dist/retrieval/chain-assembler.js.map +1 -1
- package/dist/retrieval/index.d.ts +2 -0
- package/dist/retrieval/index.d.ts.map +1 -1
- package/dist/retrieval/index.js +2 -0
- package/dist/retrieval/index.js.map +1 -1
- package/dist/retrieval/mmr.d.ts +1 -0
- package/dist/retrieval/mmr.d.ts.map +1 -1
- package/dist/retrieval/mmr.js +35 -1
- package/dist/retrieval/mmr.js.map +1 -1
- package/dist/retrieval/search-assembler.d.ts +10 -1
- package/dist/retrieval/search-assembler.d.ts.map +1 -1
- package/dist/retrieval/search-assembler.js +249 -81
- package/dist/retrieval/search-assembler.js.map +1 -1
- package/dist/retrieval/session-reconstructor.d.ts +36 -0
- package/dist/retrieval/session-reconstructor.d.ts.map +1 -1
- package/dist/retrieval/session-reconstructor.js +126 -0
- package/dist/retrieval/session-reconstructor.js.map +1 -1
- package/dist/storage/db.d.ts.map +1 -1
- package/dist/storage/db.js +15 -0
- package/dist/storage/db.js.map +1 -1
- package/dist/storage/index-entry-store.d.ts +71 -0
- package/dist/storage/index-entry-store.d.ts.map +1 -0
- package/dist/storage/index-entry-store.js +275 -0
- package/dist/storage/index-entry-store.js.map +1 -0
- package/dist/storage/index.d.ts +5 -2
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +5 -1
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/migrations.d.ts.map +1 -1
- package/dist/storage/migrations.js +102 -0
- package/dist/storage/migrations.js.map +1 -1
- package/dist/storage/schema.sql +68 -2
- package/dist/storage/session-state-store.d.ts +61 -0
- package/dist/storage/session-state-store.d.ts.map +1 -0
- package/dist/storage/session-state-store.js +119 -0
- package/dist/storage/session-state-store.js.map +1 -0
- package/dist/storage/types.d.ts +50 -0
- package/dist/storage/types.d.ts.map +1 -1
- package/dist/storage/vector-store.d.ts +17 -2
- package/dist/storage/vector-store.d.ts.map +1 -1
- package/dist/storage/vector-store.js +96 -36
- package/dist/storage/vector-store.js.map +1 -1
- package/package.json +4 -2
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Re-scorer Ceiling Analysis
|
|
3
|
+
*
|
|
4
|
+
* Measures how much headroom a second-stage re-scorer would have by answering:
|
|
5
|
+
* 1. At high K values, what % of ground-truth chunks appear in vector search?
|
|
6
|
+
* 2. When found, what's their rank distribution?
|
|
7
|
+
* 3. If a perfect re-scorer promoted every found target to rank 1,
|
|
8
|
+
* what would budget assembly look like?
|
|
9
|
+
*
|
|
10
|
+
* Tests both raw chunk search and index entry search paths.
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* npx tsx src/eval/experiments/rescorer-ceiling/run-experiment.ts [--sample-size=50]
|
|
14
|
+
*/
|
|
15
|
+
import { getDb } from '../../../storage/db.js';
|
|
16
|
+
import { vectorStore, indexVectorStore } from '../../../storage/vector-store.js';
|
|
17
|
+
import { getChunkById } from '../../../storage/chunk-store.js';
|
|
18
|
+
import { getIndexEntryCount, getIndexedChunkCount, dereferenceToChunkIds, } from '../../../storage/index-entry-store.js';
|
|
19
|
+
import { getAllClusters, getClusterChunkIds } from '../../../storage/cluster-store.js';
|
|
20
|
+
import { Embedder } from '../../../models/embedder.js';
|
|
21
|
+
import { getModel } from '../../../models/model-registry.js';
|
|
22
|
+
import { loadConfig, toRuntimeConfig } from '../../../config/loader.js';
|
|
23
|
+
import { generateSearchQueries } from '../index-vs-chunk/query-generator.js';
|
|
24
|
+
// ── Helpers ────────────────────────────────────────────────────────────────
|
|
25
|
+
function createRng(seed) {
|
|
26
|
+
let s = seed;
|
|
27
|
+
return () => {
|
|
28
|
+
s = (s * 1664525 + 1013904223) & 0x7fffffff;
|
|
29
|
+
return s / 0x7fffffff;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function sampleChunks(sampleSize, seed) {
|
|
33
|
+
getDb();
|
|
34
|
+
const clusters = getAllClusters();
|
|
35
|
+
if (clusters.length === 0)
|
|
36
|
+
throw new Error('No clusters found.');
|
|
37
|
+
const rng = createRng(seed);
|
|
38
|
+
const result = [];
|
|
39
|
+
const shuffled = [...clusters].sort(() => rng() - 0.5);
|
|
40
|
+
for (const cluster of shuffled) {
|
|
41
|
+
if (result.length >= sampleSize)
|
|
42
|
+
break;
|
|
43
|
+
const chunkIds = getClusterChunkIds(cluster.id);
|
|
44
|
+
if (chunkIds.length < 2)
|
|
45
|
+
continue;
|
|
46
|
+
const numPicks = Math.min(2, Math.ceil(sampleSize / clusters.length), chunkIds.length);
|
|
47
|
+
const shuffledIds = [...chunkIds].sort(() => rng() - 0.5);
|
|
48
|
+
for (let i = 0; i < numPicks && result.length < sampleSize; i++) {
|
|
49
|
+
const chunk = getChunkById(shuffledIds[i]);
|
|
50
|
+
if (!chunk || chunk.content.length < 100)
|
|
51
|
+
continue;
|
|
52
|
+
result.push({
|
|
53
|
+
id: chunk.id,
|
|
54
|
+
sessionSlug: chunk.sessionSlug,
|
|
55
|
+
content: chunk.content,
|
|
56
|
+
clusterId: cluster.id,
|
|
57
|
+
clusterName: cluster.name,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
function percentile(sorted, p) {
|
|
64
|
+
if (sorted.length === 0)
|
|
65
|
+
return 0;
|
|
66
|
+
const idx = Math.ceil((p / 100) * sorted.length) - 1;
|
|
67
|
+
return sorted[Math.max(0, idx)];
|
|
68
|
+
}
|
|
69
|
+
// ── Search functions ───────────────────────────────────────────────────────
|
|
70
|
+
async function searchChunkPath(embedding, targetId, limit) {
|
|
71
|
+
const results = await vectorStore.search(embedding, limit);
|
|
72
|
+
const rank = results.findIndex((r) => r.id === targetId) + 1;
|
|
73
|
+
return { found: rank > 0, rank };
|
|
74
|
+
}
|
|
75
|
+
async function searchIndexPath(embedding, targetId, limit, entriesPerChunk) {
|
|
76
|
+
const indexLimit = Math.ceil(limit * entriesPerChunk);
|
|
77
|
+
const results = await indexVectorStore.search(embedding, indexLimit);
|
|
78
|
+
// Check each result — an index entry may dereference to multiple chunks
|
|
79
|
+
for (let i = 0; i < results.length; i++) {
|
|
80
|
+
const chunkIds = dereferenceToChunkIds([results[i].id]);
|
|
81
|
+
if (chunkIds.includes(targetId)) {
|
|
82
|
+
return { found: true, rank: i + 1 };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return { found: false, rank: 0 };
|
|
86
|
+
}
|
|
87
|
+
// ── Main ───────────────────────────────────────────────────────────────────
|
|
88
|
+
async function runAnalysis() {
|
|
89
|
+
const args = process.argv.slice(2);
|
|
90
|
+
const sampleSizeArg = args.find((a) => a.startsWith('--sample-size='));
|
|
91
|
+
const sampleSize = sampleSizeArg ? parseInt(sampleSizeArg.split('=')[1], 10) : 50;
|
|
92
|
+
const seed = 42;
|
|
93
|
+
console.log('=== Re-scorer Ceiling Analysis ===\n');
|
|
94
|
+
getDb();
|
|
95
|
+
const externalConfig = loadConfig();
|
|
96
|
+
const config = toRuntimeConfig(externalConfig);
|
|
97
|
+
const maxTokens = config.mcpMaxResponseTokens;
|
|
98
|
+
const useIndex = config.semanticIndex.useForSearch && getIndexEntryCount() > 0;
|
|
99
|
+
let entriesPerChunk = 1;
|
|
100
|
+
if (useIndex) {
|
|
101
|
+
const indexedChunks = getIndexedChunkCount();
|
|
102
|
+
entriesPerChunk = indexedChunks > 0 ? getIndexEntryCount() / indexedChunks : 1;
|
|
103
|
+
}
|
|
104
|
+
console.log(`Search path: ${useIndex ? 'INDEX' : 'CHUNK'}`);
|
|
105
|
+
if (useIndex)
|
|
106
|
+
console.log(`Entries per chunk: ${entriesPerChunk.toFixed(1)}`);
|
|
107
|
+
console.log(`Max response tokens: ${maxTokens}`);
|
|
108
|
+
vectorStore.setModelId(config.embeddingModel);
|
|
109
|
+
if (useIndex)
|
|
110
|
+
indexVectorStore.setModelId(config.embeddingModel);
|
|
111
|
+
// 1. Sample and generate queries
|
|
112
|
+
console.log(`\nSampling ${sampleSize} chunks...`);
|
|
113
|
+
const sampledChunks = sampleChunks(sampleSize, seed);
|
|
114
|
+
console.log(` Sampled ${sampledChunks.length} chunks`);
|
|
115
|
+
console.log('Generating queries...');
|
|
116
|
+
const queries = await generateSearchQueries(sampledChunks, config.clusterRefreshModel);
|
|
117
|
+
console.log(` Generated ${queries.length} queries`);
|
|
118
|
+
// Get token sizes for budget analysis
|
|
119
|
+
const chunkTokenSizes = new Map();
|
|
120
|
+
for (const q of queries) {
|
|
121
|
+
const chunk = getChunkById(q.groundTruthChunkId);
|
|
122
|
+
if (chunk)
|
|
123
|
+
chunkTokenSizes.set(q.groundTruthChunkId, chunk.approxTokens || 500);
|
|
124
|
+
}
|
|
125
|
+
// 2. Prepare embedder
|
|
126
|
+
const embedder = new Embedder();
|
|
127
|
+
await embedder.load(getModel(config.embeddingModel));
|
|
128
|
+
console.log('Embedding queries...');
|
|
129
|
+
const queryEmbeddings = [];
|
|
130
|
+
for (const q of queries) {
|
|
131
|
+
const { embedding } = await embedder.embed(q.query, true);
|
|
132
|
+
queryEmbeddings.push(embedding);
|
|
133
|
+
}
|
|
134
|
+
console.log(` Embedded ${queryEmbeddings.length} queries\n`);
|
|
135
|
+
// 3. Sweep K values
|
|
136
|
+
const kValues = [50, 100, 200, 500, 1000, 2000];
|
|
137
|
+
console.log(`K values: ${kValues.join(', ')}\n`);
|
|
138
|
+
const resultsByK = new Map();
|
|
139
|
+
for (const k of kValues) {
|
|
140
|
+
const ranks = [];
|
|
141
|
+
let misses = 0;
|
|
142
|
+
for (let qi = 0; qi < queries.length; qi++) {
|
|
143
|
+
const q = queries[qi];
|
|
144
|
+
const embedding = queryEmbeddings[qi];
|
|
145
|
+
const result = useIndex
|
|
146
|
+
? await searchIndexPath(embedding, q.groundTruthChunkId, k, entriesPerChunk)
|
|
147
|
+
: await searchChunkPath(embedding, q.groundTruthChunkId, k);
|
|
148
|
+
if (result.found) {
|
|
149
|
+
ranks.push(result.rank);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
misses++;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
resultsByK.set(k, { ranks, misses });
|
|
156
|
+
const total = queries.length;
|
|
157
|
+
const found = ranks.length;
|
|
158
|
+
const recall = ((found / total) * 100).toFixed(1);
|
|
159
|
+
const sortedRanks = [...ranks].sort((a, b) => a - b);
|
|
160
|
+
const p50 = percentile(sortedRanks, 50);
|
|
161
|
+
const p90 = percentile(sortedRanks, 90);
|
|
162
|
+
const p99 = percentile(sortedRanks, 99);
|
|
163
|
+
console.log(`── K=${k} ──`);
|
|
164
|
+
console.log(` Recall: ${found}/${total} (${recall}%)`);
|
|
165
|
+
if (found > 0) {
|
|
166
|
+
console.log(` Rank distribution: median=${p50}, p90=${p90}, p99=${p99}, max=${sortedRanks[sortedRanks.length - 1]}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// 4. Budget ceiling analysis
|
|
170
|
+
console.log('\n══ Re-scorer Ceiling (perfect re-ranker) ══\n');
|
|
171
|
+
console.log('If a perfect re-scorer promoted every found target to the top of results,');
|
|
172
|
+
console.log(`how many would fit within the ${maxTokens}-token budget?\n`);
|
|
173
|
+
for (const k of kValues) {
|
|
174
|
+
const { ranks: _ranks, misses: _misses } = resultsByK.get(k);
|
|
175
|
+
const total = queries.length;
|
|
176
|
+
// Every found chunk would be promoted to position 1 by a perfect re-scorer
|
|
177
|
+
// so budget is the only constraint
|
|
178
|
+
let fitsInBudget = 0;
|
|
179
|
+
let tooLarge = 0;
|
|
180
|
+
for (let qi = 0; qi < queries.length; qi++) {
|
|
181
|
+
const targetId = queries[qi].groundTruthChunkId;
|
|
182
|
+
const result = useIndex
|
|
183
|
+
? await searchIndexPath(queryEmbeddings[qi], targetId, k, entriesPerChunk)
|
|
184
|
+
: await searchChunkPath(queryEmbeddings[qi], targetId, k);
|
|
185
|
+
if (!result.found)
|
|
186
|
+
continue;
|
|
187
|
+
const tokens = chunkTokenSizes.get(targetId) ?? 500;
|
|
188
|
+
if (tokens <= maxTokens) {
|
|
189
|
+
fitsInBudget++;
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
tooLarge++;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
const ceiling = ((fitsInBudget / total) * 100).toFixed(1);
|
|
196
|
+
const currentBest = resultsByK.get(k);
|
|
197
|
+
console.log(` K=${k}: ceiling=${ceiling}% (${fitsInBudget}/${total}) [recall=${((currentBest.ranks.length / total) * 100).toFixed(1)}%, oversized=${tooLarge}]`);
|
|
198
|
+
}
|
|
199
|
+
// 5. Rank bucket analysis — where do found targets cluster?
|
|
200
|
+
console.log('\n══ Rank Buckets (K=2000) ══\n');
|
|
201
|
+
const best = resultsByK.get(kValues[kValues.length - 1]);
|
|
202
|
+
if (best && best.ranks.length > 0) {
|
|
203
|
+
const buckets = [
|
|
204
|
+
{ label: '1-10', min: 1, max: 10 },
|
|
205
|
+
{ label: '11-50', min: 11, max: 50 },
|
|
206
|
+
{ label: '51-100', min: 51, max: 100 },
|
|
207
|
+
{ label: '101-200', min: 101, max: 200 },
|
|
208
|
+
{ label: '201-500', min: 201, max: 500 },
|
|
209
|
+
{ label: '501-1000', min: 501, max: 1000 },
|
|
210
|
+
{ label: '1001-2000', min: 1001, max: 2000 },
|
|
211
|
+
];
|
|
212
|
+
for (const { label, min, max } of buckets) {
|
|
213
|
+
const count = best.ranks.filter((r) => r >= min && r <= max).length;
|
|
214
|
+
const bar = '█'.repeat(Math.ceil(count / queries.length * 50));
|
|
215
|
+
console.log(` ${label.padStart(10)}: ${String(count).padStart(3)} ${bar}`);
|
|
216
|
+
}
|
|
217
|
+
console.log(` ${'miss'.padStart(10)}: ${String(best.misses).padStart(3)}`);
|
|
218
|
+
}
|
|
219
|
+
await embedder.dispose();
|
|
220
|
+
console.log('\nDone.');
|
|
221
|
+
}
|
|
222
|
+
runAnalysis().catch((err) => {
|
|
223
|
+
console.error('Analysis failed:', err);
|
|
224
|
+
process.exit(1);
|
|
225
|
+
});
|
|
226
|
+
//# sourceMappingURL=run-experiment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-experiment.js","sourceRoot":"","sources":["../../../../src/eval/experiments/rescorer-ceiling/run-experiment.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Index entry generation: hybrid jeopardy + summary, heuristic fallback.
|
|
3
|
+
*
|
|
4
|
+
* For each chunk, generates:
|
|
5
|
+
* - 3-5 Jeopardy-style search target queries (embed close to user queries)
|
|
6
|
+
* - 1 summary description (embed close to chunk content, safety net for recall)
|
|
7
|
+
*
|
|
8
|
+
* LLM generation batches chunks into Haiku calls. Each query/summary becomes
|
|
9
|
+
* a separate index entry. Heuristic fallback extracts the first meaningful
|
|
10
|
+
* lines when offline.
|
|
11
|
+
*/
|
|
12
|
+
import type { IndexEntryInput } from '../storage/types.js';
|
|
13
|
+
/** Chunk data needed for index entry generation. */
|
|
14
|
+
export interface ChunkForIndexing {
|
|
15
|
+
id: string;
|
|
16
|
+
sessionSlug: string;
|
|
17
|
+
startTime: string;
|
|
18
|
+
content: string;
|
|
19
|
+
approxTokens: number;
|
|
20
|
+
agentId?: string | null;
|
|
21
|
+
teamName?: string | null;
|
|
22
|
+
}
|
|
23
|
+
/** Options for LLM generation. */
|
|
24
|
+
export interface GenerateOptions {
|
|
25
|
+
/** Override model. Default: from config (clusterRefreshModel). */
|
|
26
|
+
model?: string;
|
|
27
|
+
/** Max tokens per chunk sent to LLM. Default: 500. */
|
|
28
|
+
maxChunkTokens?: number;
|
|
29
|
+
/** Target description length (unused for jeopardy, kept for API compat). */
|
|
30
|
+
targetDescriptionTokens?: number;
|
|
31
|
+
/** Called before each sub-batch API call for rate limiting. */
|
|
32
|
+
onBeforeBatch?: () => Promise<void>;
|
|
33
|
+
}
|
|
34
|
+
/** Parsed output for a single chunk from the LLM response. */
|
|
35
|
+
export interface ParsedChunkEntries {
|
|
36
|
+
/** Summary description (~20-30 words), null if missing. */
|
|
37
|
+
summary: string | null;
|
|
38
|
+
/** Jeopardy-style search queries (3-5 per chunk). */
|
|
39
|
+
queries: string[];
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Generate index entries for a batch of chunks using LLM.
|
|
43
|
+
*
|
|
44
|
+
* Produces 3-5 Jeopardy-style queries + 1 summary per chunk.
|
|
45
|
+
* Each query becomes a 'jeopardy' entry, the summary becomes an 'llm' entry.
|
|
46
|
+
* Falls back to heuristic if the API call fails.
|
|
47
|
+
*/
|
|
48
|
+
export declare function generateLLMEntries(chunks: ChunkForIndexing[], sessionSlug: string, options?: GenerateOptions): Promise<IndexEntryInput[]>;
|
|
49
|
+
/**
|
|
50
|
+
* Generate a heuristic index entry for a single chunk.
|
|
51
|
+
*
|
|
52
|
+
* Extracts the first meaningful content lines, trimmed to ~130 tokens.
|
|
53
|
+
* Used as offline fallback or when LLM is unavailable.
|
|
54
|
+
*/
|
|
55
|
+
export declare function generateHeuristicEntry(chunk: ChunkForIndexing, sessionSlug: string): IndexEntryInput;
|
|
56
|
+
/**
|
|
57
|
+
* Build the LLM prompt for hybrid jeopardy + summary generation.
|
|
58
|
+
*/
|
|
59
|
+
export declare function buildGenerationPrompt(chunks: Array<{
|
|
60
|
+
index: number;
|
|
61
|
+
content: string;
|
|
62
|
+
id: string;
|
|
63
|
+
}>): string;
|
|
64
|
+
/**
|
|
65
|
+
* Parse the LLM response into a map of chunk index → parsed entries.
|
|
66
|
+
*
|
|
67
|
+
* Handles various LLM output formats:
|
|
68
|
+
* - "0:" header then "- query" bullets + "SUMMARY:" line
|
|
69
|
+
* - "Chunk 0:" / "**Chunk 0:**" headers
|
|
70
|
+
* - "0: - query" inline format
|
|
71
|
+
* - "0: SKIP" for trivial chunks
|
|
72
|
+
*/
|
|
73
|
+
export declare function parseGenerationResponse(text: string, expectedCount: number): Map<number, ParsedChunkEntries>;
|
|
74
|
+
//# sourceMappingURL=index-generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-generator.d.ts","sourceRoot":"","sources":["../../src/index-entries/index-generator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAOH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAI3D,oDAAoD;AACpD,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,kCAAkC;AAClC,MAAM,WAAW,eAAe;IAC9B,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sDAAsD;IACtD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,4EAA4E;IAC5E,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,+DAA+D;IAC/D,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAED,8DAA8D;AAC9D,MAAM,WAAW,kBAAkB;IACjC,2DAA2D;IAC3D,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,qDAAqD;IACrD,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAQD;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,gBAAgB,EAAE,EAC1B,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC,eAAe,EAAE,CAAC,CA6F5B;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,gBAAgB,EACvB,WAAW,EAAE,MAAM,GAClB,eAAe,CAajB;AA6BD;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,GAC5D,MAAM,CAsCR;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAyE5G"}
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Index entry generation: hybrid jeopardy + summary, heuristic fallback.
|
|
3
|
+
*
|
|
4
|
+
* For each chunk, generates:
|
|
5
|
+
* - 3-5 Jeopardy-style search target queries (embed close to user queries)
|
|
6
|
+
* - 1 summary description (embed close to chunk content, safety net for recall)
|
|
7
|
+
*
|
|
8
|
+
* LLM generation batches chunks into Haiku calls. Each query/summary becomes
|
|
9
|
+
* a separate index entry. Heuristic fallback extracts the first meaningful
|
|
10
|
+
* lines when offline.
|
|
11
|
+
*/
|
|
12
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
13
|
+
import { getConfig } from '../config/memory-config.js';
|
|
14
|
+
import { approximateTokens } from '../utils/token-counter.js';
|
|
15
|
+
import { createSecretStore } from '../utils/secret-store.js';
|
|
16
|
+
import { createLogger } from '../utils/logger.js';
|
|
17
|
+
const log = createLogger('index-generator');
|
|
18
|
+
/**
|
|
19
|
+
* Maximum chunks per LLM batch. With 3-5 queries + 1 summary per chunk
|
|
20
|
+
* (~400 tokens output each), 8 chunks fits within Haiku's 4096 output cap.
|
|
21
|
+
*/
|
|
22
|
+
const MAX_CHUNKS_PER_BATCH = 8;
|
|
23
|
+
/**
|
|
24
|
+
* Generate index entries for a batch of chunks using LLM.
|
|
25
|
+
*
|
|
26
|
+
* Produces 3-5 Jeopardy-style queries + 1 summary per chunk.
|
|
27
|
+
* Each query becomes a 'jeopardy' entry, the summary becomes an 'llm' entry.
|
|
28
|
+
* Falls back to heuristic if the API call fails.
|
|
29
|
+
*/
|
|
30
|
+
export async function generateLLMEntries(chunks, sessionSlug, options) {
|
|
31
|
+
if (chunks.length === 0)
|
|
32
|
+
return [];
|
|
33
|
+
const config = getConfig();
|
|
34
|
+
const model = options?.model ?? config.clusterRefreshModel;
|
|
35
|
+
const maxChunkTokens = options?.maxChunkTokens ?? 500;
|
|
36
|
+
// Get Anthropic client
|
|
37
|
+
const client = await getAnthropicClient();
|
|
38
|
+
if (!client) {
|
|
39
|
+
log.info('No API key available, falling back to heuristic generation');
|
|
40
|
+
return chunks.map((chunk) => generateHeuristicEntry(chunk, sessionSlug));
|
|
41
|
+
}
|
|
42
|
+
// Split into sub-batches that fit within Haiku's output limits
|
|
43
|
+
const allResults = [];
|
|
44
|
+
for (let batchStart = 0; batchStart < chunks.length; batchStart += MAX_CHUNKS_PER_BATCH) {
|
|
45
|
+
const batchChunks = chunks.slice(batchStart, batchStart + MAX_CHUNKS_PER_BATCH);
|
|
46
|
+
// Rate limit before each sub-batch API call
|
|
47
|
+
if (options?.onBeforeBatch) {
|
|
48
|
+
await options.onBeforeBatch();
|
|
49
|
+
}
|
|
50
|
+
// Truncate chunk content for prompt
|
|
51
|
+
const truncatedChunks = batchChunks.map((chunk, i) => {
|
|
52
|
+
const maxChars = maxChunkTokens * 4;
|
|
53
|
+
const content = chunk.content.length > maxChars
|
|
54
|
+
? chunk.content.slice(0, maxChars) + '\n...[truncated]'
|
|
55
|
+
: chunk.content;
|
|
56
|
+
return { index: i, content, id: chunk.id };
|
|
57
|
+
});
|
|
58
|
+
const prompt = buildGenerationPrompt(truncatedChunks);
|
|
59
|
+
try {
|
|
60
|
+
const response = await callWithRetry(client, model, prompt, batchChunks.length);
|
|
61
|
+
const text = response.content[0].type === 'text' ? response.content[0].text : '';
|
|
62
|
+
const entriesByChunk = parseGenerationResponse(text, batchChunks.length);
|
|
63
|
+
for (let i = 0; i < batchChunks.length; i++) {
|
|
64
|
+
const chunk = batchChunks[i];
|
|
65
|
+
const parsed = entriesByChunk.get(i);
|
|
66
|
+
if (parsed && (parsed.queries.length > 0 || parsed.summary)) {
|
|
67
|
+
// Create one entry per jeopardy query
|
|
68
|
+
for (const query of parsed.queries) {
|
|
69
|
+
allResults.push({
|
|
70
|
+
chunkIds: [chunk.id],
|
|
71
|
+
sessionSlug,
|
|
72
|
+
startTime: chunk.startTime,
|
|
73
|
+
description: query,
|
|
74
|
+
approxTokens: approximateTokens(query),
|
|
75
|
+
agentId: chunk.agentId,
|
|
76
|
+
teamName: chunk.teamName,
|
|
77
|
+
generationMethod: 'jeopardy',
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
// Create one entry for the summary
|
|
81
|
+
if (parsed.summary) {
|
|
82
|
+
allResults.push({
|
|
83
|
+
chunkIds: [chunk.id],
|
|
84
|
+
sessionSlug,
|
|
85
|
+
startTime: chunk.startTime,
|
|
86
|
+
description: parsed.summary,
|
|
87
|
+
approxTokens: approximateTokens(parsed.summary),
|
|
88
|
+
agentId: chunk.agentId,
|
|
89
|
+
teamName: chunk.teamName,
|
|
90
|
+
generationMethod: 'llm',
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
// SKIP or missing — fall back to heuristic
|
|
96
|
+
allResults.push(generateHeuristicEntry(chunk, sessionSlug));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
log.warn('LLM batch failed, falling back to heuristic for batch', {
|
|
102
|
+
error: error.message,
|
|
103
|
+
batchStart,
|
|
104
|
+
batchSize: batchChunks.length,
|
|
105
|
+
});
|
|
106
|
+
for (const chunk of batchChunks) {
|
|
107
|
+
allResults.push(generateHeuristicEntry(chunk, sessionSlug));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return allResults;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Generate a heuristic index entry for a single chunk.
|
|
115
|
+
*
|
|
116
|
+
* Extracts the first meaningful content lines, trimmed to ~130 tokens.
|
|
117
|
+
* Used as offline fallback or when LLM is unavailable.
|
|
118
|
+
*/
|
|
119
|
+
export function generateHeuristicEntry(chunk, sessionSlug) {
|
|
120
|
+
const description = generateHeuristicDescription(chunk.content);
|
|
121
|
+
return {
|
|
122
|
+
chunkIds: [chunk.id],
|
|
123
|
+
sessionSlug,
|
|
124
|
+
startTime: chunk.startTime,
|
|
125
|
+
description,
|
|
126
|
+
approxTokens: approximateTokens(description),
|
|
127
|
+
agentId: chunk.agentId,
|
|
128
|
+
teamName: chunk.teamName,
|
|
129
|
+
generationMethod: 'heuristic',
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Generate a heuristic description from chunk content.
|
|
134
|
+
* Takes the first meaningful lines up to ~130 tokens.
|
|
135
|
+
*/
|
|
136
|
+
function generateHeuristicDescription(content) {
|
|
137
|
+
const lines = content.split('\n');
|
|
138
|
+
const meaningful = [];
|
|
139
|
+
let tokenCount = 0;
|
|
140
|
+
const targetTokens = 130;
|
|
141
|
+
for (const line of lines) {
|
|
142
|
+
const trimmed = line.trim();
|
|
143
|
+
// Skip empty lines and pure separators
|
|
144
|
+
if (!trimmed || /^[-=_*]{3,}$/.test(trimmed))
|
|
145
|
+
continue;
|
|
146
|
+
// Skip tool use blocks and code fence markers
|
|
147
|
+
if (trimmed.startsWith('```') || trimmed.startsWith('Tool:'))
|
|
148
|
+
continue;
|
|
149
|
+
const lineTokens = approximateTokens(trimmed);
|
|
150
|
+
if (tokenCount + lineTokens > targetTokens && meaningful.length > 0)
|
|
151
|
+
break;
|
|
152
|
+
meaningful.push(trimmed);
|
|
153
|
+
tokenCount += lineTokens;
|
|
154
|
+
}
|
|
155
|
+
return meaningful.join(' ').slice(0, targetTokens * 5); // ~5 chars/token safety cap
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Build the LLM prompt for hybrid jeopardy + summary generation.
|
|
159
|
+
*/
|
|
160
|
+
export function buildGenerationPrompt(chunks) {
|
|
161
|
+
const chunkTexts = chunks
|
|
162
|
+
.map((c) => `--- Chunk ${c.index} ---\n${c.content}`)
|
|
163
|
+
.join('\n\n');
|
|
164
|
+
return `You are building a search index for a memory system used by AI agents (Claude Opus, Sonnet) and human developers. For each conversation chunk below, produce:
|
|
165
|
+
|
|
166
|
+
1. **3-5 search queries** — Jeopardy-style: the chunk is the "answer", what are the "questions" a user would type to find it?
|
|
167
|
+
2. **1 summary** — a concise description of what the chunk covers (~20-30 words)
|
|
168
|
+
|
|
169
|
+
Rules for queries:
|
|
170
|
+
- Each query should be a natural question or search phrase (5-20 words)
|
|
171
|
+
- Queries must be SPECIFIC enough to uniquely target THIS chunk, not the general topic
|
|
172
|
+
- Focus on the concrete outcome, decision, error, or technique — not the topic category
|
|
173
|
+
- Include specific names: file paths, function names, error messages, library names where relevant
|
|
174
|
+
- Do NOT include dates, project names, or agent IDs
|
|
175
|
+
- Do NOT copy text verbatim from the chunk
|
|
176
|
+
|
|
177
|
+
Rules for summaries:
|
|
178
|
+
- Capture the key topic, action, or decision in ~20-30 words
|
|
179
|
+
- Include specific technical terms that someone might search for
|
|
180
|
+
- Do NOT include dates, project names, or agent IDs
|
|
181
|
+
|
|
182
|
+
SKIP rules: ONLY write "SKIP" if the chunk is entirely greetings, acknowledgements, or whitespace with absolutely no technical or topical content. If in doubt, do NOT skip.
|
|
183
|
+
|
|
184
|
+
${chunkTexts}
|
|
185
|
+
|
|
186
|
+
Respond with entries grouped by chunk number:
|
|
187
|
+
0:
|
|
188
|
+
- [query 1]
|
|
189
|
+
- [query 2]
|
|
190
|
+
- [query 3]
|
|
191
|
+
SUMMARY: [one-sentence summary]
|
|
192
|
+
1:
|
|
193
|
+
- [query 1]
|
|
194
|
+
- [query 2]
|
|
195
|
+
SUMMARY: [summary]
|
|
196
|
+
...`;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Parse the LLM response into a map of chunk index → parsed entries.
|
|
200
|
+
*
|
|
201
|
+
* Handles various LLM output formats:
|
|
202
|
+
* - "0:" header then "- query" bullets + "SUMMARY:" line
|
|
203
|
+
* - "Chunk 0:" / "**Chunk 0:**" headers
|
|
204
|
+
* - "0: - query" inline format
|
|
205
|
+
* - "0: SKIP" for trivial chunks
|
|
206
|
+
*/
|
|
207
|
+
export function parseGenerationResponse(text, expectedCount) {
|
|
208
|
+
const results = new Map();
|
|
209
|
+
const lines = text.split('\n');
|
|
210
|
+
let currentIndex = -1;
|
|
211
|
+
const ensureEntry = (idx) => {
|
|
212
|
+
if (!results.has(idx)) {
|
|
213
|
+
results.set(idx, { summary: null, queries: [] });
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
for (const line of lines) {
|
|
217
|
+
// Match "0:", "Chunk 0:", "**Chunk 0:**", "## Chunk 0", etc. (header-only line)
|
|
218
|
+
const indexMatch = line.match(/^(?:\*{0,2})?(?:#{0,3}\s*)?(?:Chunk\s+)?(\d+)(?:\*{0,2})?:(?:\*{0,2})?\s*$/i);
|
|
219
|
+
if (indexMatch) {
|
|
220
|
+
const idx = parseInt(indexMatch[1], 10);
|
|
221
|
+
if (idx >= 0 && idx < expectedCount) {
|
|
222
|
+
currentIndex = idx;
|
|
223
|
+
ensureEntry(currentIndex);
|
|
224
|
+
}
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
// Handle "0: SKIP" or "Chunk 0: SKIP"
|
|
228
|
+
const skipMatch = line.match(/^(?:\*{0,2})?(?:Chunk\s+)?(\d+)(?:\*{0,2})?:\s*SKIP\s*$/i);
|
|
229
|
+
if (skipMatch) {
|
|
230
|
+
const idx = parseInt(skipMatch[1], 10);
|
|
231
|
+
if (idx >= 0 && idx < expectedCount) {
|
|
232
|
+
currentIndex = idx;
|
|
233
|
+
// Don't add to results — SKIP means no entries
|
|
234
|
+
}
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
// Handle "0: - query" or "Chunk 0: - query" on same line
|
|
238
|
+
const inlineMatch = line.match(/^(?:\*{0,2})?(?:Chunk\s+)?(\d+)(?:\*{0,2})?:\s*[-•]\s*(.+)/i);
|
|
239
|
+
if (inlineMatch) {
|
|
240
|
+
const idx = parseInt(inlineMatch[1], 10);
|
|
241
|
+
if (idx >= 0 && idx < expectedCount) {
|
|
242
|
+
currentIndex = idx;
|
|
243
|
+
ensureEntry(currentIndex);
|
|
244
|
+
const query = inlineMatch[2].trim();
|
|
245
|
+
if (query) {
|
|
246
|
+
results.get(currentIndex).queries.push(query);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
// Handle SUMMARY: line (with or without ** bold)
|
|
252
|
+
const summaryMatch = line.match(/^\s*(?:\*{0,2})?SUMMARY(?:\*{0,2})?:(?:\*{0,2})?\s*(.+)/i);
|
|
253
|
+
if (summaryMatch && currentIndex >= 0 && currentIndex < expectedCount) {
|
|
254
|
+
const summary = summaryMatch[1].trim();
|
|
255
|
+
if (summary && summary.toUpperCase() !== 'SKIP') {
|
|
256
|
+
ensureEntry(currentIndex);
|
|
257
|
+
results.get(currentIndex).summary = summary;
|
|
258
|
+
}
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
// Bullet point under current chunk
|
|
262
|
+
const queryMatch = line.match(/^\s*[-•]\s*(.+)/);
|
|
263
|
+
if (queryMatch && currentIndex >= 0 && currentIndex < expectedCount) {
|
|
264
|
+
const query = queryMatch[1].trim();
|
|
265
|
+
if (query && query.toUpperCase() !== 'SKIP') {
|
|
266
|
+
ensureEntry(currentIndex);
|
|
267
|
+
results.get(currentIndex).queries.push(query);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return results;
|
|
272
|
+
}
|
|
273
|
+
/** Max retries for rate-limited or transient API errors. */
|
|
274
|
+
const MAX_RETRIES = 3;
|
|
275
|
+
/**
|
|
276
|
+
* Call the API with exponential backoff on rate limit (429) and server errors (5xx).
|
|
277
|
+
*/
|
|
278
|
+
async function callWithRetry(client, model, prompt, batchSize) {
|
|
279
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
280
|
+
try {
|
|
281
|
+
return await client.messages.create({
|
|
282
|
+
model,
|
|
283
|
+
max_tokens: Math.min(4096, Math.max(400, batchSize * 400)),
|
|
284
|
+
messages: [{ role: 'user', content: prompt }],
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
catch (error) {
|
|
288
|
+
const status = error.status;
|
|
289
|
+
const isRetryable = status === 429 || (status !== undefined && status >= 500);
|
|
290
|
+
if (!isRetryable || attempt === MAX_RETRIES) {
|
|
291
|
+
throw error;
|
|
292
|
+
}
|
|
293
|
+
// Exponential backoff: 2s, 8s, 32s
|
|
294
|
+
const backoffMs = 2000 * Math.pow(4, attempt);
|
|
295
|
+
log.info(`API ${status} on attempt ${attempt + 1}, retrying in ${backoffMs / 1000}s`);
|
|
296
|
+
await new Promise((resolve) => setTimeout(resolve, backoffMs));
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
// Unreachable, but TypeScript needs it
|
|
300
|
+
throw new Error('Retry loop exited unexpectedly');
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Get Anthropic client, returning null if no API key is available.
|
|
304
|
+
*/
|
|
305
|
+
async function getAnthropicClient() {
|
|
306
|
+
if (!process.env.ANTHROPIC_API_KEY) {
|
|
307
|
+
try {
|
|
308
|
+
const store = createSecretStore();
|
|
309
|
+
const storedKey = await store.get('anthropic-api-key');
|
|
310
|
+
if (storedKey) {
|
|
311
|
+
process.env.ANTHROPIC_API_KEY = storedKey;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
catch {
|
|
315
|
+
// Keychain not available
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
if (!process.env.ANTHROPIC_API_KEY) {
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
return new Anthropic();
|
|
322
|
+
}
|
|
323
|
+
//# sourceMappingURL=index-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-generator.js","sourceRoot":"","sources":["../../src/index-entries/index-generator.ts"],"names":[],"mappings":"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"}
|