@exulu/backend 3.3.1 → 3.4.0
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/{catalog-UGTDNMDM.js → catalog-PZTI2MJZ.js} +1 -1
- package/dist/{chunk-7CCMW3IW.js → chunk-NUVMV5FC.js} +53 -6
- package/dist/{chunk-5FTX543Z.js → chunk-UIIUBZCO.js} +27 -55
- package/dist/{convert-exulu-tools-to-ai-sdk-tools-WQWYMU7G.js → convert-exulu-tools-to-ai-sdk-tools-AB4F55SD.js} +2 -2
- package/dist/index.cjs +162 -154
- package/dist/index.js +45 -62
- package/package.json +1 -1
|
@@ -1,5 +1,45 @@
|
|
|
1
1
|
import "dotenv/config";
|
|
2
2
|
|
|
3
|
+
// src/exulu/litellm/env.ts
|
|
4
|
+
var LiteLLMAdminError = class extends Error {
|
|
5
|
+
constructor(message, status) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.status = status;
|
|
8
|
+
this.name = "LiteLLMAdminError";
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
function litellmBase() {
|
|
12
|
+
const host = process.env.LITELLM_HOST ?? "127.0.0.1";
|
|
13
|
+
const port = process.env.LITELLM_PORT ?? "4000";
|
|
14
|
+
const masterKey = process.env.LITELLM_MASTER_KEY;
|
|
15
|
+
if (!masterKey) {
|
|
16
|
+
throw new LiteLLMAdminError("LITELLM_MASTER_KEY is not configured.");
|
|
17
|
+
}
|
|
18
|
+
return { url: `http://${host}:${port}`, masterKey };
|
|
19
|
+
}
|
|
20
|
+
function resolveLiteLLMTarget() {
|
|
21
|
+
const rawBase = process.env.LITELLM_BASE_URL;
|
|
22
|
+
if (rawBase && rawBase.trim().length > 0) {
|
|
23
|
+
const apiKey = process.env.EXULU_API_KEY;
|
|
24
|
+
if (!apiKey) {
|
|
25
|
+
throw new Error("EXULU_API_KEY is required when LITELLM_BASE_URL is set (remote LiteLLM client mode).");
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
baseUrl: rawBase.trim().replace(/\/+$/, ""),
|
|
29
|
+
authHeaders: { "exulu-api-key": apiKey },
|
|
30
|
+
remote: true
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const host = process.env.LITELLM_HOST ?? "127.0.0.1";
|
|
34
|
+
const port = process.env.LITELLM_PORT ?? "4000";
|
|
35
|
+
const masterKey = process.env.LITELLM_MASTER_KEY;
|
|
36
|
+
return {
|
|
37
|
+
baseUrl: `http://${host}:${port}`,
|
|
38
|
+
authHeaders: masterKey ? { Authorization: `Bearer ${masterKey}` } : {},
|
|
39
|
+
remote: false
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
3
43
|
// src/exulu/litellm/catalog.ts
|
|
4
44
|
var CACHE_TTL_MS = 3e4;
|
|
5
45
|
var _cache;
|
|
@@ -11,14 +51,18 @@ var fetchLiteLLMCatalog = async () => {
|
|
|
11
51
|
if (_cache && _cache.expiresAt > Date.now()) {
|
|
12
52
|
return _cache.items;
|
|
13
53
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
54
|
+
let baseUrl;
|
|
55
|
+
let authHeaders;
|
|
56
|
+
try {
|
|
57
|
+
({ baseUrl, authHeaders } = resolveLiteLLMTarget());
|
|
58
|
+
} catch {
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
if (Object.keys(authHeaders).length === 0) return [];
|
|
18
62
|
try {
|
|
19
|
-
const res = await fetch(
|
|
63
|
+
const res = await fetch(`${baseUrl}/model/info`, {
|
|
20
64
|
method: "GET",
|
|
21
|
-
headers:
|
|
65
|
+
headers: authHeaders
|
|
22
66
|
});
|
|
23
67
|
if (!res.ok) {
|
|
24
68
|
console.error(
|
|
@@ -74,6 +118,9 @@ var findLiteLLMModel = async (modelName) => {
|
|
|
74
118
|
};
|
|
75
119
|
|
|
76
120
|
export {
|
|
121
|
+
LiteLLMAdminError,
|
|
122
|
+
litellmBase,
|
|
123
|
+
resolveLiteLLMTarget,
|
|
77
124
|
__resetLiteLLMCatalogCacheForTesting,
|
|
78
125
|
fetchLiteLLMCatalog,
|
|
79
126
|
findLiteLLMModel
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import "dotenv/config";
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
LiteLLMAdminError,
|
|
4
|
+
findLiteLLMModel,
|
|
5
|
+
litellmBase,
|
|
6
|
+
resolveLiteLLMTarget
|
|
7
|
+
} from "./chunk-NUVMV5FC.js";
|
|
5
8
|
|
|
6
9
|
// src/templates/tools/convert-exulu-tools-to-ai-sdk-tools.ts
|
|
7
10
|
import { S3Client as S3Client3, PutObjectCommand as PutObjectCommand3, S3ServiceException } from "@aws-sdk/client-s3";
|
|
@@ -224,12 +227,11 @@ var waitForLiteLLMReady = async () => {
|
|
|
224
227
|
if (!isLiteLLMEnabled()) return;
|
|
225
228
|
if (_clientMode) {
|
|
226
229
|
if (internal.state === "ready") return;
|
|
227
|
-
const
|
|
228
|
-
const
|
|
229
|
-
const url = `http://${host}:${port}/health/liveliness`;
|
|
230
|
+
const { baseUrl, authHeaders, remote } = resolveLiteLLMTarget();
|
|
231
|
+
const url = remote ? `${baseUrl}/v1/models` : `${baseUrl}/health/liveliness`;
|
|
230
232
|
let res;
|
|
231
233
|
try {
|
|
232
|
-
res = await fetch(url, { method: "GET" });
|
|
234
|
+
res = await fetch(url, { method: "GET", headers: remote ? authHeaders : {} });
|
|
233
235
|
} catch (err) {
|
|
234
236
|
throw new Error(
|
|
235
237
|
`LiteLLM proxy not reachable at ${url} (is the Exulu server process running?): ${err.message}`
|
|
@@ -531,24 +533,6 @@ async function postgresClient() {
|
|
|
531
533
|
};
|
|
532
534
|
}
|
|
533
535
|
|
|
534
|
-
// src/exulu/litellm/env.ts
|
|
535
|
-
var LiteLLMAdminError = class extends Error {
|
|
536
|
-
constructor(message, status) {
|
|
537
|
-
super(message);
|
|
538
|
-
this.status = status;
|
|
539
|
-
this.name = "LiteLLMAdminError";
|
|
540
|
-
}
|
|
541
|
-
};
|
|
542
|
-
function litellmBase() {
|
|
543
|
-
const host = process.env.LITELLM_HOST ?? "127.0.0.1";
|
|
544
|
-
const port = process.env.LITELLM_PORT ?? "4000";
|
|
545
|
-
const masterKey = process.env.LITELLM_MASTER_KEY;
|
|
546
|
-
if (!masterKey) {
|
|
547
|
-
throw new LiteLLMAdminError("LITELLM_MASTER_KEY is not configured.");
|
|
548
|
-
}
|
|
549
|
-
return { url: `http://${host}:${port}`, masterKey };
|
|
550
|
-
}
|
|
551
|
-
|
|
552
536
|
// src/exulu/litellm/admin-client.ts
|
|
553
537
|
async function call(path3, body) {
|
|
554
538
|
const { url, masterKey } = litellmBase();
|
|
@@ -1042,9 +1026,7 @@ var getLiteLLMProvider = ({
|
|
|
1042
1026
|
team,
|
|
1043
1027
|
routine
|
|
1044
1028
|
}) => {
|
|
1045
|
-
const
|
|
1046
|
-
const port = process.env.LITELLM_PORT ?? "4000";
|
|
1047
|
-
const masterKey = process.env.LITELLM_MASTER_KEY;
|
|
1029
|
+
const { baseUrl, authHeaders } = resolveLiteLLMTarget();
|
|
1048
1030
|
const tags = buildTags({
|
|
1049
1031
|
user_id: user?.id,
|
|
1050
1032
|
role_id: role?.id,
|
|
@@ -1059,16 +1041,13 @@ var getLiteLLMProvider = ({
|
|
|
1059
1041
|
routine_id: routine?.id,
|
|
1060
1042
|
routine_name: routine?.name
|
|
1061
1043
|
});
|
|
1062
|
-
if (!masterKey) {
|
|
1063
|
-
throw new ResolveModelError(
|
|
1064
|
-
"LITELLM_NOT_CONFIGURED",
|
|
1065
|
-
"LITELLM_MASTER_KEY is required when EXULU_USE_LITELLM=true"
|
|
1066
|
-
);
|
|
1067
|
-
}
|
|
1068
1044
|
return createOpenAICompatible({
|
|
1069
1045
|
name: "litellm",
|
|
1070
|
-
baseURL:
|
|
1071
|
-
apiKey
|
|
1046
|
+
baseURL: `${baseUrl}/v1`,
|
|
1047
|
+
// createOpenAICompatible requires a non-empty apiKey; real auth for remote mode is the
|
|
1048
|
+
// exulu-api-key header (added via `headers`). The passthrough strips/ignores Authorization.
|
|
1049
|
+
apiKey: process.env.LITELLM_MASTER_KEY ?? process.env.EXULU_API_KEY ?? "x",
|
|
1050
|
+
headers: authHeaders,
|
|
1072
1051
|
fetch: createTaggedFetch(tags),
|
|
1073
1052
|
// Without this flag the openai-compatible provider strips any
|
|
1074
1053
|
// responseFormat.schema before sending and warns
|
|
@@ -1733,7 +1712,7 @@ var ExuluTool = class _ExuluTool {
|
|
|
1733
1712
|
if (!agent) {
|
|
1734
1713
|
throw new Error("Agent not found.");
|
|
1735
1714
|
}
|
|
1736
|
-
const { convertExuluToolsToAiSdkTools: convertExuluToolsToAiSdkTools2 } = await import("./convert-exulu-tools-to-ai-sdk-tools-
|
|
1715
|
+
const { convertExuluToolsToAiSdkTools: convertExuluToolsToAiSdkTools2 } = await import("./convert-exulu-tools-to-ai-sdk-tools-AB4F55SD.js");
|
|
1737
1716
|
const tools = await convertExuluToolsToAiSdkTools2(
|
|
1738
1717
|
[this],
|
|
1739
1718
|
[],
|
|
@@ -1939,7 +1918,6 @@ var checkLicense = () => {
|
|
|
1939
1918
|
};
|
|
1940
1919
|
|
|
1941
1920
|
// src/exulu/resolve-reranker.ts
|
|
1942
|
-
import "fs";
|
|
1943
1921
|
var ResolveRerankerError = class extends Error {
|
|
1944
1922
|
constructor(code, message) {
|
|
1945
1923
|
super(message);
|
|
@@ -1963,15 +1941,7 @@ async function resolveReranker(input) {
|
|
|
1963
1941
|
`LiteLLM is not ready: ${err.message}`
|
|
1964
1942
|
);
|
|
1965
1943
|
}
|
|
1966
|
-
const
|
|
1967
|
-
const port = process.env.LITELLM_PORT ?? "4000";
|
|
1968
|
-
const masterKey = process.env.LITELLM_MASTER_KEY;
|
|
1969
|
-
if (!masterKey) {
|
|
1970
|
-
throw new ResolveRerankerError(
|
|
1971
|
-
"LITELLM_NOT_CONFIGURED",
|
|
1972
|
-
"LITELLM_MASTER_KEY is required when EXULU_USE_LITELLM=true"
|
|
1973
|
-
);
|
|
1974
|
-
}
|
|
1944
|
+
const { baseUrl, authHeaders } = resolveLiteLLMTarget();
|
|
1975
1945
|
const resolvedUserId = user?.id ?? userId;
|
|
1976
1946
|
if (resolvedUserId) await provisionDefaultUserBudget(resolvedUserId);
|
|
1977
1947
|
const role = user?.role;
|
|
@@ -1991,7 +1961,7 @@ async function resolveReranker(input) {
|
|
|
1991
1961
|
routine_name: routine?.name,
|
|
1992
1962
|
context_name: contextName
|
|
1993
1963
|
});
|
|
1994
|
-
const endpoint =
|
|
1964
|
+
const endpoint = `${baseUrl}/v1/rerank`;
|
|
1995
1965
|
const rerank = async (query, chunks, opts) => {
|
|
1996
1966
|
try {
|
|
1997
1967
|
if (chunks.length === 0) return [];
|
|
@@ -2001,7 +1971,7 @@ async function resolveReranker(input) {
|
|
|
2001
1971
|
const res = await fetch(endpoint, {
|
|
2002
1972
|
method: "POST",
|
|
2003
1973
|
headers: {
|
|
2004
|
-
|
|
1974
|
+
...authHeaders,
|
|
2005
1975
|
"Content-Type": "application/json"
|
|
2006
1976
|
},
|
|
2007
1977
|
body: JSON.stringify({
|
|
@@ -4371,6 +4341,9 @@ var checkItemWriteAccess = async (context, record, user) => {
|
|
|
4371
4341
|
if (record.rights_mode === "private") {
|
|
4372
4342
|
return record.created_by != null && String(record.created_by) === String(user.id);
|
|
4373
4343
|
}
|
|
4344
|
+
if (record.created_by != null && String(record.created_by) === String(user.id)) {
|
|
4345
|
+
return true;
|
|
4346
|
+
}
|
|
4374
4347
|
const validRightsModes = ["users", "roles", "teams"];
|
|
4375
4348
|
if (!validRightsModes.includes(record.rights_mode)) {
|
|
4376
4349
|
return false;
|
|
@@ -7258,23 +7231,23 @@ ${JSON.stringify(config, null, 2)}`
|
|
|
7258
7231
|
|
|
7259
7232
|
// src/exulu/audit/sink.ts
|
|
7260
7233
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
7261
|
-
import { promises as
|
|
7234
|
+
import { promises as fs } from "fs";
|
|
7262
7235
|
import path2 from "path";
|
|
7263
7236
|
var createFsSpoolStore = (dir) => ({
|
|
7264
7237
|
write: async (name, body) => {
|
|
7265
|
-
await
|
|
7266
|
-
await
|
|
7238
|
+
await fs.mkdir(dir, { recursive: true });
|
|
7239
|
+
await fs.writeFile(path2.join(dir, name), body, "utf8");
|
|
7267
7240
|
},
|
|
7268
7241
|
list: async () => {
|
|
7269
7242
|
try {
|
|
7270
|
-
return (await
|
|
7243
|
+
return (await fs.readdir(dir)).filter((f) => f.endsWith(".ndjson"));
|
|
7271
7244
|
} catch {
|
|
7272
7245
|
return [];
|
|
7273
7246
|
}
|
|
7274
7247
|
},
|
|
7275
|
-
read: async (name) =>
|
|
7248
|
+
read: async (name) => fs.readFile(path2.join(dir, name), "utf8"),
|
|
7276
7249
|
remove: async (name) => {
|
|
7277
|
-
await
|
|
7250
|
+
await fs.rm(path2.join(dir, name), { force: true });
|
|
7278
7251
|
}
|
|
7279
7252
|
});
|
|
7280
7253
|
var pad = (n) => String(n).padStart(2, "0");
|
|
@@ -8155,7 +8128,6 @@ export {
|
|
|
8155
8128
|
buildTags,
|
|
8156
8129
|
budgetTagFor,
|
|
8157
8130
|
createTaggedFetch,
|
|
8158
|
-
LiteLLMAdminError,
|
|
8159
8131
|
tagDelete,
|
|
8160
8132
|
tagInfo,
|
|
8161
8133
|
getTagDailyActivity,
|
|
@@ -2,8 +2,8 @@ import "dotenv/config";
|
|
|
2
2
|
import {
|
|
3
3
|
convertExuluToolsToAiSdkTools,
|
|
4
4
|
hydrateVariables
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-UIIUBZCO.js";
|
|
6
|
+
import "./chunk-NUVMV5FC.js";
|
|
7
7
|
export {
|
|
8
8
|
convertExuluToolsToAiSdkTools,
|
|
9
9
|
hydrateVariables
|