@prom.codes/memory-mcp 0.9.1 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +216 -19
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -871,11 +871,192 @@ var VoyageRerankProvider = class {
|
|
|
871
871
|
}
|
|
872
872
|
};
|
|
873
873
|
|
|
874
|
-
// ../rerank-
|
|
875
|
-
var
|
|
874
|
+
// ../rerank-prometheus/dist/index.js
|
|
875
|
+
var DEFAULT_BASE2 = "https://api.prom.codes";
|
|
876
|
+
var DEFAULT_NAME = "prometheus";
|
|
877
|
+
var DEFAULT_MODEL2 = "prometheus";
|
|
876
878
|
var DEFAULT_BATCH4 = 100;
|
|
877
|
-
var
|
|
878
|
-
var
|
|
879
|
+
var DEFAULT_BATCH_CHARS2 = 4e5;
|
|
880
|
+
var DEFAULT_RETRIES4 = 4;
|
|
881
|
+
var DEFAULT_BACKOFF4 = 250;
|
|
882
|
+
function sleep4(ms, signal) {
|
|
883
|
+
return new Promise((resolve3, reject) => {
|
|
884
|
+
if (signal?.aborted === true) {
|
|
885
|
+
reject(new Error("aborted"));
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
const timer = setTimeout(() => {
|
|
889
|
+
signal?.removeEventListener("abort", onAbort);
|
|
890
|
+
resolve3();
|
|
891
|
+
}, ms);
|
|
892
|
+
const onAbort = () => {
|
|
893
|
+
clearTimeout(timer);
|
|
894
|
+
reject(new Error("aborted"));
|
|
895
|
+
};
|
|
896
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
897
|
+
});
|
|
898
|
+
}
|
|
899
|
+
function nonRetryable4(message) {
|
|
900
|
+
const err = new Error(message);
|
|
901
|
+
err.nonRetryable = true;
|
|
902
|
+
return err;
|
|
903
|
+
}
|
|
904
|
+
var PrometheusRerankProvider = class {
|
|
905
|
+
name;
|
|
906
|
+
model;
|
|
907
|
+
region;
|
|
908
|
+
#apiKey;
|
|
909
|
+
#url;
|
|
910
|
+
#batchSize;
|
|
911
|
+
#maxBatchChars;
|
|
912
|
+
#maxRetries;
|
|
913
|
+
#retryBaseMs;
|
|
914
|
+
#fetch;
|
|
915
|
+
#creditsUsed = 0;
|
|
916
|
+
constructor(opts) {
|
|
917
|
+
if (typeof opts.apiKey !== "string" || opts.apiKey === "") {
|
|
918
|
+
throw new Error("PrometheusRerankProvider: apiKey is required");
|
|
919
|
+
}
|
|
920
|
+
if (opts.batchSize !== void 0 && (!Number.isInteger(opts.batchSize) || opts.batchSize <= 0 || opts.batchSize > 1e3)) {
|
|
921
|
+
throw new Error(`PrometheusRerankProvider: batchSize must be an integer in 1..1000, got ${opts.batchSize}`);
|
|
922
|
+
}
|
|
923
|
+
this.name = opts.name ?? DEFAULT_NAME;
|
|
924
|
+
this.model = opts.model ?? DEFAULT_MODEL2;
|
|
925
|
+
this.region = opts.region ?? "eu";
|
|
926
|
+
this.#apiKey = opts.apiKey;
|
|
927
|
+
this.#url = `${(opts.baseUrl ?? DEFAULT_BASE2).replace(/\/+$/, "")}/rerank`;
|
|
928
|
+
this.#batchSize = opts.batchSize ?? DEFAULT_BATCH4;
|
|
929
|
+
this.#maxBatchChars = opts.maxBatchChars ?? DEFAULT_BATCH_CHARS2;
|
|
930
|
+
this.#maxRetries = opts.maxRetries ?? DEFAULT_RETRIES4;
|
|
931
|
+
this.#retryBaseMs = opts.retryBaseMs ?? DEFAULT_BACKOFF4;
|
|
932
|
+
this.#fetch = opts.fetch ?? fetch;
|
|
933
|
+
}
|
|
934
|
+
/** Cumulative credits charged across all rerank calls of this instance. */
|
|
935
|
+
get creditsUsed() {
|
|
936
|
+
return this.#creditsUsed;
|
|
937
|
+
}
|
|
938
|
+
async rerank(query, candidates, opts) {
|
|
939
|
+
if (candidates.length === 0)
|
|
940
|
+
return [];
|
|
941
|
+
const all = new Array(candidates.length);
|
|
942
|
+
let cursor = 0;
|
|
943
|
+
let start = 0;
|
|
944
|
+
while (start < candidates.length) {
|
|
945
|
+
const end = this.#batchEnd(query, candidates, start);
|
|
946
|
+
const slice = candidates.slice(start, end);
|
|
947
|
+
const scored = await this.#rerankBatch(query, slice, opts?.signal);
|
|
948
|
+
for (const hit of scored) {
|
|
949
|
+
const globalIndex = start + hit.localIndex;
|
|
950
|
+
const cand = candidates[globalIndex];
|
|
951
|
+
all[cursor++] = { id: cand.id, index: globalIndex, score: hit.score };
|
|
952
|
+
}
|
|
953
|
+
start = end;
|
|
954
|
+
}
|
|
955
|
+
all.length = cursor;
|
|
956
|
+
all.sort((a, b) => b.score - a.score);
|
|
957
|
+
if (opts?.topK !== void 0 && opts.topK >= 0 && opts.topK < all.length) {
|
|
958
|
+
return all.slice(0, opts.topK);
|
|
959
|
+
}
|
|
960
|
+
return all;
|
|
961
|
+
}
|
|
962
|
+
/**
|
|
963
|
+
* Greedy batch cut honouring both the document-count cap and the
|
|
964
|
+
* char budget (query + documents must stay under the proxy limit).
|
|
965
|
+
* Always advances by at least one candidate, even if a single
|
|
966
|
+
* document alone exceeds the budget (the proxy applies upstream
|
|
967
|
+
* truncation; refusing to send it would strand the rest).
|
|
968
|
+
*/
|
|
969
|
+
#batchEnd(query, candidates, start) {
|
|
970
|
+
let chars = query.length;
|
|
971
|
+
let end = start;
|
|
972
|
+
while (end < candidates.length && end - start < this.#batchSize) {
|
|
973
|
+
const len = candidates[end].text.length;
|
|
974
|
+
if (end > start && chars + len > this.#maxBatchChars)
|
|
975
|
+
break;
|
|
976
|
+
chars += len;
|
|
977
|
+
end += 1;
|
|
978
|
+
}
|
|
979
|
+
return end;
|
|
980
|
+
}
|
|
981
|
+
async #rerankBatch(query, batch, signal) {
|
|
982
|
+
const init = {
|
|
983
|
+
method: "POST",
|
|
984
|
+
headers: {
|
|
985
|
+
"content-type": "application/json",
|
|
986
|
+
authorization: `Bearer ${this.#apiKey}`
|
|
987
|
+
},
|
|
988
|
+
body: JSON.stringify({ query, documents: batch.map((c) => c.text) })
|
|
989
|
+
};
|
|
990
|
+
if (signal !== void 0)
|
|
991
|
+
init.signal = signal;
|
|
992
|
+
const payload = await this.#requestJson(init, signal);
|
|
993
|
+
if (payload?.ok !== true || !Array.isArray(payload.results)) {
|
|
994
|
+
throw nonRetryable4("prometheus-rerank: malformed rerank response");
|
|
995
|
+
}
|
|
996
|
+
if (payload.results.length !== batch.length) {
|
|
997
|
+
throw nonRetryable4(`prometheus-rerank: expected ${batch.length} rerank rows, got ${payload.results.length}`);
|
|
998
|
+
}
|
|
999
|
+
const credits = payload.usage?.credits;
|
|
1000
|
+
if (typeof credits === "number" && Number.isFinite(credits)) {
|
|
1001
|
+
this.#creditsUsed += credits;
|
|
1002
|
+
}
|
|
1003
|
+
return payload.results.map((row) => {
|
|
1004
|
+
if (!Number.isInteger(row.index) || row.index < 0 || row.index >= batch.length) {
|
|
1005
|
+
throw nonRetryable4(`prometheus-rerank: invalid index ${row.index} in rerank response`);
|
|
1006
|
+
}
|
|
1007
|
+
if (typeof row.relevanceScore !== "number" || !Number.isFinite(row.relevanceScore)) {
|
|
1008
|
+
throw nonRetryable4(`prometheus-rerank: invalid relevanceScore ${row.relevanceScore} at index ${row.index}`);
|
|
1009
|
+
}
|
|
1010
|
+
return { localIndex: row.index, score: row.relevanceScore };
|
|
1011
|
+
});
|
|
1012
|
+
}
|
|
1013
|
+
/**
|
|
1014
|
+
* Fetch with retry. 5xx and network errors back off exponentially;
|
|
1015
|
+
* everything else (401 invalid key, 413 oversized input, 429 monthly
|
|
1016
|
+
* quota exhausted) fails fast with the proxy's error code in the
|
|
1017
|
+
* message.
|
|
1018
|
+
*/
|
|
1019
|
+
async #requestJson(init, signal) {
|
|
1020
|
+
let attempt = 0;
|
|
1021
|
+
let lastError = null;
|
|
1022
|
+
while (attempt <= this.#maxRetries) {
|
|
1023
|
+
try {
|
|
1024
|
+
const res = await this.#fetch(this.#url, init);
|
|
1025
|
+
if (res.status >= 500 && res.status < 600) {
|
|
1026
|
+
lastError = new Error(`prometheus-rerank: HTTP ${res.status}`);
|
|
1027
|
+
attempt += 1;
|
|
1028
|
+
if (attempt > this.#maxRetries)
|
|
1029
|
+
break;
|
|
1030
|
+
await sleep4(this.#retryBaseMs * 2 ** (attempt - 1), signal);
|
|
1031
|
+
continue;
|
|
1032
|
+
}
|
|
1033
|
+
if (!res.ok) {
|
|
1034
|
+
const body = await res.json().catch(() => null);
|
|
1035
|
+
const detail = typeof body?.code === "string" ? `${body.code}${typeof body.error === "string" ? ` \u2014 ${body.error}` : ""}` : res.statusText;
|
|
1036
|
+
throw nonRetryable4(`prometheus-rerank: HTTP ${res.status} ${detail}`);
|
|
1037
|
+
}
|
|
1038
|
+
return await res.json();
|
|
1039
|
+
} catch (err) {
|
|
1040
|
+
if (err?.name === "AbortError")
|
|
1041
|
+
throw err;
|
|
1042
|
+
if (err?.nonRetryable === true)
|
|
1043
|
+
throw err;
|
|
1044
|
+
if (attempt >= this.#maxRetries)
|
|
1045
|
+
throw err;
|
|
1046
|
+
lastError = err;
|
|
1047
|
+
attempt += 1;
|
|
1048
|
+
await sleep4(this.#retryBaseMs * 2 ** (attempt - 1), signal);
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
throw lastError instanceof Error ? lastError : new Error(`prometheus-rerank: exhausted ${this.#maxRetries} retries`);
|
|
1052
|
+
}
|
|
1053
|
+
};
|
|
1054
|
+
|
|
1055
|
+
// ../rerank-openai-compat/dist/index.js
|
|
1056
|
+
var DEFAULT_MODEL3 = "bge-reranker-base";
|
|
1057
|
+
var DEFAULT_BATCH5 = 100;
|
|
1058
|
+
var DEFAULT_RETRIES5 = 6;
|
|
1059
|
+
var DEFAULT_BACKOFF5 = 2e3;
|
|
879
1060
|
var DEFAULT_RETRY_MAX3 = 6e4;
|
|
880
1061
|
var DEFAULT_TIMEOUT = 18e4;
|
|
881
1062
|
function parseRetryAfterMs3(value, now = Date.now()) {
|
|
@@ -898,7 +1079,7 @@ function parseRetryAfterMs3(value, now = Date.now()) {
|
|
|
898
1079
|
const delta = ts - now;
|
|
899
1080
|
return delta > 0 ? delta : 0;
|
|
900
1081
|
}
|
|
901
|
-
function
|
|
1082
|
+
function sleep5(ms, signal) {
|
|
902
1083
|
return new Promise((resolve3, reject) => {
|
|
903
1084
|
if (signal?.aborted === true) {
|
|
904
1085
|
reject(new Error("aborted"));
|
|
@@ -915,7 +1096,7 @@ function sleep4(ms, signal) {
|
|
|
915
1096
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
916
1097
|
});
|
|
917
1098
|
}
|
|
918
|
-
function
|
|
1099
|
+
function nonRetryable5(message) {
|
|
919
1100
|
const err = new Error(message);
|
|
920
1101
|
err.nonRetryable = true;
|
|
921
1102
|
return err;
|
|
@@ -942,14 +1123,14 @@ var OpenAICompatRerankProvider = class {
|
|
|
942
1123
|
if (opts.timeoutMs !== void 0 && (!Number.isInteger(opts.timeoutMs) || opts.timeoutMs < 0)) {
|
|
943
1124
|
throw new Error(`OpenAICompatRerankProvider: timeoutMs must be a non-negative integer (0 disables), got ${opts.timeoutMs}`);
|
|
944
1125
|
}
|
|
945
|
-
this.model = opts.model ??
|
|
1126
|
+
this.model = opts.model ?? DEFAULT_MODEL3;
|
|
946
1127
|
this.name = opts.name ?? `openai-compat:${this.model}`;
|
|
947
1128
|
this.region = opts.region ?? "self-hosted";
|
|
948
1129
|
this.#baseUrl = opts.baseUrl.replace(/\/+$/, "");
|
|
949
1130
|
this.#apiKey = opts.apiKey === void 0 || opts.apiKey === "" ? void 0 : opts.apiKey;
|
|
950
|
-
this.#batchSize = opts.batchSize ??
|
|
951
|
-
this.#maxRetries = opts.maxRetries ??
|
|
952
|
-
this.#retryBaseMs = opts.retryBaseMs ??
|
|
1131
|
+
this.#batchSize = opts.batchSize ?? DEFAULT_BATCH5;
|
|
1132
|
+
this.#maxRetries = opts.maxRetries ?? DEFAULT_RETRIES5;
|
|
1133
|
+
this.#retryBaseMs = opts.retryBaseMs ?? DEFAULT_BACKOFF5;
|
|
953
1134
|
this.#retryMaxMs = opts.retryMaxMs ?? DEFAULT_RETRY_MAX3;
|
|
954
1135
|
this.#timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT;
|
|
955
1136
|
this.#fetch = opts.fetch ?? fetch;
|
|
@@ -1018,12 +1199,12 @@ var OpenAICompatRerankProvider = class {
|
|
|
1018
1199
|
if (attempt > this.#maxRetries)
|
|
1019
1200
|
break;
|
|
1020
1201
|
const backoff = this.#computeBackoff(attempt, res.headers.get("retry-after"));
|
|
1021
|
-
await
|
|
1202
|
+
await sleep5(backoff, signal);
|
|
1022
1203
|
continue;
|
|
1023
1204
|
}
|
|
1024
1205
|
if (!res.ok) {
|
|
1025
1206
|
const text = await res.text().catch(() => "");
|
|
1026
|
-
throw
|
|
1207
|
+
throw nonRetryable5(`${this.name}: HTTP ${res.status} ${res.statusText}${text === "" ? "" : ` \u2014 ${text}`}`);
|
|
1027
1208
|
}
|
|
1028
1209
|
const payload = await res.json();
|
|
1029
1210
|
return this.#decode(payload, batch.length);
|
|
@@ -1038,7 +1219,7 @@ var OpenAICompatRerankProvider = class {
|
|
|
1038
1219
|
throw normalized;
|
|
1039
1220
|
lastError = normalized;
|
|
1040
1221
|
attempt += 1;
|
|
1041
|
-
await
|
|
1222
|
+
await sleep5(this.#computeBackoff(attempt, null), signal);
|
|
1042
1223
|
} finally {
|
|
1043
1224
|
if (timer !== void 0)
|
|
1044
1225
|
clearTimeout(timer);
|
|
@@ -1056,14 +1237,14 @@ var OpenAICompatRerankProvider = class {
|
|
|
1056
1237
|
}
|
|
1057
1238
|
#decode(payload, expected) {
|
|
1058
1239
|
if (!Array.isArray(payload.results) || payload.results.length !== expected) {
|
|
1059
|
-
throw
|
|
1240
|
+
throw nonRetryable5(`${this.name}: expected ${expected} rerank rows, got ${payload.results?.length ?? 0}`);
|
|
1060
1241
|
}
|
|
1061
1242
|
return payload.results.map((row) => {
|
|
1062
1243
|
if (!Number.isInteger(row.index) || row.index < 0 || row.index >= expected) {
|
|
1063
|
-
throw
|
|
1244
|
+
throw nonRetryable5(`${this.name}: invalid index ${row.index} in rerank response`);
|
|
1064
1245
|
}
|
|
1065
1246
|
if (typeof row.relevance_score !== "number" || !Number.isFinite(row.relevance_score)) {
|
|
1066
|
-
throw
|
|
1247
|
+
throw nonRetryable5(`${this.name}: invalid relevance_score ${row.relevance_score} at index ${row.index}`);
|
|
1067
1248
|
}
|
|
1068
1249
|
return { localIndex: row.index, score: row.relevance_score };
|
|
1069
1250
|
});
|
|
@@ -2107,9 +2288,25 @@ function discoverMemoryEmbedder(env) {
|
|
|
2107
2288
|
return { id: "none", embedder: void 0 };
|
|
2108
2289
|
}
|
|
2109
2290
|
function discoverMemoryReranker(env) {
|
|
2110
|
-
const
|
|
2111
|
-
|
|
2291
|
+
const explicit = env.PROMETHEUS_MEMORY_RERANK_PROVIDER?.toLowerCase();
|
|
2292
|
+
const hasKey = (env[API_KEY_ENV] ?? "").trim() !== "";
|
|
2293
|
+
const forced = explicit === void 0 || explicit === "" ? hasKey ? "prometheus" : "none" : explicit;
|
|
2294
|
+
if (forced === "none")
|
|
2112
2295
|
return { id: "none", provider: null };
|
|
2296
|
+
if (forced === "prometheus") {
|
|
2297
|
+
const apiKey = requireApiKey(env);
|
|
2298
|
+
const baseUrl = env.PROMETHEUS_API_URL;
|
|
2299
|
+
const provider = new PrometheusRerankProvider({
|
|
2300
|
+
name: "prometheus-rerank",
|
|
2301
|
+
apiKey,
|
|
2302
|
+
region: "eu",
|
|
2303
|
+
...baseUrl !== void 0 && baseUrl !== "" ? { baseUrl } : {},
|
|
2304
|
+
maxRetries: intEnv(env, "PROMETHEUS_MEMORY_RERANK_MAX_RETRIES", 4),
|
|
2305
|
+
retryBaseMs: intEnv(env, "PROMETHEUS_MEMORY_RERANK_RETRY_BASE_MS", 250),
|
|
2306
|
+
batchSize: intEnv(env, "PROMETHEUS_MEMORY_RERANK_BATCH", 100)
|
|
2307
|
+
});
|
|
2308
|
+
return { id: "prometheus", provider };
|
|
2309
|
+
}
|
|
2113
2310
|
if (forced === "voyage") {
|
|
2114
2311
|
const apiKey = env.VOYAGE_API_KEY;
|
|
2115
2312
|
if (apiKey === void 0 || apiKey === "") {
|
|
@@ -2146,7 +2343,7 @@ function discoverMemoryReranker(env) {
|
|
|
2146
2343
|
});
|
|
2147
2344
|
return { id: "bge", provider };
|
|
2148
2345
|
}
|
|
2149
|
-
throw new Error(`unknown PROMETHEUS_MEMORY_RERANK_PROVIDER="${forced}" (expected "none", "voyage", or "bge")`);
|
|
2346
|
+
throw new Error(`unknown PROMETHEUS_MEMORY_RERANK_PROVIDER="${forced}" (expected "none", "prometheus", "voyage", or "bge")`);
|
|
2150
2347
|
}
|
|
2151
2348
|
function discoverMemoryExtractor(env) {
|
|
2152
2349
|
const forced = (env.PROMETHEUS_MEMORY_EXTRACT_PROVIDER ?? "none").toLowerCase();
|