@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/index.cjs CHANGED
@@ -1233,8 +1233,60 @@ var init_table_names = __esm({
1233
1233
  }
1234
1234
  });
1235
1235
 
1236
+ // src/exulu/litellm/env.ts
1237
+ function litellmBase() {
1238
+ const host = process.env.LITELLM_HOST ?? "127.0.0.1";
1239
+ const port = process.env.LITELLM_PORT ?? "4000";
1240
+ const masterKey = process.env.LITELLM_MASTER_KEY;
1241
+ if (!masterKey) {
1242
+ throw new LiteLLMAdminError("LITELLM_MASTER_KEY is not configured.");
1243
+ }
1244
+ return { url: `http://${host}:${port}`, masterKey };
1245
+ }
1246
+ function resolveLiteLLMTarget() {
1247
+ const rawBase = process.env.LITELLM_BASE_URL;
1248
+ if (isLiteLLMClientMode() && rawBase && rawBase.trim().length > 0) {
1249
+ const apiKey = process.env.EXULU_API_KEY;
1250
+ if (!apiKey) {
1251
+ throw new Error("EXULU_API_KEY is required when LITELLM_BASE_URL is set (remote LiteLLM client mode).");
1252
+ }
1253
+ return {
1254
+ baseUrl: rawBase.trim().replace(/\/+$/, ""),
1255
+ authHeaders: { "exulu-api-key": apiKey },
1256
+ remote: true
1257
+ };
1258
+ }
1259
+ const host = process.env.LITELLM_HOST ?? "127.0.0.1";
1260
+ const port = process.env.LITELLM_PORT ?? "4000";
1261
+ const masterKey = process.env.LITELLM_MASTER_KEY;
1262
+ return {
1263
+ baseUrl: `http://${host}:${port}`,
1264
+ authHeaders: masterKey ? { Authorization: `Bearer ${masterKey}` } : {},
1265
+ remote: false
1266
+ };
1267
+ }
1268
+ var LiteLLMAdminError, _clientMode, isLiteLLMClientMode, setLiteLLMClientMode;
1269
+ var init_env = __esm({
1270
+ "src/exulu/litellm/env.ts"() {
1271
+ "use strict";
1272
+ init_cjs_shims();
1273
+ LiteLLMAdminError = class extends Error {
1274
+ constructor(message, status) {
1275
+ super(message);
1276
+ this.status = status;
1277
+ this.name = "LiteLLMAdminError";
1278
+ }
1279
+ };
1280
+ _clientMode = false;
1281
+ isLiteLLMClientMode = () => _clientMode;
1282
+ setLiteLLMClientMode = (value) => {
1283
+ _clientMode = value;
1284
+ };
1285
+ }
1286
+ });
1287
+
1236
1288
  // src/exulu/litellm/supervisor.ts
