mono-pilot 0.2.9 → 0.2.12
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 +270 -7
- package/dist/src/agents-paths.js +36 -0
- package/dist/src/brief/blocks.js +83 -0
- package/dist/src/brief/defaults.js +60 -0
- package/dist/src/brief/frontmatter.js +53 -0
- package/dist/src/brief/paths.js +10 -0
- package/dist/src/brief/reflection.js +27 -0
- package/dist/src/cli.js +62 -5
- package/dist/src/cluster/bus.js +102 -0
- package/dist/src/cluster/follower.js +137 -0
- package/dist/src/cluster/init.js +182 -0
- package/dist/src/cluster/leader.js +97 -0
- package/dist/src/cluster/log.js +49 -0
- package/dist/src/cluster/protocol.js +34 -0
- package/dist/src/cluster/services/bus.js +243 -0
- package/dist/src/cluster/services/embedding.js +12 -0
- package/dist/src/cluster/socket.js +86 -0
- package/dist/src/cluster/test-bus.js +175 -0
- package/dist/src/cluster_v2/connection-lifecycle.js +31 -0
- package/dist/src/cluster_v2/connection-lifecycle.test.js +24 -0
- package/dist/src/cluster_v2/connection.js +159 -0
- package/dist/src/cluster_v2/connection.test.js +55 -0
- package/dist/src/cluster_v2/events.js +102 -0
- package/dist/src/cluster_v2/index.js +2 -0
- package/dist/src/cluster_v2/observability.js +99 -0
- package/dist/src/cluster_v2/observability.test.js +46 -0
- package/dist/src/cluster_v2/rpc.js +389 -0
- package/dist/src/cluster_v2/rpc.test.js +110 -0
- package/dist/src/cluster_v2/runtime.failover.integration.test.js +156 -0
- package/dist/src/cluster_v2/runtime.js +531 -0
- package/dist/src/cluster_v2/runtime.lease-compromise.integration.test.js +91 -0
- package/dist/src/cluster_v2/runtime.lifecycle.integration.test.js +225 -0
- package/dist/src/cluster_v2/services/bus.integration.test.js +140 -0
- package/dist/src/cluster_v2/services/bus.js +450 -0
- package/dist/src/cluster_v2/services/discord/auth-store.js +82 -0
- package/dist/src/cluster_v2/services/discord/collector.js +569 -0
- package/dist/src/cluster_v2/services/discord/index.js +1 -0
- package/dist/src/cluster_v2/services/discord/oauth.js +87 -0
- package/dist/src/cluster_v2/services/discord/rpc-client.js +325 -0
- package/dist/src/cluster_v2/services/embedding.js +66 -0
- package/dist/src/cluster_v2/services/registry-cache.js +107 -0
- package/dist/src/cluster_v2/services/registry-cache.test.js +66 -0
- package/dist/src/cluster_v2/services/registry.js +36 -0
- package/dist/src/cluster_v2/services/twitter/collector.js +1055 -0
- package/dist/src/cluster_v2/services/twitter/index.js +1 -0
- package/dist/src/config/digest.js +78 -0
- package/dist/src/config/discord.js +143 -0
- package/dist/src/config/image-gen.js +48 -0
- package/dist/src/config/mono-pilot.js +31 -0
- package/dist/src/config/twitter.js +100 -0
- package/dist/src/extensions/cluster.js +311 -0
- package/dist/src/extensions/commands/build-memory.js +76 -0
- package/dist/src/extensions/commands/digest/backfill.js +779 -0
- package/dist/src/extensions/commands/digest/index.js +1133 -0
- package/dist/src/extensions/commands/image-model.js +214 -0
- package/dist/src/extensions/game/bus-injection.js +47 -0
- package/dist/src/extensions/game/identity.js +83 -0
- package/dist/src/extensions/game/mailbox.js +61 -0
- package/dist/src/extensions/game/system-prompt.js +134 -0
- package/dist/src/extensions/game/tools.js +28 -0
- package/dist/src/extensions/lifecycle.js +337 -0
- package/dist/src/extensions/mode-runtime.js +26 -2
- package/dist/src/extensions/mono-game.js +66 -0
- package/dist/src/extensions/mono-pilot.js +100 -18
- package/dist/src/extensions/nvim.js +47 -0
- package/dist/src/extensions/session-hints.js +60 -35
- package/dist/src/extensions/sftp.js +897 -0
- package/dist/src/extensions/status.js +676 -0
- package/dist/src/extensions/system-events.js +478 -0
- package/dist/src/extensions/system-prompt.js +24 -14
- package/dist/src/extensions/user-message.js +94 -50
- package/dist/src/lsp/client.js +235 -0
- package/dist/src/lsp/index.js +165 -0
- package/dist/src/lsp/runtime.js +67 -0
- package/dist/src/lsp/server.js +242 -0
- package/dist/src/mcp/config.js +112 -0
- package/dist/src/{utils/mcp-client.js → mcp/protocol.js} +1 -100
- package/dist/src/mcp/servers.js +90 -0
- package/dist/src/memory/build-memory.js +103 -0
- package/dist/src/memory/config/defaults.js +55 -0
- package/dist/src/memory/config/loader.js +29 -0
- package/dist/src/memory/config/paths.js +9 -0
- package/dist/src/memory/config/resolve.js +90 -0
- package/dist/src/memory/config/types.js +1 -0
- package/dist/src/memory/embeddings/batch-runner.js +39 -0
- package/dist/src/memory/embeddings/cache.js +47 -0
- package/dist/src/memory/embeddings/chunk-limits.js +26 -0
- package/dist/src/memory/embeddings/input-limits.js +48 -0
- package/dist/src/memory/embeddings/local.js +108 -0
- package/dist/src/memory/embeddings/types.js +1 -0
- package/dist/src/memory/index-manager.js +552 -0
- package/dist/src/memory/indexing/embeddings.js +67 -0
- package/dist/src/memory/indexing/files.js +180 -0
- package/dist/src/memory/indexing/index-file.js +105 -0
- package/dist/src/memory/log.js +38 -0
- package/dist/src/memory/paths.js +15 -0
- package/dist/src/memory/runtime/index.js +299 -0
- package/dist/src/memory/runtime/thread.js +116 -0
- package/dist/src/memory/search/fts.js +57 -0
- package/dist/src/memory/search/hybrid.js +50 -0
- package/dist/src/memory/search/text.js +30 -0
- package/dist/src/memory/search/vector.js +43 -0
- package/dist/src/memory/session/content-hash.js +7 -0
- package/dist/src/memory/session/entry.js +33 -0
- package/dist/src/memory/session/flush-policy.js +34 -0
- package/dist/src/memory/session/hook.js +191 -0
- package/dist/src/memory/session/paths.js +15 -0
- package/dist/src/memory/session/session-reader.js +88 -0
- package/dist/src/memory/session/transcript/content-hash.js +7 -0
- package/dist/src/memory/session/transcript/entry.js +28 -0
- package/dist/src/memory/session/transcript/flush.js +56 -0
- package/dist/src/memory/session/transcript/paths.js +28 -0
- package/dist/src/memory/session/transcript/reader.js +112 -0
- package/dist/src/memory/session/transcript/state.js +31 -0
- package/dist/src/memory/store/schema.js +89 -0
- package/dist/src/memory/store/sqlite.js +89 -0
- package/dist/src/memory/types.js +1 -0
- package/dist/src/memory/warm.js +25 -0
- package/dist/src/rules/discovery.js +41 -0
- package/dist/{tools → src/tools}/README.md +29 -3
- package/dist/{tools → src/tools}/apply-patch-description.md +8 -2
- package/dist/{tools → src/tools}/apply-patch.js +174 -104
- package/dist/{tools → src/tools}/apply-patch.test.js +52 -1
- package/dist/{tools/ask-question.js → src/tools/ask-user-question.js} +3 -3
- package/dist/src/tools/ast-grep.js +357 -0
- package/dist/src/tools/brief-write.js +122 -0
- package/dist/src/tools/bus-send.js +100 -0
- package/dist/{tools → src/tools}/call-mcp-tool.js +40 -124
- package/dist/src/tools/codex-apply-patch-description.md +52 -0
- package/dist/src/tools/codex-apply-patch.js +540 -0
- package/dist/{tools → src/tools}/delete.js +24 -0
- package/dist/src/tools/exit-plan-mode.js +83 -0
- package/dist/{tools → src/tools}/fetch-mcp-resource.js +56 -100
- package/dist/src/tools/generate-image.js +567 -0
- package/dist/{tools → src/tools}/glob.js +55 -1
- package/dist/{tools → src/tools}/list-mcp-resources.js +46 -57
- package/dist/{tools → src/tools}/list-mcp-tools.js +52 -63
- package/dist/src/tools/ls.js +48 -0
- package/dist/src/tools/lsp-diagnostics.js +67 -0
- package/dist/src/tools/lsp-symbols.js +54 -0
- package/dist/src/tools/mailbox.js +85 -0
- package/dist/src/tools/memory-get.js +90 -0
- package/dist/src/tools/memory-search.js +180 -0
- package/dist/{tools → src/tools}/plan-mode-reminder.md +3 -4
- package/dist/{tools → src/tools}/read-file.js +8 -19
- package/dist/{tools → src/tools}/rg.js +10 -20
- package/dist/{tools → src/tools}/shell.js +19 -42
- package/dist/{tools → src/tools}/subagent.js +255 -6
- package/dist/{tools → src/tools}/switch-mode.js +37 -6
- package/dist/{tools → src/tools}/web-fetch.js +105 -7
- package/dist/{tools → src/tools}/web-search.js +29 -1
- package/package.json +21 -9
- /package/dist/{tools → src/tools}/ask-mode-reminder.md +0 -0
- /package/dist/{tools → src/tools}/rg.test.js +0 -0
- /package/dist/{tools → src/tools}/semantic-search-description.md +0 -0
- /package/dist/{tools → src/tools}/semantic-search.js +0 -0
- /package/dist/{tools → src/tools}/shell-description.md +0 -0
- /package/dist/{tools → src/tools}/subagent-description.md +0 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
export const memorySearchDefaults = {
|
|
4
|
+
enabled: true,
|
|
5
|
+
provider: "local",
|
|
6
|
+
sources: ["memory"],
|
|
7
|
+
extraPaths: [],
|
|
8
|
+
local: {
|
|
9
|
+
modelPath: "hf:gpustack/bge-m3-GGUF/bge-m3-Q8_0.gguf",
|
|
10
|
+
modelCacheDir: join(homedir(), ".mono-pilot", "models"),
|
|
11
|
+
},
|
|
12
|
+
store: {
|
|
13
|
+
vector: {
|
|
14
|
+
enabled: true,
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
chunking: {
|
|
18
|
+
tokens: 400,
|
|
19
|
+
overlap: 80,
|
|
20
|
+
},
|
|
21
|
+
query: {
|
|
22
|
+
maxResults: 6,
|
|
23
|
+
minScore: 0.35,
|
|
24
|
+
hybrid: {
|
|
25
|
+
enabled: true,
|
|
26
|
+
vectorWeight: 0.7,
|
|
27
|
+
textWeight: 0.3,
|
|
28
|
+
candidateMultiplier: 4,
|
|
29
|
+
mmr: {
|
|
30
|
+
enabled: false,
|
|
31
|
+
lambda: 0.7,
|
|
32
|
+
},
|
|
33
|
+
temporalDecay: {
|
|
34
|
+
enabled: false,
|
|
35
|
+
halfLifeDays: 30,
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
flush: {
|
|
40
|
+
onSessionSwitch: true,
|
|
41
|
+
onSessionCompact: true,
|
|
42
|
+
deltaBytes: 100_000,
|
|
43
|
+
deltaMessages: 50,
|
|
44
|
+
},
|
|
45
|
+
sync: {
|
|
46
|
+
onSessionStart: true,
|
|
47
|
+
onSearch: true,
|
|
48
|
+
watch: true,
|
|
49
|
+
watchDebounceMs: 1500,
|
|
50
|
+
intervalMinutes: 10,
|
|
51
|
+
},
|
|
52
|
+
cache: {
|
|
53
|
+
enabled: true,
|
|
54
|
+
},
|
|
55
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { resolveMemorySearchConfig } from "./resolve.js";
|
|
4
|
+
import { getUserConfigPath } from "./paths.js";
|
|
5
|
+
function isRecord(value) {
|
|
6
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7
|
+
}
|
|
8
|
+
export async function loadMonoPilotConfig() {
|
|
9
|
+
const configPath = getUserConfigPath();
|
|
10
|
+
if (!existsSync(configPath))
|
|
11
|
+
return undefined;
|
|
12
|
+
const raw = await readFile(configPath, "utf-8");
|
|
13
|
+
let parsed;
|
|
14
|
+
try {
|
|
15
|
+
parsed = JSON.parse(raw);
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
19
|
+
throw new Error(`Invalid JSON in mono-pilot config: ${message}`);
|
|
20
|
+
}
|
|
21
|
+
if (!isRecord(parsed)) {
|
|
22
|
+
throw new Error("MonoPilot config root must be a JSON object.");
|
|
23
|
+
}
|
|
24
|
+
return parsed;
|
|
25
|
+
}
|
|
26
|
+
export async function loadResolvedMemorySearchConfig() {
|
|
27
|
+
const config = await loadMonoPilotConfig();
|
|
28
|
+
return resolveMemorySearchConfig(config?.memorySearch);
|
|
29
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
export const MONO_PILOT_CONFIG_FILENAME = "config.json";
|
|
4
|
+
export function getUserConfigDir() {
|
|
5
|
+
return join(homedir(), ".mono-pilot");
|
|
6
|
+
}
|
|
7
|
+
export function getUserConfigPath() {
|
|
8
|
+
return resolve(getUserConfigDir(), MONO_PILOT_CONFIG_FILENAME);
|
|
9
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { memorySearchDefaults } from "./defaults.js";
|
|
2
|
+
export function resolveMemorySearchConfig(overrides) {
|
|
3
|
+
if (!overrides) {
|
|
4
|
+
return {
|
|
5
|
+
...memorySearchDefaults,
|
|
6
|
+
sources: [...memorySearchDefaults.sources],
|
|
7
|
+
extraPaths: [...memorySearchDefaults.extraPaths],
|
|
8
|
+
chunking: { ...memorySearchDefaults.chunking },
|
|
9
|
+
query: {
|
|
10
|
+
...memorySearchDefaults.query,
|
|
11
|
+
hybrid: {
|
|
12
|
+
...memorySearchDefaults.query.hybrid,
|
|
13
|
+
mmr: { ...memorySearchDefaults.query.hybrid.mmr },
|
|
14
|
+
temporalDecay: { ...memorySearchDefaults.query.hybrid.temporalDecay },
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
flush: {
|
|
18
|
+
...memorySearchDefaults.flush,
|
|
19
|
+
},
|
|
20
|
+
sync: {
|
|
21
|
+
...memorySearchDefaults.sync,
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
const chunking = overrides.chunking;
|
|
26
|
+
const query = overrides.query;
|
|
27
|
+
const hybrid = query?.hybrid;
|
|
28
|
+
const flush = overrides.flush;
|
|
29
|
+
const sync = overrides.sync;
|
|
30
|
+
const store = overrides.store;
|
|
31
|
+
const vector = store?.vector;
|
|
32
|
+
const cache = overrides.cache;
|
|
33
|
+
return {
|
|
34
|
+
enabled: overrides.enabled ?? memorySearchDefaults.enabled,
|
|
35
|
+
provider: overrides.provider ?? memorySearchDefaults.provider,
|
|
36
|
+
sources: overrides.sources ? [...overrides.sources] : [...memorySearchDefaults.sources],
|
|
37
|
+
extraPaths: overrides.extraPaths ? [...overrides.extraPaths] : [...memorySearchDefaults.extraPaths],
|
|
38
|
+
local: {
|
|
39
|
+
modelPath: overrides.local?.modelPath ?? memorySearchDefaults.local.modelPath,
|
|
40
|
+
modelCacheDir: overrides.local?.modelCacheDir ?? memorySearchDefaults.local.modelCacheDir,
|
|
41
|
+
},
|
|
42
|
+
store: {
|
|
43
|
+
vector: {
|
|
44
|
+
enabled: vector?.enabled ?? memorySearchDefaults.store.vector.enabled,
|
|
45
|
+
extensionPath: vector?.extensionPath ?? memorySearchDefaults.store.vector.extensionPath,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
chunking: {
|
|
49
|
+
tokens: chunking?.tokens ?? memorySearchDefaults.chunking.tokens,
|
|
50
|
+
overlap: chunking?.overlap ?? memorySearchDefaults.chunking.overlap,
|
|
51
|
+
},
|
|
52
|
+
query: {
|
|
53
|
+
maxResults: query?.maxResults ?? memorySearchDefaults.query.maxResults,
|
|
54
|
+
minScore: query?.minScore ?? memorySearchDefaults.query.minScore,
|
|
55
|
+
hybrid: {
|
|
56
|
+
enabled: hybrid?.enabled ?? memorySearchDefaults.query.hybrid.enabled,
|
|
57
|
+
vectorWeight: hybrid?.vectorWeight ?? memorySearchDefaults.query.hybrid.vectorWeight,
|
|
58
|
+
textWeight: hybrid?.textWeight ?? memorySearchDefaults.query.hybrid.textWeight,
|
|
59
|
+
candidateMultiplier: hybrid?.candidateMultiplier ?? memorySearchDefaults.query.hybrid.candidateMultiplier,
|
|
60
|
+
mmr: {
|
|
61
|
+
enabled: hybrid?.mmr?.enabled ?? memorySearchDefaults.query.hybrid.mmr.enabled,
|
|
62
|
+
lambda: hybrid?.mmr?.lambda ?? memorySearchDefaults.query.hybrid.mmr.lambda,
|
|
63
|
+
},
|
|
64
|
+
temporalDecay: {
|
|
65
|
+
enabled: hybrid?.temporalDecay?.enabled ??
|
|
66
|
+
memorySearchDefaults.query.hybrid.temporalDecay.enabled,
|
|
67
|
+
halfLifeDays: hybrid?.temporalDecay?.halfLifeDays ??
|
|
68
|
+
memorySearchDefaults.query.hybrid.temporalDecay.halfLifeDays,
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
flush: {
|
|
73
|
+
onSessionSwitch: flush?.onSessionSwitch ?? memorySearchDefaults.flush.onSessionSwitch,
|
|
74
|
+
onSessionCompact: flush?.onSessionCompact ?? memorySearchDefaults.flush.onSessionCompact,
|
|
75
|
+
deltaBytes: flush?.deltaBytes ?? memorySearchDefaults.flush.deltaBytes,
|
|
76
|
+
deltaMessages: flush?.deltaMessages ?? memorySearchDefaults.flush.deltaMessages,
|
|
77
|
+
},
|
|
78
|
+
sync: {
|
|
79
|
+
onSessionStart: sync?.onSessionStart ?? memorySearchDefaults.sync.onSessionStart,
|
|
80
|
+
onSearch: sync?.onSearch ?? memorySearchDefaults.sync.onSearch,
|
|
81
|
+
watch: sync?.watch ?? memorySearchDefaults.sync.watch,
|
|
82
|
+
watchDebounceMs: sync?.watchDebounceMs ?? memorySearchDefaults.sync.watchDebounceMs,
|
|
83
|
+
intervalMinutes: sync?.intervalMinutes ?? memorySearchDefaults.sync.intervalMinutes,
|
|
84
|
+
},
|
|
85
|
+
cache: {
|
|
86
|
+
enabled: cache?.enabled ?? memorySearchDefaults.cache.enabled,
|
|
87
|
+
maxEntries: cache?.maxEntries ?? memorySearchDefaults.cache.maxEntries,
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export async function runEmbeddingBatches(params) {
|
|
2
|
+
const { items } = params;
|
|
3
|
+
if (items.length === 0)
|
|
4
|
+
return [];
|
|
5
|
+
const batches = splitIntoBatches(items, Math.max(1, params.maxBatchSize));
|
|
6
|
+
const results = Array.from({ length: items.length });
|
|
7
|
+
const tasks = batches.map((batch, batchIndex) => async () => {
|
|
8
|
+
const embeddings = await params.runBatch(batch);
|
|
9
|
+
const start = batchIndex * params.maxBatchSize;
|
|
10
|
+
for (let i = 0; i < batch.length; i += 1) {
|
|
11
|
+
results[start + i] = embeddings[i] ?? [];
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
await runWithConcurrency(tasks, Math.max(1, params.concurrency));
|
|
15
|
+
return results;
|
|
16
|
+
}
|
|
17
|
+
function splitIntoBatches(items, maxBatchSize) {
|
|
18
|
+
const batches = [];
|
|
19
|
+
for (let i = 0; i < items.length; i += maxBatchSize) {
|
|
20
|
+
batches.push(items.slice(i, i + maxBatchSize));
|
|
21
|
+
}
|
|
22
|
+
return batches;
|
|
23
|
+
}
|
|
24
|
+
async function runWithConcurrency(tasks, limit) {
|
|
25
|
+
if (tasks.length === 0)
|
|
26
|
+
return;
|
|
27
|
+
const resolvedLimit = Math.max(1, Math.min(limit, tasks.length));
|
|
28
|
+
let next = 0;
|
|
29
|
+
const workers = Array.from({ length: resolvedLimit }, async () => {
|
|
30
|
+
while (true) {
|
|
31
|
+
const index = next;
|
|
32
|
+
next += 1;
|
|
33
|
+
if (index >= tasks.length)
|
|
34
|
+
return;
|
|
35
|
+
await tasks[index]();
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
await Promise.all(workers);
|
|
39
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { EMBEDDING_CACHE_TABLE } from "../store/schema.js";
|
|
2
|
+
export function readEmbeddingCache(params) {
|
|
3
|
+
const row = params.db
|
|
4
|
+
.prepare(`SELECT embedding FROM ${EMBEDDING_CACHE_TABLE} WHERE provider = ? AND model = ? AND provider_key = ? AND hash = ?`)
|
|
5
|
+
.get(params.provider, params.model, params.providerKey, params.hash);
|
|
6
|
+
if (!row?.embedding)
|
|
7
|
+
return null;
|
|
8
|
+
try {
|
|
9
|
+
const parsed = JSON.parse(row.embedding);
|
|
10
|
+
return Array.isArray(parsed) ? parsed : null;
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export function writeEmbeddingCache(params) {
|
|
17
|
+
const now = Date.now();
|
|
18
|
+
params.db
|
|
19
|
+
.prepare(`INSERT INTO ${EMBEDDING_CACHE_TABLE} (provider, model, provider_key, hash, embedding, dims, updated_at)
|
|
20
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
21
|
+
ON CONFLICT(provider, model, provider_key, hash) DO UPDATE SET
|
|
22
|
+
embedding=excluded.embedding,
|
|
23
|
+
dims=excluded.dims,
|
|
24
|
+
updated_at=excluded.updated_at`)
|
|
25
|
+
.run(params.provider, params.model, params.providerKey, params.hash, JSON.stringify(params.embedding), params.embedding.length, now);
|
|
26
|
+
}
|
|
27
|
+
export function pruneEmbeddingCache(params) {
|
|
28
|
+
const maxEntries = params.maxEntries;
|
|
29
|
+
if (!maxEntries || maxEntries <= 0)
|
|
30
|
+
return;
|
|
31
|
+
const countRow = params.db
|
|
32
|
+
.prepare(`SELECT COUNT(*) AS count FROM ${EMBEDDING_CACHE_TABLE} WHERE provider = ? AND model = ? AND provider_key = ?`)
|
|
33
|
+
.get(params.provider, params.model, params.providerKey);
|
|
34
|
+
const count = countRow?.count ?? 0;
|
|
35
|
+
if (count <= maxEntries)
|
|
36
|
+
return;
|
|
37
|
+
const toDelete = count - maxEntries;
|
|
38
|
+
params.db
|
|
39
|
+
.prepare(`DELETE FROM ${EMBEDDING_CACHE_TABLE}
|
|
40
|
+
WHERE rowid IN (
|
|
41
|
+
SELECT rowid FROM ${EMBEDDING_CACHE_TABLE}
|
|
42
|
+
WHERE provider = ? AND model = ? AND provider_key = ?
|
|
43
|
+
ORDER BY updated_at ASC
|
|
44
|
+
LIMIT ?
|
|
45
|
+
)`)
|
|
46
|
+
.run(params.provider, params.model, params.providerKey, toDelete);
|
|
47
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { estimateUtf8Bytes, splitTextToUtf8ByteLimit } from "./input-limits.js";
|
|
2
|
+
import { hashText } from "../indexing/files.js";
|
|
3
|
+
export function enforceEmbeddingMaxInputTokens(provider, chunks, hardMaxInputTokens) {
|
|
4
|
+
const providerMaxInputTokens = typeof provider.maxInputTokens === "number"
|
|
5
|
+
? provider.maxInputTokens
|
|
6
|
+
: 2048;
|
|
7
|
+
const maxInputTokens = typeof hardMaxInputTokens === "number" && hardMaxInputTokens > 0
|
|
8
|
+
? Math.min(providerMaxInputTokens, hardMaxInputTokens)
|
|
9
|
+
: providerMaxInputTokens;
|
|
10
|
+
const out = [];
|
|
11
|
+
for (const chunk of chunks) {
|
|
12
|
+
if (estimateUtf8Bytes(chunk.text) <= maxInputTokens) {
|
|
13
|
+
out.push(chunk);
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
for (const text of splitTextToUtf8ByteLimit(chunk.text, maxInputTokens)) {
|
|
17
|
+
out.push({
|
|
18
|
+
startLine: chunk.startLine,
|
|
19
|
+
endLine: chunk.endLine,
|
|
20
|
+
text,
|
|
21
|
+
hash: hashText(text),
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Helpers for enforcing embedding model input size limits.
|
|
2
|
+
// Tokenizers operate over bytes; token_count <= utf8_byte_length.
|
|
3
|
+
export function estimateUtf8Bytes(text) {
|
|
4
|
+
if (!text)
|
|
5
|
+
return 0;
|
|
6
|
+
return Buffer.byteLength(text, "utf8");
|
|
7
|
+
}
|
|
8
|
+
export function splitTextToUtf8ByteLimit(text, maxUtf8Bytes) {
|
|
9
|
+
if (maxUtf8Bytes <= 0)
|
|
10
|
+
return [text];
|
|
11
|
+
if (estimateUtf8Bytes(text) <= maxUtf8Bytes)
|
|
12
|
+
return [text];
|
|
13
|
+
const parts = [];
|
|
14
|
+
let cursor = 0;
|
|
15
|
+
while (cursor < text.length) {
|
|
16
|
+
let low = cursor + 1;
|
|
17
|
+
let high = Math.min(text.length, cursor + maxUtf8Bytes);
|
|
18
|
+
let best = cursor;
|
|
19
|
+
while (low <= high) {
|
|
20
|
+
const mid = Math.floor((low + high) / 2);
|
|
21
|
+
const bytes = estimateUtf8Bytes(text.slice(cursor, mid));
|
|
22
|
+
if (bytes <= maxUtf8Bytes) {
|
|
23
|
+
best = mid;
|
|
24
|
+
low = mid + 1;
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
high = mid - 1;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (best <= cursor) {
|
|
31
|
+
best = Math.min(text.length, cursor + 1);
|
|
32
|
+
}
|
|
33
|
+
if (best < text.length &&
|
|
34
|
+
best > cursor &&
|
|
35
|
+
text.charCodeAt(best - 1) >= 0xd800 &&
|
|
36
|
+
text.charCodeAt(best - 1) <= 0xdbff &&
|
|
37
|
+
text.charCodeAt(best) >= 0xdc00 &&
|
|
38
|
+
text.charCodeAt(best) <= 0xdfff) {
|
|
39
|
+
best -= 1;
|
|
40
|
+
}
|
|
41
|
+
const part = text.slice(cursor, best);
|
|
42
|
+
if (!part)
|
|
43
|
+
break;
|
|
44
|
+
parts.push(part);
|
|
45
|
+
cursor = best;
|
|
46
|
+
}
|
|
47
|
+
return parts;
|
|
48
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
function normalizeEmbedding(vector) {
|
|
4
|
+
const sanitized = vector.map((value) => (Number.isFinite(value) ? value : 0));
|
|
5
|
+
const magnitude = Math.sqrt(sanitized.reduce((sum, value) => sum + value * value, 0));
|
|
6
|
+
if (magnitude < 1e-10) {
|
|
7
|
+
return sanitized;
|
|
8
|
+
}
|
|
9
|
+
return sanitized.map((value) => value / magnitude);
|
|
10
|
+
}
|
|
11
|
+
export async function createLocalEmbeddingProvider(params) {
|
|
12
|
+
const modelPath = params.modelPath?.trim() || "hf:gpustack/bge-m3-GGUF/bge-m3-Q8_0.gguf";
|
|
13
|
+
const modelCacheDir = params.modelCacheDir?.trim() || join(homedir(), ".mono-pilot", "models");
|
|
14
|
+
const nodeLlamaCpp = await import("node-llama-cpp");
|
|
15
|
+
const { getLlama, resolveModelFile } = nodeLlamaCpp;
|
|
16
|
+
const logLevel = nodeLlamaCpp.LlamaLogLevel?.error;
|
|
17
|
+
let llama = null;
|
|
18
|
+
let model = null;
|
|
19
|
+
let context = null;
|
|
20
|
+
let closing = false;
|
|
21
|
+
let inFlightRequests = 0;
|
|
22
|
+
let disposePromise = null;
|
|
23
|
+
const idleWaiters = new Set();
|
|
24
|
+
const notifyIfIdle = () => {
|
|
25
|
+
if (inFlightRequests !== 0) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
for (const resolve of idleWaiters) {
|
|
29
|
+
resolve();
|
|
30
|
+
}
|
|
31
|
+
idleWaiters.clear();
|
|
32
|
+
};
|
|
33
|
+
const waitForIdle = async () => {
|
|
34
|
+
if (inFlightRequests === 0) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
await new Promise((resolve) => {
|
|
38
|
+
idleWaiters.add(resolve);
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
const runWithInFlightTracking = async (operation) => {
|
|
42
|
+
if (closing) {
|
|
43
|
+
throw new Error("embedding provider is closing");
|
|
44
|
+
}
|
|
45
|
+
inFlightRequests += 1;
|
|
46
|
+
try {
|
|
47
|
+
return await operation();
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
inFlightRequests = Math.max(0, inFlightRequests - 1);
|
|
51
|
+
notifyIfIdle();
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const ensureContext = async () => {
|
|
55
|
+
if (!llama) {
|
|
56
|
+
llama = await getLlama({ logLevel: logLevel ?? "error" });
|
|
57
|
+
}
|
|
58
|
+
if (!model) {
|
|
59
|
+
const resolved = await resolveModelFile(modelPath, modelCacheDir);
|
|
60
|
+
model = await llama.loadModel({ modelPath: resolved });
|
|
61
|
+
}
|
|
62
|
+
if (!context) {
|
|
63
|
+
context = await model.createEmbeddingContext();
|
|
64
|
+
}
|
|
65
|
+
return context;
|
|
66
|
+
};
|
|
67
|
+
return {
|
|
68
|
+
id: "local",
|
|
69
|
+
model: modelPath,
|
|
70
|
+
embedQuery: async (text) => {
|
|
71
|
+
return runWithInFlightTracking(async () => {
|
|
72
|
+
const ctx = await ensureContext();
|
|
73
|
+
const embedding = await ctx.getEmbeddingFor(text);
|
|
74
|
+
return normalizeEmbedding(Array.from(embedding.vector));
|
|
75
|
+
});
|
|
76
|
+
},
|
|
77
|
+
embedBatch: async (texts) => {
|
|
78
|
+
return runWithInFlightTracking(async () => {
|
|
79
|
+
const ctx = await ensureContext();
|
|
80
|
+
const embeddings = await Promise.all(texts.map(async (text) => {
|
|
81
|
+
const embedding = await ctx.getEmbeddingFor(text);
|
|
82
|
+
return normalizeEmbedding(Array.from(embedding.vector));
|
|
83
|
+
}));
|
|
84
|
+
return embeddings;
|
|
85
|
+
});
|
|
86
|
+
},
|
|
87
|
+
dispose: async () => {
|
|
88
|
+
if (disposePromise) {
|
|
89
|
+
await disposePromise;
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
closing = true;
|
|
93
|
+
disposePromise = (async () => {
|
|
94
|
+
await waitForIdle();
|
|
95
|
+
if (context) {
|
|
96
|
+
await context.dispose();
|
|
97
|
+
context = null;
|
|
98
|
+
}
|
|
99
|
+
if (model) {
|
|
100
|
+
await model.dispose();
|
|
101
|
+
model = null;
|
|
102
|
+
}
|
|
103
|
+
llama = null;
|
|
104
|
+
})();
|
|
105
|
+
await disposePromise;
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|