@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.js
CHANGED
|
@@ -66,7 +66,7 @@ var validateJob = (job) => {
|
|
|
66
66
|
// src/registry/classes.ts
|
|
67
67
|
import "bullmq";
|
|
68
68
|
import { z } from "zod";
|
|
69
|
-
import { convertToModelMessages,
|
|
69
|
+
import { convertToModelMessages, generateObject, generateText, streamText, tool, validateUIMessages, stepCountIs } from "ai";
|
|
70
70
|
|
|
71
71
|
// types/enums/statistics.ts
|
|
72
72
|
var STATISTICS_TYPE_ENUM = {
|
|
@@ -99,7 +99,17 @@ async function ensureDatabaseExists() {
|
|
|
99
99
|
database: "postgres",
|
|
100
100
|
// Connect to default database
|
|
101
101
|
password: process.env.POSTGRES_DB_PASSWORD,
|
|
102
|
-
ssl: process.env.POSTGRES_DB_SSL === "true" ? { rejectUnauthorized: false } : false
|
|
102
|
+
ssl: process.env.POSTGRES_DB_SSL === "true" ? { rejectUnauthorized: false } : false,
|
|
103
|
+
connectionTimeoutMillis: 1e4
|
|
104
|
+
},
|
|
105
|
+
pool: {
|
|
106
|
+
min: 2,
|
|
107
|
+
max: 4,
|
|
108
|
+
acquireTimeoutMillis: 3e4,
|
|
109
|
+
createTimeoutMillis: 3e4,
|
|
110
|
+
idleTimeoutMillis: 3e4,
|
|
111
|
+
reapIntervalMillis: 1e3,
|
|
112
|
+
createRetryIntervalMillis: 200
|
|
103
113
|
}
|
|
104
114
|
});
|
|
105
115
|
try {
|
|
@@ -144,7 +154,17 @@ async function postgresClient() {
|
|
|
144
154
|
user: process.env.POSTGRES_DB_USER,
|
|
145
155
|
database: dbName,
|
|
146
156
|
password: process.env.POSTGRES_DB_PASSWORD,
|
|
147
|
-
ssl: process.env.POSTGRES_DB_SSL === "true" ? { rejectUnauthorized: false } : false
|
|
157
|
+
ssl: process.env.POSTGRES_DB_SSL === "true" ? { rejectUnauthorized: false } : false,
|
|
158
|
+
connectionTimeoutMillis: 1e4
|
|
159
|
+
},
|
|
160
|
+
pool: {
|
|
161
|
+
min: 2,
|
|
162
|
+
max: 20,
|
|
163
|
+
acquireTimeoutMillis: 3e4,
|
|
164
|
+
createTimeoutMillis: 3e4,
|
|
165
|
+
idleTimeoutMillis: 3e4,
|
|
166
|
+
reapIntervalMillis: 1e3,
|
|
167
|
+
createRetryIntervalMillis: 200
|
|
148
168
|
}
|
|
149
169
|
});
|
|
150
170
|
try {
|
|
@@ -192,7 +212,9 @@ var bullmqDecorator = async ({
|
|
|
192
212
|
workflow,
|
|
193
213
|
item,
|
|
194
214
|
context,
|
|
195
|
-
retries
|
|
215
|
+
retries,
|
|
216
|
+
backoff,
|
|
217
|
+
timeoutInSeconds
|
|
196
218
|
}) => {
|
|
197
219
|
const types = [
|
|
198
220
|
embedder,
|
|
@@ -216,30 +238,35 @@ var bullmqDecorator = async ({
|
|
|
216
238
|
if (embedder) {
|
|
217
239
|
type = "embedder";
|
|
218
240
|
}
|
|
241
|
+
const jobData = {
|
|
242
|
+
label,
|
|
243
|
+
type: `${type}`,
|
|
244
|
+
timeoutInSeconds: timeoutInSeconds || 180,
|
|
245
|
+
// 3 minutes default
|
|
246
|
+
inputs,
|
|
247
|
+
...user && { user },
|
|
248
|
+
...role && { role },
|
|
249
|
+
...trigger && { trigger },
|
|
250
|
+
...workflow && { workflow },
|
|
251
|
+
...embedder && { embedder },
|
|
252
|
+
...processor && { processor },
|
|
253
|
+
...evaluation && { evaluation },
|
|
254
|
+
...item && { item },
|
|
255
|
+
...context && { context }
|
|
256
|
+
};
|
|
219
257
|
const redisId = uuidv4();
|
|
220
258
|
const job = await queue.add(
|
|
221
259
|
`${embedder || workflow || processor || evaluation}`,
|
|
222
|
-
|
|
223
|
-
label,
|
|
224
|
-
type: `${type}`,
|
|
225
|
-
inputs,
|
|
226
|
-
...user && { user },
|
|
227
|
-
...role && { role },
|
|
228
|
-
...trigger && { trigger },
|
|
229
|
-
...workflow && { workflow },
|
|
230
|
-
...embedder && { embedder },
|
|
231
|
-
...processor && { processor },
|
|
232
|
-
...evaluation && { evaluation },
|
|
233
|
-
...item && { item },
|
|
234
|
-
...context && { context }
|
|
235
|
-
},
|
|
260
|
+
jobData,
|
|
236
261
|
{
|
|
237
262
|
jobId: redisId,
|
|
238
263
|
// Setting it to 3 as a sensible default, as
|
|
239
264
|
// many AI services are quite unstable.
|
|
240
265
|
attempts: retries || 3,
|
|
241
266
|
// todo make this configurable?
|
|
242
|
-
|
|
267
|
+
removeOnComplete: 5e3,
|
|
268
|
+
removeOnFail: 1e4,
|
|
269
|
+
backoff: backoff || {
|
|
243
270
|
type: "exponential",
|
|
244
271
|
delay: 2e3
|
|
245
272
|
}
|
|
@@ -684,6 +711,17 @@ var requestValidators = {
|
|
|
684
711
|
// src/registry/utils/graphql.ts
|
|
685
712
|
import bcrypt3 from "bcryptjs";
|
|
686
713
|
|
|
714
|
+
// types/enums/jobs.ts
|
|
715
|
+
var JOB_STATUS_ENUM = {
|
|
716
|
+
completed: "completed",
|
|
717
|
+
failed: "failed",
|
|
718
|
+
delayed: "delayed",
|
|
719
|
+
active: "active",
|
|
720
|
+
waiting: "waiting",
|
|
721
|
+
paused: "paused",
|
|
722
|
+
stuck: "stuck"
|
|
723
|
+
};
|
|
724
|
+
|
|
687
725
|
// src/postgres/core-schema.ts
|
|
688
726
|
var agentMessagesSchema = {
|
|
689
727
|
type: "agent_messages",
|
|
@@ -1118,6 +1156,45 @@ var evalSetsSchema = {
|
|
|
1118
1156
|
}
|
|
1119
1157
|
]
|
|
1120
1158
|
};
|
|
1159
|
+
var jobResultsSchema = {
|
|
1160
|
+
type: "job_results",
|
|
1161
|
+
name: {
|
|
1162
|
+
plural: "job_results",
|
|
1163
|
+
singular: "job_result"
|
|
1164
|
+
},
|
|
1165
|
+
fields: [
|
|
1166
|
+
{
|
|
1167
|
+
name: "job_id",
|
|
1168
|
+
type: "text"
|
|
1169
|
+
},
|
|
1170
|
+
{
|
|
1171
|
+
name: "state",
|
|
1172
|
+
type: "text"
|
|
1173
|
+
},
|
|
1174
|
+
{
|
|
1175
|
+
name: "error",
|
|
1176
|
+
type: "json"
|
|
1177
|
+
},
|
|
1178
|
+
{
|
|
1179
|
+
name: "label",
|
|
1180
|
+
type: "text",
|
|
1181
|
+
index: true
|
|
1182
|
+
},
|
|
1183
|
+
{
|
|
1184
|
+
name: "tries",
|
|
1185
|
+
type: "number",
|
|
1186
|
+
default: 0
|
|
1187
|
+
},
|
|
1188
|
+
{
|
|
1189
|
+
name: "result",
|
|
1190
|
+
type: "json"
|
|
1191
|
+
},
|
|
1192
|
+
{
|
|
1193
|
+
name: "metadata",
|
|
1194
|
+
type: "json"
|
|
1195
|
+
}
|
|
1196
|
+
]
|
|
1197
|
+
};
|
|
1121
1198
|
var evalRunsSchema = {
|
|
1122
1199
|
type: "eval_runs",
|
|
1123
1200
|
name: {
|
|
@@ -1126,6 +1203,15 @@ var evalRunsSchema = {
|
|
|
1126
1203
|
},
|
|
1127
1204
|
RBAC: true,
|
|
1128
1205
|
fields: [
|
|
1206
|
+
{
|
|
1207
|
+
name: "name",
|
|
1208
|
+
type: "text"
|
|
1209
|
+
},
|
|
1210
|
+
{
|
|
1211
|
+
name: "timeout_in_seconds",
|
|
1212
|
+
type: "number",
|
|
1213
|
+
default: 180
|
|
1214
|
+
},
|
|
1129
1215
|
{
|
|
1130
1216
|
name: "eval_set_id",
|
|
1131
1217
|
type: "uuid",
|
|
@@ -1137,7 +1223,7 @@ var evalRunsSchema = {
|
|
|
1137
1223
|
required: true
|
|
1138
1224
|
},
|
|
1139
1225
|
{
|
|
1140
|
-
name: "
|
|
1226
|
+
name: "eval_functions",
|
|
1141
1227
|
type: "json",
|
|
1142
1228
|
required: true
|
|
1143
1229
|
},
|
|
@@ -1148,7 +1234,7 @@ var evalRunsSchema = {
|
|
|
1148
1234
|
{
|
|
1149
1235
|
name: "scoring_method",
|
|
1150
1236
|
type: "enum",
|
|
1151
|
-
enumValues: ["
|
|
1237
|
+
enumValues: ["median", "sum", "average"],
|
|
1152
1238
|
required: true
|
|
1153
1239
|
},
|
|
1154
1240
|
{
|
|
@@ -1272,7 +1358,8 @@ var coreSchemas = {
|
|
|
1272
1358
|
variablesSchema: () => addCoreFields(variablesSchema),
|
|
1273
1359
|
rbacSchema: () => addCoreFields(rbacSchema),
|
|
1274
1360
|
workflowTemplatesSchema: () => addCoreFields(workflowTemplatesSchema),
|
|
1275
|
-
platformConfigurationsSchema: () => addCoreFields(platformConfigurationsSchema)
|
|
1361
|
+
platformConfigurationsSchema: () => addCoreFields(platformConfigurationsSchema),
|
|
1362
|
+
jobResultsSchema: () => addCoreFields(jobResultsSchema)
|
|
1276
1363
|
};
|
|
1277
1364
|
}
|
|
1278
1365
|
};
|
|
@@ -1335,11 +1422,11 @@ var bullmq = {
|
|
|
1335
1422
|
if (!data.inputs) {
|
|
1336
1423
|
throw new Error(`Missing property "inputs" in data for job ${id}.`);
|
|
1337
1424
|
}
|
|
1338
|
-
if (data.type !== "embedder" && data.type !== "workflow") {
|
|
1339
|
-
throw new Error(`Property "type" in data for job ${id} must be of value "embedder" or "
|
|
1425
|
+
if (data.type !== "embedder" && data.type !== "workflow" && data.type !== "processor" && data.type !== "eval_run" && data.type !== "eval_function") {
|
|
1426
|
+
throw new Error(`Property "type" in data for job ${id} must be of value "embedder", "workflow", "processor", "eval_run" or "eval_function".`);
|
|
1340
1427
|
}
|
|
1341
|
-
if (!data.workflow && !data.embedder) {
|
|
1342
|
-
throw new Error(`Either a workflow or
|
|
1428
|
+
if (!data.workflow && !data.embedder && !data.processor && !data.eval_run_id && !data.eval_functions?.length) {
|
|
1429
|
+
throw new Error(`Either a workflow, embedder, processor, eval_run or eval_functions must be set for job ${id}.`);
|
|
1343
1430
|
}
|
|
1344
1431
|
}
|
|
1345
1432
|
};
|
|
@@ -1543,6 +1630,7 @@ var generateApiKey = async (name, email) => {
|
|
|
1543
1630
|
};
|
|
1544
1631
|
|
|
1545
1632
|
// src/registry/utils/graphql.ts
|
|
1633
|
+
import { v4 as uuidv42 } from "uuid";
|
|
1546
1634
|
var GraphQLDate = new GraphQLScalarType({
|
|
1547
1635
|
name: "Date",
|
|
1548
1636
|
description: "Date custom scalar type",
|
|
@@ -1638,7 +1726,6 @@ ${enumValues}
|
|
|
1638
1726
|
fields.push(" maxContextLength: Int");
|
|
1639
1727
|
fields.push(" provider: String");
|
|
1640
1728
|
fields.push(" slug: String");
|
|
1641
|
-
fields.push(" evals: [AgentEvalFunction]");
|
|
1642
1729
|
}
|
|
1643
1730
|
const rbacField = table.RBAC ? " RBAC: RBACData" : "";
|
|
1644
1731
|
const typeDef = `
|
|
@@ -1850,11 +1937,6 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
1850
1937
|
if (!record) {
|
|
1851
1938
|
throw new Error("Record not found");
|
|
1852
1939
|
}
|
|
1853
|
-
if (tableNamePlural === "jobs") {
|
|
1854
|
-
if (!user.super_admin && record.created_by !== user.id) {
|
|
1855
|
-
throw new Error("You are not authorized to edit this record");
|
|
1856
|
-
}
|
|
1857
|
-
}
|
|
1858
1940
|
if (record.rights_mode === "public") {
|
|
1859
1941
|
return true;
|
|
1860
1942
|
}
|
|
@@ -2277,10 +2359,6 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2277
2359
|
}
|
|
2278
2360
|
var applyAccessControl = (table, user, query) => {
|
|
2279
2361
|
const tableNamePlural = table.name.plural.toLowerCase();
|
|
2280
|
-
if (!user.super_admin && table.name.plural === "jobs") {
|
|
2281
|
-
query = query.where("created_by", user.id);
|
|
2282
|
-
return query;
|
|
2283
|
-
}
|
|
2284
2362
|
if (table.name.plural !== "agent_sessions" && user.super_admin === true) {
|
|
2285
2363
|
return query;
|
|
2286
2364
|
}
|
|
@@ -2415,14 +2493,6 @@ var addAgentFields = async (requestedFields, agents, result, tools, user) => {
|
|
|
2415
2493
|
if (requestedFields.includes("provider")) {
|
|
2416
2494
|
result.provider = backend?.provider || "";
|
|
2417
2495
|
}
|
|
2418
|
-
if (requestedFields.includes("evals")) {
|
|
2419
|
-
result.evals = backend?.evals?.map((evalFunc) => ({
|
|
2420
|
-
id: evalFunc.id,
|
|
2421
|
-
name: evalFunc.name,
|
|
2422
|
-
description: evalFunc.description,
|
|
2423
|
-
config: evalFunc.config || []
|
|
2424
|
-
})) || [];
|
|
2425
|
-
}
|
|
2426
2496
|
if (!requestedFields.includes("backend")) {
|
|
2427
2497
|
delete result.backend;
|
|
2428
2498
|
}
|
|
@@ -3125,7 +3195,7 @@ var contextToTableDefinition = (context) => {
|
|
|
3125
3195
|
});
|
|
3126
3196
|
return addCoreFields(definition);
|
|
3127
3197
|
};
|
|
3128
|
-
function createSDL(tables, contexts, agents, tools, config) {
|
|
3198
|
+
function createSDL(tables, contexts, agents, tools, config, evals, queues3) {
|
|
3129
3199
|
const contextSchemas = contexts.map((context) => contextToTableDefinition(context));
|
|
3130
3200
|
tables.forEach((table) => {
|
|
3131
3201
|
if (!table.fields.some((field) => field.name === "createdAt")) {
|
|
@@ -3320,15 +3390,39 @@ type PageInfo {
|
|
|
3320
3390
|
typeDefs += `
|
|
3321
3391
|
providers: ProviderPaginationResult
|
|
3322
3392
|
`;
|
|
3393
|
+
typeDefs += `
|
|
3394
|
+
queue(queue: QueueEnum!): QueueResult
|
|
3395
|
+
`;
|
|
3396
|
+
typeDefs += `
|
|
3397
|
+
evals: EvalPaginationResult
|
|
3398
|
+
`;
|
|
3323
3399
|
typeDefs += `
|
|
3324
3400
|
contexts: ContextPaginationResult
|
|
3325
3401
|
`;
|
|
3326
3402
|
typeDefs += `
|
|
3327
3403
|
contextById(id: ID!): Context
|
|
3328
3404
|
`;
|
|
3405
|
+
mutationDefs += `
|
|
3406
|
+
runEval(id: ID!, test_case_ids: [ID!]): RunEvalReturnPayload
|
|
3407
|
+
`;
|
|
3408
|
+
mutationDefs += `
|
|
3409
|
+
drainQueue(queue: QueueEnum!): JobActionReturnPayload
|
|
3410
|
+
`;
|
|
3411
|
+
mutationDefs += `
|
|
3412
|
+
pauseQueue(queue: QueueEnum!): JobActionReturnPayload
|
|
3413
|
+
`;
|
|
3414
|
+
mutationDefs += `
|
|
3415
|
+
resumeQueue(queue: QueueEnum!): JobActionReturnPayload
|
|
3416
|
+
`;
|
|
3417
|
+
mutationDefs += `
|
|
3418
|
+
deleteJob(queue: QueueEnum!, id: String!): JobActionReturnPayload
|
|
3419
|
+
`;
|
|
3329
3420
|
typeDefs += `
|
|
3330
3421
|
tools: ToolPaginationResult
|
|
3331
3422
|
`;
|
|
3423
|
+
typeDefs += `
|
|
3424
|
+
jobs(queue: QueueEnum!, statusses: [JobStateEnum!]): JobPaginationResult
|
|
3425
|
+
`;
|
|
3332
3426
|
resolvers.Query["providers"] = async (_, args, context, info) => {
|
|
3333
3427
|
const requestedFields = getRequestedFields(info);
|
|
3334
3428
|
return {
|
|
@@ -3341,6 +3435,227 @@ type PageInfo {
|
|
|
3341
3435
|
})
|
|
3342
3436
|
};
|
|
3343
3437
|
};
|
|
3438
|
+
resolvers.Query["queue"] = async (_, args, context, info) => {
|
|
3439
|
+
if (!args.queue) {
|
|
3440
|
+
throw new Error("Queue name is required");
|
|
3441
|
+
}
|
|
3442
|
+
const queue = queues.list.get(args.queue);
|
|
3443
|
+
if (!queue) {
|
|
3444
|
+
throw new Error("Queue not found");
|
|
3445
|
+
}
|
|
3446
|
+
const config2 = await queue.use();
|
|
3447
|
+
return {
|
|
3448
|
+
name: config2.queue.name,
|
|
3449
|
+
concurrency: config2.concurrency,
|
|
3450
|
+
ratelimit: config2.ratelimit,
|
|
3451
|
+
isMaxed: await config2.queue.isMaxed(),
|
|
3452
|
+
isPaused: await config2.queue.isPaused(),
|
|
3453
|
+
jobs: {
|
|
3454
|
+
paused: await config2.queue.isPaused(),
|
|
3455
|
+
completed: await config2.queue.getJobCountByTypes("completed"),
|
|
3456
|
+
failed: await config2.queue.getJobCountByTypes("failed"),
|
|
3457
|
+
waiting: await config2.queue.getJobCountByTypes("waiting"),
|
|
3458
|
+
active: await config2.queue.getJobCountByTypes("active"),
|
|
3459
|
+
delayed: await config2.queue.getJobCountByTypes("delayed")
|
|
3460
|
+
}
|
|
3461
|
+
};
|
|
3462
|
+
};
|
|
3463
|
+
resolvers.Mutation["runEval"] = async (_, args, context, info) => {
|
|
3464
|
+
console.log("[EXULU] /evals/run/:id", args.id);
|
|
3465
|
+
const user = context.user;
|
|
3466
|
+
const eval_run_id = args.id;
|
|
3467
|
+
if (!user.super_admin && (!user.role || user.role.evals !== "write")) {
|
|
3468
|
+
throw new Error("You don't have permission to run evals. Required: super_admin or evals write access.");
|
|
3469
|
+
}
|
|
3470
|
+
const { db: db3 } = await postgresClient();
|
|
3471
|
+
const evalRun = await db3.from("eval_runs").where({ id: eval_run_id }).first();
|
|
3472
|
+
if (!evalRun) {
|
|
3473
|
+
throw new Error("Eval run not found in database.");
|
|
3474
|
+
}
|
|
3475
|
+
const hasAccessToEvalRun = await checkRecordAccess(evalRun, "write", user);
|
|
3476
|
+
if (!hasAccessToEvalRun) {
|
|
3477
|
+
throw new Error("You don't have access to this eval run.");
|
|
3478
|
+
}
|
|
3479
|
+
let testCaseIds = evalRun.test_case_ids ? typeof evalRun.test_case_ids === "string" ? JSON.parse(evalRun.test_case_ids) : evalRun.test_case_ids : [];
|
|
3480
|
+
const eval_functions = evalRun.eval_functions ? typeof evalRun.eval_functions === "string" ? JSON.parse(evalRun.eval_functions) : evalRun.eval_functions : [];
|
|
3481
|
+
if (!testCaseIds || testCaseIds.length === 0) {
|
|
3482
|
+
throw new Error("No test cases selected for this eval run.");
|
|
3483
|
+
}
|
|
3484
|
+
if (!eval_functions || eval_functions.length === 0) {
|
|
3485
|
+
throw new Error("No eval functions selected for this eval run.");
|
|
3486
|
+
}
|
|
3487
|
+
if (args.test_case_ids) {
|
|
3488
|
+
testCaseIds = testCaseIds.filter((testCase) => args.test_case_ids.includes(testCase));
|
|
3489
|
+
}
|
|
3490
|
+
console.log("[EXULU] test cases ids filtered", testCaseIds);
|
|
3491
|
+
const testCases = await db3.from("test_cases").whereIn("id", testCaseIds);
|
|
3492
|
+
if (testCases.length === 0) {
|
|
3493
|
+
throw new Error("No test cases found for eval run.");
|
|
3494
|
+
}
|
|
3495
|
+
const agentInstance = await loadAgent(evalRun.agent_id);
|
|
3496
|
+
if (!agentInstance) {
|
|
3497
|
+
throw new Error("Agent instance not found for eval run.");
|
|
3498
|
+
}
|
|
3499
|
+
const evalQueue = await queues.register("eval_runs", 1, 1).use();
|
|
3500
|
+
const jobIds = [];
|
|
3501
|
+
for (const testCase of testCases) {
|
|
3502
|
+
const jobData = {
|
|
3503
|
+
label: `Eval Run ${eval_run_id} - Test Case ${testCase.id}`,
|
|
3504
|
+
trigger: "api",
|
|
3505
|
+
timeoutInSeconds: evalRun.timeout_in_seconds || 180,
|
|
3506
|
+
// default to 3 minutes
|
|
3507
|
+
type: "eval_run",
|
|
3508
|
+
eval_run_id,
|
|
3509
|
+
eval_run_name: evalRun.name,
|
|
3510
|
+
test_case_id: testCase.id,
|
|
3511
|
+
test_case_name: testCase.name,
|
|
3512
|
+
eval_functions,
|
|
3513
|
+
// Array of eval function IDs - worker will create child jobs for these
|
|
3514
|
+
agent_id: evalRun.agent_id,
|
|
3515
|
+
inputs: testCase.inputs,
|
|
3516
|
+
expected_output: testCase.expected_output,
|
|
3517
|
+
expected_tools: testCase.expected_tools,
|
|
3518
|
+
expected_knowledge_sources: testCase.expected_knowledge_sources,
|
|
3519
|
+
expected_agent_tools: testCase.expected_agent_tools,
|
|
3520
|
+
config: evalRun.config,
|
|
3521
|
+
scoring_method: evalRun.scoring_method,
|
|
3522
|
+
pass_threshold: evalRun.pass_threshold,
|
|
3523
|
+
user: user.id,
|
|
3524
|
+
role: user.role?.id
|
|
3525
|
+
};
|
|
3526
|
+
const redisId = uuidv42();
|
|
3527
|
+
const job = await evalQueue.queue.add("eval_run", jobData, {
|
|
3528
|
+
jobId: redisId,
|
|
3529
|
+
// Setting it to 3 as a sensible default, as
|
|
3530
|
+
// many AI services are quite unstable.
|
|
3531
|
+
attempts: evalQueue.retries || 1,
|
|
3532
|
+
removeOnComplete: 5e3,
|
|
3533
|
+
removeOnFail: 1e4,
|
|
3534
|
+
backoff: evalQueue.backoff || {
|
|
3535
|
+
type: "exponential",
|
|
3536
|
+
delay: 2e3
|
|
3537
|
+
}
|
|
3538
|
+
});
|
|
3539
|
+
jobIds.push(job.id);
|
|
3540
|
+
}
|
|
3541
|
+
const response = {
|
|
3542
|
+
jobs: jobIds,
|
|
3543
|
+
count: jobIds.length
|
|
3544
|
+
};
|
|
3545
|
+
const requestedFields = getRequestedFields(info);
|
|
3546
|
+
const mapped = {};
|
|
3547
|
+
requestedFields.forEach((field) => {
|
|
3548
|
+
mapped[field] = response[field];
|
|
3549
|
+
});
|
|
3550
|
+
return mapped;
|
|
3551
|
+
};
|
|
3552
|
+
resolvers.Mutation["drainQueue"] = async (_, args, context, info) => {
|
|
3553
|
+
if (!args.queue) {
|
|
3554
|
+
throw new Error("Queue name is required");
|
|
3555
|
+
}
|
|
3556
|
+
const queue = queues.list.get(args.queue);
|
|
3557
|
+
if (!queue) {
|
|
3558
|
+
throw new Error("Queue not found");
|
|
3559
|
+
}
|
|
3560
|
+
const config2 = await queue.use();
|
|
3561
|
+
await config2.queue.drain();
|
|
3562
|
+
return { success: true };
|
|
3563
|
+
};
|
|
3564
|
+
resolvers.Mutation["pauseQueue"] = async (_, args, context, info) => {
|
|
3565
|
+
if (!args.queue) {
|
|
3566
|
+
throw new Error("Queue name is required");
|
|
3567
|
+
}
|
|
3568
|
+
const queue = queues.list.get(args.queue);
|
|
3569
|
+
if (!queue) {
|
|
3570
|
+
throw new Error("Queue not found");
|
|
3571
|
+
}
|
|
3572
|
+
const config2 = await queue.use();
|
|
3573
|
+
await config2.queue.pause();
|
|
3574
|
+
return { success: true };
|
|
3575
|
+
};
|
|
3576
|
+
resolvers.Mutation["resumeQueue"] = async (_, args, context, info) => {
|
|
3577
|
+
if (!args.queue) {
|
|
3578
|
+
throw new Error("Queue name is required");
|
|
3579
|
+
}
|
|
3580
|
+
const queue = queues.list.get(args.queue);
|
|
3581
|
+
if (!queue) {
|
|
3582
|
+
throw new Error("Queue not found");
|
|
3583
|
+
}
|
|
3584
|
+
const config2 = await queue.use();
|
|
3585
|
+
await config2.queue.resume();
|
|
3586
|
+
return { success: true };
|
|
3587
|
+
};
|
|
3588
|
+
resolvers.Mutation["deleteJob"] = async (_, args, context, info) => {
|
|
3589
|
+
if (!args.id) {
|
|
3590
|
+
throw new Error("Job ID is required");
|
|
3591
|
+
}
|
|
3592
|
+
if (!args.queue) {
|
|
3593
|
+
throw new Error("Queue name is required");
|
|
3594
|
+
}
|
|
3595
|
+
const queue = queues.list.get(args.queue);
|
|
3596
|
+
if (!queue) {
|
|
3597
|
+
throw new Error("Queue not found");
|
|
3598
|
+
}
|
|
3599
|
+
const config2 = await queue.use();
|
|
3600
|
+
await config2.queue.remove(args.id);
|
|
3601
|
+
return { success: true };
|
|
3602
|
+
};
|
|
3603
|
+
resolvers.Query["evals"] = async (_, args, context, info) => {
|
|
3604
|
+
const requestedFields = getRequestedFields(info);
|
|
3605
|
+
return {
|
|
3606
|
+
items: evals.map((_eval) => {
|
|
3607
|
+
const object = {};
|
|
3608
|
+
requestedFields.forEach((field) => {
|
|
3609
|
+
object[field] = _eval[field];
|
|
3610
|
+
});
|
|
3611
|
+
return object;
|
|
3612
|
+
})
|
|
3613
|
+
};
|
|
3614
|
+
};
|
|
3615
|
+
resolvers.Query["jobs"] = async (_, args, context, info) => {
|
|
3616
|
+
if (!args.queue) {
|
|
3617
|
+
throw new Error("Queue name is required");
|
|
3618
|
+
}
|
|
3619
|
+
const { client: client2 } = await redisClient();
|
|
3620
|
+
if (!client2) {
|
|
3621
|
+
throw new Error("Redis client not created properly");
|
|
3622
|
+
}
|
|
3623
|
+
const {
|
|
3624
|
+
jobs,
|
|
3625
|
+
count
|
|
3626
|
+
} = await getJobsByQueueAndName(
|
|
3627
|
+
args.queue,
|
|
3628
|
+
args.statusses,
|
|
3629
|
+
args.page || 1,
|
|
3630
|
+
args.limit || 100
|
|
3631
|
+
);
|
|
3632
|
+
console.log("[EXULU] jobs", jobs.map((job) => job.name));
|
|
3633
|
+
const requestedFields = getRequestedFields(info);
|
|
3634
|
+
return {
|
|
3635
|
+
items: await Promise.all(jobs.map(async (job) => {
|
|
3636
|
+
const object = {};
|
|
3637
|
+
for (const field of requestedFields) {
|
|
3638
|
+
if (field === "data") {
|
|
3639
|
+
object[field] = job[field];
|
|
3640
|
+
} else if (field === "timestamp") {
|
|
3641
|
+
object[field] = new Date(job[field]).toISOString();
|
|
3642
|
+
} else if (field === "state") {
|
|
3643
|
+
object[field] = await job.getState();
|
|
3644
|
+
} else {
|
|
3645
|
+
object[field] = job[field];
|
|
3646
|
+
}
|
|
3647
|
+
}
|
|
3648
|
+
return object;
|
|
3649
|
+
})),
|
|
3650
|
+
pageInfo: {
|
|
3651
|
+
pageCount: Math.ceil(count / (args.limit || 100)),
|
|
3652
|
+
itemCount: count,
|
|
3653
|
+
currentPage: args.page || 1,
|
|
3654
|
+
hasPreviousPage: args.page && args.page > 1 ? true : false,
|
|
3655
|
+
hasNextPage: args.page && args.page < Math.ceil(jobs.length / (args.limit || 100)) ? true : false
|
|
3656
|
+
}
|
|
3657
|
+
};
|
|
3658
|
+
};
|
|
3344
3659
|
resolvers.Query["contexts"] = async (_, args, context, info) => {
|
|
3345
3660
|
const data = contexts.map((context2) => ({
|
|
3346
3661
|
id: context2.id,
|
|
@@ -3446,7 +3761,32 @@ type PageInfo {
|
|
|
3446
3761
|
};
|
|
3447
3762
|
modelDefs += `
|
|
3448
3763
|
type ProviderPaginationResult {
|
|
3449
|
-
|
|
3764
|
+
items: [Provider]!
|
|
3765
|
+
}
|
|
3766
|
+
`;
|
|
3767
|
+
modelDefs += `
|
|
3768
|
+
type QueueResult {
|
|
3769
|
+
name: String!
|
|
3770
|
+
concurrency: Int!
|
|
3771
|
+
ratelimit: Int!
|
|
3772
|
+
isMaxed: Boolean!
|
|
3773
|
+
isPaused: Boolean!
|
|
3774
|
+
jobs: QueueJobsCounts
|
|
3775
|
+
}
|
|
3776
|
+
`;
|
|
3777
|
+
modelDefs += `
|
|
3778
|
+
type QueueJobsCounts {
|
|
3779
|
+
paused: Int!
|
|
3780
|
+
completed: Int!
|
|
3781
|
+
failed: Int!
|
|
3782
|
+
waiting: Int!
|
|
3783
|
+
active: Int!
|
|
3784
|
+
delayed: Int!
|
|
3785
|
+
}
|
|
3786
|
+
`;
|
|
3787
|
+
modelDefs += `
|
|
3788
|
+
type EvalPaginationResult {
|
|
3789
|
+
items: [Eval]!
|
|
3450
3790
|
}
|
|
3451
3791
|
`;
|
|
3452
3792
|
modelDefs += `
|
|
@@ -3459,6 +3799,12 @@ type PageInfo {
|
|
|
3459
3799
|
items: [Tool]!
|
|
3460
3800
|
}
|
|
3461
3801
|
`;
|
|
3802
|
+
modelDefs += `
|
|
3803
|
+
type JobPaginationResult {
|
|
3804
|
+
items: [Job]!
|
|
3805
|
+
pageInfo: PageInfo!
|
|
3806
|
+
}
|
|
3807
|
+
`;
|
|
3462
3808
|
typeDefs += "}\n";
|
|
3463
3809
|
mutationDefs += "}\n";
|
|
3464
3810
|
const genericTypes = `
|
|
@@ -3516,6 +3862,19 @@ type Provider {
|
|
|
3516
3862
|
type: EnumProviderType!
|
|
3517
3863
|
}
|
|
3518
3864
|
|
|
3865
|
+
type Eval {
|
|
3866
|
+
id: ID!
|
|
3867
|
+
name: String!
|
|
3868
|
+
description: String!
|
|
3869
|
+
llm: Boolean!
|
|
3870
|
+
config: [EvalConfig!]
|
|
3871
|
+
}
|
|
3872
|
+
|
|
3873
|
+
type EvalConfig {
|
|
3874
|
+
name: String!
|
|
3875
|
+
description: String!
|
|
3876
|
+
}
|
|
3877
|
+
|
|
3519
3878
|
type Context {
|
|
3520
3879
|
id: ID!
|
|
3521
3880
|
name: String!
|
|
@@ -3527,6 +3886,15 @@ type Context {
|
|
|
3527
3886
|
configuration: JSON
|
|
3528
3887
|
}
|
|
3529
3888
|
|
|
3889
|
+
type RunEvalReturnPayload {
|
|
3890
|
+
jobs: [String!]!
|
|
3891
|
+
count: Int!
|
|
3892
|
+
}
|
|
3893
|
+
|
|
3894
|
+
type JobActionReturnPayload {
|
|
3895
|
+
success: Boolean!
|
|
3896
|
+
}
|
|
3897
|
+
|
|
3530
3898
|
type ContextField {
|
|
3531
3899
|
name: String!
|
|
3532
3900
|
type: String!
|
|
@@ -3542,10 +3910,35 @@ type Tool {
|
|
|
3542
3910
|
config: JSON
|
|
3543
3911
|
}
|
|
3544
3912
|
|
|
3913
|
+
type Job {
|
|
3914
|
+
id: String!
|
|
3915
|
+
name: String!
|
|
3916
|
+
returnvalue: JSON
|
|
3917
|
+
stacktrace: [String]
|
|
3918
|
+
failedReason: String
|
|
3919
|
+
state: String!
|
|
3920
|
+
data: JSON
|
|
3921
|
+
timestamp: Date
|
|
3922
|
+
}
|
|
3923
|
+
|
|
3545
3924
|
enum EnumProviderType {
|
|
3546
3925
|
agent
|
|
3547
3926
|
}
|
|
3548
3927
|
|
|
3928
|
+
enum QueueEnum {
|
|
3929
|
+
${queues.list.keys().toArray().join("\n")}
|
|
3930
|
+
}
|
|
3931
|
+
|
|
3932
|
+
enum JobStateEnum {
|
|
3933
|
+
${JOB_STATUS_ENUM.active}
|
|
3934
|
+
${JOB_STATUS_ENUM.waiting}
|
|
3935
|
+
${JOB_STATUS_ENUM.delayed}
|
|
3936
|
+
${JOB_STATUS_ENUM.failed}
|
|
3937
|
+
${JOB_STATUS_ENUM.completed}
|
|
3938
|
+
${JOB_STATUS_ENUM.paused}
|
|
3939
|
+
${JOB_STATUS_ENUM.stuck}
|
|
3940
|
+
}
|
|
3941
|
+
|
|
3549
3942
|
type StatisticsResult {
|
|
3550
3943
|
group: String!
|
|
3551
3944
|
count: Int!
|
|
@@ -3575,6 +3968,25 @@ var validateCreateOrRemoveSuperAdminPermission = async (tableNamePlural, input,
|
|
|
3575
3968
|
}
|
|
3576
3969
|
}
|
|
3577
3970
|
};
|
|
3971
|
+
async function getJobsByQueueAndName(queueName, statusses, page, limit) {
|
|
3972
|
+
const queue = queues.list.get(queueName);
|
|
3973
|
+
if (!queue) {
|
|
3974
|
+
throw new Error(`Queue ${queueName} not found`);
|
|
3975
|
+
}
|
|
3976
|
+
const config = await queue.use();
|
|
3977
|
+
const startIndex = (page || 1) - 1;
|
|
3978
|
+
const endIndex = startIndex + (limit || 100);
|
|
3979
|
+
const jobs = await config.queue.getJobs(statusses || [], startIndex, endIndex, false);
|
|
3980
|
+
const counts = await config.queue.getJobCounts(...statusses || []);
|
|
3981
|
+
let total = 0;
|
|
3982
|
+
if (counts) {
|
|
3983
|
+
total = Object.keys(counts).reduce((acc, key) => acc + (counts[key] || 0), 0);
|
|
3984
|
+
}
|
|
3985
|
+
return {
|
|
3986
|
+
jobs,
|
|
3987
|
+
count: total
|
|
3988
|
+
};
|
|
3989
|
+
}
|
|
3578
3990
|
|
|
3579
3991
|
// src/registry/classes.ts
|
|
3580
3992
|
import {
|
|
@@ -3643,6 +4055,7 @@ var addPrefixToKey = (keyPath, config) => {
|
|
|
3643
4055
|
return `${prefix}/${keyPath}`;
|
|
3644
4056
|
};
|
|
3645
4057
|
var uploadFile = async (user, file, key, config, options = {}) => {
|
|
4058
|
+
console.log("[EXULU] Uploading file to S3", key);
|
|
3646
4059
|
const client2 = getS3Client(config);
|
|
3647
4060
|
let folder = `${user}/`;
|
|
3648
4061
|
const fullKey = addPrefixToKey(!key.includes(folder) ? folder + key : key, config);
|
|
@@ -4256,27 +4669,34 @@ function errorHandler(error) {
|
|
|
4256
4669
|
}
|
|
4257
4670
|
return JSON.stringify(error);
|
|
4258
4671
|
}
|
|
4259
|
-
var
|
|
4672
|
+
var ExuluEval2 = class {
|
|
4260
4673
|
id;
|
|
4261
4674
|
name;
|
|
4262
4675
|
description;
|
|
4676
|
+
llm;
|
|
4263
4677
|
execute;
|
|
4264
4678
|
config;
|
|
4265
4679
|
queue;
|
|
4266
|
-
constructor({ id, name, description, execute: execute2, config, queue }) {
|
|
4680
|
+
constructor({ id, name, description, execute: execute2, config, queue, llm }) {
|
|
4267
4681
|
this.id = id;
|
|
4268
4682
|
this.name = name;
|
|
4269
4683
|
this.description = description;
|
|
4270
4684
|
this.execute = execute2;
|
|
4271
4685
|
this.config = config;
|
|
4686
|
+
this.llm = llm;
|
|
4272
4687
|
this.queue = queue;
|
|
4273
4688
|
}
|
|
4274
|
-
async run(
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4689
|
+
async run(agent, backend, testCase, messages, config) {
|
|
4690
|
+
try {
|
|
4691
|
+
const score = await this.execute({ agent, backend, testCase, messages, config });
|
|
4692
|
+
if (score < 0 || score > 100) {
|
|
4693
|
+
throw new Error(`Eval function ${this.name} must return a score between 0 and 100, got ${score}`);
|
|
4694
|
+
}
|
|
4695
|
+
return score;
|
|
4696
|
+
} catch (error) {
|
|
4697
|
+
console.error(`[EXULU] error running eval function ${this.name}.`, error);
|
|
4698
|
+
throw new Error(`Error running eval function ${this.name}: ${error instanceof Error ? error.message : String(error)}`);
|
|
4278
4699
|
}
|
|
4279
|
-
return score;
|
|
4280
4700
|
}
|
|
4281
4701
|
};
|
|
4282
4702
|
var ExuluAgent2 = class {
|
|
@@ -4291,13 +4711,13 @@ var ExuluAgent2 = class {
|
|
|
4291
4711
|
type;
|
|
4292
4712
|
streaming = false;
|
|
4293
4713
|
maxContextLength;
|
|
4714
|
+
queue;
|
|
4294
4715
|
rateLimit;
|
|
4295
4716
|
config;
|
|
4296
|
-
evals;
|
|
4297
4717
|
// private memory: Memory | undefined; // TODO do own implementation
|
|
4298
4718
|
model;
|
|
4299
4719
|
capabilities;
|
|
4300
|
-
constructor({ id, name, description, config, rateLimit, capabilities, type, maxContextLength,
|
|
4720
|
+
constructor({ id, name, description, config, rateLimit, capabilities, type, maxContextLength, provider, queue }) {
|
|
4301
4721
|
this.id = id;
|
|
4302
4722
|
this.name = name;
|
|
4303
4723
|
this.description = description;
|
|
@@ -4306,7 +4726,7 @@ var ExuluAgent2 = class {
|
|
|
4306
4726
|
this.config = config;
|
|
4307
4727
|
this.type = type;
|
|
4308
4728
|
this.maxContextLength = maxContextLength;
|
|
4309
|
-
this.
|
|
4729
|
+
this.queue = queue;
|
|
4310
4730
|
this.capabilities = capabilities || {
|
|
4311
4731
|
text: false,
|
|
4312
4732
|
images: [],
|
|
@@ -4405,7 +4825,7 @@ var ExuluAgent2 = class {
|
|
|
4405
4825
|
prompt,
|
|
4406
4826
|
user,
|
|
4407
4827
|
session,
|
|
4408
|
-
|
|
4828
|
+
inputMessages,
|
|
4409
4829
|
currentTools,
|
|
4410
4830
|
allExuluTools,
|
|
4411
4831
|
statistics,
|
|
@@ -4423,10 +4843,10 @@ var ExuluAgent2 = class {
|
|
|
4423
4843
|
if (!this.config) {
|
|
4424
4844
|
throw new Error("Config is required for generating.");
|
|
4425
4845
|
}
|
|
4426
|
-
if (prompt &&
|
|
4846
|
+
if (prompt && inputMessages?.length) {
|
|
4427
4847
|
throw new Error("Message and prompt cannot be provided at the same time.");
|
|
4428
4848
|
}
|
|
4429
|
-
if (!prompt && !
|
|
4849
|
+
if (!prompt && !inputMessages?.length) {
|
|
4430
4850
|
throw new Error("Prompt or message is required for generating.");
|
|
4431
4851
|
}
|
|
4432
4852
|
if (outputSchema && !prompt) {
|
|
@@ -4436,18 +4856,18 @@ var ExuluAgent2 = class {
|
|
|
4436
4856
|
apiKey: providerapikey
|
|
4437
4857
|
});
|
|
4438
4858
|
console.log("[EXULU] Model for agent: " + this.name, " created for generating sync.");
|
|
4439
|
-
let messages = [];
|
|
4440
|
-
if (
|
|
4859
|
+
let messages = inputMessages || [];
|
|
4860
|
+
if (messages && session && user) {
|
|
4441
4861
|
const previousMessages = await getAgentMessages({
|
|
4442
4862
|
session,
|
|
4443
4863
|
user: user.id,
|
|
4444
4864
|
limit: 50,
|
|
4445
4865
|
page: 1
|
|
4446
4866
|
});
|
|
4447
|
-
const previousMessagesContent = previousMessages.map((
|
|
4867
|
+
const previousMessagesContent = previousMessages.map((message) => JSON.parse(message.content));
|
|
4448
4868
|
messages = await validateUIMessages({
|
|
4449
4869
|
// append the new message to the previous messages:
|
|
4450
|
-
messages: [...previousMessagesContent,
|
|
4870
|
+
messages: [...previousMessagesContent, ...messages]
|
|
4451
4871
|
});
|
|
4452
4872
|
}
|
|
4453
4873
|
console.log("[EXULU] Message count for agent: " + this.name, "loaded for generating sync.", messages.length);
|
|
@@ -4583,13 +5003,12 @@ var ExuluAgent2 = class {
|
|
|
4583
5003
|
return "";
|
|
4584
5004
|
};
|
|
4585
5005
|
generateStream = async ({
|
|
4586
|
-
express: express3,
|
|
4587
5006
|
user,
|
|
4588
5007
|
session,
|
|
4589
5008
|
message,
|
|
5009
|
+
previousMessages,
|
|
4590
5010
|
currentTools,
|
|
4591
5011
|
allExuluTools,
|
|
4592
|
-
statistics,
|
|
4593
5012
|
toolConfigs,
|
|
4594
5013
|
providerapikey,
|
|
4595
5014
|
contexts,
|
|
@@ -4597,27 +5016,34 @@ var ExuluAgent2 = class {
|
|
|
4597
5016
|
instructions
|
|
4598
5017
|
}) => {
|
|
4599
5018
|
if (!this.model) {
|
|
5019
|
+
console.error("[EXULU] Model is required for streaming.");
|
|
4600
5020
|
throw new Error("Model is required for streaming.");
|
|
4601
5021
|
}
|
|
4602
5022
|
if (!this.config) {
|
|
5023
|
+
console.error("[EXULU] Config is required for streaming.");
|
|
4603
5024
|
throw new Error("Config is required for generating.");
|
|
4604
5025
|
}
|
|
4605
5026
|
if (!message) {
|
|
5027
|
+
console.error("[EXULU] Message is required for streaming.");
|
|
4606
5028
|
throw new Error("Message is required for streaming.");
|
|
4607
5029
|
}
|
|
4608
5030
|
const model = this.model.create({
|
|
4609
5031
|
apiKey: providerapikey
|
|
4610
5032
|
});
|
|
4611
5033
|
let messages = [];
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
|
|
4620
|
-
|
|
5034
|
+
let previousMessagesContent = previousMessages || [];
|
|
5035
|
+
if (session) {
|
|
5036
|
+
console.log("[EXULU] loading previous messages from session: " + session);
|
|
5037
|
+
const previousMessages2 = await getAgentMessages({
|
|
5038
|
+
session,
|
|
5039
|
+
user: user.id,
|
|
5040
|
+
limit: 50,
|
|
5041
|
+
page: 1
|
|
5042
|
+
});
|
|
5043
|
+
previousMessagesContent = previousMessages2.map(
|
|
5044
|
+
(message2) => JSON.parse(message2.content)
|
|
5045
|
+
);
|
|
5046
|
+
}
|
|
4621
5047
|
messages = await validateUIMessages({
|
|
4622
5048
|
// append the new message to the previous messages:
|
|
4623
5049
|
messages: [...previousMessagesContent, message]
|
|
@@ -4650,80 +5076,17 @@ var ExuluAgent2 = class {
|
|
|
4650
5076
|
user,
|
|
4651
5077
|
exuluConfig
|
|
4652
5078
|
),
|
|
4653
|
-
onError: (error) => console.error("[EXULU] chat stream error.", error)
|
|
4654
|
-
// stopWhen: [stepCountIs(1)],
|
|
4655
|
-
});
|
|
4656
|
-
result.consumeStream();
|
|
4657
|
-
result.pipeUIMessageStreamToResponse(express3.res, {
|
|
4658
|
-
messageMetadata: ({ part }) => {
|
|
4659
|
-
if (part.type === "finish") {
|
|
4660
|
-
return {
|
|
4661
|
-
totalTokens: part.totalUsage.totalTokens,
|
|
4662
|
-
reasoningTokens: part.totalUsage.reasoningTokens,
|
|
4663
|
-
inputTokens: part.totalUsage.inputTokens,
|
|
4664
|
-
outputTokens: part.totalUsage.outputTokens,
|
|
4665
|
-
cachedInputTokens: part.totalUsage.cachedInputTokens
|
|
4666
|
-
};
|
|
4667
|
-
}
|
|
4668
|
-
},
|
|
4669
|
-
originalMessages: messages,
|
|
4670
|
-
sendReasoning: true,
|
|
4671
|
-
sendSources: true,
|
|
4672
5079
|
onError: (error) => {
|
|
4673
|
-
console.error("[EXULU] chat
|
|
4674
|
-
|
|
4675
|
-
},
|
|
4676
|
-
generateMessageId: createIdGenerator({
|
|
4677
|
-
prefix: "msg_",
|
|
4678
|
-
size: 16
|
|
4679
|
-
}),
|
|
4680
|
-
onFinish: async ({ messages: messages2, isContinuation, isAborted, responseMessage }) => {
|
|
4681
|
-
if (session) {
|
|
4682
|
-
await saveChat({
|
|
4683
|
-
session,
|
|
4684
|
-
user: user.id,
|
|
4685
|
-
messages: messages2.filter((x) => !previousMessagesContent.find((y) => y.id === x.id))
|
|
4686
|
-
});
|
|
4687
|
-
}
|
|
4688
|
-
const metadata = messages2[messages2.length - 1]?.metadata;
|
|
4689
|
-
console.log("[EXULU] Finished streaming", metadata);
|
|
4690
|
-
console.log("[EXULU] Statistics", statistics);
|
|
4691
|
-
if (statistics) {
|
|
4692
|
-
await Promise.all([
|
|
4693
|
-
updateStatistic({
|
|
4694
|
-
name: "count",
|
|
4695
|
-
label: statistics.label,
|
|
4696
|
-
type: STATISTICS_TYPE_ENUM.AGENT_RUN,
|
|
4697
|
-
trigger: statistics.trigger,
|
|
4698
|
-
count: 1,
|
|
4699
|
-
user: user.id,
|
|
4700
|
-
role: user?.role?.id
|
|
4701
|
-
}),
|
|
4702
|
-
...metadata?.inputTokens ? [
|
|
4703
|
-
updateStatistic({
|
|
4704
|
-
name: "inputTokens",
|
|
4705
|
-
label: statistics.label,
|
|
4706
|
-
type: STATISTICS_TYPE_ENUM.AGENT_RUN,
|
|
4707
|
-
trigger: statistics.trigger,
|
|
4708
|
-
count: metadata?.inputTokens,
|
|
4709
|
-
user: user.id,
|
|
4710
|
-
role: user?.role?.id
|
|
4711
|
-
})
|
|
4712
|
-
] : [],
|
|
4713
|
-
...metadata?.outputTokens ? [
|
|
4714
|
-
updateStatistic({
|
|
4715
|
-
name: "outputTokens",
|
|
4716
|
-
label: statistics.label,
|
|
4717
|
-
type: STATISTICS_TYPE_ENUM.AGENT_RUN,
|
|
4718
|
-
trigger: statistics.trigger,
|
|
4719
|
-
count: metadata?.outputTokens
|
|
4720
|
-
})
|
|
4721
|
-
] : []
|
|
4722
|
-
]);
|
|
4723
|
-
}
|
|
5080
|
+
console.error("[EXULU] chat stream error.", error);
|
|
5081
|
+
throw new Error(`Chat stream error: ${error instanceof Error ? error.message : String(error)}`);
|
|
4724
5082
|
}
|
|
5083
|
+
// stopWhen: [stepCountIs(1)],
|
|
4725
5084
|
});
|
|
4726
|
-
return
|
|
5085
|
+
return {
|
|
5086
|
+
stream: result,
|
|
5087
|
+
originalMessages: messages,
|
|
5088
|
+
previousMessages: previousMessagesContent
|
|
5089
|
+
};
|
|
4727
5090
|
};
|
|
4728
5091
|
};
|
|
4729
5092
|
var getAgentMessages = async ({ session, user, limit, page }) => {
|
|
@@ -4914,16 +5277,21 @@ var ExuluContext = class {
|
|
|
4914
5277
|
if (queue?.queue.name) {
|
|
4915
5278
|
console.log("[EXULU] processor is in queue mode, scheduling job.");
|
|
4916
5279
|
const job = await bullmqDecorator({
|
|
5280
|
+
timeoutInSeconds: field.processor?.config?.timeoutInSeconds || 180,
|
|
4917
5281
|
label: `${this.name} ${field.name} data processor`,
|
|
4918
5282
|
processor: `${this.id}-${field.name}`,
|
|
4919
5283
|
context: this.id,
|
|
4920
5284
|
inputs: item,
|
|
4921
5285
|
item: item.id,
|
|
4922
5286
|
queue: queue.queue,
|
|
5287
|
+
backoff: queue.backoff || {
|
|
5288
|
+
type: "exponential",
|
|
5289
|
+
delay: 2e3
|
|
5290
|
+
},
|
|
5291
|
+
retries: queue.retries || 2,
|
|
4923
5292
|
user,
|
|
4924
5293
|
role,
|
|
4925
|
-
trigger
|
|
4926
|
-
retries: 2
|
|
5294
|
+
trigger
|
|
4927
5295
|
});
|
|
4928
5296
|
return {
|
|
4929
5297
|
result: "",
|
|
@@ -4993,12 +5361,14 @@ var ExuluContext = class {
|
|
|
4993
5361
|
role
|
|
4994
5362
|
);
|
|
4995
5363
|
await db3.from(getChunksTableName(this.id)).where({ source }).delete();
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5364
|
+
if (chunks?.length) {
|
|
5365
|
+
await db3.from(getChunksTableName(this.id)).insert(chunks.map((chunk) => ({
|
|
5366
|
+
source,
|
|
5367
|
+
content: chunk.content,
|
|
5368
|
+
chunk_index: chunk.index,
|
|
5369
|
+
embedding: pgvector2.toSql(chunk.vector)
|
|
5370
|
+
})));
|
|
5371
|
+
}
|
|
5002
5372
|
await db3.from(getTableName(this.id)).where({ id: item.id }).update({
|
|
5003
5373
|
embeddings_updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
5004
5374
|
}).returning("id");
|
|
@@ -5127,9 +5497,15 @@ var ExuluContext = class {
|
|
|
5127
5497
|
if (queue?.queue.name) {
|
|
5128
5498
|
console.log("[EXULU] embedder is in queue mode, scheduling job.");
|
|
5129
5499
|
const job = await bullmqDecorator({
|
|
5500
|
+
timeoutInSeconds: queue.timeoutInSeconds || 180,
|
|
5130
5501
|
label: `${this.embedder.name}`,
|
|
5131
5502
|
embedder: this.embedder.id,
|
|
5132
5503
|
context: this.id,
|
|
5504
|
+
backoff: queue.backoff || {
|
|
5505
|
+
type: "exponential",
|
|
5506
|
+
delay: 2e3
|
|
5507
|
+
},
|
|
5508
|
+
retries: queue.retries || 2,
|
|
5133
5509
|
inputs: item,
|
|
5134
5510
|
item: item.id,
|
|
5135
5511
|
queue: queue.queue,
|
|
@@ -5381,6 +5757,8 @@ var ExuluQueues = class {
|
|
|
5381
5757
|
constructor() {
|
|
5382
5758
|
this.queues = [];
|
|
5383
5759
|
}
|
|
5760
|
+
list = /* @__PURE__ */ new Map();
|
|
5761
|
+
// list of queue names
|
|
5384
5762
|
queue(name) {
|
|
5385
5763
|
return this.queues.find((x) => x.queue?.name === name);
|
|
5386
5764
|
}
|
|
@@ -5392,40 +5770,54 @@ var ExuluQueues = class {
|
|
|
5392
5770
|
// method of ExuluQueues we need to store the desired rate limit on the queue
|
|
5393
5771
|
// here so we can use the value when creating workers for the queue instance
|
|
5394
5772
|
// as there is no way to store a rate limit value natively on a bullm queue.
|
|
5395
|
-
|
|
5396
|
-
const
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5773
|
+
register = (name, concurrency = 1, ratelimit = 1) => {
|
|
5774
|
+
const use = async () => {
|
|
5775
|
+
const existing = this.queues.find((x) => x.queue?.name === name);
|
|
5776
|
+
if (existing) {
|
|
5777
|
+
const globalConcurrency = await existing.queue.getGlobalConcurrency();
|
|
5778
|
+
if (globalConcurrency !== concurrency) {
|
|
5779
|
+
await existing.queue.setGlobalConcurrency(concurrency);
|
|
5780
|
+
}
|
|
5781
|
+
return {
|
|
5782
|
+
queue: existing.queue,
|
|
5783
|
+
ratelimit,
|
|
5784
|
+
concurrency
|
|
5785
|
+
};
|
|
5401
5786
|
}
|
|
5402
|
-
|
|
5403
|
-
queue
|
|
5404
|
-
|
|
5405
|
-
concurrency
|
|
5406
|
-
};
|
|
5407
|
-
}
|
|
5408
|
-
if (!redisServer.host?.length || !redisServer.port?.length) {
|
|
5409
|
-
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() ).`);
|
|
5410
|
-
throw new Error(`[EXULU] no redis server configured.`);
|
|
5411
|
-
}
|
|
5412
|
-
const newQueue = new Queue3(
|
|
5413
|
-
`${name}`,
|
|
5414
|
-
{
|
|
5415
|
-
connection: redisServer,
|
|
5416
|
-
telemetry: new BullMQOtel("simple-guide")
|
|
5787
|
+
if (!redisServer.host?.length || !redisServer.port?.length) {
|
|
5788
|
+
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() ).`);
|
|
5789
|
+
throw new Error(`[EXULU] no redis server configured.`);
|
|
5417
5790
|
}
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5791
|
+
const newQueue = new Queue3(
|
|
5792
|
+
`${name}`,
|
|
5793
|
+
{
|
|
5794
|
+
connection: {
|
|
5795
|
+
...redisServer,
|
|
5796
|
+
enableOfflineQueue: false
|
|
5797
|
+
},
|
|
5798
|
+
telemetry: new BullMQOtel("simple-guide")
|
|
5799
|
+
}
|
|
5800
|
+
);
|
|
5801
|
+
await newQueue.setGlobalConcurrency(concurrency);
|
|
5802
|
+
this.queues.push({
|
|
5803
|
+
queue: newQueue,
|
|
5804
|
+
ratelimit,
|
|
5805
|
+
concurrency
|
|
5806
|
+
});
|
|
5807
|
+
return {
|
|
5808
|
+
queue: newQueue,
|
|
5809
|
+
ratelimit,
|
|
5810
|
+
concurrency
|
|
5811
|
+
};
|
|
5812
|
+
};
|
|
5813
|
+
this.list.set(name, {
|
|
5814
|
+
name,
|
|
5815
|
+
concurrency,
|
|
5422
5816
|
ratelimit,
|
|
5423
|
-
|
|
5817
|
+
use
|
|
5424
5818
|
});
|
|
5425
5819
|
return {
|
|
5426
|
-
|
|
5427
|
-
ratelimit,
|
|
5428
|
-
concurrency
|
|
5820
|
+
use
|
|
5429
5821
|
};
|
|
5430
5822
|
};
|
|
5431
5823
|
};
|
|
@@ -5444,8 +5836,6 @@ import OpenAI from "openai";
|
|
|
5444
5836
|
import fs from "fs";
|
|
5445
5837
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
5446
5838
|
import "@opentelemetry/api";
|
|
5447
|
-
import { jsonSchema } from "ai";
|
|
5448
|
-
import proxy from "express-http-proxy";
|
|
5449
5839
|
import Anthropic from "@anthropic-ai/sdk";
|
|
5450
5840
|
|
|
5451
5841
|
// src/registry/utils/claude-messages.ts
|
|
@@ -5471,13 +5861,15 @@ var CLAUDE_MESSAGES = {
|
|
|
5471
5861
|
};
|
|
5472
5862
|
|
|
5473
5863
|
// src/registry/routes.ts
|
|
5864
|
+
import { createIdGenerator as createIdGenerator2 } from "ai";
|
|
5474
5865
|
var REQUEST_SIZE_LIMIT = "50mb";
|
|
5475
5866
|
var global_queues = {
|
|
5476
|
-
|
|
5867
|
+
eval_runs: "eval_runs"
|
|
5477
5868
|
};
|
|
5478
5869
|
var {
|
|
5479
5870
|
agentsSchema: agentsSchema2,
|
|
5480
5871
|
projectsSchema: projectsSchema2,
|
|
5872
|
+
jobResultsSchema: jobResultsSchema2,
|
|
5481
5873
|
testCasesSchema: testCasesSchema2,
|
|
5482
5874
|
evalSetsSchema: evalSetsSchema2,
|
|
5483
5875
|
evalRunsSchema: evalRunsSchema2,
|
|
@@ -5491,38 +5883,7 @@ var {
|
|
|
5491
5883
|
rbacSchema: rbacSchema2,
|
|
5492
5884
|
statisticsSchema: statisticsSchema2
|
|
5493
5885
|
} = coreSchemas.get();
|
|
5494
|
-
var
|
|
5495
|
-
console.log("[EXULU] creating recurring jobs.");
|
|
5496
|
-
const recurringJobSchedulersLogs = [];
|
|
5497
|
-
const queue = await queues.use(global_queues.logs_cleaner);
|
|
5498
|
-
recurringJobSchedulersLogs.push({
|
|
5499
|
-
name: global_queues.logs_cleaner,
|
|
5500
|
-
pattern: "0 10 * * * *",
|
|
5501
|
-
ttld: "30 days",
|
|
5502
|
-
opts: {
|
|
5503
|
-
backoff: 3,
|
|
5504
|
-
attempts: 5,
|
|
5505
|
-
removeOnFail: 1e3
|
|
5506
|
-
}
|
|
5507
|
-
});
|
|
5508
|
-
await queue.queue.upsertJobScheduler(
|
|
5509
|
-
"logs-cleaner-scheduler",
|
|
5510
|
-
{ pattern: "0 10 * * * *" },
|
|
5511
|
-
// every 10 minutes
|
|
5512
|
-
{
|
|
5513
|
-
name: global_queues.logs_cleaner,
|
|
5514
|
-
data: { ttld: 30 },
|
|
5515
|
-
// time to live in days
|
|
5516
|
-
opts: {
|
|
5517
|
-
backoff: 3,
|
|
5518
|
-
attempts: 5,
|
|
5519
|
-
removeOnFail: 1e3
|
|
5520
|
-
}
|
|
5521
|
-
}
|
|
5522
|
-
);
|
|
5523
|
-
return queue;
|
|
5524
|
-
};
|
|
5525
|
-
var createExpressRoutes = async (app, agents, tools, contexts, config, tracer) => {
|
|
5886
|
+
var createExpressRoutes = async (app, agents, tools, contexts, config, evals, tracer, queues3) => {
|
|
5526
5887
|
var corsOptions = {
|
|
5527
5888
|
origin: "*",
|
|
5528
5889
|
exposedHeaders: "*",
|
|
@@ -5541,19 +5902,15 @@ var createExpressRoutes = async (app, agents, tools, contexts, config, tracer) =
|
|
|
5541
5902
|
\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
|
|
5542
5903
|
\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
|
|
5543
5904
|
\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
|
|
5544
|
-
Intelligence Management Platform
|
|
5905
|
+
Intelligence Management Platform - Server
|
|
5545
5906
|
|
|
5546
5907
|
`);
|
|
5547
|
-
if (redisServer.host?.length && redisServer.port?.length) {
|
|
5548
|
-
await createRecurringJobs();
|
|
5549
|
-
} else {
|
|
5550
|
-
console.log("[EXULU] no redis server configured, not setting up recurring jobs.");
|
|
5551
|
-
}
|
|
5552
5908
|
const schema = createSDL([
|
|
5553
5909
|
usersSchema2(),
|
|
5554
5910
|
rolesSchema2(),
|
|
5555
5911
|
agentsSchema2(),
|
|
5556
5912
|
projectsSchema2(),
|
|
5913
|
+
jobResultsSchema2(),
|
|
5557
5914
|
evalRunsSchema2(),
|
|
5558
5915
|
platformConfigurationsSchema2(),
|
|
5559
5916
|
evalSetsSchema2(),
|
|
@@ -5564,7 +5921,7 @@ var createExpressRoutes = async (app, agents, tools, contexts, config, tracer) =
|
|
|
5564
5921
|
workflowTemplatesSchema2(),
|
|
5565
5922
|
statisticsSchema2(),
|
|
5566
5923
|
rbacSchema2()
|
|
5567
|
-
], contexts ?? [], agents, tools, config);
|
|
5924
|
+
], contexts ?? [], agents, tools, config, evals, queues3 || []);
|
|
5568
5925
|
const server = new ApolloServer({
|
|
5569
5926
|
cache: new InMemoryLRUCache(),
|
|
5570
5927
|
schema,
|
|
@@ -5695,103 +6052,6 @@ Mood: friendly and intelligent.
|
|
|
5695
6052
|
image: `${process.env.BACKEND}/${uuid}.png`
|
|
5696
6053
|
});
|
|
5697
6054
|
});
|
|
5698
|
-
app.post("/evals/run/:id", async (req, res) => {
|
|
5699
|
-
console.log("[EXULU] /evals/run/:id", req.params.id);
|
|
5700
|
-
const authenticationResult = await requestValidators.authenticate(req);
|
|
5701
|
-
if (!authenticationResult.user?.id) {
|
|
5702
|
-
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
5703
|
-
return;
|
|
5704
|
-
}
|
|
5705
|
-
const user = authenticationResult.user;
|
|
5706
|
-
const evalRunId = req.params.id;
|
|
5707
|
-
if (!user.super_admin && (!user.role || user.role.evals !== "write")) {
|
|
5708
|
-
res.status(403).json({
|
|
5709
|
-
message: "You don't have permission to run evals. Required: super_admin or evals write access."
|
|
5710
|
-
});
|
|
5711
|
-
return;
|
|
5712
|
-
}
|
|
5713
|
-
const { db: db3 } = await postgresClient();
|
|
5714
|
-
const evalRun = await db3.from("eval_runs").where({ id: evalRunId }).first();
|
|
5715
|
-
if (!evalRun) {
|
|
5716
|
-
res.status(404).json({
|
|
5717
|
-
message: "Eval run not found."
|
|
5718
|
-
});
|
|
5719
|
-
return;
|
|
5720
|
-
}
|
|
5721
|
-
const hasAccessToEvalRun = await checkRecordAccess(evalRun, "write", user);
|
|
5722
|
-
if (!hasAccessToEvalRun) {
|
|
5723
|
-
res.status(403).json({
|
|
5724
|
-
message: "You don't have access to this eval run."
|
|
5725
|
-
});
|
|
5726
|
-
return;
|
|
5727
|
-
}
|
|
5728
|
-
const testCaseIds = evalRun.test_case_ids ? typeof evalRun.test_case_ids === "string" ? JSON.parse(evalRun.test_case_ids) : evalRun.test_case_ids : [];
|
|
5729
|
-
const evalFunctionIds = evalRun.eval_function_ids ? typeof evalRun.eval_function_ids === "string" ? JSON.parse(evalRun.eval_function_ids) : evalRun.eval_function_ids : [];
|
|
5730
|
-
if (!testCaseIds || testCaseIds.length === 0) {
|
|
5731
|
-
res.status(400).json({
|
|
5732
|
-
message: "No test cases selected for this eval run."
|
|
5733
|
-
});
|
|
5734
|
-
return;
|
|
5735
|
-
}
|
|
5736
|
-
if (!evalFunctionIds || evalFunctionIds.length === 0) {
|
|
5737
|
-
res.status(400).json({
|
|
5738
|
-
message: "No eval functions selected for this eval run."
|
|
5739
|
-
});
|
|
5740
|
-
return;
|
|
5741
|
-
}
|
|
5742
|
-
const testCases = await db3.from("test_cases").whereIn("id", testCaseIds);
|
|
5743
|
-
if (testCases.length === 0) {
|
|
5744
|
-
res.status(404).json({
|
|
5745
|
-
message: "No test cases found."
|
|
5746
|
-
});
|
|
5747
|
-
return;
|
|
5748
|
-
}
|
|
5749
|
-
const agentInstance = await loadAgent(evalRun.agentId);
|
|
5750
|
-
if (!agentInstance) {
|
|
5751
|
-
res.status(404).json({
|
|
5752
|
-
message: "Agent instance not found."
|
|
5753
|
-
});
|
|
5754
|
-
return;
|
|
5755
|
-
}
|
|
5756
|
-
const evalQueue = await queues.use("evals");
|
|
5757
|
-
const jobIds = [];
|
|
5758
|
-
for (const testCase of testCases) {
|
|
5759
|
-
const existingJobs = await evalQueue.queue.getJobs(["waiting", "active", "delayed", "paused"]);
|
|
5760
|
-
const duplicateJob = existingJobs.find(
|
|
5761
|
-
(job2) => job2.data.evalRunId === evalRunId && job2.data.testCaseId === testCase.id && job2.data.type === "eval"
|
|
5762
|
-
);
|
|
5763
|
-
if (duplicateJob) {
|
|
5764
|
-
console.log(`[EXULU] Skipping duplicate job for eval run ${evalRunId} and test case ${testCase.id}`);
|
|
5765
|
-
continue;
|
|
5766
|
-
}
|
|
5767
|
-
const job = await evalQueue.queue.add(`eval-${testCase.id}`, {
|
|
5768
|
-
type: "eval",
|
|
5769
|
-
evalRunId,
|
|
5770
|
-
testCaseId: testCase.id,
|
|
5771
|
-
evalFunctionIds,
|
|
5772
|
-
// Array of eval function IDs - worker will create child jobs for these
|
|
5773
|
-
agentId: evalRun.agentId,
|
|
5774
|
-
inputs: testCase.inputs,
|
|
5775
|
-
expected_output: testCase.expected_output,
|
|
5776
|
-
expected_tools: testCase.expected_tools,
|
|
5777
|
-
expected_knowledge_sources: testCase.expected_knowledge_sources,
|
|
5778
|
-
expected_agent_tools: testCase.expected_agent_tools,
|
|
5779
|
-
config: evalRun.config,
|
|
5780
|
-
scoring_method: evalRun.scoring_method,
|
|
5781
|
-
pass_threshold: evalRun.pass_threshold,
|
|
5782
|
-
user: user.id,
|
|
5783
|
-
role: user.role?.id
|
|
5784
|
-
});
|
|
5785
|
-
jobIds.push(job.id);
|
|
5786
|
-
}
|
|
5787
|
-
res.status(200).json({
|
|
5788
|
-
message: `Created ${jobIds.length} eval jobs.`,
|
|
5789
|
-
jobIds,
|
|
5790
|
-
evalRunId,
|
|
5791
|
-
testCaseCount: testCases.length,
|
|
5792
|
-
evalFunctionCount: evalFunctionIds.length
|
|
5793
|
-
});
|
|
5794
|
-
});
|
|
5795
6055
|
app.get("/ping", async (req, res) => {
|
|
5796
6056
|
const authenticationResult = await requestValidators.authenticate(req);
|
|
5797
6057
|
if (!authenticationResult.user?.id) {
|
|
@@ -5920,11 +6180,11 @@ Mood: friendly and intelligent.
|
|
|
5920
6180
|
providerapikey = bytes.toString(CryptoJS3.enc.Utf8);
|
|
5921
6181
|
}
|
|
5922
6182
|
if (!!headers.stream) {
|
|
5923
|
-
|
|
5924
|
-
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
6183
|
+
const statistics = {
|
|
6184
|
+
label: agent.name,
|
|
6185
|
+
trigger: "agent"
|
|
6186
|
+
};
|
|
6187
|
+
const result = await agent.generateStream({
|
|
5928
6188
|
contexts,
|
|
5929
6189
|
user,
|
|
5930
6190
|
instructions: agentInstance.instructions,
|
|
@@ -5934,10 +6194,79 @@ Mood: friendly and intelligent.
|
|
|
5934
6194
|
allExuluTools: tools,
|
|
5935
6195
|
providerapikey,
|
|
5936
6196
|
toolConfigs: agentInstance.tools,
|
|
5937
|
-
exuluConfig: config
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
6197
|
+
exuluConfig: config
|
|
6198
|
+
});
|
|
6199
|
+
result.stream.consumeStream();
|
|
6200
|
+
result.stream.pipeUIMessageStreamToResponse(res, {
|
|
6201
|
+
messageMetadata: ({ part }) => {
|
|
6202
|
+
if (part.type === "finish") {
|
|
6203
|
+
return {
|
|
6204
|
+
totalTokens: part.totalUsage.totalTokens,
|
|
6205
|
+
reasoningTokens: part.totalUsage.reasoningTokens,
|
|
6206
|
+
inputTokens: part.totalUsage.inputTokens,
|
|
6207
|
+
outputTokens: part.totalUsage.outputTokens,
|
|
6208
|
+
cachedInputTokens: part.totalUsage.cachedInputTokens
|
|
6209
|
+
};
|
|
6210
|
+
}
|
|
6211
|
+
},
|
|
6212
|
+
originalMessages: result.originalMessages,
|
|
6213
|
+
sendReasoning: true,
|
|
6214
|
+
sendSources: true,
|
|
6215
|
+
onError: (error) => {
|
|
6216
|
+
console.error("[EXULU] chat response error.", error);
|
|
6217
|
+
return errorHandler(error);
|
|
6218
|
+
},
|
|
6219
|
+
generateMessageId: createIdGenerator2({
|
|
6220
|
+
prefix: "msg_",
|
|
6221
|
+
size: 16
|
|
6222
|
+
}),
|
|
6223
|
+
onFinish: async ({ messages, isContinuation, isAborted, responseMessage }) => {
|
|
6224
|
+
if (headers.session) {
|
|
6225
|
+
await saveChat({
|
|
6226
|
+
session: headers.session,
|
|
6227
|
+
user: user.id,
|
|
6228
|
+
messages: messages.filter((x) => !result.previousMessages.find((y) => y.id === x.id))
|
|
6229
|
+
});
|
|
6230
|
+
}
|
|
6231
|
+
const metadata = messages[messages.length - 1]?.metadata;
|
|
6232
|
+
console.log("[EXULU] Finished streaming", metadata);
|
|
6233
|
+
console.log("[EXULU] Statistics", {
|
|
6234
|
+
label: agent.name,
|
|
6235
|
+
trigger: "agent"
|
|
6236
|
+
});
|
|
6237
|
+
if (statistics) {
|
|
6238
|
+
await Promise.all([
|
|
6239
|
+
updateStatistic({
|
|
6240
|
+
name: "count",
|
|
6241
|
+
label: statistics.label,
|
|
6242
|
+
type: STATISTICS_TYPE_ENUM.AGENT_RUN,
|
|
6243
|
+
trigger: statistics.trigger,
|
|
6244
|
+
count: 1,
|
|
6245
|
+
user: user.id,
|
|
6246
|
+
role: user?.role?.id
|
|
6247
|
+
}),
|
|
6248
|
+
...metadata?.inputTokens ? [
|
|
6249
|
+
updateStatistic({
|
|
6250
|
+
name: "inputTokens",
|
|
6251
|
+
label: statistics.label,
|
|
6252
|
+
type: STATISTICS_TYPE_ENUM.AGENT_RUN,
|
|
6253
|
+
trigger: statistics.trigger,
|
|
6254
|
+
count: metadata?.inputTokens,
|
|
6255
|
+
user: user.id,
|
|
6256
|
+
role: user?.role?.id
|
|
6257
|
+
})
|
|
6258
|
+
] : [],
|
|
6259
|
+
...metadata?.outputTokens ? [
|
|
6260
|
+
updateStatistic({
|
|
6261
|
+
name: "outputTokens",
|
|
6262
|
+
label: statistics.label,
|
|
6263
|
+
type: STATISTICS_TYPE_ENUM.AGENT_RUN,
|
|
6264
|
+
trigger: statistics.trigger,
|
|
6265
|
+
count: metadata?.outputTokens
|
|
6266
|
+
})
|
|
6267
|
+
] : []
|
|
6268
|
+
]);
|
|
6269
|
+
}
|
|
5941
6270
|
}
|
|
5942
6271
|
});
|
|
5943
6272
|
return;
|
|
@@ -5946,7 +6275,7 @@ Mood: friendly and intelligent.
|
|
|
5946
6275
|
user,
|
|
5947
6276
|
instructions: agentInstance.instructions,
|
|
5948
6277
|
session: headers.session,
|
|
5949
|
-
|
|
6278
|
+
inputMessages: [req.body.message],
|
|
5950
6279
|
contexts,
|
|
5951
6280
|
currentTools: enabledTools,
|
|
5952
6281
|
allExuluTools: tools,
|
|
@@ -5968,91 +6297,6 @@ Mood: friendly and intelligent.
|
|
|
5968
6297
|
} else {
|
|
5969
6298
|
console.log("[EXULU] skipping uppy file upload routes, because no S3 compatible region, key or secret is set in ExuluApp instance.");
|
|
5970
6299
|
}
|
|
5971
|
-
app.use("/xxx/anthropic/:id", express.raw({ type: "*/*", limit: REQUEST_SIZE_LIMIT }), proxy(
|
|
5972
|
-
(req, res, next) => {
|
|
5973
|
-
return "https://api.anthropic.com";
|
|
5974
|
-
},
|
|
5975
|
-
{
|
|
5976
|
-
limit: "50mb",
|
|
5977
|
-
memoizeHost: false,
|
|
5978
|
-
preserveHostHdr: true,
|
|
5979
|
-
secure: false,
|
|
5980
|
-
reqAsBuffer: true,
|
|
5981
|
-
proxyReqBodyDecorator: function(bodyContent, srcReq) {
|
|
5982
|
-
return bodyContent;
|
|
5983
|
-
},
|
|
5984
|
-
userResDecorator: function(proxyRes, proxyResData, userReq, userRes) {
|
|
5985
|
-
console.log("[EXULU] Proxy response!", proxyResData);
|
|
5986
|
-
proxyResData = proxyResData.toString();
|
|
5987
|
-
console.log("[EXULU] Proxy response string!", proxyResData);
|
|
5988
|
-
return proxyResData;
|
|
5989
|
-
},
|
|
5990
|
-
proxyReqPathResolver: (req) => {
|
|
5991
|
-
const prefix = `/gateway/anthropic/${req.params.id}`;
|
|
5992
|
-
let path = req.url.startsWith(prefix) ? req.url.slice(prefix.length) : req.url;
|
|
5993
|
-
if (!path.startsWith("/")) path = "/" + path;
|
|
5994
|
-
console.log("[EXULU] Provider path:", path);
|
|
5995
|
-
return path;
|
|
5996
|
-
},
|
|
5997
|
-
proxyReqOptDecorator: function(proxyReqOpts, srcReq) {
|
|
5998
|
-
return new Promise(async (resolve, reject) => {
|
|
5999
|
-
try {
|
|
6000
|
-
const authenticationResult = await requestValidators.authenticate(srcReq);
|
|
6001
|
-
if (!authenticationResult.user?.id) {
|
|
6002
|
-
console.log("[EXULU] failed authentication result", authenticationResult);
|
|
6003
|
-
reject(authenticationResult.message);
|
|
6004
|
-
return;
|
|
6005
|
-
}
|
|
6006
|
-
console.log("[EXULU] Authenticated call", authenticationResult.user?.email);
|
|
6007
|
-
const { db: db3 } = await postgresClient();
|
|
6008
|
-
let query = db3("agents");
|
|
6009
|
-
query.select("*");
|
|
6010
|
-
query = applyAccessControl(agentsSchema2(), authenticationResult.user, query);
|
|
6011
|
-
query.where({ id: srcReq.params.id });
|
|
6012
|
-
const agent = await query.first();
|
|
6013
|
-
if (!agent) {
|
|
6014
|
-
reject(new Error("Agent with id " + srcReq.params.id + " not found."));
|
|
6015
|
-
return;
|
|
6016
|
-
}
|
|
6017
|
-
console.log("[EXULU] Agent loaded", agent.name);
|
|
6018
|
-
const backend = agents.find((x) => x.id === agent.backend);
|
|
6019
|
-
if (!process.env.NEXTAUTH_SECRET) {
|
|
6020
|
-
reject(new Error("Missing NEXTAUTH_SECRET"));
|
|
6021
|
-
return;
|
|
6022
|
-
}
|
|
6023
|
-
if (!agent.providerapikey) {
|
|
6024
|
-
reject(new Error("API Key not set for agent"));
|
|
6025
|
-
return;
|
|
6026
|
-
}
|
|
6027
|
-
const variableName = agent.providerapikey;
|
|
6028
|
-
const variable = await db3.from("variables").where({ name: variableName }).first();
|
|
6029
|
-
console.log("[EXULU] Variable loaded", variable);
|
|
6030
|
-
let providerapikey = variable.value;
|
|
6031
|
-
if (!variable.encrypted) {
|
|
6032
|
-
reject(new Error("API Key not encrypted for agent"));
|
|
6033
|
-
return;
|
|
6034
|
-
}
|
|
6035
|
-
if (variable.encrypted) {
|
|
6036
|
-
const bytes = CryptoJS3.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
|
|
6037
|
-
providerapikey = bytes.toString(CryptoJS3.enc.Utf8);
|
|
6038
|
-
}
|
|
6039
|
-
console.log("[EXULU] Provider API key", providerapikey);
|
|
6040
|
-
proxyReqOpts.headers["x-api-key"] = providerapikey;
|
|
6041
|
-
proxyReqOpts.rejectUnauthorized = false;
|
|
6042
|
-
delete proxyReqOpts.headers["provider"];
|
|
6043
|
-
const url = new URL("https://api.anthropic.com");
|
|
6044
|
-
proxyReqOpts.headers["host"] = url.host;
|
|
6045
|
-
proxyReqOpts.headers["anthropic-version"] = "2023-06-01";
|
|
6046
|
-
console.log("[EXULU] Proxy request headers", proxyReqOpts.headers);
|
|
6047
|
-
resolve(proxyReqOpts);
|
|
6048
|
-
} catch (error) {
|
|
6049
|
-
console.error("[EXULU] Proxy error", error);
|
|
6050
|
-
reject(error);
|
|
6051
|
-
}
|
|
6052
|
-
});
|
|
6053
|
-
}
|
|
6054
|
-
}
|
|
6055
|
-
));
|
|
6056
6300
|
app.get("/config", async (req, res) => {
|
|
6057
6301
|
res.status(200).json({
|
|
6058
6302
|
message: "Config fetched successfully.",
|
|
@@ -6224,10 +6468,36 @@ var createCustomAnthropicStreamingMessage = (message) => {
|
|
|
6224
6468
|
|
|
6225
6469
|
// src/registry/workers.ts
|
|
6226
6470
|
import IORedis from "ioredis";
|
|
6227
|
-
import { Worker } from "bullmq";
|
|
6471
|
+
import { Job, Worker } from "bullmq";
|
|
6228
6472
|
import "@opentelemetry/api";
|
|
6473
|
+
import { v4 as uuidv43 } from "uuid";
|
|
6474
|
+
import "ai";
|
|
6475
|
+
import CryptoJS4 from "crypto-js";
|
|
6476
|
+
|
|
6477
|
+
// src/registry/log-metadata.ts
|
|
6478
|
+
function logMetadata(id, additionalMetadata) {
|
|
6479
|
+
return {
|
|
6480
|
+
__logMetadata: true,
|
|
6481
|
+
id,
|
|
6482
|
+
...additionalMetadata
|
|
6483
|
+
};
|
|
6484
|
+
}
|
|
6485
|
+
|
|
6486
|
+
// src/registry/workers.ts
|
|
6229
6487
|
var redisConnection;
|
|
6230
|
-
var createWorkers = async (
|
|
6488
|
+
var createWorkers = async (agents, queues3, config, contexts, evals, tools, tracer) => {
|
|
6489
|
+
console.log(`
|
|
6490
|
+
\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
|
|
6491
|
+
\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
|
|
6492
|
+
\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
|
|
6493
|
+
\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
|
|
6494
|
+
\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
|
|
6495
|
+
\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
|
|
6496
|
+
Intelligence Management Platform - Workers
|
|
6497
|
+
|
|
6498
|
+
`);
|
|
6499
|
+
console.log("[EXULU] creating workers for " + queues3?.length + " queues.");
|
|
6500
|
+
console.log("[EXULU] queues", queues3.map((q) => q.queue.name));
|
|
6231
6501
|
if (!redisServer.host || !redisServer.port) {
|
|
6232
6502
|
console.error("[EXULU] you are trying to start worker, but no redis server is configured in the environment.");
|
|
6233
6503
|
throw new Error("No redis server configured in the environment, so cannot start worker.");
|
|
@@ -6235,80 +6505,333 @@ var createWorkers = async (queues2, config, contexts, tracer) => {
|
|
|
6235
6505
|
if (!redisConnection) {
|
|
6236
6506
|
redisConnection = new IORedis({
|
|
6237
6507
|
...redisServer,
|
|
6508
|
+
enableOfflineQueue: true,
|
|
6509
|
+
retryStrategy: function(times) {
|
|
6510
|
+
return Math.max(Math.min(Math.exp(times), 2e4), 1e3);
|
|
6511
|
+
},
|
|
6238
6512
|
maxRetriesPerRequest: null
|
|
6239
6513
|
});
|
|
6240
6514
|
}
|
|
6241
|
-
const workers =
|
|
6242
|
-
console.log(`[EXULU] creating worker for queue ${queue}.`);
|
|
6515
|
+
const workers = queues3.map((queue) => {
|
|
6516
|
+
console.log(`[EXULU] creating worker for queue ${queue.queue.name}.`);
|
|
6243
6517
|
const worker = new Worker(
|
|
6244
|
-
`${queue}`,
|
|
6518
|
+
`${queue.queue.name}`,
|
|
6245
6519
|
async (bullmqJob) => {
|
|
6520
|
+
console.log("[EXULU] starting execution for job", logMetadata(bullmqJob.name, {
|
|
6521
|
+
name: bullmqJob.name,
|
|
6522
|
+
status: await bullmqJob.getState(),
|
|
6523
|
+
type: bullmqJob.data.type
|
|
6524
|
+
}));
|
|
6246
6525
|
const { db: db3 } = await postgresClient();
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
|
|
6250
|
-
|
|
6251
|
-
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
|
|
6255
|
-
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
|
|
6259
|
-
|
|
6260
|
-
|
|
6261
|
-
|
|
6262
|
-
|
|
6263
|
-
|
|
6264
|
-
|
|
6265
|
-
|
|
6266
|
-
|
|
6267
|
-
|
|
6268
|
-
|
|
6269
|
-
|
|
6270
|
-
|
|
6271
|
-
|
|
6272
|
-
|
|
6273
|
-
|
|
6274
|
-
|
|
6275
|
-
|
|
6276
|
-
|
|
6277
|
-
|
|
6278
|
-
|
|
6526
|
+
const data = bullmqJob.data;
|
|
6527
|
+
const timeoutMs = data.timeoutInSeconds * 1e3;
|
|
6528
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
6529
|
+
setTimeout(() => {
|
|
6530
|
+
reject(new Error(`Timeout for job ${bullmqJob.id} reached after ${data.timeoutInSeconds}s`));
|
|
6531
|
+
}, timeoutMs);
|
|
6532
|
+
});
|
|
6533
|
+
const workPromise = (async () => {
|
|
6534
|
+
try {
|
|
6535
|
+
bullmq.validate(bullmqJob.id, data);
|
|
6536
|
+
if (data.type === "embedder") {
|
|
6537
|
+
console.log("[EXULU] running an embedder job.", logMetadata(bullmqJob.name));
|
|
6538
|
+
const label = `embedder-${bullmqJob.name}`;
|
|
6539
|
+
await db3.from("job_results").insert({
|
|
6540
|
+
job_id: bullmqJob.id,
|
|
6541
|
+
label,
|
|
6542
|
+
state: await bullmqJob.getState(),
|
|
6543
|
+
result: null,
|
|
6544
|
+
metadata: {}
|
|
6545
|
+
});
|
|
6546
|
+
const context = contexts.find((context2) => context2.id === data.context);
|
|
6547
|
+
if (!context) {
|
|
6548
|
+
throw new Error(`Context ${data.context} not found in the registry.`);
|
|
6549
|
+
}
|
|
6550
|
+
if (!data.embedder) {
|
|
6551
|
+
throw new Error(`No embedder set for embedder job.`);
|
|
6552
|
+
}
|
|
6553
|
+
const embedder = contexts.find((context2) => context2.embedder?.id === data.embedder);
|
|
6554
|
+
if (!embedder) {
|
|
6555
|
+
throw new Error(`Embedder ${data.embedder} not found in the registry.`);
|
|
6556
|
+
}
|
|
6557
|
+
const result = await context.createAndUpsertEmbeddings(data.inputs, config, data.user, {
|
|
6558
|
+
label: embedder.name,
|
|
6559
|
+
trigger: data.trigger
|
|
6560
|
+
}, data.role, bullmqJob.id);
|
|
6561
|
+
return {
|
|
6562
|
+
result,
|
|
6563
|
+
metadata: {}
|
|
6564
|
+
};
|
|
6279
6565
|
}
|
|
6280
|
-
|
|
6281
|
-
|
|
6282
|
-
|
|
6566
|
+
if (data.type === "processor") {
|
|
6567
|
+
console.log("[EXULU] running a processor job.", logMetadata(bullmqJob.name));
|
|
6568
|
+
const label = `processor-${bullmqJob.name}`;
|
|
6569
|
+
await db3.from("job_results").insert({
|
|
6570
|
+
job_id: bullmqJob.id,
|
|
6571
|
+
label,
|
|
6572
|
+
state: await bullmqJob.getState(),
|
|
6573
|
+
result: null,
|
|
6574
|
+
metadata: {}
|
|
6575
|
+
});
|
|
6576
|
+
const context = contexts.find((context2) => context2.id === data.context);
|
|
6577
|
+
if (!context) {
|
|
6578
|
+
throw new Error(`Context ${data.context} not found in the registry.`);
|
|
6579
|
+
}
|
|
6580
|
+
const field = context.fields.find((field2) => field2.name === data.inputs.field);
|
|
6581
|
+
if (!field) {
|
|
6582
|
+
throw new Error(`Field ${data.inputs.field} not found in the context ${data.context}.`);
|
|
6583
|
+
}
|
|
6584
|
+
if (!field.processor) {
|
|
6585
|
+
throw new Error(`Processor not set for field ${data.inputs.field} in the context ${data.context}.`);
|
|
6586
|
+
}
|
|
6587
|
+
const exuluStorage = new ExuluStorage({ config });
|
|
6588
|
+
if (!data.user) {
|
|
6589
|
+
throw new Error(`User not set for processor job.`);
|
|
6590
|
+
}
|
|
6591
|
+
if (!data.role) {
|
|
6592
|
+
throw new Error(`Role not set for processor job.`);
|
|
6593
|
+
}
|
|
6594
|
+
const result = await field.processor.execute({
|
|
6595
|
+
item: data.inputs,
|
|
6596
|
+
user: data.user,
|
|
6597
|
+
role: data.role,
|
|
6598
|
+
utils: {
|
|
6599
|
+
storage: exuluStorage,
|
|
6600
|
+
items: {
|
|
6601
|
+
update: context.updateItem,
|
|
6602
|
+
create: context.createItem,
|
|
6603
|
+
delete: context.deleteItem
|
|
6604
|
+
}
|
|
6605
|
+
},
|
|
6606
|
+
config
|
|
6607
|
+
});
|
|
6608
|
+
return {
|
|
6609
|
+
result,
|
|
6610
|
+
metadata: {}
|
|
6611
|
+
};
|
|
6283
6612
|
}
|
|
6284
|
-
if (
|
|
6285
|
-
|
|
6613
|
+
if (data.type === "eval_run") {
|
|
6614
|
+
console.log("[EXULU] running an eval run job.", logMetadata(bullmqJob.name));
|
|
6615
|
+
const label = `eval-run-${data.eval_run_id}-${data.test_case_id}`;
|
|
6616
|
+
const existingResult = await db3.from("job_results").where({ label }).first();
|
|
6617
|
+
if (existingResult) {
|
|
6618
|
+
await db3.from("job_results").where({ label }).update({
|
|
6619
|
+
job_id: bullmqJob.id,
|
|
6620
|
+
label,
|
|
6621
|
+
state: await bullmqJob.getState(),
|
|
6622
|
+
result: null,
|
|
6623
|
+
metadata: {},
|
|
6624
|
+
tries: existingResult.tries + 1
|
|
6625
|
+
});
|
|
6626
|
+
} else {
|
|
6627
|
+
await db3.from("job_results").insert({
|
|
6628
|
+
job_id: bullmqJob.id,
|
|
6629
|
+
label,
|
|
6630
|
+
state: await bullmqJob.getState(),
|
|
6631
|
+
result: null,
|
|
6632
|
+
metadata: {},
|
|
6633
|
+
tries: 1
|
|
6634
|
+
});
|
|
6635
|
+
}
|
|
6636
|
+
const {
|
|
6637
|
+
agentInstance,
|
|
6638
|
+
backend: agentBackend,
|
|
6639
|
+
user,
|
|
6640
|
+
evalRun,
|
|
6641
|
+
testCase,
|
|
6642
|
+
messages: inputMessages
|
|
6643
|
+
} = await validateEvalPayload(data, agents);
|
|
6644
|
+
const retries = 3;
|
|
6645
|
+
let attempts = 0;
|
|
6646
|
+
const promise = new Promise(async (resolve, reject) => {
|
|
6647
|
+
while (attempts < retries) {
|
|
6648
|
+
try {
|
|
6649
|
+
const messages2 = await processUiMessagesFlow({
|
|
6650
|
+
agents,
|
|
6651
|
+
agentInstance,
|
|
6652
|
+
agentBackend,
|
|
6653
|
+
inputMessages,
|
|
6654
|
+
contexts,
|
|
6655
|
+
user,
|
|
6656
|
+
tools,
|
|
6657
|
+
config
|
|
6658
|
+
});
|
|
6659
|
+
resolve(messages2);
|
|
6660
|
+
break;
|
|
6661
|
+
} catch (error) {
|
|
6662
|
+
console.error(`[EXULU] error processing UI messages flow for agent ${agentInstance.name} (${agentInstance.id}).`, logMetadata(bullmqJob.name, {
|
|
6663
|
+
error: error instanceof Error ? error.message : String(error)
|
|
6664
|
+
}));
|
|
6665
|
+
attempts++;
|
|
6666
|
+
if (attempts >= retries) {
|
|
6667
|
+
reject(error);
|
|
6668
|
+
}
|
|
6669
|
+
await new Promise((resolve2) => setTimeout(resolve2, 2e3));
|
|
6670
|
+
}
|
|
6671
|
+
}
|
|
6672
|
+
});
|
|
6673
|
+
const result = await promise;
|
|
6674
|
+
const messages = result.messages;
|
|
6675
|
+
const metadata = result.metadata;
|
|
6676
|
+
const evalFunctions = evalRun.eval_functions;
|
|
6677
|
+
let evalFunctionResults = [];
|
|
6678
|
+
for (const evalFunction of evalFunctions) {
|
|
6679
|
+
const evalMethod = evals.find((e) => e.id === evalFunction.id);
|
|
6680
|
+
if (!evalMethod) {
|
|
6681
|
+
throw new Error(`Eval function ${evalFunction.id} not found in the registry, check your code and make sure the eval function is registered correctly.`);
|
|
6682
|
+
}
|
|
6683
|
+
let result2;
|
|
6684
|
+
if (evalMethod.queue) {
|
|
6685
|
+
const queue2 = await evalMethod.queue;
|
|
6686
|
+
const jobData = {
|
|
6687
|
+
...data,
|
|
6688
|
+
type: "eval_function",
|
|
6689
|
+
eval_functions: [{
|
|
6690
|
+
id: evalFunction.id,
|
|
6691
|
+
config: evalFunction.config || {}
|
|
6692
|
+
}],
|
|
6693
|
+
// updating the input messages with the messages we want to run the eval
|
|
6694
|
+
// function on, which are the output messages from the agent.
|
|
6695
|
+
inputs: messages
|
|
6696
|
+
};
|
|
6697
|
+
const redisId = uuidv43();
|
|
6698
|
+
const job = await queue2.queue.add("eval_function", jobData, {
|
|
6699
|
+
jobId: redisId,
|
|
6700
|
+
// Setting it to 3 as a sensible default, as
|
|
6701
|
+
// many AI services are quite unstable.
|
|
6702
|
+
attempts: queue2.retries || 3,
|
|
6703
|
+
// todo make this configurable?
|
|
6704
|
+
removeOnComplete: 5e3,
|
|
6705
|
+
removeOnFail: 1e4,
|
|
6706
|
+
backoff: queue2.backoff || {
|
|
6707
|
+
type: "exponential",
|
|
6708
|
+
delay: 2e3
|
|
6709
|
+
}
|
|
6710
|
+
});
|
|
6711
|
+
if (!job.id) {
|
|
6712
|
+
throw new Error(`Tried to add job to queue ${queue2.queue.name} but failed to get the job ID.`);
|
|
6713
|
+
}
|
|
6714
|
+
result2 = await pollJobResult({ queue: queue2, jobId: job.id });
|
|
6715
|
+
const evalFunctionResult = {
|
|
6716
|
+
test_case_id: testCase.id,
|
|
6717
|
+
eval_run_id: evalRun.id,
|
|
6718
|
+
eval_function_id: evalFunction.id,
|
|
6719
|
+
result: result2 || 0
|
|
6720
|
+
};
|
|
6721
|
+
console.log(`[EXULU] eval function ${evalFunction.id} result: ${result2}`, logMetadata(bullmqJob.name, {
|
|
6722
|
+
result: result2 || 0
|
|
6723
|
+
}));
|
|
6724
|
+
evalFunctionResults.push(evalFunctionResult);
|
|
6725
|
+
} else {
|
|
6726
|
+
result2 = await evalMethod.run(
|
|
6727
|
+
agentInstance,
|
|
6728
|
+
agentBackend,
|
|
6729
|
+
testCase,
|
|
6730
|
+
messages,
|
|
6731
|
+
evalFunction.config || {}
|
|
6732
|
+
);
|
|
6733
|
+
const evalFunctionResult = {
|
|
6734
|
+
test_case_id: testCase.id,
|
|
6735
|
+
eval_run_id: evalRun.id,
|
|
6736
|
+
eval_function_id: evalFunction.id,
|
|
6737
|
+
result: result2 || 0
|
|
6738
|
+
};
|
|
6739
|
+
evalFunctionResults.push(evalFunctionResult);
|
|
6740
|
+
console.log(`[EXULU] eval function ${evalFunction.id} result: ${result2}`, logMetadata(bullmqJob.name, {
|
|
6741
|
+
result: result2 || 0
|
|
6742
|
+
}));
|
|
6743
|
+
}
|
|
6744
|
+
}
|
|
6745
|
+
const scores = evalFunctionResults.map((result2) => result2.result);
|
|
6746
|
+
let score = 0;
|
|
6747
|
+
switch (data.scoring_method) {
|
|
6748
|
+
case "median":
|
|
6749
|
+
score = getMedian(scores);
|
|
6750
|
+
break;
|
|
6751
|
+
case "average":
|
|
6752
|
+
score = getAverage(scores);
|
|
6753
|
+
break;
|
|
6754
|
+
case "sum":
|
|
6755
|
+
score = getSum(scores);
|
|
6756
|
+
break;
|
|
6757
|
+
}
|
|
6758
|
+
return {
|
|
6759
|
+
result: score,
|
|
6760
|
+
metadata: {
|
|
6761
|
+
...evalFunctionResults,
|
|
6762
|
+
...metadata
|
|
6763
|
+
}
|
|
6764
|
+
};
|
|
6286
6765
|
}
|
|
6287
|
-
|
|
6288
|
-
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
|
|
6293
|
-
|
|
6294
|
-
|
|
6766
|
+
if (data.type === "eval_function") {
|
|
6767
|
+
console.log("[EXULU] running an eval function job.", logMetadata(bullmqJob.name));
|
|
6768
|
+
if (data.eval_functions?.length !== 1) {
|
|
6769
|
+
throw new Error(`Expected 1 eval function for eval function job, got ${data.eval_functions?.length}.`);
|
|
6770
|
+
}
|
|
6771
|
+
const label = `eval-function-${data.eval_run_id}-${data.test_case_id}-${data.eval_functions?.[0]?.id}`;
|
|
6772
|
+
const existingResult = await db3.from("job_results").where({ label }).first();
|
|
6773
|
+
if (existingResult) {
|
|
6774
|
+
await db3.from("job_results").where({ label }).update({
|
|
6775
|
+
job_id: bullmqJob.id,
|
|
6776
|
+
label,
|
|
6777
|
+
state: await bullmqJob.getState(),
|
|
6778
|
+
result: null,
|
|
6779
|
+
metadata: {},
|
|
6780
|
+
tries: existingResult.tries + 1
|
|
6781
|
+
});
|
|
6782
|
+
} else {
|
|
6783
|
+
await db3.from("job_results").insert({
|
|
6784
|
+
job_id: bullmqJob.id,
|
|
6785
|
+
label,
|
|
6786
|
+
state: await bullmqJob.getState(),
|
|
6787
|
+
result: null,
|
|
6788
|
+
metadata: {},
|
|
6789
|
+
tries: 1
|
|
6790
|
+
});
|
|
6791
|
+
}
|
|
6792
|
+
const {
|
|
6793
|
+
evalRun,
|
|
6794
|
+
agentInstance,
|
|
6795
|
+
backend,
|
|
6796
|
+
testCase,
|
|
6797
|
+
messages: inputMessages
|
|
6798
|
+
} = await validateEvalPayload(data, agents);
|
|
6799
|
+
const evalFunctions = evalRun.eval_functions;
|
|
6800
|
+
let result;
|
|
6801
|
+
for (const evalFunction of evalFunctions) {
|
|
6802
|
+
const evalMethod = evals.find((e) => e.id === evalFunction.id);
|
|
6803
|
+
if (!evalMethod) {
|
|
6804
|
+
throw new Error(`Eval function ${evalFunction.id} not found in the registry, check your code and make sure the eval function is registered correctly.`);
|
|
6295
6805
|
}
|
|
6296
|
-
|
|
6297
|
-
|
|
6298
|
-
|
|
6299
|
-
|
|
6806
|
+
result = await evalMethod.run(
|
|
6807
|
+
agentInstance,
|
|
6808
|
+
backend,
|
|
6809
|
+
testCase,
|
|
6810
|
+
inputMessages,
|
|
6811
|
+
evalFunction.config || {}
|
|
6812
|
+
);
|
|
6813
|
+
console.log(`[EXULU] eval function ${evalFunction.id} result: ${result}`, logMetadata(bullmqJob.name, {
|
|
6814
|
+
result: result || 0
|
|
6815
|
+
}));
|
|
6816
|
+
}
|
|
6817
|
+
return {
|
|
6818
|
+
result,
|
|
6819
|
+
metadata: {}
|
|
6820
|
+
};
|
|
6821
|
+
}
|
|
6822
|
+
throw new Error(`Invalid job type: ${data.type} for job ${bullmqJob.name}.`);
|
|
6823
|
+
} catch (error) {
|
|
6824
|
+
console.error(`[EXULU] job failed.`, error instanceof Error ? error.message : String(error));
|
|
6825
|
+
throw error;
|
|
6300
6826
|
}
|
|
6301
|
-
}
|
|
6302
|
-
|
|
6303
|
-
status: "failed",
|
|
6304
|
-
finishedAt: /* @__PURE__ */ new Date(),
|
|
6305
|
-
error: error instanceof Error ? error.message : String(error)
|
|
6306
|
-
});
|
|
6307
|
-
throw new Error(error instanceof Error ? error.message : String(error));
|
|
6308
|
-
}
|
|
6827
|
+
})();
|
|
6828
|
+
return Promise.race([workPromise, timeoutPromise]);
|
|
6309
6829
|
},
|
|
6310
6830
|
{
|
|
6831
|
+
autorun: true,
|
|
6311
6832
|
connection: redisConnection,
|
|
6833
|
+
removeOnComplete: { count: 1e3 },
|
|
6834
|
+
removeOnFail: { count: 5e3 },
|
|
6312
6835
|
...queue.ratelimit && {
|
|
6313
6836
|
limiter: {
|
|
6314
6837
|
max: queue.ratelimit,
|
|
@@ -6317,22 +6840,312 @@ var createWorkers = async (queues2, config, contexts, tracer) => {
|
|
|
6317
6840
|
}
|
|
6318
6841
|
}
|
|
6319
6842
|
);
|
|
6320
|
-
worker.on("completed", (job, returnvalue) => {
|
|
6321
|
-
console.log(`[EXULU] completed job ${job.id}.`,
|
|
6843
|
+
worker.on("completed", async (job, returnvalue) => {
|
|
6844
|
+
console.log(`[EXULU] completed job ${job.id}.`, logMetadata(job.name, {
|
|
6845
|
+
result: returnvalue
|
|
6846
|
+
}));
|
|
6847
|
+
const { db: db3 } = await postgresClient();
|
|
6848
|
+
await db3.from("job_results").where({ job_id: job.id }).update({
|
|
6849
|
+
state: JOB_STATUS_ENUM.completed,
|
|
6850
|
+
result: returnvalue.result,
|
|
6851
|
+
metadata: returnvalue.metadata
|
|
6852
|
+
});
|
|
6322
6853
|
});
|
|
6323
|
-
worker.on("failed", (job, error, prev) => {
|
|
6854
|
+
worker.on("failed", async (job, error, prev) => {
|
|
6324
6855
|
if (job?.id) {
|
|
6325
|
-
|
|
6856
|
+
const { db: db3 } = await postgresClient();
|
|
6857
|
+
console.error(`[EXULU] failed job ${job.id}.`, error);
|
|
6858
|
+
await db3.from("job_results").where({ job_id: job.id }).update({
|
|
6859
|
+
state: JOB_STATUS_ENUM.failed,
|
|
6860
|
+
error
|
|
6861
|
+
});
|
|
6862
|
+
return;
|
|
6326
6863
|
}
|
|
6327
|
-
console.error(`[EXULU] job
|
|
6864
|
+
console.error(`[EXULU] job failed.`, job?.name ? logMetadata(job.name, {
|
|
6865
|
+
error: error instanceof Error ? error.message : String(error)
|
|
6866
|
+
}) : error);
|
|
6867
|
+
});
|
|
6868
|
+
worker.on("error", (error) => {
|
|
6869
|
+
console.error(`[EXULU] worker error.`, error);
|
|
6328
6870
|
});
|
|
6329
6871
|
worker.on("progress", (job, progress) => {
|
|
6330
|
-
console.log(`[EXULU] job progress ${job.id}.`,
|
|
6872
|
+
console.log(`[EXULU] job progress ${job.id}.`, logMetadata(job.name, {
|
|
6873
|
+
progress
|
|
6874
|
+
}));
|
|
6331
6875
|
});
|
|
6876
|
+
const gracefulShutdown = async (signal) => {
|
|
6877
|
+
console.log(`Received ${signal}, closing server...`);
|
|
6878
|
+
await worker.close();
|
|
6879
|
+
process.exit(0);
|
|
6880
|
+
};
|
|
6881
|
+
process.on("SIGINT", () => gracefulShutdown("SIGINT"));
|
|
6882
|
+
process.on("SIGTERM", () => gracefulShutdown("SIGTERM"));
|
|
6332
6883
|
return worker;
|
|
6333
6884
|
});
|
|
6334
6885
|
return workers;
|
|
6335
6886
|
};
|
|
6887
|
+
var validateEvalPayload = async (data, agents) => {
|
|
6888
|
+
if (!data.eval_run_id) {
|
|
6889
|
+
throw new Error(`No eval run ID set for eval job.`);
|
|
6890
|
+
}
|
|
6891
|
+
if (!data.test_case_id) {
|
|
6892
|
+
throw new Error(`No test case ID set for eval job.`);
|
|
6893
|
+
}
|
|
6894
|
+
if (!data.user) {
|
|
6895
|
+
throw new Error(`No user set for eval job.`);
|
|
6896
|
+
}
|
|
6897
|
+
if (!data.role) {
|
|
6898
|
+
throw new Error(`No role set for eval job.`);
|
|
6899
|
+
}
|
|
6900
|
+
if (!data.agent_id) {
|
|
6901
|
+
throw new Error(`No agent ID set for eval job.`);
|
|
6902
|
+
}
|
|
6903
|
+
if (!data.inputs?.length) {
|
|
6904
|
+
throw new Error(`No inputs set for eval job, expected array of UIMessage objects.`);
|
|
6905
|
+
}
|
|
6906
|
+
const { db: db3 } = await postgresClient();
|
|
6907
|
+
const evalRun = await db3.from("eval_runs").where({ id: data.eval_run_id }).first();
|
|
6908
|
+
if (!evalRun) {
|
|
6909
|
+
throw new Error(`Eval run ${data.eval_run_id} not found in the database.`);
|
|
6910
|
+
}
|
|
6911
|
+
const agentInstance = await loadAgent(evalRun.agent_id);
|
|
6912
|
+
if (!agentInstance) {
|
|
6913
|
+
throw new Error(`Agent ${evalRun.agent_id} not found in the database.`);
|
|
6914
|
+
}
|
|
6915
|
+
const backend = agents.find((a) => a.id === agentInstance.backend);
|
|
6916
|
+
if (!backend) {
|
|
6917
|
+
throw new Error(`Backend ${agentInstance.backend} not found in the database.`);
|
|
6918
|
+
}
|
|
6919
|
+
const user = await db3.from("users").where({ id: data.user }).first();
|
|
6920
|
+
if (!user) {
|
|
6921
|
+
throw new Error(`User ${data.user} not found in the database.`);
|
|
6922
|
+
}
|
|
6923
|
+
const testCase = await db3.from("test_cases").where({ id: data.test_case_id }).first();
|
|
6924
|
+
if (!testCase) {
|
|
6925
|
+
throw new Error(`Test case ${data.test_case_id} not found in the database.`);
|
|
6926
|
+
}
|
|
6927
|
+
return {
|
|
6928
|
+
agentInstance,
|
|
6929
|
+
backend,
|
|
6930
|
+
user,
|
|
6931
|
+
testCase,
|
|
6932
|
+
evalRun,
|
|
6933
|
+
messages: data.inputs
|
|
6934
|
+
};
|
|
6935
|
+
};
|
|
6936
|
+
var pollJobResult = async ({ queue, jobId }) => {
|
|
6937
|
+
let attempts = 0;
|
|
6938
|
+
let timeoutInSeconds = queue.timeoutInSeconds || 180;
|
|
6939
|
+
const startTime = Date.now();
|
|
6940
|
+
let result;
|
|
6941
|
+
while (true) {
|
|
6942
|
+
attempts++;
|
|
6943
|
+
const job = await Job.fromId(queue.queue, jobId);
|
|
6944
|
+
if (!job) {
|
|
6945
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
6946
|
+
continue;
|
|
6947
|
+
}
|
|
6948
|
+
const elapsedTime = Date.now() - startTime;
|
|
6949
|
+
if (elapsedTime > timeoutInSeconds * 1e3) {
|
|
6950
|
+
throw new Error(`Job ${job.id} timed out after ${timeoutInSeconds} seconds for job eval function job ${job.name}.`);
|
|
6951
|
+
}
|
|
6952
|
+
console.log(`[EXULU] polling eval function job ${job.name} for state... (attempt ${attempts})`);
|
|
6953
|
+
const jobState = await job.getState();
|
|
6954
|
+
console.log(`[EXULU] eval function job ${job.name} state: ${jobState}`);
|
|
6955
|
+
if (jobState === "failed") {
|
|
6956
|
+
throw new Error(`Job ${job.name} (${job.id}) failed with error: ${job.failedReason}.`);
|
|
6957
|
+
}
|
|
6958
|
+
if (jobState === "completed") {
|
|
6959
|
+
console.log(`[EXULU] eval function job ${job.name} completed, getting result from database...`);
|
|
6960
|
+
const { db: db3 } = await postgresClient();
|
|
6961
|
+
const entry = await db3.from("job_results").where({ job_id: job.id }).first();
|
|
6962
|
+
result = entry?.result;
|
|
6963
|
+
if (result === void 0 || result === null || result === "") {
|
|
6964
|
+
throw new Error(`Eval function ${job.id} result not found in database for job eval function job ${job.name}.`);
|
|
6965
|
+
}
|
|
6966
|
+
console.log(`[EXULU] eval function ${job.id} result: ${result}`);
|
|
6967
|
+
break;
|
|
6968
|
+
}
|
|
6969
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
6970
|
+
}
|
|
6971
|
+
return result;
|
|
6972
|
+
};
|
|
6973
|
+
var processUiMessagesFlow = async ({
|
|
6974
|
+
agents,
|
|
6975
|
+
agentInstance,
|
|
6976
|
+
agentBackend,
|
|
6977
|
+
inputMessages,
|
|
6978
|
+
contexts,
|
|
6979
|
+
user,
|
|
6980
|
+
tools,
|
|
6981
|
+
config
|
|
6982
|
+
}) => {
|
|
6983
|
+
console.log("[EXULU] processing UI messages flow for agent.");
|
|
6984
|
+
console.log("[EXULU] input messages", inputMessages);
|
|
6985
|
+
console.log("[EXULU] agent tools", agentInstance.tools?.map((x) => x.name + " (" + x.id + ")"));
|
|
6986
|
+
const disabledTools = [];
|
|
6987
|
+
let enabledTools = await getEnabledTools(agentInstance, tools, disabledTools, agents, user);
|
|
6988
|
+
console.log("[EXULU] enabled tools", enabledTools?.map((x) => x.name + " (" + x.id + ")"));
|
|
6989
|
+
const variableName = agentInstance.providerapikey;
|
|
6990
|
+
const { db: db3 } = await postgresClient();
|
|
6991
|
+
const variable = await db3.from("variables").where({ name: variableName }).first();
|
|
6992
|
+
if (!variable) {
|
|
6993
|
+
throw new Error(`Provider API key variable not found for agent ${agentInstance.name} (${agentInstance.id}).`);
|
|
6994
|
+
}
|
|
6995
|
+
let providerapikey = variable.value;
|
|
6996
|
+
if (!variable.encrypted) {
|
|
6997
|
+
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.`);
|
|
6998
|
+
}
|
|
6999
|
+
if (variable.encrypted) {
|
|
7000
|
+
const bytes = CryptoJS4.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
|
|
7001
|
+
providerapikey = bytes.toString(CryptoJS4.enc.Utf8);
|
|
7002
|
+
}
|
|
7003
|
+
const messagesWithoutPlaceholder = inputMessages.filter(
|
|
7004
|
+
(message) => message.metadata?.type !== "placeholder"
|
|
7005
|
+
);
|
|
7006
|
+
console.log("[EXULU] messages without placeholder", messagesWithoutPlaceholder);
|
|
7007
|
+
let index = 0;
|
|
7008
|
+
let messageHistory = {
|
|
7009
|
+
messages: [],
|
|
7010
|
+
metadata: {
|
|
7011
|
+
tokens: {
|
|
7012
|
+
totalTokens: 0,
|
|
7013
|
+
reasoningTokens: 0,
|
|
7014
|
+
inputTokens: 0,
|
|
7015
|
+
outputTokens: 0,
|
|
7016
|
+
cachedInputTokens: 0
|
|
7017
|
+
},
|
|
7018
|
+
duration: 0
|
|
7019
|
+
}
|
|
7020
|
+
};
|
|
7021
|
+
for (const currentMessage of messagesWithoutPlaceholder) {
|
|
7022
|
+
console.log("[EXULU] running through the conversation");
|
|
7023
|
+
console.log("[EXULU] current index", index);
|
|
7024
|
+
console.log("[EXULU] current message", currentMessage);
|
|
7025
|
+
console.log("[EXULU] message history", messageHistory);
|
|
7026
|
+
const statistics = {
|
|
7027
|
+
label: agentInstance.name,
|
|
7028
|
+
trigger: "agent"
|
|
7029
|
+
};
|
|
7030
|
+
messageHistory = await new Promise(async (resolve, reject) => {
|
|
7031
|
+
const startTime = Date.now();
|
|
7032
|
+
try {
|
|
7033
|
+
const result = await agentBackend.generateStream({
|
|
7034
|
+
contexts,
|
|
7035
|
+
user,
|
|
7036
|
+
instructions: agentInstance.instructions,
|
|
7037
|
+
session: void 0,
|
|
7038
|
+
previousMessages: messageHistory.messages,
|
|
7039
|
+
message: currentMessage,
|
|
7040
|
+
currentTools: enabledTools,
|
|
7041
|
+
allExuluTools: tools,
|
|
7042
|
+
providerapikey,
|
|
7043
|
+
toolConfigs: agentInstance.tools,
|
|
7044
|
+
exuluConfig: config
|
|
7045
|
+
});
|
|
7046
|
+
console.log("[EXULU] consuming stream for agent.");
|
|
7047
|
+
const stream = result.stream.toUIMessageStream({
|
|
7048
|
+
messageMetadata: ({ part }) => {
|
|
7049
|
+
console.log("[EXULU] part", part.type);
|
|
7050
|
+
if (part.type === "finish") {
|
|
7051
|
+
return {
|
|
7052
|
+
totalTokens: part.totalUsage.totalTokens,
|
|
7053
|
+
reasoningTokens: part.totalUsage.reasoningTokens,
|
|
7054
|
+
inputTokens: part.totalUsage.inputTokens,
|
|
7055
|
+
outputTokens: part.totalUsage.outputTokens,
|
|
7056
|
+
cachedInputTokens: part.totalUsage.cachedInputTokens
|
|
7057
|
+
};
|
|
7058
|
+
}
|
|
7059
|
+
},
|
|
7060
|
+
originalMessages: result.originalMessages,
|
|
7061
|
+
sendReasoning: true,
|
|
7062
|
+
sendSources: true,
|
|
7063
|
+
onError: (error) => {
|
|
7064
|
+
console.error("[EXULU] Ui message stream error.", error);
|
|
7065
|
+
reject(error);
|
|
7066
|
+
return `Ui message stream error: ${error instanceof Error ? error.message : String(error)}`;
|
|
7067
|
+
},
|
|
7068
|
+
onFinish: async ({ messages, isContinuation, responseMessage }) => {
|
|
7069
|
+
const metadata = messages[messages.length - 1]?.metadata;
|
|
7070
|
+
console.log("[EXULU] Stream finished with messages:", messages);
|
|
7071
|
+
console.log("[EXULU] Stream metadata", metadata);
|
|
7072
|
+
await Promise.all([
|
|
7073
|
+
updateStatistic({
|
|
7074
|
+
name: "count",
|
|
7075
|
+
label: statistics.label,
|
|
7076
|
+
type: STATISTICS_TYPE_ENUM.AGENT_RUN,
|
|
7077
|
+
trigger: statistics.trigger,
|
|
7078
|
+
count: 1,
|
|
7079
|
+
user: user.id,
|
|
7080
|
+
role: user?.role?.id
|
|
7081
|
+
}),
|
|
7082
|
+
...metadata?.inputTokens ? [
|
|
7083
|
+
updateStatistic({
|
|
7084
|
+
name: "inputTokens",
|
|
7085
|
+
label: statistics.label,
|
|
7086
|
+
type: STATISTICS_TYPE_ENUM.AGENT_RUN,
|
|
7087
|
+
trigger: statistics.trigger,
|
|
7088
|
+
count: metadata?.inputTokens,
|
|
7089
|
+
user: user.id,
|
|
7090
|
+
role: user?.role?.id
|
|
7091
|
+
})
|
|
7092
|
+
] : [],
|
|
7093
|
+
...metadata?.outputTokens ? [
|
|
7094
|
+
updateStatistic({
|
|
7095
|
+
name: "outputTokens",
|
|
7096
|
+
label: statistics.label,
|
|
7097
|
+
type: STATISTICS_TYPE_ENUM.AGENT_RUN,
|
|
7098
|
+
trigger: statistics.trigger,
|
|
7099
|
+
count: metadata?.outputTokens
|
|
7100
|
+
})
|
|
7101
|
+
] : []
|
|
7102
|
+
]);
|
|
7103
|
+
resolve({
|
|
7104
|
+
messages,
|
|
7105
|
+
metadata: {
|
|
7106
|
+
tokens: {
|
|
7107
|
+
totalTokens: messageHistory.metadata.tokens.totalTokens + metadata?.totalTokens,
|
|
7108
|
+
reasoningTokens: messageHistory.metadata.tokens.reasoningTokens + metadata?.reasoningTokens,
|
|
7109
|
+
inputTokens: messageHistory.metadata.tokens.inputTokens + metadata?.inputTokens,
|
|
7110
|
+
outputTokens: messageHistory.metadata.tokens.outputTokens + metadata?.outputTokens,
|
|
7111
|
+
cachedInputTokens: messageHistory.metadata.tokens.cachedInputTokens + metadata?.cachedInputTokens
|
|
7112
|
+
},
|
|
7113
|
+
duration: messageHistory.metadata.duration + (Date.now() - startTime)
|
|
7114
|
+
}
|
|
7115
|
+
});
|
|
7116
|
+
}
|
|
7117
|
+
});
|
|
7118
|
+
for await (const message of stream) {
|
|
7119
|
+
console.log("[EXULU] message", message);
|
|
7120
|
+
}
|
|
7121
|
+
} catch (error) {
|
|
7122
|
+
console.error(`[EXULU] error generating stream for agent ${agentInstance.name} (${agentInstance.id}).`, error);
|
|
7123
|
+
reject(error);
|
|
7124
|
+
}
|
|
7125
|
+
});
|
|
7126
|
+
index++;
|
|
7127
|
+
}
|
|
7128
|
+
console.log("[EXULU] finished processing UI messages flow for agent, messages result", messageHistory);
|
|
7129
|
+
return messageHistory;
|
|
7130
|
+
};
|
|
7131
|
+
function getMedian(arr) {
|
|
7132
|
+
if (arr.length === 0) return 0;
|
|
7133
|
+
const sortedArr = arr.slice().sort((a, b) => a - b);
|
|
7134
|
+
const mid = Math.floor(sortedArr.length / 2);
|
|
7135
|
+
if (sortedArr.length % 2 !== 0) {
|
|
7136
|
+
return sortedArr[mid];
|
|
7137
|
+
} else {
|
|
7138
|
+
return (sortedArr[mid - 1] + sortedArr[mid]) / 2;
|
|
7139
|
+
}
|
|
7140
|
+
}
|
|
7141
|
+
function getSum(arr) {
|
|
7142
|
+
if (arr.length === 0) return 0;
|
|
7143
|
+
return arr.reduce((a, b) => a + b, 0);
|
|
7144
|
+
}
|
|
7145
|
+
function getAverage(arr) {
|
|
7146
|
+
if (arr.length === 0) return 0;
|
|
7147
|
+
return arr.reduce((a, b) => a + b, 0) / arr.length;
|
|
7148
|
+
}
|
|
6336
7149
|
|
|
6337
7150
|
// src/mcp/index.ts
|
|
6338
7151
|
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -6485,7 +7298,6 @@ var claudeOpus4Agent = new ExuluAgent2({
|
|
|
6485
7298
|
audio: [],
|
|
6486
7299
|
video: []
|
|
6487
7300
|
},
|
|
6488
|
-
evals: [],
|
|
6489
7301
|
maxContextLength: 2e5,
|
|
6490
7302
|
config: {
|
|
6491
7303
|
name: `CLAUDE-OPUS-4`,
|
|
@@ -6520,7 +7332,6 @@ var claudeSonnet4Agent = new ExuluAgent2({
|
|
|
6520
7332
|
audio: [],
|
|
6521
7333
|
video: []
|
|
6522
7334
|
},
|
|
6523
|
-
evals: [],
|
|
6524
7335
|
maxContextLength: 2e5,
|
|
6525
7336
|
config: {
|
|
6526
7337
|
name: `CLAUDE-SONNET-4`,
|
|
@@ -6548,7 +7359,6 @@ var claudeSonnet45Agent = new ExuluAgent2({
|
|
|
6548
7359
|
audio: [],
|
|
6549
7360
|
video: []
|
|
6550
7361
|
},
|
|
6551
|
-
evals: [],
|
|
6552
7362
|
maxContextLength: 2e5,
|
|
6553
7363
|
config: {
|
|
6554
7364
|
name: `CLAUDE-SONNET-4.5`,
|
|
@@ -6579,7 +7389,6 @@ var gpt5proAgent = new ExuluAgent2({
|
|
|
6579
7389
|
audio: [],
|
|
6580
7390
|
video: []
|
|
6581
7391
|
},
|
|
6582
|
-
evals: [],
|
|
6583
7392
|
maxContextLength: 4e5,
|
|
6584
7393
|
config: {
|
|
6585
7394
|
name: `GPT-5-PRO`,
|
|
@@ -6607,7 +7416,6 @@ var gpt5CodexAgent = new ExuluAgent2({
|
|
|
6607
7416
|
audio: [],
|
|
6608
7417
|
video: []
|
|
6609
7418
|
},
|
|
6610
|
-
evals: [],
|
|
6611
7419
|
maxContextLength: 4e5,
|
|
6612
7420
|
config: {
|
|
6613
7421
|
name: `GPT-5-CODEX`,
|
|
@@ -6635,7 +7443,6 @@ var gpt5MiniAgent = new ExuluAgent2({
|
|
|
6635
7443
|
audio: [],
|
|
6636
7444
|
video: []
|
|
6637
7445
|
},
|
|
6638
|
-
evals: [],
|
|
6639
7446
|
maxContextLength: 4e5,
|
|
6640
7447
|
config: {
|
|
6641
7448
|
name: `GPT-5-MINI`,
|
|
@@ -6670,7 +7477,6 @@ var gpt5agent = new ExuluAgent2({
|
|
|
6670
7477
|
audio: [],
|
|
6671
7478
|
video: []
|
|
6672
7479
|
},
|
|
6673
|
-
evals: [],
|
|
6674
7480
|
maxContextLength: 4e5,
|
|
6675
7481
|
config: {
|
|
6676
7482
|
name: `GPT-5`,
|
|
@@ -6705,7 +7511,6 @@ var gpt5NanoAgent = new ExuluAgent2({
|
|
|
6705
7511
|
audio: [],
|
|
6706
7512
|
video: []
|
|
6707
7513
|
},
|
|
6708
|
-
evals: [],
|
|
6709
7514
|
maxContextLength: 4e5,
|
|
6710
7515
|
config: {
|
|
6711
7516
|
name: `GPT-5-NANO`,
|
|
@@ -6733,7 +7538,6 @@ var gpt41Agent = new ExuluAgent2({
|
|
|
6733
7538
|
audio: [],
|
|
6734
7539
|
video: []
|
|
6735
7540
|
},
|
|
6736
|
-
evals: [],
|
|
6737
7541
|
maxContextLength: 1047576,
|
|
6738
7542
|
config: {
|
|
6739
7543
|
name: `GPT-4.1`,
|
|
@@ -6761,7 +7565,6 @@ var gpt41MiniAgent = new ExuluAgent2({
|
|
|
6761
7565
|
audio: [],
|
|
6762
7566
|
video: []
|
|
6763
7567
|
},
|
|
6764
|
-
evals: [],
|
|
6765
7568
|
maxContextLength: 1047576,
|
|
6766
7569
|
config: {
|
|
6767
7570
|
name: `GPT-4.1-MINI`,
|
|
@@ -6789,7 +7592,6 @@ var gpt4oAgent = new ExuluAgent2({
|
|
|
6789
7592
|
audio: [],
|
|
6790
7593
|
video: []
|
|
6791
7594
|
},
|
|
6792
|
-
evals: [],
|
|
6793
7595
|
maxContextLength: 128e3,
|
|
6794
7596
|
config: {
|
|
6795
7597
|
name: `Default agent`,
|
|
@@ -6817,7 +7619,6 @@ var gpt4oMiniAgent = new ExuluAgent2({
|
|
|
6817
7619
|
audio: [],
|
|
6818
7620
|
video: []
|
|
6819
7621
|
},
|
|
6820
|
-
evals: [],
|
|
6821
7622
|
maxContextLength: 128e3,
|
|
6822
7623
|
config: {
|
|
6823
7624
|
name: `GPT-4O-MINI`,
|
|
@@ -6908,6 +7709,55 @@ var outputsContext = new ExuluContext({
|
|
|
6908
7709
|
// src/registry/index.ts
|
|
6909
7710
|
import winston2 from "winston";
|
|
6910
7711
|
import util from "util";
|
|
7712
|
+
|
|
7713
|
+
// src/templates/evals/index.ts
|
|
7714
|
+
import { z as z3 } from "zod";
|
|
7715
|
+
var llmAsJudgeEval = new ExuluEval2({
|
|
7716
|
+
id: "llm_as_judge",
|
|
7717
|
+
name: "LLM as Judge",
|
|
7718
|
+
description: "Evaluate the output of the LLM as a judge.",
|
|
7719
|
+
execute: async ({ agent, backend, messages, testCase, config }) => {
|
|
7720
|
+
console.log("[EXULU] running llm as judge eval", { agent, backend, messages, testCase, config });
|
|
7721
|
+
let prompt = config?.prompt;
|
|
7722
|
+
if (!prompt) {
|
|
7723
|
+
console.error("[EXULU] prompt is required.");
|
|
7724
|
+
throw new Error("Prompt is required.");
|
|
7725
|
+
}
|
|
7726
|
+
const lastMessage = messages[messages.length - 1]?.parts?.filter((part) => part.type === "text").map((part) => part.text).join("\n");
|
|
7727
|
+
console.log("[EXULU] last message", lastMessage);
|
|
7728
|
+
if (!lastMessage) {
|
|
7729
|
+
return 0;
|
|
7730
|
+
}
|
|
7731
|
+
prompt = prompt.replace("{actual_output}", lastMessage);
|
|
7732
|
+
prompt = prompt.replace("{expected_output}", testCase.expected_output);
|
|
7733
|
+
if (!agent.providerapikey) {
|
|
7734
|
+
throw new Error(`Provider API key for agent ${agent.name} is required, variable name is ${agent.providerapikey} but it is not set.`);
|
|
7735
|
+
}
|
|
7736
|
+
const providerapikey = await ExuluVariables.get(agent.providerapikey);
|
|
7737
|
+
console.log("[EXULU] prompt", prompt);
|
|
7738
|
+
const response = await backend.generateSync({
|
|
7739
|
+
prompt,
|
|
7740
|
+
outputSchema: z3.object({
|
|
7741
|
+
score: z3.number().min(0).max(100).describe("The score between 0 and 100.")
|
|
7742
|
+
}),
|
|
7743
|
+
providerapikey
|
|
7744
|
+
});
|
|
7745
|
+
console.log("[EXULU] response", response);
|
|
7746
|
+
const score = parseFloat(response.score);
|
|
7747
|
+
if (isNaN(score)) {
|
|
7748
|
+
throw new Error(`Generated score from llm as a judge eval is not a number: ${response.score}`);
|
|
7749
|
+
}
|
|
7750
|
+
return score;
|
|
7751
|
+
},
|
|
7752
|
+
config: [{
|
|
7753
|
+
name: "prompt",
|
|
7754
|
+
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."
|
|
7755
|
+
}],
|
|
7756
|
+
queue: queues.register("llm_as_judge", 1, 1).use(),
|
|
7757
|
+
llm: true
|
|
7758
|
+
});
|
|
7759
|
+
|
|
7760
|
+
// src/registry/index.ts
|
|
6911
7761
|
var isDev = process.env.NODE_ENV !== "production";
|
|
6912
7762
|
var consoleTransport = new winston2.transports.Console({
|
|
6913
7763
|
format: isDev ? winston2.format.combine(
|
|
@@ -6918,6 +7768,20 @@ var consoleTransport = new winston2.transports.Console({
|
|
|
6918
7768
|
})
|
|
6919
7769
|
) : winston2.format.json()
|
|
6920
7770
|
});
|
|
7771
|
+
var formatArg = (arg) => typeof arg === "object" ? util.inspect(arg, { depth: null, colors: isDev }) : String(arg);
|
|
7772
|
+
var createLogMethod = (logger, logLevel) => {
|
|
7773
|
+
return (...args) => {
|
|
7774
|
+
const lastArg = args[args.length - 1];
|
|
7775
|
+
let metadata = void 0;
|
|
7776
|
+
let messageArgs = args;
|
|
7777
|
+
if (lastArg && typeof lastArg === "object" && lastArg.__logMetadata === true) {
|
|
7778
|
+
metadata = lastArg;
|
|
7779
|
+
messageArgs = args.slice(0, -1);
|
|
7780
|
+
}
|
|
7781
|
+
const message = messageArgs.map(formatArg).join(" ");
|
|
7782
|
+
logger[logLevel](message, metadata);
|
|
7783
|
+
};
|
|
7784
|
+
};
|
|
6921
7785
|
var isValidPostgresName = (id) => {
|
|
6922
7786
|
const regex = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
6923
7787
|
const isValid = regex.test(id);
|
|
@@ -6927,6 +7791,7 @@ var isValidPostgresName = (id) => {
|
|
|
6927
7791
|
var ExuluApp = class {
|
|
6928
7792
|
_agents = [];
|
|
6929
7793
|
_config;
|
|
7794
|
+
_evals = [];
|
|
6930
7795
|
_queues = [];
|
|
6931
7796
|
_contexts = {};
|
|
6932
7797
|
_tools = [];
|
|
@@ -6935,7 +7800,11 @@ var ExuluApp = class {
|
|
|
6935
7800
|
}
|
|
6936
7801
|
// Factory function so we can async
|
|
6937
7802
|
// initialize the MCP server if needed.
|
|
6938
|
-
create = async ({ contexts, agents, config, tools }) => {
|
|
7803
|
+
create = async ({ contexts, agents, config, tools, evals }) => {
|
|
7804
|
+
this._evals = [
|
|
7805
|
+
llmAsJudgeEval,
|
|
7806
|
+
...evals ?? []
|
|
7807
|
+
];
|
|
6939
7808
|
this._contexts = {
|
|
6940
7809
|
...contexts,
|
|
6941
7810
|
codeStandardsContext,
|
|
@@ -6987,11 +7856,12 @@ var ExuluApp = class {
|
|
|
6987
7856
|
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");
|
|
6988
7857
|
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.`);
|
|
6989
7858
|
}
|
|
6990
|
-
const contextsArray = Object.values(contexts || {});
|
|
6991
7859
|
const queueSet = /* @__PURE__ */ new Set();
|
|
6992
|
-
|
|
6993
|
-
|
|
6994
|
-
|
|
7860
|
+
if (redisServer.host?.length && redisServer.port?.length) {
|
|
7861
|
+
queues.register(global_queues.eval_runs, 1, 1);
|
|
7862
|
+
for (const queue of queues.list.values()) {
|
|
7863
|
+
const config2 = await queue.use();
|
|
7864
|
+
queueSet.add(config2);
|
|
6995
7865
|
}
|
|
6996
7866
|
}
|
|
6997
7867
|
this._queues = [...new Set(queueSet.values())];
|
|
@@ -7067,7 +7937,7 @@ var ExuluApp = class {
|
|
|
7067
7937
|
};
|
|
7068
7938
|
bullmq = {
|
|
7069
7939
|
workers: {
|
|
7070
|
-
create: async () => {
|
|
7940
|
+
create: async (queues3) => {
|
|
7071
7941
|
if (!this._config) {
|
|
7072
7942
|
throw new Error("Config not initialized, make sure to call await ExuluApp.create() first when starting your server.");
|
|
7073
7943
|
}
|
|
@@ -7080,16 +7950,22 @@ var ExuluApp = class {
|
|
|
7080
7950
|
enableOtel: this._config?.workers?.telemetry?.enabled ?? false,
|
|
7081
7951
|
transports
|
|
7082
7952
|
});
|
|
7083
|
-
|
|
7084
|
-
console.
|
|
7085
|
-
console.
|
|
7086
|
-
console.
|
|
7087
|
-
console.
|
|
7088
|
-
|
|
7953
|
+
console.log = createLogMethod(logger, "info");
|
|
7954
|
+
console.info = createLogMethod(logger, "info");
|
|
7955
|
+
console.warn = createLogMethod(logger, "warn");
|
|
7956
|
+
console.error = createLogMethod(logger, "error");
|
|
7957
|
+
console.debug = createLogMethod(logger, "debug");
|
|
7958
|
+
let filteredQueues = this._queues;
|
|
7959
|
+
if (queues3) {
|
|
7960
|
+
filteredQueues = filteredQueues.filter((q) => queues3.includes(q.queue.name));
|
|
7961
|
+
}
|
|
7089
7962
|
return await createWorkers(
|
|
7090
|
-
this.
|
|
7963
|
+
this._agents,
|
|
7964
|
+
filteredQueues,
|
|
7091
7965
|
this._config,
|
|
7092
7966
|
Object.values(this._contexts ?? {}),
|
|
7967
|
+
this._evals,
|
|
7968
|
+
this._tools,
|
|
7093
7969
|
tracer
|
|
7094
7970
|
);
|
|
7095
7971
|
}
|
|
@@ -7106,16 +7982,16 @@ var ExuluApp = class {
|
|
|
7106
7982
|
if (this._config?.telemetry?.enabled) {
|
|
7107
7983
|
tracer = trace.getTracer("exulu", "1.0.0");
|
|
7108
7984
|
}
|
|
7985
|
+
const transports = this._config?.logger?.winston?.transports ?? [consoleTransport];
|
|
7109
7986
|
const logger = logger_default({
|
|
7110
7987
|
enableOtel: this._config?.telemetry?.enabled ?? false,
|
|
7111
|
-
transports
|
|
7988
|
+
transports
|
|
7112
7989
|
});
|
|
7113
|
-
|
|
7114
|
-
console.
|
|
7115
|
-
console.
|
|
7116
|
-
console.
|
|
7117
|
-
console.
|
|
7118
|
-
console.debug = (...args) => logger.debug(args.map(formatArg).join(" "));
|
|
7990
|
+
console.log = createLogMethod(logger, "info");
|
|
7991
|
+
console.info = createLogMethod(logger, "info");
|
|
7992
|
+
console.warn = createLogMethod(logger, "warn");
|
|
7993
|
+
console.error = createLogMethod(logger, "error");
|
|
7994
|
+
console.debug = createLogMethod(logger, "debug");
|
|
7119
7995
|
if (!this._config) {
|
|
7120
7996
|
throw new Error("Config not initialized, make sure to call await ExuluApp.create() first when starting your server.");
|
|
7121
7997
|
}
|
|
@@ -7125,7 +8001,9 @@ var ExuluApp = class {
|
|
|
7125
8001
|
this._tools,
|
|
7126
8002
|
Object.values(this._contexts ?? {}),
|
|
7127
8003
|
this._config,
|
|
7128
|
-
|
|
8004
|
+
this._evals,
|
|
8005
|
+
tracer,
|
|
8006
|
+
this._queues
|
|
7129
8007
|
);
|
|
7130
8008
|
if (this._config?.MCP.enabled) {
|
|
7131
8009
|
const mcp = new ExuluMCP();
|
|
@@ -8354,7 +9232,8 @@ var {
|
|
|
8354
9232
|
variablesSchema: variablesSchema3,
|
|
8355
9233
|
workflowTemplatesSchema: workflowTemplatesSchema3,
|
|
8356
9234
|
rbacSchema: rbacSchema3,
|
|
8357
|
-
projectsSchema: projectsSchema3
|
|
9235
|
+
projectsSchema: projectsSchema3,
|
|
9236
|
+
jobResultsSchema: jobResultsSchema3
|
|
8358
9237
|
} = coreSchemas.get();
|
|
8359
9238
|
var addMissingFields = async (knex, tableName, fields, skipFields = []) => {
|
|
8360
9239
|
for (const field of fields) {
|
|
@@ -8388,6 +9267,7 @@ var up = async function(knex) {
|
|
|
8388
9267
|
platformConfigurationsSchema3(),
|
|
8389
9268
|
statisticsSchema3(),
|
|
8390
9269
|
projectsSchema3(),
|
|
9270
|
+
jobResultsSchema3(),
|
|
8391
9271
|
rbacSchema3(),
|
|
8392
9272
|
agentsSchema3(),
|
|
8393
9273
|
variablesSchema3(),
|
|
@@ -8572,20 +9452,7 @@ var create = ({
|
|
|
8572
9452
|
};
|
|
8573
9453
|
|
|
8574
9454
|
// src/index.ts
|
|
8575
|
-
import
|
|
8576
|
-
|
|
8577
|
-
// types/enums/jobs.ts
|
|
8578
|
-
var JOB_STATUS_ENUM = {
|
|
8579
|
-
completed: "completed",
|
|
8580
|
-
failed: "failed",
|
|
8581
|
-
delayed: "delayed",
|
|
8582
|
-
active: "active",
|
|
8583
|
-
waiting: "waiting",
|
|
8584
|
-
paused: "paused",
|
|
8585
|
-
stuck: "stuck"
|
|
8586
|
-
};
|
|
8587
|
-
|
|
8588
|
-
// src/index.ts
|
|
9455
|
+
import CryptoJS5 from "crypto-js";
|
|
8589
9456
|
var ExuluJobs = {
|
|
8590
9457
|
redis: redisClient,
|
|
8591
9458
|
jobs: {
|
|
@@ -8622,8 +9489,8 @@ var ExuluVariables = {
|
|
|
8622
9489
|
throw new Error(`Variable ${name} not found.`);
|
|
8623
9490
|
}
|
|
8624
9491
|
if (variable.encrypted) {
|
|
8625
|
-
const bytes =
|
|
8626
|
-
variable.value = bytes.toString(
|
|
9492
|
+
const bytes = CryptoJS5.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
|
|
9493
|
+
variable.value = bytes.toString(CryptoJS5.enc.Utf8);
|
|
8627
9494
|
}
|
|
8628
9495
|
return variable.value;
|
|
8629
9496
|
}
|
|
@@ -8751,12 +9618,13 @@ export {
|
|
|
8751
9618
|
ExuluDefaultAgents,
|
|
8752
9619
|
ExuluDefaultContexts,
|
|
8753
9620
|
ExuluEmbedder,
|
|
8754
|
-
ExuluEval,
|
|
9621
|
+
ExuluEval2 as ExuluEval,
|
|
8755
9622
|
ExuluJobs,
|
|
8756
9623
|
ExuluOtel,
|
|
8757
9624
|
queues as ExuluQueues,
|
|
8758
9625
|
ExuluTool2 as ExuluTool,
|
|
8759
9626
|
ExuluUtils,
|
|
8760
9627
|
ExuluVariables,
|
|
8761
|
-
db2 as db
|
|
9628
|
+
db2 as db,
|
|
9629
|
+
logMetadata
|
|
8762
9630
|
};
|