@exulu/backend 3.3.0 → 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
package/dist/index.js
CHANGED
|
@@ -14,7 +14,6 @@ import {
|
|
|
14
14
|
ExuluTool,
|
|
15
15
|
KB_EDITOR_TOOL_ID,
|
|
16
16
|
LITELLM_UI_PATH,
|
|
17
|
-
LiteLLMAdminError,
|
|
18
17
|
OAUTH_CALLBACK_PATH,
|
|
19
18
|
PreviewRenderError,
|
|
20
19
|
ResolveModelError,
|
|
@@ -88,10 +87,12 @@ import {
|
|
|
88
87
|
verifyCredentialNonce,
|
|
89
88
|
waitForLiteLLMReady,
|
|
90
89
|
withRetry
|
|
91
|
-
} from "./chunk-
|
|
90
|
+
} from "./chunk-UIIUBZCO.js";
|
|
92
91
|
import {
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
LiteLLMAdminError,
|
|
93
|
+
findLiteLLMModel,
|
|
94
|
+
resolveLiteLLMTarget
|
|
95
|
+
} from "./chunk-NUVMV5FC.js";
|
|
95
96
|
|
|
96
97
|
// src/index.ts
|
|
97
98
|
import "dotenv/config";
|
|
@@ -1216,15 +1217,7 @@ async function resolveEmbedder(input) {
|
|
|
1216
1217
|
`LiteLLM is not ready: ${err.message}`
|
|
1217
1218
|
);
|
|
1218
1219
|
}
|
|
1219
|
-
const
|
|
1220
|
-
const port = process.env.LITELLM_PORT ?? "4000";
|
|
1221
|
-
const masterKey = process.env.LITELLM_MASTER_KEY;
|
|
1222
|
-
if (!masterKey) {
|
|
1223
|
-
throw new ResolveEmbedderError(
|
|
1224
|
-
"LITELLM_NOT_CONFIGURED",
|
|
1225
|
-
"LITELLM_MASTER_KEY is required when EXULU_USE_LITELLM=true"
|
|
1226
|
-
);
|
|
1227
|
-
}
|
|
1220
|
+
const { baseUrl, authHeaders } = resolveLiteLLMTarget();
|
|
1228
1221
|
const resolvedUserId = user?.id ?? userId;
|
|
1229
1222
|
if (resolvedUserId) await provisionDefaultUserBudget(resolvedUserId);
|
|
1230
1223
|
const { dimensionality, maxChunkSize, maxBatchSize } = getEmbeddingModelInfo(model);
|
|
@@ -1245,12 +1238,12 @@ async function resolveEmbedder(input) {
|
|
|
1245
1238
|
routine_name: routine?.name,
|
|
1246
1239
|
context_name: contextName
|
|
1247
1240
|
});
|
|
1248
|
-
const endpoint =
|
|
1241
|
+
const endpoint = `${baseUrl}/v1/embeddings`;
|
|
1249
1242
|
const embedBatch = async (batch) => {
|
|
1250
1243
|
const res = await fetch(endpoint, {
|
|
1251
1244
|
method: "POST",
|
|
1252
1245
|
headers: {
|
|
1253
|
-
|
|
1246
|
+
...authHeaders,
|
|
1254
1247
|
"Content-Type": "application/json"
|
|
1255
1248
|
},
|
|
1256
1249
|
body: JSON.stringify({
|
|
@@ -6199,20 +6192,25 @@ async function encryptString(string) {
|
|
|
6199
6192
|
const hash = await bcrypt.hash(string, SALT_ROUNDS);
|
|
6200
6193
|
return hash;
|
|
6201
6194
|
}
|
|
6202
|
-
var generateApiKey = async (name, email) => {
|
|
6195
|
+
var generateApiKey = async (name, email, options) => {
|
|
6203
6196
|
const { db } = await postgresClient();
|
|
6204
6197
|
email = String(email).trim().toLowerCase();
|
|
6198
|
+
const superAdmin = options?.superAdmin ?? true;
|
|
6199
|
+
const roleName = options?.roleName ?? "admin";
|
|
6200
|
+
const rolePermissions = options?.rolePermissions ?? {
|
|
6201
|
+
agents: "write",
|
|
6202
|
+
workflows: "write",
|
|
6203
|
+
variables: "write",
|
|
6204
|
+
users: "write"
|
|
6205
|
+
};
|
|
6205
6206
|
console.log("[EXULU] Inserting default user and admin role.");
|
|
6206
|
-
const existingRole = await db.from("roles").where({ name:
|
|
6207
|
+
const existingRole = await db.from("roles").where({ name: roleName }).first();
|
|
6207
6208
|
let roleId;
|
|
6208
6209
|
if (!existingRole) {
|
|
6209
|
-
console.log(
|
|
6210
|
+
console.log(`[EXULU] Creating default ${roleName} role.`);
|
|
6210
6211
|
const role = await db.from("roles").insert({
|
|
6211
|
-
name:
|
|
6212
|
-
|
|
6213
|
-
workflows: "write",
|
|
6214
|
-
variables: "write",
|
|
6215
|
-
users: "write"
|
|
6212
|
+
name: roleName,
|
|
6213
|
+
...rolePermissions
|
|
6216
6214
|
}).returning("id");
|
|
6217
6215
|
roleId = role[0].id;
|
|
6218
6216
|
} else {
|
|
@@ -6228,7 +6226,7 @@ var generateApiKey = async (name, email) => {
|
|
|
6228
6226
|
await db.from("users").insert({
|
|
6229
6227
|
name,
|
|
6230
6228
|
email,
|
|
6231
|
-
super_admin:
|
|
6229
|
+
super_admin: superAdmin,
|
|
6232
6230
|
createdAt: /* @__PURE__ */ new Date(),
|
|
6233
6231
|
updatedAt: /* @__PURE__ */ new Date(),
|
|
6234
6232
|
type: "api",
|
|
@@ -6800,6 +6798,9 @@ function createMutations(table, contexts, tools, config) {
|
|
|
6800
6798
|
}
|
|
6801
6799
|
throw new Error("Only the creator can edit this private record");
|
|
6802
6800
|
}
|
|
6801
|
+
if (record.created_by != null && String(record.created_by) === String(user.id)) {
|
|
6802
|
+
return true;
|
|
6803
|
+
}
|
|
6803
6804
|
if (record.rights_mode === "users") {
|
|
6804
6805
|
const rbacRecord = await db.from("rbac").where({
|
|
6805
6806
|
entity: table.name.singular,
|
|
@@ -12866,7 +12867,7 @@ type LiteLLMModel {
|
|
|
12866
12867
|
}
|
|
12867
12868
|
`;
|
|
12868
12869
|
resolvers.Query["litellmCatalog"] = async () => {
|
|
12869
|
-
const { fetchLiteLLMCatalog } = await import("./catalog-
|
|
12870
|
+
const { fetchLiteLLMCatalog } = await import("./catalog-PZTI2MJZ.js");
|
|
12870
12871
|
return fetchLiteLLMCatalog();
|
|
12871
12872
|
};
|
|
12872
12873
|
resolvers.Query["workflowSchedule"] = async (_, args, context, info) => {
|
|
@@ -14828,11 +14829,8 @@ var TranscriptionError = class extends Error {
|
|
|
14828
14829
|
}
|
|
14829
14830
|
};
|
|
14830
14831
|
async function transcribeAudio(args) {
|
|
14831
|
-
const
|
|
14832
|
-
const port = process.env.LITELLM_PORT ?? "4000";
|
|
14833
|
-
const masterKey = process.env.LITELLM_MASTER_KEY;
|
|
14832
|
+
const { baseUrl, authHeaders } = resolveLiteLLMTarget();
|
|
14834
14833
|
const model = process.env.TRANSCRIPTION_MODEL;
|
|
14835
|
-
if (!masterKey) throw new Error("LITELLM_MASTER_KEY is not set");
|
|
14836
14834
|
if (!model) throw new Error("TRANSCRIPTION_MODEL is not set");
|
|
14837
14835
|
const form = new FormData();
|
|
14838
14836
|
form.append(
|
|
@@ -14842,9 +14840,9 @@ async function transcribeAudio(args) {
|
|
|
14842
14840
|
);
|
|
14843
14841
|
form.append("model", model);
|
|
14844
14842
|
if (args.language) form.append("language", args.language);
|
|
14845
|
-
const res = await fetch(
|
|
14843
|
+
const res = await fetch(`${baseUrl}/v1/audio/transcriptions`, {
|
|
14846
14844
|
method: "POST",
|
|
14847
|
-
headers: {
|
|
14845
|
+
headers: { ...authHeaders },
|
|
14848
14846
|
body: form
|
|
14849
14847
|
});
|
|
14850
14848
|
if (!res.ok) {
|
|
@@ -14867,12 +14865,9 @@ var SpeechError = class extends Error {
|
|
|
14867
14865
|
}
|
|
14868
14866
|
};
|
|
14869
14867
|
async function synthesizeSpeech(args) {
|
|
14870
|
-
const
|
|
14871
|
-
const port = process.env.LITELLM_PORT ?? "4000";
|
|
14872
|
-
const masterKey = process.env.LITELLM_MASTER_KEY;
|
|
14868
|
+
const { baseUrl, authHeaders } = resolveLiteLLMTarget();
|
|
14873
14869
|
const model = process.env.TTS_MODEL;
|
|
14874
14870
|
const voice = process.env.TTS_VOICE;
|
|
14875
|
-
if (!masterKey) throw new Error("LITELLM_MASTER_KEY is not set");
|
|
14876
14871
|
if (!model) throw new Error("TTS_MODEL is not set");
|
|
14877
14872
|
if (!voice) throw new Error("TTS_VOICE is not set");
|
|
14878
14873
|
const body = {
|
|
@@ -14881,10 +14876,10 @@ async function synthesizeSpeech(args) {
|
|
|
14881
14876
|
voice,
|
|
14882
14877
|
response_format: "mp3"
|
|
14883
14878
|
};
|
|
14884
|
-
const res = await fetch(
|
|
14879
|
+
const res = await fetch(`${baseUrl}/v1/audio/speech`, {
|
|
14885
14880
|
method: "POST",
|
|
14886
14881
|
headers: {
|
|
14887
|
-
|
|
14882
|
+
...authHeaders,
|
|
14888
14883
|
"Content-Type": "application/json"
|
|
14889
14884
|
},
|
|
14890
14885
|
body: JSON.stringify(body)
|
|
@@ -14908,13 +14903,6 @@ var ImageGenerationError = class extends Error {
|
|
|
14908
14903
|
this.name = "ImageGenerationError";
|
|
14909
14904
|
}
|
|
14910
14905
|
};
|
|
14911
|
-
var resolveProxyConfig = () => {
|
|
14912
|
-
const host = process.env.LITELLM_HOST ?? "127.0.0.1";
|
|
14913
|
-
const port = process.env.LITELLM_PORT ?? "4000";
|
|
14914
|
-
const masterKey = process.env.LITELLM_MASTER_KEY;
|
|
14915
|
-
if (!masterKey) throw new Error("LITELLM_MASTER_KEY is not set");
|
|
14916
|
-
return { host, port, masterKey };
|
|
14917
|
-
};
|
|
14918
14906
|
var normalizeDataEntries = async (data) => {
|
|
14919
14907
|
const out = [];
|
|
14920
14908
|
for (const entry of data) {
|
|
@@ -14951,7 +14939,7 @@ var normalizeDataEntries = async (data) => {
|
|
|
14951
14939
|
async function generateImage(args) {
|
|
14952
14940
|
if (!args.model) throw new Error("model is required");
|
|
14953
14941
|
if (!args.prompt) throw new Error("prompt is required");
|
|
14954
|
-
const
|
|
14942
|
+
const { baseUrl, authHeaders } = resolveLiteLLMTarget();
|
|
14955
14943
|
const body = {
|
|
14956
14944
|
model: args.model,
|
|
14957
14945
|
prompt: args.prompt
|
|
@@ -14959,10 +14947,10 @@ async function generateImage(args) {
|
|
|
14959
14947
|
if (args.size) body.size = args.size;
|
|
14960
14948
|
if (args.quality) body.quality = args.quality;
|
|
14961
14949
|
if (args.n) body.n = args.n;
|
|
14962
|
-
const res = await fetch(
|
|
14950
|
+
const res = await fetch(`${baseUrl}/v1/images/generations`, {
|
|
14963
14951
|
method: "POST",
|
|
14964
14952
|
headers: {
|
|
14965
|
-
|
|
14953
|
+
...authHeaders,
|
|
14966
14954
|
"Content-Type": "application/json"
|
|
14967
14955
|
},
|
|
14968
14956
|
body: JSON.stringify(body),
|
|
@@ -14990,7 +14978,7 @@ async function editImage(args) {
|
|
|
14990
14978
|
if (!args.references || args.references.length === 0) {
|
|
14991
14979
|
throw new Error("at least one reference image is required");
|
|
14992
14980
|
}
|
|
14993
|
-
const
|
|
14981
|
+
const { baseUrl, authHeaders } = resolveLiteLLMTarget();
|
|
14994
14982
|
const form = new FormData();
|
|
14995
14983
|
form.append("model", args.model);
|
|
14996
14984
|
form.append("prompt", args.prompt);
|
|
@@ -15012,9 +15000,9 @@ async function editImage(args) {
|
|
|
15012
15000
|
args.mask.filename
|
|
15013
15001
|
);
|
|
15014
15002
|
}
|
|
15015
|
-
const res = await fetch(
|
|
15003
|
+
const res = await fetch(`${baseUrl}/v1/images/edits`, {
|
|
15016
15004
|
method: "POST",
|
|
15017
|
-
headers: {
|
|
15005
|
+
headers: { ...authHeaders },
|
|
15018
15006
|
body: form,
|
|
15019
15007
|
signal: args.signal
|
|
15020
15008
|
});
|
|
@@ -15161,6 +15149,9 @@ See docs/superpowers/specs/2026-05-31-in-chat-image-generation-design.md for the
|
|
|
15161
15149
|
// src/exulu/routes.ts
|
|
15162
15150
|
import { resolve as resolvePath } from "path";
|
|
15163
15151
|
|
|
15152
|
+
// src/exulu/litellm/passthrough-allowlist.ts
|
|
15153
|
+
var isLiteLLMPassthroughPathAllowed = (path2) => path2.startsWith("/v1/") || path2 === "/model/info";
|
|
15154
|
+
|
|
15164
15155
|
// src/exulu/litellm/usage-view.ts
|
|
15165
15156
|
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
15166
15157
|
var DATE_ONLY_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
@@ -17377,7 +17368,7 @@ ${style.markdown}` : params.prompt;
|
|
|
17377
17368
|
return;
|
|
17378
17369
|
}
|
|
17379
17370
|
const user = authenticationResult.user;
|
|
17380
|
-
if (!req.path
|
|
17371
|
+
if (!isLiteLLMPassthroughPathAllowed(req.path)) {
|
|
17381
17372
|
res.status(403).json({
|
|
17382
17373
|
detail: `Path ${req.path} is not exposed through the Exulu LiteLLM proxy.`
|
|
17383
17374
|
});
|
|
@@ -23395,15 +23386,7 @@ async function resolveOcr(input) {
|
|
|
23395
23386
|
`LiteLLM is not ready: ${err.message}`
|
|
23396
23387
|
);
|
|
23397
23388
|
}
|
|
23398
|
-
const
|
|
23399
|
-
const port = process.env.LITELLM_PORT ?? "4000";
|
|
23400
|
-
const masterKey = process.env.LITELLM_MASTER_KEY;
|
|
23401
|
-
if (!masterKey) {
|
|
23402
|
-
throw new ResolveOcrError(
|
|
23403
|
-
"LITELLM_NOT_CONFIGURED",
|
|
23404
|
-
"LITELLM_MASTER_KEY is required when EXULU_USE_LITELLM=true"
|
|
23405
|
-
);
|
|
23406
|
-
}
|
|
23389
|
+
const { baseUrl, authHeaders } = resolveLiteLLMTarget();
|
|
23407
23390
|
const resolvedUserId = user?.id ?? userId;
|
|
23408
23391
|
if (resolvedUserId) await provisionDefaultUserBudget(resolvedUserId);
|
|
23409
23392
|
const role = user?.role;
|
|
@@ -23423,12 +23406,12 @@ async function resolveOcr(input) {
|
|
|
23423
23406
|
routine_name: routine?.name,
|
|
23424
23407
|
context_name: contextName
|
|
23425
23408
|
});
|
|
23426
|
-
const endpoint =
|
|
23409
|
+
const endpoint = `${baseUrl}/v1/ocr`;
|
|
23427
23410
|
const ocr = async (document, opts) => {
|
|
23428
23411
|
const res = await fetch(endpoint, {
|
|
23429
23412
|
method: "POST",
|
|
23430
23413
|
headers: {
|
|
23431
|
-
|
|
23414
|
+
...authHeaders,
|
|
23432
23415
|
"Content-Type": "application/json"
|
|
23433
23416
|
},
|
|
23434
23417
|
body: JSON.stringify({
|