memorix 1.2.4 → 1.2.5

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.
@@ -1307,6 +1307,25 @@ function getResolvedConfig(options = {}) {
1307
1307
  const legacy = loadFileConfig();
1308
1308
  const embeddingBaseUrl = first(process.env.MEMORIX_EMBEDDING_BASE_URL, toml.embedding?.base_url, yaml.embedding?.baseUrl, legacy.embeddingApi?.baseUrl);
1309
1309
  const openRouterEmbeddingApiKey = isOpenRouterUrl(embeddingBaseUrl) ? process.env.OPENROUTER_API_KEY : void 0;
1310
+ const memoryLlmProvider = first(
1311
+ process.env.MEMORIX_LLM_PROVIDER,
1312
+ toml.memory?.llm?.provider,
1313
+ yaml.llm?.provider,
1314
+ legacy.llm?.provider
1315
+ );
1316
+ const memoryLlmModel = first(
1317
+ process.env.MEMORIX_LLM_MODEL,
1318
+ toml.memory?.llm?.model,
1319
+ yaml.llm?.model,
1320
+ legacy.llm?.model
1321
+ );
1322
+ const memoryLlmBaseUrl = first(
1323
+ process.env.MEMORIX_LLM_BASE_URL,
1324
+ toml.memory?.llm?.base_url,
1325
+ yaml.llm?.baseUrl,
1326
+ legacy.llm?.baseUrl
1327
+ );
1328
+ const openRouterMemoryLlmApiKey = isOpenRouterMemoryLane(memoryLlmProvider, memoryLlmBaseUrl) ? process.env.OPENROUTER_API_KEY : void 0;
1310
1329
  const resolved = {
1311
1330
  agent: {
1312
1331
  provider: first(
@@ -1344,9 +1363,9 @@ function getResolvedConfig(options = {}) {
1344
1363
  autoCleanup: firstBool(toml.memory?.auto_cleanup, yaml.behavior?.autoCleanup),
1345
1364
  syncAdvisory: firstBool(toml.memory?.sync_advisory, yaml.behavior?.syncAdvisory),
1346
1365
  llm: {
1347
- provider: first(process.env.MEMORIX_LLM_PROVIDER, toml.memory?.llm?.provider, yaml.llm?.provider, legacy.llm?.provider),
1348
- model: first(process.env.MEMORIX_LLM_MODEL, toml.memory?.llm?.model, yaml.llm?.model, legacy.llm?.model),
1349
- baseUrl: first(process.env.MEMORIX_LLM_BASE_URL, toml.memory?.llm?.base_url, yaml.llm?.baseUrl, legacy.llm?.baseUrl),
1366
+ provider: memoryLlmProvider,
1367
+ model: memoryLlmModel,
1368
+ baseUrl: memoryLlmBaseUrl,
1350
1369
  apiKey: first(
1351
1370
  process.env.MEMORIX_LLM_API_KEY,
1352
1371
  process.env.MEMORIX_API_KEY,
@@ -1355,7 +1374,7 @@ function getResolvedConfig(options = {}) {
1355
1374
  legacy.llm?.apiKey,
1356
1375
  process.env.OPENAI_API_KEY,
1357
1376
  process.env.ANTHROPIC_API_KEY,
1358
- process.env.OPENROUTER_API_KEY
1377
+ openRouterMemoryLlmApiKey
1359
1378
  )
1360
1379
  }
1361
1380
  },
@@ -1492,6 +1511,9 @@ function isOpenRouterUrl(value) {
1492
1511
  return /(^|\.)openrouter\.ai(?::|\/|$)/i.test(value);
1493
1512
  }
1494
1513
  }
1514
+ function isOpenRouterMemoryLane(provider2, baseUrl) {
1515
+ return provider2?.trim().toLowerCase() === "openrouter" || isOpenRouterUrl(baseUrl);
1516
+ }
1495
1517
  function normalizeExternalContext(value) {
1496
1518
  const normalized = value?.trim().toLowerCase();
1497
1519
  if (normalized === "auto" || normalized === "off") return normalized;
@@ -4259,6 +4281,29 @@ Rules:
4259
4281
  }
4260
4282
  });
4261
4283
 
4284
+ // src/timeout.ts
4285
+ function withTimeout(promise, ms, label) {
4286
+ return new Promise((resolve, reject) => {
4287
+ const timer = setTimeout(() => reject(new Error(`${label} timed out after ${ms}ms`)), ms);
4288
+ promise.then(
4289
+ (value) => {
4290
+ clearTimeout(timer);
4291
+ resolve(value);
4292
+ },
4293
+ (error) => {
4294
+ clearTimeout(timer);
4295
+ reject(error);
4296
+ }
4297
+ );
4298
+ });
4299
+ }
4300
+ var init_timeout = __esm({
4301
+ "src/timeout.ts"() {
4302
+ "use strict";
4303
+ init_esm_shims();
4304
+ }
4305
+ });
4306
+
4262
4307
  // src/project/aliases.ts
4263
4308
  var aliases_exports = {};
4264
4309
  __export(aliases_exports, {
@@ -5024,8 +5069,12 @@ async function searchObservations(options) {
5024
5069
  }
5025
5070
  };
5026
5071
  const modeKey = options.projectId ?? SEARCH_MODE_DEFAULT_KEY;
5027
- lastSearchModeByProject.set(modeKey, embeddingEnabled ? "hybrid" : "fulltext");
5072
+ const quality = options.quality ?? "balanced";
5028
5073
  const database = await getDb();
5074
+ lastSearchModeByProject.set(
5075
+ modeKey,
5076
+ quality === "fast" ? "fulltext (fast profile)" : embeddingEnabled ? "hybrid" : "fulltext"
5077
+ );
5029
5078
  let projectIds = null;
5030
5079
  if (options.projectId) {
5031
5080
  try {
@@ -5047,11 +5096,15 @@ async function searchObservations(options) {
5047
5096
  }
5048
5097
  const hasQuery = options.query && options.query.trim().length > 0;
5049
5098
  const originalQuery = options.query;
5050
- const tier = hasQuery ? classifyQueryTier(originalQuery) : "fast";
5099
+ const tier = quality === "fast" ? "fast" : hasQuery ? classifyQueryTier(originalQuery) : "fast";
5051
5100
  mark(`tier=${tier}`);
5052
- const expandedEmbeddingQuery = tier === "heavy" ? await maybeExpandSearchQuery(options.query) : options.query;
5101
+ if (quality === "thorough" && hasQuery) {
5102
+ const { initLLM: initLLM2, isLLMEnabled: isLLMEnabled2 } = await Promise.resolve().then(() => (init_provider(), provider_exports));
5103
+ if (!isLLMEnabled2()) initLLM2();
5104
+ }
5105
+ const expandedEmbeddingQuery = quality === "thorough" && tier === "heavy" ? await maybeExpandSearchQuery(options.query) : options.query;
5053
5106
  mark("queryExpansion");
5054
- const intentResult = hasQuery ? detectQueryIntent(originalQuery) : null;
5107
+ const intentResult = quality === "fast" || !hasQuery ? null : detectQueryIntent(originalQuery);
5055
5108
  const requestLimit = projectIds ? (options.limit ?? 20) * 3 : options.limit ?? 20;
5056
5109
  const defaultBoost = {
5057
5110
  title: 3,
@@ -5065,17 +5118,21 @@ async function searchObservations(options) {
5065
5118
  let searchParams = {
5066
5119
  term: originalQuery,
5067
5120
  limit: requestLimit,
5068
- includeVectors: true,
5121
+ // Fast search never uses vectors, so returning them only adds work and
5122
+ // payload pressure to an explicitly latency-sensitive path.
5123
+ includeVectors: quality !== "fast",
5069
5124
  ...Object.keys(filters).length > 0 ? { where: filters } : {},
5070
5125
  // Search specific fields (not tokens, accessCount, etc.)
5071
5126
  properties: ["title", "entityName", "narrative", "facts", "concepts", "filesModified"],
5072
5127
  // Field boosting: intent-aware or default
5073
5128
  boost: fieldBoost,
5074
5129
  // Fuzzy tolerance: allow 1-char typos for short queries, 2 for longer
5075
- ...hasQuery ? { tolerance: originalQuery.length > 6 ? 2 : 1 } : {}
5130
+ // Fuzzy matching is useful for ordinary recall, but grows sharply with
5131
+ // corpus size. The fast profile deliberately uses exact lexical matching.
5132
+ ...hasQuery && quality !== "fast" ? { tolerance: originalQuery.length > 6 ? 2 : 1 } : {}
5076
5133
  };
5077
5134
  let queryVector = null;
5078
- if (embeddingEnabled && hasQuery && tier !== "fast") {
5135
+ if (quality !== "fast" && embeddingEnabled && hasQuery && tier !== "fast") {
5079
5136
  try {
5080
5137
  const provider2 = await getEmbeddingProvider();
5081
5138
  if (provider2) {
@@ -5090,11 +5147,11 @@ async function searchObservations(options) {
5090
5147
  );
5091
5148
  } else {
5092
5149
  const EMBEDDING_TIMEOUT_MS = 15e3;
5093
- const embedPromise = provider2.embed(expandedEmbeddingQuery);
5094
- const timeoutPromise = new Promise(
5095
- (_, reject) => setTimeout(() => reject(new Error(`Embedding timeout after ${EMBEDDING_TIMEOUT_MS}ms`)), EMBEDDING_TIMEOUT_MS)
5150
+ queryVector = await withTimeout(
5151
+ provider2.embed(expandedEmbeddingQuery),
5152
+ EMBEDDING_TIMEOUT_MS,
5153
+ "Embedding"
5096
5154
  );
5097
- queryVector = await Promise.race([embedPromise, timeoutPromise]);
5098
5155
  mark("embedding");
5099
5156
  const cjkRatio = (originalQuery.match(/[\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]/g) || []).length / originalQuery.length;
5100
5157
  const isCJKHeavy = cjkRatio > 0.3;
@@ -5335,7 +5392,7 @@ async function searchObservations(options) {
5335
5392
  }
5336
5393
  }
5337
5394
  }
5338
- const shouldRerank = tier === "heavy" && hasQuery && intermediate.length > 2 && (() => {
5395
+ const shouldRerank = quality === "thorough" && tier === "heavy" && hasQuery && intermediate.length > 2 && (() => {
5339
5396
  const top = intermediate[0]?.score ?? 0;
5340
5397
  const second = intermediate[1]?.score ?? 0;
5341
5398
  return top > 0 && second / top > 0.7;
@@ -5359,11 +5416,11 @@ async function searchObservations(options) {
5359
5416
  }));
5360
5417
  const _parsedRerank = parseInt(process.env.MEMORIX_RERANK_TIMEOUT_MS || "", 10);
5361
5418
  const RERANK_TIMEOUT_MS = Number.isFinite(_parsedRerank) && _parsedRerank > 0 ? _parsedRerank : 5e3;
5362
- const rerankPromise = rerankResults2(originalQuery, candidates);
5363
- const timeoutPromise = new Promise(
5364
- (_, reject) => setTimeout(() => reject(new Error(`LLM rerank timeout after ${RERANK_TIMEOUT_MS}ms`)), RERANK_TIMEOUT_MS)
5419
+ const { reranked, usedLLM } = await withTimeout(
5420
+ rerankResults2(originalQuery, candidates),
5421
+ RERANK_TIMEOUT_MS,
5422
+ "LLM rerank"
5365
5423
  );
5366
- const { reranked, usedLLM } = await Promise.race([rerankPromise, timeoutPromise]);
5367
5424
  mark(`rerank(usedLLM=${usedLLM})`);
5368
5425
  if (usedLLM) {
5369
5426
  lastSearchModeByProject.set(modeKey, (lastSearchModeByProject.get(modeKey) ?? "fulltext") + " + LLM rerank");
@@ -5555,6 +5612,7 @@ var init_orama_store = __esm({
5555
5612
  init_project_affinity();
5556
5613
  init_intent_detector();
5557
5614
  init_query_expansion();
5615
+ init_timeout();
5558
5616
  db = null;
5559
5617
  dbInitPromise = null;
5560
5618
  dbGeneration = 0;
@@ -11799,6 +11857,7 @@ var DEFAULT_TIMEOUT_MS = 5 * 6e4;
11799
11857
  // src/runtime/project-maintenance.ts
11800
11858
  init_esm_shims();
11801
11859
  init_lifecycle();
11860
+ init_timeout();
11802
11861
  var DEFAULT_VECTOR_BATCH_SIZE = 12;
11803
11862
  var DEFAULT_RETENTION_BATCH_SIZE = 100;
11804
11863
  var DEFAULT_CONSOLIDATION_BATCH_SIZE = 200;
@@ -11869,21 +11928,6 @@ async function loadWorkspaceForMaintenance(projectId, projectDir2, mode) {
11869
11928
  ]);
11870
11929
  return versioned ?? local;
11871
11930
  }
11872
- function withTimeout(promise, ms, label) {
11873
- return new Promise((resolve, reject) => {
11874
- const timer = setTimeout(() => reject(new Error(`${label} timed out after ${ms}ms`)), ms);
11875
- promise.then(
11876
- (value) => {
11877
- clearTimeout(timer);
11878
- resolve(value);
11879
- },
11880
- (error) => {
11881
- clearTimeout(timer);
11882
- reject(error);
11883
- }
11884
- );
11885
- });
11886
- }
11887
11931
  async function runAutomaticConsolidation(projectId, projectDir2, options) {
11888
11932
  const { isLLMEnabled: isLLMEnabled2 } = await Promise.resolve().then(() => (init_provider(), provider_exports));
11889
11933
  if (!isLLMEnabled2()) {