@diegoaltoworks/chatter 2.0.0 → 2.1.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/core/chunking.d.ts +31 -0
- package/dist/core/chunking.d.ts.map +1 -0
- package/dist/core/retrieval.d.ts +39 -0
- package/dist/core/retrieval.d.ts.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +211 -25
- package/dist/index.mjs +205 -20
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +201 -19
- package/dist/mcp-server.mjs +200 -18
- package/dist/personas/index.js +4 -2
- package/dist/personas/index.mjs +4 -2
- package/dist/personas/timeContext.d.ts.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +207 -25
- package/dist/server.mjs +202 -20
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +10 -10
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Section-aware Markdown chunking.
|
|
3
|
+
*
|
|
4
|
+
* A pure line scanner (no Markdown parser dependency) that splits a document at
|
|
5
|
+
* every ATX heading, records the heading trail each chunk sat under, and keeps
|
|
6
|
+
* fenced code blocks, lists and tables whole. A section longer than the cap is
|
|
7
|
+
* split on blank-line paragraph boundaries; consecutive small sections are
|
|
8
|
+
* never merged.
|
|
9
|
+
*/
|
|
10
|
+
/** One chunk of a document, in reading order. */
|
|
11
|
+
export type SectionChunk = {
|
|
12
|
+
/** The chunk's text. A section's first chunk starts with its heading line. */
|
|
13
|
+
text: string;
|
|
14
|
+
/** Heading trail from the H1 down; empty for text before the first heading. */
|
|
15
|
+
section: string[];
|
|
16
|
+
/** Zero-based index of the chunk within the document. */
|
|
17
|
+
position: number;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Split Markdown `text` into structure-aware chunks of at most `max` characters
|
|
21
|
+
* where the structure allows it.
|
|
22
|
+
*
|
|
23
|
+
* Every ATX heading starts a new chunk, with the heading line kept at its top.
|
|
24
|
+
* A section longer than `max` is split on paragraph boundaries, each piece
|
|
25
|
+
* keeping the section's trail. A fenced code block, list or table is atomic and
|
|
26
|
+
* is emitted whole even if it alone exceeds `max`. Empty chunks are never
|
|
27
|
+
* produced, and every non-blank input line appears in exactly one chunk, in
|
|
28
|
+
* order.
|
|
29
|
+
*/
|
|
30
|
+
export declare function chunkSections(text: string, max?: number): SectionChunk[];
|
|
31
|
+
//# sourceMappingURL=chunking.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunking.d.ts","sourceRoot":"","sources":["../../src/core/chunking.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,iDAAiD;AACjD,MAAM,MAAM,YAAY,GAAG;IACzB,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAC;IACb,+EAA+E;IAC/E,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,yDAAyD;IACzD,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AA+GF;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,SAAM,GAAG,YAAY,EAAE,CA6BrE"}
|
package/dist/core/retrieval.d.ts
CHANGED
|
@@ -16,6 +16,13 @@ export declare const DEFAULT_KNOWLEDGE_DIR = "./config/knowledge";
|
|
|
16
16
|
export interface Retriever {
|
|
17
17
|
/** Retrieve up to `k` chunks across `allowedBuckets` for `query`, most relevant first. */
|
|
18
18
|
query(query: string, k: number, allowedBuckets: string[]): Promise<string[]>;
|
|
19
|
+
/**
|
|
20
|
+
* Optional record-returning variant of `query`, for a host that needs to
|
|
21
|
+
* know where a passage came from (a search route, citations). `prepareChat`
|
|
22
|
+
* never calls it; implement it only if your own code wants provenance.
|
|
23
|
+
* Same scoping and ordering rules as `query`.
|
|
24
|
+
*/
|
|
25
|
+
queryChunks?(query: string, k: number, allowedBuckets: string[]): Promise<RetrievedChunk[]>;
|
|
19
26
|
/**
|
|
20
27
|
* Optional one-time ingest/warm-up step, run once at server startup before
|
|
21
28
|
* the store answers any query. Omit it for a retriever that is always
|
|
@@ -23,6 +30,18 @@ export interface Retriever {
|
|
|
23
30
|
*/
|
|
24
31
|
build?(): Promise<void>;
|
|
25
32
|
}
|
|
33
|
+
/** One retrieval result with its provenance. See {@link Retriever.queryChunks}. */
|
|
34
|
+
export interface RetrievedChunk {
|
|
35
|
+
/** The chunk text alone, without any embedding context line. */
|
|
36
|
+
text: string;
|
|
37
|
+
bucket: string;
|
|
38
|
+
/** Where the chunk came from; knowledgeDir-relative for rows ingested in `'sections'` mode. */
|
|
39
|
+
source: string;
|
|
40
|
+
/** Heading trail from the H1 down; empty when the row has none (every `'lines'` row). */
|
|
41
|
+
section: string[];
|
|
42
|
+
/** Cosine similarity between the query and the chunk embedding. */
|
|
43
|
+
score: number;
|
|
44
|
+
}
|
|
26
45
|
/**
|
|
27
46
|
* Embeds a batch of texts into vectors, in input order. Lets
|
|
28
47
|
* {@link VectorStore} stay decoupled from any specific embeddings provider -
|
|
@@ -52,6 +71,8 @@ export declare function openLibsqlClient(database: {
|
|
|
52
71
|
url: string;
|
|
53
72
|
authToken: string;
|
|
54
73
|
}): Promise<LibsqlClient>;
|
|
74
|
+
/** Chunking strategy for {@link VectorStoreOptions.chunking}. */
|
|
75
|
+
export type ChunkingMode = "lines" | "sections";
|
|
55
76
|
/**
|
|
56
77
|
* `VectorStore` always takes an already-open libsql client rather than
|
|
57
78
|
* credentials to open its own - the same rule every other store in this
|
|
@@ -68,6 +89,15 @@ export interface VectorStoreOptions {
|
|
|
68
89
|
databaseClient: LibsqlClient;
|
|
69
90
|
/** Directory of markdown knowledge files. Default: `./config/knowledge` */
|
|
70
91
|
knowledgeDir?: string;
|
|
92
|
+
/**
|
|
93
|
+
* How knowledge files are cut into chunks. `'lines'` (default) keeps the
|
|
94
|
+
* original fixed-size line chunks, so ids and embeddings are unchanged.
|
|
95
|
+
* `'sections'` cuts at Markdown headings (see
|
|
96
|
+
* [chunking.ts](./chunking.ts)), records each chunk's heading trail and
|
|
97
|
+
* position, and embeds it with a `<source> > <heading trail>` context line.
|
|
98
|
+
* Switching modes re-embeds every chunk once on the next build.
|
|
99
|
+
*/
|
|
100
|
+
chunking?: ChunkingMode;
|
|
71
101
|
/** Logger for build progress. Default: a console logger writing to stderr. */
|
|
72
102
|
logger?: Logger;
|
|
73
103
|
/**
|
|
@@ -92,6 +122,7 @@ export declare class VectorStore implements Retriever {
|
|
|
92
122
|
*/
|
|
93
123
|
readonly db: LibsqlClient;
|
|
94
124
|
private knowledgeDir;
|
|
125
|
+
private chunking;
|
|
95
126
|
private logger;
|
|
96
127
|
private buildLock;
|
|
97
128
|
private instanceId;
|
|
@@ -113,6 +144,12 @@ export declare class VectorStore implements Retriever {
|
|
|
113
144
|
private renewBuildLock;
|
|
114
145
|
/** The ingest itself. Only ever runs with the build lock held. */
|
|
115
146
|
private buildUnderLock;
|
|
147
|
+
/**
|
|
148
|
+
* Adds the nullable `section`/`position` columns to a `chunks` table that
|
|
149
|
+
* every released version created without them. Checked via table_info so it
|
|
150
|
+
* is a no-op once present; only `'sections'` mode ever calls it.
|
|
151
|
+
*/
|
|
152
|
+
private ensureSectionColumns;
|
|
116
153
|
private cleanupStaleChunks;
|
|
117
154
|
private static cosine;
|
|
118
155
|
/**
|
|
@@ -126,6 +163,8 @@ export declare class VectorStore implements Retriever {
|
|
|
126
163
|
* interpolated. An empty list retrieves nothing, and short-circuits before
|
|
127
164
|
* the embedding call.
|
|
128
165
|
*/
|
|
166
|
+
queryChunks(q: string, k?: number, allowed?: string[]): Promise<RetrievedChunk[]>;
|
|
167
|
+
/** Same results as {@link queryChunks}, as bare text. */
|
|
129
168
|
query(q: string, k?: number, allowed?: string[]): Promise<string[]>;
|
|
130
169
|
}
|
|
131
170
|
//# sourceMappingURL=retrieval.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retrieval.d.ts","sourceRoot":"","sources":["../../src/core/retrieval.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"retrieval.d.ts","sourceRoot":"","sources":["../../src/core/retrieval.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -20,6 +20,8 @@ export type { BucketsFor, BucketsForContext } from "./core/buckets";
|
|
|
20
20
|
export { defaultBuckets, resolveBuckets } from "./core/buckets";
|
|
21
21
|
export type { BuildLock, BuildLockRow } from "./core/buildLock";
|
|
22
22
|
export { BUILD_LOCK_KEY, BUILD_LOCK_STALE_MS, canAcquireBuildLock, createTursoBuildLock, } from "./core/buildLock";
|
|
23
|
+
export type { SectionChunk } from "./core/chunking";
|
|
24
|
+
export { chunkSections } from "./core/chunking";
|
|
23
25
|
export { DEFAULT_REFUSAL, detectLeakage, guardOutput, scrubOutput } from "./core/guardrails";
|
|
24
26
|
export { completeOnce, completeStream, DEFAULT_MODEL } from "./core/llm";
|
|
25
27
|
export { loadKnowledge } from "./core/loaders";
|
|
@@ -30,7 +32,7 @@ export { normalizeChatBody, normalizeMessages } from "./core/messages";
|
|
|
30
32
|
export type { FallbackContext, FallbackFn, PipelineMessage, PipelineMode, PreparedChat, RerankContext, RerankContextArgs, RewriteQuery, RewriteQueryArgs, } from "./core/pipeline";
|
|
31
33
|
export { prepareChat } from "./core/pipeline";
|
|
32
34
|
export { DEFAULT_PROMPTS_DIR, PromptLoader } from "./core/prompts";
|
|
33
|
-
export type { Embedder, Retriever, VectorStoreOptions } from "./core/retrieval";
|
|
35
|
+
export type { ChunkingMode, Embedder, RetrievedChunk, Retriever, VectorStoreOptions, } from "./core/retrieval";
|
|
34
36
|
export { createOpenAIEmbedder, DEFAULT_KNOWLEDGE_DIR, VectorStore } from "./core/retrieval";
|
|
35
37
|
export { createSession, getActiveSessions, getSessionInfo, validateSession, } from "./core/session";
|
|
36
38
|
export type { MCPServerOptions, MCPTransportMode } from "./mcp-server";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEzE,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAIvC,YAAY,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C,YAAY,EACV,aAAa,EACb,gBAAgB,EAChB,UAAU,EACV,WAAW,EACX,QAAQ,EACR,eAAe,GAChB,MAAM,UAAU,CAAC;AAClB,YAAY,EACV,QAAQ,EACR,aAAa,EACb,cAAc,EACd,aAAa,EACb,WAAW,EACX,cAAc,EACd,mBAAmB,GACpB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAC9E,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEpE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEhE,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC7F,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAG1D,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACvE,YAAY,EACV,eAAe,EACf,UAAU,EACV,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,iBAAiB,EACjB,YAAY,EACZ,gBAAgB,GACjB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnE,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -94,6 +94,126 @@ var init_buildLock = __esm({
|
|
|
94
94
|
}
|
|
95
95
|
});
|
|
96
96
|
|
|
97
|
+
// src/core/chunking.ts
|
|
98
|
+
function headingOf(line) {
|
|
99
|
+
const match = HEADING.exec(line);
|
|
100
|
+
if (!match) return void 0;
|
|
101
|
+
const title = (match[2] ?? "").replace(/[ \t]+#+$/, "").replace(/^#+$/, "").trim();
|
|
102
|
+
return { level: match[1].length, title };
|
|
103
|
+
}
|
|
104
|
+
function startsTable(lines, i) {
|
|
105
|
+
return lines[i].includes("|") && i + 1 < lines.length && TABLE_DELIMITER.test(lines[i + 1]);
|
|
106
|
+
}
|
|
107
|
+
function fenceEnd(lines, start) {
|
|
108
|
+
const opener = FENCE.exec(lines[start]);
|
|
109
|
+
if (!opener) return start + 1;
|
|
110
|
+
const marker = opener[1];
|
|
111
|
+
const closer = new RegExp(`^ {0,3}${marker[0] === "`" ? "`" : "~"}{${marker.length},}[ \\t]*$`);
|
|
112
|
+
for (let i = start + 1; i < lines.length; i++) if (closer.test(lines[i])) return i + 1;
|
|
113
|
+
return lines.length;
|
|
114
|
+
}
|
|
115
|
+
function listEnd(lines, start) {
|
|
116
|
+
let end = start + 1;
|
|
117
|
+
for (let i = start + 1; i < lines.length; i++) {
|
|
118
|
+
const line = lines[i];
|
|
119
|
+
if (isBlank(line)) {
|
|
120
|
+
let next = i + 1;
|
|
121
|
+
while (next < lines.length && isBlank(lines[next])) next++;
|
|
122
|
+
if (next < lines.length && (LIST_ITEM.test(lines[next]) || isIndented(lines[next]))) {
|
|
123
|
+
i = next - 1;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
if (headingOf(line) && !isIndented(line)) break;
|
|
129
|
+
if (FENCE.test(line) && !isIndented(line)) break;
|
|
130
|
+
end = i + 1;
|
|
131
|
+
}
|
|
132
|
+
return end;
|
|
133
|
+
}
|
|
134
|
+
function tableEnd(lines, start) {
|
|
135
|
+
let end = start + 2;
|
|
136
|
+
while (end < lines.length && !isBlank(lines[end]) && lines[end].includes("|")) end++;
|
|
137
|
+
return end;
|
|
138
|
+
}
|
|
139
|
+
function paragraphEnd(lines, start) {
|
|
140
|
+
let end = start + 1;
|
|
141
|
+
while (end < lines.length && !isBlank(lines[end]) && !headingOf(lines[end]) && !FENCE.test(lines[end]) && !LIST_ITEM.test(lines[end])) {
|
|
142
|
+
end++;
|
|
143
|
+
}
|
|
144
|
+
return end;
|
|
145
|
+
}
|
|
146
|
+
function scan(text) {
|
|
147
|
+
const lines = text.replace(/\r\n?/g, "\n").split("\n");
|
|
148
|
+
const sections = [{ trail: [], blocks: [] }];
|
|
149
|
+
const stack = [];
|
|
150
|
+
let i = 0;
|
|
151
|
+
while (i < lines.length) {
|
|
152
|
+
const line = lines[i];
|
|
153
|
+
if (isBlank(line)) {
|
|
154
|
+
i++;
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
const heading = FENCE.test(line) ? void 0 : headingOf(line);
|
|
158
|
+
if (heading) {
|
|
159
|
+
while (stack.length && stack[stack.length - 1].level >= heading.level) stack.pop();
|
|
160
|
+
stack.push(heading);
|
|
161
|
+
sections.push({ trail: stack.map((h) => h.title), heading: line, blocks: [] });
|
|
162
|
+
i++;
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
let end;
|
|
166
|
+
if (FENCE.test(line)) end = fenceEnd(lines, i);
|
|
167
|
+
else if (LIST_ITEM.test(line)) end = listEnd(lines, i);
|
|
168
|
+
else if (startsTable(lines, i)) end = tableEnd(lines, i);
|
|
169
|
+
else end = paragraphEnd(lines, i);
|
|
170
|
+
sections[sections.length - 1].blocks.push({ lines: lines.slice(i, end) });
|
|
171
|
+
i = end;
|
|
172
|
+
}
|
|
173
|
+
return sections;
|
|
174
|
+
}
|
|
175
|
+
function chunkSections(text, max = 900) {
|
|
176
|
+
const cap = Math.max(1, max);
|
|
177
|
+
const out = [];
|
|
178
|
+
const emit = (parts, section) => {
|
|
179
|
+
const body = parts.join("\n\n");
|
|
180
|
+
if (body.trim() === "") return;
|
|
181
|
+
out.push({ text: body, section: [...section], position: out.length });
|
|
182
|
+
};
|
|
183
|
+
for (const section of scan(text)) {
|
|
184
|
+
let parts = section.heading ? [section.heading.trim()] : [];
|
|
185
|
+
let size = parts.join("\n\n").length;
|
|
186
|
+
let hasBlock = false;
|
|
187
|
+
for (const block of section.blocks) {
|
|
188
|
+
const piece = block.lines.join("\n");
|
|
189
|
+
const added = piece.length + (parts.length ? 2 : 0);
|
|
190
|
+
if (hasBlock && size + added > cap) {
|
|
191
|
+
emit(parts, section.trail);
|
|
192
|
+
parts = [];
|
|
193
|
+
size = 0;
|
|
194
|
+
hasBlock = false;
|
|
195
|
+
}
|
|
196
|
+
size += piece.length + (parts.length ? 2 : 0);
|
|
197
|
+
parts.push(piece);
|
|
198
|
+
hasBlock = true;
|
|
199
|
+
}
|
|
200
|
+
emit(parts, section.trail);
|
|
201
|
+
}
|
|
202
|
+
return out;
|
|
203
|
+
}
|
|
204
|
+
var FENCE, HEADING, LIST_ITEM, TABLE_DELIMITER, isBlank, isIndented;
|
|
205
|
+
var init_chunking = __esm({
|
|
206
|
+
"src/core/chunking.ts"() {
|
|
207
|
+
"use strict";
|
|
208
|
+
FENCE = /^ {0,3}(`{3,}|~{3,})/;
|
|
209
|
+
HEADING = /^ {0,3}(#{1,6})(?:[ \t]+(.*?))?[ \t]*$/;
|
|
210
|
+
LIST_ITEM = /^[ \t]*(?:[-*+]|\d{1,9}[.)])[ \t]+\S/;
|
|
211
|
+
TABLE_DELIMITER = /^[ \t]*\|?[ \t]*:?-+:?[ \t]*(?:\|[ \t]*:?-+:?[ \t]*)*\|?[ \t]*$/;
|
|
212
|
+
isBlank = (line) => line.trim() === "";
|
|
213
|
+
isIndented = (line) => /^(?: {2,}|\t)/.test(line);
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
|
|
97
217
|
// src/core/loaders.ts
|
|
98
218
|
function walk(dir) {
|
|
99
219
|
const items = [];
|
|
@@ -199,11 +319,13 @@ async function sha256(input) {
|
|
|
199
319
|
const hash = await crypto.subtle.digest("SHA-256", data);
|
|
200
320
|
return Array.from(new Uint8Array(hash)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
201
321
|
}
|
|
202
|
-
var EMB_MODEL, DEFAULT_KNOWLEDGE_DIR, DATABASE_CONFIG_REQUIRED_MESSAGE, VectorStore;
|
|
322
|
+
var import_node_path3, EMB_MODEL, DEFAULT_KNOWLEDGE_DIR, DATABASE_CONFIG_REQUIRED_MESSAGE, VectorStore;
|
|
203
323
|
var init_retrieval = __esm({
|
|
204
324
|
"src/core/retrieval.ts"() {
|
|
205
325
|
"use strict";
|
|
326
|
+
import_node_path3 = require("node:path");
|
|
206
327
|
init_buildLock();
|
|
328
|
+
init_chunking();
|
|
207
329
|
init_loaders();
|
|
208
330
|
init_logger();
|
|
209
331
|
EMB_MODEL = "text-embedding-3-large";
|
|
@@ -214,6 +336,7 @@ var init_retrieval = __esm({
|
|
|
214
336
|
this.embed = embed;
|
|
215
337
|
this.db = options.databaseClient;
|
|
216
338
|
this.knowledgeDir = options.knowledgeDir || DEFAULT_KNOWLEDGE_DIR;
|
|
339
|
+
this.chunking = options.chunking ?? "lines";
|
|
217
340
|
this.logger = options.logger ?? createConsoleLogger();
|
|
218
341
|
this.buildLock = options.buildLock ?? createTursoBuildLock(this.db);
|
|
219
342
|
this.instanceId = options.instanceId ?? crypto.randomUUID();
|
|
@@ -227,6 +350,7 @@ var init_retrieval = __esm({
|
|
|
227
350
|
*/
|
|
228
351
|
db;
|
|
229
352
|
knowledgeDir;
|
|
353
|
+
chunking;
|
|
230
354
|
logger;
|
|
231
355
|
buildLock;
|
|
232
356
|
instanceId;
|
|
@@ -287,11 +411,41 @@ var init_retrieval = __esm({
|
|
|
287
411
|
this.logger.info("No knowledge documents and no existing chunks - nothing to build");
|
|
288
412
|
return;
|
|
289
413
|
}
|
|
414
|
+
this.logger.info(`Chunking mode: ${this.chunking}`);
|
|
415
|
+
if (this.chunking === "sections") await this.ensureSectionColumns();
|
|
290
416
|
const rows = [];
|
|
291
417
|
for (const d of docs) {
|
|
292
|
-
|
|
293
|
-
const
|
|
294
|
-
|
|
418
|
+
if (this.chunking === "sections") {
|
|
419
|
+
const source = (0, import_node_path3.relative)(this.knowledgeDir, d.source).split(import_node_path3.sep).join("/");
|
|
420
|
+
for (const c of chunkSections(d.text)) {
|
|
421
|
+
const section = c.section.join(" > ");
|
|
422
|
+
const id = await sha256(`${d.bucket}|${source}|${section}|${c.text}`);
|
|
423
|
+
const context = section ? `${source} > ${section}` : source;
|
|
424
|
+
rows.push({
|
|
425
|
+
id,
|
|
426
|
+
bucket: d.bucket,
|
|
427
|
+
source,
|
|
428
|
+
text: c.text,
|
|
429
|
+
embedText: `${context}
|
|
430
|
+
|
|
431
|
+
${c.text}`,
|
|
432
|
+
section,
|
|
433
|
+
position: c.position
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
} else {
|
|
437
|
+
for (const part of chunk(d.text)) {
|
|
438
|
+
const id = await sha256(`${d.bucket}|${d.source}|${part}`);
|
|
439
|
+
rows.push({
|
|
440
|
+
id,
|
|
441
|
+
bucket: d.bucket,
|
|
442
|
+
source: d.source,
|
|
443
|
+
text: part,
|
|
444
|
+
embedText: part,
|
|
445
|
+
section: null,
|
|
446
|
+
position: null
|
|
447
|
+
});
|
|
448
|
+
}
|
|
295
449
|
}
|
|
296
450
|
}
|
|
297
451
|
this.logger.info(`Created ${rows.length} chunks from knowledge documents`);
|
|
@@ -300,11 +454,17 @@ var init_retrieval = __esm({
|
|
|
300
454
|
for (let i = 0; i < rows.length; i += UPSERT_BATCH) {
|
|
301
455
|
const batch = rows.slice(i, i + UPSERT_BATCH);
|
|
302
456
|
await this.db.batch(
|
|
303
|
-
batch.map(
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
457
|
+
batch.map(
|
|
458
|
+
(r) => this.chunking === "sections" ? {
|
|
459
|
+
sql: `INSERT INTO chunks(id,bucket,source,text,section,position) VALUES(?,?,?,?,?,?)
|
|
460
|
+
ON CONFLICT(id) DO NOTHING`,
|
|
461
|
+
args: [r.id, r.bucket, r.source, r.text, r.section, r.position]
|
|
462
|
+
} : {
|
|
463
|
+
sql: `INSERT INTO chunks(id,bucket,source,text) VALUES(?,?,?,?)
|
|
464
|
+
ON CONFLICT(id) DO NOTHING`,
|
|
465
|
+
args: [r.id, r.bucket, r.source, r.text]
|
|
466
|
+
}
|
|
467
|
+
),
|
|
308
468
|
"write"
|
|
309
469
|
);
|
|
310
470
|
}
|
|
@@ -325,7 +485,7 @@ var init_retrieval = __esm({
|
|
|
325
485
|
return;
|
|
326
486
|
}
|
|
327
487
|
this.logger.info(`Embedding ${missing.length} new/updated chunks with ${EMB_MODEL}...`);
|
|
328
|
-
const textById = new Map(rows.map((r) => [r.id, r.
|
|
488
|
+
const textById = new Map(rows.map((r) => [r.id, r.embedText]));
|
|
329
489
|
const BATCH = 96;
|
|
330
490
|
for (let i = 0; i < missing.length; i += BATCH) {
|
|
331
491
|
const batchIds = missing.slice(i, i + BATCH);
|
|
@@ -346,6 +506,18 @@ var init_retrieval = __esm({
|
|
|
346
506
|
}
|
|
347
507
|
this.logger.info(`Successfully embedded ${missing.length} new chunks`);
|
|
348
508
|
}
|
|
509
|
+
/**
|
|
510
|
+
* Adds the nullable `section`/`position` columns to a `chunks` table that
|
|
511
|
+
* every released version created without them. Checked via table_info so it
|
|
512
|
+
* is a no-op once present; only `'sections'` mode ever calls it.
|
|
513
|
+
*/
|
|
514
|
+
async ensureSectionColumns() {
|
|
515
|
+
const info = await this.db.execute("PRAGMA table_info(chunks)");
|
|
516
|
+
const have = new Set(info.rows.map((r) => String(r.name)));
|
|
517
|
+
if (!have.has("section")) await this.db.execute("ALTER TABLE chunks ADD COLUMN section TEXT");
|
|
518
|
+
if (!have.has("position"))
|
|
519
|
+
await this.db.execute("ALTER TABLE chunks ADD COLUMN position INTEGER");
|
|
520
|
+
}
|
|
349
521
|
// Remove chunks from database that no longer exist in markdown files
|
|
350
522
|
async cleanupStaleChunks(currentIds) {
|
|
351
523
|
const result = await this.db.execute("SELECT id FROM chunks");
|
|
@@ -392,25 +564,34 @@ var init_retrieval = __esm({
|
|
|
392
564
|
* interpolated. An empty list retrieves nothing, and short-circuits before
|
|
393
565
|
* the embedding call.
|
|
394
566
|
*/
|
|
395
|
-
async
|
|
567
|
+
async queryChunks(q, k = 6, allowed = ["base"]) {
|
|
396
568
|
if (allowed.length === 0) return [];
|
|
397
569
|
const [qv] = await this.embed([q]);
|
|
398
570
|
const placeholders = allowed.map(() => "?").join(",");
|
|
399
571
|
const res = await this.db.execute({
|
|
400
|
-
sql: `SELECT c
|
|
572
|
+
sql: `SELECT c.*, e.embedding
|
|
401
573
|
FROM chunks c
|
|
402
574
|
JOIN embeddings e ON e.id = c.id
|
|
403
575
|
WHERE c.bucket IN (${placeholders})`,
|
|
404
576
|
args: allowed
|
|
405
577
|
});
|
|
406
|
-
const scored =
|
|
407
|
-
for (const row of res.rows) {
|
|
578
|
+
const scored = res.rows.map((row) => {
|
|
408
579
|
const emb = JSON.parse(String(row.embedding));
|
|
409
|
-
const
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
580
|
+
const trail = row.section == null ? "" : String(row.section);
|
|
581
|
+
return {
|
|
582
|
+
text: String(row.text),
|
|
583
|
+
bucket: String(row.bucket ?? ""),
|
|
584
|
+
source: String(row.source ?? ""),
|
|
585
|
+
section: trail ? trail.split(" > ") : [],
|
|
586
|
+
score: _VectorStore.cosine(qv, emb)
|
|
587
|
+
};
|
|
588
|
+
});
|
|
589
|
+
scored.sort((a, b) => b.score - a.score);
|
|
590
|
+
return scored.slice(0, k);
|
|
591
|
+
}
|
|
592
|
+
/** Same results as {@link queryChunks}, as bare text. */
|
|
593
|
+
async query(q, k = 6, allowed = ["base"]) {
|
|
594
|
+
return (await this.queryChunks(q, k, allowed)).map((c) => c.text);
|
|
414
595
|
}
|
|
415
596
|
};
|
|
416
597
|
}
|
|
@@ -546,6 +727,7 @@ __export(index_exports, {
|
|
|
546
727
|
applyTransformReply: () => applyTransformReply,
|
|
547
728
|
canAcquireBuildLock: () => canAcquireBuildLock,
|
|
548
729
|
chatBodyLimit: () => chatBodyLimit,
|
|
730
|
+
chunkSections: () => chunkSections,
|
|
549
731
|
completeOnce: () => completeOnce,
|
|
550
732
|
completeStream: () => completeStream,
|
|
551
733
|
cors: () => cors,
|
|
@@ -866,6 +1048,7 @@ async function resolveBuckets({
|
|
|
866
1048
|
|
|
867
1049
|
// src/index.ts
|
|
868
1050
|
init_buildLock();
|
|
1051
|
+
init_chunking();
|
|
869
1052
|
init_loaders();
|
|
870
1053
|
init_logger();
|
|
871
1054
|
|
|
@@ -1354,6 +1537,7 @@ async function createMCPServer(config) {
|
|
|
1354
1537
|
store = new VectorStore2(createOpenAIEmbedder2(client), {
|
|
1355
1538
|
databaseClient: db,
|
|
1356
1539
|
knowledgeDir: config.knowledgeDir || DEFAULT_KNOWLEDGE_DIR2,
|
|
1540
|
+
chunking: config.chunking,
|
|
1357
1541
|
logger: log
|
|
1358
1542
|
});
|
|
1359
1543
|
}
|
|
@@ -2401,7 +2585,7 @@ function publicRoutes(deps) {
|
|
|
2401
2585
|
}
|
|
2402
2586
|
|
|
2403
2587
|
// src/server.ts
|
|
2404
|
-
var
|
|
2588
|
+
var import_node_path5 = require("node:path");
|
|
2405
2589
|
var import_hono5 = require("hono");
|
|
2406
2590
|
var import_openai2 = __toESM(require("openai"));
|
|
2407
2591
|
|
|
@@ -2505,7 +2689,7 @@ function loadServeStatic(runtime = detectRuntime()) {
|
|
|
2505
2689
|
|
|
2506
2690
|
// src/core/widgets.ts
|
|
2507
2691
|
var import_node_fs3 = require("node:fs");
|
|
2508
|
-
var
|
|
2692
|
+
var import_node_path4 = require("node:path");
|
|
2509
2693
|
var import_node_url = require("node:url");
|
|
2510
2694
|
var import_meta = {};
|
|
2511
2695
|
function resolveStatic(configStaticDir) {
|
|
@@ -2514,15 +2698,15 @@ function resolveStatic(configStaticDir) {
|
|
|
2514
2698
|
}
|
|
2515
2699
|
try {
|
|
2516
2700
|
if (import_meta.url) {
|
|
2517
|
-
const moduleDir = (0,
|
|
2518
|
-
const staticDir = (0,
|
|
2701
|
+
const moduleDir = (0, import_node_path4.dirname)((0, import_node_url.fileURLToPath)(import_meta.url));
|
|
2702
|
+
const staticDir = (0, import_node_path4.join)(moduleDir, "static");
|
|
2519
2703
|
if ((0, import_node_fs3.existsSync)(staticDir)) return { staticDir };
|
|
2520
2704
|
}
|
|
2521
2705
|
} catch {
|
|
2522
2706
|
}
|
|
2523
2707
|
try {
|
|
2524
2708
|
if (typeof __dirname !== "undefined") {
|
|
2525
|
-
const staticDir = (0,
|
|
2709
|
+
const staticDir = (0, import_node_path4.join)(__dirname, "static");
|
|
2526
2710
|
if ((0, import_node_fs3.existsSync)(staticDir)) return { staticDir };
|
|
2527
2711
|
}
|
|
2528
2712
|
} catch {
|
|
@@ -2561,6 +2745,7 @@ async function createServer(config) {
|
|
|
2561
2745
|
store = new VectorStore2(createOpenAIEmbedder2(client), {
|
|
2562
2746
|
databaseClient: db,
|
|
2563
2747
|
knowledgeDir: config.knowledgeDir || DEFAULT_KNOWLEDGE_DIR2,
|
|
2748
|
+
chunking: config.chunking,
|
|
2564
2749
|
logger
|
|
2565
2750
|
});
|
|
2566
2751
|
}
|
|
@@ -2611,7 +2796,7 @@ async function createServer(config) {
|
|
|
2611
2796
|
}
|
|
2612
2797
|
}
|
|
2613
2798
|
if (serveStatic && staticDir) {
|
|
2614
|
-
const relativePath = (0,
|
|
2799
|
+
const relativePath = (0, import_node_path5.relative)(process.cwd(), staticDir);
|
|
2615
2800
|
app.get("/chatter.js", serveStatic({ path: `${relativePath}/chatter.js` }));
|
|
2616
2801
|
app.get("/chatter.css", serveStatic({ path: `${relativePath}/chatter.css` }));
|
|
2617
2802
|
}
|
|
@@ -2773,6 +2958,7 @@ function createShortener(config, fetchImpl = fetch) {
|
|
|
2773
2958
|
applyTransformReply,
|
|
2774
2959
|
canAcquireBuildLock,
|
|
2775
2960
|
chatBodyLimit,
|
|
2961
|
+
chunkSections,
|
|
2776
2962
|
completeOnce,
|
|
2777
2963
|
completeStream,
|
|
2778
2964
|
cors,
|