memorix 1.7.2 → 1.7.3

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.
Files changed (39) hide show
  1. package/CHANGELOG.md +27 -1
  2. package/assets/star-history-dark.svg +4 -4
  3. package/assets/star-history-light.svg +4 -4
  4. package/assets/star-history.json +47 -3
  5. package/dist/cli/index.js +396 -64
  6. package/dist/index.js +368 -62
  7. package/dist/maintenance-runner.js +365 -58
  8. package/dist/media-audio-runner.js +365 -58
  9. package/dist/media-video-runner.js +365 -58
  10. package/dist/memcode-runtime/CHANGELOG.md +27 -1
  11. package/dist/sdk.js +367 -61
  12. package/dist/vector-backfill-runner.js +365 -58
  13. package/docs/ARCHITECTURE.md +1 -1
  14. package/docs/CONFIGURATION.md +57 -1
  15. package/docs/PERFORMANCE.md +3 -1
  16. package/docs/SETUP.md +2 -1
  17. package/package.json +1 -1
  18. package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
  19. package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
  20. package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
  21. package/plugins/copilot/memorix/plugin.json +1 -1
  22. package/plugins/gemini/memorix/gemini-extension.json +1 -1
  23. package/plugins/omp/memorix/package.json +1 -1
  24. package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
  25. package/plugins/pi/memorix/package.json +1 -1
  26. package/server.json +2 -2
  27. package/src/cli/commands/config-migrate.ts +7 -0
  28. package/src/cli/commands/doctor.ts +5 -1
  29. package/src/cli/commands/init.ts +17 -0
  30. package/src/cli/commands/status.ts +2 -0
  31. package/src/config/resolved-config.ts +110 -12
  32. package/src/config/toml-loader.ts +8 -0
  33. package/src/config/yaml-loader.ts +9 -0
  34. package/src/llm/provider.ts +33 -7
  35. package/src/multimodal/image-loader.ts +6 -3
  36. package/src/rerank/http-provider.ts +125 -0
  37. package/src/rerank/index.ts +81 -0
  38. package/src/rerank/policy.ts +88 -0
  39. package/src/store/orama-store.ts +79 -53
package/dist/cli/index.js CHANGED
@@ -3232,7 +3232,7 @@ ${import_picocolors.default.gray(m3)} ${s4}
3232
3232
 
3233
3233
  // src/cli/version.ts
3234
3234
  function getCliVersion() {
3235
- return true ? "1.7.2" : pkg.version;
3235
+ return true ? "1.7.3" : pkg.version;
3236
3236
  }
