@exulu/backend 1.33.0 → 1.34.1

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/.mcp.json ADDED
@@ -0,0 +1,11 @@
1
+ {
2
+ "mcpServers": {
3
+ "exulu-mcp-server-default-coding-agent": {
4
+ "type": "http",
5
+ "url": "https://backend.ai.open.de/mcp/76e6a87c-98be-480b-8081-264eb0bb4ecc",
6
+ "headers": {
7
+ "Authorization": "Bearer ${EXULU_TOKEN}"
8
+ }
9
+ }
10
+ }
11
+ }
package/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
- # [1.33.0](https://github.com/Qventu/exulu-backend/compare/v1.32.1...v1.33.0) (2025-11-14)
1
+ ## [1.34.1](https://github.com/Qventu/exulu-backend/compare/v1.34.0...v1.34.1) (2025-11-17)
2
2
 
3
3
 
4
- ### Features
4
+ ### Bug Fixes
5
5
 
6
- * add todo system, enhanced roles, session metadata, and scheduling support ([06cb449](https://github.com/Qventu/exulu-backend/commit/06cb44999d8f0fcbf60c8f5c50fc3a84db4131d9))
6
+ * project tracking anthropic passthrough and timeout for worker ([d0c39c8](https://github.com/Qventu/exulu-backend/commit/d0c39c8dec082a098b91958d9b2fa31baa31b59e))
package/dist/index.cjs CHANGED
@@ -279,7 +279,7 @@ var bullmqDecorator = async ({
279
279
  embedder,
280
280
  workflow,
281
281
  processor,
282
- eval
282
+ evaluation
283
283
  ];
284
284
  if (types.filter((type2) => type2).length > 1) {
285
285
  throw new Error("Cannot have multiple types in the same job, must be one of the following: embedder, workflow, processor or eval.");
@@ -1379,6 +1379,69 @@ var platformConfigurationsSchema = {
1379
1379
  }
1380
1380
  ]
1381
1381
  };
1382
+ var promptLibrarySchema = {
1383
+ type: "prompt_library",
1384
+ name: {
1385
+ plural: "prompt_library",
1386
+ singular: "prompt_library_item"
1387
+ },
1388
+ RBAC: true,
1389
+ fields: [
1390
+ {
1391
+ name: "name",
1392
+ type: "text",
1393
+ required: true
1394
+ },
1395
+ {
1396
+ name: "description",
1397
+ type: "text"
1398
+ },
1399
+ {
1400
+ name: "content",
1401
+ type: "longText",
1402
+ required: true
1403
+ },
1404
+ {
1405
+ name: "tags",
1406
+ type: "json"
1407
+ },
1408
+ {
1409
+ name: "usage_count",
1410
+ type: "number",
1411
+ default: 0
1412
+ },
1413
+ {
1414
+ name: "favorite_count",
1415
+ type: "number",
1416
+ default: 0
1417
+ },
1418
+ {
1419
+ name: "assigned_agents",
1420
+ type: "json"
1421
+ }
1422
+ ]
1423
+ };
1424
+ var promptFavoritesSchema = {
1425
+ type: "prompt_favorites",
1426
+ name: {
1427
+ plural: "prompt_favorites",
1428
+ singular: "prompt_favorite"
1429
+ },
1430
+ fields: [
1431
+ {
1432
+ name: "user_id",
1433
+ type: "number",
1434
+ required: true,
1435
+ index: true
1436
+ },
1437
+ {
1438
+ name: "prompt_id",
1439
+ type: "uuid",
1440
+ required: true,
1441
+ index: true
1442
+ }
1443
+ ]
1444
+ };
1382
1445
  var addCoreFields = (schema) => {
1383
1446
  schema.fields.forEach((field) => {
1384
1447
  if (field.type === "file") {
@@ -1422,7 +1485,9 @@ var coreSchemas = {
1422
1485
  rbacSchema: () => addCoreFields(rbacSchema),
1423
1486
  workflowTemplatesSchema: () => addCoreFields(workflowTemplatesSchema),
1424
1487
  platformConfigurationsSchema: () => addCoreFields(platformConfigurationsSchema),
1425
- jobResultsSchema: () => addCoreFields(jobResultsSchema)
1488
+ jobResultsSchema: () => addCoreFields(jobResultsSchema),
1489
+ promptLibrarySchema: () => addCoreFields(promptLibrarySchema),
1490
+ promptFavoritesSchema: () => addCoreFields(promptFavoritesSchema)
1426
1491
  };
1427
1492
  }
1428
1493
  };
@@ -1882,6 +1947,7 @@ input FilterOperatorJSON {
1882
1947
  eq: JSON
1883
1948
  ne: JSON
1884
1949
  in: [JSON]
1950
+ contains: JSON
1885
1951
  }
1886
1952
 
1887
1953
  input SortBy {
@@ -2383,7 +2449,9 @@ function createMutations(table, agents, contexts, tools, config) {
2383
2449
  source: source.id,
2384
2450
  context: exists.id,
2385
2451
  type: "source",
2386
- inputs: args.inputs
2452
+ inputs: args.inputs,
2453
+ user: context.user.id,
2454
+ role: context.user.role?.id
2387
2455
  });
2388
2456
  console.log("[EXULU] Source function job scheduled", job.id);
2389
2457
  return {
@@ -2412,6 +2480,15 @@ function createMutations(table, agents, contexts, tools, config) {
2412
2480
  console.log(`[EXULU] created item through source update job ${createdItem.id}`);
2413
2481
  }
2414
2482
  }
2483
+ await updateStatistic({
2484
+ name: "count",
2485
+ label: source.id,
2486
+ type: STATISTICS_TYPE_ENUM.SOURCE_UPDATE,
2487
+ trigger: "api",
2488
+ count: 1,
2489
+ user: context?.user?.id,
2490
+ role: context?.user?.role?.id
2491
+ });
2415
2492
  return {
2416
2493
  message: "Items created successfully.",
2417
2494
  jobs,
@@ -2446,7 +2523,7 @@ function createMutations(table, agents, contexts, tools, config) {
2446
2523
  jobs: jobs2.slice(0, 100)
2447
2524
  };
2448
2525
  }
2449
- query = applyFilters(query, args.where);
2526
+ query = applyFilters(query, args.where, table);
2450
2527
  const items = await query;
2451
2528
  if (items.length === 0) {
2452
2529
  throw new Error("No items found to generate chunks for.");
@@ -2481,7 +2558,7 @@ function createMutations(table, agents, contexts, tools, config) {
2481
2558
  }
2482
2559
  let query = db3.from(getTableName(id)).select("id");
2483
2560
  if (args.where) {
2484
- query = applyFilters(query, args.where);
2561
+ query = applyFilters(query, args.where, table);
2485
2562
  }
2486
2563
  const items = await query;
2487
2564
  if (items.length === 0) {
@@ -2538,18 +2615,38 @@ var applyAccessControl = (table, user, query) => {
2538
2615
  }
2539
2616
  return query;
2540
2617
  };
2541
- var converOperatorToQuery = (query, fieldName, operators) => {
2618
+ var converOperatorToQuery = (query, fieldName, operators, table) => {
2619
+ const field = table?.fields.find((f) => f.name === fieldName);
2620
+ const isJsonField = field?.type === "json";
2542
2621
  if (operators.eq !== void 0) {
2543
- query = query.where(fieldName, operators.eq);
2622
+ if (isJsonField) {
2623
+ query = query.whereRaw(`?? = ?::jsonb`, [fieldName, JSON.stringify(operators.eq)]);
2624
+ } else {
2625
+ query = query.where(fieldName, operators.eq);
2626
+ }
2544
2627
  }
2545
2628
  if (operators.ne !== void 0) {
2546
- query = query.whereRaw(`?? IS DISTINCT FROM ?`, [fieldName, operators.ne]);
2629
+ if (isJsonField) {
2630
+ query = query.whereRaw(`?? IS DISTINCT FROM ?::jsonb`, [fieldName, JSON.stringify(operators.ne)]);
2631
+ } else {
2632
+ query = query.whereRaw(`?? IS DISTINCT FROM ?`, [fieldName, operators.ne]);
2633
+ }
2547
2634
  }
2548
2635
  if (operators.in !== void 0) {
2549
- query = query.whereIn(fieldName, operators.in);
2636
+ if (isJsonField) {
2637
+ const conditions = operators.in.map((val) => `?? = ?::jsonb`).join(" OR ");
2638
+ const bindings = operators.in.flatMap((val) => [fieldName, JSON.stringify(val)]);
2639
+ query = query.whereRaw(`(${conditions})`, bindings);
2640
+ } else {
2641
+ query = query.whereIn(fieldName, operators.in);
2642
+ }
2550
2643
  }
2551
2644
  if (operators.contains !== void 0) {
2552
- query = query.where(fieldName, "like", `%${operators.contains}%`);
2645
+ if (isJsonField) {
2646
+ query = query.whereRaw(`?? @> ?::jsonb`, [fieldName, JSON.stringify(operators.contains)]);
2647
+ } else {
2648
+ query = query.where(fieldName, "like", `%${operators.contains}%`);
2649
+ }
2553
2650
  }
2554
2651
  if (operators.lte !== void 0) {
2555
2652
  query = query.where(fieldName, "<=", operators.lte);
@@ -2811,21 +2908,21 @@ var finalizeRequestedFields = async ({
2811
2908
  }
2812
2909
  return result;
2813
2910
  };
2814
- var applyFilters = (query, filters) => {
2911
+ var applyFilters = (query, filters, table) => {
2815
2912
  filters.forEach((filter) => {
2816
2913
  Object.entries(filter).forEach(([fieldName, operators]) => {
2817
2914
  if (operators) {
2818
2915
  if (operators.and !== void 0) {
2819
2916
  operators.and.forEach((operator) => {
2820
- query = converOperatorToQuery(query, fieldName, operator);
2917
+ query = converOperatorToQuery(query, fieldName, operator, table);
2821
2918
  });
2822
2919
  }
2823
2920
  if (operators.or !== void 0) {
2824
2921
  operators.or.forEach((operator) => {
2825
- query = converOperatorToQuery(query, fieldName, operator);
2922
+ query = converOperatorToQuery(query, fieldName, operator, table);
2826
2923
  });
2827
2924
  }
2828
- query = converOperatorToQuery(query, fieldName, operators);
2925
+ query = converOperatorToQuery(query, fieldName, operators, table);
2829
2926
  }
2830
2927
  });
2831
2928
  });
@@ -2865,7 +2962,7 @@ function createQueries(table, agents, tools, contexts) {
2865
2962
  const requestedFields = getRequestedFields(info);
2866
2963
  const sanitizedFields = sanitizeRequestedFields(table, requestedFields);
2867
2964
  let query = db3.from(tableNamePlural).select(sanitizedFields);
2868
- query = applyFilters(query, filters);
2965
+ query = applyFilters(query, filters, table);
2869
2966
  query = applyAccessControl(table, context.user, query);
2870
2967
  query = applySorting(query, sort);
2871
2968
  let result = await query.first();
@@ -2878,7 +2975,7 @@ function createQueries(table, agents, tools, contexts) {
2878
2975
  throw new Error("Limit cannot be greater than 500.");
2879
2976
  }
2880
2977
  let countQuery = db3(tableNamePlural);
2881
- countQuery = applyFilters(countQuery, filters);
2978
+ countQuery = applyFilters(countQuery, filters, table);
2882
2979
  countQuery = applyAccessControl(table, context.user, countQuery);
2883
2980
  const countResult = await countQuery.count("* as count");
2884
2981
  const itemCount = Number(countResult[0]?.count || 0);
@@ -2887,7 +2984,7 @@ function createQueries(table, agents, tools, contexts) {
2887
2984
  const hasPreviousPage = currentPage > 1;
2888
2985
  const hasNextPage = currentPage < pageCount - 1;
2889
2986
  let dataQuery = db3(tableNamePlural);
2890
- dataQuery = applyFilters(dataQuery, filters);
2987
+ dataQuery = applyFilters(dataQuery, filters, table);
2891
2988
  dataQuery = applyAccessControl(table, context.user, dataQuery);
2892
2989
  const requestedFields = getRequestedFields(info);
2893
2990
  dataQuery = applySorting(dataQuery, sort);
@@ -2912,7 +3009,7 @@ function createQueries(table, agents, tools, contexts) {
2912
3009
  const { filters = [], groupBy } = args;
2913
3010
  const { db: db3 } = context;
2914
3011
  let query = db3(tableNamePlural);
2915
- query = applyFilters(query, filters);
3012
+ query = applyFilters(query, filters, table);
2916
3013
  query = applyAccessControl(table, context.user, query);
2917
3014
  if (groupBy) {
2918
3015
  query = query.select(groupBy).groupBy(groupBy);
@@ -3012,11 +3109,11 @@ var vectorSearch = async ({
3012
3109
  const mainTable = getTableName(id);
3013
3110
  const chunksTable = getChunksTableName(id);
3014
3111
  let countQuery = db3(mainTable);
3015
- countQuery = applyFilters(countQuery, filters);
3112
+ countQuery = applyFilters(countQuery, filters, table);
3016
3113
  countQuery = applyAccessControl(table, user, countQuery);
3017
3114
  const columns = await db3(mainTable).columnInfo();
3018
3115
  let itemsQuery = db3(mainTable).select(Object.keys(columns).map((column) => mainTable + "." + column));
3019
- itemsQuery = applyFilters(itemsQuery, filters);
3116
+ itemsQuery = applyFilters(itemsQuery, filters, table);
3020
3117
  itemsQuery = applyAccessControl(table, user, itemsQuery);
3021
3118
  itemsQuery = applySorting(itemsQuery, sort);
3022
3119
  if (queryRewriter) {
@@ -3777,7 +3874,7 @@ type PageInfo {
3777
3874
  const {
3778
3875
  jobs,
3779
3876
  count
3780
- } = await getJobsByQueueAndName(
3877
+ } = await getJobsByQueueName(
3781
3878
  args.queue,
3782
3879
  args.statusses,
3783
3880
  args.page || 1,
@@ -4210,7 +4307,7 @@ var validateCreateOrRemoveSuperAdminPermission = async (tableNamePlural, input,
4210
4307
  }
4211
4308
  }
4212
4309
  };
4213
- async function getJobsByQueueAndName(queueName, statusses, page, limit) {
4310
+ async function getJobsByQueueName(queueName, statusses, page, limit) {
4214
4311
  const queue = queues.list.get(queueName);
4215
4312
  if (!queue) {
4216
4313
  throw new Error(`Queue ${queueName} not found`);
@@ -5681,6 +5778,9 @@ var ExuluContext = class {
5681
5778
  };
5682
5779
  };
5683
5780
  createItem = async (item, config, user, role, upsert) => {
5781
+ if (upsert && (!item.id && !item.external_id)) {
5782
+ throw new Error("Item id or external id is required for upsert.");
5783
+ }
5684
5784
  const { db: db3 } = await postgresClient();
5685
5785
  const mutation = db3.from(getTableName(
5686
5786
  this.id
@@ -5965,6 +6065,7 @@ var ExuluContext = class {
5965
6065
  };
5966
6066
  };
5967
6067
  var updateStatistic = async (statistic) => {
6068
+ console.log("[EXULU] updating statistic", statistic);
5968
6069
  const currentDate = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
5969
6070
  const { db: db3 } = await postgresClient();
5970
6071
  const existing = await db3.from("tracking").where({
@@ -6108,6 +6209,8 @@ var {
6108
6209
  variablesSchema: variablesSchema2,
6109
6210
  workflowTemplatesSchema: workflowTemplatesSchema2,
6110
6211
  rbacSchema: rbacSchema2,
6212
+ promptLibrarySchema: promptLibrarySchema2,
6213
+ promptFavoritesSchema: promptFavoritesSchema2,
6111
6214
  statisticsSchema: statisticsSchema2
6112
6215
  } = coreSchemas.get();
6113
6216
  var createExpressRoutes = async (app, agents, tools, contexts, config, evals, tracer, queues2) => {
@@ -6138,6 +6241,8 @@ var createExpressRoutes = async (app, agents, tools, contexts, config, evals, tr
6138
6241
  agentsSchema2(),
6139
6242
  projectsSchema2(),
6140
6243
  jobResultsSchema2(),
6244
+ promptLibrarySchema2(),
6245
+ promptFavoritesSchema2(),
6141
6246
  evalRunsSchema2(),
6142
6247
  platformConfigurationsSchema2(),
6143
6248
  evalSetsSchema2(),
@@ -6548,7 +6653,7 @@ Mood: friendly and intelligent.
6548
6653
  return;
6549
6654
  }
6550
6655
  let project = null;
6551
- if (!req.body.project || req.body.project === "DEFAULT") {
6656
+ if (!req.params.project || req.params.project === "DEFAULT") {
6552
6657
  project = null;
6553
6658
  } else {
6554
6659
  let projectQuery = db3("projects");
@@ -6825,7 +6930,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
6825
6930
  }));
6826
6931
  const { db: db3 } = await postgresClient();
6827
6932
  const data = bullmqJob.data;
6828
- const timeoutMs = data.timeoutInSeconds * 1e3;
6933
+ const timeoutMs = (data.timeoutInSeconds || 300) * 1e3;
6829
6934
  const timeoutPromise = new Promise((_, reject) => {
6830
6935
  setTimeout(() => {
6831
6936
  reject(new Error(`Timeout for job ${bullmqJob.id} reached after ${data.timeoutInSeconds}s`));
@@ -6916,6 +7021,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
6916
7021
  const label = `eval-run-${data.eval_run_id}-${data.test_case_id}`;
6917
7022
  const existingResult = await db3.from("job_results").where({ label }).first();
6918
7023
  if (existingResult) {
7024
+ console.log("[EXULU] found existing job result, so ");
6919
7025
  await db3.from("job_results").where({ label }).update({
6920
7026
  job_id: bullmqJob.id,
6921
7027
  label,
@@ -7017,6 +7123,8 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
7017
7123
  test_case_id: testCase.id,
7018
7124
  eval_run_id: evalRun.id,
7019
7125
  eval_function_id: evalFunction.id,
7126
+ eval_function_name: evalFunction.name,
7127
+ eval_function_config: evalFunction.config || {},
7020
7128
  result: result2 || 0
7021
7129
  };
7022
7130
  console.log(`[EXULU] eval function ${evalFunction.id} result: ${result2}`, logMetadata2(bullmqJob.name, {
@@ -7044,22 +7152,32 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
7044
7152
  }
7045
7153
  }
7046
7154
  const scores = evalFunctionResults.map((result2) => result2.result);
7155
+ console.log("[EXULU] Exulu eval run scores for test case: " + testCase.id, scores);
7047
7156
  let score = 0;
7048
- switch (data.scoring_method) {
7157
+ switch (data.scoring_method?.toLowerCase()) {
7049
7158
  case "median":
7159
+ console.log("[EXULU] Calculating median score");
7050
7160
  score = getMedian(scores);
7051
7161
  break;
7052
7162
  case "average":
7163
+ console.log("[EXULU] Calculating average score");
7053
7164
  score = getAverage(scores);
7054
7165
  break;
7055
7166
  case "sum":
7167
+ console.log("[EXULU] Calculating sum score");
7056
7168
  score = getSum(scores);
7057
7169
  break;
7170
+ default:
7171
+ console.log("[EXULU] Calculating average score");
7172
+ score = getAverage(scores);
7058
7173
  }
7059
7174
  return {
7060
7175
  result: score,
7061
7176
  metadata: {
7062
- ...evalFunctionResults,
7177
+ messages,
7178
+ function_results: [
7179
+ ...evalFunctionResults
7180
+ ],
7063
7181
  ...metadata
7064
7182
  }
7065
7183
  };
@@ -7155,6 +7273,15 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
7155
7273
  }));
7156
7274
  }
7157
7275
  }
7276
+ await updateStatistic({
7277
+ name: "count",
7278
+ label: source.id,
7279
+ type: STATISTICS_TYPE_ENUM.SOURCE_UPDATE,
7280
+ trigger: "api",
7281
+ count: 1,
7282
+ user: data?.user,
7283
+ role: data?.role
7284
+ });
7158
7285
  return {
7159
7286
  result,
7160
7287
  metadata: {
@@ -7592,6 +7719,92 @@ var ExuluMCP = class {
7592
7719
  });
7593
7720
  server.tools[tool2.id] = tool2.name;
7594
7721
  }
7722
+ const getListOfPromptTemplatesName = "getListOfPromptTemplates";
7723
+ if (!server.tools[getListOfPromptTemplatesName]) {
7724
+ server.mcp.registerTool(getListOfPromptTemplatesName, {
7725
+ title: "Get List of Prompt Templates",
7726
+ description: "Retrieves a list of prompt templates available for this agent. Returns the name, description, and ID of each template.",
7727
+ inputSchema: {
7728
+ inputs: import_zod2.z.object({})
7729
+ }
7730
+ }, async ({ inputs }, args) => {
7731
+ console.log("[EXULU] Getting list of prompt templates for agent", agentInstance.id);
7732
+ const { db: db4 } = await postgresClient();
7733
+ const prompts = await db4.from("prompt_library").select("id", "name", "description").whereRaw("assigned_agents @> ?::jsonb", [JSON.stringify(agentInstance.id)]).orderBy("updatedAt", "desc");
7734
+ console.log("[EXULU] Found", prompts.length, "prompt templates");
7735
+ return {
7736
+ content: [{
7737
+ type: "text",
7738
+ text: JSON.stringify({
7739
+ prompts: prompts.map((p) => ({
7740
+ id: p.id,
7741
+ name: p.name,
7742
+ description: p.description || "No description provided"
7743
+ })),
7744
+ count: prompts.length
7745
+ }, null, 2)
7746
+ }],
7747
+ structuredContent: {
7748
+ prompts: prompts.map((p) => ({
7749
+ id: p.id,
7750
+ name: p.name,
7751
+ description: p.description || "No description provided"
7752
+ })),
7753
+ count: prompts.length
7754
+ }
7755
+ };
7756
+ });
7757
+ }
7758
+ const getPromptTemplateDetailsName = "getPromptTemplateDetails";
7759
+ if (!server.tools[getPromptTemplateDetailsName]) {
7760
+ server.mcp.registerTool(getPromptTemplateDetailsName, {
7761
+ title: "Get Prompt Template Details",
7762
+ description: "Retrieves the full details of a specific prompt template by ID, including the actual template content with variables.",
7763
+ inputSchema: {
7764
+ inputs: import_zod2.z.object({
7765
+ id: import_zod2.z.string().describe("The ID of the prompt template to retrieve")
7766
+ })
7767
+ }
7768
+ }, async ({ inputs }, args) => {
7769
+ console.log("[EXULU] Getting prompt template details for ID", inputs.id);
7770
+ const { db: db4 } = await postgresClient();
7771
+ const prompt = await db4.from("prompt_library").select("id", "name", "description", "content", "createdAt", "updatedAt", "usage_count", "favorite_count").where({ id: inputs.id }).first();
7772
+ if (!prompt) {
7773
+ throw new Error(`Prompt template with ID ${inputs.id} not found`);
7774
+ }
7775
+ const isAssignedToAgent = await db4.from("prompt_library").select("id").where({ id: inputs.id }).whereRaw("assigned_agents @> ?::jsonb", [JSON.stringify(agentInstance.id)]).first();
7776
+ console.log("[EXULU] Prompt template found:", prompt.name);
7777
+ return {
7778
+ content: [{
7779
+ type: "text",
7780
+ text: JSON.stringify({
7781
+ id: prompt.id,
7782
+ name: prompt.name,
7783
+ description: prompt.description || "No description provided",
7784
+ content: prompt.content,
7785
+ createdAt: prompt.createdAt,
7786
+ updatedAt: prompt.updatedAt,
7787
+ usageCount: prompt.usage_count || 0,
7788
+ favoriteCount: prompt.favorite_count || 0,
7789
+ isAssignedToThisAgent: !!isAssignedToAgent
7790
+ }, null, 2)
7791
+ }],
7792
+ structuredContent: {
7793
+ id: prompt.id,
7794
+ name: prompt.name,
7795
+ description: prompt.description || "No description provided",
7796
+ content: prompt.content,
7797
+ createdAt: prompt.createdAt,
7798
+ updatedAt: prompt.updatedAt,
7799
+ usageCount: prompt.usage_count || 0,
7800
+ favoriteCount: prompt.favorite_count || 0,
7801
+ isAssignedToThisAgent: !!isAssignedToAgent
7802
+ }
7803
+ };
7804
+ });
7805
+ }
7806
+ server.tools[getListOfPromptTemplatesName] = getListOfPromptTemplatesName;
7807
+ server.tools[getPromptTemplateDetailsName] = getPromptTemplateDetailsName;
7595
7808
  return server.mcp;
7596
7809
  };
7597
7810
  create = async ({ express: express3, allTools, allAgents, allContexts, config }) => {
@@ -8206,8 +8419,8 @@ var llmAsJudgeEval = () => {
8206
8419
  console.log("[EXULU] running llm as judge eval", { agent, backend, messages, testCase, config });
8207
8420
  let prompt = config?.prompt;
8208
8421
  if (!prompt) {
8209
- console.error("[EXULU] prompt is required.");
8210
- throw new Error("Prompt is required.");
8422
+ console.error("[EXULU] prompt is required for llm as judge eval but none is provided.");
8423
+ throw new Error("Prompt is required for llm as judge eval but none is provided.");
8211
8424
  }
8212
8425
  const lastMessage = messages[messages.length - 1]?.parts?.filter((part) => part.type === "text").map((part) => part.text).join("\n");
8213
8426
  console.log("[EXULU] last message", lastMessage);
@@ -10672,7 +10885,9 @@ var {
10672
10885
  workflowTemplatesSchema: workflowTemplatesSchema3,
10673
10886
  rbacSchema: rbacSchema3,
10674
10887
  projectsSchema: projectsSchema3,
10675
- jobResultsSchema: jobResultsSchema3
10888
+ jobResultsSchema: jobResultsSchema3,
10889
+ promptLibrarySchema: promptLibrarySchema3,
10890
+ promptFavoritesSchema: promptFavoritesSchema3
10676
10891
  } = coreSchemas.get();
10677
10892
  var addMissingFields = async (knex, tableName, fields, skipFields = []) => {
10678
10893
  for (const field of fields) {
@@ -10707,6 +10922,8 @@ var up = async function(knex) {
10707
10922
  statisticsSchema3(),
10708
10923
  projectsSchema3(),
10709
10924
  jobResultsSchema3(),
10925
+ promptLibrarySchema3(),
10926
+ promptFavoritesSchema3(),
10710
10927
  rbacSchema3(),
10711
10928
  agentsSchema3(),
10712
10929
  variablesSchema3(),
package/dist/index.js CHANGED
@@ -227,7 +227,7 @@ var bullmqDecorator = async ({
227
227
  embedder,
228
228
  workflow,
229
229
  processor,
230
- eval
230
+ evaluation
231
231
  ];
232
232
  if (types.filter((type2) => type2).length > 1) {
233
233
  throw new Error("Cannot have multiple types in the same job, must be one of the following: embedder, workflow, processor or eval.");
@@ -1327,6 +1327,69 @@ var platformConfigurationsSchema = {
1327
1327
  }
1328
1328
  ]
1329
1329
  };
1330
+ var promptLibrarySchema = {
1331
+ type: "prompt_library",
1332
+ name: {
1333
+ plural: "prompt_library",
1334
+ singular: "prompt_library_item"
1335
+ },
1336
+ RBAC: true,
1337
+ fields: [
1338
+ {
1339
+ name: "name",
1340
+ type: "text",
1341
+ required: true
1342
+ },
1343
+ {
1344
+ name: "description",
1345
+ type: "text"
1346
+ },
1347
+ {
1348
+ name: "content",
1349
+ type: "longText",
1350
+ required: true
1351
+ },
1352
+ {
1353
+ name: "tags",
1354
+ type: "json"
1355
+ },
1356
+ {
1357
+ name: "usage_count",
1358
+ type: "number",
1359
+ default: 0
1360
+ },
1361
+ {
1362
+ name: "favorite_count",
1363
+ type: "number",
1364
+ default: 0
1365
+ },
1366
+ {
1367
+ name: "assigned_agents",
1368
+ type: "json"
1369
+ }
1370
+ ]
1371
+ };
1372
+ var promptFavoritesSchema = {
1373
+ type: "prompt_favorites",
1374
+ name: {
1375
+ plural: "prompt_favorites",
1376
+ singular: "prompt_favorite"
1377
+ },
1378
+ fields: [
1379
+ {
1380
+ name: "user_id",
1381
+ type: "number",
1382
+ required: true,
1383
+ index: true
1384
+ },
1385
+ {
1386
+ name: "prompt_id",
1387
+ type: "uuid",
1388
+ required: true,
1389
+ index: true
1390
+ }
1391
+ ]
1392
+ };
1330
1393
  var addCoreFields = (schema) => {
1331
1394
  schema.fields.forEach((field) => {
1332
1395
  if (field.type === "file") {
@@ -1370,7 +1433,9 @@ var coreSchemas = {
1370
1433
  rbacSchema: () => addCoreFields(rbacSchema),
1371
1434
  workflowTemplatesSchema: () => addCoreFields(workflowTemplatesSchema),
1372
1435
  platformConfigurationsSchema: () => addCoreFields(platformConfigurationsSchema),
1373
- jobResultsSchema: () => addCoreFields(jobResultsSchema)
1436
+ jobResultsSchema: () => addCoreFields(jobResultsSchema),
1437
+ promptLibrarySchema: () => addCoreFields(promptLibrarySchema),
1438
+ promptFavoritesSchema: () => addCoreFields(promptFavoritesSchema)
1374
1439
  };
1375
1440
  }
1376
1441
  };
@@ -1830,6 +1895,7 @@ input FilterOperatorJSON {
1830
1895
  eq: JSON
1831
1896
  ne: JSON
1832
1897
  in: [JSON]
1898
+ contains: JSON
1833
1899
  }
1834
1900
 
1835
1901
  input SortBy {
@@ -2331,7 +2397,9 @@ function createMutations(table, agents, contexts, tools, config) {
2331
2397
  source: source.id,
2332
2398
  context: exists.id,
2333
2399
  type: "source",
2334
- inputs: args.inputs
2400
+ inputs: args.inputs,
2401
+ user: context.user.id,
2402
+ role: context.user.role?.id
2335
2403
  });
2336
2404
  console.log("[EXULU] Source function job scheduled", job.id);
2337
2405
  return {
@@ -2360,6 +2428,15 @@ function createMutations(table, agents, contexts, tools, config) {
2360
2428
  console.log(`[EXULU] created item through source update job ${createdItem.id}`);
2361
2429
  }
2362
2430
  }
2431
+ await updateStatistic({
2432
+ name: "count",
2433
+ label: source.id,
2434
+ type: STATISTICS_TYPE_ENUM.SOURCE_UPDATE,
2435
+ trigger: "api",
2436
+ count: 1,
2437
+ user: context?.user?.id,
2438
+ role: context?.user?.role?.id
2439
+ });
2363
2440
  return {
2364
2441
  message: "Items created successfully.",
2365
2442
  jobs,
@@ -2394,7 +2471,7 @@ function createMutations(table, agents, contexts, tools, config) {
2394
2471
  jobs: jobs2.slice(0, 100)
2395
2472
  };
2396
2473
  }
2397
- query = applyFilters(query, args.where);
2474
+ query = applyFilters(query, args.where, table);
2398
2475
  const items = await query;
2399
2476
  if (items.length === 0) {
2400
2477
  throw new Error("No items found to generate chunks for.");
@@ -2429,7 +2506,7 @@ function createMutations(table, agents, contexts, tools, config) {
2429
2506
  }
2430
2507
  let query = db3.from(getTableName(id)).select("id");
2431
2508
  if (args.where) {
2432
- query = applyFilters(query, args.where);
2509
+ query = applyFilters(query, args.where, table);
2433
2510
  }
2434
2511
  const items = await query;
2435
2512
  if (items.length === 0) {
@@ -2486,18 +2563,38 @@ var applyAccessControl = (table, user, query) => {
2486
2563
  }
2487
2564
  return query;
2488
2565
  };
2489
- var converOperatorToQuery = (query, fieldName, operators) => {
2566
+ var converOperatorToQuery = (query, fieldName, operators, table) => {
2567
+ const field = table?.fields.find((f) => f.name === fieldName);
2568
+ const isJsonField = field?.type === "json";
2490
2569
  if (operators.eq !== void 0) {
2491
- query = query.where(fieldName, operators.eq);
2570
+ if (isJsonField) {
2571
+ query = query.whereRaw(`?? = ?::jsonb`, [fieldName, JSON.stringify(operators.eq)]);
2572
+ } else {
2573
+ query = query.where(fieldName, operators.eq);
2574
+ }
2492
2575
  }
2493
2576
  if (operators.ne !== void 0) {
2494
- query = query.whereRaw(`?? IS DISTINCT FROM ?`, [fieldName, operators.ne]);
2577
+ if (isJsonField) {
2578
+ query = query.whereRaw(`?? IS DISTINCT FROM ?::jsonb`, [fieldName, JSON.stringify(operators.ne)]);
2579
+ } else {
2580
+ query = query.whereRaw(`?? IS DISTINCT FROM ?`, [fieldName, operators.ne]);
2581
+ }
2495
2582
  }
2496
2583
  if (operators.in !== void 0) {
2497
- query = query.whereIn(fieldName, operators.in);
2584
+ if (isJsonField) {
2585
+ const conditions = operators.in.map((val) => `?? = ?::jsonb`).join(" OR ");
2586
+ const bindings = operators.in.flatMap((val) => [fieldName, JSON.stringify(val)]);
2587
+ query = query.whereRaw(`(${conditions})`, bindings);
2588
+ } else {
2589
+ query = query.whereIn(fieldName, operators.in);
2590
+ }
2498
2591
  }
2499
2592
  if (operators.contains !== void 0) {
2500
- query = query.where(fieldName, "like", `%${operators.contains}%`);
2593
+ if (isJsonField) {
2594
+ query = query.whereRaw(`?? @> ?::jsonb`, [fieldName, JSON.stringify(operators.contains)]);
2595
+ } else {
2596
+ query = query.where(fieldName, "like", `%${operators.contains}%`);
2597
+ }
2501
2598
  }
2502
2599
  if (operators.lte !== void 0) {
2503
2600
  query = query.where(fieldName, "<=", operators.lte);
@@ -2759,21 +2856,21 @@ var finalizeRequestedFields = async ({
2759
2856
  }
2760
2857
  return result;
2761
2858
  };
2762
- var applyFilters = (query, filters) => {
2859
+ var applyFilters = (query, filters, table) => {
2763
2860
  filters.forEach((filter) => {
2764
2861
  Object.entries(filter).forEach(([fieldName, operators]) => {
2765
2862
  if (operators) {
2766
2863
  if (operators.and !== void 0) {
2767
2864
  operators.and.forEach((operator) => {
2768
- query = converOperatorToQuery(query, fieldName, operator);
2865
+ query = converOperatorToQuery(query, fieldName, operator, table);
2769
2866
  });
2770
2867
  }
2771
2868
  if (operators.or !== void 0) {
2772
2869
  operators.or.forEach((operator) => {
2773
- query = converOperatorToQuery(query, fieldName, operator);
2870
+ query = converOperatorToQuery(query, fieldName, operator, table);
2774
2871
  });
2775
2872
  }
2776
- query = converOperatorToQuery(query, fieldName, operators);
2873
+ query = converOperatorToQuery(query, fieldName, operators, table);
2777
2874
  }
2778
2875
  });
2779
2876
  });
@@ -2813,7 +2910,7 @@ function createQueries(table, agents, tools, contexts) {
2813
2910
  const requestedFields = getRequestedFields(info);
2814
2911
  const sanitizedFields = sanitizeRequestedFields(table, requestedFields);
2815
2912
  let query = db3.from(tableNamePlural).select(sanitizedFields);
2816
- query = applyFilters(query, filters);
2913
+ query = applyFilters(query, filters, table);
2817
2914
  query = applyAccessControl(table, context.user, query);
2818
2915
  query = applySorting(query, sort);
2819
2916
  let result = await query.first();
@@ -2826,7 +2923,7 @@ function createQueries(table, agents, tools, contexts) {
2826
2923
  throw new Error("Limit cannot be greater than 500.");
2827
2924
  }
2828
2925
  let countQuery = db3(tableNamePlural);
2829
- countQuery = applyFilters(countQuery, filters);
2926
+ countQuery = applyFilters(countQuery, filters, table);
2830
2927
  countQuery = applyAccessControl(table, context.user, countQuery);
2831
2928
  const countResult = await countQuery.count("* as count");
2832
2929
  const itemCount = Number(countResult[0]?.count || 0);
@@ -2835,7 +2932,7 @@ function createQueries(table, agents, tools, contexts) {
2835
2932
  const hasPreviousPage = currentPage > 1;
2836
2933
  const hasNextPage = currentPage < pageCount - 1;
2837
2934
  let dataQuery = db3(tableNamePlural);
2838
- dataQuery = applyFilters(dataQuery, filters);
2935
+ dataQuery = applyFilters(dataQuery, filters, table);
2839
2936
  dataQuery = applyAccessControl(table, context.user, dataQuery);
2840
2937
  const requestedFields = getRequestedFields(info);
2841
2938
  dataQuery = applySorting(dataQuery, sort);
@@ -2860,7 +2957,7 @@ function createQueries(table, agents, tools, contexts) {
2860
2957
  const { filters = [], groupBy } = args;
2861
2958
  const { db: db3 } = context;
2862
2959
  let query = db3(tableNamePlural);
2863
- query = applyFilters(query, filters);
2960
+ query = applyFilters(query, filters, table);
2864
2961
  query = applyAccessControl(table, context.user, query);
2865
2962
  if (groupBy) {
2866
2963
  query = query.select(groupBy).groupBy(groupBy);
@@ -2960,11 +3057,11 @@ var vectorSearch = async ({
2960
3057
  const mainTable = getTableName(id);
2961
3058
  const chunksTable = getChunksTableName(id);
2962
3059
  let countQuery = db3(mainTable);
2963
- countQuery = applyFilters(countQuery, filters);
3060
+ countQuery = applyFilters(countQuery, filters, table);
2964
3061
  countQuery = applyAccessControl(table, user, countQuery);
2965
3062
  const columns = await db3(mainTable).columnInfo();
2966
3063
  let itemsQuery = db3(mainTable).select(Object.keys(columns).map((column) => mainTable + "." + column));
2967
- itemsQuery = applyFilters(itemsQuery, filters);
3064
+ itemsQuery = applyFilters(itemsQuery, filters, table);
2968
3065
  itemsQuery = applyAccessControl(table, user, itemsQuery);
2969
3066
  itemsQuery = applySorting(itemsQuery, sort);
2970
3067
  if (queryRewriter) {
@@ -3725,7 +3822,7 @@ type PageInfo {
3725
3822
  const {
3726
3823
  jobs,
3727
3824
  count
3728
- } = await getJobsByQueueAndName(
3825
+ } = await getJobsByQueueName(
3729
3826
  args.queue,
3730
3827
  args.statusses,
3731
3828
  args.page || 1,
@@ -4158,7 +4255,7 @@ var validateCreateOrRemoveSuperAdminPermission = async (tableNamePlural, input,
4158
4255
  }
4159
4256
  }
4160
4257
  };
4161
- async function getJobsByQueueAndName(queueName, statusses, page, limit) {
4258
+ async function getJobsByQueueName(queueName, statusses, page, limit) {
4162
4259
  const queue = queues.list.get(queueName);
4163
4260
  if (!queue) {
4164
4261
  throw new Error(`Queue ${queueName} not found`);
@@ -5648,6 +5745,9 @@ var ExuluContext = class {
5648
5745
  };
5649
5746
  };
5650
5747
  createItem = async (item, config, user, role, upsert) => {
5748
+ if (upsert && (!item.id && !item.external_id)) {
5749
+ throw new Error("Item id or external id is required for upsert.");
5750
+ }
5651
5751
  const { db: db3 } = await postgresClient();
5652
5752
  const mutation = db3.from(getTableName(
5653
5753
  this.id
@@ -5932,6 +6032,7 @@ var ExuluContext = class {
5932
6032
  };
5933
6033
  };
5934
6034
  var updateStatistic = async (statistic) => {
6035
+ console.log("[EXULU] updating statistic", statistic);
5935
6036
  const currentDate = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
5936
6037
  const { db: db3 } = await postgresClient();
5937
6038
  const existing = await db3.from("tracking").where({
@@ -6075,6 +6176,8 @@ var {
6075
6176
  variablesSchema: variablesSchema2,
6076
6177
  workflowTemplatesSchema: workflowTemplatesSchema2,
6077
6178
  rbacSchema: rbacSchema2,
6179
+ promptLibrarySchema: promptLibrarySchema2,
6180
+ promptFavoritesSchema: promptFavoritesSchema2,
6078
6181
  statisticsSchema: statisticsSchema2
6079
6182
  } = coreSchemas.get();
6080
6183
  var createExpressRoutes = async (app, agents, tools, contexts, config, evals, tracer, queues2) => {
@@ -6105,6 +6208,8 @@ var createExpressRoutes = async (app, agents, tools, contexts, config, evals, tr
6105
6208
  agentsSchema2(),
6106
6209
  projectsSchema2(),
6107
6210
  jobResultsSchema2(),
6211
+ promptLibrarySchema2(),
6212
+ promptFavoritesSchema2(),
6108
6213
  evalRunsSchema2(),
6109
6214
  platformConfigurationsSchema2(),
6110
6215
  evalSetsSchema2(),
@@ -6515,7 +6620,7 @@ Mood: friendly and intelligent.
6515
6620
  return;
6516
6621
  }
6517
6622
  let project = null;
6518
- if (!req.body.project || req.body.project === "DEFAULT") {
6623
+ if (!req.params.project || req.params.project === "DEFAULT") {
6519
6624
  project = null;
6520
6625
  } else {
6521
6626
  let projectQuery = db3("projects");
@@ -6792,7 +6897,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
6792
6897
  }));
6793
6898
  const { db: db3 } = await postgresClient();
6794
6899
  const data = bullmqJob.data;
6795
- const timeoutMs = data.timeoutInSeconds * 1e3;
6900
+ const timeoutMs = (data.timeoutInSeconds || 300) * 1e3;
6796
6901
  const timeoutPromise = new Promise((_, reject) => {
6797
6902
  setTimeout(() => {
6798
6903
  reject(new Error(`Timeout for job ${bullmqJob.id} reached after ${data.timeoutInSeconds}s`));
@@ -6883,6 +6988,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
6883
6988
  const label = `eval-run-${data.eval_run_id}-${data.test_case_id}`;
6884
6989
  const existingResult = await db3.from("job_results").where({ label }).first();
6885
6990
  if (existingResult) {
6991
+ console.log("[EXULU] found existing job result, so ");
6886
6992
  await db3.from("job_results").where({ label }).update({
6887
6993
  job_id: bullmqJob.id,
6888
6994
  label,
@@ -6984,6 +7090,8 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
6984
7090
  test_case_id: testCase.id,
6985
7091
  eval_run_id: evalRun.id,
6986
7092
  eval_function_id: evalFunction.id,
7093
+ eval_function_name: evalFunction.name,
7094
+ eval_function_config: evalFunction.config || {},
6987
7095
  result: result2 || 0
6988
7096
  };
6989
7097
  console.log(`[EXULU] eval function ${evalFunction.id} result: ${result2}`, logMetadata2(bullmqJob.name, {
@@ -7011,22 +7119,32 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
7011
7119
  }
7012
7120
  }
7013
7121
  const scores = evalFunctionResults.map((result2) => result2.result);
7122
+ console.log("[EXULU] Exulu eval run scores for test case: " + testCase.id, scores);
7014
7123
  let score = 0;
7015
- switch (data.scoring_method) {
7124
+ switch (data.scoring_method?.toLowerCase()) {
7016
7125
  case "median":
7126
+ console.log("[EXULU] Calculating median score");
7017
7127
  score = getMedian(scores);
7018
7128
  break;
7019
7129
  case "average":
7130
+ console.log("[EXULU] Calculating average score");
7020
7131
  score = getAverage(scores);
7021
7132
  break;
7022
7133
  case "sum":
7134
+ console.log("[EXULU] Calculating sum score");
7023
7135
  score = getSum(scores);
7024
7136
  break;
7137
+ default:
7138
+ console.log("[EXULU] Calculating average score");
7139
+ score = getAverage(scores);
7025
7140
  }
7026
7141
  return {
7027
7142
  result: score,
7028
7143
  metadata: {
7029
- ...evalFunctionResults,
7144
+ messages,
7145
+ function_results: [
7146
+ ...evalFunctionResults
7147
+ ],
7030
7148
  ...metadata
7031
7149
  }
7032
7150
  };
@@ -7122,6 +7240,15 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
7122
7240
  }));
7123
7241
  }
7124
7242
  }
7243
+ await updateStatistic({
7244
+ name: "count",
7245
+ label: source.id,
7246
+ type: STATISTICS_TYPE_ENUM.SOURCE_UPDATE,
7247
+ trigger: "api",
7248
+ count: 1,
7249
+ user: data?.user,
7250
+ role: data?.role
7251
+ });
7125
7252
  return {
7126
7253
  result,
7127
7254
  metadata: {
@@ -7559,6 +7686,92 @@ var ExuluMCP = class {
7559
7686
  });
7560
7687
  server.tools[tool2.id] = tool2.name;
7561
7688
  }
7689
+ const getListOfPromptTemplatesName = "getListOfPromptTemplates";
7690
+ if (!server.tools[getListOfPromptTemplatesName]) {
7691
+ server.mcp.registerTool(getListOfPromptTemplatesName, {
7692
+ title: "Get List of Prompt Templates",
7693
+ description: "Retrieves a list of prompt templates available for this agent. Returns the name, description, and ID of each template.",
7694
+ inputSchema: {
7695
+ inputs: z2.object({})
7696
+ }
7697
+ }, async ({ inputs }, args) => {
7698
+ console.log("[EXULU] Getting list of prompt templates for agent", agentInstance.id);
7699
+ const { db: db4 } = await postgresClient();
7700
+ const prompts = await db4.from("prompt_library").select("id", "name", "description").whereRaw("assigned_agents @> ?::jsonb", [JSON.stringify(agentInstance.id)]).orderBy("updatedAt", "desc");
7701
+ console.log("[EXULU] Found", prompts.length, "prompt templates");
7702
+ return {
7703
+ content: [{
7704
+ type: "text",
7705
+ text: JSON.stringify({
7706
+ prompts: prompts.map((p) => ({
7707
+ id: p.id,
7708
+ name: p.name,
7709
+ description: p.description || "No description provided"
7710
+ })),
7711
+ count: prompts.length
7712
+ }, null, 2)
7713
+ }],
7714
+ structuredContent: {
7715
+ prompts: prompts.map((p) => ({
7716
+ id: p.id,
7717
+ name: p.name,
7718
+ description: p.description || "No description provided"
7719
+ })),
7720
+ count: prompts.length
7721
+ }
7722
+ };
7723
+ });
7724
+ }
7725
+ const getPromptTemplateDetailsName = "getPromptTemplateDetails";
7726
+ if (!server.tools[getPromptTemplateDetailsName]) {
7727
+ server.mcp.registerTool(getPromptTemplateDetailsName, {
7728
+ title: "Get Prompt Template Details",
7729
+ description: "Retrieves the full details of a specific prompt template by ID, including the actual template content with variables.",
7730
+ inputSchema: {
7731
+ inputs: z2.object({
7732
+ id: z2.string().describe("The ID of the prompt template to retrieve")
7733
+ })
7734
+ }
7735
+ }, async ({ inputs }, args) => {
7736
+ console.log("[EXULU] Getting prompt template details for ID", inputs.id);
7737
+ const { db: db4 } = await postgresClient();
7738
+ const prompt = await db4.from("prompt_library").select("id", "name", "description", "content", "createdAt", "updatedAt", "usage_count", "favorite_count").where({ id: inputs.id }).first();
7739
+ if (!prompt) {
7740
+ throw new Error(`Prompt template with ID ${inputs.id} not found`);
7741
+ }
7742
+ const isAssignedToAgent = await db4.from("prompt_library").select("id").where({ id: inputs.id }).whereRaw("assigned_agents @> ?::jsonb", [JSON.stringify(agentInstance.id)]).first();
7743
+ console.log("[EXULU] Prompt template found:", prompt.name);
7744
+ return {
7745
+ content: [{
7746
+ type: "text",
7747
+ text: JSON.stringify({
7748
+ id: prompt.id,
7749
+ name: prompt.name,
7750
+ description: prompt.description || "No description provided",
7751
+ content: prompt.content,
7752
+ createdAt: prompt.createdAt,
7753
+ updatedAt: prompt.updatedAt,
7754
+ usageCount: prompt.usage_count || 0,
7755
+ favoriteCount: prompt.favorite_count || 0,
7756
+ isAssignedToThisAgent: !!isAssignedToAgent
7757
+ }, null, 2)
7758
+ }],
7759
+ structuredContent: {
7760
+ id: prompt.id,
7761
+ name: prompt.name,
7762
+ description: prompt.description || "No description provided",
7763
+ content: prompt.content,
7764
+ createdAt: prompt.createdAt,
7765
+ updatedAt: prompt.updatedAt,
7766
+ usageCount: prompt.usage_count || 0,
7767
+ favoriteCount: prompt.favorite_count || 0,
7768
+ isAssignedToThisAgent: !!isAssignedToAgent
7769
+ }
7770
+ };
7771
+ });
7772
+ }
7773
+ server.tools[getListOfPromptTemplatesName] = getListOfPromptTemplatesName;
7774
+ server.tools[getPromptTemplateDetailsName] = getPromptTemplateDetailsName;
7562
7775
  return server.mcp;
7563
7776
  };
7564
7777
  create = async ({ express: express3, allTools, allAgents, allContexts, config }) => {
@@ -8173,8 +8386,8 @@ var llmAsJudgeEval = () => {
8173
8386
  console.log("[EXULU] running llm as judge eval", { agent, backend, messages, testCase, config });
8174
8387
  let prompt = config?.prompt;
8175
8388
  if (!prompt) {
8176
- console.error("[EXULU] prompt is required.");
8177
- throw new Error("Prompt is required.");
8389
+ console.error("[EXULU] prompt is required for llm as judge eval but none is provided.");
8390
+ throw new Error("Prompt is required for llm as judge eval but none is provided.");
8178
8391
  }
8179
8392
  const lastMessage = messages[messages.length - 1]?.parts?.filter((part) => part.type === "text").map((part) => part.text).join("\n");
8180
8393
  console.log("[EXULU] last message", lastMessage);
@@ -10639,7 +10852,9 @@ var {
10639
10852
  workflowTemplatesSchema: workflowTemplatesSchema3,
10640
10853
  rbacSchema: rbacSchema3,
10641
10854
  projectsSchema: projectsSchema3,
10642
- jobResultsSchema: jobResultsSchema3
10855
+ jobResultsSchema: jobResultsSchema3,
10856
+ promptLibrarySchema: promptLibrarySchema3,
10857
+ promptFavoritesSchema: promptFavoritesSchema3
10643
10858
  } = coreSchemas.get();
10644
10859
  var addMissingFields = async (knex, tableName, fields, skipFields = []) => {
10645
10860
  for (const field of fields) {
@@ -10674,6 +10889,8 @@ var up = async function(knex) {
10674
10889
  statisticsSchema3(),
10675
10890
  projectsSchema3(),
10676
10891
  jobResultsSchema3(),
10892
+ promptLibrarySchema3(),
10893
+ promptFavoritesSchema3(),
10677
10894
  rbacSchema3(),
10678
10895
  agentsSchema3(),
10679
10896
  variablesSchema3(),
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@exulu/backend",
3
3
  "author": "Qventu Bv.",
4
- "version": "1.33.0",
4
+ "version": "1.34.1",
5
5
  "main": "./dist/index.js",
6
6
  "private": false,
7
7
  "publishConfig": {
@@ -1,15 +1,18 @@
1
1
  export type ScoringMethod = "median" | "sum" | "average"
2
2
 
3
+ export interface EvalRunEvalFunction {
4
+ id: string
5
+ name: string,
6
+ config: Record<string, any>
7
+ }
8
+
3
9
  export interface EvalRun {
4
10
  id: string
5
11
  name: string
6
12
  eval_set_id: string
7
13
  agent_id: string
8
14
  timeout_in_seconds: number
9
- eval_functions: {
10
- id: string
11
- config: Record<string, any>
12
- }[]
15
+ eval_functions: EvalRunEvalFunction[]
13
16
  config?: Record<string, any> // Optional config for eval functions
14
17
  scoring_method: ScoringMethod
15
18
  pass_threshold: number // 0-100 percentage