@exulu/backend 1.43.0 → 1.44.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/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
- # [1.43.0](https://github.com/Qventu/exulu-backend/compare/v1.42.2...v1.43.0) (2025-12-24)
1
+ # [1.44.0](https://github.com/Qventu/exulu-backend/compare/v1.43.0...v1.44.0) (2025-12-28)
2
2
 
3
3
 
4
4
  ### Features
5
5
 
6
- * add citation system, global file uploads, record copying, and refine access control ([a71e77f](https://github.com/Qventu/exulu-backend/commit/a71e77f69585bf19d182f971a3585376b1559c93))
6
+ * increase agent step limits, enhance JSON field handling, and improve eval workers ([fb5e002](https://github.com/Qventu/exulu-backend/commit/fb5e002c7a31528e3dc64df805815bdb78b5ecde))
package/dist/index.cjs CHANGED
@@ -2911,13 +2911,16 @@ var postprocessUpdate = async ({
2911
2911
  if (!context.embedder) {
2912
2912
  return result;
2913
2913
  }
2914
- const { db: db3 } = await postgresClient();
2915
- console.log("[EXULU] Deleting chunks for item", result.id);
2916
- await db3.from(getChunksTableName(context.id)).where({ source: result.id }).delete();
2917
- console.log("[EXULU] Deleted chunks for item", result.id);
2918
- console.log("[EXULU] Embedder", context.embedder);
2919
- console.log("[EXULU] Configuration", context.configuration);
2920
2914
  if (context.embedder && (context.configuration.calculateVectors === "onUpdate" || context.configuration.calculateVectors === "always")) {
2915
+ const { db: db3 } = await postgresClient();
2916
+ console.log("[EXULU] Deleting chunks for item", result.id);
2917
+ const exists = await context.chunksTableExists();
2918
+ if (exists) {
2919
+ await db3.from(getChunksTableName(context.id)).where({ source: result.id }).delete();
2920
+ console.log("[EXULU] Deleted chunks for item", result.id);
2921
+ }
2922
+ console.log("[EXULU] Embedder", context.embedder);
2923
+ console.log("[EXULU] Configuration", context.configuration);
2921
2924
  console.log("[EXULU] Generating embeddings for item", result.id);
2922
2925
  const { job } = await context.embeddings.generate.one({
2923
2926
  item: result,
@@ -2949,7 +2952,14 @@ var postprocessDeletion = async ({
2949
2952
  }
2950
2953
  if (Array.isArray(result)) {
2951
2954
  result = result.map((item) => {
2952
- return postprocessDeletion({ table, requestedFields, agents, contexts, tools, result: item });
2955
+ return postprocessDeletion({
2956
+ table,
2957
+ requestedFields,
2958
+ agents,
2959
+ contexts,
2960
+ tools,
2961
+ result: item
2962
+ });
2953
2963
  });
2954
2964
  } else {
2955
2965
  if (table.type === "items") {
@@ -2978,6 +2988,14 @@ var postprocessDeletion = async ({
2978
2988
  const { db: db3 } = await postgresClient();
2979
2989
  await db3.from("agent_messages").where({ session: result.id }).where({ session: result.id }).delete();
2980
2990
  }
2991
+ if (table.type === "eval_runs") {
2992
+ if (!result.id) {
2993
+ return result;
2994
+ }
2995
+ const { db: db3 } = await postgresClient();
2996
+ await db3.from("job_results").where({ label: { contains: result.id } }).del();
2997
+ await db3.from("eval_runs").where({ id: result.id }).del();
2998
+ }
2981
2999
  }
2982
3000
  return result;
2983
3001
  };
@@ -3956,7 +3974,7 @@ type PageInfo {
3956
3974
  getUniquePromptTags: [String!]!
3957
3975
  `;
3958
3976
  mutationDefs += `
3959
- runEval(id: ID!, test_case_ids: [ID!]): RunEvalReturnPayload
3977
+ runEval(id: ID!, cases: [ID!]): RunEvalReturnPayload
3960
3978
  `;
3961
3979
  mutationDefs += `
3962
3980
  drainQueue(queue: QueueEnum!): JobActionReturnPayload
@@ -5920,7 +5938,7 @@ var ExuluAgent2 = class {
5920
5938
  req,
5921
5939
  project
5922
5940
  ),
5923
- stopWhen: [(0, import_ai.stepCountIs)(2)]
5941
+ stopWhen: [(0, import_ai.stepCountIs)(5)]
5924
5942
  });
5925
5943
  result.text = text;
5926
5944
  inputTokens = totalUsage?.inputTokens || 0;
@@ -5981,7 +5999,7 @@ var ExuluAgent2 = class {
5981
5999
  req,
5982
6000
  project
5983
6001
  ),
5984
- stopWhen: [(0, import_ai.stepCountIs)(2)]
6002
+ stopWhen: [(0, import_ai.stepCountIs)(5)]
5985
6003
  });
5986
6004
  if (statistics) {
5987
6005
  await Promise.all([
@@ -6201,8 +6219,8 @@ ${extractedText}
6201
6219
  onError: (error) => {
6202
6220
  console.error("[EXULU] chat stream error.", error);
6203
6221
  throw new Error(`Chat stream error: ${error instanceof Error ? error.message : String(error)}`);
6204
- }
6205
- // stopWhen: [stepCountIs(1)],
6222
+ },
6223
+ stopWhen: [(0, import_ai.stepCountIs)(5)]
6206
6224
  });
6207
6225
  return {
6208
6226
  stream: result,
@@ -6730,6 +6748,13 @@ var ExuluContext = class {
6730
6748
  throw new Error("Item id or external id is required for upsert.");
6731
6749
  }
6732
6750
  const { db: db3 } = await postgresClient();
6751
+ Object.keys(item).forEach((key) => {
6752
+ if (this.fields.find((field) => field.name === key)?.type === "json") {
6753
+ if (typeof item[key] === "object" || Array.isArray(item[key])) {
6754
+ item[key] = JSON.stringify(item[key]);
6755
+ }
6756
+ }
6757
+ });
6733
6758
  const mutation = db3.from(getTableName(
6734
6759
  this.id
6735
6760
  )).insert(
@@ -6819,6 +6844,13 @@ var ExuluContext = class {
6819
6844
  if (!record) {
6820
6845
  throw new Error("Item not found.");
6821
6846
  }
6847
+ Object.keys(item).forEach((key) => {
6848
+ if (this.fields.find((field) => field.name === key)?.type === "json") {
6849
+ if (typeof item[key] === "object" || Array.isArray(item[key])) {
6850
+ item[key] = JSON.stringify(item[key]);
6851
+ }
6852
+ }
6853
+ });
6822
6854
  const mutation = db3.from(
6823
6855
  getTableName(this.id)
6824
6856
  ).where(
@@ -7033,6 +7065,8 @@ var ExuluContext = class {
7033
7065
  table.text("created_by");
7034
7066
  table.text("ttl");
7035
7067
  table.text("rights_mode").defaultTo(this.configuration?.defaultRightsMode ?? "private");
7068
+ table.timestamp("embeddings_updated_at").defaultTo(null);
7069
+ table.timestamp("last_processed_at").defaultTo(null);
7036
7070
  table.integer("textlength");
7037
7071
  table.text("source");
7038
7072
  table.integer("chunks_count").defaultTo(0);
@@ -8026,6 +8060,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
8026
8060
  console.log("[EXULU] creating workers for " + queues2?.length + " queues.");
8027
8061
  console.log("[EXULU] queues", queues2.map((q) => q.queue.name));
8028
8062
  installGlobalErrorHandlers();
8063
+ process.setMaxListeners(Math.max(queues2.length * 2 + 5, 15));
8029
8064
  if (!redisServer.host || !redisServer.port) {
8030
8065
  console.error("[EXULU] you are trying to start worker, but no redis server is configured in the environment.");
8031
8066
  throw new Error("No redis server configured in the environment, so cannot start worker.");
@@ -8058,7 +8093,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
8058
8093
  }));
8059
8094
  const { db: db3 } = await postgresClient();
8060
8095
  const data = bullmqJob.data;
8061
- const timeoutInSeconds = data.timeoutInSeconds || 600;
8096
+ const timeoutInSeconds = data.timeoutInSeconds || queue.timeoutInSeconds || 600;
8062
8097
  const timeoutMs = timeoutInSeconds * 1e3;
8063
8098
  let timeoutHandle;
8064
8099
  const timeoutPromise = new Promise((_, reject) => {
@@ -8262,7 +8297,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
8262
8297
  attempts: queue2.retries || 3,
8263
8298
  // todo make this configurable?
8264
8299
  removeOnComplete: 5e3,
8265
- removeOnFail: 1e4,
8300
+ removeOnFail: 5e3,
8266
8301
  backoff: queue2.backoff || {
8267
8302
  type: "exponential",
8268
8303
  delay: 2e3
@@ -8484,8 +8519,8 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
8484
8519
  const { db: db3 } = await postgresClient();
8485
8520
  await db3.from("job_results").where({ job_id: job.id }).update({
8486
8521
  state: JOB_STATUS_ENUM.completed,
8487
- result: returnvalue.result ? JSON.stringify(returnvalue.result) : null,
8488
- metadata: returnvalue.metadata ? JSON.stringify(returnvalue.metadata) : null
8522
+ result: returnvalue.result != null ? JSON.stringify(returnvalue.result) : null,
8523
+ metadata: returnvalue.metadata != null ? JSON.stringify(returnvalue.metadata) : null
8489
8524
  });
8490
8525
  });
8491
8526
  worker.on("failed", async (job, error, prev) => {
@@ -8596,9 +8631,11 @@ var pollJobResult = async ({ queue, jobId }) => {
8596
8631
  console.log(`[EXULU] eval function job ${job.name} completed, getting result from database...`);
8597
8632
  const { db: db3 } = await postgresClient();
8598
8633
  const entry = await db3.from("job_results").where({ job_id: job.id }).first();
8634
+ console.log("[EXULU] eval function job ${job.name} result", entry);
8599
8635
  result = entry?.result;
8600
8636
  if (result === void 0 || result === null || result === "") {
8601
- throw new Error(`Eval function ${job.id} result not found in database for job eval function job ${job.name}.`);
8637
+ throw new Error(`Eval function ${job.id} result not found in database
8638
+ for job eval function job ${job.name}. Entry data from DB: ${JSON.stringify(entry)}.`);
8602
8639
  }
8603
8640
  console.log(`[EXULU] eval function ${job.id} result: ${result}`);
8604
8641
  break;
@@ -9721,8 +9758,14 @@ var llmAsJudgeEval = () => {
9721
9758
  console.error("[EXULU] prompt is required for llm as judge eval but none is provided.");
9722
9759
  throw new Error("Prompt is required for llm as judge eval but none is provided.");
9723
9760
  }
9761
+ console.log("[EXULU] messages", messages);
9762
+ const lastTypes = messages[messages.length - 1]?.parts?.map((part) => ({
9763
+ type: part.type,
9764
+ text: part.type === "text" ? part.text?.slice(0, 100) : void 0
9765
+ }));
9724
9766
  const lastMessage = messages[messages.length - 1]?.parts?.filter((part) => part.type === "text").map((part) => part.text).join("\n");
9725
9767
  console.log("[EXULU] last message", lastMessage);
9768
+ console.log("[EXULU] last types", lastTypes);
9726
9769
  if (!lastMessage) {
9727
9770
  return 0;
9728
9771
  }
@@ -10791,8 +10834,8 @@ var ExuluApp = class {
10791
10834
  const queueSet = /* @__PURE__ */ new Set();
10792
10835
  if (redisServer.host?.length && redisServer.port?.length) {
10793
10836
  queues.register(global_queues.eval_runs, {
10794
- worker: 1,
10795
- queue: 1
10837
+ worker: 10,
10838
+ queue: 10
10796
10839
  }, 1);
10797
10840
  for (const queue of queues.list.values()) {
10798
10841
  const config2 = await queue.use();
package/dist/index.js CHANGED
@@ -2860,13 +2860,16 @@ var postprocessUpdate = async ({
2860
2860
  if (!context.embedder) {
2861
2861
  return result;
2862
2862
  }
2863
- const { db: db3 } = await postgresClient();
2864
- console.log("[EXULU] Deleting chunks for item", result.id);
2865
- await db3.from(getChunksTableName(context.id)).where({ source: result.id }).delete();
2866
- console.log("[EXULU] Deleted chunks for item", result.id);
2867
- console.log("[EXULU] Embedder", context.embedder);
2868
- console.log("[EXULU] Configuration", context.configuration);
2869
2863
  if (context.embedder && (context.configuration.calculateVectors === "onUpdate" || context.configuration.calculateVectors === "always")) {
2864
+ const { db: db3 } = await postgresClient();
2865
+ console.log("[EXULU] Deleting chunks for item", result.id);
2866
+ const exists = await context.chunksTableExists();
2867
+ if (exists) {
2868
+ await db3.from(getChunksTableName(context.id)).where({ source: result.id }).delete();
2869
+ console.log("[EXULU] Deleted chunks for item", result.id);
2870
+ }
2871
+ console.log("[EXULU] Embedder", context.embedder);
2872
+ console.log("[EXULU] Configuration", context.configuration);
2870
2873
  console.log("[EXULU] Generating embeddings for item", result.id);
2871
2874
  const { job } = await context.embeddings.generate.one({
2872
2875
  item: result,
@@ -2898,7 +2901,14 @@ var postprocessDeletion = async ({
2898
2901
  }
2899
2902
  if (Array.isArray(result)) {
2900
2903
  result = result.map((item) => {
2901
- return postprocessDeletion({ table, requestedFields, agents, contexts, tools, result: item });
2904
+ return postprocessDeletion({
2905
+ table,
2906
+ requestedFields,
2907
+ agents,
2908
+ contexts,
2909
+ tools,
2910
+ result: item
2911
+ });
2902
2912
  });
2903
2913
  } else {
2904
2914
  if (table.type === "items") {
@@ -2927,6 +2937,14 @@ var postprocessDeletion = async ({
2927
2937
  const { db: db3 } = await postgresClient();
2928
2938
  await db3.from("agent_messages").where({ session: result.id }).where({ session: result.id }).delete();
2929
2939
  }
2940
+ if (table.type === "eval_runs") {
2941
+ if (!result.id) {
2942
+ return result;
2943
+ }
2944
+ const { db: db3 } = await postgresClient();
2945
+ await db3.from("job_results").where({ label: { contains: result.id } }).del();
2946
+ await db3.from("eval_runs").where({ id: result.id }).del();
2947
+ }
2930
2948
  }
2931
2949
  return result;
2932
2950
  };
@@ -3905,7 +3923,7 @@ type PageInfo {
3905
3923
  getUniquePromptTags: [String!]!
3906
3924
  `;
3907
3925
  mutationDefs += `
3908
- runEval(id: ID!, test_case_ids: [ID!]): RunEvalReturnPayload
3926
+ runEval(id: ID!, cases: [ID!]): RunEvalReturnPayload
3909
3927
  `;
3910
3928
  mutationDefs += `
3911
3929
  drainQueue(queue: QueueEnum!): JobActionReturnPayload
@@ -5888,7 +5906,7 @@ var ExuluAgent2 = class {
5888
5906
  req,
5889
5907
  project
5890
5908
  ),
5891
- stopWhen: [stepCountIs(2)]
5909
+ stopWhen: [stepCountIs(5)]
5892
5910
  });
5893
5911
  result.text = text;
5894
5912
  inputTokens = totalUsage?.inputTokens || 0;
@@ -5949,7 +5967,7 @@ var ExuluAgent2 = class {
5949
5967
  req,
5950
5968
  project
5951
5969
  ),
5952
- stopWhen: [stepCountIs(2)]
5970
+ stopWhen: [stepCountIs(5)]
5953
5971
  });
5954
5972
  if (statistics) {
5955
5973
  await Promise.all([
@@ -6169,8 +6187,8 @@ ${extractedText}
6169
6187
  onError: (error) => {
6170
6188
  console.error("[EXULU] chat stream error.", error);
6171
6189
  throw new Error(`Chat stream error: ${error instanceof Error ? error.message : String(error)}`);
6172
- }
6173
- // stopWhen: [stepCountIs(1)],
6190
+ },
6191
+ stopWhen: [stepCountIs(5)]
6174
6192
  });
6175
6193
  return {
6176
6194
  stream: result,
@@ -6698,6 +6716,13 @@ var ExuluContext = class {
6698
6716
  throw new Error("Item id or external id is required for upsert.");
6699
6717
  }
6700
6718
  const { db: db3 } = await postgresClient();
6719
+ Object.keys(item).forEach((key) => {
6720
+ if (this.fields.find((field) => field.name === key)?.type === "json") {
6721
+ if (typeof item[key] === "object" || Array.isArray(item[key])) {
6722
+ item[key] = JSON.stringify(item[key]);
6723
+ }
6724
+ }
6725
+ });
6701
6726
  const mutation = db3.from(getTableName(
6702
6727
  this.id
6703
6728
  )).insert(
@@ -6787,6 +6812,13 @@ var ExuluContext = class {
6787
6812
  if (!record) {
6788
6813
  throw new Error("Item not found.");
6789
6814
  }
6815
+ Object.keys(item).forEach((key) => {
6816
+ if (this.fields.find((field) => field.name === key)?.type === "json") {
6817
+ if (typeof item[key] === "object" || Array.isArray(item[key])) {
6818
+ item[key] = JSON.stringify(item[key]);
6819
+ }
6820
+ }
6821
+ });
6790
6822
  const mutation = db3.from(
6791
6823
  getTableName(this.id)
6792
6824
  ).where(
@@ -7001,6 +7033,8 @@ var ExuluContext = class {
7001
7033
  table.text("created_by");
7002
7034
  table.text("ttl");
7003
7035
  table.text("rights_mode").defaultTo(this.configuration?.defaultRightsMode ?? "private");
7036
+ table.timestamp("embeddings_updated_at").defaultTo(null);
7037
+ table.timestamp("last_processed_at").defaultTo(null);
7004
7038
  table.integer("textlength");
7005
7039
  table.text("source");
7006
7040
  table.integer("chunks_count").defaultTo(0);
@@ -7994,6 +8028,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
7994
8028
  console.log("[EXULU] creating workers for " + queues2?.length + " queues.");
7995
8029
  console.log("[EXULU] queues", queues2.map((q) => q.queue.name));
7996
8030
  installGlobalErrorHandlers();
8031
+ process.setMaxListeners(Math.max(queues2.length * 2 + 5, 15));
7997
8032
  if (!redisServer.host || !redisServer.port) {
7998
8033
  console.error("[EXULU] you are trying to start worker, but no redis server is configured in the environment.");
7999
8034
  throw new Error("No redis server configured in the environment, so cannot start worker.");
@@ -8026,7 +8061,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
8026
8061
  }));
8027
8062
  const { db: db3 } = await postgresClient();
8028
8063
  const data = bullmqJob.data;
8029
- const timeoutInSeconds = data.timeoutInSeconds || 600;
8064
+ const timeoutInSeconds = data.timeoutInSeconds || queue.timeoutInSeconds || 600;
8030
8065
  const timeoutMs = timeoutInSeconds * 1e3;
8031
8066
  let timeoutHandle;
8032
8067
  const timeoutPromise = new Promise((_, reject) => {
@@ -8230,7 +8265,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
8230
8265
  attempts: queue2.retries || 3,
8231
8266
  // todo make this configurable?
8232
8267
  removeOnComplete: 5e3,
8233
- removeOnFail: 1e4,
8268
+ removeOnFail: 5e3,
8234
8269
  backoff: queue2.backoff || {
8235
8270
  type: "exponential",
8236
8271
  delay: 2e3
@@ -8452,8 +8487,8 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
8452
8487
  const { db: db3 } = await postgresClient();
8453
8488
  await db3.from("job_results").where({ job_id: job.id }).update({
8454
8489
  state: JOB_STATUS_ENUM.completed,
8455
- result: returnvalue.result ? JSON.stringify(returnvalue.result) : null,
8456
- metadata: returnvalue.metadata ? JSON.stringify(returnvalue.metadata) : null
8490
+ result: returnvalue.result != null ? JSON.stringify(returnvalue.result) : null,
8491
+ metadata: returnvalue.metadata != null ? JSON.stringify(returnvalue.metadata) : null
8457
8492
  });
8458
8493
  });
8459
8494
  worker.on("failed", async (job, error, prev) => {
@@ -8564,9 +8599,11 @@ var pollJobResult = async ({ queue, jobId }) => {
8564
8599
  console.log(`[EXULU] eval function job ${job.name} completed, getting result from database...`);
8565
8600
  const { db: db3 } = await postgresClient();
8566
8601
  const entry = await db3.from("job_results").where({ job_id: job.id }).first();
8602
+ console.log("[EXULU] eval function job ${job.name} result", entry);
8567
8603
  result = entry?.result;
8568
8604
  if (result === void 0 || result === null || result === "") {
8569
- throw new Error(`Eval function ${job.id} result not found in database for job eval function job ${job.name}.`);
8605
+ throw new Error(`Eval function ${job.id} result not found in database
8606
+ for job eval function job ${job.name}. Entry data from DB: ${JSON.stringify(entry)}.`);
8570
8607
  }
8571
8608
  console.log(`[EXULU] eval function ${job.id} result: ${result}`);
8572
8609
  break;
@@ -9689,8 +9726,14 @@ var llmAsJudgeEval = () => {
9689
9726
  console.error("[EXULU] prompt is required for llm as judge eval but none is provided.");
9690
9727
  throw new Error("Prompt is required for llm as judge eval but none is provided.");
9691
9728
  }
9729
+ console.log("[EXULU] messages", messages);
9730
+ const lastTypes = messages[messages.length - 1]?.parts?.map((part) => ({
9731
+ type: part.type,
9732
+ text: part.type === "text" ? part.text?.slice(0, 100) : void 0
9733
+ }));
9692
9734
  const lastMessage = messages[messages.length - 1]?.parts?.filter((part) => part.type === "text").map((part) => part.text).join("\n");
9693
9735
  console.log("[EXULU] last message", lastMessage);
9736
+ console.log("[EXULU] last types", lastTypes);
9694
9737
  if (!lastMessage) {
9695
9738
  return 0;
9696
9739
  }
@@ -10759,8 +10802,8 @@ var ExuluApp = class {
10759
10802
  const queueSet = /* @__PURE__ */ new Set();
10760
10803
  if (redisServer.host?.length && redisServer.port?.length) {
10761
10804
  queues.register(global_queues.eval_runs, {
10762
- worker: 1,
10763
- queue: 1
10805
+ worker: 10,
10806
+ queue: 10
10764
10807
  }, 1);
10765
10808
  for (const queue of queues.list.values()) {
10766
10809
  const config2 = await queue.use();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@exulu/backend",
3
3
  "author": "Qventu Bv.",
4
- "version": "1.43.0",
4
+ "version": "1.44.0",
5
5
  "main": "./dist/index.js",
6
6
  "private": false,
7
7
  "publishConfig": {