@graphmemory/server 1.3.0 → 1.3.2
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/api/index.js +4 -4
- package/dist/api/rest/code.js +2 -1
- package/dist/api/rest/docs.js +2 -1
- package/dist/api/rest/embed.js +8 -1
- package/dist/api/rest/index.js +25 -7
- package/dist/api/rest/knowledge.js +4 -2
- package/dist/api/rest/skills.js +2 -1
- package/dist/api/rest/tasks.js +2 -1
- package/dist/api/rest/validation.js +41 -40
- package/dist/api/rest/websocket.js +24 -7
- package/dist/api/tools/knowledge/add-attachment.js +2 -1
- package/dist/api/tools/skills/add-attachment.js +2 -1
- package/dist/api/tools/tasks/add-attachment.js +2 -1
- package/dist/cli/index.js +7 -4
- package/dist/cli/indexer.js +38 -25
- package/dist/graphs/attachment-types.js +5 -0
- package/dist/graphs/code.js +34 -8
- package/dist/graphs/docs.js +5 -3
- package/dist/graphs/file-index.js +5 -3
- package/dist/graphs/knowledge.js +11 -4
- package/dist/graphs/skill.js +12 -5
- package/dist/graphs/task.js +12 -5
- package/dist/lib/defaults.js +78 -0
- package/dist/lib/embedder.js +11 -12
- package/dist/lib/embedding-codec.js +3 -5
- package/dist/lib/graph-persistence.js +68 -0
- package/dist/lib/jwt.js +2 -2
- package/dist/lib/mirror-watcher.js +4 -3
- package/dist/lib/multi-config.js +3 -1
- package/dist/lib/parsers/docs.js +2 -1
- package/dist/lib/parsers/languages/typescript.js +39 -17
- package/dist/lib/project-manager.js +7 -1
- package/dist/lib/search/bm25.js +5 -4
- package/dist/lib/search/code.js +2 -1
- package/dist/lib/search/docs.js +2 -1
- package/dist/lib/search/file-index.js +2 -1
- package/dist/lib/search/files.js +3 -2
- package/dist/lib/search/knowledge.js +2 -1
- package/dist/lib/search/skills.js +2 -1
- package/dist/lib/search/tasks.js +2 -1
- package/dist/ui/assets/{NoteForm-aZX9f6-3.js → NoteForm-SQ0b93i0.js} +1 -1
- package/dist/ui/assets/{SkillForm-KYa3o92l.js → SkillForm-BVsGrNPb.js} +1 -1
- package/dist/ui/assets/{TaskForm-Bl5nkybO.js → TaskForm-DgPVeiI9.js} +1 -1
- package/dist/ui/assets/{_articleId_-DjbCByxM.js → _articleId_-FqdaSeYS.js} +1 -1
- package/dist/ui/assets/{_docId_-hdCDjclV.js → _docId_-Q0Wmjtp6.js} +1 -1
- package/dist/ui/assets/{_filePath_-CpG836v4.js → _filePath_-BR0gOT_z.js} +1 -1
- package/dist/ui/assets/{_noteId_-C1enaQd1.js → _noteId_-BMWd415J.js} +1 -1
- package/dist/ui/assets/{_skillId_-hPoCet7J.js → _skillId_-CsHgildJ.js} +1 -1
- package/dist/ui/assets/{_taskId_-DSB3dLVz.js → _taskId_-xDHTfbQw.js} +1 -1
- package/dist/ui/assets/{_toolName_-3SmCfxZy.js → _toolName_-BSa2uNSu.js} +1 -1
- package/dist/ui/assets/{attachments-CEQ-2nMo.js → attachments-NSvN5_0A.js} +1 -1
- package/dist/ui/assets/{docs-CrXsRcOG.js → docs-iUK8E40J.js} +1 -1
- package/dist/ui/assets/{edit-TUIIpUMF.js → edit-BzIJy_Oo.js} +1 -1
- package/dist/ui/assets/{edit-BYiy1FZy.js → edit-Dnc067B2.js} +1 -1
- package/dist/ui/assets/{edit-hc-ZWz3y.js → edit-U_UEI361.js} +1 -1
- package/dist/ui/assets/{files-0bPg6NH9.js → files-B4svJUZh.js} +1 -1
- package/dist/ui/assets/{graph-DXGud_wF.js → graph-CcNP1ckP.js} +1 -1
- package/dist/ui/assets/{help-DJ52_fxN.js → help-BJZZtKAR.js} +1 -1
- package/dist/ui/assets/{help-CEMQqZUR.js → help-D6XKMuzk.js} +16 -4
- package/dist/ui/assets/index-CEweXD9O.js +2 -0
- package/dist/ui/assets/{knowledge-DeygeGGH.js → knowledge-CV99ToEV.js} +1 -1
- package/dist/ui/assets/{new-CpD7hOBA.js → new-BypesKiP.js} +1 -1
- package/dist/ui/assets/{new-s8c0M75X.js → new-Dcx8wlp4.js} +1 -1
- package/dist/ui/assets/{new-DHTg3Dqq.js → new-Sq3NY2oa.js} +1 -1
- package/dist/ui/assets/{prompts-BgOmdxgM.js → prompts-DbsIe3Pm.js} +1 -1
- package/dist/ui/assets/{search-EpJhdP2a.js → search-D87r7lIL.js} +1 -1
- package/dist/ui/assets/{skill-y9pizyqE.js → skill-BltAsz7M.js} +1 -1
- package/dist/ui/assets/{skills-Cga9iUZN.js → skills-Dtmg2kDA.js} +1 -1
- package/dist/ui/assets/{tasks-CobouTKV.js → tasks-BRqIwKCG.js} +1 -1
- package/dist/ui/assets/{tools-JxKH5BDF.js → tools-CM3gQ4TK.js} +1 -1
- package/dist/ui/index.html +1 -1
- package/package.json +5 -2
- package/dist/ui/assets/index-BCZDAYZi.js +0 -2
package/dist/cli/indexer.js
CHANGED
|
@@ -13,6 +13,7 @@ const docs_2 = require("../graphs/docs");
|
|
|
13
13
|
const code_1 = require("../lib/parsers/code");
|
|
14
14
|
const code_2 = require("../graphs/code");
|
|
15
15
|
const watcher_1 = require("../lib/watcher");
|
|
16
|
+
const defaults_1 = require("../lib/defaults");
|
|
16
17
|
const knowledge_1 = require("../graphs/knowledge");
|
|
17
18
|
const task_1 = require("../graphs/task");
|
|
18
19
|
const skill_1 = require("../graphs/skill");
|
|
@@ -103,7 +104,7 @@ function createProjectIndexer(docGraph, codeGraph, config, knowledgeGraph, fileI
|
|
|
103
104
|
const rootChunk = chunks.find(c => c.level === 1);
|
|
104
105
|
const embedText = rootChunk?.title
|
|
105
106
|
? `${fileId} ${rootChunk.title}`
|
|
106
|
-
: `${fileId} ${rootChunk?.content.slice(0,
|
|
107
|
+
: `${fileId} ${rootChunk?.content.slice(0, defaults_1.INDEXER_PREVIEW_LEN) ?? ''}`;
|
|
107
108
|
batchInputs.push({ title: embedText, content: '' });
|
|
108
109
|
const embeddings = await (0, embedder_1.embedBatch)(batchInputs, config.docsModelName);
|
|
109
110
|
for (let i = 0; i < chunks.length; i++) {
|
|
@@ -207,7 +208,9 @@ function createProjectIndexer(docGraph, codeGraph, config, knowledgeGraph, fileI
|
|
|
207
208
|
}
|
|
208
209
|
function dispatchAdd(absolutePath) {
|
|
209
210
|
const rel = path_1.default.relative(config.projectDir, absolutePath);
|
|
210
|
-
if (config.docsInclude && !isExcluded(rel, docsExclude) && micromatch_1.default.isMatch(rel, config.docsInclude)) {
|
|
211
|
+
if (docGraph && config.docsInclude && !isExcluded(rel, docsExclude) && micromatch_1.default.isMatch(rel, config.docsInclude)) {
|
|
212
|
+
if (rel.endsWith('.md'))
|
|
213
|
+
(0, docs_1.clearWikiIndexCache)(config.projectDir);
|
|
211
214
|
enqueueDoc(() => indexDocFile(absolutePath));
|
|
212
215
|
}
|
|
213
216
|
if (codeGraph && config.codeInclude && !isExcluded(rel, codeExclude) && micromatch_1.default.isMatch(rel, config.codeInclude)) {
|
|
@@ -220,33 +223,43 @@ function createProjectIndexer(docGraph, codeGraph, config, knowledgeGraph, fileI
|
|
|
220
223
|
function dispatchRemove(absolutePath) {
|
|
221
224
|
const rel = path_1.default.relative(config.projectDir, absolutePath);
|
|
222
225
|
if (docGraph && config.docsInclude && !isExcluded(rel, docsExclude) && micromatch_1.default.isMatch(rel, config.docsInclude)) {
|
|
223
|
-
(
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
(0,
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
226
|
+
if (rel.endsWith('.md'))
|
|
227
|
+
(0, docs_1.clearWikiIndexCache)(config.projectDir);
|
|
228
|
+
// Enqueue removal to avoid racing with in-flight indexDocFile tasks
|
|
229
|
+
enqueueDoc(async () => {
|
|
230
|
+
(0, docs_2.removeFile)(docGraph, rel);
|
|
231
|
+
if (knowledgeGraph)
|
|
232
|
+
(0, knowledge_1.cleanupProxies)(knowledgeGraph, 'docs', docGraph, config.projectId);
|
|
233
|
+
if (taskGraph)
|
|
234
|
+
(0, task_1.cleanupProxies)(taskGraph, 'docs', docGraph, config.projectId);
|
|
235
|
+
if (skillGraph)
|
|
236
|
+
(0, skill_1.cleanupProxies)(skillGraph, 'docs', docGraph, config.projectId);
|
|
237
|
+
process.stderr.write(`[indexer] removed doc ${rel}\n`);
|
|
238
|
+
});
|
|
231
239
|
}
|
|
232
240
|
if (codeGraph && config.codeInclude && !isExcluded(rel, codeExclude) && micromatch_1.default.isMatch(rel, config.codeInclude)) {
|
|
233
|
-
(
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
+
enqueueCode(async () => {
|
|
242
|
+
(0, code_2.removeCodeFile)(codeGraph, rel);
|
|
243
|
+
if (knowledgeGraph)
|
|
244
|
+
(0, knowledge_1.cleanupProxies)(knowledgeGraph, 'code', codeGraph, config.projectId);
|
|
245
|
+
if (taskGraph)
|
|
246
|
+
(0, task_1.cleanupProxies)(taskGraph, 'code', codeGraph, config.projectId);
|
|
247
|
+
if (skillGraph)
|
|
248
|
+
(0, skill_1.cleanupProxies)(skillGraph, 'code', codeGraph, config.projectId);
|
|
249
|
+
process.stderr.write(`[indexer] removed code ${rel}\n`);
|
|
250
|
+
});
|
|
241
251
|
}
|
|
242
252
|
if (fileIndexGraph && !isExcluded(rel, filesExclude)) {
|
|
243
|
-
(
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
253
|
+
enqueueFile(async () => {
|
|
254
|
+
(0, file_index_1.removeFileEntry)(fileIndexGraph, rel);
|
|
255
|
+
if (knowledgeGraph)
|
|
256
|
+
(0, knowledge_1.cleanupProxies)(knowledgeGraph, 'files', fileIndexGraph, config.projectId);
|
|
257
|
+
if (taskGraph)
|
|
258
|
+
(0, task_1.cleanupProxies)(taskGraph, 'files', fileIndexGraph, config.projectId);
|
|
259
|
+
if (skillGraph)
|
|
260
|
+
(0, skill_1.cleanupProxies)(skillGraph, 'files', fileIndexGraph, config.projectId);
|
|
261
|
+
process.stderr.write(`[indexer] removed file ${rel}\n`);
|
|
262
|
+
});
|
|
250
263
|
}
|
|
251
264
|
}
|
|
252
265
|
// ---------------------------------------------------------------------------
|
|
@@ -36,10 +36,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.MAX_ATTACHMENTS_PER_ENTITY = exports.MAX_ATTACHMENT_SIZE = void 0;
|
|
39
40
|
exports.scanAttachments = scanAttachments;
|
|
40
41
|
const fs = __importStar(require("fs"));
|
|
41
42
|
const path = __importStar(require("path"));
|
|
42
43
|
const mime_1 = __importDefault(require("mime"));
|
|
44
|
+
/** Maximum size of a single attachment in bytes (10 MB). */
|
|
45
|
+
exports.MAX_ATTACHMENT_SIZE = 10 * 1024 * 1024;
|
|
46
|
+
/** Maximum number of attachments per entity (note/task/skill). */
|
|
47
|
+
exports.MAX_ATTACHMENTS_PER_ENTITY = 20;
|
|
43
48
|
/**
|
|
44
49
|
* Scan the attachments/ subdirectory of an entity directory.
|
|
45
50
|
* Returns metadata for each file found.
|
package/dist/graphs/code.js
CHANGED
|
@@ -22,6 +22,8 @@ const code_1 = require("../lib/search/code");
|
|
|
22
22
|
const files_1 = require("../lib/search/files");
|
|
23
23
|
const bm25_1 = require("../lib/search/bm25");
|
|
24
24
|
const embedding_codec_1 = require("../lib/embedding-codec");
|
|
25
|
+
const graph_persistence_1 = require("../lib/graph-persistence");
|
|
26
|
+
const defaults_1 = require("../lib/defaults");
|
|
25
27
|
// ---------------------------------------------------------------------------
|
|
26
28
|
// CRUD
|
|
27
29
|
// ---------------------------------------------------------------------------
|
|
@@ -87,10 +89,10 @@ function resolvePendingImports(graph) {
|
|
|
87
89
|
}
|
|
88
90
|
/**
|
|
89
91
|
* Resolve pending extends/implements edges after all files have been indexed.
|
|
90
|
-
*
|
|
92
|
+
* When multiple candidates share the same name, prefers the one whose file
|
|
93
|
+
* is imported by the source file (falls back to first match).
|
|
91
94
|
*/
|
|
92
95
|
function resolvePendingEdges(graph) {
|
|
93
|
-
// Build name → nodeId index for fast lookup
|
|
94
96
|
const nameIndex = new Map();
|
|
95
97
|
graph.forEachNode((id, attrs) => {
|
|
96
98
|
if (attrs.kind === 'class' || attrs.kind === 'interface') {
|
|
@@ -99,6 +101,18 @@ function resolvePendingEdges(graph) {
|
|
|
99
101
|
nameIndex.set(attrs.name, list);
|
|
100
102
|
}
|
|
101
103
|
});
|
|
104
|
+
// Build file → imported file IDs index for disambiguation
|
|
105
|
+
const fileImports = new Map();
|
|
106
|
+
graph.forEachNode((id, attrs) => {
|
|
107
|
+
if (attrs.kind === 'file') {
|
|
108
|
+
const imported = new Set();
|
|
109
|
+
graph.forEachOutEdge(id, (_edge, edgeAttrs, _src, target) => {
|
|
110
|
+
if (edgeAttrs.kind === 'imports')
|
|
111
|
+
imported.add(target);
|
|
112
|
+
});
|
|
113
|
+
fileImports.set(id, imported);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
102
116
|
let created = 0;
|
|
103
117
|
graph.forEachNode((id, attrs) => {
|
|
104
118
|
if (!attrs.pendingEdges || attrs.pendingEdges.length === 0)
|
|
@@ -107,8 +121,20 @@ function resolvePendingEdges(graph) {
|
|
|
107
121
|
for (const edge of attrs.pendingEdges) {
|
|
108
122
|
const candidates = nameIndex.get(edge.toName);
|
|
109
123
|
if (candidates && candidates.length > 0 && graph.hasNode(edge.from)) {
|
|
110
|
-
|
|
111
|
-
|
|
124
|
+
let toId;
|
|
125
|
+
if (candidates.length === 1) {
|
|
126
|
+
toId = candidates[0];
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
// Disambiguate: prefer candidate whose file is imported by edge.from's file
|
|
130
|
+
const fromFileId = edge.from.split('::')[0];
|
|
131
|
+
const imports = fileImports.get(fromFileId);
|
|
132
|
+
const match = imports && candidates.find(c => {
|
|
133
|
+
const cFileId = c.split('::')[0];
|
|
134
|
+
return imports.has(cFileId);
|
|
135
|
+
});
|
|
136
|
+
toId = match ?? candidates[0];
|
|
137
|
+
}
|
|
112
138
|
if (toId !== edge.from && !graph.hasEdge(edge.from, toId)) {
|
|
113
139
|
graph.addEdgeWithKey(`${edge.from}→${toId}`, edge.from, toId, { kind: edge.kind });
|
|
114
140
|
created++;
|
|
@@ -142,7 +168,7 @@ function getCodeFileMtime(graph, fileId) {
|
|
|
142
168
|
return graph.getNodeAttribute(nodes[0], 'mtime');
|
|
143
169
|
}
|
|
144
170
|
/** List all indexed files with symbol counts. */
|
|
145
|
-
function listCodeFiles(graph, filter, limit =
|
|
171
|
+
function listCodeFiles(graph, filter, limit = defaults_1.LIST_LIMIT_SMALL) {
|
|
146
172
|
const files = new Map();
|
|
147
173
|
const lowerFilter = filter?.toLowerCase();
|
|
148
174
|
graph.forEachNode((_, attrs) => {
|
|
@@ -182,10 +208,10 @@ function loadCodeGraph(graphMemory, fresh = false, embeddingFingerprint) {
|
|
|
182
208
|
if (fresh)
|
|
183
209
|
return graph;
|
|
184
210
|
const file = path_1.default.join(graphMemory, 'code.json');
|
|
185
|
-
|
|
211
|
+
const data = (0, graph_persistence_1.readJsonWithTmpFallback)(file);
|
|
212
|
+
if (!data)
|
|
186
213
|
return graph;
|
|
187
214
|
try {
|
|
188
|
-
const data = JSON.parse(fs_1.default.readFileSync(file, 'utf-8'));
|
|
189
215
|
const stored = data.embeddingModel;
|
|
190
216
|
if (embeddingFingerprint && stored !== embeddingFingerprint) {
|
|
191
217
|
process.stderr.write(`[code-graph] Embedding config changed, re-indexing code graph\n`);
|
|
@@ -207,7 +233,7 @@ class CodeGraphManager {
|
|
|
207
233
|
_graph;
|
|
208
234
|
embedFns;
|
|
209
235
|
ext;
|
|
210
|
-
_bm25Index = new bm25_1.BM25Index((attrs) => `${attrs.name} ${attrs.signature} ${attrs.docComment} ${attrs.body}`);
|
|
236
|
+
_bm25Index = new bm25_1.BM25Index((attrs) => `${attrs.name} ${attrs.signature} ${attrs.docComment} ${attrs.body.slice(0, defaults_1.BM25_BODY_MAX_CHARS)}`);
|
|
211
237
|
constructor(_graph, embedFns, ext = {}) {
|
|
212
238
|
this._graph = _graph;
|
|
213
239
|
this.embedFns = embedFns;
|
package/dist/graphs/docs.js
CHANGED
|
@@ -21,6 +21,8 @@ const docs_1 = require("../lib/search/docs");
|
|
|
21
21
|
const files_1 = require("../lib/search/files");
|
|
22
22
|
const bm25_1 = require("../lib/search/bm25");
|
|
23
23
|
const embedding_codec_1 = require("../lib/embedding-codec");
|
|
24
|
+
const graph_persistence_1 = require("../lib/graph-persistence");
|
|
25
|
+
const defaults_1 = require("../lib/defaults");
|
|
24
26
|
function createGraph() {
|
|
25
27
|
return new graphology_1.DirectedGraph({ multi: false, allowSelfLoops: false });
|
|
26
28
|
}
|
|
@@ -108,7 +110,7 @@ function getFileMtime(graph, fileId) {
|
|
|
108
110
|
return 0;
|
|
109
111
|
return graph.getNodeAttribute(nodes[0], 'mtime');
|
|
110
112
|
}
|
|
111
|
-
function listFiles(graph, filter, limit =
|
|
113
|
+
function listFiles(graph, filter, limit = defaults_1.LIST_LIMIT_SMALL) {
|
|
112
114
|
const files = new Map();
|
|
113
115
|
const lowerFilter = filter?.toLowerCase();
|
|
114
116
|
graph.forEachNode((_, attrs) => {
|
|
@@ -153,10 +155,10 @@ function loadGraph(graphMemory, fresh = false, embeddingFingerprint) {
|
|
|
153
155
|
if (fresh)
|
|
154
156
|
return graph;
|
|
155
157
|
const file = path_1.default.join(graphMemory, 'docs.json');
|
|
156
|
-
|
|
158
|
+
const data = (0, graph_persistence_1.readJsonWithTmpFallback)(file);
|
|
159
|
+
if (!data)
|
|
157
160
|
return graph;
|
|
158
161
|
try {
|
|
159
|
-
const data = JSON.parse(fs_1.default.readFileSync(file, 'utf-8'));
|
|
160
162
|
const stored = data.embeddingModel;
|
|
161
163
|
if (embeddingFingerprint && stored !== embeddingFingerprint) {
|
|
162
164
|
process.stderr.write(`[graph] Embedding config changed, re-indexing docs graph\n`);
|
|
@@ -20,6 +20,8 @@ const file_lang_1 = require("../graphs/file-lang");
|
|
|
20
20
|
const manager_types_1 = require("../graphs/manager-types");
|
|
21
21
|
const file_index_1 = require("../lib/search/file-index");
|
|
22
22
|
const embedding_codec_1 = require("../lib/embedding-codec");
|
|
23
|
+
const graph_persistence_1 = require("../lib/graph-persistence");
|
|
24
|
+
const defaults_1 = require("../lib/defaults");
|
|
23
25
|
// ---------------------------------------------------------------------------
|
|
24
26
|
// CRUD
|
|
25
27
|
// ---------------------------------------------------------------------------
|
|
@@ -148,7 +150,7 @@ function getFileEntryMtime(graph, filePath) {
|
|
|
148
150
|
* Otherwise returns all file nodes matching the filters.
|
|
149
151
|
*/
|
|
150
152
|
function listAllFiles(graph, options = {}) {
|
|
151
|
-
const { directory, extension, language, filter, limit =
|
|
153
|
+
const { directory, extension, language, filter, limit = defaults_1.LIST_LIMIT_LARGE } = options;
|
|
152
154
|
const lowerFilter = filter?.toLowerCase();
|
|
153
155
|
const results = [];
|
|
154
156
|
if (directory !== undefined) {
|
|
@@ -261,10 +263,10 @@ function loadFileIndexGraph(graphMemory, fresh = false, embeddingFingerprint) {
|
|
|
261
263
|
if (fresh)
|
|
262
264
|
return graph;
|
|
263
265
|
const file = path_1.default.join(graphMemory, 'file-index.json');
|
|
264
|
-
|
|
266
|
+
const data = (0, graph_persistence_1.readJsonWithTmpFallback)(file);
|
|
267
|
+
if (!data)
|
|
265
268
|
return graph;
|
|
266
269
|
try {
|
|
267
|
-
const data = JSON.parse(fs_1.default.readFileSync(file, 'utf-8'));
|
|
268
270
|
const stored = data.embeddingModel;
|
|
269
271
|
if (embeddingFingerprint && stored !== embeddingFingerprint) {
|
|
270
272
|
process.stderr.write(`[file-index] Embedding config changed, re-indexing file index\n`);
|
package/dist/graphs/knowledge.js
CHANGED
|
@@ -29,6 +29,8 @@ const knowledge_1 = require("../lib/search/knowledge");
|
|
|
29
29
|
const bm25_1 = require("../lib/search/bm25");
|
|
30
30
|
const file_mirror_1 = require("../lib/file-mirror");
|
|
31
31
|
const embedding_codec_1 = require("../lib/embedding-codec");
|
|
32
|
+
const graph_persistence_1 = require("../lib/graph-persistence");
|
|
33
|
+
const defaults_1 = require("../lib/defaults");
|
|
32
34
|
const attachment_types_1 = require("../graphs/attachment-types");
|
|
33
35
|
const file_import_1 = require("../lib/file-import");
|
|
34
36
|
// ---------------------------------------------------------------------------
|
|
@@ -162,7 +164,7 @@ function getNote(graph, noteId) {
|
|
|
162
164
|
return { id: noteId, ...graph.getNodeAttributes(noteId) };
|
|
163
165
|
}
|
|
164
166
|
/** List notes with optional filter (substring in title/id) and tag filter. Excludes proxy nodes. */
|
|
165
|
-
function listNotes(graph, filter, tag, limit =
|
|
167
|
+
function listNotes(graph, filter, tag, limit = defaults_1.LIST_LIMIT_SMALL) {
|
|
166
168
|
const lowerFilter = filter?.toLowerCase();
|
|
167
169
|
const lowerTag = tag?.toLowerCase();
|
|
168
170
|
const results = [];
|
|
@@ -179,7 +181,7 @@ function listNotes(graph, filter, tag, limit = 20) {
|
|
|
179
181
|
if (!attrs.tags.some(t => t.toLowerCase() === lowerTag))
|
|
180
182
|
return;
|
|
181
183
|
}
|
|
182
|
-
results.push({ id, title: attrs.title, content: attrs.content.slice(0,
|
|
184
|
+
results.push({ id, title: attrs.title, content: attrs.content.slice(0, defaults_1.CONTENT_PREVIEW_LEN), tags: attrs.tags, updatedAt: attrs.updatedAt });
|
|
183
185
|
});
|
|
184
186
|
return results
|
|
185
187
|
.sort((a, b) => b.updatedAt - a.updatedAt)
|
|
@@ -349,10 +351,10 @@ function loadKnowledgeGraph(graphMemory, fresh = false, embeddingFingerprint) {
|
|
|
349
351
|
if (fresh)
|
|
350
352
|
return graph;
|
|
351
353
|
const file = path_1.default.join(graphMemory, 'knowledge.json');
|
|
352
|
-
|
|
354
|
+
const data = (0, graph_persistence_1.readJsonWithTmpFallback)(file);
|
|
355
|
+
if (!data)
|
|
353
356
|
return graph;
|
|
354
357
|
try {
|
|
355
|
-
const data = JSON.parse(fs_1.default.readFileSync(file, 'utf-8'));
|
|
356
358
|
const stored = data.embeddingModel;
|
|
357
359
|
if (embeddingFingerprint && stored !== embeddingFingerprint) {
|
|
358
360
|
process.stderr.write(`[knowledge-graph] Embedding config changed, re-indexing knowledge graph\n`);
|
|
@@ -601,6 +603,11 @@ class KnowledgeGraphManager {
|
|
|
601
603
|
return null;
|
|
602
604
|
if (!this._graph.hasNode(noteId) || isProxy(this._graph, noteId))
|
|
603
605
|
return null;
|
|
606
|
+
if (data.length > attachment_types_1.MAX_ATTACHMENT_SIZE)
|
|
607
|
+
return null;
|
|
608
|
+
const existing = (0, attachment_types_1.scanAttachments)(path_1.default.join(dir, noteId));
|
|
609
|
+
if (existing.length >= attachment_types_1.MAX_ATTACHMENTS_PER_ENTITY)
|
|
610
|
+
return null;
|
|
604
611
|
const safe = (0, file_mirror_1.sanitizeFilename)(filename);
|
|
605
612
|
if (!safe)
|
|
606
613
|
return null;
|
package/dist/graphs/skill.js
CHANGED
|
@@ -31,6 +31,8 @@ const skills_1 = require("../lib/search/skills");
|
|
|
31
31
|
const bm25_1 = require("../lib/search/bm25");
|
|
32
32
|
const file_mirror_1 = require("../lib/file-mirror");
|
|
33
33
|
const embedding_codec_1 = require("../lib/embedding-codec");
|
|
34
|
+
const graph_persistence_1 = require("../lib/graph-persistence");
|
|
35
|
+
const defaults_1 = require("../lib/defaults");
|
|
34
36
|
const attachment_types_1 = require("../graphs/attachment-types");
|
|
35
37
|
const file_import_1 = require("../lib/file-import");
|
|
36
38
|
// ---------------------------------------------------------------------------
|
|
@@ -290,7 +292,7 @@ function getSkill(graph, skillId) {
|
|
|
290
292
|
}
|
|
291
293
|
/** List skills with optional filters. Excludes proxy nodes. */
|
|
292
294
|
function listSkills(graph, opts = {}) {
|
|
293
|
-
const { source, tag, filter, limit =
|
|
295
|
+
const { source, tag, filter, limit = defaults_1.LIST_LIMIT_LARGE } = opts;
|
|
294
296
|
const lowerFilter = filter?.toLowerCase();
|
|
295
297
|
const lowerTag = tag?.toLowerCase();
|
|
296
298
|
const results = [];
|
|
@@ -310,7 +312,7 @@ function listSkills(graph, opts = {}) {
|
|
|
310
312
|
results.push({
|
|
311
313
|
id,
|
|
312
314
|
title: attrs.title,
|
|
313
|
-
description: attrs.description?.slice(0,
|
|
315
|
+
description: attrs.description?.slice(0, defaults_1.CONTENT_PREVIEW_LEN),
|
|
314
316
|
steps: attrs.steps,
|
|
315
317
|
triggers: attrs.triggers,
|
|
316
318
|
inputHints: attrs.inputHints,
|
|
@@ -493,10 +495,10 @@ function loadSkillGraph(graphMemory, fresh = false, embeddingFingerprint) {
|
|
|
493
495
|
if (fresh)
|
|
494
496
|
return graph;
|
|
495
497
|
const file = path_1.default.join(graphMemory, 'skills.json');
|
|
496
|
-
|
|
498
|
+
const data = (0, graph_persistence_1.readJsonWithTmpFallback)(file);
|
|
499
|
+
if (!data)
|
|
497
500
|
return graph;
|
|
498
501
|
try {
|
|
499
|
-
const data = JSON.parse(fs_1.default.readFileSync(file, 'utf-8'));
|
|
500
502
|
const stored = data.embeddingModel;
|
|
501
503
|
if (embeddingFingerprint && stored !== embeddingFingerprint) {
|
|
502
504
|
process.stderr.write(`[skill-graph] Embedding config changed, re-indexing skill graph\n`);
|
|
@@ -829,10 +831,15 @@ class SkillGraphManager {
|
|
|
829
831
|
return null;
|
|
830
832
|
if (!this._graph.hasNode(skillId) || isProxy(this._graph, skillId))
|
|
831
833
|
return null;
|
|
834
|
+
if (data.length > attachment_types_1.MAX_ATTACHMENT_SIZE)
|
|
835
|
+
return null;
|
|
836
|
+
const entityDir = path_1.default.join(dir, skillId);
|
|
837
|
+
const existing = (0, attachment_types_1.scanAttachments)(entityDir);
|
|
838
|
+
if (existing.length >= attachment_types_1.MAX_ATTACHMENTS_PER_ENTITY)
|
|
839
|
+
return null;
|
|
832
840
|
const safe = (0, file_mirror_1.sanitizeFilename)(filename);
|
|
833
841
|
if (!safe)
|
|
834
842
|
return null;
|
|
835
|
-
const entityDir = path_1.default.join(dir, skillId);
|
|
836
843
|
(0, file_mirror_1.writeAttachment)(dir, skillId, safe, data);
|
|
837
844
|
this.mirrorTracker?.recordWrite(path_1.default.join(entityDir, 'attachments', safe));
|
|
838
845
|
(0, file_mirror_1.mirrorAttachmentEvent)(entityDir, 'add', safe);
|
package/dist/graphs/task.js
CHANGED
|
@@ -31,6 +31,8 @@ const tasks_1 = require("../lib/search/tasks");
|
|
|
31
31
|
const bm25_1 = require("../lib/search/bm25");
|
|
32
32
|
const file_mirror_1 = require("../lib/file-mirror");
|
|
33
33
|
const embedding_codec_1 = require("../lib/embedding-codec");
|
|
34
|
+
const graph_persistence_1 = require("../lib/graph-persistence");
|
|
35
|
+
const defaults_1 = require("../lib/defaults");
|
|
34
36
|
const attachment_types_1 = require("../graphs/attachment-types");
|
|
35
37
|
const file_import_1 = require("../lib/file-import");
|
|
36
38
|
// ---------------------------------------------------------------------------
|
|
@@ -301,7 +303,7 @@ function getTask(graph, taskId) {
|
|
|
301
303
|
}
|
|
302
304
|
/** List tasks with optional filters. Excludes proxy nodes. */
|
|
303
305
|
function listTasks(graph, opts = {}) {
|
|
304
|
-
const { status, priority, tag, filter, assignee, limit =
|
|
306
|
+
const { status, priority, tag, filter, assignee, limit = defaults_1.LIST_LIMIT_LARGE } = opts;
|
|
305
307
|
const lowerFilter = filter?.toLowerCase();
|
|
306
308
|
const lowerTag = tag?.toLowerCase();
|
|
307
309
|
const results = [];
|
|
@@ -325,7 +327,7 @@ function listTasks(graph, opts = {}) {
|
|
|
325
327
|
results.push({
|
|
326
328
|
id,
|
|
327
329
|
title: attrs.title,
|
|
328
|
-
description: attrs.description?.slice(0,
|
|
330
|
+
description: attrs.description?.slice(0, defaults_1.CONTENT_PREVIEW_LEN),
|
|
329
331
|
status: attrs.status,
|
|
330
332
|
priority: attrs.priority,
|
|
331
333
|
tags: attrs.tags,
|
|
@@ -514,10 +516,10 @@ function loadTaskGraph(graphMemory, fresh = false, embeddingFingerprint) {
|
|
|
514
516
|
if (fresh)
|
|
515
517
|
return graph;
|
|
516
518
|
const file = path_1.default.join(graphMemory, 'tasks.json');
|
|
517
|
-
|
|
519
|
+
const data = (0, graph_persistence_1.readJsonWithTmpFallback)(file);
|
|
520
|
+
if (!data)
|
|
518
521
|
return graph;
|
|
519
522
|
try {
|
|
520
|
-
const data = JSON.parse(fs_1.default.readFileSync(file, 'utf-8'));
|
|
521
523
|
const stored = data.embeddingModel;
|
|
522
524
|
if (embeddingFingerprint && stored !== embeddingFingerprint) {
|
|
523
525
|
process.stderr.write(`[task-graph] Embedding config changed, re-indexing task graph\n`);
|
|
@@ -800,10 +802,15 @@ class TaskGraphManager {
|
|
|
800
802
|
return null;
|
|
801
803
|
if (!this._graph.hasNode(taskId) || isProxy(this._graph, taskId))
|
|
802
804
|
return null;
|
|
805
|
+
if (data.length > attachment_types_1.MAX_ATTACHMENT_SIZE)
|
|
806
|
+
return null;
|
|
807
|
+
const entityDir = path_1.default.join(dir, taskId);
|
|
808
|
+
const existing = (0, attachment_types_1.scanAttachments)(entityDir);
|
|
809
|
+
if (existing.length >= attachment_types_1.MAX_ATTACHMENTS_PER_ENTITY)
|
|
810
|
+
return null;
|
|
803
811
|
const safe = (0, file_mirror_1.sanitizeFilename)(filename);
|
|
804
812
|
if (!safe)
|
|
805
813
|
return null;
|
|
806
|
-
const entityDir = path_1.default.join(dir, taskId);
|
|
807
814
|
(0, file_mirror_1.writeAttachment)(dir, taskId, safe, data);
|
|
808
815
|
this.mirrorTracker?.recordWrite(path_1.default.join(entityDir, 'attachments', safe));
|
|
809
816
|
(0, file_mirror_1.mirrorAttachmentEvent)(entityDir, 'add', safe);
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Central named constants for all tunable values.
|
|
4
|
+
* Import from here instead of hardcoding magic numbers.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.WIKI_MAX_DEPTH = exports.DEFAULT_EMBEDDING_CACHE_SIZE = exports.MIRROR_MTIME_TOLERANCE_MS = exports.MIRROR_MAX_ENTRIES = exports.MIRROR_STALE_MS = exports.ERROR_BODY_LIMIT = exports.GRACEFUL_SHUTDOWN_TIMEOUT_MS = exports.SESSION_SWEEP_INTERVAL_MS = exports.RATE_LIMIT_WINDOW_MS = exports.REMOTE_BASE_DELAY_MS = exports.REMOTE_MAX_RETRIES = exports.WS_DEBOUNCE_MS = exports.AUTO_SAVE_INTERVAL_MS = exports.MAX_PASSWORD_LEN = exports.MAX_ATTACHMENT_FILENAME_LEN = exports.MAX_PROJECT_ID_LEN = exports.MAX_LINK_KIND_LEN = exports.MAX_TARGET_NODE_ID_LEN = exports.MAX_SKILL_TRIGGERS_COUNT = exports.MAX_SKILL_TRIGGER_LEN = exports.MAX_SKILL_STEPS_COUNT = exports.MAX_SKILL_STEP_LEN = exports.MAX_ASSIGNEE_LEN = exports.MAX_DESCRIPTION_LEN = exports.MAX_SEARCH_TOP_K = exports.MAX_SEARCH_QUERY_LEN = exports.MAX_TAGS_COUNT = exports.MAX_TAG_LEN = exports.MAX_NOTE_CONTENT_LEN = exports.MAX_TITLE_LEN = exports.INDEXER_PREVIEW_LEN = exports.CONTENT_PREVIEW_LEN = exports.LIST_LIMIT_LARGE = exports.LIST_LIMIT_SMALL = exports.SIGNATURE_MAX_LEN = exports.MAX_UPLOAD_SIZE = exports.MAX_BODY_SIZE = exports.BM25_BODY_MAX_CHARS = exports.FILE_SEARCH_TOP_K = exports.SEARCH_MIN_SCORE_FILES = exports.SEARCH_MIN_SCORE_CODE = exports.SEARCH_MIN_SCORE = exports.SEARCH_BFS_DECAY = exports.SEARCH_MAX_RESULTS = exports.SEARCH_BFS_DEPTH = exports.SEARCH_TOP_K = exports.RRF_K = exports.BM25_IDF_OFFSET = exports.BM25_B = exports.BM25_K1 = void 0;
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Search — BM25, RRF, BFS
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
exports.BM25_K1 = 1.2;
|
|
12
|
+
exports.BM25_B = 0.75;
|
|
13
|
+
exports.BM25_IDF_OFFSET = 0.5;
|
|
14
|
+
exports.RRF_K = 60;
|
|
15
|
+
exports.SEARCH_TOP_K = 5;
|
|
16
|
+
exports.SEARCH_BFS_DEPTH = 1;
|
|
17
|
+
exports.SEARCH_MAX_RESULTS = 20;
|
|
18
|
+
exports.SEARCH_BFS_DECAY = 0.8;
|
|
19
|
+
exports.SEARCH_MIN_SCORE = 0.5;
|
|
20
|
+
exports.SEARCH_MIN_SCORE_CODE = 0.3;
|
|
21
|
+
exports.SEARCH_MIN_SCORE_FILES = 0.3;
|
|
22
|
+
exports.FILE_SEARCH_TOP_K = 10;
|
|
23
|
+
exports.BM25_BODY_MAX_CHARS = 2000;
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Limits — sizes, counts, truncation
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
exports.MAX_BODY_SIZE = 10 * 1024 * 1024; // 10 MB
|
|
28
|
+
exports.MAX_UPLOAD_SIZE = 50 * 1024 * 1024; // 50 MB (multer)
|
|
29
|
+
exports.SIGNATURE_MAX_LEN = 300;
|
|
30
|
+
exports.LIST_LIMIT_SMALL = 20;
|
|
31
|
+
exports.LIST_LIMIT_LARGE = 50;
|
|
32
|
+
exports.CONTENT_PREVIEW_LEN = 500;
|
|
33
|
+
exports.INDEXER_PREVIEW_LEN = 200;
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
// Validation — REST API schema limits
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
exports.MAX_TITLE_LEN = 500;
|
|
38
|
+
exports.MAX_NOTE_CONTENT_LEN = 1_000_000;
|
|
39
|
+
exports.MAX_TAG_LEN = 100;
|
|
40
|
+
exports.MAX_TAGS_COUNT = 100;
|
|
41
|
+
exports.MAX_SEARCH_QUERY_LEN = 2000;
|
|
42
|
+
exports.MAX_SEARCH_TOP_K = 500;
|
|
43
|
+
exports.MAX_DESCRIPTION_LEN = 500_000;
|
|
44
|
+
exports.MAX_ASSIGNEE_LEN = 100;
|
|
45
|
+
exports.MAX_SKILL_STEP_LEN = 10_000;
|
|
46
|
+
exports.MAX_SKILL_STEPS_COUNT = 100;
|
|
47
|
+
exports.MAX_SKILL_TRIGGER_LEN = 500;
|
|
48
|
+
exports.MAX_SKILL_TRIGGERS_COUNT = 50;
|
|
49
|
+
exports.MAX_TARGET_NODE_ID_LEN = 500;
|
|
50
|
+
exports.MAX_LINK_KIND_LEN = 100;
|
|
51
|
+
exports.MAX_PROJECT_ID_LEN = 200;
|
|
52
|
+
exports.MAX_ATTACHMENT_FILENAME_LEN = 255;
|
|
53
|
+
exports.MAX_PASSWORD_LEN = 256;
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// Timing — intervals, retries, timeouts
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
exports.AUTO_SAVE_INTERVAL_MS = 30_000;
|
|
58
|
+
exports.WS_DEBOUNCE_MS = 1000;
|
|
59
|
+
exports.REMOTE_MAX_RETRIES = 3;
|
|
60
|
+
exports.REMOTE_BASE_DELAY_MS = 200;
|
|
61
|
+
exports.RATE_LIMIT_WINDOW_MS = 60_000;
|
|
62
|
+
exports.SESSION_SWEEP_INTERVAL_MS = 60_000;
|
|
63
|
+
exports.GRACEFUL_SHUTDOWN_TIMEOUT_MS = 5000;
|
|
64
|
+
exports.ERROR_BODY_LIMIT = 500;
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// Mirror
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
exports.MIRROR_STALE_MS = 10_000;
|
|
69
|
+
exports.MIRROR_MAX_ENTRIES = 10_000;
|
|
70
|
+
exports.MIRROR_MTIME_TOLERANCE_MS = 100;
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
// Embedder
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
exports.DEFAULT_EMBEDDING_CACHE_SIZE = 10_000;
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
// Parser
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
exports.WIKI_MAX_DEPTH = 10;
|
package/dist/lib/embedder.js
CHANGED
|
@@ -12,10 +12,11 @@ exports.cosineSimilarity = cosineSimilarity;
|
|
|
12
12
|
const transformers_1 = require("@huggingface/transformers");
|
|
13
13
|
const fs_1 = __importDefault(require("fs"));
|
|
14
14
|
const path_1 = __importDefault(require("path"));
|
|
15
|
+
const defaults_1 = require("../lib/defaults");
|
|
15
16
|
// ---------------------------------------------------------------------------
|
|
16
17
|
// LRU cache for embedding vectors (avoids re-computing identical texts)
|
|
17
18
|
// ---------------------------------------------------------------------------
|
|
18
|
-
const DEFAULT_CACHE_SIZE =
|
|
19
|
+
const DEFAULT_CACHE_SIZE = defaults_1.DEFAULT_EMBEDDING_CACHE_SIZE;
|
|
19
20
|
class LruCache {
|
|
20
21
|
maxSize;
|
|
21
22
|
map = new Map();
|
|
@@ -97,26 +98,24 @@ async function loadModel(model, embedding, modelsDir, name = 'default') {
|
|
|
97
98
|
// ---------------------------------------------------------------------------
|
|
98
99
|
// Remote embedding HTTP client
|
|
99
100
|
// ---------------------------------------------------------------------------
|
|
100
|
-
const REMOTE_MAX_RETRIES = 3;
|
|
101
|
-
const REMOTE_BASE_DELAY_MS = 200;
|
|
102
101
|
async function remoteEmbed(url, texts, apiKey) {
|
|
103
102
|
const headers = { 'Content-Type': 'application/json' };
|
|
104
103
|
if (apiKey)
|
|
105
104
|
headers['Authorization'] = `Bearer ${apiKey}`;
|
|
106
105
|
const body = JSON.stringify({ texts });
|
|
107
|
-
for (let attempt = 0; attempt < REMOTE_MAX_RETRIES; attempt++) {
|
|
106
|
+
for (let attempt = 0; attempt < defaults_1.REMOTE_MAX_RETRIES; attempt++) {
|
|
108
107
|
let resp;
|
|
109
108
|
try {
|
|
110
109
|
resp = await fetch(url, { method: 'POST', headers, body });
|
|
111
110
|
}
|
|
112
111
|
catch (err) {
|
|
113
112
|
// Network error — retry
|
|
114
|
-
if (attempt < REMOTE_MAX_RETRIES - 1) {
|
|
115
|
-
const delay = REMOTE_BASE_DELAY_MS * 2 ** attempt;
|
|
113
|
+
if (attempt < defaults_1.REMOTE_MAX_RETRIES - 1) {
|
|
114
|
+
const delay = defaults_1.REMOTE_BASE_DELAY_MS * 2 ** attempt;
|
|
116
115
|
await new Promise(r => setTimeout(r, delay));
|
|
117
116
|
continue;
|
|
118
117
|
}
|
|
119
|
-
throw new Error(`Remote embed network error after ${REMOTE_MAX_RETRIES} attempts: ${err}`);
|
|
118
|
+
throw new Error(`Remote embed network error after ${defaults_1.REMOTE_MAX_RETRIES} attempts: ${err}`);
|
|
120
119
|
}
|
|
121
120
|
if (resp.ok) {
|
|
122
121
|
const data = await resp.json();
|
|
@@ -124,17 +123,17 @@ async function remoteEmbed(url, texts, apiKey) {
|
|
|
124
123
|
}
|
|
125
124
|
// Client errors (4xx) — don't retry
|
|
126
125
|
if (resp.status < 500) {
|
|
127
|
-
const respBody = (await resp.text()).slice(0,
|
|
126
|
+
const respBody = (await resp.text()).slice(0, defaults_1.ERROR_BODY_LIMIT);
|
|
128
127
|
throw new Error(`Remote embed failed (${resp.status}): ${respBody}`);
|
|
129
128
|
}
|
|
130
129
|
// Server errors (5xx) — retry
|
|
131
|
-
if (attempt < REMOTE_MAX_RETRIES - 1) {
|
|
132
|
-
const delay = REMOTE_BASE_DELAY_MS * 2 ** attempt;
|
|
130
|
+
if (attempt < defaults_1.REMOTE_MAX_RETRIES - 1) {
|
|
131
|
+
const delay = defaults_1.REMOTE_BASE_DELAY_MS * 2 ** attempt;
|
|
133
132
|
await new Promise(r => setTimeout(r, delay));
|
|
134
133
|
continue;
|
|
135
134
|
}
|
|
136
|
-
const respBody = (await resp.text()).slice(0,
|
|
137
|
-
throw new Error(`Remote embed failed after ${REMOTE_MAX_RETRIES} attempts (${resp.status}): ${respBody}`);
|
|
135
|
+
const respBody = (await resp.text()).slice(0, defaults_1.ERROR_BODY_LIMIT);
|
|
136
|
+
throw new Error(`Remote embed failed after ${defaults_1.REMOTE_MAX_RETRIES} attempts (${resp.status}): ${respBody}`);
|
|
138
137
|
}
|
|
139
138
|
throw new Error('Remote embed: unreachable');
|
|
140
139
|
}
|
|
@@ -5,17 +5,15 @@
|
|
|
5
5
|
* Backwards compatible: detects old format (number[]) on load.
|
|
6
6
|
*/
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.float32ToBase64 = float32ToBase64;
|
|
9
|
+
exports.base64ToFloat32 = base64ToFloat32;
|
|
8
10
|
exports.compressEmbeddings = compressEmbeddings;
|
|
9
11
|
exports.decompressEmbeddings = decompressEmbeddings;
|
|
10
12
|
const EMBEDDING_FIELDS = ['embedding', 'fileEmbedding'];
|
|
11
13
|
/** Convert a number[] to a Base64-encoded Float32Array. */
|
|
12
14
|
function float32ToBase64(arr) {
|
|
13
15
|
const f32 = new Float32Array(arr);
|
|
14
|
-
|
|
15
|
-
let binary = '';
|
|
16
|
-
for (let i = 0; i < bytes.length; i++)
|
|
17
|
-
binary += String.fromCharCode(bytes[i]);
|
|
18
|
-
return Buffer.from(binary, 'binary').toString('base64');
|
|
16
|
+
return Buffer.from(f32.buffer).toString('base64');
|
|
19
17
|
}
|
|
20
18
|
/** Convert a Base64-encoded Float32Array back to number[]. */
|
|
21
19
|
function base64ToFloat32(b64) {
|