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