1237
- var import_node_child_process, import_node_fs3, import_node_path2, LITELLM_UI_PATH, MAX_CRASHES, INITIAL_BACKOFF_MS, MAX_BACKOFF_MS, READY_TIMEOUT_MS, WAIT_TIMEOUT_MS, READY_POLL_INTERVAL_MS, SHUTDOWN_GRACE_MS, internal, isLiteLLMEnabled, resolveConfig, log2, pollHealth, spawnLiteLLM, supervise, _packageRoot, _clientMode, setLiteLLMPackageRoot, enableLiteLLMClientMode, startLiteLLMSupervisor, waitForLiteLLMReady, stopLiteLLM, shutdownHandlersRegistered, registerShutdownHandlers, getSupervisorState;
1289
+ var import_node_child_process, import_node_fs3, import_node_path2, LITELLM_UI_PATH, MAX_CRASHES, INITIAL_BACKOFF_MS, MAX_BACKOFF_MS, READY_TIMEOUT_MS, WAIT_TIMEOUT_MS, READY_POLL_INTERVAL_MS, SHUTDOWN_GRACE_MS, internal, isLiteLLMEnabled, resolveConfig, log2, pollHealth, spawnLiteLLM, supervise, _packageRoot, setLiteLLMPackageRoot, enableLiteLLMClientMode, startLiteLLMSupervisor, waitForLiteLLMReady, stopLiteLLM, shutdownHandlersRegistered, registerShutdownHandlers, getSupervisorState;
1238
1290
  var init_supervisor = __esm({
1239
1291
  "src/exulu/litellm/supervisor.ts"() {
1240
1292
  "use strict";
@@ -1242,6 +1294,7 @@ var init_supervisor = __esm({
1242
1294
  import_node_child_process = require("child_process");
1243
1295
  import_node_fs3 = require("fs");
1244
1296
  import_node_path2 = require("path");
1297
+ init_env();
1245
1298
  LITELLM_UI_PATH = "/litellm-admin";
1246
1299
  MAX_CRASHES = 5;
1247
1300
  INITIAL_BACKOFF_MS = 1e3;
@@ -1368,13 +1421,12 @@ var init_supervisor = __esm({
1368
1421
  internal.backoffMs = Math.min(internal.backoffMs * 2, MAX_BACKOFF_MS);
1369
1422
  }
1370
1423
  };
1371
- _clientMode = false;
1372
1424
  setLiteLLMPackageRoot = (root) => {
1373
1425
  _packageRoot = root;
1374
1426
  };
1375
1427
  enableLiteLLMClientMode = () => {
1376
1428
  if (internal.readyPromise) return;
1377
- _clientMode = true;
1429
+ setLiteLLMClientMode(true);
1378
1430
  };
1379
1431
  startLiteLLMSupervisor = async (options = {}) => {
1380
1432
  if (!isLiteLLMEnabled()) return;
@@ -1424,14 +1476,13 @@ var init_supervisor = __esm({
1424
1476
  };
1425
1477
  waitForLiteLLMReady = async () => {
1426
1478
  if (!isLiteLLMEnabled()) return;
1427
- if (_clientMode) {
1479
+ if (isLiteLLMClientMode()) {
1428
1480
  if (internal.state === "ready") return;
1429
- const host = process.env.LITELLM_HOST ?? "127.0.0.1";
1430
- const port = process.env.LITELLM_PORT ?? "4000";
1431
- const url = `http://${host}:${port}/health/liveliness`;
1481
+ const { baseUrl, authHeaders, remote } = resolveLiteLLMTarget();
1482
+ const url = remote ? `${baseUrl}/v1/models` : `${baseUrl}/health/liveliness`;
1432
1483
  let res;
1433
1484
  try {
1434
- res = await fetch(url, { method: "GET" });
1485
+ res = await fetch(url, { method: "GET", headers: remote ? authHeaders : {} });
1435
1486
  } catch (err) {
1436
1487
  throw new Error(
1437
1488
  `LiteLLM proxy not reachable at ${url} (is the Exulu server process running?): ${err.message}`
@@ -1625,31 +1676,6 @@ var init_tags = __esm({
1625
1676
  }
1626
1677
  });
1627
1678
 
1628
- // src/exulu/litellm/env.ts
1629
- function litellmBase() {
1630
- const host = process.env.LITELLM_HOST ?? "127.0.0.1";
1631
- const port = process.env.LITELLM_PORT ?? "4000";
1632
- const masterKey = process.env.LITELLM_MASTER_KEY;
1633
- if (!masterKey) {
1634
- throw new LiteLLMAdminError("LITELLM_MASTER_KEY is not configured.");
1635
- }
1636
- return { url: `http://${host}:${port}`, masterKey };
1637
- }
1638
- var LiteLLMAdminError;
1639
- var init_env = __esm({
1640
- "src/exulu/litellm/env.ts"() {
1641
- "use strict";
1642
- init_cjs_shims();
1643
- LiteLLMAdminError = class extends Error {
1644
- constructor(message, status) {
1645
- super(message);
1646
- this.status = status;
1647
- this.name = "LiteLLMAdminError";
1648
- }
1649
- };
1650
- }
1651
- });
1652
-
1653
1679
  // src/exulu/litellm/admin-client.ts
1654
1680
  async function call(path4, body) {
1655
1681
  const { url, masterKey } = litellmBase();
@@ -2285,6 +2311,7 @@ var init_resolve_model = __esm({
2285
2311
  init_supervisor();
2286
2312
  init_tags();
2287
2313
  init_budget_service();
2314
+ init_env();
2288
2315
  ResolveModelError = class extends Error {
2289
2316
  constructor(code, message) {
2290
2317
  super(message);
@@ -2300,9 +2327,7 @@ var init_resolve_model = __esm({
2300
2327
  team,
2301
2328
  routine
2302
2329
  }) => {
2303
- const host = process.env.LITELLM_HOST ?? "127.0.0.1";
2304
- const port = process.env.LITELLM_PORT ?? "4000";
2305
- const masterKey = process.env.LITELLM_MASTER_KEY;
2330
+ const { baseUrl, authHeaders } = resolveLiteLLMTarget();
2306
2331
  const tags = buildTags({
2307
2332
  user_id: user?.id,
2308
2333
  role_id: role?.id,
@@ -2317,16 +2342,13 @@ var init_resolve_model = __esm({
2317
2342
  routine_id: routine?.id,
2318
2343
  routine_name: routine?.name
2319
2344
  });
2320
- if (!masterKey) {
2321
- throw new ResolveModelError(
2322
- "LITELLM_NOT_CONFIGURED",
2323
- "LITELLM_MASTER_KEY is required when EXULU_USE_LITELLM=true"
2324
- );
2325
- }
2326
2345
  return (0, import_openai_compatible.createOpenAICompatible)({
2327
2346
  name: "litellm",
2328
- baseURL: `http://${host}:${port}/v1`,
2329
- apiKey: masterKey,
2347
+ baseURL: `${baseUrl}/v1`,
2348
+ // createOpenAICompatible requires a non-empty apiKey; real auth for remote mode is the
2349
+ // exulu-api-key header (added via `headers`). The passthrough strips/ignores Authorization.
2350
+ apiKey: process.env.LITELLM_MASTER_KEY ?? process.env.EXULU_API_KEY ?? "x",
2351
+ headers: authHeaders,
2330
2352
  fetch: createTaggedFetch(tags),
2331
2353
  // Without this flag the openai-compatible provider strips any
2332
2354
  // responseFormat.schema before sending and warns
@@ -3320,6 +3342,9 @@ var init_check_item_write_access = __esm({
3320
3342
  if (record.rights_mode === "private") {
3321
3343
  return record.created_by != null && String(record.created_by) === String(user.id);
3322
3344
  }
3345
+ if (record.created_by != null && String(record.created_by) === String(user.id)) {
3346
+ return true;
3347
+ }
3323
3348
  const validRightsModes = ["users", "roles", "teams"];
3324
3349
  if (!validRightsModes.includes(record.rights_mode)) {
3325
3350
  return false;
@@ -4958,28 +4983,33 @@ __export(catalog_exports, {
4958
4983
  fetchLiteLLMCatalog: () => fetchLiteLLMCatalog,
4959
4984
  findLiteLLMModel: () => findLiteLLMModel
4960
4985
  });
4961
- var CACHE_TTL_MS, _cache, __resetLiteLLMCatalogCacheForTesting, fetchLiteLLMCatalog, findLiteLLMModel;
4986
+ var CACHE_TTL_MS, _cache, __resetLiteLLMCatalogCacheForTesting, fetchFullCatalog, fetchLiteLLMCatalog, findLiteLLMModel;
4962
4987
  var init_catalog = __esm({
4963
4988
  "src/exulu/litellm/catalog.ts"() {
4964
4989
  "use strict";
4965
4990
  init_cjs_shims();
4991
+ init_env();
4966
4992
  CACHE_TTL_MS = 3e4;
4967
4993
  __resetLiteLLMCatalogCacheForTesting = () => {
4968
4994
  _cache = void 0;
4969
4995
  };
4970
- fetchLiteLLMCatalog = async () => {
4996
+ fetchFullCatalog = async () => {
4971
4997
  if (process.env.EXULU_USE_LITELLM !== "true") return [];
4972
4998
  if (_cache && _cache.expiresAt > Date.now()) {
4973
4999
  return _cache.items;
4974
5000
  }
4975
- const host = process.env.LITELLM_HOST ?? "127.0.0.1";
4976
- const port = process.env.LITELLM_PORT ?? "4000";
4977
- const masterKey = process.env.LITELLM_MASTER_KEY;
4978
- if (!masterKey) return [];
5001
+ let baseUrl;
5002
+ let authHeaders;
5003
+ try {
5004
+ ({ baseUrl, authHeaders } = resolveLiteLLMTarget());
5005
+ } catch {
5006
+ return [];
5007
+ }
5008
+ if (Object.keys(authHeaders).length === 0) return [];
4979
5009
  try {
4980
- const res = await fetch(`http://${host}:${port}/model/info`, {
5010
+ const res = await fetch(`${baseUrl}/model/info`, {
4981
5011
  method: "GET",
4982
- headers: { Authorization: `Bearer ${masterKey}` }
5012
+ headers: authHeaders
4983
5013
  });
4984
5014
  if (!res.ok) {
4985
5015
  console.error(
@@ -5022,15 +5052,19 @@ var init_catalog = __esm({
5022
5052
  }
5023
5053
  const uniqueItems = Array.from(map.values());
5024
5054
  _cache = { expiresAt: Date.now() + CACHE_TTL_MS, items: uniqueItems };
5025
- return uniqueItems.filter((m) => m.type !== "speech_to_text" && m.type !== "text_to_speech");
5055
+ return uniqueItems;
5026
5056
  } catch (err) {
5027
5057
  console.error("[EXULU] litellmCatalog: failed to fetch /model/info:", err);
5028
5058
  return [];
5029
5059
  }
5030
5060
  };
5061
+ fetchLiteLLMCatalog = async () => {
5062
+ const items = await fetchFullCatalog();
5063
+ return items.filter((m) => m.type !== "speech_to_text" && m.type !== "text_to_speech");
5064
+ };
5031
5065
  findLiteLLMModel = async (modelName) => {
5032
5066
  if (!modelName) return void 0;
5033
- const items = await fetchLiteLLMCatalog();
5067
+ const items = await fetchFullCatalog();
5034
5068
  return items.find((m) => m.model_name === modelName);
5035
5069
  };
5036
5070
  }
@@ -6564,15 +6598,7 @@ async function resolveReranker(input) {
6564
6598
  `LiteLLM is not ready: ${err.message}`
6565
6599
  );
6566
6600
  }
6567
- const host = process.env.LITELLM_HOST ?? "127.0.0.1";
6568
- const port = process.env.LITELLM_PORT ?? "4000";
6569
- const masterKey = process.env.LITELLM_MASTER_KEY;
6570
- if (!masterKey) {
6571
- throw new ResolveRerankerError(
6572
- "LITELLM_NOT_CONFIGURED",
6573
- "LITELLM_MASTER_KEY is required when EXULU_USE_LITELLM=true"
6574
- );
6575
- }
6601
+ const { baseUrl, authHeaders } = resolveLiteLLMTarget();
6576
6602
  const resolvedUserId = user?.id ?? userId;
6577
6603
  if (resolvedUserId) await provisionDefaultUserBudget(resolvedUserId);
6578
6604
  const role = user?.role;
@@ -6592,7 +6618,7 @@ async function resolveReranker(input) {
6592
6618
  routine_name: routine?.name,
6593
6619
  context_name: contextName
6594
6620
  });
6595
- const endpoint = `http://${host}:${port}/v1/rerank`;
6621
+ const endpoint = `${baseUrl}/v1/rerank`;
6596
6622
  const rerank2 = async (query, chunks, opts) => {
6597
6623
  try {
6598
6624
  if (chunks.length === 0) return [];
@@ -6602,7 +6628,7 @@ async function resolveReranker(input) {
6602
6628
  const res = await fetch(endpoint, {
6603
6629
  method: "POST",
6604
6630
  headers: {
6605
- Authorization: `Bearer ${masterKey}`,
6631
+ ...authHeaders,
6606
6632
  "Content-Type": "application/json"
6607
6633
  },
6608
6634
  body: JSON.stringify({
@@ -6643,7 +6669,7 @@ async function resolveReranker(input) {
6643
6669
  };
6644
6670
  return { model, rerank: rerank2 };
6645
6671
  }
6646
- var import_fs2, ResolveRerankerError;
6672
+ var ResolveRerankerError;
6647
6673
  var init_resolve_reranker = __esm({
6648
6674
  "src/exulu/resolve-reranker.ts"() {
6649
6675
  "use strict";
@@ -6651,7 +6677,7 @@ var init_resolve_reranker = __esm({
6651
6677
  init_supervisor();
6652
6678
  init_budget_service();
6653
6679
  init_tags();
6654
- import_fs2 = require("fs");
6680
+ init_env();
6655
6681
  ResolveRerankerError = class extends Error {
6656
6682
  constructor(code, message) {
6657
6683
  super(message);
@@ -10019,6 +10045,7 @@ init_cjs_shims();
10019
10045
  init_supervisor();
10020
10046
  init_budget_service();
10021
10047
  init_tags();
10048
+ init_env();
10022
10049
  var ResolveEmbedderError = class extends Error {
10023
10050
  constructor(code, message) {
10024
10051
  super(message);
@@ -10042,15 +10069,7 @@ async function resolveEmbedder(input) {
10042
10069
  `LiteLLM is not ready: ${err.message}`
10043
10070
  );
10044
10071
  }
10045
- const host = process.env.LITELLM_HOST ?? "127.0.0.1";
10046
- const port = process.env.LITELLM_PORT ?? "4000";
10047
- const masterKey = process.env.LITELLM_MASTER_KEY;
10048
- if (!masterKey) {
10049
- throw new ResolveEmbedderError(
10050
- "LITELLM_NOT_CONFIGURED",
10051
- "LITELLM_MASTER_KEY is required when EXULU_USE_LITELLM=true"
10052
- );
10053
- }
10072
+ const { baseUrl, authHeaders } = resolveLiteLLMTarget();
10054
10073
  const resolvedUserId = user?.id ?? userId;
10055
10074
  if (resolvedUserId) await provisionDefaultUserBudget(resolvedUserId);
10056
10075
  const { dimensionality, maxChunkSize, maxBatchSize } = getEmbeddingModelInfo(model);
@@ -10071,12 +10090,12 @@ async function resolveEmbedder(input) {
10071
10090
  routine_name: routine?.name,
10072
10091
  context_name: contextName
10073
10092
  });
10074
- const endpoint = `http://${host}:${port}/v1/embeddings`;
10093
+ const endpoint = `${baseUrl}/v1/embeddings`;
10075
10094
  const embedBatch = async (batch) => {
10076
10095
  const res = await fetch(endpoint, {
10077
10096
  method: "POST",
10078
10097
  headers: {
10079
- Authorization: `Bearer ${masterKey}`,
10098
+ ...authHeaders,
10080
10099
  "Content-Type": "application/json"
10081
10100
  },
10082
10101
  body: JSON.stringify({
@@ -15120,20 +15139,25 @@ async function encryptString(string) {
15120
15139
  const hash = await import_bcryptjs2.default.hash(string, SALT_ROUNDS);
15121
15140
  return hash;
15122
15141
  }
15123
- var generateApiKey = async (name, email) => {
15142
+ var generateApiKey = async (name, email, options) => {
15124
15143
  const { db: db2 } = await postgresClient();
15125
15144
  email = String(email).trim().toLowerCase();
15145
+ const superAdmin = options?.superAdmin ?? true;
15146
+ const roleName = options?.roleName ?? "admin";
15147
+ const rolePermissions = options?.rolePermissions ?? {
15148
+ agents: "write",
15149
+ workflows: "write",
15150
+ variables: "write",
15151
+ users: "write"
15152
+ };
15126
15153
  console.log("[EXULU] Inserting default user and admin role.");
15127
- const existingRole = await db2.from("roles").where({ name: "admin" }).first();
15154
+ const existingRole = await db2.from("roles").where({ name: roleName }).first();
15128
15155
  let roleId;
15129
15156
  if (!existingRole) {
15130
- console.log("[EXULU] Creating default admin role.");
15157
+ console.log(`[EXULU] Creating default ${roleName} role.`);
15131
15158
  const role = await db2.from("roles").insert({
15132
- name: "admin",
15133
- agents: "write",
15134
- workflows: "write",
15135
- variables: "write",
15136
- users: "write"
15159
+ name: roleName,
15160
+ ...rolePermissions
15137
15161
  }).returning("id");
15138
15162
  roleId = role[0].id;
15139
15163
  } else {
@@ -15149,7 +15173,7 @@ var generateApiKey = async (name, email) => {
15149
15173
  await db2.from("users").insert({
15150
15174
  name,
15151
15175
  email,
15152
- super_admin: true,
15176
+ super_admin: superAdmin,
15153
15177
  createdAt: /* @__PURE__ */ new Date(),
15154
15178
  updatedAt: /* @__PURE__ */ new Date(),
15155
15179
  type: "api",
@@ -15735,6 +15759,9 @@ function createMutations(table, contexts, tools, config) {
15735
15759
  }
15736
15760
  throw new Error("Only the creator can edit this private record");
15737
15761
  }
15762
+ if (record.created_by != null && String(record.created_by) === String(user.id)) {
15763
+ return true;
15764
+ }
15738
15765
  if (record.rights_mode === "users") {
15739
15766
  const rbacRecord = await db2.from("rbac").where({
15740
15767
  entity: table.name.singular,
@@ -19681,7 +19708,7 @@ var mapRoutineRunRow = (row, routineById) => {
19681
19708
 
19682
19709
  // src/graphql/schemas/index.ts
19683
19710
  init_entitlements();
19684
- var import_fs3 = require("fs");
19711
+ var import_fs2 = require("fs");
19685
19712
 
19686
19713
  // src/exulu/transcription/service.ts
19687
19714
  init_cjs_shims();
@@ -23636,7 +23663,7 @@ var import_utils5 = require("@apollo/utils.keyvaluecache");
23636
23663
  var import_body_parser = __toESM(require("body-parser"), 1);
23637
23664
  var import_crypto_js7 = require("crypto-js");
23638
23665
  var import_openai = require("openai");
23639
- var import_fs4 = __toESM(require("fs"), 1);
23666
+ var import_fs3 = __toESM(require("fs"), 1);
23640
23667
  var import_node_crypto15 = require("crypto");
23641
23668
  var import_api2 = require("@opentelemetry/api");
23642
23669
  var import_jszip3 = __toESM(require("jszip"), 1);
@@ -23855,6 +23882,19 @@ ${summary}` }],
23855
23882
 
23856
23883
  // src/exulu/transcribe.ts
23857
23884
  init_cjs_shims();
23885
+ init_env();
23886
+ init_catalog();
23887
+ var TRANSCRIBE_SYSTEM_PROMPT = "You are a speech-to-text transcription engine. Detect the language actually spoken and transcribe it word-for-word in that same language. Never translate. Output only the transcript text \u2014 no quotes, labels, or commentary. If there is no intelligible speech, output nothing.";
23888
+ function isGeminiChatTranscriptionModel(entry) {
23889
+ const upstream = entry?.upstream_model ?? "";
23890
+ return /^vertex_ai\//i.test(upstream) && /gemini/i.test(upstream);
23891
+ }
23892
+ function cleanTranscript(raw) {
23893
+ let text = (raw ?? "").trim();
23894
+ const wrapped = text.match(/^(["'`])([\s\S]*)\1$/);
23895
+ if (wrapped) text = (wrapped[2] ?? "").trim();
23896
+ return text;
23897
+ }
23858
23898
  var TranscriptionError = class extends Error {
23859
23899
  constructor(upstreamStatus, message) {
23860
23900
  super(message);
@@ -23863,12 +23903,16 @@ var TranscriptionError = class extends Error {
23863
23903
  }
23864
23904
  };
23865
23905
  async function transcribeAudio(args) {
23866
- const host = process.env.LITELLM_HOST ?? "127.0.0.1";
23867
- const port = process.env.LITELLM_PORT ?? "4000";
23868
- const masterKey = process.env.LITELLM_MASTER_KEY;
23906
+ const target = resolveLiteLLMTarget();
23869
23907
  const model = process.env.TRANSCRIPTION_MODEL;
23870
- if (!masterKey) throw new Error("LITELLM_MASTER_KEY is not set");
23871
23908
  if (!model) throw new Error("TRANSCRIPTION_MODEL is not set");
23909
+ const entry = await findLiteLLMModel(model).catch(() => void 0);
23910
+ if (isGeminiChatTranscriptionModel(entry)) {
23911
+ return transcribeViaChat(args, target, model);
23912
+ }
23913
+ return transcribeViaAudioEndpoint(args, target, model);
23914
+ }
23915
+ async function transcribeViaAudioEndpoint(args, target, model) {
23872
23916
  const form = new FormData();
23873
23917
  form.append(
23874
23918
  "file",
@@ -23877,9 +23921,9 @@ async function transcribeAudio(args) {
23877
23921
  );
23878
23922
  form.append("model", model);
23879
23923
  if (args.language) form.append("language", args.language);
23880
- const res = await fetch(`http://${host}:${port}/v1/audio/transcriptions`, {
23924
+ const res = await fetch(`${target.baseUrl}/v1/audio/transcriptions`, {
23881
23925
  method: "POST",
23882
- headers: { Authorization: `Bearer ${masterKey}` },
23926
+ headers: { ...target.authHeaders },
23883
23927
  body: form
23884
23928
  });
23885
23929
  if (!res.ok) {
@@ -23892,9 +23936,46 @@ async function transcribeAudio(args) {
23892
23936
  const json = await res.json();
23893
23937
  return { text: typeof json.text === "string" ? json.text : "" };
23894
23938
  }
23939
+ async function transcribeViaChat(args, target, model) {
23940
+ const subtype = args.file.mimetype.replace(/^audio\//, "").split(";")[0];
23941
+ const format = (subtype && subtype.length > 0 ? subtype : "wav").toLowerCase();
23942
+ const body = {
23943
+ model,
23944
+ temperature: 0,
23945
+ reasoning_effort: "disable",
23946
+ messages: [
23947
+ { role: "system", content: TRANSCRIBE_SYSTEM_PROMPT },
23948
+ {
23949
+ role: "user",
23950
+ content: [
23951
+ { type: "text", text: "Transcribe this audio." },
23952
+ {
23953
+ type: "input_audio",
23954
+ input_audio: { data: args.file.buffer.toString("base64"), format }
23955
+ }
23956
+ ]
23957
+ }
23958
+ ]
23959
+ };
23960
+ const res = await fetch(`${target.baseUrl}/v1/chat/completions`, {
23961
+ method: "POST",
23962
+ headers: { ...target.authHeaders, "Content-Type": "application/json" },
23963
+ body: JSON.stringify(body)
23964
+ });
23965
+ if (!res.ok) {
23966
+ const errBody = await res.text().catch(() => "");
23967
+ throw new TranscriptionError(
23968
+ res.status,
23969
+ `LiteLLM transcription failed (status ${res.status}): ${errBody}`.trim()
23970
+ );
23971
+ }
23972
+ const json = await res.json();
23973
+ return { text: cleanTranscript(json.choices?.[0]?.message?.content) };
23974
+ }
23895
23975
 
23896
23976
  // src/exulu/speech.ts
23897
23977
  init_cjs_shims();
23978
+ init_env();
23898
23979
  var SpeechError = class extends Error {
23899
23980
  constructor(upstreamStatus, message) {
23900
23981
  super(message);
@@ -23903,12 +23984,9 @@ var SpeechError = class extends Error {
23903
23984
  }
23904
23985
  };
23905
23986
  async function synthesizeSpeech(args) {
23906
- const host = process.env.LITELLM_HOST ?? "127.0.0.1";
23907
- const port = process.env.LITELLM_PORT ?? "4000";
23908
- const masterKey = process.env.LITELLM_MASTER_KEY;
23987
+ const { baseUrl, authHeaders } = resolveLiteLLMTarget();
23909
23988
  const model = process.env.TTS_MODEL;
23910
23989
  const voice = process.env.TTS_VOICE;
23911
- if (!masterKey) throw new Error("LITELLM_MASTER_KEY is not set");
23912
23990
  if (!model) throw new Error("TTS_MODEL is not set");
23913
23991
  if (!voice) throw new Error("TTS_VOICE is not set");
23914
23992
  const body = {
@@ -23917,10 +23995,10 @@ async function synthesizeSpeech(args) {
23917
23995
  voice,
23918
23996
  response_format: "mp3"
23919
23997
  };
23920
- const res = await fetch(`http://${host}:${port}/v1/audio/speech`, {
23998
+ const res = await fetch(`${baseUrl}/v1/audio/speech`, {
23921
23999
  method: "POST",
23922
24000
  headers: {
23923
- Authorization: `Bearer ${masterKey}`,
24001
+ ...authHeaders,
23924
24002
  "Content-Type": "application/json"
23925
24003
  },
23926
24004
  body: JSON.stringify(body)
@@ -23938,6 +24016,7 @@ async function synthesizeSpeech(args) {
23938
24016
 
23939
24017
  // src/exulu/image-generation.ts
23940
24018
  init_cjs_shims();
24019
+ init_env();
23941
24020
  var ImageGenerationError = class extends Error {
23942
24021
  constructor(upstreamStatus, message) {
23943
24022
  super(message);
@@ -23945,13 +24024,6 @@ var ImageGenerationError = class extends Error {
23945
24024
  this.name = "ImageGenerationError";
23946
24025
  }
23947
24026
  };
23948
- var resolveProxyConfig = () => {
23949
- const host = process.env.LITELLM_HOST ?? "127.0.0.1";
23950
- const port = process.env.LITELLM_PORT ?? "4000";
23951
- const masterKey = process.env.LITELLM_MASTER_KEY;
23952
- if (!masterKey) throw new Error("LITELLM_MASTER_KEY is not set");
23953
- return { host, port, masterKey };
23954
- };
23955
24027
  var normalizeDataEntries = async (data) => {
23956
24028
  const out = [];
23957
24029
  for (const entry of data) {
@@ -23988,7 +24060,7 @@ var normalizeDataEntries = async (data) => {
23988
24060
  async function generateImage(args) {
23989
24061
  if (!args.model) throw new Error("model is required");
23990
24062
  if (!args.prompt) throw new Error("prompt is required");
23991
- const cfg = resolveProxyConfig();
24063
+ const { baseUrl, authHeaders } = resolveLiteLLMTarget();
23992
24064
  const body = {
23993
24065
  model: args.model,
23994
24066
  prompt: args.prompt
@@ -23996,10 +24068,10 @@ async function generateImage(args) {
23996
24068
  if (args.size) body.size = args.size;
23997
24069
  if (args.quality) body.quality = args.quality;
23998
24070
  if (args.n) body.n = args.n;
23999
- const res = await fetch(`http://${cfg.host}:${cfg.port}/v1/images/generations`, {
24071
+ const res = await fetch(`${baseUrl}/v1/images/generations`, {
24000
24072
  method: "POST",
24001
24073
  headers: {
24002
- Authorization: `Bearer ${cfg.masterKey}`,
24074
+ ...authHeaders,
24003
24075
  "Content-Type": "application/json"
24004
24076
  },
24005
24077
  body: JSON.stringify(body),
@@ -24027,7 +24099,7 @@ async function editImage(args) {
24027
24099
  if (!args.references || args.references.length === 0) {
24028
24100
  throw new Error("at least one reference image is required");
24029
24101
  }
24030
- const cfg = resolveProxyConfig();
24102
+ const { baseUrl, authHeaders } = resolveLiteLLMTarget();
24031
24103
  const form = new FormData();
24032
24104
  form.append("model", args.model);
24033
24105
  form.append("prompt", args.prompt);
@@ -24049,9 +24121,9 @@ async function editImage(args) {
24049
24121
  args.mask.filename
24050
24122
  );
24051
24123
  }
24052
- const res = await fetch(`http://${cfg.host}:${cfg.port}/v1/images/edits`, {
24124
+ const res = await fetch(`${baseUrl}/v1/images/edits`, {
24053
24125
  method: "POST",
24054
- headers: { Authorization: `Bearer ${cfg.masterKey}` },
24126
+ headers: { ...authHeaders },
24055
24127
  body: form,
24056
24128
  signal: args.signal
24057
24129
  });
@@ -24201,6 +24273,12 @@ var import_node_path9 = require("path");
24201
24273
  init_tags();
24202
24274
  init_admin_client();
24203
24275
  init_env();
24276
+
24277
+ // src/exulu/litellm/passthrough-allowlist.ts
24278
+ init_cjs_shims();
24279
+ var isLiteLLMPassthroughPathAllowed = (path4) => path4.startsWith("/v1/") || path4 === "/model/info";
24280
+
24281
+ // src/exulu/routes.ts
24204
24282
  init_activity_client();
24205
24283
  init_budget_service();
24206
24284
 
@@ -24901,7 +24979,7 @@ var REQUEST_SIZE_LIMIT = "50mb";
24901
24979
  var getExuluVersionNumber = async () => {
24902
24980
  try {
24903
24981
  const path4 = process.cwd();
24904
- const packageJson = import_fs4.default.readFileSync(path4 + "/package.json", "utf8");
24982
+ const packageJson = import_fs3.default.readFileSync(path4 + "/package.json", "utf8");
24905
24983
  const packageData = JSON.parse(packageJson);
24906
24984
  const exuluVersion = packageData.dependencies["@exulu/backend"];
24907
24985
  console.log(`[EXULU] Installed exulu-backend version: ${exuluVersion}`);
@@ -26453,7 +26531,7 @@ ${style.markdown}` : params.prompt;
26453
26531
  return;
26454
26532
  }
26455
26533
  const user = authenticationResult.user;
26456
- if (!req.path.startsWith("/v1/")) {
26534
+ if (!isLiteLLMPassthroughPathAllowed(req.path)) {
26457
26535
  res.status(403).json({
26458
26536
  detail: `Path ${req.path} is not exposed through the Exulu LiteLLM proxy.`
26459
26537
  });
@@ -29883,7 +29961,7 @@ init_cjs_shims();
29883
29961
  var import_child_process = require("child_process");
29884
29962
  var import_util = require("util");
29885
29963
  var import_path3 = require("path");
29886
- var import_fs5 = require("fs");
29964
+ var import_fs4 = require("fs");
29887
29965
  var import_url = require("url");
29888
29966
  var execAsync4 = (0, import_util.promisify)(import_child_process.exec);
29889
29967
  function getPackageRoot() {
@@ -29893,9 +29971,9 @@ function getPackageRoot() {
29893
29971
  const maxAttempts = 10;
29894
29972
  while (attempts < maxAttempts) {
29895
29973
  const packageJsonPath = (0, import_path3.join)(currentDir, "package.json");
29896
- if ((0, import_fs5.existsSync)(packageJsonPath)) {
29974
+ if ((0, import_fs4.existsSync)(packageJsonPath)) {
29897
29975
  try {
29898
- const packageJson = JSON.parse((0, import_fs5.readFileSync)(packageJsonPath, "utf-8"));
29976
+ const packageJson = JSON.parse((0, import_fs4.readFileSync)(packageJsonPath, "utf-8"));
29899
29977
  if (packageJson.name === "@exulu/backend") {
29900
29978
  return currentDir;
29901
29979
  }
@@ -29922,7 +30000,7 @@ function isPythonEnvironmentSetup(packageRoot) {
29922
30000
  const root = packageRoot ?? getPackageRoot();
29923
30001
  const venvPath = getVenvPath(root);
29924
30002
  const pythonPath = (0, import_path3.join)(venvPath, "bin", "python");
29925
- return (0, import_fs5.existsSync)(venvPath) && (0, import_fs5.existsSync)(pythonPath);
30003
+ return (0, import_fs4.existsSync)(venvPath) && (0, import_fs4.existsSync)(pythonPath);
29926
30004
  }
29927
30005
  async function setupPythonEnvironment(options = {}) {
29928
30006
  const {
@@ -29943,7 +30021,7 @@ async function setupPythonEnvironment(options = {}) {
29943
30021
  };
29944
30022
  }
29945
30023
  const setupScriptPath = getSetupScriptPath(packageRoot);
29946
- if (!(0, import_fs5.existsSync)(setupScriptPath)) {
30024
+ if (!(0, import_fs4.existsSync)(setupScriptPath)) {
29947
30025
  return {
29948
30026
  success: false,
29949
30027
  message: `Setup script not found at: ${setupScriptPath}`,
@@ -30025,13 +30103,13 @@ async function validatePythonEnvironment(packageRoot, checkPackages = true) {
30025
30103
  const root = packageRoot ?? getPackageRoot();
30026
30104
  const venvPath = getVenvPath(root);
30027
30105
  const pythonPath = (0, import_path3.join)(venvPath, "bin", "python");
30028
- if (!(0, import_fs5.existsSync)(venvPath)) {
30106
+ if (!(0, import_fs4.existsSync)(venvPath)) {
30029
30107
  return {
30030
30108
  valid: false,
30031
30109
  message: getPythonSetupInstructions()
30032
30110
  };
30033
30111
  }
30034
- if (!(0, import_fs5.existsSync)(pythonPath)) {
30112
+ if (!(0, import_fs4.existsSync)(pythonPath)) {
30035
30113
  return {
30036
30114
  valid: false,
30037
30115
  message: "Python virtual environment is corrupted. Please run:\n await setupPythonEnvironment({ force: true })"
@@ -32581,7 +32659,7 @@ var MarkdownChunker = class {
32581
32659
 
32582
32660
  // ee/python/documents/processing/doc_processor.ts
32583
32661
  init_cjs_shims();
32584
- var fs5 = __toESM(require("fs"), 1);
32662
+ var fs4 = __toESM(require("fs"), 1);
32585
32663
  var path3 = __toESM(require("path"), 1);
32586
32664
  var import_ai14 = require("ai");
32587
32665
  var import_zod27 = require("zod");
@@ -32599,7 +32677,7 @@ init_cjs_shims();
32599
32677
  var import_child_process2 = require("child_process");
32600
32678
  var import_util3 = require("util");
32601
32679
  var import_path4 = require("path");
32602
- var import_fs6 = require("fs");
32680
+ var import_fs5 = require("fs");
32603
32681
  var import_url2 = require("url");
32604
32682
  var execAsync5 = (0, import_util3.promisify)(import_child_process2.exec);
32605
32683
  function getPackageRoot2() {
@@ -32609,9 +32687,9 @@ function getPackageRoot2() {
32609
32687
  const maxAttempts = 10;
32610
32688
  while (attempts < maxAttempts) {
32611
32689
  const packageJsonPath = (0, import_path4.join)(currentDir, "package.json");
32612
- if ((0, import_fs6.existsSync)(packageJsonPath)) {
32690
+ if ((0, import_fs5.existsSync)(packageJsonPath)) {
32613
32691
  try {
32614
- const packageJson = JSON.parse((0, import_fs6.readFileSync)(packageJsonPath, "utf-8"));
32692
+ const packageJson = JSON.parse((0, import_fs5.readFileSync)(packageJsonPath, "utf-8"));
32615
32693
  if (packageJson.name === "@exulu/backend") {
32616
32694
  return currentDir;
32617
32695
  }
@@ -32673,7 +32751,7 @@ async function executePythonScript(config) {
32673
32751
  await validatePythonEnvironmentForExecution(packageRoot);
32674
32752
  }
32675
32753
  const resolvedScriptPath = (0, import_path4.resolve)(packageRoot, scriptPath);
32676
- if (!(0, import_fs6.existsSync)(resolvedScriptPath)) {
32754
+ if (!(0, import_fs5.existsSync)(resolvedScriptPath)) {
32677
32755
  throw new PythonExecutionError(
32678
32756
  `Python script not found: ${resolvedScriptPath}`,
32679
32757
  "",
@@ -32743,6 +32821,7 @@ init_cjs_shims();
32743
32821
  init_supervisor();
32744
32822
  init_budget_service();
32745
32823
  init_tags();
32824
+ init_env();
32746
32825
  var ResolveOcrError = class extends Error {
32747
32826
  constructor(code, message) {
32748
32827
  super(message);
@@ -32766,15 +32845,7 @@ async function resolveOcr(input) {
32766
32845
  `LiteLLM is not ready: ${err.message}`
32767
32846
  );
32768
32847
  }
32769
- const host = process.env.LITELLM_HOST ?? "127.0.0.1";
32770
- const port = process.env.LITELLM_PORT ?? "4000";
32771
- const masterKey = process.env.LITELLM_MASTER_KEY;
32772
- if (!masterKey) {
32773
- throw new ResolveOcrError(
32774
- "LITELLM_NOT_CONFIGURED",
32775
- "LITELLM_MASTER_KEY is required when EXULU_USE_LITELLM=true"
32776
- );
32777
- }
32848
+ const { baseUrl, authHeaders } = resolveLiteLLMTarget();
32778
32849
  const resolvedUserId = user?.id ?? userId;
32779
32850
  if (resolvedUserId) await provisionDefaultUserBudget(resolvedUserId);
32780
32851
  const role = user?.role;
@@ -32794,12 +32865,12 @@ async function resolveOcr(input) {
32794
32865
  routine_name: routine?.name,
32795
32866
  context_name: contextName
32796
32867
  });
32797
- const endpoint = `http://${host}:${port}/v1/ocr`;
32868
+ const endpoint = `${baseUrl}/v1/ocr`;
32798
32869
  const ocr = async (document2, opts) => {
32799
32870
  const res = await fetch(endpoint, {
32800
32871
  method: "POST",
32801
32872
  headers: {
32802
- Authorization: `Bearer ${masterKey}`,
32873
+ ...authHeaders,
32803
32874
  "Content-Type": "application/json"
32804
32875
  },
32805
32876
  body: JSON.stringify({
@@ -32877,9 +32948,9 @@ async function resolveVlmModel(config) {
32877
32948
  }
32878
32949
  async function processImage(buffer, paths, config, verbose = false) {
32879
32950
  try {
32880
- await fs5.promises.mkdir(paths.images, { recursive: true });
32951
+ await fs4.promises.mkdir(paths.images, { recursive: true });
32881
32952
  const imagePath = path3.join(paths.images, "1.png");
32882
- await fs5.promises.writeFile(imagePath, buffer);
32953
+ await fs4.promises.writeFile(imagePath, buffer);
32883
32954
  console.log(`[EXULU] Image saved to: ${imagePath}`);
32884
32955
  let json = [{
32885
32956
  page: 1,
@@ -32897,7 +32968,7 @@ async function processImage(buffer, paths, config, verbose = false) {
32897
32968
  verbose,
32898
32969
  config.vlm.concurrency
32899
32970
  );
32900
- await fs5.promises.writeFile(
32971
+ await fs4.promises.writeFile(
32901
32972
  paths.json,
32902
32973
  JSON.stringify(json, null, 2),
32903
32974
  "utf-8"
@@ -32908,14 +32979,14 @@ async function processImage(buffer, paths, config, verbose = false) {
32908
32979
  } else {
32909
32980
  console.log("[EXULU] No VLM configured, image saved without content extraction");
32910
32981
  console.log("[EXULU] Note: Enable VLM in config to extract text/content from images");
32911
- await fs5.promises.writeFile(
32982
+ await fs4.promises.writeFile(
32912
32983
  paths.json,
32913
32984
  JSON.stringify(json, null, 2),
32914
32985
  "utf-8"
32915
32986
  );
32916
32987
  }
32917
32988
  const markdown = json.map((p) => p.vlm_corrected_text ?? p.content).join("\n\n\n<!-- END_OF_PAGE -->\n\n\n");
32918
- await fs5.promises.writeFile(paths.markdown, markdown, "utf-8");
32989
+ await fs4.promises.writeFile(paths.markdown, markdown, "utf-8");
32919
32990
  return {
32920
32991
  markdown,
32921
32992
  json
@@ -32968,7 +33039,7 @@ function reconstructHeadings(correctedText, headingsHierarchy) {
32968
33039
  return result;
32969
33040
  }
32970
33041
  async function validatePageWithVLM(page, imagePath, model) {
32971
- const imageBuffer = await fs5.promises.readFile(imagePath);
33042
+ const imageBuffer = await fs4.promises.readFile(imagePath);
32972
33043
  const imageBase64 = imageBuffer.toString("base64");
32973
33044
  const mimeType = "image/png";
32974
33045
  const prompt = `You are a document validation assistant. Your task is to analyze a page image and correct the output of an OCR/parsing pipeline. The content may include tables, technical diagrams, schematics, and structured text.
@@ -33303,7 +33374,7 @@ ${setupResult.output || ""}`);
33303
33374
  if (!result.success) {
33304
33375
  throw new Error(`Document processing failed: ${result.stderr}`);
33305
33376
  }
33306
- const jsonContent = await fs5.promises.readFile(paths.json, "utf-8");
33377
+ const jsonContent = await fs4.promises.readFile(paths.json, "utf-8");
33307
33378
  json = JSON.parse(jsonContent);
33308
33379
  } else if (config?.processor.name === "officeparser") {
33309
33380
  const text = await (0, import_officeparser3.parseOfficeAsync)(buffer, {
@@ -33336,7 +33407,7 @@ ${setupResult.output || ""}`);
33336
33407
  await new Promise((resolve8) => setImmediate(resolve8));
33337
33408
  await new Promise((resolve8) => setTimeout(resolve8, Math.floor(Math.random() * 1e3) + 200));
33338
33409
  console.log(`[EXULU] OCR chunk ${i + 1}/${pdfChunks.length}: pages ${chunk.start_page}\u2013${chunk.end_page - 1}`);
33339
- const chunkBuffer = await fs5.promises.readFile(chunk.path);
33410
+ const chunkBuffer = await fs4.promises.readFile(chunk.path);
33340
33411
  const chunkBase64 = chunkBuffer.toString("base64");
33341
33412
  const chunkResponse = await withRetry(async () => {
33342
33413
  return await resolved.ocr({
@@ -33353,9 +33424,9 @@ ${setupResult.output || ""}`);
33353
33424
  );
33354
33425
  const parser = new import_liteparse.LiteParse();
33355
33426
  const screenshots = await parser.screenshot(paths.source, void 0);
33356
- await fs5.promises.mkdir(paths.images, { recursive: true });
33427
+ await fs4.promises.mkdir(paths.images, { recursive: true });
33357
33428
  for (const screenshot of screenshots) {
33358
- await fs5.promises.writeFile(
33429
+ await fs4.promises.writeFile(
33359
33430
  path3.join(
33360
33431
  paths.images,
33361
33432
  `${screenshot.pageNum}.png`
@@ -33370,15 +33441,15 @@ ${setupResult.output || ""}`);
33370
33441
  image: screenshots.find((s) => s.pageNum === page.index + 1)?.imagePath,
33371
33442
  headings: []
33372
33443
  }));
33373
- fs5.writeFileSync(paths.json, JSON.stringify(json, null, 2));
33444
+ fs4.writeFileSync(paths.json, JSON.stringify(json, null, 2));
33374
33445
  } else if (config?.processor.name === "liteparse") {
33375
33446
  const parser = new import_liteparse.LiteParse();
33376
33447
  const result = await parser.parse(paths.source);
33377
33448
  const screenshots = await parser.screenshot(paths.source, void 0);
33378
33449
  console.log(`[EXULU] Liteparse screenshots: ${JSON.stringify(screenshots)}`);
33379
- await fs5.promises.mkdir(paths.images, { recursive: true });
33450
+ await fs4.promises.mkdir(paths.images, { recursive: true });
33380
33451
  for (const screenshot of screenshots) {
33381
- await fs5.promises.writeFile(path3.join(paths.images, `${screenshot.pageNum}.png`), screenshot.imageBuffer);
33452
+ await fs4.promises.writeFile(path3.join(paths.images, `${screenshot.pageNum}.png`), screenshot.imageBuffer);
33382
33453
  screenshot.imagePath = path3.join(paths.images, `${screenshot.pageNum}.png`);
33383
33454
  }
33384
33455
  json = result.pages.map((page) => ({
@@ -33386,7 +33457,7 @@ ${setupResult.output || ""}`);
33386
33457
  content: page.text,
33387
33458
  image: screenshots.find((s) => s.pageNum === page.pageNum)?.imagePath
33388
33459
  }));
33389
- fs5.writeFileSync(paths.json, JSON.stringify(json, null, 2));
33460
+ fs4.writeFileSync(paths.json, JSON.stringify(json, null, 2));
33390
33461
  }
33391
33462
  console.log(`[EXULU]
33392
33463
  \u2713 Document processing completed successfully`);
@@ -33418,13 +33489,13 @@ ${setupResult.output || ""}`);
33418
33489
  console.log(`[EXULU] Corrected: ${page.vlm_corrected_text.substring(0, 150)}...`);
33419
33490
  });
33420
33491
  }
33421
- await fs5.promises.writeFile(
33492
+ await fs4.promises.writeFile(
33422
33493
  paths.json,
33423
33494
  JSON.stringify(json, null, 2),
33424
33495
  "utf-8"
33425
33496
  );
33426
33497
  }
33427
- const markdownStream = fs5.createWriteStream(paths.markdown, { encoding: "utf-8" });
33498
+ const markdownStream = fs4.createWriteStream(paths.markdown, { encoding: "utf-8" });
33428
33499
  for (let i = 0; i < json.length; i++) {
33429
33500
  const p = json[i];
33430
33501
  if (!p) continue;
@@ -33440,7 +33511,7 @@ ${setupResult.output || ""}`);
33440
33511
  });
33441
33512
  console.log(`[EXULU] Validated output saved to: ${paths.json}`);
33442
33513
  console.log(`[EXULU] Validated markdown saved to: ${paths.markdown}`);
33443
- const markdown = await fs5.promises.readFile(paths.markdown, "utf-8");
33514
+ const markdown = await fs4.promises.readFile(paths.markdown, "utf-8");
33444
33515
  const processedJson = json.map((e) => {
33445
33516
  const finalContent = e.vlm_corrected_text ?? e.content;
33446
33517
  return {
@@ -33471,7 +33542,7 @@ var loadFile = async (file, name, tempDir) => {
33471
33542
  let buffer;
33472
33543
  if (Buffer.isBuffer(file)) {
33473
33544
  filePath = path3.join(tempDir, `${UUID}.${fileType}`);
33474
- await fs5.promises.writeFile(filePath, file);
33545
+ await fs4.promises.writeFile(filePath, file);
33475
33546
  buffer = file;
33476
33547
  } else {
33477
33548
  filePath = filePath.trim();
@@ -33479,11 +33550,11 @@ var loadFile = async (file, name, tempDir) => {
33479
33550
  const response = await fetch(filePath);
33480
33551
  const array = await response.arrayBuffer();
33481
33552
  const tempFilePath = path3.join(tempDir, `${UUID}.${fileType}`);
33482
- await fs5.promises.writeFile(tempFilePath, Buffer.from(array));
33553
+ await fs4.promises.writeFile(tempFilePath, Buffer.from(array));
33483
33554
  buffer = Buffer.from(array);
33484
33555
  filePath = tempFilePath;
33485
33556
  } else {
33486
- buffer = await fs5.promises.readFile(file);
33557
+ buffer = await fs4.promises.readFile(file);
33487
33558
  }
33488
33559
  }
33489
33560
  return { filePath, fileType, buffer };
@@ -33501,9 +33572,9 @@ async function documentProcessor({
33501
33572
  const tempDir = path3.join(process.cwd(), "temp", uuid);
33502
33573
  const localFilesAndFoldersToDelete = [tempDir];
33503
33574
  console.log(`[EXULU] Temporary directory for processing document ${name}: ${tempDir}`);
33504
- await fs5.promises.mkdir(tempDir, { recursive: true });
33575
+ await fs4.promises.mkdir(tempDir, { recursive: true });
33505
33576
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
33506
- await fs5.promises.writeFile(path3.join(tempDir, "created_at.txt"), timestamp);
33577
+ await fs4.promises.writeFile(path3.join(tempDir, "created_at.txt"), timestamp);
33507
33578
  try {
33508
33579
  const {
33509
33580
  filePath,
@@ -33544,7 +33615,7 @@ async function documentProcessor({
33544
33615
  if (config?.debugging?.deleteTempFiles !== false) {
33545
33616
  for (const file2 of localFilesAndFoldersToDelete) {
33546
33617
  try {
33547
- await fs5.promises.rm(file2, { recursive: true });
33618
+ await fs4.promises.rm(file2, { recursive: true });
33548
33619
  console.log(`[EXULU] Deleted file or folder: ${file2}`);
33549
33620
  } catch (error) {
33550
33621
  console.error(`[EXULU] Error deleting file or folder: ${file2}`, error);