@pentatonic-ai/ai-agent-sdk 0.5.11 → 0.7.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 +345 -174
- package/bin/__tests__/callback-server.test.js +70 -0
- package/bin/__tests__/credentials.test.js +58 -0
- package/bin/__tests__/login.test.js +210 -0
- package/bin/__tests__/pkce.test.js +39 -0
- package/bin/__tests__/whoami.test.js +77 -0
- package/bin/cli.js +109 -440
- package/bin/commands/config.js +251 -0
- package/bin/commands/login.js +219 -0
- package/bin/commands/whoami.js +41 -0
- package/bin/lib/callback-server.js +137 -0
- package/bin/lib/credentials.js +100 -0
- package/bin/lib/pkce.js +26 -0
- package/package.json +4 -2
- package/packages/doctor/__tests__/detect.test.js +2 -6
- package/packages/doctor/src/checks/local-memory.js +164 -196
- package/packages/doctor/src/detect.js +11 -3
- package/packages/memory/src/__tests__/corpus-chunkers.test.js +143 -0
- package/packages/memory/src/__tests__/corpus-discover.test.js +175 -0
- package/packages/memory/src/__tests__/corpus-ingest.test.js +236 -0
- package/packages/memory/src/__tests__/corpus-signatures.test.js +175 -0
- package/packages/memory/src/__tests__/corpus-state.test.js +161 -0
- package/packages/memory/src/__tests__/ingest-corpus-opts.test.js +129 -0
- package/packages/memory/src/__tests__/search-kind.test.js +108 -0
- package/packages/memory/src/corpus/adapters.js +398 -0
- package/packages/memory/src/corpus/chunkers.js +328 -0
- package/packages/memory/src/corpus/cli.js +613 -0
- package/packages/memory/src/corpus/discover.js +379 -0
- package/packages/memory/src/corpus/index.js +68 -0
- package/packages/memory/src/corpus/ingest.js +356 -0
- package/packages/memory/src/corpus/signatures.js +280 -0
- package/packages/memory/src/corpus/state.js +134 -0
- package/packages/memory/src/index.js +18 -0
- package/packages/memory/src/ingest.js +20 -11
- package/packages/memory/src/openclaw/index.js +39 -1
- package/packages/memory/src/search.js +30 -7
- package/packages/memory-engine/.env.example +13 -0
- package/packages/memory-engine/README.md +131 -0
- package/packages/memory-engine/bench/README.md +99 -0
- package/packages/memory-engine/bench/scorecards-engine/agent-coding__pentatonic-baseline__20260427-142523.json +1115 -0
- package/packages/memory-engine/bench/scorecards-engine/chat-recall__pentatonic-baseline__20260427-142648.json +819 -0
- package/packages/memory-engine/bench/scorecards-engine/circular-economy__pentatonic-baseline__20260427-142757.json +1278 -0
- package/packages/memory-engine/bench/scorecards-engine/customer-support__pentatonic-baseline__20260427-142900.json +1018 -0
- package/packages/memory-engine/bench/scorecards-engine/marketplace-ops__pentatonic-baseline__20260427-142957.json +1038 -0
- package/packages/memory-engine/bench/scorecards-engine/product-catalogue__pentatonic-baseline__20260427-143122.json +961 -0
- package/packages/memory-engine/bench/scorecards-engine-via-docker/agent-coding__pentatonic-memory__20260427-161812.json +1115 -0
- package/packages/memory-engine/bench/scorecards-engine-via-docker/chat-recall__pentatonic-memory__20260427-161701.json +819 -0
- package/packages/memory-engine/bench/scorecards-engine-via-docker/circular-economy__pentatonic-memory__20260427-161713.json +1278 -0
- package/packages/memory-engine/bench/scorecards-engine-via-docker/customer-support__pentatonic-memory__20260427-161723.json +1018 -0
- package/packages/memory-engine/bench/scorecards-engine-via-docker/marketplace-ops__pentatonic-memory__20260427-161732.json +1038 -0
- package/packages/memory-engine/bench/scorecards-engine-via-docker/product-catalogue__pentatonic-memory__20260427-161741.json +937 -0
- package/packages/memory-engine/bench/scorecards-engine-via-l2-7-layer-populated/agent-coding__pentatonic-memory__20260427-184718.json +1115 -0
- package/packages/memory-engine/bench/scorecards-engine-via-l2-7-layer-populated/chat-recall__pentatonic-memory__20260427-184614.json +819 -0
- package/packages/memory-engine/bench/scorecards-engine-via-l2-7-layer-populated/circular-economy__pentatonic-memory__20260427-184809.json +1278 -0
- package/packages/memory-engine/bench/scorecards-engine-via-l2-7-layer-populated/customer-support__pentatonic-memory__20260427-184854.json +1018 -0
- package/packages/memory-engine/bench/scorecards-engine-via-l2-7-layer-populated/marketplace-ops__pentatonic-memory__20260427-184929.json +1038 -0
- package/packages/memory-engine/bench/scorecards-engine-via-l2-7-layer-populated/product-catalogue__pentatonic-memory__20260427-185015.json +961 -0
- package/packages/memory-engine/bench/scorecards-engine-via-l2-empty-layers/agent-coding__pentatonic-memory__20260427-175252.json +1115 -0
- package/packages/memory-engine/bench/scorecards-engine-via-l2-empty-layers/chat-recall__pentatonic-memory__20260427-175312.json +819 -0
- package/packages/memory-engine/bench/scorecards-engine-via-l2-empty-layers/circular-economy__pentatonic-memory__20260427-175335.json +1278 -0
- package/packages/memory-engine/bench/scorecards-engine-via-l2-empty-layers/customer-support__pentatonic-memory__20260427-175355.json +1018 -0
- package/packages/memory-engine/bench/scorecards-engine-via-l2-empty-layers/marketplace-ops__pentatonic-memory__20260427-175413.json +1038 -0
- package/packages/memory-engine/bench/scorecards-engine-via-l2-empty-layers/product-catalogue__pentatonic-memory__20260427-175430.json +883 -0
- package/packages/memory-engine/bench/scorecards-engine-via-shim/agent-coding__pentatonic-memory__20260427-155409.json +1115 -0
- package/packages/memory-engine/bench/scorecards-engine-via-shim/chat-recall__pentatonic-memory__20260427-155421.json +819 -0
- package/packages/memory-engine/bench/scorecards-engine-via-shim/circular-economy__pentatonic-memory__20260427-155433.json +1278 -0
- package/packages/memory-engine/bench/scorecards-engine-via-shim/customer-support__pentatonic-memory__20260427-155443.json +1018 -0
- package/packages/memory-engine/bench/scorecards-engine-via-shim/marketplace-ops__pentatonic-memory__20260427-155453.json +1038 -0
- package/packages/memory-engine/bench/scorecards-engine-via-shim/product-catalogue__pentatonic-memory__20260427-155503.json +937 -0
- package/packages/memory-engine/bench/scorecards-pentatonic-baseline/agent-coding__pentatonic-memory-latest__20260427-145103.json +1115 -0
- package/packages/memory-engine/bench/scorecards-pentatonic-baseline/agent-coding__pentatonic-memory__20260427-144909.json +1115 -0
- package/packages/memory-engine/bench/scorecards-pentatonic-baseline/chat-recall__pentatonic-memory-latest__20260427-145153.json +819 -0
- package/packages/memory-engine/bench/scorecards-pentatonic-baseline/chat-recall__pentatonic-memory__20260427-145120.json +542 -0
- package/packages/memory-engine/bench/scorecards-pentatonic-baseline/circular-economy__pentatonic-memory-latest__20260427-145313.json +1278 -0
- package/packages/memory-engine/bench/scorecards-pentatonic-baseline/circular-economy__pentatonic-memory__20260427-145207.json +894 -0
- package/packages/memory-engine/bench/scorecards-pentatonic-baseline/customer-support__pentatonic-memory-latest__20260427-145412.json +1018 -0
- package/packages/memory-engine/bench/scorecards-pentatonic-baseline/customer-support__pentatonic-memory__20260427-145327.json +680 -0
- package/packages/memory-engine/bench/scorecards-pentatonic-baseline/marketplace-ops__pentatonic-memory-latest__20260427-145517.json +1038 -0
- package/packages/memory-engine/bench/scorecards-pentatonic-baseline/marketplace-ops__pentatonic-memory__20260427-145422.json +693 -0
- package/packages/memory-engine/bench/scorecards-pentatonic-baseline/product-catalogue__pentatonic-memory-latest__20260427-145616.json +961 -0
- package/packages/memory-engine/bench/scorecards-pentatonic-baseline/product-catalogue__pentatonic-memory__20260427-145528.json +727 -0
- package/packages/memory-engine/compat/Dockerfile +11 -0
- package/packages/memory-engine/compat/server.py +680 -0
- package/packages/memory-engine/docker-compose.yml +243 -0
- package/packages/memory-engine/docs/MIGRATION.md +178 -0
- package/packages/memory-engine/docs/RUNBOOK-AWS.md +375 -0
- package/packages/memory-engine/docs/why-v05-underperforms.md +138 -0
- package/packages/memory-engine/engine/README.md +52 -0
- package/packages/memory-engine/engine/l2-hybridrag-proxy.py +1543 -0
- package/packages/memory-engine/engine/l5-comms-layer.py +663 -0
- package/packages/memory-engine/engine/l6-document-store.py +1018 -0
- package/packages/memory-engine/engine/services/l2/Dockerfile +41 -0
- package/packages/memory-engine/engine/services/l2/init_databases.py +81 -0
- package/packages/memory-engine/engine/services/l2/l2-hybridrag-proxy.py +1543 -0
- package/packages/memory-engine/engine/services/l4/Dockerfile +15 -0
- package/packages/memory-engine/engine/services/l4/server.py +235 -0
- package/packages/memory-engine/engine/services/l5/Dockerfile +9 -0
- package/packages/memory-engine/engine/services/l5/l5-comms-layer.py +678 -0
- package/packages/memory-engine/engine/services/l6/Dockerfile +11 -0
- package/packages/memory-engine/engine/services/l6/l6-document-store.py +1016 -0
- package/packages/memory-engine/engine/services/nv-embed/Dockerfile +28 -0
- package/packages/memory-engine/engine/services/nv-embed/server.py +152 -0
- package/packages/memory-engine/pme_memory/__init__.py +0 -0
- package/packages/memory-engine/pme_memory/__main__.py +129 -0
- package/packages/memory-engine/pme_memory/artifacts.py +95 -0
- package/packages/memory-engine/pme_memory/embed.py +74 -0
- package/packages/memory-engine/pme_memory/health.py +36 -0
- package/packages/memory-engine/pme_memory/hygiene.py +159 -0
- package/packages/memory-engine/pme_memory/indexer.py +200 -0
- package/packages/memory-engine/pme_memory/needs.py +55 -0
- package/packages/memory-engine/pme_memory/provenance.py +80 -0
- package/packages/memory-engine/pme_memory/scoring.py +168 -0
- package/packages/memory-engine/pme_memory/search.py +52 -0
- package/packages/memory-engine/pme_memory/store.py +86 -0
- package/packages/memory-engine/pme_memory/synthesis.py +114 -0
- package/packages/memory-engine/pyproject.toml +65 -0
- package/packages/memory-engine/scripts/kg-extractor.py +557 -0
- package/packages/memory-engine/scripts/kg-preflexor-v2.py +738 -0
- package/packages/memory-engine/tests/test_api_contract.sh +57 -0
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File chunkers for corpus ingest.
|
|
3
|
+
*
|
|
4
|
+
* Each chunker takes a file (path + content + metadata) and returns
|
|
5
|
+
* an array of Chunk objects ready for embedding + memory ingest.
|
|
6
|
+
*
|
|
7
|
+
* { content: string, metadata: { kind, name?, lineRange?, ... } }
|
|
8
|
+
*
|
|
9
|
+
* Strategy by file type:
|
|
10
|
+
* - .md/.mdx/.rst/.txt → heading-aware split with overlap
|
|
11
|
+
* - code (.ts/.js/.py/.go/.rs/.java/.rb/...) → sliding window with
|
|
12
|
+
* line-stable boundaries (split on blank lines, not mid-statement)
|
|
13
|
+
* - .json/.yaml/.yml/.toml → whole file as one chunk if small,
|
|
14
|
+
* otherwise top-level key split
|
|
15
|
+
* - everything else → sliding window
|
|
16
|
+
*
|
|
17
|
+
* Tree-sitter integration for proper AST-aware code chunking is a
|
|
18
|
+
* follow-up (see specs/01 §10). Sliding window with blank-line snapping
|
|
19
|
+
* gives 80% of the value at 5% of the dependency cost.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const DEFAULT_CHUNK_TOKEN_TARGET = 800;
|
|
23
|
+
const DEFAULT_CHUNK_OVERLAP_TOKENS = 150;
|
|
24
|
+
const TOKEN_PER_CHAR_HEURISTIC = 0.25; // 4 chars ≈ 1 token
|
|
25
|
+
|
|
26
|
+
const CODE_EXTENSIONS = new Set([
|
|
27
|
+
".js", ".jsx", ".mjs", ".cjs",
|
|
28
|
+
".ts", ".tsx",
|
|
29
|
+
".py", ".pyi",
|
|
30
|
+
".go",
|
|
31
|
+
".rs",
|
|
32
|
+
".java", ".kt", ".kts", ".scala",
|
|
33
|
+
".rb",
|
|
34
|
+
".php",
|
|
35
|
+
".c", ".h", ".cpp", ".hpp", ".cc",
|
|
36
|
+
".cs",
|
|
37
|
+
".swift",
|
|
38
|
+
".sh", ".bash", ".zsh",
|
|
39
|
+
".sql",
|
|
40
|
+
".lua",
|
|
41
|
+
".pl", ".pm",
|
|
42
|
+
".r",
|
|
43
|
+
".erl", ".ex", ".exs",
|
|
44
|
+
".clj", ".cljs",
|
|
45
|
+
".dart",
|
|
46
|
+
".groovy",
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
const PROSE_EXTENSIONS = new Set([
|
|
50
|
+
".md", ".mdx", ".markdown",
|
|
51
|
+
".rst",
|
|
52
|
+
".txt",
|
|
53
|
+
".adoc",
|
|
54
|
+
".org",
|
|
55
|
+
]);
|
|
56
|
+
|
|
57
|
+
const STRUCTURED_EXTENSIONS = new Set([
|
|
58
|
+
".json",
|
|
59
|
+
".yaml", ".yml",
|
|
60
|
+
".toml",
|
|
61
|
+
".ini",
|
|
62
|
+
".env.example", // not real .env (which is hard-excluded)
|
|
63
|
+
]);
|
|
64
|
+
|
|
65
|
+
function approxTokens(text) {
|
|
66
|
+
return Math.ceil(text.length * TOKEN_PER_CHAR_HEURISTIC);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function tokensToChars(tokens) {
|
|
70
|
+
return Math.ceil(tokens / TOKEN_PER_CHAR_HEURISTIC);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Chunk a file into ingest-ready pieces.
|
|
75
|
+
*
|
|
76
|
+
* @param {object} file - { relPath, content, ext, basename }
|
|
77
|
+
* @param {object} [opts]
|
|
78
|
+
* @param {number} [opts.chunkTokens=800] - Target chunk size in tokens
|
|
79
|
+
* @param {number} [opts.overlapTokens=150] - Overlap between chunks
|
|
80
|
+
* @returns {Array<{content: string, metadata: object}>}
|
|
81
|
+
*/
|
|
82
|
+
export function chunkFile(file, opts = {}) {
|
|
83
|
+
const { ext = "", content = "", relPath = "" } = file;
|
|
84
|
+
if (!content.trim()) return [];
|
|
85
|
+
|
|
86
|
+
const chunkTokens = opts.chunkTokens || DEFAULT_CHUNK_TOKEN_TARGET;
|
|
87
|
+
const overlap = opts.overlapTokens ?? DEFAULT_CHUNK_OVERLAP_TOKENS;
|
|
88
|
+
|
|
89
|
+
// Tiny files: one chunk, no splitting
|
|
90
|
+
if (approxTokens(content) <= chunkTokens) {
|
|
91
|
+
return [{
|
|
92
|
+
content,
|
|
93
|
+
metadata: {
|
|
94
|
+
kind: classifyKind(ext),
|
|
95
|
+
chunk_index: 0,
|
|
96
|
+
total_chunks: 1,
|
|
97
|
+
},
|
|
98
|
+
}];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (PROSE_EXTENSIONS.has(ext)) {
|
|
102
|
+
return chunkMarkdown(content, chunkTokens, overlap);
|
|
103
|
+
}
|
|
104
|
+
if (STRUCTURED_EXTENSIONS.has(ext)) {
|
|
105
|
+
return chunkStructured(content, ext, chunkTokens);
|
|
106
|
+
}
|
|
107
|
+
if (CODE_EXTENSIONS.has(ext)) {
|
|
108
|
+
return chunkCode(content, chunkTokens, overlap, ext);
|
|
109
|
+
}
|
|
110
|
+
return chunkSlidingWindow(content, chunkTokens, overlap, "text");
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function classifyKind(ext) {
|
|
114
|
+
if (PROSE_EXTENSIONS.has(ext)) return "prose";
|
|
115
|
+
if (CODE_EXTENSIONS.has(ext)) return "code";
|
|
116
|
+
if (STRUCTURED_EXTENSIONS.has(ext)) return "config";
|
|
117
|
+
return "text";
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Markdown chunker — splits on h1/h2/h3 headings, keeping each section
|
|
122
|
+
* intact when it fits, otherwise sliding-window inside the section.
|
|
123
|
+
* Each chunk carries its heading path in metadata so retrieval can
|
|
124
|
+
* surface "from README.md > Installation > Local setup".
|
|
125
|
+
*/
|
|
126
|
+
function chunkMarkdown(content, chunkTokens, overlap) {
|
|
127
|
+
const lines = content.split("\n");
|
|
128
|
+
const chunks = [];
|
|
129
|
+
const headingStack = []; // [{level, text}]
|
|
130
|
+
let buffer = [];
|
|
131
|
+
let bufferStartLine = 0;
|
|
132
|
+
|
|
133
|
+
function flush() {
|
|
134
|
+
if (!buffer.length) return;
|
|
135
|
+
const text = buffer.join("\n").trim();
|
|
136
|
+
if (!text) {
|
|
137
|
+
buffer = [];
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (approxTokens(text) > chunkTokens) {
|
|
141
|
+
// Section too big — fall back to sliding window inside it
|
|
142
|
+
const sub = chunkSlidingWindow(text, chunkTokens, overlap, "prose");
|
|
143
|
+
for (const c of sub) {
|
|
144
|
+
chunks.push({
|
|
145
|
+
content: c.content,
|
|
146
|
+
metadata: {
|
|
147
|
+
kind: "prose",
|
|
148
|
+
heading_path: headingPath(),
|
|
149
|
+
line_start: bufferStartLine + 1,
|
|
150
|
+
chunk_index: chunks.length,
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
} else {
|
|
155
|
+
chunks.push({
|
|
156
|
+
content: text,
|
|
157
|
+
metadata: {
|
|
158
|
+
kind: "prose",
|
|
159
|
+
heading_path: headingPath(),
|
|
160
|
+
line_start: bufferStartLine + 1,
|
|
161
|
+
line_end: bufferStartLine + buffer.length,
|
|
162
|
+
chunk_index: chunks.length,
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
buffer = [];
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function headingPath() {
|
|
170
|
+
return headingStack.map((h) => h.text).join(" > ");
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
for (let i = 0; i < lines.length; i++) {
|
|
174
|
+
const line = lines[i];
|
|
175
|
+
const m = line.match(/^(#{1,6})\s+(.+?)\s*$/);
|
|
176
|
+
if (m) {
|
|
177
|
+
flush();
|
|
178
|
+
const level = m[1].length;
|
|
179
|
+
while (
|
|
180
|
+
headingStack.length &&
|
|
181
|
+
headingStack[headingStack.length - 1].level >= level
|
|
182
|
+
) {
|
|
183
|
+
headingStack.pop();
|
|
184
|
+
}
|
|
185
|
+
headingStack.push({ level, text: m[2] });
|
|
186
|
+
bufferStartLine = i;
|
|
187
|
+
buffer.push(line);
|
|
188
|
+
} else {
|
|
189
|
+
if (!buffer.length) bufferStartLine = i;
|
|
190
|
+
buffer.push(line);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
flush();
|
|
194
|
+
|
|
195
|
+
// Stamp total_chunks
|
|
196
|
+
for (const c of chunks) c.metadata.total_chunks = chunks.length;
|
|
197
|
+
return chunks;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Code chunker — sliding window that snaps to blank-line boundaries so
|
|
202
|
+
* we don't split mid-function. Tracks line ranges in metadata.
|
|
203
|
+
*/
|
|
204
|
+
function chunkCode(content, chunkTokens, overlap, ext) {
|
|
205
|
+
const lines = content.split("\n");
|
|
206
|
+
const targetChars = tokensToChars(chunkTokens);
|
|
207
|
+
const overlapChars = tokensToChars(overlap);
|
|
208
|
+
const chunks = [];
|
|
209
|
+
|
|
210
|
+
let cursor = 0;
|
|
211
|
+
let charCount = 0;
|
|
212
|
+
let chunkStart = 0;
|
|
213
|
+
let lineCharOffsets = [0];
|
|
214
|
+
for (let i = 0; i < lines.length; i++) {
|
|
215
|
+
lineCharOffsets.push(lineCharOffsets[i] + lines[i].length + 1);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function emit(startLine, endLine) {
|
|
219
|
+
const text = lines.slice(startLine, endLine + 1).join("\n").trim();
|
|
220
|
+
if (!text) return;
|
|
221
|
+
chunks.push({
|
|
222
|
+
content: text,
|
|
223
|
+
metadata: {
|
|
224
|
+
kind: "code",
|
|
225
|
+
ext,
|
|
226
|
+
line_start: startLine + 1,
|
|
227
|
+
line_end: endLine + 1,
|
|
228
|
+
chunk_index: chunks.length,
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
while (cursor < lines.length) {
|
|
234
|
+
let endLine = cursor;
|
|
235
|
+
let chunkChars = 0;
|
|
236
|
+
while (endLine < lines.length && chunkChars < targetChars) {
|
|
237
|
+
chunkChars += lines[endLine].length + 1;
|
|
238
|
+
endLine++;
|
|
239
|
+
}
|
|
240
|
+
// Snap end to a blank line if one is within +/- 5 lines
|
|
241
|
+
let snapTarget = endLine;
|
|
242
|
+
for (let k = 0; k < 5 && endLine - 1 - k > cursor; k++) {
|
|
243
|
+
if (!lines[endLine - 1 - k].trim()) {
|
|
244
|
+
snapTarget = endLine - k;
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
snapTarget = Math.min(snapTarget, lines.length);
|
|
249
|
+
|
|
250
|
+
emit(cursor, snapTarget - 1);
|
|
251
|
+
|
|
252
|
+
if (snapTarget >= lines.length) break;
|
|
253
|
+
|
|
254
|
+
// Compute overlap in lines: walk back until overlapChars consumed
|
|
255
|
+
let overlapStart = snapTarget;
|
|
256
|
+
let oc = 0;
|
|
257
|
+
while (overlapStart > cursor + 1 && oc < overlapChars) {
|
|
258
|
+
overlapStart--;
|
|
259
|
+
oc += lines[overlapStart].length + 1;
|
|
260
|
+
}
|
|
261
|
+
cursor = overlapStart;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
for (const c of chunks) c.metadata.total_chunks = chunks.length;
|
|
265
|
+
return chunks;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Structured-data chunker — small files become one chunk; bigger ones
|
|
270
|
+
* split at top-level keys (JSON/YAML) or at section boundaries (TOML/INI).
|
|
271
|
+
* Doesn't try to parse; uses heuristics so we don't crash on malformed
|
|
272
|
+
* configs.
|
|
273
|
+
*/
|
|
274
|
+
function chunkStructured(content, ext, chunkTokens) {
|
|
275
|
+
if (approxTokens(content) <= chunkTokens) {
|
|
276
|
+
return [{
|
|
277
|
+
content,
|
|
278
|
+
metadata: { kind: "config", ext, chunk_index: 0, total_chunks: 1 },
|
|
279
|
+
}];
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// For larger configs, just sliding-window — config files at this size
|
|
283
|
+
// are usually generated and unlikely to be hand-edited reference material.
|
|
284
|
+
return chunkSlidingWindow(content, chunkTokens, 0, "config", { ext });
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Generic sliding-window chunker. Used as fallback and inside other
|
|
289
|
+
* chunkers when a section is too large.
|
|
290
|
+
*/
|
|
291
|
+
function chunkSlidingWindow(content, chunkTokens, overlap, kind, extraMeta = {}) {
|
|
292
|
+
const targetChars = tokensToChars(chunkTokens);
|
|
293
|
+
const overlapChars = tokensToChars(overlap);
|
|
294
|
+
const chunks = [];
|
|
295
|
+
|
|
296
|
+
let cursor = 0;
|
|
297
|
+
while (cursor < content.length) {
|
|
298
|
+
let end = Math.min(cursor + targetChars, content.length);
|
|
299
|
+
|
|
300
|
+
// Snap to nearest newline if within 200 chars
|
|
301
|
+
if (end < content.length) {
|
|
302
|
+
const nl = content.lastIndexOf("\n", end);
|
|
303
|
+
if (nl > cursor && end - nl < 200) end = nl;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const text = content.slice(cursor, end).trim();
|
|
307
|
+
if (text) {
|
|
308
|
+
chunks.push({
|
|
309
|
+
content: text,
|
|
310
|
+
metadata: {
|
|
311
|
+
kind,
|
|
312
|
+
chunk_index: chunks.length,
|
|
313
|
+
char_start: cursor,
|
|
314
|
+
char_end: end,
|
|
315
|
+
...extraMeta,
|
|
316
|
+
},
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (end >= content.length) break;
|
|
321
|
+
cursor = Math.max(end - overlapChars, cursor + 1);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
for (const c of chunks) c.metadata.total_chunks = chunks.length;
|
|
325
|
+
return chunks;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
export { CODE_EXTENSIONS, PROSE_EXTENSIONS, STRUCTURED_EXTENSIONS, approxTokens };
|