mnueron 0.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/ARCHITECTURE.md +161 -0
- package/INSTALL.md +262 -0
- package/LICENSE +21 -0
- package/README.md +305 -0
- package/dashboard/index.html +838 -0
- package/dist/cli.js +685 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.js +44 -0
- package/dist/config.js.map +1 -0
- package/dist/dashboard/server.js +234 -0
- package/dist/dashboard/server.js.map +1 -0
- package/dist/detectors/claude_code.js +72 -0
- package/dist/detectors/claude_code.js.map +1 -0
- package/dist/detectors/claude_desktop.js +37 -0
- package/dist/detectors/claude_desktop.js.map +1 -0
- package/dist/detectors/cursor.js +36 -0
- package/dist/detectors/cursor.js.map +1 -0
- package/dist/detectors/extra.js +59 -0
- package/dist/detectors/extra.js.map +1 -0
- package/dist/detectors/index.js +14 -0
- package/dist/detectors/index.js.map +1 -0
- package/dist/detectors/json_detector.js +95 -0
- package/dist/detectors/json_detector.js.map +1 -0
- package/dist/detectors/types.js +13 -0
- package/dist/detectors/types.js.map +1 -0
- package/dist/import/claude.js +82 -0
- package/dist/import/claude.js.map +1 -0
- package/dist/import/openai.js +102 -0
- package/dist/import/openai.js.map +1 -0
- package/dist/index.js +77 -0
- package/dist/index.js.map +1 -0
- package/dist/plugins/loader.js +175 -0
- package/dist/plugins/loader.js.map +1 -0
- package/dist/plugins/types.js +24 -0
- package/dist/plugins/types.js.map +1 -0
- package/dist/setup.js +123 -0
- package/dist/setup.js.map +1 -0
- package/dist/store/chunking.js +150 -0
- package/dist/store/chunking.js.map +1 -0
- package/dist/store/embeddings.js +126 -0
- package/dist/store/embeddings.js.map +1 -0
- package/dist/store/local.js +720 -0
- package/dist/store/local.js.map +1 -0
- package/dist/store/provider.js +7 -0
- package/dist/store/provider.js.map +1 -0
- package/dist/store/redactor.js +114 -0
- package/dist/store/redactor.js.map +1 -0
- package/dist/store/remote.js +62 -0
- package/dist/store/remote.js.map +1 -0
- package/dist/tools.js +312 -0
- package/dist/tools.js.map +1 -0
- package/package.json +55 -0
package/dist/setup.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The setup wizard. Detects every supported AI dev tool, configures each one
|
|
3
|
+
* to point at this mnueron installation, and reports back.
|
|
4
|
+
*
|
|
5
|
+
* Usage from CLI:
|
|
6
|
+
* mnueron setup → interactive: configure all detected
|
|
7
|
+
* mnueron setup --yes → non-interactive: just do it
|
|
8
|
+
* mnueron setup --only claude-desktop → just one tool
|
|
9
|
+
* mnueron setup --hosted https://api.engrama.dev --token mn_xxx
|
|
10
|
+
* → configure for hosted mode
|
|
11
|
+
* mnueron setup --uninstall → remove from all tools
|
|
12
|
+
*/
|
|
13
|
+
import { dirname, resolve } from 'node:path';
|
|
14
|
+
import { fileURLToPath } from 'node:url';
|
|
15
|
+
import { allDetectors } from './detectors/index.js';
|
|
16
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
const MNUERON_ENTRY = resolve(HERE, 'index.js');
|
|
18
|
+
const SERVER_NAME = "mnueron"; // or 'engrama' when rebranded
|
|
19
|
+
export async function runSetup(opts = {}) {
|
|
20
|
+
const detectors = allDetectors().filter(d => !opts.only || opts.only.includes(d.id));
|
|
21
|
+
if (opts.uninstall)
|
|
22
|
+
return runUninstall(detectors, opts);
|
|
23
|
+
const entry = buildEntry(opts);
|
|
24
|
+
const reports = [];
|
|
25
|
+
for (const d of detectors) {
|
|
26
|
+
const status = d.status();
|
|
27
|
+
if (!status.installed) {
|
|
28
|
+
reports.push({
|
|
29
|
+
tool: d.displayName, status: 'not-found',
|
|
30
|
+
detail: 'tool not detected on this machine',
|
|
31
|
+
});
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (opts.dryRun) {
|
|
35
|
+
reports.push({
|
|
36
|
+
tool: d.displayName, status: 'skipped',
|
|
37
|
+
detail: `would write to ${status.configPath}`,
|
|
38
|
+
configPath: status.configPath ?? undefined,
|
|
39
|
+
});
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
const result = d.install(SERVER_NAME, entry);
|
|
43
|
+
if (!result.ok) {
|
|
44
|
+
reports.push({ tool: d.displayName, status: 'error', detail: result.message });
|
|
45
|
+
}
|
|
46
|
+
else if (result.changed) {
|
|
47
|
+
reports.push({
|
|
48
|
+
tool: d.displayName,
|
|
49
|
+
status: status.alreadyConfigured ? 'updated' : 'configured',
|
|
50
|
+
detail: result.message,
|
|
51
|
+
configPath: result.configPath,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
reports.push({
|
|
56
|
+
tool: d.displayName, status: 'unchanged',
|
|
57
|
+
detail: 'already up to date',
|
|
58
|
+
configPath: result.configPath,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return reports;
|
|
63
|
+
}
|
|
64
|
+
function runUninstall(detectors, _opts) {
|
|
65
|
+
const reports = [];
|
|
66
|
+
for (const d of detectors) {
|
|
67
|
+
const result = d.uninstall(SERVER_NAME);
|
|
68
|
+
if (!result.ok) {
|
|
69
|
+
reports.push({ tool: d.displayName, status: 'error', detail: result.message });
|
|
70
|
+
}
|
|
71
|
+
else if (result.removed) {
|
|
72
|
+
reports.push({ tool: d.displayName, status: 'uninstalled', detail: result.message });
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
reports.push({ tool: d.displayName, status: 'not-found', detail: result.message });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return reports;
|
|
79
|
+
}
|
|
80
|
+
function buildEntry(opts) {
|
|
81
|
+
const entry = {
|
|
82
|
+
command: 'node',
|
|
83
|
+
args: [MNUERON_ENTRY],
|
|
84
|
+
};
|
|
85
|
+
if (opts.hosted) {
|
|
86
|
+
entry.env = {
|
|
87
|
+
MNUERON_API_URL: opts.hosted.url,
|
|
88
|
+
MNUERON_API_TOKEN: opts.hosted.token,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return entry;
|
|
92
|
+
}
|
|
93
|
+
/** Pretty-print the report for terminal output. */
|
|
94
|
+
export function formatReport(reports) {
|
|
95
|
+
const lines = [];
|
|
96
|
+
const icons = {
|
|
97
|
+
configured: '✓',
|
|
98
|
+
updated: '↻',
|
|
99
|
+
unchanged: '·',
|
|
100
|
+
skipped: '○',
|
|
101
|
+
uninstalled: '✗',
|
|
102
|
+
'not-found': ' ',
|
|
103
|
+
error: '!',
|
|
104
|
+
};
|
|
105
|
+
const found = reports.filter(r => r.status !== 'not-found');
|
|
106
|
+
const missing = reports.filter(r => r.status === 'not-found');
|
|
107
|
+
if (found.length > 0) {
|
|
108
|
+
lines.push('Configured:');
|
|
109
|
+
for (const r of found) {
|
|
110
|
+
lines.push(` ${icons[r.status]} ${r.tool.padEnd(22)} ${r.detail}`);
|
|
111
|
+
if (r.configPath)
|
|
112
|
+
lines.push(` ${r.configPath}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (missing.length > 0) {
|
|
116
|
+
lines.push('');
|
|
117
|
+
lines.push('Not detected:');
|
|
118
|
+
for (const r of missing)
|
|
119
|
+
lines.push(` ${r.tool}`);
|
|
120
|
+
}
|
|
121
|
+
return lines.join('\n');
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content chunking for long memories.
|
|
3
|
+
*
|
|
4
|
+
* Why: a backfilled claude.ai chat can be 300K+ characters. Stored as one
|
|
5
|
+
* memory it's hard to search precisely, hard to display, and blows up
|
|
6
|
+
* agent context windows when retrieved. Splitting it into per-turn chunks
|
|
7
|
+
* (or sliding-window chunks for unstructured long text) gives us:
|
|
8
|
+
*
|
|
9
|
+
* - finer-grained semantic search (each turn embeds independently)
|
|
10
|
+
* - dashboard-friendly previews (one chunk per visible row)
|
|
11
|
+
* - context-safe agent reads (typical chunk ~1-4KB instead of 300KB)
|
|
12
|
+
* - cluster-on-display via parent_ref metadata
|
|
13
|
+
*
|
|
14
|
+
* Strategy:
|
|
15
|
+
* 1. If content has `**User:** ... **Assistant:** ...` (or Claude/ChatGPT)
|
|
16
|
+
* structure with at least 2 turns, split per turn.
|
|
17
|
+
* 2. Otherwise sliding-window split at sentence boundaries with overlap.
|
|
18
|
+
* 3. Either way: each output chunk respects `maxChars`.
|
|
19
|
+
*
|
|
20
|
+
* Output chunks carry their position (`index`) and optional `role`. The
|
|
21
|
+
* caller is responsible for stamping `parent_ref` + `chunk_index` into
|
|
22
|
+
* memory metadata when saving — see LocalProvider.save().
|
|
23
|
+
*/
|
|
24
|
+
/** Don't bother chunking content shorter than this. */
|
|
25
|
+
export const DEFAULT_CHUNK_THRESHOLD = 6000;
|
|
26
|
+
const ROLE_HEADER = /\*\*(User|Assistant|Claude|ChatGPT|Gemini|System|Human):\*\*/gi;
|
|
27
|
+
export function shouldChunk(content, threshold = DEFAULT_CHUNK_THRESHOLD) {
|
|
28
|
+
return (content?.length ?? 0) > threshold;
|
|
29
|
+
}
|
|
30
|
+
export function chunkContent(content, opts = {}) {
|
|
31
|
+
const max = opts.maxChars ?? 4000;
|
|
32
|
+
const overlap = opts.overlapChars ?? 200;
|
|
33
|
+
const minLen = opts.minChars ?? 80;
|
|
34
|
+
if (!content || content.length === 0)
|
|
35
|
+
return [];
|
|
36
|
+
// Strategy 1: try transcript-aware chunking
|
|
37
|
+
const transcript = chunkTranscript(content, max, minLen);
|
|
38
|
+
if (transcript.length >= 2)
|
|
39
|
+
return transcript;
|
|
40
|
+
// Strategy 2: sliding window with sentence-boundary preference
|
|
41
|
+
return chunkSlidingWindow(content, max, overlap, minLen);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Split content on speaker headers (**User:**, **Claude:**, etc.).
|
|
45
|
+
* Returns at least 2 chunks only if there are at least 2 distinct turns;
|
|
46
|
+
* the caller falls back to sliding-window otherwise.
|
|
47
|
+
*/
|
|
48
|
+
function chunkTranscript(content, max, minLen) {
|
|
49
|
+
ROLE_HEADER.lastIndex = 0;
|
|
50
|
+
const headers = [];
|
|
51
|
+
let m;
|
|
52
|
+
while ((m = ROLE_HEADER.exec(content)) !== null) {
|
|
53
|
+
headers.push({ index: m.index, role: m[1].toLowerCase() });
|
|
54
|
+
}
|
|
55
|
+
if (headers.length < 2)
|
|
56
|
+
return [];
|
|
57
|
+
// The text before the first header (title, preamble) becomes chunk 0.
|
|
58
|
+
const chunks = [];
|
|
59
|
+
if (headers[0].index > minLen) {
|
|
60
|
+
chunks.push({
|
|
61
|
+
content: content.slice(0, headers[0].index).trim(),
|
|
62
|
+
index: chunks.length,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
for (let i = 0; i < headers.length; i++) {
|
|
66
|
+
const start = headers[i].index;
|
|
67
|
+
const end = (i + 1 < headers.length) ? headers[i + 1].index : content.length;
|
|
68
|
+
const turn = content.slice(start, end).trim();
|
|
69
|
+
if (!turn)
|
|
70
|
+
continue;
|
|
71
|
+
const role = normalizeRole(headers[i].role);
|
|
72
|
+
if (turn.length <= max) {
|
|
73
|
+
chunks.push({ content: turn, index: chunks.length, role });
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
// Turn longer than max — slide-window subdivide while preserving the role label.
|
|
77
|
+
const sub = chunkSlidingWindow(turn, max, 200, minLen);
|
|
78
|
+
for (const s of sub) {
|
|
79
|
+
chunks.push({ content: s.content, index: chunks.length, role });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return mergeOrphans(chunks, minLen);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Generic chunker: split at sentence boundaries when possible, fall back
|
|
86
|
+
* to hard char limit. Overlapping windows so a search hit near a boundary
|
|
87
|
+
* still has context.
|
|
88
|
+
*/
|
|
89
|
+
function chunkSlidingWindow(content, max, overlap, minLen) {
|
|
90
|
+
if (content.length <= max)
|
|
91
|
+
return [{ content, index: 0 }];
|
|
92
|
+
const chunks = [];
|
|
93
|
+
let start = 0;
|
|
94
|
+
while (start < content.length) {
|
|
95
|
+
let end = Math.min(start + max, content.length);
|
|
96
|
+
if (end < content.length) {
|
|
97
|
+
// Prefer ending at a sentence boundary in the latter half of the window.
|
|
98
|
+
const lookbackFrom = end;
|
|
99
|
+
const minEnd = start + Math.floor(max / 2);
|
|
100
|
+
const candidates = ['. ', '.\n', '! ', '? ', '\n\n'];
|
|
101
|
+
let bestEnd = -1;
|
|
102
|
+
for (const sep of candidates) {
|
|
103
|
+
const i = content.lastIndexOf(sep, lookbackFrom);
|
|
104
|
+
if (i >= minEnd && i + sep.length <= end + 1) {
|
|
105
|
+
bestEnd = Math.max(bestEnd, i + sep.length);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (bestEnd > 0)
|
|
109
|
+
end = bestEnd;
|
|
110
|
+
}
|
|
111
|
+
chunks.push({
|
|
112
|
+
content: content.slice(start, end).trim(),
|
|
113
|
+
index: chunks.length,
|
|
114
|
+
});
|
|
115
|
+
if (end >= content.length)
|
|
116
|
+
break;
|
|
117
|
+
// Pull back by overlap, but never make zero progress.
|
|
118
|
+
start = Math.max(end - overlap, start + Math.max(1, Math.floor(max / 4)));
|
|
119
|
+
}
|
|
120
|
+
return mergeOrphans(chunks, minLen);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* If the last chunk is too short to stand alone, fold it into the
|
|
124
|
+
* previous one. Re-indexes the result.
|
|
125
|
+
*/
|
|
126
|
+
function mergeOrphans(chunks, minLen) {
|
|
127
|
+
if (chunks.length < 2)
|
|
128
|
+
return chunks;
|
|
129
|
+
const out = [];
|
|
130
|
+
for (const c of chunks) {
|
|
131
|
+
if (out.length > 0 && c.content.length < minLen) {
|
|
132
|
+
const prev = out[out.length - 1];
|
|
133
|
+
prev.content = `${prev.content}\n${c.content}`.trim();
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
out.push({ ...c });
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// Re-number
|
|
140
|
+
return out.map((c, i) => ({ ...c, index: i }));
|
|
141
|
+
}
|
|
142
|
+
function normalizeRole(raw) {
|
|
143
|
+
const r = raw.toLowerCase();
|
|
144
|
+
if (r === 'human')
|
|
145
|
+
return 'user';
|
|
146
|
+
if (r === 'claude' || r === 'chatgpt' || r === 'gemini')
|
|
147
|
+
return 'assistant';
|
|
148
|
+
return r;
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=chunking.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunking.js","sourceRoot":"","sources":["../../src/store/chunking.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local embedding generation for hybrid search.
|
|
3
|
+
*
|
|
4
|
+
* Uses Transformers.js + all-MiniLM-L6-v2 (ONNX). ~25MB model, 384-dim
|
|
5
|
+
* normalized vectors. Runs entirely on CPU through onnxruntime-node — no
|
|
6
|
+
* external API calls, no telemetry, no internet after the first download.
|
|
7
|
+
*
|
|
8
|
+
* Lifecycle:
|
|
9
|
+
* - First call to embed() lazily downloads the model from Hugging Face's
|
|
10
|
+
* CDN and caches it at ~/.mnueron/models. Takes ~5–15s the first time
|
|
11
|
+
* on a typical connection. Subsequent process starts are instant
|
|
12
|
+
* (cached locally).
|
|
13
|
+
* - If model load fails (no network on first run, corrupt cache, etc.)
|
|
14
|
+
* `isReady()` stays false and the caller should fall back to FTS5-only.
|
|
15
|
+
*
|
|
16
|
+
* The model is bundle-light (the npm package is ~5MB; the heavy ONNX weights
|
|
17
|
+
* are pulled on demand) so we don't bloat the npm install.
|
|
18
|
+
*/
|
|
19
|
+
import { homedir } from 'node:os';
|
|
20
|
+
import { join } from 'node:path';
|
|
21
|
+
import { mkdirSync } from 'node:fs';
|
|
22
|
+
const MODEL_DIR = join(homedir(), '.mnueron', 'models');
|
|
23
|
+
const MODEL_ID = 'Xenova/all-MiniLM-L6-v2';
|
|
24
|
+
const EMBED_DIM = 384;
|
|
25
|
+
mkdirSync(MODEL_DIR, { recursive: true });
|
|
26
|
+
let pipe = null;
|
|
27
|
+
let pipePromise = null;
|
|
28
|
+
let lastError = null;
|
|
29
|
+
async function loadPipeline() {
|
|
30
|
+
if (pipe)
|
|
31
|
+
return pipe;
|
|
32
|
+
if (pipePromise)
|
|
33
|
+
return pipePromise;
|
|
34
|
+
pipePromise = (async () => {
|
|
35
|
+
// Lazy-import so the heavy module is only paid for when search is actually
|
|
36
|
+
// used. Important for the MCP server, which is spawned cheaply per session.
|
|
37
|
+
const { pipeline, env } = await import('@xenova/transformers');
|
|
38
|
+
// Cache to our own dir so users can easily wipe / inspect / back up.
|
|
39
|
+
// Also disable telemetry — Transformers.js doesn't phone home but we
|
|
40
|
+
// make the intent explicit.
|
|
41
|
+
env.cacheDir = MODEL_DIR;
|
|
42
|
+
env.allowLocalModels = true;
|
|
43
|
+
env.allowRemoteModels = true; // needed for first-run download
|
|
44
|
+
env.useBrowserCache = false;
|
|
45
|
+
try {
|
|
46
|
+
const p = await pipeline('feature-extraction', MODEL_ID, {
|
|
47
|
+
quantized: true, // smaller, faster, no quality loss for retrieval
|
|
48
|
+
});
|
|
49
|
+
pipe = p;
|
|
50
|
+
return p;
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
lastError = e?.message ?? String(e);
|
|
54
|
+
pipePromise = null; // allow retry on next call
|
|
55
|
+
throw e;
|
|
56
|
+
}
|
|
57
|
+
})();
|
|
58
|
+
return pipePromise;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Generate an embedding for a single piece of text. Returns null if the
|
|
62
|
+
* pipeline can't be loaded — caller should treat the search request as
|
|
63
|
+
* keyword-only in that case.
|
|
64
|
+
*/
|
|
65
|
+
export async function embed(text) {
|
|
66
|
+
const trimmed = text?.trim();
|
|
67
|
+
if (!trimmed)
|
|
68
|
+
return null;
|
|
69
|
+
try {
|
|
70
|
+
const p = await loadPipeline();
|
|
71
|
+
const out = await p(trimmed, { pooling: 'mean', normalize: true });
|
|
72
|
+
// out.data is a Float32Array of length 384
|
|
73
|
+
return out.data;
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Batch generation. Faster than calling embed() N times because the model
|
|
81
|
+
* processes them as a single forward pass when possible.
|
|
82
|
+
*/
|
|
83
|
+
export async function embedBatch(texts) {
|
|
84
|
+
const cleaned = texts.map(t => (t ?? '').trim());
|
|
85
|
+
const haveContent = cleaned.some(t => t.length > 0);
|
|
86
|
+
if (!haveContent)
|
|
87
|
+
return cleaned.map(() => null);
|
|
88
|
+
try {
|
|
89
|
+
const p = await loadPipeline();
|
|
90
|
+
const results = [];
|
|
91
|
+
// Transformers.js batches naturally when given an array.
|
|
92
|
+
const out = await p(cleaned, { pooling: 'mean', normalize: true });
|
|
93
|
+
// out.dims = [N, 384]; out.data is a flat Float32Array of length N*384
|
|
94
|
+
const N = cleaned.length;
|
|
95
|
+
for (let i = 0; i < N; i++) {
|
|
96
|
+
if (!cleaned[i]) {
|
|
97
|
+
results.push(null);
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const start = i * EMBED_DIM;
|
|
101
|
+
const slice = new Float32Array(EMBED_DIM);
|
|
102
|
+
slice.set(out.data.slice(start, start + EMBED_DIM));
|
|
103
|
+
results.push(slice);
|
|
104
|
+
}
|
|
105
|
+
return results;
|
|
106
|
+
}
|
|
107
|
+
catch (e) {
|
|
108
|
+
return texts.map(() => null);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
export function isReady() {
|
|
112
|
+
return pipe != null;
|
|
113
|
+
}
|
|
114
|
+
export function getLastError() {
|
|
115
|
+
return lastError;
|
|
116
|
+
}
|
|
117
|
+
export const EMBEDDING_DIM = EMBED_DIM;
|
|
118
|
+
export const EMBEDDING_MODEL = MODEL_ID;
|
|
119
|
+
/**
|
|
120
|
+
* Pre-warm the model. Useful at MCP-server startup to avoid paying the
|
|
121
|
+
* load cost on the first user query. Non-blocking by default.
|
|
122
|
+
*/
|
|
123
|
+
export function preload() {
|
|
124
|
+
return loadPipeline().then(() => undefined).catch(() => undefined);
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=embeddings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embeddings.js","sourceRoot":"","sources":["../../src/store/embeddings.ts"],"names":[],"mappings":"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"}
|