@exulu/backend 1.58.0 → 1.59.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.
@@ -0,0 +1,64 @@
1
+ # LiteLLM Proxy configuration for Exulu.
2
+ #
3
+ # This file is read by the LiteLLM process Exulu spawns when EXULU_USE_LITELLM=true.
4
+ # Documentation: https://docs.litellm.ai/docs/proxy/configs
5
+ #
6
+ # To use:
7
+ # 1. Copy this file to config.yaml in the same directory.
8
+ # 2. Edit model_list to add the providers you want to expose.
9
+ # 3. Set the referenced env vars (LITELLM_MASTER_KEY, GOOGLE_VERTEX_PROJECT, etc.).
10
+ # 4. Set EXULU_USE_LITELLM=true and (re)start Exulu.
11
+
12
+ general_settings:
13
+ master_key: os.environ/LITELLM_MASTER_KEY
14
+
15
+ # Database mode (optional). Set `database_url` only if you want LiteLLM's
16
+ # user/key/budget/spend tracking tables. When set, LiteLLM creates ~60
17
+ # tables in the target database (all prefixed `LiteLLM_`).
18
+ #
19
+ # ⚠ ALWAYS use a DEDICATED Postgres database for LiteLLM — never share it
20
+ # with Exulu's main database. LiteLLM's schema sync drops any tables in
21
+ # the public schema that aren't part of its schema; sharing a database
22
+ # with Exulu has destroyed application data in the past.
23
+ #
24
+ # Exulu's ExuluDatabase.init runs `prisma db push` against this URL
25
+ # automatically at boot, but only after verifying:
26
+ # 1. database_url is NOT the same as Exulu's own Postgres (warns + skips)
27
+ # 2. the target database contains no non-LiteLLM_* tables (warns + skips)
28
+ # 3. the push itself runs without --accept-data-loss (Prisma refuses any
29
+ # schema change that would drop data)
30
+ # Re-running on an already-set-up database is a no-op.
31
+ #
32
+ # database_url: "postgresql://user:pass@host:5432/litellm_dedicated"
33
+
34
+ model_list:
35
+ # Each entry's `model_name` is what Exulu agents will reference in their
36
+ # `model` field after toggling LiteLLM on. Pick stable, human-friendly names.
37
+
38
+ - model_name: vertex-flash
39
+ litellm_params:
40
+ model: vertex_ai/gemini-2.5-flash
41
+ vertex_project: os.environ/GOOGLE_VERTEX_PROJECT
42
+ vertex_location: europe-west1
43
+ vertex_credentials: os.environ/GOOGLE_VERTEX_CREDENTIALS_JSON
44
+
45
+ - model_name: vertex-pro
46
+ litellm_params:
47
+ model: vertex_ai/gemini-2.5-pro
48
+ vertex_project: os.environ/GOOGLE_VERTEX_PROJECT
49
+ vertex_location: europe-west1
50
+ vertex_credentials: os.environ/GOOGLE_VERTEX_CREDENTIALS_JSON
51
+
52
+ - model_name: claude-haiku
53
+ litellm_params:
54
+ model: anthropic/claude-haiku-4-5
55
+ api_key: os.environ/ANTHROPIC_API_KEY
56
+
57
+ - model_name: gpt-5
58
+ litellm_params:
59
+ model: openai/gpt-5
60
+ api_key: os.environ/OPENAI_API_KEY
61
+
62
+ # Per-model rate limits and budgets are configured here in LiteLLM-native form.
63
+ # See https://docs.litellm.ai/docs/proxy/users for budget configuration and
64
+ # https://docs.litellm.ai/docs/proxy/rate_limit_tiers for rate limiting.
@@ -3,3 +3,18 @@ transformers
3
3
  pyinstaller
4
4
  docling-hierarchical-pdf
5
5
  defusedxml
6
+ # LiteLLM proxy — only used when EXULU_USE_LITELLM=true. Always installed so
7
+ # the dep is ready when the env var is flipped. Pinned to a tested version;
8
+ # upgrade deliberately.
9
+ litellm[proxy]==1.85.1
10
+ # Prisma Python client — required by LiteLLM proxy whenever a `database_url`
11
+ # is set in config.litellm.yaml (used for user/budget tracking, key
12
+ # management, etc.). NOT included in litellm[proxy] in 1.85.1, hence the
13
+ # separate pin. setup.sh runs `prisma generate` against LiteLLM's bundled
14
+ # schema after pip install so the Python client module is materialized.
15
+ prisma==0.15.0
16
+ # Vertex AI SDK — required by LiteLLM when routing to `vertex_ai/*` models
17
+ # (the `vertexai` Python module lives inside google-cloud-aiplatform).
18
+ # NOT included in litellm[proxy]; without it, requests to Vertex models
19
+ # fail with "No module named 'vertexai'".
20
+ google-cloud-aiplatform>=1.38
@@ -203,6 +203,19 @@ pip install -r "$REQUIREMENTS_FILE"
203
203
 
204
204
  print_success "All dependencies installed successfully"
205
205
 
