unforgit 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-CKUDYQYP.js → chunk-TGDR7Y7T.js} +161 -18
- package/dist/chunk-TGDR7Y7T.js.map +1 -0
- package/dist/index.js +76 -25
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-CKUDYQYP.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
getDbPath,
|
|
18
18
|
getTemplate,
|
|
19
19
|
isInitialized,
|
|
20
|
+
isOpenAIConfigured,
|
|
20
21
|
loadConfig,
|
|
21
22
|
mergeAndRank,
|
|
22
23
|
parseConfidence,
|
|
@@ -24,12 +25,13 @@ import {
|
|
|
24
25
|
parseThreshold,
|
|
25
26
|
parseTtl,
|
|
26
27
|
persistReviewableSuggestions,
|
|
28
|
+
resolveEmbeddingProvider,
|
|
27
29
|
resolveLifecycleConfig,
|
|
28
30
|
resolveVisibility,
|
|
29
31
|
runLocalLifecycleMaintenance,
|
|
30
32
|
saveConfig,
|
|
31
33
|
validateMemoryType
|
|
32
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-TGDR7Y7T.js";
|
|
33
35
|
|
|
34
36
|
// src/index.ts
|
|
35
37
|
import { Command as Command31 } from "commander";
|
|
@@ -2922,6 +2924,7 @@ function buildBackfillJsonPayload(options) {
|
|
|
2922
2924
|
return {
|
|
2923
2925
|
dryRun: options.dryRun,
|
|
2924
2926
|
model: options.model,
|
|
2927
|
+
...options.provider ? { provider: options.provider } : {},
|
|
2925
2928
|
planned: options.planned,
|
|
2926
2929
|
processed: options.processed,
|
|
2927
2930
|
errors: failures.length,
|
|
@@ -2938,16 +2941,21 @@ function buildBackfillJsonPayload(options) {
|
|
|
2938
2941
|
};
|
|
2939
2942
|
}
|
|
2940
2943
|
var embeddingsCommand = new Command24("embeddings").description("Manage memory embeddings for semantic search");
|
|
2941
|
-
embeddingsCommand.command("backfill").description("Generate embeddings for memories that don't have them").option("--batch-size <n>", "Number of memories to process in parallel", "5").option("--delay <ms>", "Delay between batches (ms)", "500").option("--dry-run", "Show what would be done without making changes").option("--
|
|
2944
|
+
embeddingsCommand.command("backfill").description("Generate embeddings for memories that don't have them").option("--batch-size <n>", "Number of memories to process in parallel", "5").option("--delay <ms>", "Delay between batches (ms)", "500").option("--dry-run", "Show what would be done without making changes").option("--provider <provider>", "Embedding provider: auto, local, openai, disabled").option("--model <model>", "Embedding model (defaults to configured provider model)").action(async (opts) => {
|
|
2942
2945
|
if (!isInitialized(cwd3)) {
|
|
2943
2946
|
logger.error("Unforgit not initialized. Run 'unforgit init' first.");
|
|
2944
2947
|
process.exit(EXIT_CONFIG_ERROR);
|
|
2945
2948
|
}
|
|
2946
2949
|
const config = loadConfig(cwd3);
|
|
2947
|
-
const
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2950
|
+
const embeddingConfig = {
|
|
2951
|
+
provider: opts.provider ?? config.embeddings?.provider ?? "auto",
|
|
2952
|
+
model: opts.model ?? config.embeddings?.model,
|
|
2953
|
+
apiKey: process.env.OPENAI_API_KEY
|
|
2954
|
+
};
|
|
2955
|
+
const provider = resolveEmbeddingProvider(embeddingConfig);
|
|
2956
|
+
if (!provider.available && !opts.dryRun) {
|
|
2957
|
+
logger.error(provider.reason ?? "Embedding provider is not available.");
|
|
2958
|
+
logger.error("Use 'unforgit config set embeddings.provider local' for no-key local embeddings.");
|
|
2951
2959
|
process.exit(EXIT_ERROR);
|
|
2952
2960
|
}
|
|
2953
2961
|
const dbPath = getDbPath(cwd3);
|
|
@@ -2955,12 +2963,17 @@ embeddingsCommand.command("backfill").description("Generate embeddings for memor
|
|
|
2955
2963
|
const orgId = config.remote.orgId || "local";
|
|
2956
2964
|
const repoId = config.remote.repoId || "local";
|
|
2957
2965
|
try {
|
|
2958
|
-
const memories = store.getMemoriesWithoutEmbeddings(orgId, repoId
|
|
2966
|
+
const memories = store.getMemoriesWithoutEmbeddings(orgId, repoId, {
|
|
2967
|
+
model: provider.model,
|
|
2968
|
+
provider: provider.provider,
|
|
2969
|
+
dimensions: provider.dimensions
|
|
2970
|
+
});
|
|
2959
2971
|
const stats = store.getEmbeddingStats(orgId, repoId);
|
|
2960
2972
|
if (isJsonMode() && opts.dryRun) {
|
|
2961
2973
|
outputJson(buildBackfillJsonPayload({
|
|
2962
2974
|
dryRun: true,
|
|
2963
|
-
model:
|
|
2975
|
+
model: provider.model,
|
|
2976
|
+
provider: provider.provider,
|
|
2964
2977
|
statsBefore: stats,
|
|
2965
2978
|
planned: memories.length,
|
|
2966
2979
|
processed: 0,
|
|
@@ -2981,7 +2994,8 @@ embeddingsCommand.command("backfill").description("Generate embeddings for memor
|
|
|
2981
2994
|
if (isJsonMode()) {
|
|
2982
2995
|
outputJson(buildBackfillJsonPayload({
|
|
2983
2996
|
dryRun: Boolean(opts.dryRun),
|
|
2984
|
-
model:
|
|
2997
|
+
model: provider.model,
|
|
2998
|
+
provider: provider.provider,
|
|
2985
2999
|
statsBefore: stats,
|
|
2986
3000
|
planned: 0,
|
|
2987
3001
|
processed: 0
|
|
@@ -2992,6 +3006,8 @@ embeddingsCommand.command("backfill").description("Generate embeddings for memor
|
|
|
2992
3006
|
return;
|
|
2993
3007
|
}
|
|
2994
3008
|
logger.info(`Found ${memories.length} memories without embeddings.`);
|
|
3009
|
+
logger.info(`Embedding provider: ${provider.provider}`);
|
|
3010
|
+
logger.info(`Embedding model: ${provider.model}`);
|
|
2995
3011
|
if (opts.dryRun) {
|
|
2996
3012
|
logger.info("\n[Dry run - no changes made]");
|
|
2997
3013
|
logger.info("Would generate embeddings for:");
|
|
@@ -3015,10 +3031,9 @@ Generating embeddings (batch size: ${batchSize}, delay: ${delay}ms)...`);
|
|
|
3015
3031
|
batch.map(async (memory) => {
|
|
3016
3032
|
try {
|
|
3017
3033
|
const result = await generateEmbedding(memory.text, {
|
|
3018
|
-
|
|
3019
|
-
model: opts.model
|
|
3034
|
+
...embeddingConfig
|
|
3020
3035
|
});
|
|
3021
|
-
await store.storeEmbedding(memory.id, result.embedding, result.model);
|
|
3036
|
+
await store.storeEmbedding(memory.id, result.embedding, result.model, result.provider);
|
|
3022
3037
|
processed++;
|
|
3023
3038
|
logger.progress(processed, memories.length, "embeddings");
|
|
3024
3039
|
} catch (err) {
|
|
@@ -3044,7 +3059,8 @@ Backfill complete:`);
|
|
|
3044
3059
|
if (isJsonMode()) {
|
|
3045
3060
|
outputJson(buildBackfillJsonPayload({
|
|
3046
3061
|
dryRun: false,
|
|
3047
|
-
model:
|
|
3062
|
+
model: provider.model,
|
|
3063
|
+
provider: provider.provider,
|
|
3048
3064
|
statsBefore: stats,
|
|
3049
3065
|
statsAfter,
|
|
3050
3066
|
planned: memories.length,
|
|
@@ -3368,17 +3384,39 @@ var doctorCommand = new Command27("doctor").description("Check system health and
|
|
|
3368
3384
|
status: "ok",
|
|
3369
3385
|
message: `${memoryStats.total} memories (${memoryStats.byType.episodic} episodic, ${memoryStats.byType.semantic} semantic, ${memoryStats.byType.procedural} procedural)`
|
|
3370
3386
|
});
|
|
3387
|
+
const provider = resolveEmbeddingProvider({
|
|
3388
|
+
provider: config.embeddings?.provider ?? "auto",
|
|
3389
|
+
model: config.embeddings?.model,
|
|
3390
|
+
apiKey: process.env.OPENAI_API_KEY
|
|
3391
|
+
});
|
|
3392
|
+
results.push({
|
|
3393
|
+
check: "embedding-provider",
|
|
3394
|
+
status: provider.available ? "ok" : "warn",
|
|
3395
|
+
message: `${provider.provider} embeddings (${provider.model}, ${provider.dimensions} dimensions)${provider.reason ? `: ${provider.reason}` : ""}`,
|
|
3396
|
+
details: {
|
|
3397
|
+
provider: provider.provider,
|
|
3398
|
+
model: provider.model,
|
|
3399
|
+
dimensions: provider.dimensions,
|
|
3400
|
+
available: provider.available
|
|
3401
|
+
}
|
|
3402
|
+
});
|
|
3371
3403
|
const stats = store.getEmbeddingStats(orgId, repoId);
|
|
3404
|
+
const incompatibleOrMissing = store.getMemoriesWithoutEmbeddings(orgId, repoId, {
|
|
3405
|
+
model: provider.model,
|
|
3406
|
+
provider: provider.provider,
|
|
3407
|
+
dimensions: provider.dimensions
|
|
3408
|
+
}).length;
|
|
3372
3409
|
if (stats.total === 0) {
|
|
3373
3410
|
results.push({ check: "embeddings", status: "ok", message: "No memories yet" });
|
|
3374
|
-
} else if (
|
|
3375
|
-
results.push({ check: "embeddings", status: "ok", message: `All ${stats.total} memories have embeddings` });
|
|
3411
|
+
} else if (incompatibleOrMissing === 0) {
|
|
3412
|
+
results.push({ check: "embeddings", status: "ok", message: `All ${stats.total} memories have compatible embeddings` });
|
|
3376
3413
|
} else {
|
|
3377
|
-
const
|
|
3414
|
+
const compatible = stats.total - incompatibleOrMissing;
|
|
3415
|
+
const pct = (compatible / stats.total * 100).toFixed(1);
|
|
3378
3416
|
results.push({
|
|
3379
3417
|
check: "embeddings",
|
|
3380
3418
|
status: "warn",
|
|
3381
|
-
message: `${
|
|
3419
|
+
message: `${incompatibleOrMissing}/${stats.total} memories lack compatible embeddings for ${provider.provider}/${provider.model} (${pct}% coverage). Run 'unforgit embeddings backfill'`,
|
|
3382
3420
|
fix: "Run 'unforgit embeddings backfill'."
|
|
3383
3421
|
});
|
|
3384
3422
|
}
|
|
@@ -3464,11 +3502,12 @@ var doctorCommand = new Command27("doctor").description("Check system health and
|
|
|
3464
3502
|
});
|
|
3465
3503
|
}
|
|
3466
3504
|
} catch (err) {
|
|
3505
|
+
const localhostRemote = isLocalhostUrl(config.remote.url);
|
|
3467
3506
|
results.push({
|
|
3468
3507
|
check: "remote",
|
|
3469
3508
|
status: "error",
|
|
3470
|
-
message: `Cannot connect to ${config.remote.url}: ${err instanceof Error ? err.message : err}`,
|
|
3471
|
-
fix: "Start the Unforgit API server or update remote.url in unforgit.yaml."
|
|
3509
|
+
message: localhostRemote ? `Cannot connect to local remote API at ${config.remote.url}: ${err instanceof Error ? err.message : err}. Local memory and local embeddings still work; only remote sync is unavailable.` : `Cannot connect to ${config.remote.url}: ${err instanceof Error ? err.message : err}`,
|
|
3510
|
+
fix: localhostRemote ? "remote.url points to localhost. Start the Unforgit API server on this machine/port, update remote.url, or disable sync if this repository is intentionally local-only." : "Start the Unforgit API server or update remote.url in unforgit.yaml."
|
|
3472
3511
|
});
|
|
3473
3512
|
}
|
|
3474
3513
|
} else {
|
|
@@ -3479,15 +3518,19 @@ var doctorCommand = new Command27("doctor").description("Check system health and
|
|
|
3479
3518
|
fix: "Run 'unforgit remote add origin <url>' if this repo should sync remotely."
|
|
3480
3519
|
});
|
|
3481
3520
|
}
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
results.push({ check: "openai", status: "ok", message: "OpenAI API key configured ([REDACTED])" });
|
|
3521
|
+
if (isOpenAIConfigured()) {
|
|
3522
|
+
results.push({ check: "openai", status: "ok", message: "OpenAI API key configured ([REDACTED]) for optional cloud AI features" });
|
|
3485
3523
|
} else {
|
|
3524
|
+
const embeddingProvider = resolveEmbeddingProvider({
|
|
3525
|
+
provider: config.embeddings?.provider ?? "auto",
|
|
3526
|
+
model: config.embeddings?.model
|
|
3527
|
+
});
|
|
3528
|
+
const message = embeddingProvider.provider === "local" ? "No OpenAI API key configured; local embeddings are active. OpenAI is only needed for OpenAI-backed embeddings or AI consolidation." : "No OpenAI API key. OpenAI-backed embeddings and AI consolidation require it.";
|
|
3486
3529
|
results.push({
|
|
3487
3530
|
check: "openai",
|
|
3488
|
-
status: "warn",
|
|
3489
|
-
message
|
|
3490
|
-
fix: "
|
|
3531
|
+
status: embeddingProvider.provider === "local" ? "ok" : "warn",
|
|
3532
|
+
message,
|
|
3533
|
+
fix: embeddingProvider.provider === "local" ? void 0 : "Set OPENAI_API_KEY or configure embeddings.provider=local."
|
|
3491
3534
|
});
|
|
3492
3535
|
}
|
|
3493
3536
|
} catch (err) {
|
|
@@ -3516,6 +3559,14 @@ function summarize(results) {
|
|
|
3516
3559
|
function buildPayload(results) {
|
|
3517
3560
|
return { summary: summarize(results), results };
|
|
3518
3561
|
}
|
|
3562
|
+
function isLocalhostUrl(value) {
|
|
3563
|
+
try {
|
|
3564
|
+
const url = new URL(value);
|
|
3565
|
+
return ["localhost", "127.0.0.1", "::1", "[::1]"].includes(url.hostname);
|
|
3566
|
+
} catch {
|
|
3567
|
+
return false;
|
|
3568
|
+
}
|
|
3569
|
+
}
|
|
3519
3570
|
function exitForResults(results) {
|
|
3520
3571
|
if (results.some((r) => r.status === "error")) {
|
|
3521
3572
|
process.exit(EXIT_ERROR);
|