@exulu/backend 1.27.2 → 1.28.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/.github/workflows/{release.yml → release-backend.yml} +2 -2
- package/.nvmrc +1 -1
- package/CHANGELOG.md +3 -3
- package/dist/index.cjs +1404 -535
- package/dist/index.d.cts +160 -27
- package/dist/index.d.ts +160 -27
- package/dist/index.js +1404 -536
- package/package.json +3 -3
- package/types/models/eval-run.ts +37 -0
- package/types/models/test-case.ts +25 -0
package/dist/index.cjs
CHANGED
|
@@ -40,14 +40,15 @@ __export(index_exports, {
|
|
|
40
40
|
ExuluDefaultAgents: () => ExuluDefaultAgents,
|
|
41
41
|
ExuluDefaultContexts: () => ExuluDefaultContexts,
|
|
42
42
|
ExuluEmbedder: () => ExuluEmbedder,
|
|
43
|
-
ExuluEval: () =>
|
|
43
|
+
ExuluEval: () => ExuluEval2,
|
|
44
44
|
ExuluJobs: () => ExuluJobs,
|
|
45
45
|
ExuluOtel: () => ExuluOtel,
|
|
46
46
|
ExuluQueues: () => queues,
|
|
47
47
|
ExuluTool: () => ExuluTool2,
|
|
48
48
|
ExuluUtils: () => ExuluUtils,
|
|
49
49
|
ExuluVariables: () => ExuluVariables,
|
|
50
|
-
db: () => db2
|
|
50
|
+
db: () => db2,
|
|
51
|
+
logMetadata: () => logMetadata
|
|
51
52
|
});
|
|
52
53
|
module.exports = __toCommonJS(index_exports);
|
|
53
54
|
var import_config = require("dotenv/config");
|
|
@@ -150,7 +151,17 @@ async function ensureDatabaseExists() {
|
|
|
150
151
|
database: "postgres",
|
|
151
152
|
// Connect to default database
|
|
152
153
|
password: process.env.POSTGRES_DB_PASSWORD,
|
|
153
|
-
ssl: process.env.POSTGRES_DB_SSL === "true" ? { rejectUnauthorized: false } : false
|
|
154
|
+
ssl: process.env.POSTGRES_DB_SSL === "true" ? { rejectUnauthorized: false } : false,
|
|
155
|
+
connectionTimeoutMillis: 1e4
|
|
156
|
+
},
|
|
157
|
+
pool: {
|
|
158
|
+
min: 2,
|
|
159
|
+
max: 4,
|
|
160
|
+
acquireTimeoutMillis: 3e4,
|
|
161
|
+
createTimeoutMillis: 3e4,
|
|
162
|
+
idleTimeoutMillis: 3e4,
|
|
163
|
+
reapIntervalMillis: 1e3,
|
|
164
|
+
createRetryIntervalMillis: 200
|
|
154
165
|
}
|
|
155
166
|
});
|
|
156
167
|
try {
|
|
@@ -195,7 +206,17 @@ async function postgresClient() {
|
|
|
195
206
|
user: process.env.POSTGRES_DB_USER,
|
|
196
207
|
database: dbName,
|
|
197
208
|
password: process.env.POSTGRES_DB_PASSWORD,
|
|
198
|
-
ssl: process.env.POSTGRES_DB_SSL === "true" ? { rejectUnauthorized: false } : false
|
|
209
|
+
ssl: process.env.POSTGRES_DB_SSL === "true" ? { rejectUnauthorized: false } : false,
|
|
210
|
+
connectionTimeoutMillis: 1e4
|
|
211
|
+
},
|
|
212
|
+
pool: {
|
|
213
|
+
min: 2,
|
|
214
|
+
max: 20,
|
|
215
|
+
acquireTimeoutMillis: 3e4,
|
|
216
|
+
createTimeoutMillis: 3e4,
|
|
217
|
+
idleTimeoutMillis: 3e4,
|
|
218
|
+
reapIntervalMillis: 1e3,
|
|
219
|
+
createRetryIntervalMillis: 200
|
|
199
220
|
}
|
|
200
221
|
});
|
|
201
222
|
try {
|
|
@@ -243,7 +264,9 @@ var bullmqDecorator = async ({
|
|
|
243
264
|
workflow,
|
|
244
265
|
item,
|
|
245
266
|
context,
|
|
246
|
-
retries
|
|
267
|
+
retries,
|
|
268
|
+
backoff,
|
|
269
|
+
timeoutInSeconds
|
|
247
270
|
}) => {
|
|
248
271
|
const types = [
|
|
249
272
|
embedder,
|
|
@@ -267,30 +290,35 @@ var bullmqDecorator = async ({
|
|
|
267
290
|
if (embedder) {
|
|
268
291
|
type = "embedder";
|
|
269
292
|
}
|
|
293
|
+
const jobData = {
|
|
294
|
+
label,
|
|
295
|
+
type: `${type}`,
|
|
296
|
+
timeoutInSeconds: timeoutInSeconds || 180,
|
|
297
|
+
// 3 minutes default
|
|
298
|
+
inputs,
|
|
299
|
+
...user && { user },
|
|
300
|
+
...role && { role },
|
|
301
|
+
...trigger && { trigger },
|
|
302
|
+
...workflow && { workflow },
|
|
303
|
+
...embedder && { embedder },
|
|
304
|
+
...processor && { processor },
|
|
305
|
+
...evaluation && { evaluation },
|
|
306
|
+
...item && { item },
|
|
307
|
+
...context && { context }
|
|
308
|
+
};
|
|
270
309
|
const redisId = (0, import_uuid.v4)();
|
|
271
310
|
const job = await queue.add(
|
|
272
311
|
`${embedder || workflow || processor || evaluation}`,
|
|
273
|
-
|
|
274
|
-
label,
|
|
275
|
-
type: `${type}`,
|
|
276
|
-
inputs,
|
|
277
|
-
...user && { user },
|
|
278
|
-
...role && { role },
|
|
279
|
-
...trigger && { trigger },
|
|
280
|
-
...workflow && { workflow },
|
|
281
|
-
...embedder && { embedder },
|
|
282
|
-
...processor && { processor },
|
|
283
|
-
...evaluation && { evaluation },
|
|
284
|
-
...item && { item },
|
|
285
|
-
...context && { context }
|
|
286
|
-
},
|
|
312
|
+
jobData,
|
|
287
313
|
{
|
|
288
314
|
jobId: redisId,
|
|
289
315
|
// Setting it to 3 as a sensible default, as
|
|
290
316
|
// many AI services are quite unstable.
|
|
291
317
|
attempts: retries || 3,
|
|
292
318
|
// todo make this configurable?
|
|
293
|
-
|
|
319
|
+
removeOnComplete: 5e3,
|
|
320
|
+
removeOnFail: 1e4,
|
|
321
|
+
backoff: backoff || {
|
|
294
322
|
type: "exponential",
|
|
295
323
|
delay: 2e3
|
|
296
324
|
}
|
|
@@ -735,6 +763,17 @@ var requestValidators = {
|
|
|
735
763
|
// src/registry/utils/graphql.ts
|
|
736
764
|
var import_bcryptjs3 = __toESM(require("bcryptjs"), 1);
|
|
737
765
|
|
|
766
|
+
// types/enums/jobs.ts
|
|
767
|
+
var JOB_STATUS_ENUM = {
|
|
768
|
+
completed: "completed",
|
|
769
|
+
failed: "failed",
|
|
770
|
+
delayed: "delayed",
|
|
771
|
+
active: "active",
|
|
772
|
+
waiting: "waiting",
|
|
773
|
+
paused: "paused",
|
|
774
|
+
stuck: "stuck"
|
|
775
|
+
};
|
|
776
|
+
|
|
738
777
|
// src/postgres/core-schema.ts
|
|
739
778
|
var agentMessagesSchema = {
|
|
740
779
|
type: "agent_messages",
|
|
@@ -1169,6 +1208,45 @@ var evalSetsSchema = {
|
|
|
1169
1208
|
}
|
|
1170
1209
|
]
|
|
1171
1210
|
};
|
|
1211
|
+
var jobResultsSchema = {
|
|
1212
|
+
type: "job_results",
|
|
1213
|
+
name: {
|
|
1214
|
+
plural: "job_results",
|
|
1215
|
+
singular: "job_result"
|
|
1216
|
+
},
|
|
1217
|
+
fields: [
|
|
1218
|
+
{
|
|
1219
|
+
name: "job_id",
|
|
1220
|
+
type: "text"
|
|
1221
|
+
},
|
|
1222
|
+
{
|
|
1223
|
+
name: "state",
|
|
1224
|
+
type: "text"
|
|
1225
|
+
},
|
|
1226
|
+
{
|
|
1227
|
+
name: "error",
|
|
1228
|
+
type: "json"
|
|
1229
|
+
},
|
|
1230
|
+
{
|
|
1231
|
+
name: "label",
|
|
1232
|
+
type: "text",
|
|
1233
|
+
index: true
|
|
1234
|
+
},
|
|
1235
|
+
{
|
|
1236
|
+
name: "tries",
|
|
1237
|
+
type: "number",
|
|
1238
|
+
default: 0
|
|
1239
|
+
},
|
|
1240
|
+
{
|
|
1241
|
+
name: "result",
|
|
1242
|
+
type: "json"
|
|
1243
|
+
},
|
|
1244
|
+
{
|
|
1245
|
+
name: "metadata",
|
|
1246
|
+
type: "json"
|
|
1247
|
+
}
|
|
1248
|
+
]
|
|
1249
|
+
};
|
|
1172
1250
|
var evalRunsSchema = {
|
|
1173
1251
|
type: "eval_runs",
|
|
1174
1252
|
name: {
|
|
@@ -1177,6 +1255,15 @@ var evalRunsSchema = {
|
|
|
1177
1255
|
},
|
|
1178
1256
|
RBAC: true,
|
|
1179
1257
|
fields: [
|
|
1258
|
+
{
|
|
1259
|
+
name: "name",
|
|
1260
|
+
type: "text"
|
|
1261
|
+
},
|
|
1262
|
+
{
|
|
1263
|
+
name: "timeout_in_seconds",
|
|
1264
|
+
type: "number",
|
|
1265
|
+
default: 180
|
|
1266
|
+
},
|
|
1180
1267
|
{
|
|
1181
1268
|
name: "eval_set_id",
|
|
1182
1269
|
type: "uuid",
|
|
@@ -1188,7 +1275,7 @@ var evalRunsSchema = {
|
|
|
1188
1275
|
required: true
|
|
1189
1276
|
},
|
|
1190
1277
|
{
|
|
1191
|
-
name: "
|
|
1278
|
+
name: "eval_functions",
|
|
1192
1279
|
type: "json",
|
|
1193
1280
|
required: true
|
|
1194
1281
|
},
|
|
@@ -1199,7 +1286,7 @@ var evalRunsSchema = {
|
|
|
1199
1286
|
{
|
|
1200
1287
|
name: "scoring_method",
|
|
1201
1288
|
type: "enum",
|
|
1202
|
-
enumValues: ["
|
|
1289
|
+
enumValues: ["median", "sum", "average"],
|
|
1203
1290
|
required: true
|
|
1204
1291
|
},
|
|
1205
1292
|
{
|
|
@@ -1323,7 +1410,8 @@ var coreSchemas = {
|
|
|
1323
1410
|
variablesSchema: () => addCoreFields(variablesSchema),
|
|
1324
1411
|
rbacSchema: () => addCoreFields(rbacSchema),
|
|
1325
1412
|
workflowTemplatesSchema: () => addCoreFields(workflowTemplatesSchema),
|
|
1326
|
-
platformConfigurationsSchema: () => addCoreFields(platformConfigurationsSchema)
|
|
1413
|
+
platformConfigurationsSchema: () => addCoreFields(platformConfigurationsSchema),
|
|
1414
|
+
jobResultsSchema: () => addCoreFields(jobResultsSchema)
|
|
1327
1415
|
};
|
|
1328
1416
|
}
|
|
1329
1417
|
};
|
|
@@ -1386,11 +1474,11 @@ var bullmq = {
|
|
|
1386
1474
|
if (!data.inputs) {
|
|
1387
1475
|
throw new Error(`Missing property "inputs" in data for job ${id}.`);
|
|
1388
1476
|
}
|
|
1389
|
-
if (data.type !== "embedder" && data.type !== "workflow") {
|
|
1390
|
-
throw new Error(`Property "type" in data for job ${id} must be of value "embedder" or "
|
|
1477
|
+
if (data.type !== "embedder" && data.type !== "workflow" && data.type !== "processor" && data.type !== "eval_run" && data.type !== "eval_function") {
|
|
1478
|
+
throw new Error(`Property "type" in data for job ${id} must be of value "embedder", "workflow", "processor", "eval_run" or "eval_function".`);
|
|
1391
1479
|
}
|
|
1392
|
-
if (!data.workflow && !data.embedder) {
|
|
1393
|
-
throw new Error(`Either a workflow or
|
|
1480
|
+
if (!data.workflow && !data.embedder && !data.processor && !data.eval_run_id && !data.eval_functions?.length) {
|
|
1481
|
+
throw new Error(`Either a workflow, embedder, processor, eval_run or eval_functions must be set for job ${id}.`);
|
|
1394
1482
|
}
|
|
1395
1483
|
}
|
|
1396
1484
|
};
|
|
@@ -1594,6 +1682,7 @@ var generateApiKey = async (name, email) => {
|
|
|
1594
1682
|
};
|
|
1595
1683
|
|
|
1596
1684
|
// src/registry/utils/graphql.ts
|
|
1685
|
+
var import_uuid2 = require("uuid");
|
|
1597
1686
|
var GraphQLDate = new import_graphql2.GraphQLScalarType({
|
|
1598
1687
|
name: "Date",
|
|
1599
1688
|
description: "Date custom scalar type",
|
|
@@ -1689,7 +1778,6 @@ ${enumValues}
|
|
|
1689
1778
|
fields.push(" maxContextLength: Int");
|
|
1690
1779
|
fields.push(" provider: String");
|
|
1691
1780
|
fields.push(" slug: String");
|
|
1692
|
-
fields.push(" evals: [AgentEvalFunction]");
|
|
1693
1781
|
}
|
|
1694
1782
|
const rbacField = table.RBAC ? " RBAC: RBACData" : "";
|
|
1695
1783
|
const typeDef = `
|
|
@@ -1901,11 +1989,6 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
1901
1989
|
if (!record) {
|
|
1902
1990
|
throw new Error("Record not found");
|
|
1903
1991
|
}
|
|
1904
|
-
if (tableNamePlural === "jobs") {
|
|
1905
|
-
if (!user.super_admin && record.created_by !== user.id) {
|
|
1906
|
-
throw new Error("You are not authorized to edit this record");
|
|
1907
|
-
}
|
|
1908
|
-
}
|
|
1909
1992
|
if (record.rights_mode === "public") {
|
|
1910
1993
|
return true;
|
|
1911
1994
|
}
|
|
@@ -2328,10 +2411,6 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2328
2411
|
}
|
|
2329
2412
|
var applyAccessControl = (table, user, query) => {
|
|
2330
2413
|
const tableNamePlural = table.name.plural.toLowerCase();
|
|
2331
|
-
if (!user.super_admin && table.name.plural === "jobs") {
|
|
2332
|
-
query = query.where("created_by", user.id);
|
|
2333
|
-
return query;
|
|
2334
|
-
}
|
|
2335
2414
|
if (table.name.plural !== "agent_sessions" && user.super_admin === true) {
|
|
2336
2415
|
return query;
|
|
2337
2416
|
}
|
|
@@ -2466,14 +2545,6 @@ var addAgentFields = async (requestedFields, agents, result, tools, user) => {
|
|
|
2466
2545
|
if (requestedFields.includes("provider")) {
|
|
2467
2546
|
result.provider = backend?.provider || "";
|
|
2468
2547
|
}
|
|
2469
|
-
if (requestedFields.includes("evals")) {
|
|
2470
|
-
result.evals = backend?.evals?.map((evalFunc) => ({
|
|
2471
|
-
id: evalFunc.id,
|
|
2472
|
-
name: evalFunc.name,
|
|
2473
|
-
description: evalFunc.description,
|
|
2474
|
-
config: evalFunc.config || []
|
|
2475
|
-
})) || [];
|
|
2476
|
-
}
|
|
2477
2548
|
if (!requestedFields.includes("backend")) {
|
|
2478
2549
|
delete result.backend;
|
|
2479
2550
|
}
|
|
@@ -3176,7 +3247,7 @@ var contextToTableDefinition = (context) => {
|
|
|
3176
3247
|
});
|
|
3177
3248
|
return addCoreFields(definition);
|
|
3178
3249
|
};
|
|
3179
|
-
function createSDL(tables, contexts, agents, tools, config) {
|
|
3250
|
+
function createSDL(tables, contexts, agents, tools, config, evals, queues3) {
|
|
3180
3251
|
const contextSchemas = contexts.map((context) => contextToTableDefinition(context));
|
|
3181
3252
|
tables.forEach((table) => {
|
|
3182
3253
|
if (!table.fields.some((field) => field.name === "createdAt")) {
|
|
@@ -3371,15 +3442,39 @@ type PageInfo {
|
|
|
3371
3442
|
typeDefs += `
|
|
3372
3443
|
providers: ProviderPaginationResult
|
|
3373
3444
|
`;
|
|
3445
|
+
typeDefs += `
|
|
3446
|
+
queue(queue: QueueEnum!): QueueResult
|
|
3447
|
+
`;
|
|
3448
|
+
typeDefs += `
|
|
3449
|
+
evals: EvalPaginationResult
|
|
3450
|
+
`;
|
|
3374
3451
|
typeDefs += `
|
|
3375
3452
|
contexts: ContextPaginationResult
|
|
3376
3453
|
`;
|
|
3377
3454
|
typeDefs += `
|
|
3378
3455
|
contextById(id: ID!): Context
|
|
3379
3456
|
`;
|
|
3457
|
+
mutationDefs += `
|
|
3458
|
+
runEval(id: ID!, test_case_ids: [ID!]): RunEvalReturnPayload
|
|
3459
|
+
`;
|
|
3460
|
+
mutationDefs += `
|
|
3461
|
+
drainQueue(queue: QueueEnum!): JobActionReturnPayload
|
|
3462
|
+
`;
|
|
3463
|
+
mutationDefs += `
|
|
3464
|
+
pauseQueue(queue: QueueEnum!): JobActionReturnPayload
|
|
3465
|
+
`;
|
|
3466
|
+
mutationDefs += `
|
|
3467
|
+
resumeQueue(queue: QueueEnum!): JobActionReturnPayload
|
|
3468
|
+
`;
|
|
3469
|
+
mutationDefs += `
|
|
3470
|
+
deleteJob(queue: QueueEnum!, id: String!): JobActionReturnPayload
|
|
3471
|
+
`;
|
|
3380
3472
|
typeDefs += `
|
|
3381
3473
|
tools: ToolPaginationResult
|
|
3382
3474
|
`;
|
|
3475
|
+
typeDefs += `
|
|
3476
|
+
jobs(queue: QueueEnum!, statusses: [JobStateEnum!]): JobPaginationResult
|
|
3477
|
+
`;
|
|
3383
3478
|
resolvers.Query["providers"] = async (_, args, context, info) => {
|
|
3384
3479
|
const requestedFields = getRequestedFields(info);
|
|
3385
3480
|
return {
|
|
@@ -3392,6 +3487,227 @@ type PageInfo {
|
|
|
3392
3487
|
})
|
|
3393
3488
|
};
|
|
3394
3489
|
};
|
|
3490
|
+
resolvers.Query["queue"] = async (_, args, context, info) => {
|
|
3491
|
+
if (!args.queue) {
|
|
3492
|
+
throw new Error("Queue name is required");
|
|
3493
|
+
}
|
|
3494
|
+
const queue = queues.list.get(args.queue);
|
|
3495
|
+
if (!queue) {
|
|
3496
|
+
throw new Error("Queue not found");
|
|
3497
|
+
}
|
|
3498
|
+
const config2 = await queue.use();
|
|
3499
|
+
return {
|
|
3500
|
+
name: config2.queue.name,
|
|
3501
|
+
concurrency: config2.concurrency,
|
|
3502
|
+
ratelimit: config2.ratelimit,
|
|
3503
|
+
isMaxed: await config2.queue.isMaxed(),
|
|
3504
|
+
isPaused: await config2.queue.isPaused(),
|
|
3505
|
+
jobs: {
|
|
3506
|
+
paused: await config2.queue.isPaused(),
|
|
3507
|
+
completed: await config2.queue.getJobCountByTypes("completed"),
|
|
3508
|
+
failed: await config2.queue.getJobCountByTypes("failed"),
|
|
3509
|
+
waiting: await config2.queue.getJobCountByTypes("waiting"),
|
|
3510
|
+
active: await config2.queue.getJobCountByTypes("active"),
|
|
3511
|
+
delayed: await config2.queue.getJobCountByTypes("delayed")
|
|
3512
|
+
}
|
|
3513
|
+
};
|
|
3514
|
+
};
|
|
3515
|
+
resolvers.Mutation["runEval"] = async (_, args, context, info) => {
|
|
3516
|
+
console.log("[EXULU] /evals/run/:id", args.id);
|
|
3517
|
+
const user = context.user;
|
|
3518
|
+
const eval_run_id = args.id;
|
|
3519
|
+
if (!user.super_admin && (!user.role || user.role.evals !== "write")) {
|
|
3520
|
+
throw new Error("You don't have permission to run evals. Required: super_admin or evals write access.");
|
|
3521
|
+
}
|
|
3522
|
+
const { db: db3 } = await postgresClient();
|
|
3523
|
+
const evalRun = await db3.from("eval_runs").where({ id: eval_run_id }).first();
|
|
3524
|
+
if (!evalRun) {
|
|
3525
|
+
throw new Error("Eval run not found in database.");
|
|
3526
|
+
}
|
|
3527
|
+
const hasAccessToEvalRun = await checkRecordAccess(evalRun, "write", user);
|
|
3528
|
+
if (!hasAccessToEvalRun) {
|
|
3529
|
+
throw new Error("You don't have access to this eval run.");
|
|
3530
|
+
}
|
|
3531
|
+
let testCaseIds = evalRun.test_case_ids ? typeof evalRun.test_case_ids === "string" ? JSON.parse(evalRun.test_case_ids) : evalRun.test_case_ids : [];
|
|
3532
|
+
const eval_functions = evalRun.eval_functions ? typeof evalRun.eval_functions === "string" ? JSON.parse(evalRun.eval_functions) : evalRun.eval_functions : [];
|
|
3533
|
+
if (!testCaseIds || testCaseIds.length === 0) {
|
|
3534
|
+
throw new Error("No test cases selected for this eval run.");
|
|
3535
|
+
}
|
|
3536
|
+
if (!eval_functions || eval_functions.length === 0) {
|
|
3537
|
+
throw new Error("No eval functions selected for this eval run.");
|
|
3538
|
+
}
|
|
3539
|
+
if (args.test_case_ids) {
|
|
3540
|
+
testCaseIds = testCaseIds.filter((testCase) => args.test_case_ids.includes(testCase));
|
|
3541
|
+
}
|
|
3542
|
+
console.log("[EXULU] test cases ids filtered", testCaseIds);
|
|
3543
|
+
const testCases = await db3.from("test_cases").whereIn("id", testCaseIds);
|
|
3544
|
+
if (testCases.length === 0) {
|
|
3545
|
+
throw new Error("No test cases found for eval run.");
|
|
3546
|
+
}
|
|
3547
|
+
const agentInstance = await loadAgent(evalRun.agent_id);
|
|
3548
|
+
if (!agentInstance) {
|
|
3549
|
+
throw new Error("Agent instance not found for eval run.");
|
|
3550
|
+
}
|
|
3551
|
+
const evalQueue = await queues.register("eval_runs", 1, 1).use();
|
|
3552
|
+
const jobIds = [];
|
|
3553
|
+
for (const testCase of testCases) {
|
|
3554
|
+
const jobData = {
|
|
3555
|
+
label: `Eval Run ${eval_run_id} - Test Case ${testCase.id}`,
|
|
3556
|
+
trigger: "api",
|
|
3557
|
+
timeoutInSeconds: evalRun.timeout_in_seconds || 180,
|
|
3558
|
+
// default to 3 minutes
|
|
3559
|
+
type: "eval_run",
|
|
3560
|
+
eval_run_id,
|
|
3561
|
+
eval_run_name: evalRun.name,
|
|
3562
|
+
test_case_id: testCase.id,
|
|
3563
|
+
test_case_name: testCase.name,
|
|
3564
|
+
eval_functions,
|
|
3565
|
+
// Array of eval function IDs - worker will create child jobs for these
|
|
3566
|
+
agent_id: evalRun.agent_id,
|
|
3567
|
+
inputs: testCase.inputs,
|
|
3568
|
+
expected_output: testCase.expected_output,
|
|
3569
|
+
expected_tools: testCase.expected_tools,
|
|
3570
|
+
expected_knowledge_sources: testCase.expected_knowledge_sources,
|
|
3571
|
+
expected_agent_tools: testCase.expected_agent_tools,
|
|
3572
|
+
config: evalRun.config,
|
|
3573
|
+
scoring_method: evalRun.scoring_method,
|
|
3574
|
+
pass_threshold: evalRun.pass_threshold,
|
|
3575
|
+
user: user.id,
|
|
3576
|
+
role: user.role?.id
|
|
3577
|
+
};
|
|
3578
|
+
const redisId = (0, import_uuid2.v4)();
|
|
3579
|
+
const job = await evalQueue.queue.add("eval_run", jobData, {
|
|
3580
|
+
jobId: redisId,
|
|
3581
|
+
// Setting it to 3 as a sensible default, as
|
|
3582
|
+
// many AI services are quite unstable.
|
|
3583
|
+
attempts: evalQueue.retries || 1,
|
|
3584
|
+
removeOnComplete: 5e3,
|
|
3585
|
+
removeOnFail: 1e4,
|
|
3586
|
+
backoff: evalQueue.backoff || {
|
|
3587
|
+
type: "exponential",
|
|
3588
|
+
delay: 2e3
|
|
3589
|
+
}
|
|
3590
|
+
});
|
|
3591
|
+
jobIds.push(job.id);
|
|
3592
|
+
}
|
|
3593
|
+
const response = {
|
|
3594
|
+
jobs: jobIds,
|
|
3595
|
+
count: jobIds.length
|
|
3596
|
+
};
|
|
3597
|
+
const requestedFields = getRequestedFields(info);
|
|
3598
|
+
const mapped = {};
|
|
3599
|
+
requestedFields.forEach((field) => {
|
|
3600
|
+
mapped[field] = response[field];
|
|
3601
|
+
});
|
|
3602
|
+
return mapped;
|
|
3603
|
+
};
|
|
3604
|
+
resolvers.Mutation["drainQueue"] = async (_, args, context, info) => {
|
|
3605
|
+
if (!args.queue) {
|
|
3606
|
+
throw new Error("Queue name is required");
|
|
3607
|
+
}
|
|
3608
|
+
const queue = queues.list.get(args.queue);
|
|
3609
|
+
if (!queue) {
|
|
3610
|
+
throw new Error("Queue not found");
|
|
3611
|
+
}
|
|
3612
|
+
const config2 = await queue.use();
|
|
3613
|
+
await config2.queue.drain();
|
|
3614
|
+
return { success: true };
|
|
3615
|
+
};
|
|
3616
|
+
resolvers.Mutation["pauseQueue"] = async (_, args, context, info) => {
|
|
3617
|
+
if (!args.queue) {
|
|
3618
|
+
throw new Error("Queue name is required");
|
|
3619
|
+
}
|
|
3620
|
+
const queue = queues.list.get(args.queue);
|
|
3621
|
+
if (!queue) {
|
|
3622
|
+
throw new Error("Queue not found");
|
|
3623
|
+
}
|
|
3624
|
+
const config2 = await queue.use();
|
|
3625
|
+
await config2.queue.pause();
|
|
3626
|
+
return { success: true };
|
|
3627
|
+
};
|
|
3628
|
+
resolvers.Mutation["resumeQueue"] = async (_, args, context, info) => {
|
|
3629
|
+
if (!args.queue) {
|
|
3630
|
+
throw new Error("Queue name is required");
|
|
3631
|
+
}
|
|
3632
|
+
const queue = queues.list.get(args.queue);
|
|
3633
|
+
if (!queue) {
|
|
3634
|
+
throw new Error("Queue not found");
|
|
3635
|
+
}
|
|
3636
|
+
const config2 = await queue.use();
|
|
3637
|
+
await config2.queue.resume();
|
|
3638
|
+
return { success: true };
|
|
3639
|
+
};
|
|
3640
|
+
resolvers.Mutation["deleteJob"] = async (_, args, context, info) => {
|
|
3641
|
+
if (!args.id) {
|
|
3642
|
+
throw new Error("Job ID is required");
|
|
3643
|
+
}
|
|
3644
|
+
if (!args.queue) {
|
|
3645
|
+
throw new Error("Queue name is required");
|
|
3646
|
+
}
|
|
3647
|
+
const queue = queues.list.get(args.queue);
|
|
3648
|
+
if (!queue) {
|
|
3649
|
+
throw new Error("Queue not found");
|
|
3650
|
+
}
|
|
3651
|
+
const config2 = await queue.use();
|
|
3652
|
+
await config2.queue.remove(args.id);
|
|
3653
|
+
return { success: true };
|
|
3654
|
+
};
|
|
3655
|
+
resolvers.Query["evals"] = async (_, args, context, info) => {
|
|
3656
|
+
const requestedFields = getRequestedFields(info);
|
|
3657
|
+
return {
|
|
3658
|
+
items: evals.map((_eval) => {
|
|
3659
|
+
const object = {};
|
|
3660
|
+
requestedFields.forEach((field) => {
|
|
3661
|
+
object[field] = _eval[field];
|
|
3662
|
+
});
|
|
3663
|
+
return object;
|
|
3664
|
+
})
|
|
3665
|
+
};
|
|
3666
|
+
};
|
|
3667
|
+
resolvers.Query["jobs"] = async (_, args, context, info) => {
|
|
3668
|
+
if (!args.queue) {
|
|
3669
|
+
throw new Error("Queue name is required");
|
|
3670
|
+
}
|
|
3671
|
+
const { client: client2 } = await redisClient();
|
|
3672
|
+
if (!client2) {
|
|
3673
|
+
throw new Error("Redis client not created properly");
|
|
3674
|
+
}
|
|
3675
|
+
const {
|
|
3676
|
+
jobs,
|
|
3677
|
+
count
|
|
3678
|
+
} = await getJobsByQueueAndName(
|
|
3679
|
+
args.queue,
|
|
3680
|
+
args.statusses,
|
|
3681
|
+
args.page || 1,
|
|
3682
|
+
args.limit || 100
|
|
3683
|
+
);
|
|
3684
|
+
console.log("[EXULU] jobs", jobs.map((job) => job.name));
|
|
3685
|
+
const requestedFields = getRequestedFields(info);
|
|
3686
|
+
return {
|
|
3687
|
+
items: await Promise.all(jobs.map(async (job) => {
|
|
3688
|
+
const object = {};
|
|
3689
|
+
for (const field of requestedFields) {
|
|
3690
|
+
if (field === "data") {
|
|
3691
|
+
object[field] = job[field];
|
|
3692
|
+
} else if (field === "timestamp") {
|
|
3693
|
+
object[field] = new Date(job[field]).toISOString();
|
|
3694
|
+
} else if (field === "state") {
|
|
3695
|
+
object[field] = await job.getState();
|
|
3696
|
+
} else {
|
|
3697
|
+
object[field] = job[field];
|
|
3698
|
+
}
|
|
3699
|
+
}
|
|
3700
|
+
return object;
|
|
3701
|
+
})),
|
|
3702
|
+
pageInfo: {
|
|
3703
|
+
pageCount: Math.ceil(count / (args.limit || 100)),
|
|
3704
|
+
itemCount: count,
|
|
3705
|
+
currentPage: args.page || 1,
|
|
3706
|
+
hasPreviousPage: args.page && args.page > 1 ? true : false,
|
|
3707
|
+
hasNextPage: args.page && args.page < Math.ceil(jobs.length / (args.limit || 100)) ? true : false
|
|
3708
|
+
}
|
|
3709
|
+
};
|
|
3710
|
+
};
|
|
3395
3711
|
resolvers.Query["contexts"] = async (_, args, context, info) => {
|
|
3396
3712
|
const data = contexts.map((context2) => ({
|
|
3397
3713
|
id: context2.id,
|
|
@@ -3497,7 +3813,32 @@ type PageInfo {
|
|
|
3497
3813
|
};
|
|
3498
3814
|
modelDefs += `
|
|
3499
3815
|
type ProviderPaginationResult {
|
|
3500
|
-
|
|
3816
|
+
items: [Provider]!
|
|
3817
|
+
}
|
|
3818
|
+
`;
|
|
3819
|
+
modelDefs += `
|
|
3820
|
+
type QueueResult {
|
|
3821
|
+
name: String!
|
|
3822
|
+
concurrency: Int!
|
|
3823
|
+
ratelimit: Int!
|
|
3824
|
+
isMaxed: Boolean!
|
|
3825
|
+
isPaused: Boolean!
|
|
3826
|
+
jobs: QueueJobsCounts
|
|
3827
|
+
}
|
|
3828
|
+
`;
|
|
3829
|
+
modelDefs += `
|
|
3830
|
+
type QueueJobsCounts {
|
|
3831
|
+
paused: Int!
|
|
3832
|
+
completed: Int!
|
|
3833
|
+
failed: Int!
|
|
3834
|
+
waiting: Int!
|
|
3835
|
+
active: Int!
|
|
3836
|
+
delayed: Int!
|
|
3837
|
+
}
|
|
3838
|
+
`;
|
|
3839
|
+
modelDefs += `
|
|
3840
|
+
type EvalPaginationResult {
|
|
3841
|
+
items: [Eval]!
|
|
3501
3842
|
}
|
|
3502
3843
|
`;
|
|
3503
3844
|
modelDefs += `
|
|
@@ -3510,6 +3851,12 @@ type PageInfo {
|
|
|
3510
3851
|
items: [Tool]!
|
|
3511
3852
|
}
|
|
3512
3853
|
`;
|
|
3854
|
+
modelDefs += `
|
|
3855
|
+
type JobPaginationResult {
|
|
3856
|
+
items: [Job]!
|
|
3857
|
+
pageInfo: PageInfo!
|
|
3858
|
+
}
|
|
3859
|
+
`;
|
|
3513
3860
|
typeDefs += "}\n";
|
|
3514
3861
|
mutationDefs += "}\n";
|
|
3515
3862
|
const genericTypes = `
|
|
@@ -3567,6 +3914,19 @@ type Provider {
|
|
|
3567
3914
|
type: EnumProviderType!
|
|
3568
3915
|
}
|
|
3569
3916
|
|
|
3917
|
+
type Eval {
|
|
3918
|
+
id: ID!
|
|
3919
|
+
name: String!
|
|
3920
|
+
description: String!
|
|
3921
|
+
llm: Boolean!
|
|
3922
|
+
config: [EvalConfig!]
|
|
3923
|
+
}
|
|
3924
|
+
|
|
3925
|
+
type EvalConfig {
|
|
3926
|
+
name: String!
|
|
3927
|
+
description: String!
|
|
3928
|
+
}
|
|
3929
|
+
|
|
3570
3930
|
type Context {
|
|
3571
3931
|
id: ID!
|
|
3572
3932
|
name: String!
|
|
@@ -3578,6 +3938,15 @@ type Context {
|
|
|
3578
3938
|
configuration: JSON
|
|
3579
3939
|
}
|
|
3580
3940
|
|
|
3941
|
+
type RunEvalReturnPayload {
|
|
3942
|
+
jobs: [String!]!
|
|
3943
|
+
count: Int!
|
|
3944
|
+
}
|
|
3945
|
+
|
|
3946
|
+
type JobActionReturnPayload {
|
|
3947
|
+
success: Boolean!
|
|
3948
|
+
}
|
|
3949
|
+
|
|
3581
3950
|
type ContextField {
|
|
3582
3951
|
name: String!
|
|
3583
3952
|
type: String!
|
|
@@ -3593,10 +3962,35 @@ type Tool {
|
|
|
3593
3962
|
config: JSON
|
|
3594
3963
|
}
|
|
3595
3964
|
|
|
3965
|
+
type Job {
|
|
3966
|
+
id: String!
|
|
3967
|
+
name: String!
|
|
3968
|
+
returnvalue: JSON
|
|
3969
|
+
stacktrace: [String]
|
|
3970
|
+
failedReason: String
|
|
3971
|
+
state: String!
|
|
3972
|
+
data: JSON
|
|
3973
|
+
timestamp: Date
|
|
3974
|
+
}
|
|
3975
|
+
|
|
3596
3976
|
enum EnumProviderType {
|
|
3597
3977
|
agent
|
|
3598
3978
|
}
|
|
3599
3979
|
|
|
3980
|
+
enum QueueEnum {
|
|
3981
|
+
${queues.list.keys().toArray().join("\n")}
|
|
3982
|
+
}
|
|
3983
|
+
|
|
3984
|
+
enum JobStateEnum {
|
|
3985
|
+
${JOB_STATUS_ENUM.active}
|
|
3986
|
+
${JOB_STATUS_ENUM.waiting}
|
|
3987
|
+
${JOB_STATUS_ENUM.delayed}
|
|
3988
|
+
${JOB_STATUS_ENUM.failed}
|
|
3989
|
+
${JOB_STATUS_ENUM.completed}
|
|
3990
|
+
${JOB_STATUS_ENUM.paused}
|
|
3991
|
+
${JOB_STATUS_ENUM.stuck}
|
|
3992
|
+
}
|
|
3993
|
+
|
|
3600
3994
|
type StatisticsResult {
|
|
3601
3995
|
group: String!
|
|
3602
3996
|
count: Int!
|
|
@@ -3626,6 +4020,25 @@ var validateCreateOrRemoveSuperAdminPermission = async (tableNamePlural, input,
|
|
|
3626
4020
|
}
|
|
3627
4021
|
}
|
|
3628
4022
|
};
|
|
4023
|
+
async function getJobsByQueueAndName(queueName, statusses, page, limit) {
|
|
4024
|
+
const queue = queues.list.get(queueName);
|
|
4025
|
+
if (!queue) {
|
|
4026
|
+
throw new Error(`Queue ${queueName} not found`);
|
|
4027
|
+
}
|
|
4028
|
+
const config = await queue.use();
|
|
4029
|
+
const startIndex = (page || 1) - 1;
|
|
4030
|
+
const endIndex = startIndex + (limit || 100);
|
|
4031
|
+
const jobs = await config.queue.getJobs(statusses || [], startIndex, endIndex, false);
|
|
4032
|
+
const counts = await config.queue.getJobCounts(...statusses || []);
|
|
4033
|
+
let total = 0;
|
|
4034
|
+
if (counts) {
|
|
4035
|
+
total = Object.keys(counts).reduce((acc, key) => acc + (counts[key] || 0), 0);
|
|
4036
|
+
}
|
|
4037
|
+
return {
|
|
4038
|
+
jobs,
|
|
4039
|
+
count: total
|
|
4040
|
+
};
|
|
4041
|
+
}
|
|
3629
4042
|
|
|
3630
4043
|
// src/registry/classes.ts
|
|
3631
4044
|
var import_client_s32 = require("@aws-sdk/client-s3");
|
|
@@ -3675,6 +4088,7 @@ var addPrefixToKey = (keyPath, config) => {
|
|
|
3675
4088
|
return `${prefix}/${keyPath}`;
|
|
3676
4089
|
};
|
|
3677
4090
|
var uploadFile = async (user, file, key, config, options = {}) => {
|
|
4091
|
+
console.log("[EXULU] Uploading file to S3", key);
|
|
3678
4092
|
const client2 = getS3Client(config);
|
|
3679
4093
|
let folder = `${user}/`;
|
|
3680
4094
|
const fullKey = addPrefixToKey(!key.includes(folder) ? folder + key : key, config);
|
|
@@ -4288,27 +4702,34 @@ function errorHandler(error) {
|
|
|
4288
4702
|
}
|
|
4289
4703
|
return JSON.stringify(error);
|
|
4290
4704
|
}
|
|
4291
|
-
var
|
|
4705
|
+
var ExuluEval2 = class {
|
|
4292
4706
|
id;
|
|
4293
4707
|
name;
|
|
4294
4708
|
description;
|
|
4709
|
+
llm;
|
|
4295
4710
|
execute;
|
|
4296
4711
|
config;
|
|
4297
4712
|
queue;
|
|
4298
|
-
constructor({ id, name, description, execute: execute2, config, queue }) {
|
|
4713
|
+
constructor({ id, name, description, execute: execute2, config, queue, llm }) {
|
|
4299
4714
|
this.id = id;
|
|
4300
4715
|
this.name = name;
|
|
4301
4716
|
this.description = description;
|
|
4302
4717
|
this.execute = execute2;
|
|
4303
4718
|
this.config = config;
|
|
4719
|
+
this.llm = llm;
|
|
4304
4720
|
this.queue = queue;
|
|
4305
4721
|
}
|
|
4306
|
-
async run(
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4722
|
+
async run(agent, backend, testCase, messages, config) {
|
|
4723
|
+
try {
|
|
4724
|
+
const score = await this.execute({ agent, backend, testCase, messages, config });
|
|
4725
|
+
if (score < 0 || score > 100) {
|
|
4726
|
+
throw new Error(`Eval function ${this.name} must return a score between 0 and 100, got ${score}`);
|
|
4727
|
+
}
|
|
4728
|
+
return score;
|
|
4729
|
+
} catch (error) {
|
|
4730
|
+
console.error(`[EXULU] error running eval function ${this.name}.`, error);
|
|
4731
|
+
throw new Error(`Error running eval function ${this.name}: ${error instanceof Error ? error.message : String(error)}`);
|
|
4310
4732
|
}
|
|
4311
|
-
return score;
|
|
4312
4733
|
}
|
|
4313
4734
|
};
|
|
4314
4735
|
var ExuluAgent2 = class {
|
|
@@ -4323,13 +4744,13 @@ var ExuluAgent2 = class {
|
|
|
4323
4744
|
type;
|
|
4324
4745
|
streaming = false;
|
|
4325
4746
|
maxContextLength;
|
|
4747
|
+
queue;
|
|
4326
4748
|
rateLimit;
|
|
4327
4749
|
config;
|
|
4328
|
-
evals;
|
|
4329
4750
|
// private memory: Memory | undefined; // TODO do own implementation
|
|
4330
4751
|
model;
|
|
4331
4752
|
capabilities;
|
|
4332
|
-
constructor({ id, name, description, config, rateLimit, capabilities, type, maxContextLength,
|
|
4753
|
+
constructor({ id, name, description, config, rateLimit, capabilities, type, maxContextLength, provider, queue }) {
|
|
4333
4754
|
this.id = id;
|
|
4334
4755
|
this.name = name;
|
|
4335
4756
|
this.description = description;
|
|
@@ -4338,7 +4759,7 @@ var ExuluAgent2 = class {
|
|
|
4338
4759
|
this.config = config;
|
|
4339
4760
|
this.type = type;
|
|
4340
4761
|
this.maxContextLength = maxContextLength;
|
|
4341
|
-
this.
|
|
4762
|
+
this.queue = queue;
|
|
4342
4763
|
this.capabilities = capabilities || {
|
|
4343
4764
|
text: false,
|
|
4344
4765
|
images: [],
|
|
@@ -4437,7 +4858,7 @@ var ExuluAgent2 = class {
|
|
|
4437
4858
|
prompt,
|
|
4438
4859
|
user,
|
|
4439
4860
|
session,
|
|
4440
|
-
|
|
4861
|
+
inputMessages,
|
|
4441
4862
|
currentTools,
|
|
4442
4863
|
allExuluTools,
|
|
4443
4864
|
statistics,
|
|
@@ -4455,10 +4876,10 @@ var ExuluAgent2 = class {
|
|
|
4455
4876
|
if (!this.config) {
|
|
4456
4877
|
throw new Error("Config is required for generating.");
|
|
4457
4878
|
}
|
|
4458
|
-
if (prompt &&
|
|
4879
|
+
if (prompt && inputMessages?.length) {
|
|
4459
4880
|
throw new Error("Message and prompt cannot be provided at the same time.");
|
|
4460
4881
|
}
|
|
4461
|
-
if (!prompt && !
|
|
4882
|
+
if (!prompt && !inputMessages?.length) {
|
|
4462
4883
|
throw new Error("Prompt or message is required for generating.");
|
|
4463
4884
|
}
|
|
4464
4885
|
if (outputSchema && !prompt) {
|
|
@@ -4468,18 +4889,18 @@ var ExuluAgent2 = class {
|
|
|
4468
4889
|
apiKey: providerapikey
|
|
4469
4890
|
});
|
|
4470
4891
|
console.log("[EXULU] Model for agent: " + this.name, " created for generating sync.");
|
|
4471
|
-
let messages = [];
|
|
4472
|
-
if (
|
|
4892
|
+
let messages = inputMessages || [];
|
|
4893
|
+
if (messages && session && user) {
|
|
4473
4894
|
const previousMessages = await getAgentMessages({
|
|
4474
4895
|
session,
|
|
4475
4896
|
user: user.id,
|
|
4476
4897
|
limit: 50,
|
|
4477
4898
|
page: 1
|
|
4478
4899
|
});
|
|
4479
|
-
const previousMessagesContent = previousMessages.map((
|
|
4900
|
+
const previousMessagesContent = previousMessages.map((message) => JSON.parse(message.content));
|
|
4480
4901
|
messages = await (0, import_ai.validateUIMessages)({
|
|
4481
4902
|
// append the new message to the previous messages:
|
|
4482
|
-
messages: [...previousMessagesContent,
|
|
4903
|
+
messages: [...previousMessagesContent, ...messages]
|
|
4483
4904
|
});
|
|
4484
4905
|
}
|
|
4485
4906
|
console.log("[EXULU] Message count for agent: " + this.name, "loaded for generating sync.", messages.length);
|
|
@@ -4615,13 +5036,12 @@ var ExuluAgent2 = class {
|
|
|
4615
5036
|
return "";
|
|
4616
5037
|
};
|
|
4617
5038
|
generateStream = async ({
|
|
4618
|
-
express: express3,
|
|
4619
5039
|
user,
|
|
4620
5040
|
session,
|
|
4621
5041
|
message,
|
|
5042
|
+
previousMessages,
|
|
4622
5043
|
currentTools,
|
|
4623
5044
|
allExuluTools,
|
|
4624
|
-
statistics,
|
|
4625
5045
|
toolConfigs,
|
|
4626
5046
|
providerapikey,
|
|
4627
5047
|
contexts,
|
|
@@ -4629,27 +5049,34 @@ var ExuluAgent2 = class {
|
|
|
4629
5049
|
instructions
|
|
4630
5050
|
}) => {
|
|
4631
5051
|
if (!this.model) {
|
|
5052
|
+
console.error("[EXULU] Model is required for streaming.");
|
|
4632
5053
|
throw new Error("Model is required for streaming.");
|
|
4633
5054
|
}
|
|
4634
5055
|
if (!this.config) {
|
|
5056
|
+
console.error("[EXULU] Config is required for streaming.");
|
|
4635
5057
|
throw new Error("Config is required for generating.");
|
|
4636
5058
|
}
|
|
4637
5059
|
if (!message) {
|
|
5060
|
+
console.error("[EXULU] Message is required for streaming.");
|
|
4638
5061
|
throw new Error("Message is required for streaming.");
|
|
4639
5062
|
}
|
|
4640
5063
|
const model = this.model.create({
|
|
4641
5064
|
apiKey: providerapikey
|
|
4642
5065
|
});
|
|
4643
5066
|
let messages = [];
|
|
4644
|
-
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
5067
|
+
let previousMessagesContent = previousMessages || [];
|
|
5068
|
+
if (session) {
|
|
5069
|
+
console.log("[EXULU] loading previous messages from session: " + session);
|
|
5070
|
+
const previousMessages2 = await getAgentMessages({
|
|
5071
|
+
session,
|
|
5072
|
+
user: user.id,
|
|
5073
|
+
limit: 50,
|
|
5074
|
+
page: 1
|
|
5075
|
+
});
|
|
5076
|
+
previousMessagesContent = previousMessages2.map(
|
|
5077
|
+
(message2) => JSON.parse(message2.content)
|
|
5078
|
+
);
|
|
5079
|
+
}
|
|
4653
5080
|
messages = await (0, import_ai.validateUIMessages)({
|
|
4654
5081
|
// append the new message to the previous messages:
|
|
4655
5082
|
messages: [...previousMessagesContent, message]
|
|
@@ -4682,80 +5109,17 @@ var ExuluAgent2 = class {
|
|
|
4682
5109
|
user,
|
|
4683
5110
|
exuluConfig
|
|
4684
5111
|
),
|
|
4685
|
-
onError: (error) => console.error("[EXULU] chat stream error.", error)
|
|
4686
|
-
// stopWhen: [stepCountIs(1)],
|
|
4687
|
-
});
|
|
4688
|
-
result.consumeStream();
|
|
4689
|
-
result.pipeUIMessageStreamToResponse(express3.res, {
|
|
4690
|
-
messageMetadata: ({ part }) => {
|
|
4691
|
-
if (part.type === "finish") {
|
|
4692
|
-
return {
|
|
4693
|
-
totalTokens: part.totalUsage.totalTokens,
|
|
4694
|
-
reasoningTokens: part.totalUsage.reasoningTokens,
|
|
4695
|
-
inputTokens: part.totalUsage.inputTokens,
|
|
4696
|
-
outputTokens: part.totalUsage.outputTokens,
|
|
4697
|
-
cachedInputTokens: part.totalUsage.cachedInputTokens
|
|
4698
|
-
};
|
|
4699
|
-
}
|
|
4700
|
-
},
|
|
4701
|
-
originalMessages: messages,
|
|
4702
|
-
sendReasoning: true,
|
|
4703
|
-
sendSources: true,
|
|
4704
5112
|
onError: (error) => {
|
|
4705
|
-
console.error("[EXULU] chat
|
|
4706
|
-
|
|
4707
|
-
},
|
|
4708
|
-
generateMessageId: (0, import_ai.createIdGenerator)({
|
|
4709
|
-
prefix: "msg_",
|
|
4710
|
-
size: 16
|
|
4711
|
-
}),
|
|
4712
|
-
onFinish: async ({ messages: messages2, isContinuation, isAborted, responseMessage }) => {
|
|
4713
|
-
if (session) {
|
|
4714
|
-
await saveChat({
|
|
4715
|
-
session,
|
|
4716
|
-
user: user.id,
|
|
4717
|
-
messages: messages2.filter((x) => !previousMessagesContent.find((y) => y.id === x.id))
|
|
4718
|
-
});
|
|
4719
|
-
}
|
|
4720
|
-
const metadata = messages2[messages2.length - 1]?.metadata;
|
|
4721
|
-
console.log("[EXULU] Finished streaming", metadata);
|
|
4722
|
-
console.log("[EXULU] Statistics", statistics);
|
|
4723
|
-
if (statistics) {
|
|
4724
|
-
await Promise.all([
|
|
4725
|
-
updateStatistic({
|
|
4726
|
-
name: "count",
|
|
4727
|
-
label: statistics.label,
|
|
4728
|
-
type: STATISTICS_TYPE_ENUM.AGENT_RUN,
|
|
4729
|
-
trigger: statistics.trigger,
|
|
4730
|
-
count: 1,
|
|
4731
|
-
user: user.id,
|
|
4732
|
-
role: user?.role?.id
|
|
4733
|
-
}),
|
|
4734
|
-
...metadata?.inputTokens ? [
|
|
4735
|
-
updateStatistic({
|
|
4736
|
-
name: "inputTokens",
|
|
4737
|
-
label: statistics.label,
|
|
4738
|
-
type: STATISTICS_TYPE_ENUM.AGENT_RUN,
|
|
4739
|
-
trigger: statistics.trigger,
|
|
4740
|
-
count: metadata?.inputTokens,
|
|
4741
|
-
user: user.id,
|
|
4742
|
-
role: user?.role?.id
|
|
4743
|
-
})
|
|
4744
|
-
] : [],
|
|
4745
|
-
...metadata?.outputTokens ? [
|
|
4746
|
-
updateStatistic({
|
|
4747
|
-
name: "outputTokens",
|
|
4748
|
-
label: statistics.label,
|
|
4749
|
-
type: STATISTICS_TYPE_ENUM.AGENT_RUN,
|
|
4750
|
-
trigger: statistics.trigger,
|
|
4751
|
-
count: metadata?.outputTokens
|
|
4752
|
-
})
|
|
4753
|
-
] : []
|
|
4754
|
-
]);
|
|
4755
|
-
}
|
|
5113
|
+
console.error("[EXULU] chat stream error.", error);
|
|
5114
|
+
throw new Error(`Chat stream error: ${error instanceof Error ? error.message : String(error)}`);
|
|
4756
5115
|
}
|
|
5116
|
+
// stopWhen: [stepCountIs(1)],
|
|
4757
5117
|
});
|
|
4758
|
-
return
|
|
5118
|
+
return {
|
|
5119
|
+
stream: result,
|
|
5120
|
+
originalMessages: messages,
|
|
5121
|
+
previousMessages: previousMessagesContent
|
|
5122
|
+
};
|
|
4759
5123
|
};
|
|
4760
5124
|
};
|
|
4761
5125
|
var getAgentMessages = async ({ session, user, limit, page }) => {
|
|
@@ -4946,16 +5310,21 @@ var ExuluContext = class {
|
|
|
4946
5310
|
if (queue?.queue.name) {
|
|
4947
5311
|
console.log("[EXULU] processor is in queue mode, scheduling job.");
|
|
4948
5312
|
const job = await bullmqDecorator({
|
|
5313
|
+
timeoutInSeconds: field.processor?.config?.timeoutInSeconds || 180,
|
|
4949
5314
|
label: `${this.name} ${field.name} data processor`,
|
|
4950
5315
|
processor: `${this.id}-${field.name}`,
|
|
4951
5316
|
context: this.id,
|
|
4952
5317
|
inputs: item,
|
|
4953
5318
|
item: item.id,
|
|
4954
5319
|
queue: queue.queue,
|
|
5320
|
+
backoff: queue.backoff || {
|
|
5321
|
+
type: "exponential",
|
|
5322
|
+
delay: 2e3
|
|
5323
|
+
},
|
|
5324
|
+
retries: queue.retries || 2,
|
|
4955
5325
|
user,
|
|
4956
5326
|
role,
|
|
4957
|
-
trigger
|
|
4958
|
-
retries: 2
|
|
5327
|
+
trigger
|
|
4959
5328
|
});
|
|
4960
5329
|
return {
|
|
4961
5330
|
result: "",
|
|
@@ -5025,12 +5394,14 @@ var ExuluContext = class {
|
|
|
5025
5394
|
role
|
|
5026
5395
|
);
|
|
5027
5396
|
await db3.from(getChunksTableName(this.id)).where({ source }).delete();
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5397
|
+
if (chunks?.length) {
|
|
5398
|
+
await db3.from(getChunksTableName(this.id)).insert(chunks.map((chunk) => ({
|
|
5399
|
+
source,
|
|
5400
|
+
content: chunk.content,
|
|
5401
|
+
chunk_index: chunk.index,
|
|
5402
|
+
embedding: import_knex5.default.toSql(chunk.vector)
|
|
5403
|
+
})));
|
|
5404
|
+
}
|
|
5034
5405
|
await db3.from(getTableName(this.id)).where({ id: item.id }).update({
|
|
5035
5406
|
embeddings_updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
5036
5407
|
}).returning("id");
|
|
@@ -5159,9 +5530,15 @@ var ExuluContext = class {
|
|
|
5159
5530
|
if (queue?.queue.name) {
|
|
5160
5531
|
console.log("[EXULU] embedder is in queue mode, scheduling job.");
|
|
5161
5532
|
const job = await bullmqDecorator({
|
|
5533
|
+
timeoutInSeconds: queue.timeoutInSeconds || 180,
|
|
5162
5534
|
label: `${this.embedder.name}`,
|
|
5163
5535
|
embedder: this.embedder.id,
|
|
5164
5536
|
context: this.id,
|
|
5537
|
+
backoff: queue.backoff || {
|
|
5538
|
+
type: "exponential",
|
|
5539
|
+
delay: 2e3
|
|
5540
|
+
},
|
|
5541
|
+
retries: queue.retries || 2,
|
|
5165
5542
|
inputs: item,
|
|
5166
5543
|
item: item.id,
|
|
5167
5544
|
queue: queue.queue,
|
|
@@ -5413,6 +5790,8 @@ var ExuluQueues = class {
|
|
|
5413
5790
|
constructor() {
|
|
5414
5791
|
this.queues = [];
|
|
5415
5792
|
}
|
|
5793
|
+
list = /* @__PURE__ */ new Map();
|
|
5794
|
+
// list of queue names
|
|
5416
5795
|
queue(name) {
|
|
5417
5796
|
return this.queues.find((x) => x.queue?.name === name);
|
|
5418
5797
|
}
|
|
@@ -5424,40 +5803,54 @@ var ExuluQueues = class {
|
|
|
5424
5803
|
// method of ExuluQueues we need to store the desired rate limit on the queue
|
|
5425
5804
|
// here so we can use the value when creating workers for the queue instance
|
|
5426
5805
|
// as there is no way to store a rate limit value natively on a bullm queue.
|
|
5427
|
-
|
|
5428
|
-
const
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5806
|
+
register = (name, concurrency = 1, ratelimit = 1) => {
|
|
5807
|
+
const use = async () => {
|
|
5808
|
+
const existing = this.queues.find((x) => x.queue?.name === name);
|
|
5809
|
+
if (existing) {
|
|
5810
|
+
const globalConcurrency = await existing.queue.getGlobalConcurrency();
|
|
5811
|
+
if (globalConcurrency !== concurrency) {
|
|
5812
|
+
await existing.queue.setGlobalConcurrency(concurrency);
|
|
5813
|
+
}
|
|
5814
|
+
return {
|
|
5815
|
+
queue: existing.queue,
|
|
5816
|
+
ratelimit,
|
|
5817
|
+
concurrency
|
|
5818
|
+
};
|
|
5433
5819
|
}
|
|
5434
|
-
|
|
5435
|
-
queue
|
|
5436
|
-
|
|
5437
|
-
concurrency
|
|
5438
|
-
};
|
|
5439
|
-
}
|
|
5440
|
-
if (!redisServer.host?.length || !redisServer.port?.length) {
|
|
5441
|
-
console.error(`[EXULU] no redis server configured, but you are trying to use a queue ( ${name}), likely in an agent or embedder (look for ExuluQueues.use() ).`);
|
|
5442
|
-
throw new Error(`[EXULU] no redis server configured.`);
|
|
5443
|
-
}
|
|
5444
|
-
const newQueue = new import_bullmq4.Queue(
|
|
5445
|
-
`${name}`,
|
|
5446
|
-
{
|
|
5447
|
-
connection: redisServer,
|
|
5448
|
-
telemetry: new import_bullmq_otel.BullMQOtel("simple-guide")
|
|
5820
|
+
if (!redisServer.host?.length || !redisServer.port?.length) {
|
|
5821
|
+
console.error(`[EXULU] no redis server configured, but you are trying to use a queue ( ${name}), likely in an agent or embedder (look for ExuluQueues.register().use() ).`);
|
|
5822
|
+
throw new Error(`[EXULU] no redis server configured.`);
|
|
5449
5823
|
}
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5824
|
+
const newQueue = new import_bullmq4.Queue(
|
|
5825
|
+
`${name}`,
|
|
5826
|
+
{
|
|
5827
|
+
connection: {
|
|
5828
|
+
...redisServer,
|
|
5829
|
+
enableOfflineQueue: false
|
|
5830
|
+
},
|
|
5831
|
+
telemetry: new import_bullmq_otel.BullMQOtel("simple-guide")
|
|
5832
|
+
}
|
|
5833
|
+
);
|
|
5834
|
+
await newQueue.setGlobalConcurrency(concurrency);
|
|
5835
|
+
this.queues.push({
|
|
5836
|
+
queue: newQueue,
|
|
5837
|
+
ratelimit,
|
|
5838
|
+
concurrency
|
|
5839
|
+
});
|
|
5840
|
+
return {
|
|
5841
|
+
queue: newQueue,
|
|
5842
|
+
ratelimit,
|
|
5843
|
+
concurrency
|
|
5844
|
+
};
|
|
5845
|
+
};
|
|
5846
|
+
this.list.set(name, {
|
|
5847
|
+
name,
|
|
5848
|
+
concurrency,
|
|
5454
5849
|
ratelimit,
|
|
5455
|
-
|
|
5850
|
+
use
|
|
5456
5851
|
});
|
|
5457
5852
|
return {
|
|
5458
|
-
|
|
5459
|
-
ratelimit,
|
|
5460
|
-
concurrency
|
|
5853
|
+
use
|
|
5461
5854
|
};
|
|
5462
5855
|
};
|
|
5463
5856
|
};
|
|
@@ -5476,8 +5869,6 @@ var import_openai = __toESM(require("openai"), 1);
|
|
|
5476
5869
|
var import_fs = __toESM(require("fs"), 1);
|
|
5477
5870
|
var import_node_crypto3 = require("crypto");
|
|
5478
5871
|
var import_api = require("@opentelemetry/api");
|
|
5479
|
-
var import_ai2 = require("ai");
|
|
5480
|
-
var import_express_http_proxy = __toESM(require("express-http-proxy"), 1);
|
|
5481
5872
|
var import_sdk = __toESM(require("@anthropic-ai/sdk"), 1);
|
|
5482
5873
|
|
|
5483
5874
|
// src/registry/utils/claude-messages.ts
|
|
@@ -5503,13 +5894,15 @@ var CLAUDE_MESSAGES = {
|
|
|
5503
5894
|
};
|
|
5504
5895
|
|
|
5505
5896
|
// src/registry/routes.ts
|
|
5897
|
+
var import_ai2 = require("ai");
|
|
5506
5898
|
var REQUEST_SIZE_LIMIT = "50mb";
|
|
5507
5899
|
var global_queues = {
|
|
5508
|
-
|
|
5900
|
+
eval_runs: "eval_runs"
|
|
5509
5901
|
};
|
|
5510
5902
|
var {
|
|
5511
5903
|
agentsSchema: agentsSchema2,
|
|
5512
5904
|
projectsSchema: projectsSchema2,
|
|
5905
|
+
jobResultsSchema: jobResultsSchema2,
|
|
5513
5906
|
testCasesSchema: testCasesSchema2,
|
|
5514
5907
|
evalSetsSchema: evalSetsSchema2,
|
|
5515
5908
|
evalRunsSchema: evalRunsSchema2,
|
|
@@ -5523,38 +5916,7 @@ var {
|
|
|
5523
5916
|
rbacSchema: rbacSchema2,
|
|
5524
5917
|
statisticsSchema: statisticsSchema2
|
|
5525
5918
|
} = coreSchemas.get();
|
|
5526
|
-
var
|
|
5527
|
-
console.log("[EXULU] creating recurring jobs.");
|
|
5528
|
-
const recurringJobSchedulersLogs = [];
|
|
5529
|
-
const queue = await queues.use(global_queues.logs_cleaner);
|
|
5530
|
-
recurringJobSchedulersLogs.push({
|
|
5531
|
-
name: global_queues.logs_cleaner,
|
|
5532
|
-
pattern: "0 10 * * * *",
|
|
5533
|
-
ttld: "30 days",
|
|
5534
|
-
opts: {
|
|
5535
|
-
backoff: 3,
|
|
5536
|
-
attempts: 5,
|
|
5537
|
-
removeOnFail: 1e3
|
|
5538
|
-
}
|
|
5539
|
-
});
|
|
5540
|
-
await queue.queue.upsertJobScheduler(
|
|
5541
|
-
"logs-cleaner-scheduler",
|
|
5542
|
-
{ pattern: "0 10 * * * *" },
|
|
5543
|
-
// every 10 minutes
|
|
5544
|
-
{
|
|
5545
|
-
name: global_queues.logs_cleaner,
|
|
5546
|
-
data: { ttld: 30 },
|
|
5547
|
-
// time to live in days
|
|
5548
|
-
opts: {
|
|
5549
|
-
backoff: 3,
|
|
5550
|
-
attempts: 5,
|
|
5551
|
-
removeOnFail: 1e3
|
|
5552
|
-
}
|
|
5553
|
-
}
|
|
5554
|
-
);
|
|
5555
|
-
return queue;
|
|
5556
|
-
};
|
|
5557
|
-
var createExpressRoutes = async (app, agents, tools, contexts, config, tracer) => {
|
|
5919
|
+
var createExpressRoutes = async (app, agents, tools, contexts, config, evals, tracer, queues3) => {
|
|
5558
5920
|
var corsOptions = {
|
|
5559
5921
|
origin: "*",
|
|
5560
5922
|
exposedHeaders: "*",
|
|
@@ -5573,19 +5935,15 @@ var createExpressRoutes = async (app, agents, tools, contexts, config, tracer) =
|
|
|
5573
5935
|
\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
|
|
5574
5936
|
\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2554\u255D \u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D
|
|
5575
5937
|
\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D
|
|
5576
|
-
Intelligence Management Platform
|
|
5938
|
+
Intelligence Management Platform - Server
|
|
5577
5939
|
|
|
5578
5940
|
`);
|
|
5579
|
-
if (redisServer.host?.length && redisServer.port?.length) {
|
|
5580
|
-
await createRecurringJobs();
|
|
5581
|
-
} else {
|
|
5582
|
-
console.log("[EXULU] no redis server configured, not setting up recurring jobs.");
|
|
5583
|
-
}
|
|
5584
5941
|
const schema = createSDL([
|
|
5585
5942
|
usersSchema2(),
|
|
5586
5943
|
rolesSchema2(),
|
|
5587
5944
|
agentsSchema2(),
|
|
5588
5945
|
projectsSchema2(),
|
|
5946
|
+
jobResultsSchema2(),
|
|
5589
5947
|
evalRunsSchema2(),
|
|
5590
5948
|
platformConfigurationsSchema2(),
|
|
5591
5949
|
evalSetsSchema2(),
|
|
@@ -5596,7 +5954,7 @@ var createExpressRoutes = async (app, agents, tools, contexts, config, tracer) =
|
|
|
5596
5954
|
workflowTemplatesSchema2(),
|
|
5597
5955
|
statisticsSchema2(),
|
|
5598
5956
|
rbacSchema2()
|
|
5599
|
-
], contexts ?? [], agents, tools, config);
|
|
5957
|
+
], contexts ?? [], agents, tools, config, evals, queues3 || []);
|
|
5600
5958
|
const server = new import_server3.ApolloServer({
|
|
5601
5959
|
cache: new import_utils3.InMemoryLRUCache(),
|
|
5602
5960
|
schema,
|
|
@@ -5727,103 +6085,6 @@ Mood: friendly and intelligent.
|
|
|
5727
6085
|
image: `${process.env.BACKEND}/${uuid}.png`
|
|
5728
6086
|
});
|
|
5729
6087
|
});
|
|
5730
|
-
app.post("/evals/run/:id", async (req, res) => {
|
|
5731
|
-
console.log("[EXULU] /evals/run/:id", req.params.id);
|
|
5732
|
-
const authenticationResult = await requestValidators.authenticate(req);
|
|
5733
|
-
if (!authenticationResult.user?.id) {
|
|
5734
|
-
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
5735
|
-
return;
|
|
5736
|
-
}
|
|
5737
|
-
const user = authenticationResult.user;
|
|
5738
|
-
const evalRunId = req.params.id;
|
|
5739
|
-
if (!user.super_admin && (!user.role || user.role.evals !== "write")) {
|
|
5740
|
-
res.status(403).json({
|
|
5741
|
-
message: "You don't have permission to run evals. Required: super_admin or evals write access."
|
|
5742
|
-
});
|
|
5743
|
-
return;
|
|
5744
|
-
}
|
|
5745
|
-
const { db: db3 } = await postgresClient();
|
|
5746
|
-
const evalRun = await db3.from("eval_runs").where({ id: evalRunId }).first();
|
|
5747
|
-
if (!evalRun) {
|
|
5748
|
-
res.status(404).json({
|
|
5749
|
-
message: "Eval run not found."
|
|
5750
|
-
});
|
|
5751
|
-
return;
|
|
5752
|
-
}
|
|
5753
|
-
const hasAccessToEvalRun = await checkRecordAccess(evalRun, "write", user);
|
|
5754
|
-
if (!hasAccessToEvalRun) {
|
|
5755
|
-
res.status(403).json({
|
|
5756
|
-
message: "You don't have access to this eval run."
|
|
5757
|
-
});
|
|
5758
|
-
return;
|
|
5759
|
-
}
|
|
5760
|
-
const testCaseIds = evalRun.test_case_ids ? typeof evalRun.test_case_ids === "string" ? JSON.parse(evalRun.test_case_ids) : evalRun.test_case_ids : [];
|
|
5761
|
-
const evalFunctionIds = evalRun.eval_function_ids ? typeof evalRun.eval_function_ids === "string" ? JSON.parse(evalRun.eval_function_ids) : evalRun.eval_function_ids : [];
|
|
5762
|
-
if (!testCaseIds || testCaseIds.length === 0) {
|
|
5763
|
-
res.status(400).json({
|
|
5764
|
-
message: "No test cases selected for this eval run."
|
|
5765
|
-
});
|
|
5766
|
-
return;
|
|
5767
|
-
}
|
|
5768
|
-
if (!evalFunctionIds || evalFunctionIds.length === 0) {
|
|
5769
|
-
res.status(400).json({
|
|
5770
|
-
message: "No eval functions selected for this eval run."
|
|
5771
|
-
});
|
|
5772
|
-
return;
|
|
5773
|
-
}
|
|
5774
|
-
const testCases = await db3.from("test_cases").whereIn("id", testCaseIds);
|
|
5775
|
-
if (testCases.length === 0) {
|
|
5776
|
-
res.status(404).json({
|
|
5777
|
-
message: "No test cases found."
|
|
5778
|
-
});
|
|
5779
|
-
return;
|
|
5780
|
-
}
|
|
5781
|
-
const agentInstance = await loadAgent(evalRun.agentId);
|
|
5782
|
-
if (!agentInstance) {
|
|
5783
|
-
res.status(404).json({
|
|
5784
|
-
message: "Agent instance not found."
|
|
5785
|
-
});
|
|
5786
|
-
return;
|
|
5787
|
-
}
|
|
5788
|
-
const evalQueue = await queues.use("evals");
|
|
5789
|
-
const jobIds = [];
|
|
5790
|
-
for (const testCase of testCases) {
|
|
5791
|
-
const existingJobs = await evalQueue.queue.getJobs(["waiting", "active", "delayed", "paused"]);
|
|
5792
|
-
const duplicateJob = existingJobs.find(
|
|
5793
|
-
(job2) => job2.data.evalRunId === evalRunId && job2.data.testCaseId === testCase.id && job2.data.type === "eval"
|
|
5794
|
-
);
|
|
5795
|
-
if (duplicateJob) {
|
|
5796
|
-
console.log(`[EXULU] Skipping duplicate job for eval run ${evalRunId} and test case ${testCase.id}`);
|
|
5797
|
-
continue;
|
|
5798
|
-
}
|
|
5799
|
-
const job = await evalQueue.queue.add(`eval-${testCase.id}`, {
|
|
5800
|
-
type: "eval",
|
|
5801
|
-
evalRunId,
|
|
5802
|
-
testCaseId: testCase.id,
|
|
5803
|
-
evalFunctionIds,
|
|
5804
|
-
// Array of eval function IDs - worker will create child jobs for these
|
|
5805
|
-
agentId: evalRun.agentId,
|
|
5806
|
-
inputs: testCase.inputs,
|
|
5807
|
-
expected_output: testCase.expected_output,
|
|
5808
|
-
expected_tools: testCase.expected_tools,
|
|
5809
|
-
expected_knowledge_sources: testCase.expected_knowledge_sources,
|
|
5810
|
-
expected_agent_tools: testCase.expected_agent_tools,
|
|
5811
|
-
config: evalRun.config,
|
|
5812
|
-
scoring_method: evalRun.scoring_method,
|
|
5813
|
-
pass_threshold: evalRun.pass_threshold,
|
|
5814
|
-
user: user.id,
|
|
5815
|
-
role: user.role?.id
|
|
5816
|
-
});
|
|
5817
|
-
jobIds.push(job.id);
|
|
5818
|
-
}
|
|
5819
|
-
res.status(200).json({
|
|
5820
|
-
message: `Created ${jobIds.length} eval jobs.`,
|
|
5821
|
-
jobIds,
|
|
5822
|
-
evalRunId,
|
|
5823
|
-
testCaseCount: testCases.length,
|
|
5824
|
-
evalFunctionCount: evalFunctionIds.length
|
|
5825
|
-
});
|
|
5826
|
-
});
|
|
5827
6088
|
app.get("/ping", async (req, res) => {
|
|
5828
6089
|
const authenticationResult = await requestValidators.authenticate(req);
|
|
5829
6090
|
if (!authenticationResult.user?.id) {
|
|
@@ -5952,11 +6213,11 @@ Mood: friendly and intelligent.
|
|
|
5952
6213
|
providerapikey = bytes.toString(import_crypto_js3.default.enc.Utf8);
|
|
5953
6214
|
}
|
|
5954
6215
|
if (!!headers.stream) {
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
|
|
6216
|
+
const statistics = {
|
|
6217
|
+
label: agent.name,
|
|
6218
|
+
trigger: "agent"
|
|
6219
|
+
};
|
|
6220
|
+
const result = await agent.generateStream({
|
|
5960
6221
|
contexts,
|
|
5961
6222
|
user,
|
|
5962
6223
|
instructions: agentInstance.instructions,
|
|
@@ -5966,10 +6227,79 @@ Mood: friendly and intelligent.
|
|
|
5966
6227
|
allExuluTools: tools,
|
|
5967
6228
|
providerapikey,
|
|
5968
6229
|
toolConfigs: agentInstance.tools,
|
|
5969
|
-
exuluConfig: config
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
6230
|
+
exuluConfig: config
|
|
6231
|
+
});
|
|
6232
|
+
result.stream.consumeStream();
|
|
6233
|
+
result.stream.pipeUIMessageStreamToResponse(res, {
|
|
6234
|
+
messageMetadata: ({ part }) => {
|
|
6235
|
+
if (part.type === "finish") {
|
|
6236
|
+
return {
|
|
6237
|
+
totalTokens: part.totalUsage.totalTokens,
|
|
6238
|
+
reasoningTokens: part.totalUsage.reasoningTokens,
|
|
6239
|
+
inputTokens: part.totalUsage.inputTokens,
|
|
6240
|
+
outputTokens: part.totalUsage.outputTokens,
|
|
6241
|
+
cachedInputTokens: part.totalUsage.cachedInputTokens
|
|
6242
|
+
};
|
|
6243
|
+
}
|
|
6244
|
+
},
|
|
6245
|
+
originalMessages: result.originalMessages,
|
|
6246
|
+
sendReasoning: true,
|
|
6247
|
+
sendSources: true,
|
|
6248
|
+
onError: (error) => {
|
|
6249
|
+
console.error("[EXULU] chat response error.", error);
|
|
6250
|
+
return errorHandler(error);
|
|
6251
|
+
},
|
|
6252
|
+
generateMessageId: (0, import_ai2.createIdGenerator)({
|
|
6253
|
+
prefix: "msg_",
|
|
6254
|
+
size: 16
|
|
6255
|
+
}),
|
|
6256
|
+
onFinish: async ({ messages, isContinuation, isAborted, responseMessage }) => {
|
|
6257
|
+
if (headers.session) {
|
|
6258
|
+
await saveChat({
|
|
6259
|
+
session: headers.session,
|
|
6260
|
+
user: user.id,
|
|
6261
|
+
messages: messages.filter((x) => !result.previousMessages.find((y) => y.id === x.id))
|
|
6262
|
+
});
|
|
6263
|
+
}
|
|
6264
|
+
const metadata = messages[messages.length - 1]?.metadata;
|
|
6265
|
+
console.log("[EXULU] Finished streaming", metadata);
|
|
6266
|
+
console.log("[EXULU] Statistics", {
|
|
6267
|
+
label: agent.name,
|
|
6268
|
+
trigger: "agent"
|
|
6269
|
+
});
|
|
6270
|
+
if (statistics) {
|
|
6271
|
+
await Promise.all([
|
|
6272
|
+
updateStatistic({
|
|
6273
|
+
name: "count",
|
|
6274
|
+
label: statistics.label,
|
|
6275
|
+
type: STATISTICS_TYPE_ENUM.AGENT_RUN,
|
|
6276
|
+
trigger: statistics.trigger,
|
|
6277
|
+
count: 1,
|
|
6278
|
+
user: user.id,
|
|
6279
|
+
role: user?.role?.id
|
|
6280
|
+
}),
|
|
6281
|
+
...metadata?.inputTokens ? [
|
|
6282
|
+
updateStatistic({
|
|
6283
|
+
name: "inputTokens",
|
|
6284
|
+
label: statistics.label,
|
|
6285
|
+
type: STATISTICS_TYPE_ENUM.AGENT_RUN,
|
|
6286
|
+
trigger: statistics.trigger,
|
|
6287
|
+
count: metadata?.inputTokens,
|
|
6288
|
+
user: user.id,
|
|
6289
|
+
role: user?.role?.id
|
|
6290
|
+
})
|
|
6291
|
+
] : [],
|
|
6292
|
+
...metadata?.outputTokens ? [
|
|
6293
|
+
updateStatistic({
|
|
6294
|
+
name: "outputTokens",
|
|
6295
|
+
label: statistics.label,
|
|
6296
|
+
type: STATISTICS_TYPE_ENUM.AGENT_RUN,
|
|
6297
|
+
trigger: statistics.trigger,
|
|
6298
|
+
count: metadata?.outputTokens
|
|
6299
|
+
})
|
|
6300
|
+
] : []
|
|
6301
|
+
]);
|
|
6302
|
+
}
|
|
5973
6303
|
}
|
|
5974
6304
|
});
|
|
5975
6305
|
return;
|
|
@@ -5978,7 +6308,7 @@ Mood: friendly and intelligent.
|
|
|
5978
6308
|
user,
|
|
5979
6309
|
instructions: agentInstance.instructions,
|
|
5980
6310
|
session: headers.session,
|
|
5981
|
-
|
|
6311
|
+
inputMessages: [req.body.message],
|
|
5982
6312
|
contexts,
|
|
5983
6313
|
currentTools: enabledTools,
|
|
5984
6314
|
allExuluTools: tools,
|
|
@@ -6000,91 +6330,6 @@ Mood: friendly and intelligent.
|
|
|
6000
6330
|
} else {
|
|
6001
6331
|
console.log("[EXULU] skipping uppy file upload routes, because no S3 compatible region, key or secret is set in ExuluApp instance.");
|
|
6002
6332
|
}
|
|
6003
|
-
app.use("/xxx/anthropic/:id", import_express4.default.raw({ type: "*/*", limit: REQUEST_SIZE_LIMIT }), (0, import_express_http_proxy.default)(
|
|
6004
|
-
(req, res, next) => {
|
|
6005
|
-
return "https://api.anthropic.com";
|
|
6006
|
-
},
|
|
6007
|
-
{
|
|
6008
|
-
limit: "50mb",
|
|
6009
|
-
memoizeHost: false,
|
|
6010
|
-
preserveHostHdr: true,
|
|
6011
|
-
secure: false,
|
|
6012
|
-
reqAsBuffer: true,
|
|
6013
|
-
proxyReqBodyDecorator: function(bodyContent, srcReq) {
|
|
6014
|
-
return bodyContent;
|
|
6015
|
-
},
|
|
6016
|
-
userResDecorator: function(proxyRes, proxyResData, userReq, userRes) {
|
|
6017
|
-
console.log("[EXULU] Proxy response!", proxyResData);
|
|
6018
|
-
proxyResData = proxyResData.toString();
|
|
6019
|
-
console.log("[EXULU] Proxy response string!", proxyResData);
|
|
6020
|
-
return proxyResData;
|
|
6021
|
-
},
|
|
6022
|
-
proxyReqPathResolver: (req) => {
|
|
6023
|
-
const prefix = `/gateway/anthropic/${req.params.id}`;
|
|
6024
|
-
let path = req.url.startsWith(prefix) ? req.url.slice(prefix.length) : req.url;
|
|
6025
|
-
if (!path.startsWith("/")) path = "/" + path;
|
|
6026
|
-
console.log("[EXULU] Provider path:", path);
|
|
6027
|
-
return path;
|
|
6028
|
-
},
|
|
6029
|
-
proxyReqOptDecorator: function(proxyReqOpts, srcReq) {
|
|
6030
|
-
return new Promise(async (resolve, reject) => {
|
|
6031
|
-
try {
|
|
6032
|
-
const authenticationResult = await requestValidators.authenticate(srcReq);
|
|
6033
|
-
if (!authenticationResult.user?.id) {
|
|
6034
|
-
console.log("[EXULU] failed authentication result", authenticationResult);
|
|
6035
|
-
reject(authenticationResult.message);
|
|
6036
|
-
return;
|
|
6037
|
-
}
|
|
6038
|
-
console.log("[EXULU] Authenticated call", authenticationResult.user?.email);
|
|
6039
|
-
const { db: db3 } = await postgresClient();
|
|
6040
|
-
let query = db3("agents");
|
|
6041
|
-
query.select("*");
|
|
6042
|
-
query = applyAccessControl(agentsSchema2(), authenticationResult.user, query);
|
|
6043
|
-
query.where({ id: srcReq.params.id });
|
|
6044
|
-
const agent = await query.first();
|
|
6045
|
-
if (!agent) {
|
|
6046
|
-
reject(new Error("Agent with id " + srcReq.params.id + " not found."));
|
|
6047
|
-
return;
|
|
6048
|
-
}
|
|
6049
|
-
console.log("[EXULU] Agent loaded", agent.name);
|
|
6050
|
-
const backend = agents.find((x) => x.id === agent.backend);
|
|
6051
|
-
if (!process.env.NEXTAUTH_SECRET) {
|
|
6052
|
-
reject(new Error("Missing NEXTAUTH_SECRET"));
|
|
6053
|
-
return;
|
|
6054
|
-
}
|
|
6055
|
-
if (!agent.providerapikey) {
|
|
6056
|
-
reject(new Error("API Key not set for agent"));
|
|
6057
|
-
return;
|
|
6058
|
-
}
|
|
6059
|
-
const variableName = agent.providerapikey;
|
|
6060
|
-
const variable = await db3.from("variables").where({ name: variableName }).first();
|
|
6061
|
-
console.log("[EXULU] Variable loaded", variable);
|
|
6062
|
-
let providerapikey = variable.value;
|
|
6063
|
-
if (!variable.encrypted) {
|
|
6064
|
-
reject(new Error("API Key not encrypted for agent"));
|
|
6065
|
-
return;
|
|
6066
|
-
}
|
|
6067
|
-
if (variable.encrypted) {
|
|
6068
|
-
const bytes = import_crypto_js3.default.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
|
|
6069
|
-
providerapikey = bytes.toString(import_crypto_js3.default.enc.Utf8);
|
|
6070
|
-
}
|
|
6071
|
-
console.log("[EXULU] Provider API key", providerapikey);
|
|
6072
|
-
proxyReqOpts.headers["x-api-key"] = providerapikey;
|
|
6073
|
-
proxyReqOpts.rejectUnauthorized = false;
|
|
6074
|
-
delete proxyReqOpts.headers["provider"];
|
|
6075
|
-
const url = new URL("https://api.anthropic.com");
|
|
6076
|
-
proxyReqOpts.headers["host"] = url.host;
|
|
6077
|
-
proxyReqOpts.headers["anthropic-version"] = "2023-06-01";
|
|
6078
|
-
console.log("[EXULU] Proxy request headers", proxyReqOpts.headers);
|
|
6079
|
-
resolve(proxyReqOpts);
|
|
6080
|
-
} catch (error) {
|
|
6081
|
-
console.error("[EXULU] Proxy error", error);
|
|
6082
|
-
reject(error);
|
|
6083
|
-
}
|
|
6084
|
-
});
|
|
6085
|
-
}
|
|
6086
|
-
}
|
|
6087
|
-
));
|
|
6088
6333
|
app.get("/config", async (req, res) => {
|
|
6089
6334
|
res.status(200).json({
|
|
6090
6335
|
message: "Config fetched successfully.",
|
|
@@ -6258,8 +6503,34 @@ var createCustomAnthropicStreamingMessage = (message) => {
|
|
|
6258
6503
|
var import_ioredis = __toESM(require("ioredis"), 1);
|
|
6259
6504
|
var import_bullmq5 = require("bullmq");
|
|
6260
6505
|
var import_api2 = require("@opentelemetry/api");
|
|
6506
|
+
var import_uuid3 = require("uuid");
|
|
6507
|
+
var import_ai3 = require("ai");
|
|
6508
|
+
var import_crypto_js4 = __toESM(require("crypto-js"), 1);
|
|
6509
|
+
|
|
6510
|
+
// src/registry/log-metadata.ts
|
|
6511
|
+
function logMetadata(id, additionalMetadata) {
|
|
6512
|
+
return {
|
|
6513
|
+
__logMetadata: true,
|
|
6514
|
+
id,
|
|
6515
|
+
...additionalMetadata
|
|
6516
|
+
};
|
|
6517
|
+
}
|
|
6518
|
+
|
|
6519
|
+
// src/registry/workers.ts
|
|
6261
6520
|
var redisConnection;
|
|
6262
|
-
var createWorkers = async (
|
|
6521
|
+
var createWorkers = async (agents, queues3, config, contexts, evals, tools, tracer) => {
|
|
6522
|
+
console.log(`
|
|
6523
|
+
\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557
|
|
6524
|
+
\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u255A\u2588\u2588\u2557\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
|
|
6525
|
+
\u2588\u2588\u2588\u2588\u2588\u2557 \u255A\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
|
|
6526
|
+
\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
|
|
6527
|
+
\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2554\u255D \u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D
|
|
6528
|
+
\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D
|
|
6529
|
+
Intelligence Management Platform - Workers
|
|
6530
|
+
|
|
6531
|
+
`);
|
|
6532
|
+
console.log("[EXULU] creating workers for " + queues3?.length + " queues.");
|
|
6533
|
+
console.log("[EXULU] queues", queues3.map((q) => q.queue.name));
|
|
6263
6534
|
if (!redisServer.host || !redisServer.port) {
|
|
6264
6535
|
console.error("[EXULU] you are trying to start worker, but no redis server is configured in the environment.");
|
|
6265
6536
|
throw new Error("No redis server configured in the environment, so cannot start worker.");
|
|
@@ -6267,80 +6538,333 @@ var createWorkers = async (queues2, config, contexts, tracer) => {
|
|
|
6267
6538
|
if (!redisConnection) {
|
|
6268
6539
|
redisConnection = new import_ioredis.default({
|
|
6269
6540
|
...redisServer,
|
|
6541
|
+
enableOfflineQueue: true,
|
|
6542
|
+
retryStrategy: function(times) {
|
|
6543
|
+
return Math.max(Math.min(Math.exp(times), 2e4), 1e3);
|
|
6544
|
+
},
|
|
6270
6545
|
maxRetriesPerRequest: null
|
|
6271
6546
|
});
|
|
6272
6547
|
}
|
|
6273
|
-
const workers =
|
|
6274
|
-
console.log(`[EXULU] creating worker for queue ${queue}.`);
|
|
6548
|
+
const workers = queues3.map((queue) => {
|
|
6549
|
+
console.log(`[EXULU] creating worker for queue ${queue.queue.name}.`);
|
|
6275
6550
|
const worker = new import_bullmq5.Worker(
|
|
6276
|
-
`${queue}`,
|
|
6551
|
+
`${queue.queue.name}`,
|
|
6277
6552
|
async (bullmqJob) => {
|
|
6553
|
+
console.log("[EXULU] starting execution for job", logMetadata(bullmqJob.name, {
|
|
6554
|
+
name: bullmqJob.name,
|
|
6555
|
+
status: await bullmqJob.getState(),
|
|
6556
|
+
type: bullmqJob.data.type
|
|
6557
|
+
}));
|
|
6278
6558
|
const { db: db3 } = await postgresClient();
|
|
6279
|
-
|
|
6280
|
-
|
|
6281
|
-
|
|
6282
|
-
|
|
6283
|
-
|
|
6284
|
-
|
|
6285
|
-
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
|
|
6293
|
-
|
|
6294
|
-
|
|
6295
|
-
|
|
6296
|
-
|
|
6297
|
-
|
|
6298
|
-
|
|
6299
|
-
|
|
6300
|
-
|
|
6301
|
-
|
|
6302
|
-
|
|
6303
|
-
|
|
6304
|
-
|
|
6305
|
-
|
|
6306
|
-
|
|
6307
|
-
|
|
6308
|
-
|
|
6309
|
-
|
|
6310
|
-
|
|
6559
|
+
const data = bullmqJob.data;
|
|
6560
|
+
const timeoutMs = data.timeoutInSeconds * 1e3;
|
|
6561
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
6562
|
+
setTimeout(() => {
|
|
6563
|
+
reject(new Error(`Timeout for job ${bullmqJob.id} reached after ${data.timeoutInSeconds}s`));
|
|
6564
|
+
}, timeoutMs);
|
|
6565
|
+
});
|
|
6566
|
+
const workPromise = (async () => {
|
|
6567
|
+
try {
|
|
6568
|
+
bullmq.validate(bullmqJob.id, data);
|
|
6569
|
+
if (data.type === "embedder") {
|
|
6570
|
+
console.log("[EXULU] running an embedder job.", logMetadata(bullmqJob.name));
|
|
6571
|
+
const label = `embedder-${bullmqJob.name}`;
|
|
6572
|
+
await db3.from("job_results").insert({
|
|
6573
|
+
job_id: bullmqJob.id,
|
|
6574
|
+
label,
|
|
6575
|
+
state: await bullmqJob.getState(),
|
|
6576
|
+
result: null,
|
|
6577
|
+
metadata: {}
|
|
6578
|
+
});
|
|
6579
|
+
const context = contexts.find((context2) => context2.id === data.context);
|
|
6580
|
+
if (!context) {
|
|
6581
|
+
throw new Error(`Context ${data.context} not found in the registry.`);
|
|
6582
|
+
}
|
|
6583
|
+
if (!data.embedder) {
|
|
6584
|
+
throw new Error(`No embedder set for embedder job.`);
|
|
6585
|
+
}
|
|
6586
|
+
const embedder = contexts.find((context2) => context2.embedder?.id === data.embedder);
|
|
6587
|
+
if (!embedder) {
|
|
6588
|
+
throw new Error(`Embedder ${data.embedder} not found in the registry.`);
|
|
6589
|
+
}
|
|
6590
|
+
const result = await context.createAndUpsertEmbeddings(data.inputs, config, data.user, {
|
|
6591
|
+
label: embedder.name,
|
|
6592
|
+
trigger: data.trigger
|
|
6593
|
+
}, data.role, bullmqJob.id);
|
|
6594
|
+
return {
|
|
6595
|
+
result,
|
|
6596
|
+
metadata: {}
|
|
6597
|
+
};
|
|
6311
6598
|
}
|
|
6312
|
-
|
|
6313
|
-
|
|
6314
|
-
|
|
6599
|
+
if (data.type === "processor") {
|
|
6600
|
+
console.log("[EXULU] running a processor job.", logMetadata(bullmqJob.name));
|
|
6601
|
+
const label = `processor-${bullmqJob.name}`;
|
|
6602
|
+
await db3.from("job_results").insert({
|
|
6603
|
+
job_id: bullmqJob.id,
|
|
6604
|
+
label,
|
|
6605
|
+
state: await bullmqJob.getState(),
|
|
6606
|
+
result: null,
|
|
6607
|
+
metadata: {}
|
|
6608
|
+
});
|
|
6609
|
+
const context = contexts.find((context2) => context2.id === data.context);
|
|
6610
|
+
if (!context) {
|
|
6611
|
+
throw new Error(`Context ${data.context} not found in the registry.`);
|
|
6612
|
+
}
|
|
6613
|
+
const field = context.fields.find((field2) => field2.name === data.inputs.field);
|
|
6614
|
+
if (!field) {
|
|
6615
|
+
throw new Error(`Field ${data.inputs.field} not found in the context ${data.context}.`);
|
|
6616
|
+
}
|
|
6617
|
+
if (!field.processor) {
|
|
6618
|
+
throw new Error(`Processor not set for field ${data.inputs.field} in the context ${data.context}.`);
|
|
6619
|
+
}
|
|
6620
|
+
const exuluStorage = new ExuluStorage({ config });
|
|
6621
|
+
if (!data.user) {
|
|
6622
|
+
throw new Error(`User not set for processor job.`);
|
|
6623
|
+
}
|
|
6624
|
+
if (!data.role) {
|
|
6625
|
+
throw new Error(`Role not set for processor job.`);
|
|
6626
|
+
}
|
|
6627
|
+
const result = await field.processor.execute({
|
|
6628
|
+
item: data.inputs,
|
|
6629
|
+
user: data.user,
|
|
6630
|
+
role: data.role,
|
|
6631
|
+
utils: {
|
|
6632
|
+
storage: exuluStorage,
|
|
6633
|
+
items: {
|
|
6634
|
+
update: context.updateItem,
|
|
6635
|
+
create: context.createItem,
|
|
6636
|
+
delete: context.deleteItem
|
|
6637
|
+
}
|
|
6638
|
+
},
|
|
6639
|
+
config
|
|
6640
|
+
});
|
|
6641
|
+
return {
|
|
6642
|
+
result,
|
|
6643
|
+
metadata: {}
|
|
6644
|
+
};
|
|
6315
6645
|
}
|
|
6316
|
-
if (
|
|
6317
|
-
|
|
6646
|
+
if (data.type === "eval_run") {
|
|
6647
|
+
console.log("[EXULU] running an eval run job.", logMetadata(bullmqJob.name));
|
|
6648
|
+
const label = `eval-run-${data.eval_run_id}-${data.test_case_id}`;
|
|
6649
|
+
const existingResult = await db3.from("job_results").where({ label }).first();
|
|
6650
|
+
if (existingResult) {
|
|
6651
|
+
await db3.from("job_results").where({ label }).update({
|
|
6652
|
+
job_id: bullmqJob.id,
|
|
6653
|
+
label,
|
|
6654
|
+
state: await bullmqJob.getState(),
|
|
6655
|
+
result: null,
|
|
6656
|
+
metadata: {},
|
|
6657
|
+
tries: existingResult.tries + 1
|
|
6658
|
+
});
|
|
6659
|
+
} else {
|
|
6660
|
+
await db3.from("job_results").insert({
|
|
6661
|
+
job_id: bullmqJob.id,
|
|
6662
|
+
label,
|
|
6663
|
+
state: await bullmqJob.getState(),
|
|
6664
|
+
result: null,
|
|
6665
|
+
metadata: {},
|
|
6666
|
+
tries: 1
|
|
6667
|
+
});
|
|
6668
|
+
}
|
|
6669
|
+
const {
|
|
6670
|
+
agentInstance,
|
|
6671
|
+
backend: agentBackend,
|
|
6672
|
+
user,
|
|
6673
|
+
evalRun,
|
|
6674
|
+
testCase,
|
|
6675
|
+
messages: inputMessages
|
|
6676
|
+
} = await validateEvalPayload(data, agents);
|
|
6677
|
+
const retries = 3;
|
|
6678
|
+
let attempts = 0;
|
|
6679
|
+
const promise = new Promise(async (resolve, reject) => {
|
|
6680
|
+
while (attempts < retries) {
|
|
6681
|
+
try {
|
|
6682
|
+
const messages2 = await processUiMessagesFlow({
|
|
6683
|
+
agents,
|
|
6684
|
+
agentInstance,
|
|
6685
|
+
agentBackend,
|
|
6686
|
+
inputMessages,
|
|
6687
|
+
contexts,
|
|
6688
|
+
user,
|
|
6689
|
+
tools,
|
|
6690
|
+
config
|
|
6691
|
+
});
|
|
6692
|
+
resolve(messages2);
|
|
6693
|
+
break;
|
|
6694
|
+
} catch (error) {
|
|
6695
|
+
console.error(`[EXULU] error processing UI messages flow for agent ${agentInstance.name} (${agentInstance.id}).`, logMetadata(bullmqJob.name, {
|
|
6696
|
+
error: error instanceof Error ? error.message : String(error)
|
|
6697
|
+
}));
|
|
6698
|
+
attempts++;
|
|
6699
|
+
if (attempts >= retries) {
|
|
6700
|
+
reject(error);
|
|
6701
|
+
}
|
|
6702
|
+
await new Promise((resolve2) => setTimeout(resolve2, 2e3));
|
|
6703
|
+
}
|
|
6704
|
+
}
|
|
6705
|
+
});
|
|
6706
|
+
const result = await promise;
|
|
6707
|
+
const messages = result.messages;
|
|
6708
|
+
const metadata = result.metadata;
|
|
6709
|
+
const evalFunctions = evalRun.eval_functions;
|
|
6710
|
+
let evalFunctionResults = [];
|
|
6711
|
+
for (const evalFunction of evalFunctions) {
|
|
6712
|
+
const evalMethod = evals.find((e) => e.id === evalFunction.id);
|
|
6713
|
+
if (!evalMethod) {
|
|
6714
|
+
throw new Error(`Eval function ${evalFunction.id} not found in the registry, check your code and make sure the eval function is registered correctly.`);
|
|
6715
|
+
}
|
|
6716
|
+
let result2;
|
|
6717
|
+
if (evalMethod.queue) {
|
|
6718
|
+
const queue2 = await evalMethod.queue;
|
|
6719
|
+
const jobData = {
|
|
6720
|
+
...data,
|
|
6721
|
+
type: "eval_function",
|
|
6722
|
+
eval_functions: [{
|
|
6723
|
+
id: evalFunction.id,
|
|
6724
|
+
config: evalFunction.config || {}
|
|
6725
|
+
}],
|
|
6726
|
+
// updating the input messages with the messages we want to run the eval
|
|
6727
|
+
// function on, which are the output messages from the agent.
|
|
6728
|
+
inputs: messages
|
|
6729
|
+
};
|
|
6730
|
+
const redisId = (0, import_uuid3.v4)();
|
|
6731
|
+
const job = await queue2.queue.add("eval_function", jobData, {
|
|
6732
|
+
jobId: redisId,
|
|
6733
|
+
// Setting it to 3 as a sensible default, as
|
|
6734
|
+
// many AI services are quite unstable.
|
|
6735
|
+
attempts: queue2.retries || 3,
|
|
6736
|
+
// todo make this configurable?
|
|
6737
|
+
removeOnComplete: 5e3,
|
|
6738
|
+
removeOnFail: 1e4,
|
|
6739
|
+
backoff: queue2.backoff || {
|
|
6740
|
+
type: "exponential",
|
|
6741
|
+
delay: 2e3
|
|
6742
|
+
}
|
|
6743
|
+
});
|
|
6744
|
+
if (!job.id) {
|
|
6745
|
+
throw new Error(`Tried to add job to queue ${queue2.queue.name} but failed to get the job ID.`);
|
|
6746
|
+
}
|
|
6747
|
+
result2 = await pollJobResult({ queue: queue2, jobId: job.id });
|
|
6748
|
+
const evalFunctionResult = {
|
|
6749
|
+
test_case_id: testCase.id,
|
|
6750
|
+
eval_run_id: evalRun.id,
|
|
6751
|
+
eval_function_id: evalFunction.id,
|
|
6752
|
+
result: result2 || 0
|
|
6753
|
+
};
|
|
6754
|
+
console.log(`[EXULU] eval function ${evalFunction.id} result: ${result2}`, logMetadata(bullmqJob.name, {
|
|
6755
|
+
result: result2 || 0
|
|
6756
|
+
}));
|
|
6757
|
+
evalFunctionResults.push(evalFunctionResult);
|
|
6758
|
+
} else {
|
|
6759
|
+
result2 = await evalMethod.run(
|
|
6760
|
+
agentInstance,
|
|
6761
|
+
agentBackend,
|
|
6762
|
+
testCase,
|
|
6763
|
+
messages,
|
|
6764
|
+
evalFunction.config || {}
|
|
6765
|
+
);
|
|
6766
|
+
const evalFunctionResult = {
|
|
6767
|
+
test_case_id: testCase.id,
|
|
6768
|
+
eval_run_id: evalRun.id,
|
|
6769
|
+
eval_function_id: evalFunction.id,
|
|
6770
|
+
result: result2 || 0
|
|
6771
|
+
};
|
|
6772
|
+
evalFunctionResults.push(evalFunctionResult);
|
|
6773
|
+
console.log(`[EXULU] eval function ${evalFunction.id} result: ${result2}`, logMetadata(bullmqJob.name, {
|
|
6774
|
+
result: result2 || 0
|
|
6775
|
+
}));
|
|
6776
|
+
}
|
|
6777
|
+
}
|
|
6778
|
+
const scores = evalFunctionResults.map((result2) => result2.result);
|
|
6779
|
+
let score = 0;
|
|
6780
|
+
switch (data.scoring_method) {
|
|
6781
|
+
case "median":
|
|
6782
|
+
score = getMedian(scores);
|
|
6783
|
+
break;
|
|
6784
|
+
case "average":
|
|
6785
|
+
score = getAverage(scores);
|
|
6786
|
+
break;
|
|
6787
|
+
case "sum":
|
|
6788
|
+
score = getSum(scores);
|
|
6789
|
+
break;
|
|
6790
|
+
}
|
|
6791
|
+
return {
|
|
6792
|
+
result: score,
|
|
6793
|
+
metadata: {
|
|
6794
|
+
...evalFunctionResults,
|
|
6795
|
+
...metadata
|
|
6796
|
+
}
|
|
6797
|
+
};
|
|
6318
6798
|
}
|
|
6319
|
-
|
|
6320
|
-
|
|
6321
|
-
|
|
6322
|
-
|
|
6323
|
-
|
|
6324
|
-
|
|
6325
|
-
|
|
6326
|
-
|
|
6799
|
+
if (data.type === "eval_function") {
|
|
6800
|
+
console.log("[EXULU] running an eval function job.", logMetadata(bullmqJob.name));
|
|
6801
|
+
if (data.eval_functions?.length !== 1) {
|
|
6802
|
+
throw new Error(`Expected 1 eval function for eval function job, got ${data.eval_functions?.length}.`);
|
|
6803
|
+
}
|
|
6804
|
+
const label = `eval-function-${data.eval_run_id}-${data.test_case_id}-${data.eval_functions?.[0]?.id}`;
|
|
6805
|
+
const existingResult = await db3.from("job_results").where({ label }).first();
|
|
6806
|
+
if (existingResult) {
|
|
6807
|
+
await db3.from("job_results").where({ label }).update({
|
|
6808
|
+
job_id: bullmqJob.id,
|
|
6809
|
+
label,
|
|
6810
|
+
state: await bullmqJob.getState(),
|
|
6811
|
+
result: null,
|
|
6812
|
+
metadata: {},
|
|
6813
|
+
tries: existingResult.tries + 1
|
|
6814
|
+
});
|
|
6815
|
+
} else {
|
|
6816
|
+
await db3.from("job_results").insert({
|
|
6817
|
+
job_id: bullmqJob.id,
|
|
6818
|
+
label,
|
|
6819
|
+
state: await bullmqJob.getState(),
|
|
6820
|
+
result: null,
|
|
6821
|
+
metadata: {},
|
|
6822
|
+
tries: 1
|
|
6823
|
+
});
|
|
6824
|
+
}
|
|
6825
|
+
const {
|
|
6826
|
+
evalRun,
|
|
6827
|
+
agentInstance,
|
|
6828
|
+
backend,
|
|
6829
|
+
testCase,
|
|
6830
|
+
messages: inputMessages
|
|
6831
|
+
} = await validateEvalPayload(data, agents);
|
|
6832
|
+
const evalFunctions = evalRun.eval_functions;
|
|
6833
|
+
let result;
|
|
6834
|
+
for (const evalFunction of evalFunctions) {
|
|
6835
|
+
const evalMethod = evals.find((e) => e.id === evalFunction.id);
|
|
6836
|
+
if (!evalMethod) {
|
|
6837
|
+
throw new Error(`Eval function ${evalFunction.id} not found in the registry, check your code and make sure the eval function is registered correctly.`);
|
|
6327
6838
|
}
|
|
6328
|
-
|
|
6329
|
-
|
|
6330
|
-
|
|
6331
|
-
|
|
6839
|
+
result = await evalMethod.run(
|
|
6840
|
+
agentInstance,
|
|
6841
|
+
backend,
|
|
6842
|
+
testCase,
|
|
6843
|
+
inputMessages,
|
|
6844
|
+
evalFunction.config || {}
|
|
6845
|
+
);
|
|
6846
|
+
console.log(`[EXULU] eval function ${evalFunction.id} result: ${result}`, logMetadata(bullmqJob.name, {
|
|
6847
|
+
result: result || 0
|
|
6848
|
+
}));
|
|
6849
|
+
}
|
|
6850
|
+
return {
|
|
6851
|
+
result,
|
|
6852
|
+
metadata: {}
|
|
6853
|
+
};
|
|
6854
|
+
}
|
|
6855
|
+
throw new Error(`Invalid job type: ${data.type} for job ${bullmqJob.name}.`);
|
|
6856
|
+
} catch (error) {
|
|
6857
|
+
console.error(`[EXULU] job failed.`, error instanceof Error ? error.message : String(error));
|
|
6858
|
+
throw error;
|
|
6332
6859
|
}
|
|
6333
|
-
}
|
|
6334
|
-
|
|
6335
|
-
status: "failed",
|
|
6336
|
-
finishedAt: /* @__PURE__ */ new Date(),
|
|
6337
|
-
error: error instanceof Error ? error.message : String(error)
|
|
6338
|
-
});
|
|
6339
|
-
throw new Error(error instanceof Error ? error.message : String(error));
|
|
6340
|
-
}
|
|
6860
|
+
})();
|
|
6861
|
+
return Promise.race([workPromise, timeoutPromise]);
|
|
6341
6862
|
},
|
|
6342
6863
|
{
|
|
6864
|
+
autorun: true,
|
|
6343
6865
|
connection: redisConnection,
|
|
6866
|
+
removeOnComplete: { count: 1e3 },
|
|
6867
|
+
removeOnFail: { count: 5e3 },
|
|
6344
6868
|
...queue.ratelimit && {
|
|
6345
6869
|
limiter: {
|
|
6346
6870
|
max: queue.ratelimit,
|
|
@@ -6349,22 +6873,312 @@ var createWorkers = async (queues2, config, contexts, tracer) => {
|
|
|
6349
6873
|
}
|
|
6350
6874
|
}
|
|
6351
6875
|
);
|
|
6352
|
-
worker.on("completed", (job, returnvalue) => {
|
|
6353
|
-
console.log(`[EXULU] completed job ${job.id}.`,
|
|
6876
|
+
worker.on("completed", async (job, returnvalue) => {
|
|
6877
|
+
console.log(`[EXULU] completed job ${job.id}.`, logMetadata(job.name, {
|
|
6878
|
+
result: returnvalue
|
|
6879
|
+
}));
|
|
6880
|
+
const { db: db3 } = await postgresClient();
|
|
6881
|
+
await db3.from("job_results").where({ job_id: job.id }).update({
|
|
6882
|
+
state: JOB_STATUS_ENUM.completed,
|
|
6883
|
+
result: returnvalue.result,
|
|
6884
|
+
metadata: returnvalue.metadata
|
|
6885
|
+
});
|
|
6354
6886
|
});
|
|
6355
|
-
worker.on("failed", (job, error, prev) => {
|
|
6887
|
+
worker.on("failed", async (job, error, prev) => {
|
|
6356
6888
|
if (job?.id) {
|
|
6357
|
-
|
|
6889
|
+
const { db: db3 } = await postgresClient();
|
|
6890
|
+
console.error(`[EXULU] failed job ${job.id}.`, error);
|
|
6891
|
+
await db3.from("job_results").where({ job_id: job.id }).update({
|
|
6892
|
+
state: JOB_STATUS_ENUM.failed,
|
|
6893
|
+
error
|
|
6894
|
+
});
|
|
6895
|
+
return;
|
|
6358
6896
|
}
|
|
6359
|
-
console.error(`[EXULU] job
|
|
6897
|
+
console.error(`[EXULU] job failed.`, job?.name ? logMetadata(job.name, {
|
|
6898
|
+
error: error instanceof Error ? error.message : String(error)
|
|
6899
|
+
}) : error);
|
|
6900
|
+
});
|
|
6901
|
+
worker.on("error", (error) => {
|
|
6902
|
+
console.error(`[EXULU] worker error.`, error);
|
|
6360
6903
|
});
|
|
6361
6904
|
worker.on("progress", (job, progress) => {
|
|
6362
|
-
console.log(`[EXULU] job progress ${job.id}.`,
|
|
6905
|
+
console.log(`[EXULU] job progress ${job.id}.`, logMetadata(job.name, {
|
|
6906
|
+
progress
|
|
6907
|
+
}));
|
|
6363
6908
|
});
|
|
6909
|
+
const gracefulShutdown = async (signal) => {
|
|
6910
|
+
console.log(`Received ${signal}, closing server...`);
|
|
6911
|
+
await worker.close();
|
|
6912
|
+
process.exit(0);
|
|
6913
|
+
};
|
|
6914
|
+
process.on("SIGINT", () => gracefulShutdown("SIGINT"));
|
|
6915
|
+
process.on("SIGTERM", () => gracefulShutdown("SIGTERM"));
|
|
6364
6916
|
return worker;
|
|
6365
6917
|
});
|
|
6366
6918
|
return workers;
|
|
6367
6919
|
};
|
|
6920
|
+
var validateEvalPayload = async (data, agents) => {
|
|
6921
|
+
if (!data.eval_run_id) {
|
|
6922
|
+
throw new Error(`No eval run ID set for eval job.`);
|
|
6923
|
+
}
|
|
6924
|
+
if (!data.test_case_id) {
|
|
6925
|
+
throw new Error(`No test case ID set for eval job.`);
|
|
6926
|
+
}
|
|
6927
|
+
if (!data.user) {
|
|
6928
|
+
throw new Error(`No user set for eval job.`);
|
|
6929
|
+
}
|
|
6930
|
+
if (!data.role) {
|
|
6931
|
+
throw new Error(`No role set for eval job.`);
|
|
6932
|
+
}
|
|
6933
|
+
if (!data.agent_id) {
|
|
6934
|
+
throw new Error(`No agent ID set for eval job.`);
|
|
6935
|
+
}
|
|
6936
|
+
if (!data.inputs?.length) {
|
|
6937
|
+
throw new Error(`No inputs set for eval job, expected array of UIMessage objects.`);
|
|
6938
|
+
}
|
|
6939
|
+
const { db: db3 } = await postgresClient();
|
|
6940
|
+
const evalRun = await db3.from("eval_runs").where({ id: data.eval_run_id }).first();
|
|
6941
|
+
if (!evalRun) {
|
|
6942
|
+
throw new Error(`Eval run ${data.eval_run_id} not found in the database.`);
|
|
6943
|
+
}
|
|
6944
|
+
const agentInstance = await loadAgent(evalRun.agent_id);
|
|
6945
|
+
if (!agentInstance) {
|
|
6946
|
+
throw new Error(`Agent ${evalRun.agent_id} not found in the database.`);
|
|
6947
|
+
}
|
|
6948
|
+
const backend = agents.find((a) => a.id === agentInstance.backend);
|
|
6949
|
+
if (!backend) {
|
|
6950
|
+
throw new Error(`Backend ${agentInstance.backend} not found in the database.`);
|
|
6951
|
+
}
|
|
6952
|
+
const user = await db3.from("users").where({ id: data.user }).first();
|
|
6953
|
+
if (!user) {
|
|
6954
|
+
throw new Error(`User ${data.user} not found in the database.`);
|
|
6955
|
+
}
|
|
6956
|
+
const testCase = await db3.from("test_cases").where({ id: data.test_case_id }).first();
|
|
6957
|
+
if (!testCase) {
|
|
6958
|
+
throw new Error(`Test case ${data.test_case_id} not found in the database.`);
|
|
6959
|
+
}
|
|
6960
|
+
return {
|
|
6961
|
+
agentInstance,
|
|
6962
|
+
backend,
|
|
6963
|
+
user,
|
|
6964
|
+
testCase,
|
|
6965
|
+
evalRun,
|
|
6966
|
+
messages: data.inputs
|
|
6967
|
+
};
|
|
6968
|
+
};
|
|
6969
|
+
var pollJobResult = async ({ queue, jobId }) => {
|
|
6970
|
+
let attempts = 0;
|
|
6971
|
+
let timeoutInSeconds = queue.timeoutInSeconds || 180;
|
|
6972
|
+
const startTime = Date.now();
|
|
6973
|
+
let result;
|
|
6974
|
+
while (true) {
|
|
6975
|
+
attempts++;
|
|
6976
|
+
const job = await import_bullmq5.Job.fromId(queue.queue, jobId);
|
|
6977
|
+
if (!job) {
|
|
6978
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
6979
|
+
continue;
|
|
6980
|
+
}
|
|
6981
|
+
const elapsedTime = Date.now() - startTime;
|
|
6982
|
+
if (elapsedTime > timeoutInSeconds * 1e3) {
|
|
6983
|
+
throw new Error(`Job ${job.id} timed out after ${timeoutInSeconds} seconds for job eval function job ${job.name}.`);
|
|
6984
|
+
}
|
|
6985
|
+
console.log(`[EXULU] polling eval function job ${job.name} for state... (attempt ${attempts})`);
|
|
6986
|
+
const jobState = await job.getState();
|
|
6987
|
+
console.log(`[EXULU] eval function job ${job.name} state: ${jobState}`);
|
|
6988
|
+
if (jobState === "failed") {
|
|
6989
|
+
throw new Error(`Job ${job.name} (${job.id}) failed with error: ${job.failedReason}.`);
|
|
6990
|
+
}
|
|
6991
|
+
if (jobState === "completed") {
|
|
6992
|
+
console.log(`[EXULU] eval function job ${job.name} completed, getting result from database...`);
|
|
6993
|
+
const { db: db3 } = await postgresClient();
|
|
6994
|
+
const entry = await db3.from("job_results").where({ job_id: job.id }).first();
|
|
6995
|
+
result = entry?.result;
|
|
6996
|
+
if (result === void 0 || result === null || result === "") {
|
|
6997
|
+
throw new Error(`Eval function ${job.id} result not found in database for job eval function job ${job.name}.`);
|
|
6998
|
+
}
|
|
6999
|
+
console.log(`[EXULU] eval function ${job.id} result: ${result}`);
|
|
7000
|
+
break;
|
|
7001
|
+
}
|
|
7002
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
7003
|
+
}
|
|
7004
|
+
return result;
|
|
7005
|
+
};
|
|
7006
|
+
var processUiMessagesFlow = async ({
|
|
7007
|
+
agents,
|
|
7008
|
+
agentInstance,
|
|
7009
|
+
agentBackend,
|
|
7010
|
+
inputMessages,
|
|
7011
|
+
contexts,
|
|
7012
|
+
user,
|
|
7013
|
+
tools,
|
|
7014
|
+
config
|
|
7015
|
+
}) => {
|
|
7016
|
+
console.log("[EXULU] processing UI messages flow for agent.");
|
|
7017
|
+
console.log("[EXULU] input messages", inputMessages);
|
|
7018
|
+
console.log("[EXULU] agent tools", agentInstance.tools?.map((x) => x.name + " (" + x.id + ")"));
|
|
7019
|
+
const disabledTools = [];
|
|
7020
|
+
let enabledTools = await getEnabledTools(agentInstance, tools, disabledTools, agents, user);
|
|
7021
|
+
console.log("[EXULU] enabled tools", enabledTools?.map((x) => x.name + " (" + x.id + ")"));
|
|
7022
|
+
const variableName = agentInstance.providerapikey;
|
|
7023
|
+
const { db: db3 } = await postgresClient();
|
|
7024
|
+
const variable = await db3.from("variables").where({ name: variableName }).first();
|
|
7025
|
+
if (!variable) {
|
|
7026
|
+
throw new Error(`Provider API key variable not found for agent ${agentInstance.name} (${agentInstance.id}).`);
|
|
7027
|
+
}
|
|
7028
|
+
let providerapikey = variable.value;
|
|
7029
|
+
if (!variable.encrypted) {
|
|
7030
|
+
throw new Error(`Provider API key variable not encrypted for agent ${agentInstance.name} (${agentInstance.id}), for security reasons you are only allowed to use encrypted variables for provider API keys.`);
|
|
7031
|
+
}
|
|
7032
|
+
if (variable.encrypted) {
|
|
7033
|
+
const bytes = import_crypto_js4.default.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
|
|
7034
|
+
providerapikey = bytes.toString(import_crypto_js4.default.enc.Utf8);
|
|
7035
|
+
}
|
|
7036
|
+
const messagesWithoutPlaceholder = inputMessages.filter(
|
|
7037
|
+
(message) => message.metadata?.type !== "placeholder"
|
|
7038
|
+
);
|
|
7039
|
+
console.log("[EXULU] messages without placeholder", messagesWithoutPlaceholder);
|
|
7040
|
+
let index = 0;
|
|
7041
|
+
let messageHistory = {
|
|
7042
|
+
messages: [],
|
|
7043
|
+
metadata: {
|
|
7044
|
+
tokens: {
|
|
7045
|
+
totalTokens: 0,
|
|
7046
|
+
reasoningTokens: 0,
|
|
7047
|
+
inputTokens: 0,
|
|
7048
|
+
outputTokens: 0,
|
|
7049
|
+
cachedInputTokens: 0
|
|
7050
|
+
},
|
|
7051
|
+
duration: 0
|
|
7052
|
+
}
|
|
7053
|
+
};
|
|
7054
|
+
for (const currentMessage of messagesWithoutPlaceholder) {
|
|
7055
|
+
console.log("[EXULU] running through the conversation");
|
|
7056
|
+
console.log("[EXULU] current index", index);
|
|
7057
|
+
console.log("[EXULU] current message", currentMessage);
|
|
7058
|
+
console.log("[EXULU] message history", messageHistory);
|
|
7059
|
+
const statistics = {
|
|
7060
|
+
label: agentInstance.name,
|
|
7061
|
+
trigger: "agent"
|
|
7062
|
+
};
|
|
7063
|
+
messageHistory = await new Promise(async (resolve, reject) => {
|
|
7064
|
+
const startTime = Date.now();
|
|
7065
|
+
try {
|
|
7066
|
+
const result = await agentBackend.generateStream({
|
|
7067
|
+
contexts,
|
|
7068
|
+
user,
|
|
7069
|
+
instructions: agentInstance.instructions,
|
|
7070
|
+
session: void 0,
|
|
7071
|
+
previousMessages: messageHistory.messages,
|
|
7072
|
+
message: currentMessage,
|
|
7073
|
+
currentTools: enabledTools,
|
|
7074
|
+
allExuluTools: tools,
|
|
7075
|
+
providerapikey,
|
|
7076
|
+
toolConfigs: agentInstance.tools,
|
|
7077
|
+
exuluConfig: config
|
|
7078
|
+
});
|
|
7079
|
+
console.log("[EXULU] consuming stream for agent.");
|
|
7080
|
+
const stream = result.stream.toUIMessageStream({
|
|
7081
|
+
messageMetadata: ({ part }) => {
|
|
7082
|
+
console.log("[EXULU] part", part.type);
|
|
7083
|
+
if (part.type === "finish") {
|
|
7084
|
+
return {
|
|
7085
|
+
totalTokens: part.totalUsage.totalTokens,
|
|
7086
|
+
reasoningTokens: part.totalUsage.reasoningTokens,
|
|
7087
|
+
inputTokens: part.totalUsage.inputTokens,
|
|
7088
|
+
outputTokens: part.totalUsage.outputTokens,
|
|
7089
|
+
cachedInputTokens: part.totalUsage.cachedInputTokens
|
|
7090
|
+
};
|
|
7091
|
+
}
|
|
7092
|
+
},
|
|
7093
|
+
originalMessages: result.originalMessages,
|
|
7094
|
+
sendReasoning: true,
|
|
7095
|
+
sendSources: true,
|
|
7096
|
+
onError: (error) => {
|
|
7097
|
+
console.error("[EXULU] Ui message stream error.", error);
|
|
7098
|
+
reject(error);
|
|
7099
|
+
return `Ui message stream error: ${error instanceof Error ? error.message : String(error)}`;
|
|
7100
|
+
},
|
|
7101
|
+
onFinish: async ({ messages, isContinuation, responseMessage }) => {
|
|
7102
|
+
const metadata = messages[messages.length - 1]?.metadata;
|
|
7103
|
+
console.log("[EXULU] Stream finished with messages:", messages);
|
|
7104
|
+
console.log("[EXULU] Stream metadata", metadata);
|
|
7105
|
+
await Promise.all([
|
|
7106
|
+
updateStatistic({
|
|
7107
|
+
name: "count",
|
|
7108
|
+
label: statistics.label,
|
|
7109
|
+
type: STATISTICS_TYPE_ENUM.AGENT_RUN,
|
|
7110
|
+
trigger: statistics.trigger,
|
|
7111
|
+
count: 1,
|
|
7112
|
+
user: user.id,
|
|
7113
|
+
role: user?.role?.id
|
|
7114
|
+
}),
|
|
7115
|
+
...metadata?.inputTokens ? [
|
|
7116
|
+
updateStatistic({
|
|
7117
|
+
name: "inputTokens",
|
|
7118
|
+
label: statistics.label,
|
|
7119
|
+
type: STATISTICS_TYPE_ENUM.AGENT_RUN,
|
|
7120
|
+
trigger: statistics.trigger,
|
|
7121
|
+
count: metadata?.inputTokens,
|
|
7122
|
+
user: user.id,
|
|
7123
|
+
role: user?.role?.id
|
|
7124
|
+
})
|
|
7125
|
+
] : [],
|
|
7126
|
+
...metadata?.outputTokens ? [
|
|
7127
|
+
updateStatistic({
|
|
7128
|
+
name: "outputTokens",
|
|
7129
|
+
label: statistics.label,
|
|
7130
|
+
type: STATISTICS_TYPE_ENUM.AGENT_RUN,
|
|
7131
|
+
trigger: statistics.trigger,
|
|
7132
|
+
count: metadata?.outputTokens
|
|
7133
|
+
})
|
|
7134
|
+
] : []
|
|
7135
|
+
]);
|
|
7136
|
+
resolve({
|
|
7137
|
+
messages,
|
|
7138
|
+
metadata: {
|
|
7139
|
+
tokens: {
|
|
7140
|
+
totalTokens: messageHistory.metadata.tokens.totalTokens + metadata?.totalTokens,
|
|
7141
|
+
reasoningTokens: messageHistory.metadata.tokens.reasoningTokens + metadata?.reasoningTokens,
|
|
7142
|
+
inputTokens: messageHistory.metadata.tokens.inputTokens + metadata?.inputTokens,
|
|
7143
|
+
outputTokens: messageHistory.metadata.tokens.outputTokens + metadata?.outputTokens,
|
|
7144
|
+
cachedInputTokens: messageHistory.metadata.tokens.cachedInputTokens + metadata?.cachedInputTokens
|
|
7145
|
+
},
|
|
7146
|
+
duration: messageHistory.metadata.duration + (Date.now() - startTime)
|
|
7147
|
+
}
|
|
7148
|
+
});
|
|
7149
|
+
}
|
|
7150
|
+
});
|
|
7151
|
+
for await (const message of stream) {
|
|
7152
|
+
console.log("[EXULU] message", message);
|
|
7153
|
+
}
|
|
7154
|
+
} catch (error) {
|
|
7155
|
+
console.error(`[EXULU] error generating stream for agent ${agentInstance.name} (${agentInstance.id}).`, error);
|
|
7156
|
+
reject(error);
|
|
7157
|
+
}
|
|
7158
|
+
});
|
|
7159
|
+
index++;
|
|
7160
|
+
}
|
|
7161
|
+
console.log("[EXULU] finished processing UI messages flow for agent, messages result", messageHistory);
|
|
7162
|
+
return messageHistory;
|
|
7163
|
+
};
|
|
7164
|
+
function getMedian(arr) {
|
|
7165
|
+
if (arr.length === 0) return 0;
|
|
7166
|
+
const sortedArr = arr.slice().sort((a, b) => a - b);
|
|
7167
|
+
const mid = Math.floor(sortedArr.length / 2);
|
|
7168
|
+
if (sortedArr.length % 2 !== 0) {
|
|
7169
|
+
return sortedArr[mid];
|
|
7170
|
+
} else {
|
|
7171
|
+
return (sortedArr[mid - 1] + sortedArr[mid]) / 2;
|
|
7172
|
+
}
|
|
7173
|
+
}
|
|
7174
|
+
function getSum(arr) {
|
|
7175
|
+
if (arr.length === 0) return 0;
|
|
7176
|
+
return arr.reduce((a, b) => a + b, 0);
|
|
7177
|
+
}
|
|
7178
|
+
function getAverage(arr) {
|
|
7179
|
+
if (arr.length === 0) return 0;
|
|
7180
|
+
return arr.reduce((a, b) => a + b, 0) / arr.length;
|
|
7181
|
+
}
|
|
6368
7182
|
|
|
6369
7183
|
// src/mcp/index.ts
|
|
6370
7184
|
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
@@ -6517,7 +7331,6 @@ var claudeOpus4Agent = new ExuluAgent2({
|
|
|
6517
7331
|
audio: [],
|
|
6518
7332
|
video: []
|
|
6519
7333
|
},
|
|
6520
|
-
evals: [],
|
|
6521
7334
|
maxContextLength: 2e5,
|
|
6522
7335
|
config: {
|
|
6523
7336
|
name: `CLAUDE-OPUS-4`,
|
|
@@ -6552,7 +7365,6 @@ var claudeSonnet4Agent = new ExuluAgent2({
|
|
|
6552
7365
|
audio: [],
|
|
6553
7366
|
video: []
|
|
6554
7367
|
},
|
|
6555
|
-
evals: [],
|
|
6556
7368
|
maxContextLength: 2e5,
|
|
6557
7369
|
config: {
|
|
6558
7370
|
name: `CLAUDE-SONNET-4`,
|
|
@@ -6580,7 +7392,6 @@ var claudeSonnet45Agent = new ExuluAgent2({
|
|
|
6580
7392
|
audio: [],
|
|
6581
7393
|
video: []
|
|
6582
7394
|
},
|
|
6583
|
-
evals: [],
|
|
6584
7395
|
maxContextLength: 2e5,
|
|
6585
7396
|
config: {
|
|
6586
7397
|
name: `CLAUDE-SONNET-4.5`,
|
|
@@ -6611,7 +7422,6 @@ var gpt5proAgent = new ExuluAgent2({
|
|
|
6611
7422
|
audio: [],
|
|
6612
7423
|
video: []
|
|
6613
7424
|
},
|
|
6614
|
-
evals: [],
|
|
6615
7425
|
maxContextLength: 4e5,
|
|
6616
7426
|
config: {
|
|
6617
7427
|
name: `GPT-5-PRO`,
|
|
@@ -6639,7 +7449,6 @@ var gpt5CodexAgent = new ExuluAgent2({
|
|
|
6639
7449
|
audio: [],
|
|
6640
7450
|
video: []
|
|
6641
7451
|
},
|
|
6642
|
-
evals: [],
|
|
6643
7452
|
maxContextLength: 4e5,
|
|
6644
7453
|
config: {
|
|
6645
7454
|
name: `GPT-5-CODEX`,
|
|
@@ -6667,7 +7476,6 @@ var gpt5MiniAgent = new ExuluAgent2({
|
|
|
6667
7476
|
audio: [],
|
|
6668
7477
|
video: []
|
|
6669
7478
|
},
|
|
6670
|
-
evals: [],
|
|
6671
7479
|
maxContextLength: 4e5,
|
|
6672
7480
|
config: {
|
|
6673
7481
|
name: `GPT-5-MINI`,
|
|
@@ -6702,7 +7510,6 @@ var gpt5agent = new ExuluAgent2({
|
|
|
6702
7510
|
audio: [],
|
|
6703
7511
|
video: []
|
|
6704
7512
|
},
|
|
6705
|
-
evals: [],
|
|
6706
7513
|
maxContextLength: 4e5,
|
|
6707
7514
|
config: {
|
|
6708
7515
|
name: `GPT-5`,
|
|
@@ -6737,7 +7544,6 @@ var gpt5NanoAgent = new ExuluAgent2({
|
|
|
6737
7544
|
audio: [],
|
|
6738
7545
|
video: []
|
|
6739
7546
|
},
|
|
6740
|
-
evals: [],
|
|
6741
7547
|
maxContextLength: 4e5,
|
|
6742
7548
|
config: {
|
|
6743
7549
|
name: `GPT-5-NANO`,
|
|
@@ -6765,7 +7571,6 @@ var gpt41Agent = new ExuluAgent2({
|
|
|
6765
7571
|
audio: [],
|
|
6766
7572
|
video: []
|
|
6767
7573
|
},
|
|
6768
|
-
evals: [],
|
|
6769
7574
|
maxContextLength: 1047576,
|
|
6770
7575
|
config: {
|
|
6771
7576
|
name: `GPT-4.1`,
|
|
@@ -6793,7 +7598,6 @@ var gpt41MiniAgent = new ExuluAgent2({
|
|
|
6793
7598
|
audio: [],
|
|
6794
7599
|
video: []
|
|
6795
7600
|
},
|
|
6796
|
-
evals: [],
|
|
6797
7601
|
maxContextLength: 1047576,
|
|
6798
7602
|
config: {
|
|
6799
7603
|
name: `GPT-4.1-MINI`,
|
|
@@ -6821,7 +7625,6 @@ var gpt4oAgent = new ExuluAgent2({
|
|
|
6821
7625
|
audio: [],
|
|
6822
7626
|
video: []
|
|
6823
7627
|
},
|
|
6824
|
-
evals: [],
|
|
6825
7628
|
maxContextLength: 128e3,
|
|
6826
7629
|
config: {
|
|
6827
7630
|
name: `Default agent`,
|
|
@@ -6849,7 +7652,6 @@ var gpt4oMiniAgent = new ExuluAgent2({
|
|
|
6849
7652
|
audio: [],
|
|
6850
7653
|
video: []
|
|
6851
7654
|
},
|
|
6852
|
-
evals: [],
|
|
6853
7655
|
maxContextLength: 128e3,
|
|
6854
7656
|
config: {
|
|
6855
7657
|
name: `GPT-4O-MINI`,
|
|
@@ -6940,6 +7742,55 @@ var outputsContext = new ExuluContext({
|
|
|
6940
7742
|
// src/registry/index.ts
|
|
6941
7743
|
var import_winston2 = __toESM(require("winston"), 1);
|
|
6942
7744
|
var import_util = __toESM(require("util"), 1);
|
|
7745
|
+
|
|
7746
|
+
// src/templates/evals/index.ts
|
|
7747
|
+
var import_zod3 = require("zod");
|
|
7748
|
+
var llmAsJudgeEval = new ExuluEval2({
|
|
7749
|
+
id: "llm_as_judge",
|
|
7750
|
+
name: "LLM as Judge",
|
|
7751
|
+
description: "Evaluate the output of the LLM as a judge.",
|
|
7752
|
+
execute: async ({ agent, backend, messages, testCase, config }) => {
|
|
7753
|
+
console.log("[EXULU] running llm as judge eval", { agent, backend, messages, testCase, config });
|
|
7754
|
+
let prompt = config?.prompt;
|
|
7755
|
+
if (!prompt) {
|
|
7756
|
+
console.error("[EXULU] prompt is required.");
|
|
7757
|
+
throw new Error("Prompt is required.");
|
|
7758
|
+
}
|
|
7759
|
+
const lastMessage = messages[messages.length - 1]?.parts?.filter((part) => part.type === "text").map((part) => part.text).join("\n");
|
|
7760
|
+
console.log("[EXULU] last message", lastMessage);
|
|
7761
|
+
if (!lastMessage) {
|
|
7762
|
+
return 0;
|
|
7763
|
+
}
|
|
7764
|
+
prompt = prompt.replace("{actual_output}", lastMessage);
|
|
7765
|
+
prompt = prompt.replace("{expected_output}", testCase.expected_output);
|
|
7766
|
+
if (!agent.providerapikey) {
|
|
7767
|
+
throw new Error(`Provider API key for agent ${agent.name} is required, variable name is ${agent.providerapikey} but it is not set.`);
|
|
7768
|
+
}
|
|
7769
|
+
const providerapikey = await ExuluVariables.get(agent.providerapikey);
|
|
7770
|
+
console.log("[EXULU] prompt", prompt);
|
|
7771
|
+
const response = await backend.generateSync({
|
|
7772
|
+
prompt,
|
|
7773
|
+
outputSchema: import_zod3.z.object({
|
|
7774
|
+
score: import_zod3.z.number().min(0).max(100).describe("The score between 0 and 100.")
|
|
7775
|
+
}),
|
|
7776
|
+
providerapikey
|
|
7777
|
+
});
|
|
7778
|
+
console.log("[EXULU] response", response);
|
|
7779
|
+
const score = parseFloat(response.score);
|
|
7780
|
+
if (isNaN(score)) {
|
|
7781
|
+
throw new Error(`Generated score from llm as a judge eval is not a number: ${response.score}`);
|
|
7782
|
+
}
|
|
7783
|
+
return score;
|
|
7784
|
+
},
|
|
7785
|
+
config: [{
|
|
7786
|
+
name: "prompt",
|
|
7787
|
+
description: "The prompt to send to the LLM as a judge, make sure to instruct the LLM to output a numerical score between 0 and 100. Add {actual_output} to the prompt to replace with the last message content, and {expected_output} to replace with the expected output."
|
|
7788
|
+
}],
|
|
7789
|
+
queue: queues.register("llm_as_judge", 1, 1).use(),
|
|
7790
|
+
llm: true
|
|
7791
|
+
});
|
|
7792
|
+
|
|
7793
|
+
// src/registry/index.ts
|
|
6943
7794
|
var isDev = process.env.NODE_ENV !== "production";
|
|
6944
7795
|
var consoleTransport = new import_winston2.default.transports.Console({
|
|
6945
7796
|
format: isDev ? import_winston2.default.format.combine(
|
|
@@ -6950,6 +7801,20 @@ var consoleTransport = new import_winston2.default.transports.Console({
|
|
|
6950
7801
|
})
|
|
6951
7802
|
) : import_winston2.default.format.json()
|
|
6952
7803
|
});
|
|
7804
|
+
var formatArg = (arg) => typeof arg === "object" ? import_util.default.inspect(arg, { depth: null, colors: isDev }) : String(arg);
|
|
7805
|
+
var createLogMethod = (logger, logLevel) => {
|
|
7806
|
+
return (...args) => {
|
|
7807
|
+
const lastArg = args[args.length - 1];
|
|
7808
|
+
let metadata = void 0;
|
|
7809
|
+
let messageArgs = args;
|
|
7810
|
+
if (lastArg && typeof lastArg === "object" && lastArg.__logMetadata === true) {
|
|
7811
|
+
metadata = lastArg;
|
|
7812
|
+
messageArgs = args.slice(0, -1);
|
|
7813
|
+
}
|
|
7814
|
+
const message = messageArgs.map(formatArg).join(" ");
|
|
7815
|
+
logger[logLevel](message, metadata);
|
|
7816
|
+
};
|
|
7817
|
+
};
|
|
6953
7818
|
var isValidPostgresName = (id) => {
|
|
6954
7819
|
const regex = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
6955
7820
|
const isValid = regex.test(id);
|
|
@@ -6959,6 +7824,7 @@ var isValidPostgresName = (id) => {
|
|
|
6959
7824
|
var ExuluApp = class {
|
|
6960
7825
|
_agents = [];
|
|
6961
7826
|
_config;
|
|
7827
|
+
_evals = [];
|
|
6962
7828
|
_queues = [];
|
|
6963
7829
|
_contexts = {};
|
|
6964
7830
|
_tools = [];
|
|
@@ -6967,7 +7833,11 @@ var ExuluApp = class {
|
|
|
6967
7833
|
}
|
|
6968
7834
|
// Factory function so we can async
|
|
6969
7835
|
// initialize the MCP server if needed.
|
|
6970
|
-
create = async ({ contexts, agents, config, tools }) => {
|
|
7836
|
+
create = async ({ contexts, agents, config, tools, evals }) => {
|
|
7837
|
+
this._evals = [
|
|
7838
|
+
llmAsJudgeEval,
|
|
7839
|
+
...evals ?? []
|
|
7840
|
+
];
|
|
6971
7841
|
this._contexts = {
|
|
6972
7842
|
...contexts,
|
|
6973
7843
|
codeStandardsContext,
|
|
@@ -7019,11 +7889,12 @@ var ExuluApp = class {
|
|
|
7019
7889
|
console.error(`%c[EXULU] Invalid ID found for a context, tool or agent: ${invalid.map((x) => x.id).join(", ")}. An ID must begin with a letter (a-z) or underscore (_). Subsequent characters in a name can be letters, digits (0-9), or underscores and be a max length of 80 characters and at least 5 characters long.`, "color: orange; font-weight: bold; \n \n");
|
|
7020
7890
|
throw new Error(`Invalid ID found for a context, tool or agent: ${invalid.map((x) => x.id).join(", ")}. An ID must begin with a letter (a-z) or underscore (_). Subsequent characters in a name can be letters, digits (0-9), or underscores and be a max length of 80 characters and at least 5 characters long.`);
|
|
7021
7891
|
}
|
|
7022
|
-
const contextsArray = Object.values(contexts || {});
|
|
7023
7892
|
const queueSet = /* @__PURE__ */ new Set();
|
|
7024
|
-
|
|
7025
|
-
|
|
7026
|
-
|
|
7893
|
+
if (redisServer.host?.length && redisServer.port?.length) {
|
|
7894
|
+
queues.register(global_queues.eval_runs, 1, 1);
|
|
7895
|
+
for (const queue of queues.list.values()) {
|
|
7896
|
+
const config2 = await queue.use();
|
|
7897
|
+
queueSet.add(config2);
|
|
7027
7898
|
}
|
|
7028
7899
|
}
|
|
7029
7900
|
this._queues = [...new Set(queueSet.values())];
|
|
@@ -7099,7 +7970,7 @@ var ExuluApp = class {
|
|
|
7099
7970
|
};
|
|
7100
7971
|
bullmq = {
|
|
7101
7972
|
workers: {
|
|
7102
|
-
create: async () => {
|
|
7973
|
+
create: async (queues3) => {
|
|
7103
7974
|
if (!this._config) {
|
|
7104
7975
|
throw new Error("Config not initialized, make sure to call await ExuluApp.create() first when starting your server.");
|
|
7105
7976
|
}
|
|
@@ -7112,16 +7983,22 @@ var ExuluApp = class {
|
|
|
7112
7983
|
enableOtel: this._config?.workers?.telemetry?.enabled ?? false,
|
|
7113
7984
|
transports
|
|
7114
7985
|
});
|
|
7115
|
-
|
|
7116
|
-
console.
|
|
7117
|
-
console.
|
|
7118
|
-
console.
|
|
7119
|
-
console.
|
|
7120
|
-
|
|
7986
|
+
console.log = createLogMethod(logger, "info");
|
|
7987
|
+
console.info = createLogMethod(logger, "info");
|
|
7988
|
+
console.warn = createLogMethod(logger, "warn");
|
|
7989
|
+
console.error = createLogMethod(logger, "error");
|
|
7990
|
+
console.debug = createLogMethod(logger, "debug");
|
|
7991
|
+
let filteredQueues = this._queues;
|
|
7992
|
+
if (queues3) {
|
|
7993
|
+
filteredQueues = filteredQueues.filter((q) => queues3.includes(q.queue.name));
|
|
7994
|
+
}
|
|
7121
7995
|
return await createWorkers(
|
|
7122
|
-
this.
|
|
7996
|
+
this._agents,
|
|
7997
|
+
filteredQueues,
|
|
7123
7998
|
this._config,
|
|
7124
7999
|
Object.values(this._contexts ?? {}),
|
|
8000
|
+
this._evals,
|
|
8001
|
+
this._tools,
|
|
7125
8002
|
tracer
|
|
7126
8003
|
);
|
|
7127
8004
|
}
|
|
@@ -7138,16 +8015,16 @@ var ExuluApp = class {
|
|
|
7138
8015
|
if (this._config?.telemetry?.enabled) {
|
|
7139
8016
|
tracer = import_api4.trace.getTracer("exulu", "1.0.0");
|
|
7140
8017
|
}
|
|
8018
|
+
const transports = this._config?.logger?.winston?.transports ?? [consoleTransport];
|
|
7141
8019
|
const logger = logger_default({
|
|
7142
8020
|
enableOtel: this._config?.telemetry?.enabled ?? false,
|
|
7143
|
-
transports
|
|
8021
|
+
transports
|
|
7144
8022
|
});
|
|
7145
|
-
|
|
7146
|
-
console.
|
|
7147
|
-
console.
|
|
7148
|
-
console.
|
|
7149
|
-
console.
|
|
7150
|
-
console.debug = (...args) => logger.debug(args.map(formatArg).join(" "));
|
|
8023
|
+
console.log = createLogMethod(logger, "info");
|
|
8024
|
+
console.info = createLogMethod(logger, "info");
|
|
8025
|
+
console.warn = createLogMethod(logger, "warn");
|
|
8026
|
+
console.error = createLogMethod(logger, "error");
|
|
8027
|
+
console.debug = createLogMethod(logger, "debug");
|
|
7151
8028
|
if (!this._config) {
|
|
7152
8029
|
throw new Error("Config not initialized, make sure to call await ExuluApp.create() first when starting your server.");
|
|
7153
8030
|
}
|
|
@@ -7157,7 +8034,9 @@ var ExuluApp = class {
|
|
|
7157
8034
|
this._tools,
|
|
7158
8035
|
Object.values(this._contexts ?? {}),
|
|
7159
8036
|
this._config,
|
|
7160
|
-
|
|
8037
|
+
this._evals,
|
|
8038
|
+
tracer,
|
|
8039
|
+
this._queues
|
|
7161
8040
|
);
|
|
7162
8041
|
if (this._config?.MCP.enabled) {
|
|
7163
8042
|
const mcp = new ExuluMCP();
|
|
@@ -8386,7 +9265,8 @@ var {
|
|
|
8386
9265
|
variablesSchema: variablesSchema3,
|
|
8387
9266
|
workflowTemplatesSchema: workflowTemplatesSchema3,
|
|
8388
9267
|
rbacSchema: rbacSchema3,
|
|
8389
|
-
projectsSchema: projectsSchema3
|
|
9268
|
+
projectsSchema: projectsSchema3,
|
|
9269
|
+
jobResultsSchema: jobResultsSchema3
|
|
8390
9270
|
} = coreSchemas.get();
|
|
8391
9271
|
var addMissingFields = async (knex, tableName, fields, skipFields = []) => {
|
|
8392
9272
|
for (const field of fields) {
|
|
@@ -8420,6 +9300,7 @@ var up = async function(knex) {
|
|
|
8420
9300
|
platformConfigurationsSchema3(),
|
|
8421
9301
|
statisticsSchema3(),
|
|
8422
9302
|
projectsSchema3(),
|
|
9303
|
+
jobResultsSchema3(),
|
|
8423
9304
|
rbacSchema3(),
|
|
8424
9305
|
agentsSchema3(),
|
|
8425
9306
|
variablesSchema3(),
|
|
@@ -8604,20 +9485,7 @@ var create = ({
|
|
|
8604
9485
|
};
|
|
8605
9486
|
|
|
8606
9487
|
// src/index.ts
|
|
8607
|
-
var
|
|
8608
|
-
|
|
8609
|
-
// types/enums/jobs.ts
|
|
8610
|
-
var JOB_STATUS_ENUM = {
|
|
8611
|
-
completed: "completed",
|
|
8612
|
-
failed: "failed",
|
|
8613
|
-
delayed: "delayed",
|
|
8614
|
-
active: "active",
|
|
8615
|
-
waiting: "waiting",
|
|
8616
|
-
paused: "paused",
|
|
8617
|
-
stuck: "stuck"
|
|
8618
|
-
};
|
|
8619
|
-
|
|
8620
|
-
// src/index.ts
|
|
9488
|
+
var import_crypto_js5 = __toESM(require("crypto-js"), 1);
|
|
8621
9489
|
var ExuluJobs = {
|
|
8622
9490
|
redis: redisClient,
|
|
8623
9491
|
jobs: {
|
|
@@ -8654,8 +9522,8 @@ var ExuluVariables = {
|
|
|
8654
9522
|
throw new Error(`Variable ${name} not found.`);
|
|
8655
9523
|
}
|
|
8656
9524
|
if (variable.encrypted) {
|
|
8657
|
-
const bytes =
|
|
8658
|
-
variable.value = bytes.toString(
|
|
9525
|
+
const bytes = import_crypto_js5.default.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
|
|
9526
|
+
variable.value = bytes.toString(import_crypto_js5.default.enc.Utf8);
|
|
8659
9527
|
}
|
|
8660
9528
|
return variable.value;
|
|
8661
9529
|
}
|
|
@@ -8791,5 +9659,6 @@ var ExuluChunkers = {
|
|
|
8791
9659
|
ExuluTool,
|
|
8792
9660
|
ExuluUtils,
|
|
8793
9661
|
ExuluVariables,
|
|
8794
|
-
db
|
|
9662
|
+
db,
|
|
9663
|
+
logMetadata
|
|
8795
9664
|
});
|