memorix 1.1.11 → 1.1.13
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/CHANGELOG.md +25 -0
- package/README.md +3 -1
- package/README.zh-CN.md +3 -1
- package/dist/cli/index.js +679 -78
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +289 -44
- package/dist/index.js.map +1 -1
- package/dist/maintenance-runner.js +252 -36
- package/dist/maintenance-runner.js.map +1 -1
- package/dist/memcode-runtime/CHANGELOG.md +25 -0
- package/dist/memcode-runtime/package.json +5 -4
- package/dist/sdk.js +289 -44
- package/dist/sdk.js.map +1 -1
- package/docs/AGENT_OPERATOR_PLAYBOOK.md +2 -2
- package/docs/DEVELOPMENT.md +3 -10
- package/docs/INTEGRATIONS.md +1 -1
- package/docs/SETUP.md +3 -1
- package/docs/dev-log/progress.txt +34 -53
- package/docs/hooks-architecture.md +24 -12
- package/package.json +1 -1
- package/plugins/codex/memorix/.codex-plugin/plugin.json +2 -1
- package/plugins/codex/memorix/hooks/hooks.json +30 -0
- package/src/cli/commands/agent-integrations.ts +384 -2
- package/src/cli/commands/hooks-install.ts +2 -1
- package/src/cli/commands/setup.ts +20 -2
- package/src/cli/tui/chat-service.ts +3 -9
- package/src/cli/tui/data.ts +9 -11
- package/src/embedding/api-provider.ts +105 -6
- package/src/embedding/provider.ts +31 -3
- package/src/hooks/handler.ts +51 -14
- package/src/hooks/installers/index.ts +48 -7
- package/src/hooks/normalizer.ts +15 -0
- package/src/memory/observations.ts +90 -15
- package/src/store/orama-store.ts +169 -17
|
@@ -4773,6 +4773,45 @@ function dimsCacheKey(config) {
|
|
|
4773
4773
|
config.requestedDimensions ?? "native"
|
|
4774
4774
|
].join("|");
|
|
4775
4775
|
}
|
|
4776
|
+
function isValidDimension(value) {
|
|
4777
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0;
|
|
4778
|
+
}
|
|
4779
|
+
async function loadCachedVectorDimensions(config) {
|
|
4780
|
+
try {
|
|
4781
|
+
const raw = await readFile2(CACHE_META_FILE, "utf-8");
|
|
4782
|
+
const data = JSON.parse(raw);
|
|
4783
|
+
const namespace = cacheNamespace(config);
|
|
4784
|
+
if (!Array.isArray(data?.entries)) return null;
|
|
4785
|
+
const entry = data.entries.find(
|
|
4786
|
+
(candidate) => typeof candidate === "object" && candidate !== null && candidate.namespace === namespace && isValidDimension(candidate.dimensions)
|
|
4787
|
+
);
|
|
4788
|
+
return entry?.dimensions ?? null;
|
|
4789
|
+
} catch {
|
|
4790
|
+
return null;
|
|
4791
|
+
}
|
|
4792
|
+
}
|
|
4793
|
+
async function saveCachedVectorDimensions(config, dimensions) {
|
|
4794
|
+
if (!isValidDimension(dimensions)) return;
|
|
4795
|
+
try {
|
|
4796
|
+
await mkdir2(CACHE_DIR2, { recursive: true });
|
|
4797
|
+
let entries = [];
|
|
4798
|
+
try {
|
|
4799
|
+
const raw = await readFile2(CACHE_META_FILE, "utf-8");
|
|
4800
|
+
const data = JSON.parse(raw);
|
|
4801
|
+
if (Array.isArray(data?.entries)) {
|
|
4802
|
+
entries = data.entries.filter(
|
|
4803
|
+
(candidate) => typeof candidate === "object" && candidate !== null && typeof candidate.namespace === "string" && isValidDimension(candidate.dimensions) && typeof candidate.ts === "number"
|
|
4804
|
+
);
|
|
4805
|
+
}
|
|
4806
|
+
} catch {
|
|
4807
|
+
}
|
|
4808
|
+
const namespace = cacheNamespace(config);
|
|
4809
|
+
entries = entries.filter((entry) => entry.namespace !== namespace);
|
|
4810
|
+
entries.push({ namespace, dimensions, ts: Date.now() });
|
|
4811
|
+
await writeFile2(CACHE_META_FILE, JSON.stringify({ version: 1, entries }));
|
|
4812
|
+
} catch {
|
|
4813
|
+
}
|
|
4814
|
+
}
|
|
4776
4815
|
async function loadCachedDims(config) {
|
|
4777
4816
|
try {
|
|
4778
4817
|
const raw = await readFile2(DIMS_CACHE_FILE, "utf-8");
|
|
@@ -4835,6 +4874,7 @@ async function saveCachedDims(config, dimensions) {
|
|
|
4835
4874
|
await writeFile2(DIMS_CACHE_FILE, JSON.stringify({ entries }));
|
|
4836
4875
|
} catch {
|
|
4837
4876
|
}
|
|
4877
|
+
await saveCachedVectorDimensions(config, dimensions);
|
|
4838
4878
|
}
|
|
4839
4879
|
async function saveDiskCacheNow() {
|
|
4840
4880
|
if (!diskCacheDirty2) return;
|
|
@@ -4917,7 +4957,7 @@ async function fetchWithRetry(url, apiKey, body, attempt = 0) {
|
|
|
4917
4957
|
const errorText2 = await response.text().catch(() => "unknown error");
|
|
4918
4958
|
throw new Error(`Embedding API error (${response.status}): ${errorText2}`);
|
|
4919
4959
|
}
|
|
4920
|
-
var CACHE_DIR2, CACHE_FILE2, DIMS_CACHE_FILE, cache3, MAX_CACHE_SIZE3, diskCacheDirty2, diskSaveTimer, diskCacheLoaded, diskCacheLoadPromise, MAX_INPUT_CHARS, MAX_CONCURRENCY, DISK_SAVE_DEBOUNCE_MS, DEFAULT_MAX_BATCH_SIZE, DASHSCOPE_MAX_BATCH_SIZE, MAX_RETRIES, BASE_DELAY_MS, APIEmbeddingProvider;
|
|
4960
|
+
var CACHE_DIR2, CACHE_FILE2, DIMS_CACHE_FILE, CACHE_META_FILE, cache3, MAX_CACHE_SIZE3, diskCacheDirty2, diskSaveTimer, diskCacheLoaded, diskCacheLoadPromise, MAX_INPUT_CHARS, MAX_CONCURRENCY, DISK_SAVE_DEBOUNCE_MS, DEFAULT_MAX_BATCH_SIZE, DASHSCOPE_MAX_BATCH_SIZE, MAX_RETRIES, BASE_DELAY_MS, APIEmbeddingProvider;
|
|
4921
4961
|
var init_api_provider = __esm({
|
|
4922
4962
|
"src/embedding/api-provider.ts"() {
|
|
4923
4963
|
"use strict";
|
|
@@ -4925,6 +4965,7 @@ var init_api_provider = __esm({
|
|
|
4925
4965
|
CACHE_DIR2 = process.env.MEMORIX_DATA_DIR || join4(homedir7(), ".memorix", "data");
|
|
4926
4966
|
CACHE_FILE2 = join4(CACHE_DIR2, ".embedding-api-cache.json");
|
|
4927
4967
|
DIMS_CACHE_FILE = join4(CACHE_DIR2, ".embedding-dims-cache.json");
|
|
4968
|
+
CACHE_META_FILE = join4(CACHE_DIR2, ".embedding-api-cache-meta.json");
|
|
4928
4969
|
cache3 = /* @__PURE__ */ new Map();
|
|
4929
4970
|
MAX_CACHE_SIZE3 = 1e4;
|
|
4930
4971
|
diskCacheDirty2 = false;
|
|
@@ -4951,13 +4992,22 @@ var init_api_provider = __esm({
|
|
|
4951
4992
|
this.dimensions = detectedDimensions;
|
|
4952
4993
|
this.name = `api-${config.model.replace(/\//g, "-")}`;
|
|
4953
4994
|
}
|
|
4954
|
-
static async create() {
|
|
4995
|
+
static async create(options = {}) {
|
|
4955
4996
|
const config = _APIEmbeddingProvider.resolveConfig();
|
|
4956
|
-
|
|
4997
|
+
const allowNetworkProbe = options.allowNetworkProbe !== false;
|
|
4957
4998
|
let probeDimensions = await loadCachedDims(config);
|
|
4999
|
+
let dimensionSource = "dims-cache";
|
|
5000
|
+
if (probeDimensions === null) {
|
|
5001
|
+
probeDimensions = await loadCachedVectorDimensions(config);
|
|
5002
|
+
if (probeDimensions !== null) dimensionSource = "vector-cache";
|
|
5003
|
+
}
|
|
4958
5004
|
if (probeDimensions !== null) {
|
|
4959
|
-
console.error(`[memorix] API embedding: ${config.model} @ ${config.baseUrl} (${probeDimensions}d) [
|
|
5005
|
+
console.error(`[memorix] API embedding: ${config.model} @ ${config.baseUrl} (${probeDimensions}d) [${dimensionSource}]`);
|
|
5006
|
+
if (dimensionSource === "dims-cache") {
|
|
5007
|
+
void saveCachedVectorDimensions(config, probeDimensions);
|
|
5008
|
+
}
|
|
4960
5009
|
} else {
|
|
5010
|
+
if (!allowNetworkProbe) return null;
|
|
4961
5011
|
probeDimensions = await _APIEmbeddingProvider.probeAPI(config);
|
|
4962
5012
|
console.error(`[memorix] API embedding: ${config.model} @ ${config.baseUrl} (${probeDimensions}d)`);
|
|
4963
5013
|
saveCachedDims(config, probeDimensions).catch(() => {
|
|
@@ -4966,6 +5016,7 @@ var init_api_provider = __esm({
|
|
|
4966
5016
|
if (config.requestedDimensions) {
|
|
4967
5017
|
console.error(`[memorix] Dimension shortening: ${config.requestedDimensions}d requested`);
|
|
4968
5018
|
}
|
|
5019
|
+
startDiskCacheLoad();
|
|
4969
5020
|
return new _APIEmbeddingProvider(config, probeDimensions);
|
|
4970
5021
|
}
|
|
4971
5022
|
static resolveConfig() {
|
|
@@ -5144,6 +5195,13 @@ var init_api_provider = __esm({
|
|
|
5144
5195
|
scheduleDiskSave();
|
|
5145
5196
|
return results;
|
|
5146
5197
|
}
|
|
5198
|
+
async getCachedEmbeddings(texts) {
|
|
5199
|
+
await ensureDiskCacheLoaded();
|
|
5200
|
+
return texts.map((text) => {
|
|
5201
|
+
const hash = textHash2(normalizeText(text), this.cacheKeyNamespace);
|
|
5202
|
+
return cache3.get(hash) ?? null;
|
|
5203
|
+
});
|
|
5204
|
+
}
|
|
5147
5205
|
getStats() {
|
|
5148
5206
|
return {
|
|
5149
5207
|
totalTokens: this.totalTokensUsed,
|
|
@@ -5252,10 +5310,10 @@ async function createTransformersProvider() {
|
|
|
5252
5310
|
return null;
|
|
5253
5311
|
}
|
|
5254
5312
|
}
|
|
5255
|
-
async function createAPIProvider() {
|
|
5313
|
+
async function createAPIProvider(options) {
|
|
5256
5314
|
try {
|
|
5257
5315
|
const { APIEmbeddingProvider: APIEmbeddingProvider2 } = await Promise.resolve().then(() => (init_api_provider(), api_provider_exports));
|
|
5258
|
-
return await APIEmbeddingProvider2.create();
|
|
5316
|
+
return await APIEmbeddingProvider2.create(options ?? {});
|
|
5259
5317
|
} catch (e) {
|
|
5260
5318
|
warnOnce(`[memorix] Failed to init API embedding: ${e instanceof Error ? e.message : e}`);
|
|
5261
5319
|
return null;
|
|
@@ -5310,11 +5368,24 @@ function wrapProvider(candidate) {
|
|
|
5310
5368
|
}
|
|
5311
5369
|
throw error;
|
|
5312
5370
|
}
|
|
5313
|
-
}
|
|
5371
|
+
},
|
|
5372
|
+
...candidate.getCachedEmbeddings ? {
|
|
5373
|
+
async getCachedEmbeddings(texts) {
|
|
5374
|
+
return candidate.getCachedEmbeddings(texts);
|
|
5375
|
+
}
|
|
5376
|
+
} : {}
|
|
5314
5377
|
};
|
|
5315
5378
|
}
|
|
5316
|
-
async function getEmbeddingProvider() {
|
|
5379
|
+
async function getEmbeddingProvider(options = {}) {
|
|
5317
5380
|
if (provider) return provider;
|
|
5381
|
+
if (options.allowNetworkProbe === false) {
|
|
5382
|
+
const mode = getEmbeddingMode2();
|
|
5383
|
+
if (mode !== "api" && !(mode === "auto" && hasAPIEmbeddingConfig())) {
|
|
5384
|
+
return null;
|
|
5385
|
+
}
|
|
5386
|
+
const cached = await createAPIProvider({ allowNetworkProbe: false });
|
|
5387
|
+
return cached ? wrapProvider(cached) : null;
|
|
5388
|
+
}
|
|
5318
5389
|
if (lastInitWasTemporaryFailure) {
|
|
5319
5390
|
const elapsed = Date.now() - lastFailureTimestamp;
|
|
5320
5391
|
if (elapsed < RETRY_COOLDOWN_MS) {
|
|
@@ -6315,12 +6386,15 @@ __export(orama_store_exports, {
|
|
|
6315
6386
|
batchGenerateEmbeddings: () => batchGenerateEmbeddings,
|
|
6316
6387
|
generateEmbedding: () => generateEmbedding,
|
|
6317
6388
|
getDb: () => getDb,
|
|
6389
|
+
getDeferredCachedVectorHydration: () => getDeferredCachedVectorHydration,
|
|
6318
6390
|
getLastSearchMode: () => getLastSearchMode,
|
|
6319
6391
|
getObservationCount: () => getObservationCount,
|
|
6320
6392
|
getObservationsByIds: () => getObservationsByIds,
|
|
6321
6393
|
getTimeline: () => getTimeline,
|
|
6322
6394
|
getVectorDimensions: () => getVectorDimensions,
|
|
6395
|
+
hasObservationVector: () => hasObservationVector,
|
|
6323
6396
|
hydrateIndex: () => hydrateIndex,
|
|
6397
|
+
hydrateIndexForStartup: () => hydrateIndexForStartup,
|
|
6324
6398
|
insertObservation: () => insertObservation,
|
|
6325
6399
|
isEmbeddingEnabled: () => isEmbeddingEnabled,
|
|
6326
6400
|
makeOramaObservationId: () => makeOramaObservationId,
|
|
@@ -6381,11 +6455,10 @@ function stripVectorSearchParams(params) {
|
|
|
6381
6455
|
const { mode, vector, similarity, hybridWeights, ...rest } = params;
|
|
6382
6456
|
return rest;
|
|
6383
6457
|
}
|
|
6384
|
-
async function
|
|
6385
|
-
|
|
6386
|
-
const
|
|
6387
|
-
|
|
6388
|
-
embeddingDimensions = provider2?.dimensions ?? null;
|
|
6458
|
+
async function initializeDb(options, generation) {
|
|
6459
|
+
const provider2 = await getEmbeddingProvider({ allowNetworkProbe: options.allowNetworkProbe });
|
|
6460
|
+
const nextEmbeddingEnabled = provider2 !== null;
|
|
6461
|
+
const nextEmbeddingDimensions = provider2?.dimensions ?? null;
|
|
6389
6462
|
const baseSchema = {
|
|
6390
6463
|
id: "string",
|
|
6391
6464
|
observationId: "number",
|
|
@@ -6408,15 +6481,39 @@ async function getDb() {
|
|
|
6408
6481
|
documentType: "string",
|
|
6409
6482
|
knowledgeLayer: "string"
|
|
6410
6483
|
};
|
|
6411
|
-
const dims =
|
|
6412
|
-
const schema =
|
|
6413
|
-
|
|
6414
|
-
|
|
6484
|
+
const dims = nextEmbeddingDimensions ?? 384;
|
|
6485
|
+
const schema = nextEmbeddingEnabled ? { ...baseSchema, embedding: `vector[${dims}]` } : baseSchema;
|
|
6486
|
+
const nextDb = await create({ schema });
|
|
6487
|
+
if (generation !== dbGeneration) {
|
|
6488
|
+
return getDb(options);
|
|
6489
|
+
}
|
|
6490
|
+
indexEmbeddingProvider = provider2;
|
|
6491
|
+
embeddingEnabled = nextEmbeddingEnabled;
|
|
6492
|
+
embeddingDimensions = nextEmbeddingDimensions;
|
|
6493
|
+
db = nextDb;
|
|
6494
|
+
return nextDb;
|
|
6495
|
+
}
|
|
6496
|
+
async function getDb(options = {}) {
|
|
6497
|
+
if (db) return db;
|
|
6498
|
+
if (dbInitPromise) return dbInitPromise;
|
|
6499
|
+
const initPromise2 = initializeDb(options, dbGeneration);
|
|
6500
|
+
dbInitPromise = initPromise2;
|
|
6501
|
+
try {
|
|
6502
|
+
return await initPromise2;
|
|
6503
|
+
} finally {
|
|
6504
|
+
if (dbInitPromise === initPromise2) {
|
|
6505
|
+
dbInitPromise = null;
|
|
6506
|
+
}
|
|
6507
|
+
}
|
|
6415
6508
|
}
|
|
6416
6509
|
async function resetDb() {
|
|
6510
|
+
dbGeneration++;
|
|
6417
6511
|
db = null;
|
|
6512
|
+
dbInitPromise = null;
|
|
6418
6513
|
embeddingEnabled = false;
|
|
6419
6514
|
embeddingDimensions = null;
|
|
6515
|
+
indexEmbeddingProvider = null;
|
|
6516
|
+
deferredCachedVectorHydration = null;
|
|
6420
6517
|
lastSearchModeByProject.clear();
|
|
6421
6518
|
docByObservationKey.clear();
|
|
6422
6519
|
}
|
|
@@ -6444,14 +6541,64 @@ async function batchGenerateEmbeddings(texts) {
|
|
|
6444
6541
|
return texts.map(() => null);
|
|
6445
6542
|
}
|
|
6446
6543
|
}
|
|
6447
|
-
async function
|
|
6448
|
-
|
|
6544
|
+
async function getCachedEmbeddings(texts) {
|
|
6545
|
+
if (!embeddingEnabled || texts.length === 0) return texts.map(() => null);
|
|
6546
|
+
const provider2 = indexEmbeddingProvider;
|
|
6547
|
+
if (!provider2?.getCachedEmbeddings) return texts.map(() => null);
|
|
6548
|
+
try {
|
|
6549
|
+
return await provider2.getCachedEmbeddings(texts);
|
|
6550
|
+
} catch {
|
|
6551
|
+
return texts.map(() => null);
|
|
6552
|
+
}
|
|
6553
|
+
}
|
|
6554
|
+
function observationEmbeddingText(observation) {
|
|
6555
|
+
return [
|
|
6556
|
+
observation.title ?? "",
|
|
6557
|
+
observation.narrative ?? "",
|
|
6558
|
+
...Array.isArray(observation.facts) ? observation.facts : []
|
|
6559
|
+
].join(" ");
|
|
6560
|
+
}
|
|
6561
|
+
function documentEmbeddingText(document) {
|
|
6562
|
+
return [document.title ?? "", document.narrative ?? "", document.facts ?? ""].join(" ");
|
|
6563
|
+
}
|
|
6564
|
+
function isCompatibleCachedVector(vector) {
|
|
6565
|
+
return Boolean(
|
|
6566
|
+
vector && embeddingDimensions !== null && vector.length === embeddingDimensions && vector.every(Number.isFinite)
|
|
6567
|
+
);
|
|
6568
|
+
}
|
|
6569
|
+
async function attachCachedVectors(database, candidates) {
|
|
6570
|
+
const cachedVectors = await getCachedEmbeddings(
|
|
6571
|
+
candidates.map(({ observation }) => observationEmbeddingText(observation))
|
|
6572
|
+
);
|
|
6573
|
+
if (db !== database) return;
|
|
6574
|
+
for (let index = 0; index < candidates.length; index++) {
|
|
6575
|
+
const vector = cachedVectors[index];
|
|
6576
|
+
if (!isCompatibleCachedVector(vector)) continue;
|
|
6577
|
+
const existing = getByID(database, candidates[index].id);
|
|
6578
|
+
if (!existing || documentEmbeddingText(existing) !== observationEmbeddingText(candidates[index].observation)) continue;
|
|
6579
|
+
try {
|
|
6580
|
+
await update(database, candidates[index].id, { ...existing, embedding: vector });
|
|
6581
|
+
} catch {
|
|
6582
|
+
}
|
|
6583
|
+
}
|
|
6584
|
+
}
|
|
6585
|
+
async function hydrateIndex(observations2, options = {}) {
|
|
6586
|
+
const database = await getDb({ allowNetworkProbe: options.allowNetworkProbe });
|
|
6587
|
+
const candidates = [];
|
|
6588
|
+
for (const observation of observations2) {
|
|
6589
|
+
if (!observation || !observation.id || !observation.projectId) continue;
|
|
6590
|
+
const id = makeOramaObservationId(observation.projectId, observation.id);
|
|
6591
|
+
if (getByID(database, id)) continue;
|
|
6592
|
+
candidates.push({ observation, id });
|
|
6593
|
+
}
|
|
6594
|
+
const deferCachedVectors = options.deferCachedVectors === true && Boolean(indexEmbeddingProvider?.getCachedEmbeddings);
|
|
6595
|
+
const cachedVectors = deferCachedVectors ? candidates.map(() => null) : await getCachedEmbeddings(candidates.map(({ observation }) => observationEmbeddingText(observation)));
|
|
6449
6596
|
let inserted = 0;
|
|
6450
|
-
for (
|
|
6451
|
-
|
|
6597
|
+
for (let index = 0; index < candidates.length; index++) {
|
|
6598
|
+
const { observation: obs, id } = candidates[index];
|
|
6452
6599
|
try {
|
|
6453
|
-
const
|
|
6454
|
-
|
|
6600
|
+
const vector = cachedVectors[index];
|
|
6601
|
+
const compatibleVector = isCompatibleCachedVector(vector) ? vector : null;
|
|
6455
6602
|
const doc = {
|
|
6456
6603
|
id,
|
|
6457
6604
|
observationId: obs.id,
|
|
@@ -6470,7 +6617,8 @@ async function hydrateIndex(observations2) {
|
|
|
6470
6617
|
status: obs.status ?? "active",
|
|
6471
6618
|
source: obs.source || "agent",
|
|
6472
6619
|
documentType: "observation",
|
|
6473
|
-
knowledgeLayer: resolveKnowledgeLayer("observation", obs.sourceDetail, obs.source)
|
|
6620
|
+
knowledgeLayer: resolveKnowledgeLayer("observation", obs.sourceDetail, obs.source),
|
|
6621
|
+
...compatibleVector ? { embedding: compatibleVector } : {}
|
|
6474
6622
|
};
|
|
6475
6623
|
await insert2(database, doc);
|
|
6476
6624
|
rememberObservationDoc(doc);
|
|
@@ -6478,8 +6626,24 @@ async function hydrateIndex(observations2) {
|
|
|
6478
6626
|
} catch {
|
|
6479
6627
|
}
|
|
6480
6628
|
}
|
|
6629
|
+
deferredCachedVectorHydration = deferCachedVectors ? attachCachedVectors(database, candidates).catch(() => {
|
|
6630
|
+
}) : null;
|
|
6481
6631
|
return inserted;
|
|
6482
6632
|
}
|
|
6633
|
+
async function hydrateIndexForStartup(observations2) {
|
|
6634
|
+
return hydrateIndex(observations2, {
|
|
6635
|
+
allowNetworkProbe: false,
|
|
6636
|
+
deferCachedVectors: true
|
|
6637
|
+
});
|
|
6638
|
+
}
|
|
6639
|
+
function getDeferredCachedVectorHydration() {
|
|
6640
|
+
return deferredCachedVectorHydration;
|
|
6641
|
+
}
|
|
6642
|
+
function hasObservationVector(projectId, observationId) {
|
|
6643
|
+
if (!db || !embeddingEnabled || embeddingDimensions === null) return false;
|
|
6644
|
+
const document = getByID(db, makeOramaObservationId(projectId, observationId));
|
|
6645
|
+
return Array.isArray(document?.embedding) && document.embedding.length === embeddingDimensions && document.embedding.every(Number.isFinite);
|
|
6646
|
+
}
|
|
6483
6647
|
async function insertObservation(doc) {
|
|
6484
6648
|
const database = await getDb();
|
|
6485
6649
|
await insert2(database, doc);
|
|
@@ -7013,7 +7177,7 @@ function formatTime(isoDate) {
|
|
|
7013
7177
|
return isoDate;
|
|
7014
7178
|
}
|
|
7015
7179
|
}
|
|
7016
|
-
var db, embeddingEnabled, embeddingDimensions, docByObservationKey, NON_CJK_HYBRID_SIMILARITY, lastSearchModeByProject, SEARCH_MODE_DEFAULT_KEY, COMMAND_LOG_TITLE, COMMAND_STYLE_TITLE, COMMAND_LIKE_QUERY2, COMMAND_INTENT_QUERY, NATURAL_LANGUAGE_MARKERS;
|
|
7180
|
+
var db, dbInitPromise, dbGeneration, embeddingEnabled, embeddingDimensions, indexEmbeddingProvider, deferredCachedVectorHydration, docByObservationKey, NON_CJK_HYBRID_SIMILARITY, lastSearchModeByProject, SEARCH_MODE_DEFAULT_KEY, COMMAND_LOG_TITLE, COMMAND_STYLE_TITLE, COMMAND_LIKE_QUERY2, COMMAND_INTENT_QUERY, NATURAL_LANGUAGE_MARKERS;
|
|
7017
7181
|
var init_orama_store = __esm({
|
|
7018
7182
|
"src/store/orama-store.ts"() {
|
|
7019
7183
|
"use strict";
|
|
@@ -7025,8 +7189,12 @@ var init_orama_store = __esm({
|
|
|
7025
7189
|
init_intent_detector();
|
|
7026
7190
|
init_query_expansion();
|
|
7027
7191
|
db = null;
|
|
7192
|
+
dbInitPromise = null;
|
|
7193
|
+
dbGeneration = 0;
|
|
7028
7194
|
embeddingEnabled = false;
|
|
7029
7195
|
embeddingDimensions = null;
|
|
7196
|
+
indexEmbeddingProvider = null;
|
|
7197
|
+
deferredCachedVectorHydration = null;
|
|
7030
7198
|
docByObservationKey = /* @__PURE__ */ new Map();
|
|
7031
7199
|
NON_CJK_HYBRID_SIMILARITY = 0.45;
|
|
7032
7200
|
lastSearchModeByProject = /* @__PURE__ */ new Map();
|
|
@@ -7217,7 +7385,18 @@ async function bindObservationCodeRefsBestEffort(observation) {
|
|
|
7217
7385
|
function isVectorCompatibleWithCurrentIndex(embedding) {
|
|
7218
7386
|
if (!embedding) return false;
|
|
7219
7387
|
const vectorDimensions = getVectorDimensions();
|
|
7220
|
-
return vectorDimensions
|
|
7388
|
+
return vectorDimensions !== null && embedding.length === vectorDimensions && embedding.every(Number.isFinite);
|
|
7389
|
+
}
|
|
7390
|
+
async function upgradeVectorSchemaAfterFirstEmbedding(embedding) {
|
|
7391
|
+
if (isVectorCompatibleWithCurrentIndex(embedding)) return true;
|
|
7392
|
+
if (getVectorDimensions() !== null || !embedding.every(Number.isFinite)) return false;
|
|
7393
|
+
const targetProjectDir = projectDir;
|
|
7394
|
+
if (!vectorSchemaUpgradePromise) {
|
|
7395
|
+
vectorSchemaUpgradePromise = reindexObservations().then(() => projectDir === targetProjectDir && isVectorCompatibleWithCurrentIndex(embedding)).catch(() => false).finally(() => {
|
|
7396
|
+
vectorSchemaUpgradePromise = null;
|
|
7397
|
+
});
|
|
7398
|
+
}
|
|
7399
|
+
return vectorSchemaUpgradePromise;
|
|
7221
7400
|
}
|
|
7222
7401
|
async function initObservations(dir) {
|
|
7223
7402
|
if (projectDir === dir) return;
|
|
@@ -7227,6 +7406,7 @@ async function initObservations(dir) {
|
|
|
7227
7406
|
nextId = await store.loadIdCounter();
|
|
7228
7407
|
projectDir = dir;
|
|
7229
7408
|
searchIndexPrepared = false;
|
|
7409
|
+
vectorSchemaUpgradePromise = null;
|
|
7230
7410
|
}
|
|
7231
7411
|
async function ensureFreshObservations() {
|
|
7232
7412
|
if (!projectDir) return false;
|
|
@@ -7406,6 +7586,9 @@ async function storeObservation(input) {
|
|
|
7406
7586
|
const searchableText = [input.title, input.narrative, ...input.facts ?? []].join(" ");
|
|
7407
7587
|
generateEmbedding(searchableText).then(async (embedding) => {
|
|
7408
7588
|
if (embedding) {
|
|
7589
|
+
if (!isVectorCompatibleWithCurrentIndex(embedding)) {
|
|
7590
|
+
await upgradeVectorSchemaAfterFirstEmbedding(embedding);
|
|
7591
|
+
}
|
|
7409
7592
|
if (!isVectorCompatibleWithCurrentIndex(embedding)) {
|
|
7410
7593
|
const vectorDimensions = getVectorDimensions();
|
|
7411
7594
|
console.error(
|
|
@@ -7516,6 +7699,13 @@ async function upsertObservation(existing, input, now) {
|
|
|
7516
7699
|
vectorMissingIds.add(obsId);
|
|
7517
7700
|
generateEmbedding(searchableText).then(async (embedding) => {
|
|
7518
7701
|
if (embedding) {
|
|
7702
|
+
if (!isVectorCompatibleWithCurrentIndex(embedding)) {
|
|
7703
|
+
await upgradeVectorSchemaAfterFirstEmbedding(embedding);
|
|
7704
|
+
}
|
|
7705
|
+
if (!isVectorCompatibleWithCurrentIndex(embedding)) {
|
|
7706
|
+
queueVectorBackfill(existing.projectId);
|
|
7707
|
+
return;
|
|
7708
|
+
}
|
|
7519
7709
|
try {
|
|
7520
7710
|
const { removeObservation: removeObs } = await Promise.resolve().then(() => (init_orama_store(), orama_store_exports));
|
|
7521
7711
|
await removeObs(makeOramaObservationId(existing.projectId, obsId));
|
|
@@ -7660,14 +7850,21 @@ async function reindexObservations() {
|
|
|
7660
7850
|
let embeddings = observations.map(() => null);
|
|
7661
7851
|
const provider2 = await getEmbeddingProvider();
|
|
7662
7852
|
const canBatchEmbedAtStartup = provider2 !== null && !provider2.name.startsWith("api-");
|
|
7853
|
+
await getDb();
|
|
7854
|
+
const texts = observations.map(
|
|
7855
|
+
(obs) => [obs.title, obs.narrative, ...obs.facts].join(" ")
|
|
7856
|
+
);
|
|
7857
|
+
if (provider2?.getCachedEmbeddings) {
|
|
7858
|
+
try {
|
|
7859
|
+
embeddings = await provider2.getCachedEmbeddings(texts);
|
|
7860
|
+
} catch {
|
|
7861
|
+
}
|
|
7862
|
+
}
|
|
7663
7863
|
if (provider2 && !canBatchEmbedAtStartup) {
|
|
7664
|
-
console.error("[memorix] Startup reindex:
|
|
7864
|
+
console.error("[memorix] Startup reindex: restored cached API embeddings; uncached vectors stay in background recovery");
|
|
7665
7865
|
}
|
|
7666
7866
|
if (canBatchEmbedAtStartup) {
|
|
7667
7867
|
try {
|
|
7668
|
-
const texts = observations.map(
|
|
7669
|
-
(obs) => [obs.title, obs.narrative, ...obs.facts].join(" ")
|
|
7670
|
-
);
|
|
7671
7868
|
embeddings = await batchGenerateEmbeddings(texts);
|
|
7672
7869
|
} catch {
|
|
7673
7870
|
}
|
|
@@ -7720,16 +7917,31 @@ async function reindexObservations() {
|
|
|
7720
7917
|
}
|
|
7721
7918
|
async function prepareSearchIndex() {
|
|
7722
7919
|
if (searchIndexPrepared) return 0;
|
|
7723
|
-
const count2 = await
|
|
7920
|
+
const count2 = await hydrateIndexForStartup(observations);
|
|
7724
7921
|
if (count2 === 0) {
|
|
7725
7922
|
searchIndexPrepared = true;
|
|
7726
7923
|
return 0;
|
|
7727
7924
|
}
|
|
7728
|
-
|
|
7729
|
-
|
|
7730
|
-
|
|
7731
|
-
|
|
7925
|
+
const hydratedProjectDir = projectDir;
|
|
7926
|
+
const queueMissingVectors = () => {
|
|
7927
|
+
if (projectDir !== hydratedProjectDir) return;
|
|
7928
|
+
vectorMissingIds.clear();
|
|
7929
|
+
if (!isEmbeddingExplicitlyDisabled()) {
|
|
7930
|
+
const projectsWithMissingVectors = /* @__PURE__ */ new Set();
|
|
7931
|
+
for (const obs of observations) {
|
|
7932
|
+
if (!hasObservationVector(obs.projectId, obs.id)) {
|
|
7933
|
+
vectorMissingIds.add(obs.id);
|
|
7934
|
+
projectsWithMissingVectors.add(obs.projectId);
|
|
7935
|
+
}
|
|
7936
|
+
}
|
|
7937
|
+
for (const projectId of projectsWithMissingVectors) queueVectorBackfill(projectId);
|
|
7732
7938
|
}
|
|
7939
|
+
};
|
|
7940
|
+
const cacheHydration = getDeferredCachedVectorHydration();
|
|
7941
|
+
if (cacheHydration) {
|
|
7942
|
+
void cacheHydration.finally(queueMissingVectors);
|
|
7943
|
+
} else {
|
|
7944
|
+
queueMissingVectors();
|
|
7733
7945
|
}
|
|
7734
7946
|
searchIndexPrepared = true;
|
|
7735
7947
|
return count2;
|
|
@@ -7789,6 +8001,9 @@ async function backfillVectorEmbeddings(options = {}) {
|
|
|
7789
8001
|
try {
|
|
7790
8002
|
const embedding = await generateEmbedding(text);
|
|
7791
8003
|
if (embedding) {
|
|
8004
|
+
if (!isVectorCompatibleWithCurrentIndex(embedding)) {
|
|
8005
|
+
await upgradeVectorSchemaAfterFirstEmbedding(embedding);
|
|
8006
|
+
}
|
|
7792
8007
|
if (!isVectorCompatibleWithCurrentIndex(embedding)) {
|
|
7793
8008
|
const vectorDimensions = getVectorDimensions();
|
|
7794
8009
|
console.error(
|
|
@@ -7851,7 +8066,7 @@ async function backfillVectorEmbeddings(options = {}) {
|
|
|
7851
8066
|
}
|
|
7852
8067
|
return { attempted: ids.length, succeeded, failed };
|
|
7853
8068
|
}
|
|
7854
|
-
var observations, nextId, projectDir, searchIndexPrepared, vectorMissingIds, vectorBackfillRunning, lastVectorBackfill, embeddingFailureLogTimestamps, EMBEDDING_FAILURE_LOG_COOLDOWN_MS;
|
|
8069
|
+
var observations, nextId, projectDir, searchIndexPrepared, vectorMissingIds, vectorBackfillRunning, vectorSchemaUpgradePromise, lastVectorBackfill, embeddingFailureLogTimestamps, EMBEDDING_FAILURE_LOG_COOLDOWN_MS;
|
|
7855
8070
|
var init_observations = __esm({
|
|
7856
8071
|
"src/memory/observations.ts"() {
|
|
7857
8072
|
"use strict";
|
|
@@ -7869,6 +8084,7 @@ var init_observations = __esm({
|
|
|
7869
8084
|
searchIndexPrepared = false;
|
|
7870
8085
|
vectorMissingIds = /* @__PURE__ */ new Set();
|
|
7871
8086
|
vectorBackfillRunning = false;
|
|
8087
|
+
vectorSchemaUpgradePromise = null;
|
|
7872
8088
|
lastVectorBackfill = null;
|
|
7873
8089
|
embeddingFailureLogTimestamps = /* @__PURE__ */ new Map();
|
|
7874
8090
|
EMBEDDING_FAILURE_LOG_COOLDOWN_MS = 3e4;
|