206
+ # Step 6.5: Generate Prisma client for LiteLLM database mode.
207
+ # LiteLLM's PrismaClient does `from prisma import Prisma`, which only works
208
+ # after `prisma generate` has materialized the Python client against
209
+ # LiteLLM's bundled schema. Skip silently if LiteLLM isn't installed or its
210
+ # schema isn't where we expect — database mode is opt-in via config.litellm.yaml.
211
+ LITELLM_PROXY_DIR=$(find "$VENV_DIR/lib" -path "*/litellm/proxy" -type d 2>/dev/null | head -1)
212
+ if [ -n "$LITELLM_PROXY_DIR" ] && [ -f "$LITELLM_PROXY_DIR/schema.prisma" ]; then
213
+ print_info "Generating Prisma client for LiteLLM..."
214
+ (cd "$LITELLM_PROXY_DIR" && PATH="$VENV_DIR/bin:$PATH" "$VENV_DIR/bin/prisma" generate > /dev/null 2>&1) \
215
+ && print_success "Prisma client generated for LiteLLM" \
216
+ || print_warning "Prisma generate failed; LiteLLM database mode (database_url in config.litellm.yaml) may not work until you run 'cd $LITELLM_PROXY_DIR && PATH=$VENV_DIR/bin:\$PATH $VENV_DIR/bin/prisma generate'"
217
+ fi
218
+
206
219
  # Step 7: Validate installation
207
220
  echo ""
208
221
  echo "Step 7: Validating installation..."
package/ee/workers.ts CHANGED
@@ -10,6 +10,7 @@ import { getTableName, type ExuluContext } from "@SRC/exulu/context.ts";
10
10
  import type { ExuluReranker } from "@SRC/exulu/reranker.ts";
11
11
  import type { ExuluEval } from "@SRC/exulu/evals.ts";
12
12
  import type { ExuluTool } from "@SRC/exulu/tool.ts";
13
+ import { resolveModel } from "@SRC/exulu/resolve-model.ts";
13
14
  import { postgresClient } from "@SRC/postgres/client";
14
15
  import type { BullMqJobData } from "@EE/queues/decorator.ts";
15
16
  import { type Tracer } from "@opentelemetry/api";
@@ -1379,37 +1380,21 @@ export const processUiMessagesFlow = async ({
1379
1380
  enabledTools?.map((x) => x.name + " (" + x.id + ")"),
1380
1381
  );
1381
1382
 
1382
- // Get the variable name from user's anthropic_token field
1383
- const variableName = agent.providerapikey;
1384
-
1385
- // Look up the variable from the variables table
1386
- const { db } = await postgresClient();
1387
-
1388
- let providerapikey: string | undefined;
1389
-
1390
- if (variableName) {
1391
- const variable = await db.from("variables").where({ name: variableName }).first();
1392
- if (!variable) {
1393
- throw new Error(
1394
- `Provider API key variable not found for agent ${agent.name} (${agent.id}).`,
1395
- );
1396
- }
1397
-
1398
- // Get the API key from the variable (decrypt if encrypted)
1399
- providerapikey = variable.value;
1400
-
1401
- if (!variable.encrypted) {
1402
- throw new Error(
1403
- `Provider API key variable not encrypted for agent ${agent.name} (${agent.id}), for security reasons you are only allowed to use encrypted variables for provider API keys.`,
1404
- );
1405
- }
1406
-
1407
- if (variable.encrypted) {
1408
- const bytes = CryptoJS.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
1409
- providerapikey = bytes.toString(CryptoJS.enc.Utf8);
1410
- }
1383
+ if (!agent.model) {
1384
+ throw new Error(
1385
+ `Agent ${agent.name} (${agent.id}) has no model configured.`,
1386
+ );
1411
1387
  }
1412
1388
 
1389
+ const resolved = await resolveModel({
1390
+ modelId: agent.model,
1391
+ user,
1392
+ providers,
1393
+ agent: { id: agent.id },
1394
+ });
1395
+ const providerapikey = resolved.apiKey;
1396
+ const resolvedLanguageModel = resolved.languageModel;
1397
+
1413
1398
  // Remove placeholder agent response before sending
1414
1399
  const messagesWithoutPlaceholder = inputMessages.filter(
1415
1400
  (message) => (message.metadata as any)?.type !== "placeholder",
@@ -1512,6 +1497,7 @@ export const processUiMessagesFlow = async ({
1512
1497
  message: currentMessage,
1513
1498
  currentTools: enabledTools,
1514
1499
  allExuluTools: tools,
1500
+ languageModel: resolvedLanguageModel,
1515
1501
  providerapikey,
1516
1502
  toolConfigs: agent.tools,
1517
1503
  exuluConfig: config,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@exulu/backend",
3
3
  "author": "Qventu Bv.",
4
- "version": "1.58.0",
4
+ "version": "1.59.0",
5
5
  "main": "./dist/index.js",
6
6
  "private": false,
7
7
  "publishConfig": {
@@ -63,6 +63,7 @@
63
63
  "@types/express": "^5.0.1",
64
64
  "@types/graphql-type-json": "^0.3.5",
65
65
  "@types/jest": "^29.5.14",
66
+ "@types/multer": "^2.1.0",
66
67
  "@types/node": "^22.14.0",
67
68
  "@types/pg": "^8.15.1",
68
69
  "@types/supertest": "^6.0.2",
@@ -139,6 +140,7 @@
139
140
  "knex": "^3.1.0",
140
141
  "link": "^2.1.1",
141
142
  "mammoth": "^1.11.0",
143
+ "multer": "^2.1.1",
142
144
  "natural": "^8.1.0",
143
145
  "nodemailer": "^8.0.7",
144
146
  "officeparser": "^5.2.2",