@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.
@@ -3,7 +3,7 @@ import {
3
3
  __resetLiteLLMCatalogCacheForTesting,
4
4
  fetchLiteLLMCatalog,
5
5
  findLiteLLMModel
6
- } from "./chunk-7CCMW3IW.js";
6
+ } from "./chunk-NUVMV5FC.js";
7
7
  export {
8
8
  __resetLiteLLMCatalogCacheForTesting,
9
9
  fetchLiteLLMCatalog,
@@ -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
- const host = process.env.LITELLM_HOST ?? "127.0.0.1";
15
- const port = process.env.LITELLM_PORT ?? "4000";
16
- const masterKey = process.env.LITELLM_MASTER_KEY;
17
- if (!masterKey) return [];
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(`http://${host}:${port}/model/info`, {
63
+ const res = await fetch(`${baseUrl}/model/info`, {
20
64
  method: "GET",
21
- headers: { Authorization: `Bearer ${masterKey}` }
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
- findLiteLLMModel
4
- } from "./chunk-7CCMW3IW.js";
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 host = process.env.LITELLM_HOST ?? "127.0.0.1";
228
- const port = process.env.LITELLM_PORT ?? "4000";
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 host = process.env.LITELLM_HOST ?? "127.0.0.1";
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: `http://${host}:${port}/v1`,
1071
- apiKey: masterKey,
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-WQWYMU7G.js");
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 host = process.env.LITELLM_HOST ?? "127.0.0.1";
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 = `http://${host}:${port}/v1/rerank`;
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
- Authorization: `Bearer ${masterKey}`,
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 fs2 } from "fs";
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 fs2.mkdir(dir, { recursive: true });
7266
- await fs2.writeFile(path2.join(dir, name), body, "utf8");
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 fs2.readdir(dir)).filter((f) => f.endsWith(".ndjson"));
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) => fs2.readFile(path2.join(dir, name), "utf8"),
7248
+ read: async (name) => fs.readFile(path2.join(dir, name), "utf8"),
7276
7249
  remove: async (name) => {
7277
- await fs2.rm(path2.join(dir, name), { force: true });
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-5FTX543Z.js";
6
- import "./chunk-7CCMW3IW.js";
5
+ } from "./chunk-UIIUBZCO.js";
6
+ import "./chunk-NUVMV5FC.js";
7
7
  export {
8
8
  convertExuluToolsToAiSdkTools,
9
9
  hydrateVariables