@exulu/backend 1.61.2 → 1.62.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.
@@ -236,7 +236,7 @@ var resolveConfig = (packageRoot) => {
236
236
  const host = process.env.LITELLM_HOST ?? "127.0.0.1";
237
237
  const port = process.env.LITELLM_PORT ?? "4000";
238
238
  const masterKey = process.env.LITELLM_MASTER_KEY;
239
- const configPath = process.env.LITELLM_CONFIG_PATH ?? resolve(packageRoot, "./config.litellm.yaml");
239
+ const configPath = process.env.LITELLM_CONFIG_PATH ?? resolve(process.cwd(), "./config.litellm.yaml");
240
240
  const venvBin = resolve(packageRoot, "ee/python/.venv/bin");
241
241
  const venvPython = resolve(venvBin, "python");
242
242
  const litellmBin = resolve(venvBin, "litellm");
@@ -263,7 +263,13 @@ var spawnLiteLLM = (cfg) => {
263
263
  `Spawning LiteLLM: ${cfg.litellmBin} --config ${cfg.configPath} --port ${cfg.port} --host ${cfg.host}`
264
264
  );
265
265
  const { DEBUG: _debug, ...rest } = process.env;
266
- const childEnv = { ...rest, DEBUG: "false" };
266
+ const childEnv = {
267
+ ...rest,
268
+ DEBUG: "false"
269
+ };
270
+ if (process.env.EXULU_LITELLM_UI_PATH && !process.env.SERVER_ROOT_PATH) {
271
+ childEnv.SERVER_ROOT_PATH = process.env.EXULU_LITELLM_UI_PATH;
272
+ }
267
273
  const child = spawn(
268
274
  cfg.litellmBin,
269
275
  [
@@ -577,7 +583,17 @@ var getLiteLLMProvider = ({
577
583
  name: "litellm",
578
584
  baseURL: `http://${host}:${port}/v1`,
579
585
  apiKey: masterKey,
580
- fetch: createTaggedFetch(tags)
586
+ fetch: createTaggedFetch(tags),
587
+ // Without this flag the openai-compatible provider strips any
588
+ // responseFormat.schema before sending and warns
589
+ // "JSON response format schema is only supported with structuredOutputs".
590
+ // Models then return free-form JSON that fails Zod parsing in callers
591
+ // using `Output.object({ schema })`. LiteLLM forwards
592
+ // `response_format: { type: "json_schema", ... }` to every upstream it
593
+ // supports — including Vertex Gemini, which translates it into
594
+ // responseSchema/responseMimeType — so enabling this matches the actual
595
+ // proxy contract.
596
+ supportsStructuredOutputs: true
581
597
  });
582
598
  return _litellmProvider;
583
599
  };
@@ -744,7 +760,7 @@ var ExuluTool = class {
744
760
  });
745
761
  providerapikey = resolved.apiKey;
746
762
  }