3237
3237
  var init_version = __esm({
3238
3238
  "src/cli/version.ts"() {
@@ -20748,6 +20748,7 @@ function loadYamlConfig(projectRoot) {
20748
20748
  llm: { ...userConfig.llm, ...projectConfig.llm },
20749
20749
  agent: { ...userConfig.agent, ...projectConfig.agent },
20750
20750
  embedding: { ...userConfig.embedding, ...projectConfig.embedding },
20751
+ rerank: { ...userConfig.rerank, ...projectConfig.rerank },
20751
20752
  git: { ...userConfig.git, ...projectConfig.git },
20752
20753
  codegraph: { ...userConfig.codegraph, ...projectConfig.codegraph },
20753
20754
  behavior: { ...userConfig.behavior, ...projectConfig.behavior },
@@ -21024,6 +21025,7 @@ function mergeTomlConfig(base, override) {
21024
21025
  llm: { ...base.memory?.llm, ...override.memory?.llm }
21025
21026
  },
21026
21027
  embedding: { ...base.embedding, ...override.embedding },
21028
+ rerank: { ...base.rerank, ...override.rerank },
21027
21029
  hooks: { ...base.hooks, ...override.hooks },
21028
21030
  git: { ...base.git, ...override.git },
21029
21031
  codegraph: { ...base.codegraph, ...override.codegraph },
@@ -21575,6 +21577,7 @@ __export(resolved_config_exports, {
21575
21577
  getResolvedConfigForCwd: () => getResolvedConfigForCwd,
21576
21578
  getResolvedEmbeddingLane: () => getResolvedEmbeddingLane,
21577
21579
  getResolvedMemoryLane: () => getResolvedMemoryLane,
21580
+ getResolvedRerankLane: () => getResolvedRerankLane,
21578
21581
  resetResolvedConfigCache: () => resetResolvedConfigCache
21579
21582
  });
21580
21583
  import { homedir as homedir5 } from "os";
@@ -21607,6 +21610,16 @@ function getResolvedConfig(options = {}) {
21607
21610
  legacy.llm?.baseUrl
21608
21611
  );
21609
21612
  const openRouterMemoryLlmApiKey = isOpenRouterMemoryLane(memoryLlmProvider, memoryLlmBaseUrl) ? process.env.OPENROUTER_API_KEY : void 0;
21613
+ const memoryLlmApiKey = first(
21614
+ process.env.MEMORIX_LLM_API_KEY,
21615
+ process.env.MEMORIX_API_KEY,
21616
+ toml.memory?.llm?.api_key,
21617
+ yaml.llm?.apiKey,
21618
+ legacy.llm?.apiKey,
21619
+ process.env.OPENAI_API_KEY,
21620
+ process.env.ANTHROPIC_API_KEY,
21621
+ openRouterMemoryLlmApiKey
21622
+ );
21610
21623
  const resolved = {
21611
21624
  agent: {
21612
21625
  provider: first(
@@ -21647,16 +21660,7 @@ function getResolvedConfig(options = {}) {
21647
21660
  provider: memoryLlmProvider,
21648
21661
  model: memoryLlmModel,
21649
21662
  baseUrl: memoryLlmBaseUrl,
21650
- apiKey: first(
21651
- process.env.MEMORIX_LLM_API_KEY,
21652
- process.env.MEMORIX_API_KEY,
21653
- toml.memory?.llm?.api_key,
21654
- yaml.llm?.apiKey,
21655
- legacy.llm?.apiKey,
21656
- process.env.OPENAI_API_KEY,
21657
- process.env.ANTHROPIC_API_KEY,
21658
- openRouterMemoryLlmApiKey
21659
- )
21663
+ apiKey: memoryLlmApiKey
21660
21664
  }
21661
21665
  },
21662
21666
  embedding: {
@@ -21666,6 +21670,12 @@ function getResolvedConfig(options = {}) {
21666
21670
  apiKey: first(process.env.MEMORIX_EMBEDDING_API_KEY, toml.embedding?.api_key, yaml.embedding?.apiKey, legacy.embeddingApi?.apiKey, openRouterEmbeddingApiKey),
21667
21671
  dimensions: firstNumber(parseNumber(process.env.MEMORIX_EMBEDDING_DIMENSIONS), toml.embedding?.dimensions, yaml.embedding?.dimensions, legacy.embeddingApi?.dimensions)
21668
21672
  },
21673
+ rerank: resolveRerankLane({
21674
+ toml,
21675
+ yaml,
21676
+ memoryLlmApiKey,
21677
+ memoryLlmBaseUrl
21678
+ }),
21669
21679
  git: {
21670
21680
  autoHook: firstBool(toml.git?.auto_hook, yaml.git?.autoHook),
21671
21681
  ingestOnCommit: firstBool(toml.git?.ingest_on_commit, yaml.git?.ingestOnCommit),
@@ -21735,6 +21745,9 @@ function getResolvedMemoryLane(options = {}) {
21735
21745
  function getResolvedEmbeddingLane(options = {}) {
21736
21746
  return getResolvedConfig(options).embedding;
21737
21747
  }
21748
+ function getResolvedRerankLane(options = {}) {
21749
+ return getResolvedConfig(options).rerank;
21750
+ }
21738
21751
  function resetResolvedConfigCache() {
21739
21752
  }
21740
21753
  function first(...values) {
@@ -21777,6 +21790,10 @@ function getEnvSourceNames() {
21777
21790
  "MEMORIX_EMBEDDING_BASE_URL",
21778
21791
  "MEMORIX_EMBEDDING_MODEL",
21779
21792
  "MEMORIX_EMBEDDING_DIMENSIONS",
21793
+ "MEMORIX_RERANK_PROVIDER",
21794
+ "MEMORIX_RERANK_MODEL",
21795
+ "MEMORIX_RERANK_BASE_URL",
21796
+ "MEMORIX_RERANK_API_KEY",
21780
21797
  "MEMORIX_CODEGRAPH_EXTERNAL_CONTEXT",
21781
21798
  "MEMORIX_CODEGRAPH_EXTERNAL_COMMAND",
21782
21799
  "MEMORIX_CODEGRAPH_EXTERNAL_TIMEOUT_MS",
@@ -21800,6 +21817,63 @@ function normalizeExternalContext(value) {
21800
21817
  if (normalized === "auto" || normalized === "off") return normalized;
21801
21818
  return void 0;
21802
21819
  }
21820
+ function normalizeRerankProvider(value) {
21821
+ const normalized = value?.trim().toLowerCase();
21822
+ if (normalized === "http") return "http";
21823
+ return "off";
21824
+ }
21825
+ function resolveRerankLane(args) {
21826
+ const provider2 = normalizeRerankProvider(first(
21827
+ process.env.MEMORIX_RERANK_PROVIDER,
21828
+ args.toml.rerank?.provider,
21829
+ args.yaml.rerank?.provider,
21830
+ "off"
21831
+ ));
21832
+ const model = first(
21833
+ process.env.MEMORIX_RERANK_MODEL,
21834
+ args.toml.rerank?.model,
21835
+ args.yaml.rerank?.model
21836
+ );
21837
+ const explicitBaseUrl = first(
21838
+ process.env.MEMORIX_RERANK_BASE_URL,
21839
+ args.toml.rerank?.base_url,
21840
+ args.yaml.rerank?.baseUrl
21841
+ );
21842
+ const baseUrl = provider2 === "http" ? first(explicitBaseUrl, args.memoryLlmBaseUrl) : explicitBaseUrl;
21843
+ const dedicatedApiKey = first(
21844
+ process.env.MEMORIX_RERANK_API_KEY,
21845
+ args.toml.rerank?.api_key,
21846
+ args.yaml.rerank?.apiKey
21847
+ );
21848
+ const sameEndpoint = provider2 === "http" && sameTrustedEndpoint(baseUrl, args.memoryLlmBaseUrl);
21849
+ const inheritedApiKey = sameEndpoint ? args.memoryLlmApiKey : void 0;
21850
+ const apiKey = first(dedicatedApiKey, inheritedApiKey);
21851
+ const canSendRequest = provider2 === "http" && Boolean(baseUrl) && (Boolean(apiKey) || !args.memoryLlmApiKey || sameEndpoint);
21852
+ return { provider: provider2, model, baseUrl, apiKey, canSendRequest };
21853
+ }
21854
+ function sameTrustedEndpoint(left, right) {
21855
+ const a4 = normalizeTrustedEndpoint(left);
21856
+ const b4 = normalizeTrustedEndpoint(right);
21857
+ return Boolean(a4 && b4 && a4 === b4);
21858
+ }
21859
+ function normalizeTrustedEndpoint(value) {
21860
+ const trimmed = value?.trim();
21861
+ if (!trimmed) return void 0;
21862
+ try {
21863
+ const url2 = new URL(trimmed);
21864
+ const protocol = url2.protocol.toLowerCase();
21865
+ const hostname2 = url2.hostname.toLowerCase();
21866
+ const port = url2.port;
21867
+ let pathname = url2.pathname.replace(/\/+$/, "");
21868
+ if (pathname.toLowerCase().endsWith("/rerank")) {
21869
+ pathname = pathname.slice(0, -"/rerank".length).replace(/\/+$/, "");
21870
+ }
21871
+ return `${protocol}//${hostname2}${port ? `:${port}` : ""}${pathname}`;
21872
+ } catch {
21873
+ const fallback = trimmed.replace(/\/+$/, "").replace(/\/rerank$/i, "").replace(/\/+$/, "");
21874
+ return fallback.toLowerCase() || void 0;
21875
+ }
21876
+ }
21803
21877
  var init_resolved_config = __esm({
21804
21878
  "src/config/resolved-config.ts"() {
21805
21879
  "use strict";
@@ -24035,9 +24109,27 @@ __export(provider_exports2, {
24035
24109
  getLLMConfig: () => getLLMConfig,
24036
24110
  initLLM: () => initLLM,
24037
24111
  isLLMEnabled: () => isLLMEnabled,
24112
+ normalizeOpenAICompatibleBaseUrl: () => normalizeOpenAICompatibleBaseUrl,
24038
24113
  parseLLMTimeoutMs: () => parseLLMTimeoutMs,
24039
24114
  setLLMConfig: () => setLLMConfig
24040
24115
  });
24116
+ function normalizeOpenAICompatibleBaseUrl(baseUrl) {
24117
+ const base = baseUrl.trim().replace(/\/+$/, "");
24118
+ if (!base) return base;
24119
+ let pathname = "";
24120
+ try {
24121
+ pathname = new URL(base).pathname.replace(/\/+$/, "");
24122
+ } catch {
24123
+ pathname = base.split(/[?#]/, 1)[0].replace(/\/+$/, "");
24124
+ }
24125
+ const lastSegment = pathname.split("/").filter(Boolean).at(-1);
24126
+ if (lastSegment && OPENAI_API_VERSION_SEGMENT.test(lastSegment)) {
24127
+ return base;
24128
+ }
24129
+ const queryOrHash = base.match(/[?#].*$/)?.[0] ?? "";
24130
+ const pathWithoutQuery = base.slice(0, base.length - queryOrHash.length).replace(/\/+$/, "");
24131
+ return `${pathWithoutQuery}/v1${queryOrHash}`;
24132
+ }
24041
24133
  function parseLLMTimeoutMs(raw) {
24042
24134
  if (raw === void 0 || raw.trim() === "") return LLM_TIMEOUT_DEFAULT_MS;
24043
24135
  const parsed = Number(raw);
@@ -24141,8 +24233,7 @@ async function callLLM(systemPrompt, userMessage, signal) {
24141
24233
  }
24142
24234
  async function callOpenAICompatible(systemPrompt, userMessage, signal) {
24143
24235
  const config2 = currentConfig;
24144
- let base = config2.baseUrl.replace(/\/+$/, "");
24145
- if (!base.endsWith("/v1")) base += "/v1";
24236
+ const base = normalizeOpenAICompatibleBaseUrl(config2.baseUrl);
24146
24237
  const url2 = `${base}/chat/completions`;
24147
24238
  const fetchSignal = signal ? AbortSignal.any([signal, AbortSignal.timeout(LLM_CALL_TIMEOUT_MS)]) : AbortSignal.timeout(LLM_CALL_TIMEOUT_MS);
24148
24239
  const response = await fetch(url2, {
@@ -24221,8 +24312,7 @@ async function callLLMWithTools(messages, tools, signal) {
24221
24312
  }
24222
24313
  async function callOpenAIWithTools(messages, tools, signal) {
24223
24314
  const config2 = currentConfig;
24224
- let base = config2.baseUrl.replace(/\/+$/, "");
24225
- if (!base.endsWith("/v1")) base += "/v1";
24315
+ const base = normalizeOpenAICompatibleBaseUrl(config2.baseUrl);
24226
24316
  const url2 = `${base}/chat/completions`;
24227
24317
  const openaiMessages = messages.map((msg) => {
24228
24318
  if (msg.role === "tool") {
@@ -24372,8 +24462,7 @@ async function callAnthropicWithTools(messages, tools, signal) {
24372
24462
  }
24373
24463
  async function* callOpenAIWithToolsStream(messages, tools) {
24374
24464
  const config2 = currentConfig;
24375
- let base = config2.baseUrl.replace(/\/+$/, "");
24376
- if (!base.endsWith("/v1")) base += "/v1";
24465
+ const base = normalizeOpenAICompatibleBaseUrl(config2.baseUrl);
24377
24466
  const url2 = `${base}/chat/completions`;
24378
24467
  const openaiMessages = messages.map((msg) => {
24379
24468
  if (msg.role === "tool") {
@@ -24621,7 +24710,7 @@ async function* callAnthropicWithToolsStream(messages, tools) {
24621
24710
  }
24622
24711
  };
24623
24712
  }
24624
- var LLM_TIMEOUT_DEFAULT_MS, LLM_TIMEOUT_MIN_MS, LLM_TIMEOUT_MAX_MS, MAX_LLM_RESPONSE_BYTES, LLM_CALL_TIMEOUT_MS, PROVIDER_DEFAULTS, currentConfig;
24713
+ var LLM_TIMEOUT_DEFAULT_MS, LLM_TIMEOUT_MIN_MS, LLM_TIMEOUT_MAX_MS, MAX_LLM_RESPONSE_BYTES, OPENAI_API_VERSION_SEGMENT, LLM_CALL_TIMEOUT_MS, PROVIDER_DEFAULTS, currentConfig;
24625
24714
  var init_provider2 = __esm({
24626
24715
  "src/llm/provider.ts"() {
24627
24716
  "use strict";
@@ -24631,6 +24720,7 @@ var init_provider2 = __esm({
24631
24720
  LLM_TIMEOUT_MIN_MS = 1e3;
24632
24721
  LLM_TIMEOUT_MAX_MS = 3e5;
24633
24722
  MAX_LLM_RESPONSE_BYTES = 2 * 1024 * 1024;
24723
+ OPENAI_API_VERSION_SEGMENT = /^v\d+(?:[a-z][a-z0-9-]*)?$/i;
24634
24724
  LLM_CALL_TIMEOUT_MS = parseLLMTimeoutMs(process.env.MEMORIX_LLM_TIMEOUT_MS);
24635
24725
  PROVIDER_DEFAULTS = {
24636
24726
  openai: { baseUrl: "https://api.openai.com/v1", model: "gpt-4.1-nano" },
@@ -24690,6 +24780,48 @@ Rules:
24690
24780
  }
24691
24781
  });
24692
24782
 
24783
+ // src/rerank/policy.ts
24784
+ function parseRerankTimeoutMs(raw) {
24785
+ const value = raw?.trim();
24786
+ if (!value) return DEFAULT_RERANK_TIMEOUT_MS;
24787
+ const parsed = Number(value);
24788
+ if (!Number.isInteger(parsed) || Number.isNaN(parsed)) {
24789
+ console.warn(
24790
+ `[memorix] MEMORIX_RERANK_TIMEOUT_MS="${raw}" is invalid (must be a positive integer between ${RERANK_TIMEOUT_MIN_MS}-${RERANK_TIMEOUT_MAX_MS}ms). Using default ${DEFAULT_RERANK_TIMEOUT_MS}ms.`
24791
+ );
24792
+ return DEFAULT_RERANK_TIMEOUT_MS;
24793
+ }
24794
+ if (parsed < RERANK_TIMEOUT_MIN_MS) return RERANK_TIMEOUT_MIN_MS;
24795
+ if (parsed > RERANK_TIMEOUT_MAX_MS) return RERANK_TIMEOUT_MAX_MS;
24796
+ return parsed;
24797
+ }
24798
+ function resolveRerankTimeoutMs(explicitTimeoutMs) {
24799
+ if (typeof explicitTimeoutMs === "number" && Number.isFinite(explicitTimeoutMs) && explicitTimeoutMs > 0) {
24800
+ return explicitTimeoutMs;
24801
+ }
24802
+ return parseRerankTimeoutMs(process.env.MEMORIX_RERANK_TIMEOUT_MS);
24803
+ }
24804
+ function shouldAttemptHttpRerank(input) {
24805
+ return input.quality === "thorough" && input.hasQuery && input.candidateCount >= MIN_RERANK_CANDIDATES;
24806
+ }
24807
+ function shouldAttemptLlmRerank(input) {
24808
+ if (input.httpRerankConfigured) return false;
24809
+ if (input.quality !== "thorough" || input.tier !== "heavy" || !input.hasQuery) return false;
24810
+ if (input.candidateCount < MIN_RERANK_CANDIDATES) return false;
24811
+ return input.topScore > 0 && input.secondScore / input.topScore > 0.7;
24812
+ }
24813
+ var DEFAULT_RERANK_TIMEOUT_MS, RERANK_TIMEOUT_MIN_MS, RERANK_TIMEOUT_MAX_MS, MIN_RERANK_CANDIDATES;
24814
+ var init_policy = __esm({
24815
+ "src/rerank/policy.ts"() {
24816
+ "use strict";
24817
+ init_esm_shims();
24818
+ DEFAULT_RERANK_TIMEOUT_MS = 3e4;
24819
+ RERANK_TIMEOUT_MIN_MS = 1e3;
24820
+ RERANK_TIMEOUT_MAX_MS = 3e5;
24821
+ MIN_RERANK_CANDIDATES = 3;
24822
+ }
24823
+ });
24824
+
24693
24825
  // src/timeout.ts
24694
24826
  function withTimeout(promise2, ms2, label) {
24695
24827
  return new Promise((resolve6, reject) => {
@@ -24975,6 +25107,148 @@ var init_aliases = __esm({
24975
25107
  }
24976
25108
  });
24977
25109
 
25110
+ // src/rerank/http-provider.ts
25111
+ function buildRerankUrl(baseUrl) {
25112
+ const trimmed = baseUrl.trim().replace(/\/+$/, "");
25113
+ if (trimmed.endsWith("/rerank")) return trimmed;
25114
+ return `${trimmed}/rerank`;
25115
+ }
25116
+ function parseRerankResponse(payload) {
25117
+ const rows = extractRows(payload);
25118
+ if (rows.length === 0) {
25119
+ throw new Error("Neural rerank response contained no ranked results");
25120
+ }
25121
+ return rows.sort((a4, b4) => b4.score - a4.score);
25122
+ }
25123
+ function extractRows(payload) {
25124
+ if (Array.isArray(payload)) {
25125
+ return payload.map(rowFromUnknown).filter((row) => row !== null);
25126
+ }
25127
+ if (payload && typeof payload === "object" && "results" in payload) {
25128
+ const results = payload.results;
25129
+ if (Array.isArray(results)) {
25130
+ return results.map(rowFromUnknown).filter((row) => row !== null);
25131
+ }
25132
+ }
25133
+ throw new Error("Neural rerank response was not Cohere or TEI shaped");
25134
+ }
25135
+ function rowFromUnknown(value) {
25136
+ if (!value || typeof value !== "object") return null;
25137
+ const row = value;
25138
+ if (typeof row.index !== "number" || !Number.isFinite(row.index)) return null;
25139
+ const score = typeof row.relevance_score === "number" ? row.relevance_score : typeof row.score === "number" ? row.score : null;
25140
+ if (score === null || !Number.isFinite(score)) return null;
25141
+ return { index: row.index, score };
25142
+ }
25143
+ function buildRerankBody(query, documents, model) {
25144
+ const body = {
25145
+ query,
25146
+ documents,
25147
+ top_n: documents.length,
25148
+ return_documents: false
25149
+ };
25150
+ const trimmed = model?.trim();
25151
+ if (trimmed) body.model = trimmed;
25152
+ return body;
25153
+ }
25154
+ async function rerankViaHttp(query, documents, config2) {
25155
+ if (documents.length === 0) return [];
25156
+ const url2 = buildRerankUrl(config2.baseUrl);
25157
+ const headers = {
25158
+ "content-type": "application/json"
25159
+ };
25160
+ if (config2.apiKey) {
25161
+ headers.authorization = `Bearer ${config2.apiKey}`;
25162
+ }
25163
+ const controller = new AbortController();
25164
+ const timeoutMs = resolveRerankTimeoutMs(config2.timeoutMs);
25165
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
25166
+ try {
25167
+ const fetchImpl = config2.fetchImpl ?? fetch;
25168
+ const response = await fetchImpl(url2, {
25169
+ method: "POST",
25170
+ headers,
25171
+ signal: controller.signal,
25172
+ body: JSON.stringify(buildRerankBody(query, documents, config2.model))
25173
+ });
25174
+ if (!response.ok) {
25175
+ throw new Error(`Neural rerank HTTP ${response.status} from ${url2}`);
25176
+ }
25177
+ return parseRerankResponse(await response.json());
25178
+ } finally {
25179
+ clearTimeout(timer);
25180
+ }
25181
+ }
25182
+ var init_http_provider = __esm({
25183
+ "src/rerank/http-provider.ts"() {
25184
+ "use strict";
25185
+ init_esm_shims();
25186
+ init_policy();
25187
+ }
25188
+ });
25189
+
25190
+ // src/rerank/index.ts
25191
+ var rerank_exports = {};
25192
+ __export(rerank_exports, {
25193
+ DEFAULT_RERANK_TIMEOUT_MS: () => DEFAULT_RERANK_TIMEOUT_MS,
25194
+ MIN_RERANK_CANDIDATES: () => MIN_RERANK_CANDIDATES,
25195
+ isNeuralRerankEnabled: () => isNeuralRerankEnabled,
25196
+ neuralRerankCandidates: () => neuralRerankCandidates,
25197
+ parseRerankTimeoutMs: () => parseRerankTimeoutMs,
25198
+ resolveRerankTimeoutMs: () => resolveRerankTimeoutMs,
25199
+ shouldAttemptHttpRerank: () => shouldAttemptHttpRerank,
25200
+ shouldAttemptLlmRerank: () => shouldAttemptLlmRerank
25201
+ });
25202
+ function isNeuralRerankEnabled(options = {}) {
25203
+ const lane = getResolvedRerankLane(options);
25204
+ return lane.provider === "http" && Boolean(lane.baseUrl);
25205
+ }
25206
+ async function neuralRerankCandidates(query, candidates, options = {}) {
25207
+ if (!isNeuralRerankEnabled(options) || candidates.length === 0) return null;
25208
+ const lane = getResolvedRerankLane(options);
25209
+ if (!lane.canSendRequest || !lane.baseUrl) return null;
25210
+ const toRerank = candidates.slice(0, MAX_RERANK);
25211
+ const rest = candidates.slice(MAX_RERANK);
25212
+ const documents = toRerank.map((candidate2) => candidateText(candidate2));
25213
+ const ranked = await rerankViaHttp(query, documents, {
25214
+ provider: "http",
25215
+ model: lane.model,
25216
+ baseUrl: lane.baseUrl,
25217
+ apiKey: lane.apiKey,
25218
+ timeoutMs: resolveRerankTimeoutMs()
25219
+ });
25220
+ const seen = /* @__PURE__ */ new Set();
25221
+ const reranked = [];
25222
+ for (const row of ranked) {
25223
+ const candidate2 = toRerank[row.index];
25224
+ if (!candidate2 || seen.has(row.index)) continue;
25225
+ reranked.push(candidate2);
25226
+ seen.add(row.index);
25227
+ }
25228
+ for (const [index, candidate2] of toRerank.entries()) {
25229
+ if (!seen.has(index)) reranked.push(candidate2);
25230
+ }
25231
+ reranked.push(...rest);
25232
+ return reranked;
25233
+ }
25234
+ function candidateText(candidate2) {
25235
+ const narrative = candidate2.narrative?.trim();
25236
+ if (narrative) return `${candidate2.title} \u2014 ${narrative.slice(0, 200)}`;
25237
+ return candidate2.title;
25238
+ }
25239
+ var MAX_RERANK;
25240
+ var init_rerank = __esm({
25241
+ "src/rerank/index.ts"() {
25242
+ "use strict";
25243
+ init_esm_shims();
25244
+ init_resolved_config();
25245
+ init_http_provider();
25246
+ init_policy();
25247
+ init_policy();
25248
+ MAX_RERANK = 10;
25249
+ }
25250
+ });
25251
+
24978
25252
  // src/llm/quality.ts
24979
25253
  var quality_exports = {};
24980
25254
  __export(quality_exports, {
@@ -25014,9 +25288,9 @@ async function rerankResults(query, candidates) {
25014
25288
  if (!isLLMEnabled() || candidates.length <= 2) {
25015
25289
  return { reranked: candidates, usedLLM: false };
25016
25290
  }
25017
- const MAX_RERANK = 10;
25018
- const toRerank = candidates.slice(0, MAX_RERANK);
25019
- const rest = candidates.slice(MAX_RERANK);
25291
+ const MAX_RERANK2 = 10;
25292
+ const toRerank = candidates.slice(0, MAX_RERANK2);
25293
+ const rest = candidates.slice(MAX_RERANK2);
25020
25294
  try {
25021
25295
  const candidateList = toRerank.map(
25022
25296
  (c6) => `[ID: ${c6.id}] (${c6.type}) ${c6.title}${c6.narrative ? ` \u2014 ${c6.narrative.substring(0, 100)}` : ""}`
@@ -185792,46 +186066,78 @@ async function searchObservations(options) {
185792
186066
  }
185793
186067
  }
185794
186068
  }
185795
- const shouldRerank = quality === "thorough" && tier === "heavy" && hasQuery && intermediate.length > 2 && (() => {
185796
- const top = intermediate[0]?.score ?? 0;
185797
- const second2 = intermediate[1]?.score ?? 0;
185798
- return top > 0 && second2 / top > 0.7;
185799
- })();
185800
- if (shouldRerank) {
185801
- try {
185802
- const { rerankResults: rerankResults2 } = await Promise.resolve().then(() => (init_quality(), quality_exports));
185803
- const narrativeMap = /* @__PURE__ */ new Map();
185804
- for (const hit of results.hits) {
185805
- const doc = hit.document;
185806
- narrativeMap.set(makeEntryKey(doc.projectId, doc.observationId), doc.narrative);
185807
- }
185808
- const RERANK_TOP_K = 5;
185809
- const toRerank = intermediate.slice(0, RERANK_TOP_K);
185810
- const candidates = toRerank.map((e5, index) => ({
185811
- id: `r${index + 1}`,
185812
- title: e5.title,
185813
- type: e5.type,
185814
- score: e5.score,
185815
- narrative: narrativeMap.get(makeEntryKey(e5.projectId, e5.id))
185816
- }));
185817
- const _parsedRerank = parseInt(process.env.MEMORIX_RERANK_TIMEOUT_MS || "", 10);
185818
- const RERANK_TIMEOUT_MS = Number.isFinite(_parsedRerank) && _parsedRerank > 0 ? _parsedRerank : 5e3;
185819
- const { reranked, usedLLM } = await withTimeout(
185820
- rerankResults2(originalQuery, candidates),
185821
- RERANK_TIMEOUT_MS,
185822
- "LLM rerank"
185823
- );
185824
- mark(`rerank(usedLLM=${usedLLM})`);
185825
- if (usedLLM) {
185826
- lastSearchModeByProject.set(modeKey, (lastSearchModeByProject.get(modeKey) ?? "fulltext") + " + LLM rerank");
185827
- const candidateMap = new Map(candidates.map((candidate2, index) => [candidate2.id, toRerank[index]]));
185828
- const rerankedTop = reranked.map((r6) => candidateMap.get(r6.id)).filter((e5) => e5 != null);
185829
- if (rerankedTop.length > 0) {
185830
- intermediate = [...rerankedTop, ...intermediate.slice(RERANK_TOP_K)];
186069
+ const httpGate = shouldAttemptHttpRerank({
186070
+ quality,
186071
+ hasQuery: Boolean(hasQuery),
186072
+ candidateCount: intermediate.length
186073
+ });
186074
+ const llmGateWithoutHttp = shouldAttemptLlmRerank({
186075
+ quality,
186076
+ tier,
186077
+ hasQuery: Boolean(hasQuery),
186078
+ candidateCount: intermediate.length,
186079
+ topScore: intermediate[0]?.score ?? 0,
186080
+ secondScore: intermediate[1]?.score ?? 0,
186081
+ httpRerankConfigured: false
186082
+ });
186083
+ if (httpGate || llmGateWithoutHttp) {
186084
+ const narrativeMap = /* @__PURE__ */ new Map();
186085
+ for (const hit of results.hits) {
186086
+ const doc = hit.document;
186087
+ narrativeMap.set(makeEntryKey(doc.projectId, doc.observationId), doc.narrative);
186088
+ }
186089
+ const RERANK_TOP_K = 5;
186090
+ const toRerank = intermediate.slice(0, RERANK_TOP_K);
186091
+ const candidates = toRerank.map((e5, index) => ({
186092
+ id: `r${index + 1}`,
186093
+ title: e5.title,
186094
+ type: e5.type,
186095
+ score: e5.score,
186096
+ narrative: narrativeMap.get(makeEntryKey(e5.projectId, e5.id))
186097
+ }));
186098
+ const RERANK_TIMEOUT_MS = parseRerankTimeoutMs(process.env.MEMORIX_RERANK_TIMEOUT_MS);
186099
+ const candidateMap = new Map(candidates.map((candidate2, index) => [candidate2.id, toRerank[index]]));
186100
+ const { isNeuralRerankEnabled: isNeuralRerankEnabled2, neuralRerankCandidates: neuralRerankCandidates2 } = await Promise.resolve().then(() => (init_rerank(), rerank_exports));
186101
+ const httpConfigured = isNeuralRerankEnabled2();
186102
+ if (httpConfigured && httpGate) {
186103
+ try {
186104
+ const neural = await withTimeout(
186105
+ neuralRerankCandidates2(originalQuery, candidates),
186106
+ RERANK_TIMEOUT_MS,
186107
+ "neural rerank"
186108
+ );
186109
+ if (neural && neural.length > 0) {
186110
+ const rerankedTop = neural.map((row) => candidateMap.get(row.id)).filter((entry) => entry != null);
186111
+ if (rerankedTop.length > 0) {
186112
+ intermediate = [...rerankedTop, ...intermediate.slice(RERANK_TOP_K)];
186113
+ lastSearchModeByProject.set(modeKey, (lastSearchModeByProject.get(modeKey) ?? "fulltext") + " + neural rerank");
186114
+ mark("rerank(usedNeural=true)");
186115
+ }
185831
186116
  }
186117
+ } catch {
186118
+ console.error("[memorix] neural rerank failed or timed out; keeping original order");
185832
186119
  }
185833
- } catch (error40) {
185834
- console.error("[memorix] LLM rerank failed or timed out, using original order");
186120
+ } else if (!httpConfigured && llmGateWithoutHttp) {
186121
+ try {
186122
+ const { rerankResults: rerankResults2 } = await Promise.resolve().then(() => (init_quality(), quality_exports));
186123
+ const { reranked, usedLLM } = await withTimeout(
186124
+ rerankResults2(originalQuery, candidates),
186125
+ RERANK_TIMEOUT_MS,
186126
+ "LLM rerank"
186127
+ );
186128
+ mark(`rerank(usedLLM=${usedLLM})`);
186129
+ if (usedLLM) {
186130
+ lastSearchModeByProject.set(modeKey, (lastSearchModeByProject.get(modeKey) ?? "fulltext") + " + LLM rerank");
186131
+ const rerankedTop = reranked.map((row) => candidateMap.get(row.id)).filter((entry) => entry != null);
186132
+ if (rerankedTop.length > 0) {
186133
+ intermediate = [...rerankedTop, ...intermediate.slice(RERANK_TOP_K)];
186134
+ }
186135
+ }
186136
+ } catch {
186137
+ console.error("[memorix] LLM rerank failed or timed out, using original order");
186138
+ }
186139
+ } else {
186140
+ mark(`rerank(skipped,tier=${tier})`);
185835
186141
  }
185836
186142
  } else {
185837
186143
  mark(`rerank(skipped,tier=${tier})`);
@@ -186013,6 +186319,7 @@ var init_orama_store = __esm({
186013
186319
  init_project_affinity();
186014
186320
  init_intent_detector();
186015
186321
  init_query_expansion();
186322
+ init_policy();
186016
186323
  init_timeout();
186017
186324
  db = null;
186018
186325
  dbInitPromise = null;
@@ -190318,6 +190625,16 @@ function buildInitTomlConfig(options) {
190318
190625
  }
190319
190626
  }
190320
190627
  lines.push("");
190628
+ lines.push("[rerank]");
190629
+ lines.push("# Optional HTTP rerank against any Cohere-compatible /rerank API.");
190630
+ lines.push('# provider = "http"');
190631
+ lines.push('# model = "rerank-model"');
190632
+ lines.push("# base_url inherits [memory.llm] when unset.");
190633
+ if (isGlobal) {
190634
+ lines.push("# Bearer inherits the memory LLM key only when that endpoint matches.");
190635
+ lines.push("# A different rerank URL needs MEMORIX_RERANK_API_KEY.");
190636
+ }
190637
+ lines.push("");
190321
190638
  lines.push("[git]");
190322
190639
  lines.push(`auto_hook = ${options.gitAutoHook}`);
190323
190640
  lines.push("ingest_on_commit = true");
@@ -190490,6 +190807,12 @@ var init_init = __esm({
190490
190807
  envLines.push("# MEMORIX_EMBEDDING_BASE_URL=https://api.openai.com/v1");
190491
190808
  envLines.push("");
190492
190809
  }
190810
+ envLines.push("# Optional HTTP rerank (Cohere-compatible POST {base_url}/rerank)");
190811
+ envLines.push("# MEMORIX_RERANK_PROVIDER=http");
190812
+ envLines.push("# MEMORIX_RERANK_MODEL=rerank-model");
190813
+ envLines.push("# MEMORIX_RERANK_BASE_URL=https://api.example.com/v1");
190814
+ envLines.push("# MEMORIX_RERANK_API_KEY= # required when BASE_URL is not the memory LLM endpoint");
190815
+ envLines.push("");
190493
190816
  envLines.push("# Optional memory LLM simple key (does not apply to embedding or agent lanes)");
190494
190817
  envLines.push("# MEMORIX_API_KEY=sk-your-key-here");
190495
190818
  envLines.push("");
@@ -194916,6 +195239,12 @@ function serializeResolvedConfigToToml(config2, options = {}) {
194916
195239
  api_key: includeSecrets ? config2.embedding.apiKey : void 0,
194917
195240
  dimensions: config2.embedding.dimensions
194918
195241
  });
195242
+ writeSection(lines, "rerank", {
195243
+ provider: config2.rerank.provider,
195244
+ model: config2.rerank.model,
195245
+ base_url: config2.rerank.baseUrl,
195246
+ api_key: includeSecrets ? config2.rerank.apiKey : void 0
195247
+ });
194919
195248
  writeSection(lines, "memory", {
194920
195249
  inject: config2.memory.inject,
194921
195250
  formation: config2.memory.formation,
@@ -259680,8 +260009,7 @@ async function callVisionLLM(systemPrompt, imageBase64, mimeType) {
259680
260009
  if (!apiKey) {
259681
260010
  throw new Error("No LLM API key configured for image analysis.");
259682
260011
  }
259683
- let baseUrl = getLLMBaseUrl("https://api.openai.com/v1").replace(/\/+$/, "");
259684
- if (!baseUrl.endsWith("/v1")) baseUrl += "/v1";
260012
+ const baseUrl = normalizeOpenAICompatibleBaseUrl(getLLMBaseUrl("https://api.openai.com/v1"));
259685
260013
  const model = getLLMModel("gpt-4o");
259686
260014
  const response = await fetch(`${baseUrl}/chat/completions`, {
259687
260015
  method: "POST",
@@ -260220,7 +260548,7 @@ The path should point to a directory containing a .git folder.`
260220
260548
  };
260221
260549
  const server = existingServer ?? new McpServer({
260222
260550
  name: "memorix",
260223
- version: true ? "1.7.2" : "1.0.1"
260551
+ version: true ? "1.7.3" : "1.0.1"
260224
260552
  });
260225
260553
  const originalRegisterTool = server.registerTool.bind(server);
260226
260554
  server.registerTool = ((name, ...args) => {
@@ -268586,6 +268914,7 @@ var init_status = __esm({
268586
268914
  diagLines.push(` Agent lane: ${formatLane(agent.provider, agent.model, agent.baseUrl, agent.apiKey)}`);
268587
268915
  diagLines.push(` Memory LLM lane: ${formatLane(resolved.memory.llm.provider, resolved.memory.llm.model, resolved.memory.llm.baseUrl, resolved.memory.llm.apiKey)}`);
268588
268916
  diagLines.push(` Embedding lane: ${formatLane(resolved.embedding.provider, resolved.embedding.model, resolved.embedding.baseUrl, resolved.embedding.apiKey)}`);
268917
+ diagLines.push(` Rerank lane: ${formatLane(resolved.rerank.provider, resolved.rerank.model, resolved.rerank.baseUrl, resolved.rerank.apiKey)}`);
268589
268918
  diagLines.push(` Memory behavior: inject=${resolved.memory.inject ?? "default"}, formation=${resolved.memory.formation ?? "default"}, autoCleanup=${resolved.memory.autoCleanup ?? "default"}`);
268590
268919
  diagLines.push(` Git behavior: autoHook=${resolved.git.autoHook ?? "default"}, ingestOnCommit=${resolved.git.ingestOnCommit ?? "default"}, maxDiffSize=${resolved.git.maxDiffSize ?? "default"}, skipMergeCommits=${resolved.git.skipMergeCommits ?? "default"}`);
268591
268920
  diagLines.push(` Server: transport=${resolved.server.transport ?? "default"}, dashboard=${resolved.server.dashboard ?? "default"}, dashboardPort=${resolved.server.dashboardPort ?? "default"}`);
@@ -268619,7 +268948,8 @@ var init_status = __esm({
268619
268948
  lanes: {
268620
268949
  agent: summarizeLane(agent.provider, agent.model, agent.baseUrl, agent.apiKey),
268621
268950
  memoryLlm: summarizeLane(resolved.memory.llm.provider, resolved.memory.llm.model, resolved.memory.llm.baseUrl, resolved.memory.llm.apiKey),
268622
- embedding: summarizeLane(resolved.embedding.provider, resolved.embedding.model, resolved.embedding.baseUrl, resolved.embedding.apiKey)
268951
+ embedding: summarizeLane(resolved.embedding.provider, resolved.embedding.model, resolved.embedding.baseUrl, resolved.embedding.apiKey),
268952
+ rerank: summarizeLane(resolved.rerank.provider, resolved.rerank.model, resolved.rerank.baseUrl, resolved.rerank.apiKey)
268623
268953
  },
268624
268954
  memory: {
268625
268955
  inject: resolved.memory.inject ?? "default",
@@ -274942,7 +275272,9 @@ var init_doctor = __esm({
274942
275272
  if (isLLMEnabled2()) {
274943
275273
  const config2 = getLLMConfig2();
274944
275274
  lines.push(ok(`Provider: ${config2?.provider}/${config2?.model}`));
274945
- lines.push(info2("Capabilities: fact extraction, auto-dedup, LLM rerank"));
275275
+ const { isNeuralRerankEnabled: isNeuralRerankEnabled2 } = await Promise.resolve().then(() => (init_rerank(), rerank_exports));
275276
+ const rerankCap = isNeuralRerankEnabled2() ? "HTTP rerank" : "LLM rerank";
275277
+ lines.push(info2(`Capabilities: fact extraction, auto-dedup, ${rerankCap}`));
274946
275278
  report.llm = { enabled: true, provider: config2?.provider, model: config2?.model };
274947
275279
  } else {
274948
275280
  lines.push(info2("LLM mode: off (heuristic dedup only)"));