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.
- package/CHANGELOG.md +27 -1
- package/assets/star-history-dark.svg +4 -4
- package/assets/star-history-light.svg +4 -4
- package/assets/star-history.json +47 -3
- package/dist/cli/index.js +396 -64
- package/dist/index.js +368 -62
- package/dist/maintenance-runner.js +365 -58
- package/dist/media-audio-runner.js +365 -58
- package/dist/media-video-runner.js +365 -58
- package/dist/memcode-runtime/CHANGELOG.md +27 -1
- package/dist/sdk.js +367 -61
- package/dist/vector-backfill-runner.js +365 -58
- package/docs/ARCHITECTURE.md +1 -1
- package/docs/CONFIGURATION.md +57 -1
- package/docs/PERFORMANCE.md +3 -1
- package/docs/SETUP.md +2 -1
- package/package.json +1 -1
- package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
- package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
- package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/copilot/memorix/plugin.json +1 -1
- package/plugins/gemini/memorix/gemini-extension.json +1 -1
- package/plugins/omp/memorix/package.json +1 -1
- package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/pi/memorix/package.json +1 -1
- package/server.json +2 -2
- package/src/cli/commands/config-migrate.ts +7 -0
- package/src/cli/commands/doctor.ts +5 -1
- package/src/cli/commands/init.ts +17 -0
- package/src/cli/commands/status.ts +2 -0
- package/src/config/resolved-config.ts +110 -12
- package/src/config/toml-loader.ts +8 -0
- package/src/config/yaml-loader.ts +9 -0
- package/src/llm/provider.ts +33 -7
- package/src/multimodal/image-loader.ts +6 -3
- package/src/rerank/http-provider.ts +125 -0
- package/src/rerank/index.ts +81 -0
- package/src/rerank/policy.ts +88 -0
- package/src/store/orama-store.ts +79 -53
package/dist/index.js
CHANGED
|
@@ -2793,6 +2793,7 @@ function loadYamlConfig(projectRoot) {
|
|
|
2793
2793
|
llm: { ...userConfig.llm, ...projectConfig.llm },
|
|
2794
2794
|
agent: { ...userConfig.agent, ...projectConfig.agent },
|
|
2795
2795
|
embedding: { ...userConfig.embedding, ...projectConfig.embedding },
|
|
2796
|
+
rerank: { ...userConfig.rerank, ...projectConfig.rerank },
|
|
2796
2797
|
git: { ...userConfig.git, ...projectConfig.git },
|
|
2797
2798
|
codegraph: { ...userConfig.codegraph, ...projectConfig.codegraph },
|
|
2798
2799
|
behavior: { ...userConfig.behavior, ...projectConfig.behavior },
|
|
@@ -3068,6 +3069,7 @@ function mergeTomlConfig(base, override) {
|
|
|
3068
3069
|
llm: { ...base.memory?.llm, ...override.memory?.llm }
|
|
3069
3070
|
},
|
|
3070
3071
|
embedding: { ...base.embedding, ...override.embedding },
|
|
3072
|
+
rerank: { ...base.rerank, ...override.rerank },
|
|
3071
3073
|
hooks: { ...base.hooks, ...override.hooks },
|
|
3072
3074
|
git: { ...base.git, ...override.git },
|
|
3073
3075
|
codegraph: { ...base.codegraph, ...override.codegraph },
|
|
@@ -3223,6 +3225,7 @@ __export(resolved_config_exports, {
|
|
|
3223
3225
|
getResolvedConfigForCwd: () => getResolvedConfigForCwd,
|
|
3224
3226
|
getResolvedEmbeddingLane: () => getResolvedEmbeddingLane,
|
|
3225
3227
|
getResolvedMemoryLane: () => getResolvedMemoryLane,
|
|
3228
|
+
getResolvedRerankLane: () => getResolvedRerankLane,
|
|
3226
3229
|
resetResolvedConfigCache: () => resetResolvedConfigCache
|
|
3227
3230
|
});
|
|
3228
3231
|
import { homedir as homedir5 } from "os";
|
|
@@ -3255,6 +3258,16 @@ function getResolvedConfig(options = {}) {
|
|
|
3255
3258
|
legacy.llm?.baseUrl
|
|
3256
3259
|
);
|
|
3257
3260
|
const openRouterMemoryLlmApiKey = isOpenRouterMemoryLane(memoryLlmProvider, memoryLlmBaseUrl) ? process.env.OPENROUTER_API_KEY : void 0;
|
|
3261
|
+
const memoryLlmApiKey = first(
|
|
3262
|
+
process.env.MEMORIX_LLM_API_KEY,
|
|
3263
|
+
process.env.MEMORIX_API_KEY,
|
|
3264
|
+
toml.memory?.llm?.api_key,
|
|
3265
|
+
yaml.llm?.apiKey,
|
|
3266
|
+
legacy.llm?.apiKey,
|
|
3267
|
+
process.env.OPENAI_API_KEY,
|
|
3268
|
+
process.env.ANTHROPIC_API_KEY,
|
|
3269
|
+
openRouterMemoryLlmApiKey
|
|
3270
|
+
);
|
|
3258
3271
|
const resolved = {
|
|
3259
3272
|
agent: {
|
|
3260
3273
|
provider: first(
|
|
@@ -3295,16 +3308,7 @@ function getResolvedConfig(options = {}) {
|
|
|
3295
3308
|
provider: memoryLlmProvider,
|
|
3296
3309
|
model: memoryLlmModel,
|
|
3297
3310
|
baseUrl: memoryLlmBaseUrl,
|
|
3298
|
-
apiKey:
|
|
3299
|
-
process.env.MEMORIX_LLM_API_KEY,
|
|
3300
|
-
process.env.MEMORIX_API_KEY,
|
|
3301
|
-
toml.memory?.llm?.api_key,
|
|
3302
|
-
yaml.llm?.apiKey,
|
|
3303
|
-
legacy.llm?.apiKey,
|
|
3304
|
-
process.env.OPENAI_API_KEY,
|
|
3305
|
-
process.env.ANTHROPIC_API_KEY,
|
|
3306
|
-
openRouterMemoryLlmApiKey
|
|
3307
|
-
)
|
|
3311
|
+
apiKey: memoryLlmApiKey
|
|
3308
3312
|
}
|
|
3309
3313
|
},
|
|
3310
3314
|
embedding: {
|
|
@@ -3314,6 +3318,12 @@ function getResolvedConfig(options = {}) {
|
|
|
3314
3318
|
apiKey: first(process.env.MEMORIX_EMBEDDING_API_KEY, toml.embedding?.api_key, yaml.embedding?.apiKey, legacy.embeddingApi?.apiKey, openRouterEmbeddingApiKey),
|
|
3315
3319
|
dimensions: firstNumber(parseNumber(process.env.MEMORIX_EMBEDDING_DIMENSIONS), toml.embedding?.dimensions, yaml.embedding?.dimensions, legacy.embeddingApi?.dimensions)
|
|
3316
3320
|
},
|
|
3321
|
+
rerank: resolveRerankLane({
|
|
3322
|
+
toml,
|
|
3323
|
+
yaml,
|
|
3324
|
+
memoryLlmApiKey,
|
|
3325
|
+
memoryLlmBaseUrl
|
|
3326
|
+
}),
|
|
3317
3327
|
git: {
|
|
3318
3328
|
autoHook: firstBool(toml.git?.auto_hook, yaml.git?.autoHook),
|
|
3319
3329
|
ingestOnCommit: firstBool(toml.git?.ingest_on_commit, yaml.git?.ingestOnCommit),
|
|
@@ -3383,6 +3393,9 @@ function getResolvedMemoryLane(options = {}) {
|
|
|
3383
3393
|
function getResolvedEmbeddingLane(options = {}) {
|
|
3384
3394
|
return getResolvedConfig(options).embedding;
|
|
3385
3395
|
}
|
|
3396
|
+
function getResolvedRerankLane(options = {}) {
|
|
3397
|
+
return getResolvedConfig(options).rerank;
|
|
3398
|
+
}
|
|
3386
3399
|
function resetResolvedConfigCache() {
|
|
3387
3400
|
}
|
|
3388
3401
|
function first(...values) {
|
|
@@ -3425,6 +3438,10 @@ function getEnvSourceNames() {
|
|
|
3425
3438
|
"MEMORIX_EMBEDDING_BASE_URL",
|
|
3426
3439
|
"MEMORIX_EMBEDDING_MODEL",
|
|
3427
3440
|
"MEMORIX_EMBEDDING_DIMENSIONS",
|
|
3441
|
+
"MEMORIX_RERANK_PROVIDER",
|
|
3442
|
+
"MEMORIX_RERANK_MODEL",
|
|
3443
|
+
"MEMORIX_RERANK_BASE_URL",
|
|
3444
|
+
"MEMORIX_RERANK_API_KEY",
|
|
3428
3445
|
"MEMORIX_CODEGRAPH_EXTERNAL_CONTEXT",
|
|
3429
3446
|
"MEMORIX_CODEGRAPH_EXTERNAL_COMMAND",
|
|
3430
3447
|
"MEMORIX_CODEGRAPH_EXTERNAL_TIMEOUT_MS",
|
|
@@ -3448,6 +3465,63 @@ function normalizeExternalContext(value) {
|
|
|
3448
3465
|
if (normalized === "auto" || normalized === "off") return normalized;
|
|
3449
3466
|
return void 0;
|
|
3450
3467
|
}
|
|
3468
|
+
function normalizeRerankProvider(value) {
|
|
3469
|
+
const normalized = value?.trim().toLowerCase();
|
|
3470
|
+
if (normalized === "http") return "http";
|
|
3471
|
+
return "off";
|
|
3472
|
+
}
|
|
3473
|
+
function resolveRerankLane(args) {
|
|
3474
|
+
const provider2 = normalizeRerankProvider(first(
|
|
3475
|
+
process.env.MEMORIX_RERANK_PROVIDER,
|
|
3476
|
+
args.toml.rerank?.provider,
|
|
3477
|
+
args.yaml.rerank?.provider,
|
|
3478
|
+
"off"
|
|
3479
|
+
));
|
|
3480
|
+
const model = first(
|
|
3481
|
+
process.env.MEMORIX_RERANK_MODEL,
|
|
3482
|
+
args.toml.rerank?.model,
|
|
3483
|
+
args.yaml.rerank?.model
|
|
3484
|
+
);
|
|
3485
|
+
const explicitBaseUrl = first(
|
|
3486
|
+
process.env.MEMORIX_RERANK_BASE_URL,
|
|
3487
|
+
args.toml.rerank?.base_url,
|
|
3488
|
+
args.yaml.rerank?.baseUrl
|
|
3489
|
+
);
|
|
3490
|
+
const baseUrl = provider2 === "http" ? first(explicitBaseUrl, args.memoryLlmBaseUrl) : explicitBaseUrl;
|
|
3491
|
+
const dedicatedApiKey = first(
|
|
3492
|
+
process.env.MEMORIX_RERANK_API_KEY,
|
|
3493
|
+
args.toml.rerank?.api_key,
|
|
3494
|
+
args.yaml.rerank?.apiKey
|
|
3495
|
+
);
|
|
3496
|
+
const sameEndpoint = provider2 === "http" && sameTrustedEndpoint(baseUrl, args.memoryLlmBaseUrl);
|
|
3497
|
+
const inheritedApiKey = sameEndpoint ? args.memoryLlmApiKey : void 0;
|
|
3498
|
+
const apiKey = first(dedicatedApiKey, inheritedApiKey);
|
|
3499
|
+
const canSendRequest = provider2 === "http" && Boolean(baseUrl) && (Boolean(apiKey) || !args.memoryLlmApiKey || sameEndpoint);
|
|
3500
|
+
return { provider: provider2, model, baseUrl, apiKey, canSendRequest };
|
|
3501
|
+
}
|
|
3502
|
+
function sameTrustedEndpoint(left, right) {
|
|
3503
|
+
const a = normalizeTrustedEndpoint(left);
|
|
3504
|
+
const b = normalizeTrustedEndpoint(right);
|
|
3505
|
+
return Boolean(a && b && a === b);
|
|
3506
|
+
}
|
|
3507
|
+
function normalizeTrustedEndpoint(value) {
|
|
3508
|
+
const trimmed = value?.trim();
|
|
3509
|
+
if (!trimmed) return void 0;
|
|
3510
|
+
try {
|
|
3511
|
+
const url = new URL(trimmed);
|
|
3512
|
+
const protocol = url.protocol.toLowerCase();
|
|
3513
|
+
const hostname = url.hostname.toLowerCase();
|
|
3514
|
+
const port = url.port;
|
|
3515
|
+
let pathname = url.pathname.replace(/\/+$/, "");
|
|
3516
|
+
if (pathname.toLowerCase().endsWith("/rerank")) {
|
|
3517
|
+
pathname = pathname.slice(0, -"/rerank".length).replace(/\/+$/, "");
|
|
3518
|
+
}
|
|
3519
|
+
return `${protocol}//${hostname}${port ? `:${port}` : ""}${pathname}`;
|
|
3520
|
+
} catch {
|
|
3521
|
+
const fallback = trimmed.replace(/\/+$/, "").replace(/\/rerank$/i, "").replace(/\/+$/, "");
|
|
3522
|
+
return fallback.toLowerCase() || void 0;
|
|
3523
|
+
}
|
|
3524
|
+
}
|
|
3451
3525
|
var init_resolved_config = __esm({
|
|
3452
3526
|
"src/config/resolved-config.ts"() {
|
|
3453
3527
|
"use strict";
|
|
@@ -5683,9 +5757,27 @@ __export(provider_exports2, {
|
|
|
5683
5757
|
getLLMConfig: () => getLLMConfig,
|
|
5684
5758
|
initLLM: () => initLLM,
|
|
5685
5759
|
isLLMEnabled: () => isLLMEnabled,
|
|
5760
|
+
normalizeOpenAICompatibleBaseUrl: () => normalizeOpenAICompatibleBaseUrl,
|
|
5686
5761
|
parseLLMTimeoutMs: () => parseLLMTimeoutMs,
|
|
5687
5762
|
setLLMConfig: () => setLLMConfig
|
|
5688
5763
|
});
|
|
5764
|
+
function normalizeOpenAICompatibleBaseUrl(baseUrl) {
|
|
5765
|
+
const base = baseUrl.trim().replace(/\/+$/, "");
|
|
5766
|
+
if (!base) return base;
|
|
5767
|
+
let pathname = "";
|
|
5768
|
+
try {
|
|
5769
|
+
pathname = new URL(base).pathname.replace(/\/+$/, "");
|
|
5770
|
+
} catch {
|
|
5771
|
+
pathname = base.split(/[?#]/, 1)[0].replace(/\/+$/, "");
|
|
5772
|
+
}
|
|
5773
|
+
const lastSegment = pathname.split("/").filter(Boolean).at(-1);
|
|
5774
|
+
if (lastSegment && OPENAI_API_VERSION_SEGMENT.test(lastSegment)) {
|
|
5775
|
+
return base;
|
|
5776
|
+
}
|
|
5777
|
+
const queryOrHash = base.match(/[?#].*$/)?.[0] ?? "";
|
|
5778
|
+
const pathWithoutQuery = base.slice(0, base.length - queryOrHash.length).replace(/\/+$/, "");
|
|
5779
|
+
return `${pathWithoutQuery}/v1${queryOrHash}`;
|
|
5780
|
+
}
|
|
5689
5781
|
function parseLLMTimeoutMs(raw) {
|
|
5690
5782
|
if (raw === void 0 || raw.trim() === "") return LLM_TIMEOUT_DEFAULT_MS;
|
|
5691
5783
|
const parsed = Number(raw);
|
|
@@ -5789,8 +5881,7 @@ async function callLLM(systemPrompt, userMessage, signal) {
|
|
|
5789
5881
|
}
|
|
5790
5882
|
async function callOpenAICompatible(systemPrompt, userMessage, signal) {
|
|
5791
5883
|
const config = currentConfig;
|
|
5792
|
-
|
|
5793
|
-
if (!base.endsWith("/v1")) base += "/v1";
|
|
5884
|
+
const base = normalizeOpenAICompatibleBaseUrl(config.baseUrl);
|
|
5794
5885
|
const url = `${base}/chat/completions`;
|
|
5795
5886
|
const fetchSignal = signal ? AbortSignal.any([signal, AbortSignal.timeout(LLM_CALL_TIMEOUT_MS)]) : AbortSignal.timeout(LLM_CALL_TIMEOUT_MS);
|
|
5796
5887
|
const response = await fetch(url, {
|
|
@@ -5869,8 +5960,7 @@ async function callLLMWithTools(messages, tools, signal) {
|
|
|
5869
5960
|
}
|
|
5870
5961
|
async function callOpenAIWithTools(messages, tools, signal) {
|
|
5871
5962
|
const config = currentConfig;
|
|
5872
|
-
|
|
5873
|
-
if (!base.endsWith("/v1")) base += "/v1";
|
|
5963
|
+
const base = normalizeOpenAICompatibleBaseUrl(config.baseUrl);
|
|
5874
5964
|
const url = `${base}/chat/completions`;
|
|
5875
5965
|
const openaiMessages = messages.map((msg) => {
|
|
5876
5966
|
if (msg.role === "tool") {
|
|
@@ -6020,8 +6110,7 @@ async function callAnthropicWithTools(messages, tools, signal) {
|
|
|
6020
6110
|
}
|
|
6021
6111
|
async function* callOpenAIWithToolsStream(messages, tools) {
|
|
6022
6112
|
const config = currentConfig;
|
|
6023
|
-
|
|
6024
|
-
if (!base.endsWith("/v1")) base += "/v1";
|
|
6113
|
+
const base = normalizeOpenAICompatibleBaseUrl(config.baseUrl);
|
|
6025
6114
|
const url = `${base}/chat/completions`;
|
|
6026
6115
|
const openaiMessages = messages.map((msg) => {
|
|
6027
6116
|
if (msg.role === "tool") {
|
|
@@ -6269,7 +6358,7 @@ async function* callAnthropicWithToolsStream(messages, tools) {
|
|
|
6269
6358
|
}
|
|
6270
6359
|
};
|
|
6271
6360
|
}
|
|
6272
|
-
var LLM_TIMEOUT_DEFAULT_MS, LLM_TIMEOUT_MIN_MS, LLM_TIMEOUT_MAX_MS, MAX_LLM_RESPONSE_BYTES, LLM_CALL_TIMEOUT_MS, PROVIDER_DEFAULTS, currentConfig;
|
|
6361
|
+
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;
|
|
6273
6362
|
var init_provider2 = __esm({
|
|
6274
6363
|
"src/llm/provider.ts"() {
|
|
6275
6364
|
"use strict";
|
|
@@ -6279,6 +6368,7 @@ var init_provider2 = __esm({
|
|
|
6279
6368
|
LLM_TIMEOUT_MIN_MS = 1e3;
|
|
6280
6369
|
LLM_TIMEOUT_MAX_MS = 3e5;
|
|
6281
6370
|
MAX_LLM_RESPONSE_BYTES = 2 * 1024 * 1024;
|
|
6371
|
+
OPENAI_API_VERSION_SEGMENT = /^v\d+(?:[a-z][a-z0-9-]*)?$/i;
|
|
6282
6372
|
LLM_CALL_TIMEOUT_MS = parseLLMTimeoutMs(process.env.MEMORIX_LLM_TIMEOUT_MS);
|
|
6283
6373
|
PROVIDER_DEFAULTS = {
|
|
6284
6374
|
openai: { baseUrl: "https://api.openai.com/v1", model: "gpt-4.1-nano" },
|
|
@@ -6338,6 +6428,48 @@ Rules:
|
|
|
6338
6428
|
}
|
|
6339
6429
|
});
|
|
6340
6430
|
|
|
6431
|
+
// src/rerank/policy.ts
|
|
6432
|
+
function parseRerankTimeoutMs(raw) {
|
|
6433
|
+
const value = raw?.trim();
|
|
6434
|
+
if (!value) return DEFAULT_RERANK_TIMEOUT_MS;
|
|
6435
|
+
const parsed = Number(value);
|
|
6436
|
+
if (!Number.isInteger(parsed) || Number.isNaN(parsed)) {
|
|
6437
|
+
console.warn(
|
|
6438
|
+
`[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.`
|
|
6439
|
+
);
|
|
6440
|
+
return DEFAULT_RERANK_TIMEOUT_MS;
|
|
6441
|
+
}
|
|
6442
|
+
if (parsed < RERANK_TIMEOUT_MIN_MS) return RERANK_TIMEOUT_MIN_MS;
|
|
6443
|
+
if (parsed > RERANK_TIMEOUT_MAX_MS) return RERANK_TIMEOUT_MAX_MS;
|
|
6444
|
+
return parsed;
|
|
6445
|
+
}
|
|
6446
|
+
function resolveRerankTimeoutMs(explicitTimeoutMs) {
|
|
6447
|
+
if (typeof explicitTimeoutMs === "number" && Number.isFinite(explicitTimeoutMs) && explicitTimeoutMs > 0) {
|
|
6448
|
+
return explicitTimeoutMs;
|
|
6449
|
+
}
|
|
6450
|
+
return parseRerankTimeoutMs(process.env.MEMORIX_RERANK_TIMEOUT_MS);
|
|
6451
|
+
}
|
|
6452
|
+
function shouldAttemptHttpRerank(input) {
|
|
6453
|
+
return input.quality === "thorough" && input.hasQuery && input.candidateCount >= MIN_RERANK_CANDIDATES;
|
|
6454
|
+
}
|
|
6455
|
+
function shouldAttemptLlmRerank(input) {
|
|
6456
|
+
if (input.httpRerankConfigured) return false;
|
|
6457
|
+
if (input.quality !== "thorough" || input.tier !== "heavy" || !input.hasQuery) return false;
|
|
6458
|
+
if (input.candidateCount < MIN_RERANK_CANDIDATES) return false;
|
|
6459
|
+
return input.topScore > 0 && input.secondScore / input.topScore > 0.7;
|
|
6460
|
+
}
|
|
6461
|
+
var DEFAULT_RERANK_TIMEOUT_MS, RERANK_TIMEOUT_MIN_MS, RERANK_TIMEOUT_MAX_MS, MIN_RERANK_CANDIDATES;
|
|
6462
|
+
var init_policy = __esm({
|
|
6463
|
+
"src/rerank/policy.ts"() {
|
|
6464
|
+
"use strict";
|
|
6465
|
+
init_esm_shims();
|
|
6466
|
+
DEFAULT_RERANK_TIMEOUT_MS = 3e4;
|
|
6467
|
+
RERANK_TIMEOUT_MIN_MS = 1e3;
|
|
6468
|
+
RERANK_TIMEOUT_MAX_MS = 3e5;
|
|
6469
|
+
MIN_RERANK_CANDIDATES = 3;
|
|
6470
|
+
}
|
|
6471
|
+
});
|
|
6472
|
+
|
|
6341
6473
|
// src/timeout.ts
|
|
6342
6474
|
function withTimeout(promise, ms, label) {
|
|
6343
6475
|
return new Promise((resolve2, reject) => {
|
|
@@ -6623,6 +6755,148 @@ var init_aliases = __esm({
|
|
|
6623
6755
|
}
|
|
6624
6756
|
});
|
|
6625
6757
|
|
|
6758
|
+
// src/rerank/http-provider.ts
|
|
6759
|
+
function buildRerankUrl(baseUrl) {
|
|
6760
|
+
const trimmed = baseUrl.trim().replace(/\/+$/, "");
|
|
6761
|
+
if (trimmed.endsWith("/rerank")) return trimmed;
|
|
6762
|
+
return `${trimmed}/rerank`;
|
|
6763
|
+
}
|
|
6764
|
+
function parseRerankResponse(payload) {
|
|
6765
|
+
const rows = extractRows(payload);
|
|
6766
|
+
if (rows.length === 0) {
|
|
6767
|
+
throw new Error("Neural rerank response contained no ranked results");
|
|
6768
|
+
}
|
|
6769
|
+
return rows.sort((a, b) => b.score - a.score);
|
|
6770
|
+
}
|
|
6771
|
+
function extractRows(payload) {
|
|
6772
|
+
if (Array.isArray(payload)) {
|
|
6773
|
+
return payload.map(rowFromUnknown).filter((row) => row !== null);
|
|
6774
|
+
}
|
|
6775
|
+
if (payload && typeof payload === "object" && "results" in payload) {
|
|
6776
|
+
const results = payload.results;
|
|
6777
|
+
if (Array.isArray(results)) {
|
|
6778
|
+
return results.map(rowFromUnknown).filter((row) => row !== null);
|
|
6779
|
+
}
|
|
6780
|
+
}
|
|
6781
|
+
throw new Error("Neural rerank response was not Cohere or TEI shaped");
|
|
6782
|
+
}
|
|
6783
|
+
function rowFromUnknown(value) {
|
|
6784
|
+
if (!value || typeof value !== "object") return null;
|
|
6785
|
+
const row = value;
|
|
6786
|
+
if (typeof row.index !== "number" || !Number.isFinite(row.index)) return null;
|
|
6787
|
+
const score = typeof row.relevance_score === "number" ? row.relevance_score : typeof row.score === "number" ? row.score : null;
|
|
6788
|
+
if (score === null || !Number.isFinite(score)) return null;
|
|
6789
|
+
return { index: row.index, score };
|
|
6790
|
+
}
|
|
6791
|
+
function buildRerankBody(query, documents, model) {
|
|
6792
|
+
const body = {
|
|
6793
|
+
query,
|
|
6794
|
+
documents,
|
|
6795
|
+
top_n: documents.length,
|
|
6796
|
+
return_documents: false
|
|
6797
|
+
};
|
|
6798
|
+
const trimmed = model?.trim();
|
|
6799
|
+
if (trimmed) body.model = trimmed;
|
|
6800
|
+
return body;
|
|
6801
|
+
}
|
|
6802
|
+
async function rerankViaHttp(query, documents, config) {
|
|
6803
|
+
if (documents.length === 0) return [];
|
|
6804
|
+
const url = buildRerankUrl(config.baseUrl);
|
|
6805
|
+
const headers = {
|
|
6806
|
+
"content-type": "application/json"
|
|
6807
|
+
};
|
|
6808
|
+
if (config.apiKey) {
|
|
6809
|
+
headers.authorization = `Bearer ${config.apiKey}`;
|
|
6810
|
+
}
|
|
6811
|
+
const controller = new AbortController();
|
|
6812
|
+
const timeoutMs = resolveRerankTimeoutMs(config.timeoutMs);
|
|
6813
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
6814
|
+
try {
|
|
6815
|
+
const fetchImpl = config.fetchImpl ?? fetch;
|
|
6816
|
+
const response = await fetchImpl(url, {
|
|
6817
|
+
method: "POST",
|
|
6818
|
+
headers,
|
|
6819
|
+
signal: controller.signal,
|
|
6820
|
+
body: JSON.stringify(buildRerankBody(query, documents, config.model))
|
|
6821
|
+
});
|
|
6822
|
+
if (!response.ok) {
|
|
6823
|
+
throw new Error(`Neural rerank HTTP ${response.status} from ${url}`);
|
|
6824
|
+
}
|
|
6825
|
+
return parseRerankResponse(await response.json());
|
|
6826
|
+
} finally {
|
|
6827
|
+
clearTimeout(timer);
|
|
6828
|
+
}
|
|
6829
|
+
}
|
|
6830
|
+
var init_http_provider = __esm({
|
|
6831
|
+
"src/rerank/http-provider.ts"() {
|
|
6832
|
+
"use strict";
|
|
6833
|
+
init_esm_shims();
|
|
6834
|
+
init_policy();
|
|
6835
|
+
}
|
|
6836
|
+
});
|
|
6837
|
+
|
|
6838
|
+
// src/rerank/index.ts
|
|
6839
|
+
var rerank_exports = {};
|
|
6840
|
+
__export(rerank_exports, {
|
|
6841
|
+
DEFAULT_RERANK_TIMEOUT_MS: () => DEFAULT_RERANK_TIMEOUT_MS,
|
|
6842
|
+
MIN_RERANK_CANDIDATES: () => MIN_RERANK_CANDIDATES,
|
|
6843
|
+
isNeuralRerankEnabled: () => isNeuralRerankEnabled,
|
|
6844
|
+
neuralRerankCandidates: () => neuralRerankCandidates,
|
|
6845
|
+
parseRerankTimeoutMs: () => parseRerankTimeoutMs,
|
|
6846
|
+
resolveRerankTimeoutMs: () => resolveRerankTimeoutMs,
|
|
6847
|
+
shouldAttemptHttpRerank: () => shouldAttemptHttpRerank,
|
|
6848
|
+
shouldAttemptLlmRerank: () => shouldAttemptLlmRerank
|
|
6849
|
+
});
|
|
6850
|
+
function isNeuralRerankEnabled(options = {}) {
|
|
6851
|
+
const lane = getResolvedRerankLane(options);
|
|
6852
|
+
return lane.provider === "http" && Boolean(lane.baseUrl);
|
|
6853
|
+
}
|
|
6854
|
+
async function neuralRerankCandidates(query, candidates, options = {}) {
|
|
6855
|
+
if (!isNeuralRerankEnabled(options) || candidates.length === 0) return null;
|
|
6856
|
+
const lane = getResolvedRerankLane(options);
|
|
6857
|
+
if (!lane.canSendRequest || !lane.baseUrl) return null;
|
|
6858
|
+
const toRerank = candidates.slice(0, MAX_RERANK);
|
|
6859
|
+
const rest = candidates.slice(MAX_RERANK);
|
|
6860
|
+
const documents = toRerank.map((candidate) => candidateText(candidate));
|
|
6861
|
+
const ranked = await rerankViaHttp(query, documents, {
|
|
6862
|
+
provider: "http",
|
|
6863
|
+
model: lane.model,
|
|
6864
|
+
baseUrl: lane.baseUrl,
|
|
6865
|
+
apiKey: lane.apiKey,
|
|
6866
|
+
timeoutMs: resolveRerankTimeoutMs()
|
|
6867
|
+
});
|
|
6868
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6869
|
+
const reranked = [];
|
|
6870
|
+
for (const row of ranked) {
|
|
6871
|
+
const candidate = toRerank[row.index];
|
|
6872
|
+
if (!candidate || seen.has(row.index)) continue;
|
|
6873
|
+
reranked.push(candidate);
|
|
6874
|
+
seen.add(row.index);
|
|
6875
|
+
}
|
|
6876
|
+
for (const [index, candidate] of toRerank.entries()) {
|
|
6877
|
+
if (!seen.has(index)) reranked.push(candidate);
|
|
6878
|
+
}
|
|
6879
|
+
reranked.push(...rest);
|
|
6880
|
+
return reranked;
|
|
6881
|
+
}
|
|
6882
|
+
function candidateText(candidate) {
|
|
6883
|
+
const narrative = candidate.narrative?.trim();
|
|
6884
|
+
if (narrative) return `${candidate.title} \u2014 ${narrative.slice(0, 200)}`;
|
|
6885
|
+
return candidate.title;
|
|
6886
|
+
}
|
|
6887
|
+
var MAX_RERANK;
|
|
6888
|
+
var init_rerank = __esm({
|
|
6889
|
+
"src/rerank/index.ts"() {
|
|
6890
|
+
"use strict";
|
|
6891
|
+
init_esm_shims();
|
|
6892
|
+
init_resolved_config();
|
|
6893
|
+
init_http_provider();
|
|
6894
|
+
init_policy();
|
|
6895
|
+
init_policy();
|
|
6896
|
+
MAX_RERANK = 10;
|
|
6897
|
+
}
|
|
6898
|
+
});
|
|
6899
|
+
|
|
6626
6900
|
// src/llm/quality.ts
|
|
6627
6901
|
var quality_exports = {};
|
|
6628
6902
|
__export(quality_exports, {
|
|
@@ -6662,9 +6936,9 @@ async function rerankResults(query, candidates) {
|
|
|
6662
6936
|
if (!isLLMEnabled() || candidates.length <= 2) {
|
|
6663
6937
|
return { reranked: candidates, usedLLM: false };
|
|
6664
6938
|
}
|
|
6665
|
-
const
|
|
6666
|
-
const toRerank = candidates.slice(0,
|
|
6667
|
-
const rest = candidates.slice(
|
|
6939
|
+
const MAX_RERANK2 = 10;
|
|
6940
|
+
const toRerank = candidates.slice(0, MAX_RERANK2);
|
|
6941
|
+
const rest = candidates.slice(MAX_RERANK2);
|
|
6668
6942
|
try {
|
|
6669
6943
|
const candidateList = toRerank.map(
|
|
6670
6944
|
(c) => `[ID: ${c.id}] (${c.type}) ${c.title}${c.narrative ? ` \u2014 ${c.narrative.substring(0, 100)}` : ""}`
|
|
@@ -7557,46 +7831,78 @@ async function searchObservations(options) {
|
|
|
7557
7831
|
}
|
|
7558
7832
|
}
|
|
7559
7833
|
}
|
|
7560
|
-
const
|
|
7561
|
-
|
|
7562
|
-
|
|
7563
|
-
|
|
7564
|
-
})
|
|
7565
|
-
|
|
7566
|
-
|
|
7567
|
-
|
|
7568
|
-
|
|
7569
|
-
|
|
7570
|
-
|
|
7571
|
-
|
|
7572
|
-
|
|
7573
|
-
|
|
7574
|
-
|
|
7575
|
-
|
|
7576
|
-
|
|
7577
|
-
|
|
7578
|
-
|
|
7579
|
-
|
|
7580
|
-
|
|
7581
|
-
|
|
7582
|
-
|
|
7583
|
-
|
|
7584
|
-
|
|
7585
|
-
|
|
7586
|
-
|
|
7587
|
-
|
|
7588
|
-
|
|
7589
|
-
|
|
7590
|
-
|
|
7591
|
-
|
|
7592
|
-
|
|
7593
|
-
|
|
7594
|
-
|
|
7595
|
-
|
|
7834
|
+
const httpGate = shouldAttemptHttpRerank({
|
|
7835
|
+
quality,
|
|
7836
|
+
hasQuery: Boolean(hasQuery),
|
|
7837
|
+
candidateCount: intermediate.length
|
|
7838
|
+
});
|
|
7839
|
+
const llmGateWithoutHttp = shouldAttemptLlmRerank({
|
|
7840
|
+
quality,
|
|
7841
|
+
tier,
|
|
7842
|
+
hasQuery: Boolean(hasQuery),
|
|
7843
|
+
candidateCount: intermediate.length,
|
|
7844
|
+
topScore: intermediate[0]?.score ?? 0,
|
|
7845
|
+
secondScore: intermediate[1]?.score ?? 0,
|
|
7846
|
+
httpRerankConfigured: false
|
|
7847
|
+
});
|
|
7848
|
+
if (httpGate || llmGateWithoutHttp) {
|
|
7849
|
+
const narrativeMap = /* @__PURE__ */ new Map();
|
|
7850
|
+
for (const hit of results.hits) {
|
|
7851
|
+
const doc = hit.document;
|
|
7852
|
+
narrativeMap.set(makeEntryKey(doc.projectId, doc.observationId), doc.narrative);
|
|
7853
|
+
}
|
|
7854
|
+
const RERANK_TOP_K = 5;
|
|
7855
|
+
const toRerank = intermediate.slice(0, RERANK_TOP_K);
|
|
7856
|
+
const candidates = toRerank.map((e2, index) => ({
|
|
7857
|
+
id: `r${index + 1}`,
|
|
7858
|
+
title: e2.title,
|
|
7859
|
+
type: e2.type,
|
|
7860
|
+
score: e2.score,
|
|
7861
|
+
narrative: narrativeMap.get(makeEntryKey(e2.projectId, e2.id))
|
|
7862
|
+
}));
|
|
7863
|
+
const RERANK_TIMEOUT_MS = parseRerankTimeoutMs(process.env.MEMORIX_RERANK_TIMEOUT_MS);
|
|
7864
|
+
const candidateMap = new Map(candidates.map((candidate, index) => [candidate.id, toRerank[index]]));
|
|
7865
|
+
const { isNeuralRerankEnabled: isNeuralRerankEnabled2, neuralRerankCandidates: neuralRerankCandidates2 } = await Promise.resolve().then(() => (init_rerank(), rerank_exports));
|
|
7866
|
+
const httpConfigured = isNeuralRerankEnabled2();
|
|
7867
|
+
if (httpConfigured && httpGate) {
|
|
7868
|
+
try {
|
|
7869
|
+
const neural = await withTimeout(
|
|
7870
|
+
neuralRerankCandidates2(originalQuery, candidates),
|
|
7871
|
+
RERANK_TIMEOUT_MS,
|
|
7872
|
+
"neural rerank"
|
|
7873
|
+
);
|
|
7874
|
+
if (neural && neural.length > 0) {
|
|
7875
|
+
const rerankedTop = neural.map((row) => candidateMap.get(row.id)).filter((entry) => entry != null);
|
|
7876
|
+
if (rerankedTop.length > 0) {
|
|
7877
|
+
intermediate = [...rerankedTop, ...intermediate.slice(RERANK_TOP_K)];
|
|
7878
|
+
lastSearchModeByProject.set(modeKey, (lastSearchModeByProject.get(modeKey) ?? "fulltext") + " + neural rerank");
|
|
7879
|
+
mark("rerank(usedNeural=true)");
|
|
7880
|
+
}
|
|
7596
7881
|
}
|
|
7882
|
+
} catch {
|
|
7883
|
+
console.error("[memorix] neural rerank failed or timed out; keeping original order");
|
|
7597
7884
|
}
|
|
7598
|
-
}
|
|
7599
|
-
|
|
7885
|
+
} else if (!httpConfigured && llmGateWithoutHttp) {
|
|
7886
|
+
try {
|
|
7887
|
+
const { rerankResults: rerankResults2 } = await Promise.resolve().then(() => (init_quality(), quality_exports));
|
|
7888
|
+
const { reranked, usedLLM } = await withTimeout(
|
|
7889
|
+
rerankResults2(originalQuery, candidates),
|
|
7890
|
+
RERANK_TIMEOUT_MS,
|
|
7891
|
+
"LLM rerank"
|
|
7892
|
+
);
|
|
7893
|
+
mark(`rerank(usedLLM=${usedLLM})`);
|
|
7894
|
+
if (usedLLM) {
|
|
7895
|
+
lastSearchModeByProject.set(modeKey, (lastSearchModeByProject.get(modeKey) ?? "fulltext") + " + LLM rerank");
|
|
7896
|
+
const rerankedTop = reranked.map((row) => candidateMap.get(row.id)).filter((entry) => entry != null);
|
|
7897
|
+
if (rerankedTop.length > 0) {
|
|
7898
|
+
intermediate = [...rerankedTop, ...intermediate.slice(RERANK_TOP_K)];
|
|
7899
|
+
}
|
|
7900
|
+
}
|
|
7901
|
+
} catch {
|
|
7902
|
+
console.error("[memorix] LLM rerank failed or timed out, using original order");
|
|
7903
|
+
}
|
|
7904
|
+
} else {
|
|
7905
|
+
mark(`rerank(skipped,tier=${tier})`);
|
|
7600
7906
|
}
|
|
7601
7907
|
} else {
|
|
7602
7908
|
mark(`rerank(skipped,tier=${tier})`);
|
|
@@ -7777,6 +8083,7 @@ var init_orama_store = __esm({
|
|
|
7777
8083
|
init_project_affinity();
|
|
7778
8084
|
init_intent_detector();
|
|
7779
8085
|
init_query_expansion();
|
|
8086
|
+
init_policy();
|
|
7780
8087
|
init_timeout();
|
|
7781
8088
|
db = null;
|
|
7782
8089
|
dbInitPromise = null;
|
|
@@ -165217,8 +165524,7 @@ async function callVisionLLM(systemPrompt, imageBase64, mimeType) {
|
|
|
165217
165524
|
if (!apiKey) {
|
|
165218
165525
|
throw new Error("No LLM API key configured for image analysis.");
|
|
165219
165526
|
}
|
|
165220
|
-
|
|
165221
|
-
if (!baseUrl.endsWith("/v1")) baseUrl += "/v1";
|
|
165527
|
+
const baseUrl = normalizeOpenAICompatibleBaseUrl(getLLMBaseUrl("https://api.openai.com/v1"));
|
|
165222
165528
|
const model = getLLMModel("gpt-4o");
|
|
165223
165529
|
const response = await fetch(`${baseUrl}/chat/completions`, {
|
|
165224
165530
|
method: "POST",
|
|
@@ -167302,7 +167608,7 @@ The path should point to a directory containing a .git folder.`
|
|
|
167302
167608
|
};
|
|
167303
167609
|
const server = existingServer ?? new McpServer({
|
|
167304
167610
|
name: "memorix",
|
|
167305
|
-
version: true ? "1.7.
|
|
167611
|
+
version: true ? "1.7.3" : "1.0.1"
|
|
167306
167612
|
});
|
|
167307
167613
|
const originalRegisterTool = server.registerTool.bind(server);
|
|
167308
167614
|
server.registerTool = ((name, ...args) => {
|
|
@@ -171742,7 +172048,7 @@ var init_serve_shared = __esm({
|
|
|
171742
172048
|
|
|
171743
172049
|
// src/cli/version.ts
|
|
171744
172050
|
function getCliVersion() {
|
|
171745
|
-
return true ? "1.7.
|
|
172051
|
+
return true ? "1.7.3" : pkg.version;
|
|
171746
172052
|
}
|
|
171747
172053
|
var init_version3 = __esm({
|
|
171748
172054
|
"src/cli/version.ts"() {
|