747
- const { convertExuluToolsToAiSdkTools: convertExuluToolsToAiSdkTools2 } = await import("./convert-exulu-tools-to-ai-sdk-tools-GU3BGHDW.js");
763
+ const { convertExuluToolsToAiSdkTools: convertExuluToolsToAiSdkTools2 } = await import("./convert-exulu-tools-to-ai-sdk-tools-2HF7PPYW.js");
748
764
  const tools = await convertExuluToolsToAiSdkTools2(
749
765
  [this],
750
766
  [],
@@ -1767,22 +1783,45 @@ var createUppyRoutes = async (app, config) => {
1767
1783
  } else {
1768
1784
  prefix += "global";
1769
1785
  }
1786
+ console.log("[EXULU] bucket", config.fileUploads.s3Bucket);
1770
1787
  console.log("[EXULU] prefix", prefix);
1771
- const command = new ListObjectsV2Command({
1772
- Bucket: config.fileUploads.s3Bucket,
1773
- Prefix: prefix,
1774
- MaxKeys: 9,
1775
- ...req.query.continuationToken && {
1776
- ContinuationToken: req.query.continuationToken
1777
- }
1778
- });
1779
- const response = await client.send(command);
1788
+ let response;
1780
1789
  if (req.query.search) {
1781
- const search = req.query.search;
1790
+ const search = req.query.search.toLowerCase();
1782
1791
  console.log("[EXULU] Filtering files by search query", req.query.search);
1783
- response.Contents = response.Contents?.filter(
1784
- (content) => content?.Key?.toLowerCase().includes(search.toLowerCase())
1785
- );
1792
+ const matched = [];
1793
+ let token;
1794
+ do {
1795
+ const page = await client.send(
1796
+ new ListObjectsV2Command({
1797
+ Bucket: config.fileUploads.s3Bucket,
1798
+ Prefix: prefix,
1799
+ ...token && { ContinuationToken: token }
1800
+ })
1801
+ );
1802
+ for (const obj of page.Contents ?? []) {
1803
+ if (obj.Key?.toLowerCase().includes(search)) {
1804
+ matched.push(obj);
1805
+ }
1806
+ }
1807
+ token = page.IsTruncated ? page.NextContinuationToken : void 0;
1808
+ } while (token);
1809
+ response = {
1810
+ $metadata: {},
1811
+ Contents: matched,
1812
+ KeyCount: matched.length,
1813
+ IsTruncated: false
1814
+ };
1815
+ } else {
1816
+ const command = new ListObjectsV2Command({
1817
+ Bucket: config.fileUploads.s3Bucket,
1818
+ Prefix: prefix,
1819
+ MaxKeys: 9,
1820
+ ...req.query.continuationToken && {
1821
+ ContinuationToken: req.query.continuationToken
1822
+ }
1823
+ });
1824
+ response = await client.send(command);
1786
1825
  }
1787
1826
  res.json({
1788
1827
  ...response,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  convertExuluToolsToAiSdkTools
3
- } from "./chunk-KKJF3NAY.js";
3
+ } from "./chunk-LYNLQWXC.js";
4
4
  export {
5
5
  convertExuluToolsToAiSdkTools
6
6
  };
package/dist/index.cjs CHANGED
@@ -403,7 +403,7 @@ var init_supervisor = __esm({
403
403
  const host = process.env.LITELLM_HOST ?? "127.0.0.1";
404
404
  const port = process.env.LITELLM_PORT ?? "4000";
405
405
  const masterKey = process.env.LITELLM_MASTER_KEY;
406
- const configPath = process.env.LITELLM_CONFIG_PATH ?? (0, import_node_path.resolve)(packageRoot, "./config.litellm.yaml");
406
+ const configPath = process.env.LITELLM_CONFIG_PATH ?? (0, import_node_path.resolve)(process.cwd(), "./config.litellm.yaml");
407
407
  const venvBin = (0, import_node_path.resolve)(packageRoot, "ee/python/.venv/bin");
408
408
  const venvPython = (0, import_node_path.resolve)(venvBin, "python");
409
409
  const litellmBin = (0, import_node_path.resolve)(venvBin, "litellm");
@@ -430,7 +430,13 @@ var init_supervisor = __esm({
430
430
  `Spawning LiteLLM: ${cfg.litellmBin} --config ${cfg.configPath} --port ${cfg.port} --host ${cfg.host}`
431
431
  );
432
432
  const { DEBUG: _debug, ...rest } = process.env;
433
- const childEnv = { ...rest, DEBUG: "false" };
433
+ const childEnv = {
434
+ ...rest,
435
+ DEBUG: "false"
436
+ };
437
+ if (process.env.EXULU_LITELLM_UI_PATH && !process.env.SERVER_ROOT_PATH) {
438
+ childEnv.SERVER_ROOT_PATH = process.env.EXULU_LITELLM_UI_PATH;
439
+ }
434
440
  const child = (0, import_node_child_process.spawn)(
435
441
  cfg.litellmBin,
436
442
  [
@@ -1002,7 +1008,17 @@ var init_resolve_model = __esm({
1002
1008
  name: "litellm",
1003
1009
  baseURL: `http://${host}:${port}/v1`,
1004
1010
  apiKey: masterKey,
1005
- fetch: createTaggedFetch(tags)
1011
+ fetch: createTaggedFetch(tags),
1012
+ // Without this flag the openai-compatible provider strips any
1013
+ // responseFormat.schema before sending and warns
1014
+ // "JSON response format schema is only supported with structuredOutputs".
1015
+ // Models then return free-form JSON that fails Zod parsing in callers
1016
+ // using `Output.object({ schema })`. LiteLLM forwards
1017
+ // `response_format: { type: "json_schema", ... }` to every upstream it
1018
+ // supports — including Vertex Gemini, which translates it into
1019
+ // responseSchema/responseMimeType — so enabling this matches the actual
1020
+ // proxy contract.
1021
+ supportsStructuredOutputs: true
1006
1022
  });
1007
1023
  return _litellmProvider;
1008
1024
  };
@@ -1847,22 +1863,45 @@ var init_uppy = __esm({
1847
1863
  } else {
1848
1864
  prefix += "global";
1849
1865
  }
1866
+ console.log("[EXULU] bucket", config.fileUploads.s3Bucket);
1850
1867
  console.log("[EXULU] prefix", prefix);
1851
- const command = new import_client_s3.ListObjectsV2Command({
1852
- Bucket: config.fileUploads.s3Bucket,
1853
- Prefix: prefix,
1854
- MaxKeys: 9,
1855
- ...req.query.continuationToken && {
1856
- ContinuationToken: req.query.continuationToken
1857
- }
1858
- });
1859
- const response = await client2.send(command);
1868
+ let response;
1860
1869
  if (req.query.search) {
1861
- const search = req.query.search;
1870
+ const search = req.query.search.toLowerCase();
1862
1871
  console.log("[EXULU] Filtering files by search query", req.query.search);
1863
- response.Contents = response.Contents?.filter(
1864
- (content) => content?.Key?.toLowerCase().includes(search.toLowerCase())
1865
- );
1872
+ const matched = [];
1873
+ let token;
1874
+ do {
1875
+ const page = await client2.send(
1876
+ new import_client_s3.ListObjectsV2Command({
1877
+ Bucket: config.fileUploads.s3Bucket,
1878
+ Prefix: prefix,
1879
+ ...token && { ContinuationToken: token }
1880
+ })
1881
+ );
1882
+ for (const obj of page.Contents ?? []) {
1883
+ if (obj.Key?.toLowerCase().includes(search)) {
1884
+ matched.push(obj);
1885
+ }
1886
+ }
1887
+ token = page.IsTruncated ? page.NextContinuationToken : void 0;
1888
+ } while (token);
1889
+ response = {
1890
+ $metadata: {},
1891
+ Contents: matched,
1892
+ KeyCount: matched.length,
1893
+ IsTruncated: false
1894
+ };
1895
+ } else {
1896
+ const command = new import_client_s3.ListObjectsV2Command({
1897
+ Bucket: config.fileUploads.s3Bucket,
1898
+ Prefix: prefix,
1899
+ MaxKeys: 9,
1900
+ ...req.query.continuationToken && {
1901
+ ContinuationToken: req.query.continuationToken
1902
+ }
1903
+ });
1904
+ response = await client2.send(command);
1866
1905
  }
1867
1906
  res.json({
1868
1907
  ...response,
@@ -13011,30 +13050,6 @@ type LiteLLMModel {
13011
13050
  pageInfo: PageInfo!
13012
13051
  }
13013
13052
  `;
13014
- typeDefs += `
13015
- agentWorldAgents: [AgentWorldAgent!]!
13016
- `;
13017
- resolvers.Query["agentWorldAgents"] = async (_, _args, context) => {
13018
- const { db: db2 } = context;
13019
- const sessions = await db2("agent_sessions as s").select([
13020
- "s.id as sessionId",
13021
- "s.agent as agentId",
13022
- "s.currenttask as currentTask",
13023
- "s.createdAt as lastActivityAt",
13024
- "s.created_by as userId"
13025
- ]).whereNotNull("s.currenttask").limit(20).orderBy("s.createdAt", "desc");
13026
- return sessions.map((row) => {
13027
- const agent = providers.find((p) => p.id === row.agentId);
13028
- return {
13029
- sessionId: row.sessionId,
13030
- agentId: row.agentId ?? "",
13031
- agentName: agent?.name ?? row.agentId ?? "Agent",
13032
- agentImage: agent?.image ?? null,
13033
- currentTask: row.currentTask,
13034
- lastActivityAt: row.lastActivityAt
13035
- };
13036
- });
13037
- };
13038
13053
  typeDefs += "}\n";
13039
13054
  mutationDefs += "}\n";
13040
13055
  const genericTypes = `
@@ -13249,15 +13264,6 @@ type StatisticsResult {
13249
13264
  group: String!
13250
13265
  count: Int!
13251
13266
  }
13252
-
13253
- type AgentWorldAgent {
13254
- sessionId: ID!
13255
- agentId: ID!
13256
- agentName: String!
13257
- agentImage: String
13258
- currentTask: String
13259
- lastActivityAt: String
13260
- }
13261
13267
  `;
13262
13268
  const fullSDL = typeDefs + mutationDefs + modelDefs + genericTypes;
13263
13269
  const schema = (0, import_schema.makeExecutableSchema)({
@@ -13468,7 +13474,7 @@ var import_utils5 = require("@apollo/utils.keyvaluecache");
13468
13474
  var import_body_parser = __toESM(require("body-parser"), 1);
13469
13475
  var import_crypto_js8 = __toESM(require("crypto-js"), 1);
13470
13476
  var import_openai = __toESM(require("openai"), 1);
13471
- var import_fs4 = __toESM(require("fs"), 1);
13477
+ var import_fs3 = __toESM(require("fs"), 1);
13472
13478
  var import_node_crypto5 = require("crypto");
13473
13479
  var import_api2 = require("@opentelemetry/api");
13474
13480
  init_check_record_access();
@@ -13551,7 +13557,7 @@ async function clearSessionCurrentTask(session) {
13551
13557
  }
13552
13558
 
13553
13559
  // src/exulu/provider.ts
13554
- var import_fs2 = __toESM(require("fs"), 1);
13560
+ var import_fs2 = require("fs");
13555
13561
  var ExuluProvider = class {
13556
13562
  // Must begin with a letter (a-z) or underscore (_). Subsequent characters in a name can be letters, digits (0-9), or
13557
13563
  // underscores and be a max length of 80 characters and at least 5 characters long.
@@ -14214,7 +14220,6 @@ ${extractedText}
14214
14220
  // todo make this configurable?
14215
14221
  page: 1
14216
14222
  });
14217
- import_fs2.default.writeFileSync("pre-fetched-relevant-information.json", JSON.stringify(result2, null, 2));
14218
14223
  if (result2?.chunks?.length) {
14219
14224
  memoryItems = result2.chunks;
14220
14225
  memoryContext = `
@@ -14345,7 +14350,6 @@ ${skillsList}
14345
14350
 
14346
14351
  When a tool execution is not approved by the user, do not retry it unless explicitly asked by the user. ' +
14347
14352
  'Inform the user that the action was not performed.`;
14348
- import_fs2.default.writeFileSync("system-prompt.txt", system);
14349
14353
  console.log("[EXULU] Tools", currentTools?.map((x) => x.name));
14350
14354
  console.log("[EXULU] Skills", currentSkills?.map((x) => x.name));
14351
14355
  const tools = await convertExuluToolsToAiSdkTools(
@@ -14866,212 +14870,6 @@ See docs/superpowers/specs/2026-05-31-in-chat-image-generation-design.md for the
14866
14870
 
14867
14871
  // src/exulu/routes.ts
14868
14872
  var import_node_path5 = require("path");
14869
-
14870
- // src/utils/python-setup.ts
14871
- init_cjs_shims();
14872
- var import_child_process = require("child_process");
14873
- var import_util = require("util");
14874
- var import_path = require("path");
14875
- var import_fs3 = require("fs");
14876
- var import_url = require("url");
14877
- var execAsync4 = (0, import_util.promisify)(import_child_process.exec);
14878
- function getPackageRoot() {
14879
- const currentFile = (0, import_url.fileURLToPath)(importMetaUrl);
14880
- let currentDir = (0, import_path.dirname)(currentFile);
14881
- let attempts = 0;
14882
- const maxAttempts = 10;
14883
- while (attempts < maxAttempts) {
14884
- const packageJsonPath = (0, import_path.join)(currentDir, "package.json");
14885
- if ((0, import_fs3.existsSync)(packageJsonPath)) {
14886
- try {
14887
- const packageJson = JSON.parse((0, import_fs3.readFileSync)(packageJsonPath, "utf-8"));
14888
- if (packageJson.name === "@exulu/backend") {
14889
- return currentDir;
14890
- }
14891
- } catch {
14892
- }
14893
- }
14894
- const parentDir = (0, import_path.resolve)(currentDir, "..");
14895
- if (parentDir === currentDir) {
14896
- break;
14897
- }
14898
- currentDir = parentDir;
14899
- attempts++;
14900
- }
14901
- const fallback = (0, import_path.resolve)((0, import_path.dirname)((0, import_url.fileURLToPath)(importMetaUrl)), "../..");
14902
- return fallback;
14903
- }
14904
- function getSetupScriptPath(packageRoot) {
14905
- return (0, import_path.resolve)(packageRoot, "ee/python/setup.sh");
14906
- }
14907
- function getVenvPath(packageRoot) {
14908
- return (0, import_path.resolve)(packageRoot, "ee/python/.venv");
14909
- }
14910
- function isPythonEnvironmentSetup(packageRoot) {
14911
- const root = packageRoot ?? getPackageRoot();
14912
- const venvPath = getVenvPath(root);
14913
- const pythonPath = (0, import_path.join)(venvPath, "bin", "python");
14914
- return (0, import_fs3.existsSync)(venvPath) && (0, import_fs3.existsSync)(pythonPath);
14915
- }
14916
- async function setupPythonEnvironment(options = {}) {
14917
- const {
14918
- packageRoot = getPackageRoot(),
14919
- force = false,
14920
- verbose = false,
14921
- timeout = 6e5
14922
- // 10 minutes
14923
- } = options;
14924
- if (!force && isPythonEnvironmentSetup(packageRoot)) {
14925
- if (verbose) {
14926
- console.log("\u2713 Python environment already set up");
14927
- }
14928
- return {
14929
- success: true,
14930
- message: "Python environment already exists",
14931
- alreadyExists: true
14932
- };
14933
- }
14934
- const setupScriptPath = getSetupScriptPath(packageRoot);
14935
- if (!(0, import_fs3.existsSync)(setupScriptPath)) {
14936
- return {
14937
- success: false,
14938
- message: `Setup script not found at: ${setupScriptPath}`,
14939
- alreadyExists: false
14940
- };
14941
- }
14942
- try {
14943
- if (verbose) {
14944
- console.log("Setting up Python environment...");
14945
- }
14946
- const { stdout, stderr } = await execAsync4(`bash "${setupScriptPath}"`, {
14947
- cwd: packageRoot,
14948
- timeout,
14949
- env: {
14950
- ...process.env,
14951
- // Ensure script can write to the directory
14952
- PYTHONDONTWRITEBYTECODE: "1"
14953
- },
14954
- maxBuffer: 10 * 1024 * 1024
14955
- // 10MB buffer
14956
- });
14957
- const output = stdout + stderr;
14958
- const versionMatch = output.match(/Python (\d+\.\d+\.\d+)/);
14959
- const pythonVersion = versionMatch ? versionMatch[1] : void 0;
14960
- if (verbose) {
14961
- console.log(output);
14962
- }
14963
- return {
14964
- success: true,
14965
- message: "Python environment set up successfully",
14966
- alreadyExists: false,
14967
- pythonVersion,
14968
- output
14969
- };
14970
- } catch (error) {
14971
- const errorOutput = error.stdout + error.stderr;
14972
- return {
14973
- success: false,
14974
- message: `Setup failed: ${error.message}`,
14975
- alreadyExists: false,
14976
- output: errorOutput
14977
- };
14978
- }
14979
- }
14980
- function getPythonSetupInstructions() {
14981
- return `
14982
- Python environment not set up. Please run one of the following commands:
14983
-
14984
- Option 1 (Automatic):
14985
- import { setupPythonEnvironment } from '@exulu/backend';
14986
- await setupPythonEnvironment();
14987
-
14988
- Option 2 (Manual - for package consumers):
14989
- npx @exulu/backend setup-python
14990
-
14991
- Option 3 (Manual - for contributors):
14992
- npm run python:setup
14993
-
14994
- These commands will automatically create a Python virtual environment (.venv)
14995
- in the @exulu/backend package and install all required dependencies.
14996
-
14997
- Requirements:
14998
- - Python 3.10 or higher must be installed
14999
- - pip must be available
15000
- - venv module must be available (for creating virtual environments)
15001
-
15002
- If Python dependencies are not installed, install them first, then run one of the commands above:
15003
- - macOS: brew install python@3.12
15004
- - Ubuntu/Debian: sudo apt-get install python3.12 python3-pip python3-venv
15005
- - Alpine Linux: apk add python3 py3-pip python3-dev
15006
- - Windows: Download from https://www.python.org/downloads/
15007
-
15008
- Note: In Docker containers, ensure you install all three components:
15009
- Ubuntu/Debian: apt-get install -y python3 python3-pip python3-venv
15010
- Alpine: apk add python3 py3-pip python3-dev
15011
- `.trim();
15012
- }
15013
- async function validatePythonEnvironment(packageRoot, checkPackages = true) {
15014
- const root = packageRoot ?? getPackageRoot();
15015
- const venvPath = getVenvPath(root);
15016
- const pythonPath = (0, import_path.join)(venvPath, "bin", "python");
15017
- if (!(0, import_fs3.existsSync)(venvPath)) {
15018
- return {
15019
- valid: false,
15020
- message: getPythonSetupInstructions()
15021
- };
15022
- }
15023
- if (!(0, import_fs3.existsSync)(pythonPath)) {
15024
- return {
15025
- valid: false,
15026
- message: "Python virtual environment is corrupted. Please run:\n await setupPythonEnvironment({ force: true })"
15027
- };
15028
- }
15029
- try {
15030
- await execAsync4(`"${pythonPath}" --version`, { cwd: root });
15031
- } catch {
15032
- return {
15033
- valid: false,
15034
- message: "Python executable is not working. Please run:\n await setupPythonEnvironment({ force: true })"
15035
- };
15036
- }
15037
- if (checkPackages) {
15038
- const criticalPackages = ["docling", "transformers"];
15039
- const missingPackages = [];
15040
- for (const pkg of criticalPackages) {
15041
- try {
15042
- await execAsync4(`"${pythonPath}" -c "import ${pkg}"`, {
15043
- cwd: root,
15044
- timeout: 1e4
15045
- // 10 second timeout per import check
15046
- });
15047
- } catch {
15048
- missingPackages.push(pkg);
15049
- }
15050
- }
15051
- if (missingPackages.length > 0) {
15052
- return {
15053
- valid: false,
15054
- message: `Python environment exists but required packages are not installed: ${missingPackages.join(", ")}
15055
-
15056
- This usually happens when:
15057
- 1. The .venv folder was copied but dependencies were not installed
15058
- 2. The package was installed via npm but setup script was not run
15059
-
15060
- Please run:
15061
- await setupPythonEnvironment({ force: true })
15062
-
15063
- Or manually run the setup script:
15064
- bash ` + getSetupScriptPath(root)
15065
- };
15066
- }
15067
- }
15068
- return {
15069
- valid: true,
15070
- message: "Python environment is valid"
15071
- };
15072
- }
15073
-
15074
- // src/exulu/routes.ts
15075
14873
  init_tags();
15076
14874
  var import_multer = __toESM(require("multer"), 1);
15077
14875
 
@@ -15747,7 +15545,7 @@ var REQUEST_SIZE_LIMIT = "50mb";
15747
15545
  var getExuluVersionNumber = async () => {
15748
15546
  try {
15749
15547
  const path3 = process.cwd();
15750
- const packageJson = import_fs4.default.readFileSync(path3 + "/package.json", "utf8");
15548
+ const packageJson = import_fs3.default.readFileSync(path3 + "/package.json", "utf8");
15751
15549
  const packageData = JSON.parse(packageJson);
15752
15550
  const exuluVersion = packageData.dependencies["@exulu/backend"];
15753
15551
  console.log(`[EXULU] Installed exulu-backend version: ${exuluVersion}`);
@@ -16667,7 +16465,7 @@ ${customInstructions}` : agent.instructions;
16667
16465
  const imageModelsByName = (() => {
16668
16466
  if (!isLiteLLMEnabled() || !config?.fileUploads) return /* @__PURE__ */ new Map();
16669
16467
  try {
16670
- const configPath = process.env.LITELLM_CONFIG_PATH ?? (0, import_node_path5.resolve)(getPackageRoot(), "./config.litellm.yaml");
16468
+ const configPath = process.env.LITELLM_CONFIG_PATH ?? (0, import_node_path5.resolve)(process.cwd(), "./config.litellm.yaml");
16671
16469
  const models2 = parseImageGenerationModels(configPath);
16672
16470
  return new Map(models2.map((m) => [m.model_name, m]));
16673
16471
  } catch (err) {
@@ -17145,6 +16943,80 @@ ${style.markdown}` : params.prompt;
17145
16943
  }));
17146
16944
  res.status(200).json({ history });
17147
16945
  });
16946
+ const litellmUiPath = "/litellm-admin";
16947
+ if (isLiteLLMEnabled() && litellmUiPath) {
16948
+ console.log("[EXULU] Registering LiteLLM UI at", litellmUiPath);
16949
+ app.use(litellmUiPath, async (req, res) => {
16950
+ const host = process.env.LITELLM_HOST ?? "127.0.0.1";
16951
+ const port = process.env.LITELLM_PORT ?? "4000";
16952
+ const upstreamUrl = `http://${host}:${port}${litellmUiPath}${req.url}`;
16953
+ const upstreamHeaders = {};
16954
+ for (const [name, value] of Object.entries(req.headers)) {
16955
+ if (value === void 0) continue;
16956
+ const lower = name.toLowerCase();
16957
+ if (lower === "host" || lower === "content-length" || lower === "connection" || lower === "transfer-encoding" || lower === "accept-encoding")
16958
+ continue;
16959
+ upstreamHeaders[name] = Array.isArray(value) ? value.join(", ") : value;
16960
+ }
16961
+ const methodHasBody = !["GET", "HEAD"].includes(req.method);
16962
+ let body;
16963
+ if (methodHasBody && req.body && typeof req.body === "object" && Object.keys(req.body).length > 0) {
16964
+ body = JSON.stringify(req.body);
16965
+ upstreamHeaders["content-type"] = "application/json";
16966
+ }
16967
+ try {
16968
+ const upstream = await fetch(upstreamUrl, {
16969
+ method: req.method,
16970
+ headers: upstreamHeaders,
16971
+ body,
16972
+ // Pass redirects through to the browser so LiteLLM's post-login
16973
+ // redirect lands the user on the right URL (the Location already
16974
+ // includes SERVER_ROOT_PATH).
16975
+ redirect: "manual"
16976
+ });
16977
+ res.status(upstream.status);
16978
+ const upstreamOrigin = `http://${host}:${port}`;
16979
+ upstream.headers.forEach((value, name) => {
16980
+ const lower = name.toLowerCase();
16981
+ if (lower === "content-encoding" || lower === "content-length" || lower === "transfer-encoding" || lower === "connection")
16982
+ return;
16983
+ if (lower === "location") {
16984
+ let loc = value.startsWith(upstreamOrigin) ? value.slice(upstreamOrigin.length) : value;
16985
+ if (loc.startsWith("/") && loc !== litellmUiPath && !loc.startsWith(`${litellmUiPath}/`)) {
16986
+ loc = `${litellmUiPath}${loc}`;
16987
+ }
16988
+ res.setHeader(name, loc);
16989
+ return;
16990
+ }
16991
+ res.setHeader(name, value);
16992
+ });
16993
+ if (!upstream.body) {
16994
+ res.end();
16995
+ return;
16996
+ }
16997
+ const reader = upstream.body.getReader();
16998
+ try {
16999
+ while (true) {
17000
+ const { done, value } = await reader.read();
17001
+ if (done) break;
17002
+ if (value) res.write(value);
17003
+ }
17004
+ } finally {
17005
+ reader.releaseLock();
17006
+ }
17007
+ res.end();
17008
+ } catch (err) {
17009
+ console.error("[EXULU] LiteLLM UI proxy failed", err);
17010
+ if (!res.headersSent) {
17011
+ res.status(502).json({
17012
+ detail: err instanceof Error ? err.message : "LiteLLM UI proxy failed."
17013
+ });
17014
+ } else {
17015
+ res.end();
17016
+ }
17017
+ }
17018
+ });
17019
+ }
17148
17020
  app.use("/litellm/:project", async (req, res) => {
17149
17021
  if (!isLiteLLMEnabled()) {
17150
17022
  res.status(503).json({
@@ -20654,6 +20526,210 @@ init_entitlements();
20654
20526
  init_system_dependencies();
20655
20527
  init_supervisor();
20656
20528
 
20529
+ // src/utils/python-setup.ts
20530
+ init_cjs_shims();
20531
+ var import_child_process = require("child_process");
20532
+ var import_util = require("util");
20533
+ var import_path = require("path");
20534
+ var import_fs4 = require("fs");
20535
+ var import_url = require("url");
20536
+ var execAsync4 = (0, import_util.promisify)(import_child_process.exec);
20537
+ function getPackageRoot() {
20538
+ const currentFile = (0, import_url.fileURLToPath)(importMetaUrl);
20539
+ let currentDir = (0, import_path.dirname)(currentFile);
20540
+ let attempts = 0;
20541
+ const maxAttempts = 10;
20542
+ while (attempts < maxAttempts) {
20543
+ const packageJsonPath = (0, import_path.join)(currentDir, "package.json");
20544
+ if ((0, import_fs4.existsSync)(packageJsonPath)) {
20545
+ try {
20546
+ const packageJson = JSON.parse((0, import_fs4.readFileSync)(packageJsonPath, "utf-8"));
20547
+ if (packageJson.name === "@exulu/backend") {
20548
+ return currentDir;
20549
+ }
20550
+ } catch {
20551
+ }
20552
+ }
20553
+ const parentDir = (0, import_path.resolve)(currentDir, "..");
20554
+ if (parentDir === currentDir) {
20555
+ break;
20556
+ }
20557
+ currentDir = parentDir;
20558
+ attempts++;
20559
+ }
20560
+ const fallback = (0, import_path.resolve)((0, import_path.dirname)((0, import_url.fileURLToPath)(importMetaUrl)), "../..");
20561
+ return fallback;
20562
+ }
20563
+ function getSetupScriptPath(packageRoot) {
20564
+ return (0, import_path.resolve)(packageRoot, "ee/python/setup.sh");
20565
+ }
20566
+ function getVenvPath(packageRoot) {
20567
+ return (0, import_path.resolve)(packageRoot, "ee/python/.venv");
20568
+ }
20569
+ function isPythonEnvironmentSetup(packageRoot) {
20570
+ const root = packageRoot ?? getPackageRoot();
20571
+ const venvPath = getVenvPath(root);
20572
+ const pythonPath = (0, import_path.join)(venvPath, "bin", "python");
20573
+ return (0, import_fs4.existsSync)(venvPath) && (0, import_fs4.existsSync)(pythonPath);
20574
+ }
20575
+ async function setupPythonEnvironment(options = {}) {
20576
+ const {
20577
+ packageRoot = getPackageRoot(),
20578
+ force = false,
20579
+ verbose = false,
20580
+ timeout = 6e5
20581
+ // 10 minutes
20582
+ } = options;
20583
+ if (!force && isPythonEnvironmentSetup(packageRoot)) {
20584
+ if (verbose) {
20585
+ console.log("\u2713 Python environment already set up");
20586
+ }
20587
+ return {
20588
+ success: true,
20589
+ message: "Python environment already exists",
20590
+ alreadyExists: true
20591
+ };
20592
+ }
20593
+ const setupScriptPath = getSetupScriptPath(packageRoot);
20594
+ if (!(0, import_fs4.existsSync)(setupScriptPath)) {
20595
+ return {
20596
+ success: false,
20597
+ message: `Setup script not found at: ${setupScriptPath}`,
20598
+ alreadyExists: false
20599
+ };
20600
+ }
20601
+ try {
20602
+ if (verbose) {
20603
+ console.log("Setting up Python environment...");
20604
+ }
20605
+ const { stdout, stderr } = await execAsync4(`bash "${setupScriptPath}"`, {
20606
+ cwd: packageRoot,
20607
+ timeout,
20608
+ env: {
20609
+ ...process.env,
20610
+ // Ensure script can write to the directory
20611
+ PYTHONDONTWRITEBYTECODE: "1"
20612
+ },
20613
+ maxBuffer: 10 * 1024 * 1024
20614
+ // 10MB buffer
20615
+ });
20616
+ const output = stdout + stderr;
20617
+ const versionMatch = output.match(/Python (\d+\.\d+\.\d+)/);
20618
+ const pythonVersion = versionMatch ? versionMatch[1] : void 0;
20619
+ if (verbose) {
20620
+ console.log(output);
20621
+ }
20622
+ return {
20623
+ success: true,
20624
+ message: "Python environment set up successfully",
20625
+ alreadyExists: false,
20626
+ pythonVersion,
20627
+ output
20628
+ };
20629
+ } catch (error) {
20630
+ const errorOutput = error.stdout + error.stderr;
20631
+ return {
20632
+ success: false,
20633
+ message: `Setup failed: ${error.message}`,
20634
+ alreadyExists: false,
20635
+ output: errorOutput
20636
+ };
20637
+ }
20638
+ }
20639
+ function getPythonSetupInstructions() {
20640
+ return `
20641
+ Python environment not set up. Please run one of the following commands:
20642
+
20643
+ Option 1 (Automatic):
20644
+ import { setupPythonEnvironment } from '@exulu/backend';
20645
+ await setupPythonEnvironment();
20646
+
20647
+ Option 2 (Manual - for package consumers):
20648
+ npx @exulu/backend setup-python
20649
+
20650
+ Option 3 (Manual - for contributors):
20651
+ npm run python:setup
20652
+
20653
+ These commands will automatically create a Python virtual environment (.venv)
20654
+ in the @exulu/backend package and install all required dependencies.
20655
+
20656
+ Requirements:
20657
+ - Python 3.10 or higher must be installed
20658
+ - pip must be available
20659
+ - venv module must be available (for creating virtual environments)
20660
+
20661
+ If Python dependencies are not installed, install them first, then run one of the commands above:
20662
+ - macOS: brew install python@3.12
20663
+ - Ubuntu/Debian: sudo apt-get install python3.12 python3-pip python3-venv
20664
+ - Alpine Linux: apk add python3 py3-pip python3-dev
20665
+ - Windows: Download from https://www.python.org/downloads/
20666
+
20667
+ Note: In Docker containers, ensure you install all three components:
20668
+ Ubuntu/Debian: apt-get install -y python3 python3-pip python3-venv
20669
+ Alpine: apk add python3 py3-pip python3-dev
20670
+ `.trim();
20671
+ }
20672
+ async function validatePythonEnvironment(packageRoot, checkPackages = true) {
20673
+ const root = packageRoot ?? getPackageRoot();
20674
+ const venvPath = getVenvPath(root);
20675
+ const pythonPath = (0, import_path.join)(venvPath, "bin", "python");
20676
+ if (!(0, import_fs4.existsSync)(venvPath)) {
20677
+ return {
20678
+ valid: false,
20679
+ message: getPythonSetupInstructions()
20680
+ };
20681
+ }
20682
+ if (!(0, import_fs4.existsSync)(pythonPath)) {
20683
+ return {
20684
+ valid: false,
20685
+ message: "Python virtual environment is corrupted. Please run:\n await setupPythonEnvironment({ force: true })"
20686
+ };
20687
+ }
20688
+ try {
20689
+ await execAsync4(`"${pythonPath}" --version`, { cwd: root });
20690
+ } catch {
20691
+ return {
20692
+ valid: false,
20693
+ message: "Python executable is not working. Please run:\n await setupPythonEnvironment({ force: true })"
20694
+ };
20695
+ }
20696
+ if (checkPackages) {
20697
+ const criticalPackages = ["docling", "transformers"];
20698
+ const missingPackages = [];
20699
+ for (const pkg of criticalPackages) {
20700
+ try {
20701
+ await execAsync4(`"${pythonPath}" -c "import ${pkg}"`, {
20702
+ cwd: root,
20703
+ timeout: 1e4
20704
+ // 10 second timeout per import check
20705
+ });
20706
+ } catch {
20707
+ missingPackages.push(pkg);
20708
+ }
20709
+ }
20710
+ if (missingPackages.length > 0) {
20711
+ return {
20712
+ valid: false,
20713
+ message: `Python environment exists but required packages are not installed: ${missingPackages.join(", ")}
20714
+
20715
+ This usually happens when:
20716
+ 1. The .venv folder was copied but dependencies were not installed
20717
+ 2. The package was installed via npm but setup script was not run
20718
+
20719
+ Please run:
20720
+ await setupPythonEnvironment({ force: true })
20721
+
20722
+ Or manually run the setup script:
20723
+ bash ` + getSetupScriptPath(root)
20724
+ };
20725
+ }
20726
+ }
20727
+ return {
20728
+ valid: true,
20729
+ message: "Python environment is valid"
20730
+ };
20731
+ }
20732
+
20657
20733
  // src/templates/contexts/index.ts
20658
20734
  init_cjs_shims();
20659
20735
 
@@ -20816,6 +20892,12 @@ var ExuluApp = class {
20816
20892
  ...providers ?? []
20817
20893
  ];
20818
20894
  this._config = config;
20895
+ if (isLiteLLMEnabled()) {
20896
+ const uiPath = "/litellm-admin";
20897
+ if (uiPath) {
20898
+ process.env.EXULU_LITELLM_UI_PATH = uiPath;
20899
+ }
20900
+ }
20819
20901
  const transcriptionTools = [];
20820
20902
  if (process.env.TRANSCRIPTION_MODEL && config?.fileUploads && config?.fileUploads?.s3region && config?.fileUploads?.s3key && config?.fileUploads?.s3secret && config?.fileUploads?.s3Bucket) {
20821
20903
  transcriptionTools.push(transcribeTool);
@@ -20823,7 +20905,7 @@ var ExuluApp = class {
20823
20905
  const imageGenerationTools = [];
20824
20906
  const s3Configured = !!config?.fileUploads && !!config.fileUploads.s3region && !!config.fileUploads.s3key && !!config.fileUploads.s3secret && !!config.fileUploads.s3Bucket;
20825
20907
  if (isLiteLLMEnabled() && s3Configured) {
20826
- const configPath = process.env.LITELLM_CONFIG_PATH ?? (0, import_node_path7.resolve)(getPackageRoot(), "./config.litellm.yaml");
20908
+ const configPath = process.env.LITELLM_CONFIG_PATH ?? (0, import_node_path7.resolve)(process.cwd(), "./config.litellm.yaml");
20827
20909
  const imageModels = parseImageGenerationModels(configPath);
20828
20910
  if (imageModels.length > 0) {
20829
20911
  console.log(
@@ -23097,7 +23179,7 @@ ${WARNING_BANNER}`);
23097
23179
  };
23098
23180
  var log3 = (line) => console.log(`[EXULU-LITELLM] ${line}`);
23099
23181
  var initLiteLLMDatabase = async (packageRoot) => {
23100
- const configPath = process.env.LITELLM_CONFIG_PATH ?? (0, import_node_path8.resolve)(packageRoot, "./config.litellm.yaml");
23182
+ const configPath = process.env.LITELLM_CONFIG_PATH ?? (0, import_node_path8.resolve)(process.cwd(), "./config.litellm.yaml");
23101
23183
  const safety = checkLiteLLMDatabaseSafety(configPath);
23102
23184
  if (safety.ok && safety.reason === "no-litellm-db-mode") return;
23103
23185
  if (!safety.ok && safety.reason === "unparseable-url") {
package/dist/index.js CHANGED
@@ -53,7 +53,7 @@ import {
53
53
  vectorSearch,
54
54
  waitForLiteLLMReady,
55
55
  withRetry
56
- } from "./chunk-KKJF3NAY.js";
56
+ } from "./chunk-LYNLQWXC.js";
57
57
  import {
58
58
  findLiteLLMModel
59
59
  } from "./chunk-ILAHW4UT.js";
@@ -5182,30 +5182,6 @@ type LiteLLMModel {
5182
5182
  pageInfo: PageInfo!
5183
5183
  }
5184
5184
  `;
5185
- typeDefs += `
5186
- agentWorldAgents: [AgentWorldAgent!]!
5187
- `;
5188
- resolvers.Query["agentWorldAgents"] = async (_, _args, context) => {
5189
- const { db } = context;
5190
- const sessions = await db("agent_sessions as s").select([
5191
- "s.id as sessionId",
5192
- "s.agent as agentId",
5193
- "s.currenttask as currentTask",
5194
- "s.createdAt as lastActivityAt",
5195
- "s.created_by as userId"
5196
- ]).whereNotNull("s.currenttask").limit(20).orderBy("s.createdAt", "desc");
5197
- return sessions.map((row) => {
5198
- const agent = providers.find((p) => p.id === row.agentId);
5199
- return {
5200
- sessionId: row.sessionId,
5201
- agentId: row.agentId ?? "",
5202
- agentName: agent?.name ?? row.agentId ?? "Agent",
5203
- agentImage: agent?.image ?? null,
5204
- currentTask: row.currentTask,
5205
- lastActivityAt: row.lastActivityAt
5206
- };
5207
- });
5208
- };
5209
5185
  typeDefs += "}\n";
5210
5186
  mutationDefs += "}\n";
5211
5187
  const genericTypes = `
@@ -5420,15 +5396,6 @@ type StatisticsResult {
5420
5396
  group: String!
5421
5397
  count: Int!
5422
5398
  }
5423
-
5424
- type AgentWorldAgent {
5425
- sessionId: ID!
5426
- agentId: ID!
5427
- agentName: String!
5428
- agentImage: String
5429
- currentTask: String
5430
- lastActivityAt: String
5431
- }
5432
5399
  `;
5433
5400
  const fullSDL = typeDefs + mutationDefs + modelDefs + genericTypes;
5434
5401
  const schema = makeExecutableSchema({
@@ -5703,7 +5670,7 @@ async function clearSessionCurrentTask(session) {
5703
5670
  }
5704
5671
 
5705
5672
  // src/exulu/provider.ts
5706
- import fs2 from "fs";
5673
+ import "fs";
5707
5674
  var ExuluProvider = class {
5708
5675
  // Must begin with a letter (a-z) or underscore (_). Subsequent characters in a name can be letters, digits (0-9), or
5709
5676
  // underscores and be a max length of 80 characters and at least 5 characters long.
@@ -6366,7 +6333,6 @@ ${extractedText}
6366
6333
  // todo make this configurable?
6367
6334
  page: 1
6368
6335
  });
6369
- fs2.writeFileSync("pre-fetched-relevant-information.json", JSON.stringify(result2, null, 2));
6370
6336
  if (result2?.chunks?.length) {
6371
6337
  memoryItems = result2.chunks;
6372
6338
  memoryContext = `
@@ -6497,7 +6463,6 @@ ${skillsList}
6497
6463
 
6498
6464
  When a tool execution is not approved by the user, do not retry it unless explicitly asked by the user. ' +
6499
6465
  'Inform the user that the action was not performed.`;
6500
- fs2.writeFileSync("system-prompt.txt", system);
6501
6466
  console.log("[EXULU] Tools", currentTools?.map((x) => x.name));
6502
6467
  console.log("[EXULU] Skills", currentSkills?.map((x) => x.name));
6503
6468
  const tools = await convertExuluToolsToAiSdkTools(
@@ -8595,7 +8560,7 @@ ${customInstructions}` : agent.instructions;
8595
8560
  const imageModelsByName = (() => {
8596
8561
  if (!isLiteLLMEnabled() || !config?.fileUploads) return /* @__PURE__ */ new Map();
8597
8562
  try {
8598
- const configPath = process.env.LITELLM_CONFIG_PATH ?? resolvePath(getPackageRoot(), "./config.litellm.yaml");
8563
+ const configPath = process.env.LITELLM_CONFIG_PATH ?? resolvePath(process.cwd(), "./config.litellm.yaml");
8599
8564
  const models2 = parseImageGenerationModels(configPath);
8600
8565
  return new Map(models2.map((m) => [m.model_name, m]));
8601
8566
  } catch (err) {
@@ -9073,6 +9038,80 @@ ${style.markdown}` : params.prompt;
9073
9038
  }));
9074
9039
  res.status(200).json({ history });
9075
9040
  });
9041
+ const litellmUiPath = "/litellm-admin";
9042
+ if (isLiteLLMEnabled() && litellmUiPath) {
9043
+ console.log("[EXULU] Registering LiteLLM UI at", litellmUiPath);
9044
+ app.use(litellmUiPath, async (req, res) => {
9045
+ const host = process.env.LITELLM_HOST ?? "127.0.0.1";
9046
+ const port = process.env.LITELLM_PORT ?? "4000";
9047
+ const upstreamUrl = `http://${host}:${port}${litellmUiPath}${req.url}`;
9048
+ const upstreamHeaders = {};
9049
+ for (const [name, value] of Object.entries(req.headers)) {
9050
+ if (value === void 0) continue;
9051
+ const lower = name.toLowerCase();
9052
+ if (lower === "host" || lower === "content-length" || lower === "connection" || lower === "transfer-encoding" || lower === "accept-encoding")
9053
+ continue;
9054
+ upstreamHeaders[name] = Array.isArray(value) ? value.join(", ") : value;
9055
+ }
9056
+ const methodHasBody = !["GET", "HEAD"].includes(req.method);
9057
+ let body;
9058
+ if (methodHasBody && req.body && typeof req.body === "object" && Object.keys(req.body).length > 0) {
9059
+ body = JSON.stringify(req.body);
9060
+ upstreamHeaders["content-type"] = "application/json";
9061
+ }
9062
+ try {
9063
+ const upstream = await fetch(upstreamUrl, {
9064
+ method: req.method,
9065
+ headers: upstreamHeaders,
9066
+ body,
9067
+ // Pass redirects through to the browser so LiteLLM's post-login
9068
+ // redirect lands the user on the right URL (the Location already
9069
+ // includes SERVER_ROOT_PATH).
9070
+ redirect: "manual"
9071
+ });
9072
+ res.status(upstream.status);
9073
+ const upstreamOrigin = `http://${host}:${port}`;
9074
+ upstream.headers.forEach((value, name) => {
9075
+ const lower = name.toLowerCase();
9076
+ if (lower === "content-encoding" || lower === "content-length" || lower === "transfer-encoding" || lower === "connection")
9077
+ return;
9078
+ if (lower === "location") {
9079
+ let loc = value.startsWith(upstreamOrigin) ? value.slice(upstreamOrigin.length) : value;
9080
+ if (loc.startsWith("/") && loc !== litellmUiPath && !loc.startsWith(`${litellmUiPath}/`)) {
9081
+ loc = `${litellmUiPath}${loc}`;
9082
+ }
9083
+ res.setHeader(name, loc);
9084
+ return;
9085
+ }
9086
+ res.setHeader(name, value);
9087
+ });
9088
+ if (!upstream.body) {
9089
+ res.end();
9090
+ return;
9091
+ }
9092
+ const reader = upstream.body.getReader();
9093
+ try {
9094
+ while (true) {
9095
+ const { done, value } = await reader.read();
9096
+ if (done) break;
9097
+ if (value) res.write(value);
9098
+ }
9099
+ } finally {
9100
+ reader.releaseLock();
9101
+ }
9102
+ res.end();
9103
+ } catch (err) {
9104
+ console.error("[EXULU] LiteLLM UI proxy failed", err);
9105
+ if (!res.headersSent) {
9106
+ res.status(502).json({
9107
+ detail: err instanceof Error ? err.message : "LiteLLM UI proxy failed."
9108
+ });
9109
+ } else {
9110
+ res.end();
9111
+ }
9112
+ }
9113
+ });
9114
+ }
9076
9115
  app.use("/litellm/:project", async (req, res) => {
9077
9116
  if (!isLiteLLMEnabled()) {
9078
9117
  res.status(503).json({
@@ -12678,6 +12717,12 @@ var ExuluApp = class {
12678
12717
  ...providers ?? []
12679
12718
  ];
12680
12719
  this._config = config;
12720
+ if (isLiteLLMEnabled()) {
12721
+ const uiPath = "/litellm-admin";
12722
+ if (uiPath) {
12723
+ process.env.EXULU_LITELLM_UI_PATH = uiPath;
12724
+ }
12725
+ }
12681
12726
  const transcriptionTools = [];
12682
12727
  if (process.env.TRANSCRIPTION_MODEL && config?.fileUploads && config?.fileUploads?.s3region && config?.fileUploads?.s3key && config?.fileUploads?.s3secret && config?.fileUploads?.s3Bucket) {
12683
12728
  transcriptionTools.push(transcribeTool);
@@ -12685,7 +12730,7 @@ var ExuluApp = class {
12685
12730
  const imageGenerationTools = [];
12686
12731
  const s3Configured = !!config?.fileUploads && !!config.fileUploads.s3region && !!config.fileUploads.s3key && !!config.fileUploads.s3secret && !!config.fileUploads.s3Bucket;
12687
12732
  if (isLiteLLMEnabled() && s3Configured) {
12688
- const configPath = process.env.LITELLM_CONFIG_PATH ?? resolve(getPackageRoot(), "./config.litellm.yaml");
12733
+ const configPath = process.env.LITELLM_CONFIG_PATH ?? resolve(process.cwd(), "./config.litellm.yaml");
12689
12734
  const imageModels = parseImageGenerationModels(configPath);
12690
12735
  if (imageModels.length > 0) {
12691
12736
  console.log(
@@ -14920,7 +14965,7 @@ ${WARNING_BANNER}`);
14920
14965
  };
14921
14966
  var log2 = (line) => console.log(`[EXULU-LITELLM] ${line}`);
14922
14967
  var initLiteLLMDatabase = async (packageRoot) => {
14923
- const configPath = process.env.LITELLM_CONFIG_PATH ?? resolve2(packageRoot, "./config.litellm.yaml");
14968
+ const configPath = process.env.LITELLM_CONFIG_PATH ?? resolve2(process.cwd(), "./config.litellm.yaml");
14924
14969
  const safety = checkLiteLLMDatabaseSafety(configPath);
14925
14970
  if (safety.ok && safety.reason === "no-litellm-db-mode") return;
14926
14971
  if (!safety.ok && safety.reason === "unparseable-url") {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@exulu/backend",
3
3
  "author": "Qventu Bv.",
4
- "version": "1.61.2",
4
+ "version": "1.62.0",
5
5
  "main": "./dist/index.js",
6
6
  "private": false,
7
7
  "publishConfig": {