@exulu/backend 1.39.3 → 1.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -2
- package/changelog-backend-10.11.2025_03.12.2025.md +1 -1
- package/dist/index.cjs +289 -184
- package/dist/index.d.cts +24 -14
- package/dist/index.d.ts +24 -14
- package/dist/index.js +289 -184
- package/package.json +1 -1
- package/types/models/context.ts +8 -0
package/dist/index.js
CHANGED
|
@@ -15,7 +15,6 @@ var redisServer = {
|
|
|
15
15
|
// src/redis/client.ts
|
|
16
16
|
var client = {};
|
|
17
17
|
async function redisClient() {
|
|
18
|
-
console.log("[EXULU] redisServer:", redisServer);
|
|
19
18
|
if (!redisServer.host || !redisServer.port) {
|
|
20
19
|
return { client: null };
|
|
21
20
|
}
|
|
@@ -96,7 +95,6 @@ var db = {};
|
|
|
96
95
|
var databaseExistsChecked = false;
|
|
97
96
|
var dbName = process.env.POSTGRES_DB_NAME || "exulu";
|
|
98
97
|
async function ensureDatabaseExists() {
|
|
99
|
-
console.log(`[EXULU] Ensuring ${dbName} database exists...`);
|
|
100
98
|
const defaultKnex = Knex({
|
|
101
99
|
client: "pg",
|
|
102
100
|
connection: {
|
|
@@ -140,16 +138,7 @@ async function ensureDatabaseExists() {
|
|
|
140
138
|
async function postgresClient() {
|
|
141
139
|
if (!db["exulu"]) {
|
|
142
140
|
try {
|
|
143
|
-
console.log(`[EXULU] Connecting to ${dbName} database.`);
|
|
144
|
-
console.log("[EXULU] POSTGRES_DB_HOST:", process.env.POSTGRES_DB_HOST);
|
|
145
|
-
console.log("[EXULU] POSTGRES_DB_PORT:", process.env.POSTGRES_DB_PORT);
|
|
146
|
-
console.log("[EXULU] POSTGRES_DB_USER:", process.env.POSTGRES_DB_USER);
|
|
147
|
-
console.log("[EXULU] POSTGRES_DB_PASSWORD:", process.env.POSTGRES_DB_PASSWORD);
|
|
148
|
-
console.log("[EXULU] POSTGRES_DB_NAME:", dbName);
|
|
149
|
-
console.log("[EXULU] POSTGRES_DB_SSL:", process.env.POSTGRES_DB_SSL);
|
|
150
|
-
console.log("[EXULU] Database exists checked:", databaseExistsChecked);
|
|
151
141
|
if (!databaseExistsChecked) {
|
|
152
|
-
console.log(`[EXULU] Ensuring ${dbName} database exists...`);
|
|
153
142
|
await ensureDatabaseExists();
|
|
154
143
|
databaseExistsChecked = true;
|
|
155
144
|
}
|
|
@@ -474,7 +463,6 @@ var authentication = async ({
|
|
|
474
463
|
}
|
|
475
464
|
if (authtoken) {
|
|
476
465
|
try {
|
|
477
|
-
console.log("[EXULU] authtoken", authtoken);
|
|
478
466
|
if (!authtoken?.email) {
|
|
479
467
|
return {
|
|
480
468
|
error: true,
|
|
@@ -3678,7 +3666,10 @@ type PageInfo {
|
|
|
3678
3666
|
const config2 = await queue.use();
|
|
3679
3667
|
return {
|
|
3680
3668
|
name: config2.queue.name,
|
|
3681
|
-
concurrency:
|
|
3669
|
+
concurrency: {
|
|
3670
|
+
worker: config2.concurrency?.worker || void 0,
|
|
3671
|
+
queue: config2.concurrency?.queue || void 0
|
|
3672
|
+
},
|
|
3682
3673
|
ratelimit: config2.ratelimit,
|
|
3683
3674
|
isMaxed: await config2.queue.isMaxed(),
|
|
3684
3675
|
isPaused: await config2.queue.isPaused(),
|
|
@@ -3728,7 +3719,10 @@ type PageInfo {
|
|
|
3728
3719
|
if (!agentInstance) {
|
|
3729
3720
|
throw new Error("Agent instance not found for eval run.");
|
|
3730
3721
|
}
|
|
3731
|
-
const evalQueue = await queues.register("eval_runs",
|
|
3722
|
+
const evalQueue = await queues.register("eval_runs", {
|
|
3723
|
+
worker: 1,
|
|
3724
|
+
queue: 1
|
|
3725
|
+
}, 1).use();
|
|
3732
3726
|
const jobIds = [];
|
|
3733
3727
|
for (const testCase of testCases) {
|
|
3734
3728
|
const jobData = {
|
|
@@ -3852,7 +3846,6 @@ type PageInfo {
|
|
|
3852
3846
|
if (!client2) {
|
|
3853
3847
|
throw new Error("Redis client not created properly");
|
|
3854
3848
|
}
|
|
3855
|
-
console.log("[EXULU] Jobs pagination args", args);
|
|
3856
3849
|
const {
|
|
3857
3850
|
jobs,
|
|
3858
3851
|
count
|
|
@@ -3862,7 +3855,6 @@ type PageInfo {
|
|
|
3862
3855
|
args.page || 1,
|
|
3863
3856
|
args.limit || 100
|
|
3864
3857
|
);
|
|
3865
|
-
console.log("[EXULU] jobs", jobs.map((job) => job.name));
|
|
3866
3858
|
const requestedFields = getRequestedFields(info);
|
|
3867
3859
|
return {
|
|
3868
3860
|
items: await Promise.all(jobs.map(async (job) => {
|
|
@@ -3891,6 +3883,25 @@ type PageInfo {
|
|
|
3891
3883
|
};
|
|
3892
3884
|
resolvers.Query["contexts"] = async (_, args, context, info) => {
|
|
3893
3885
|
const data = await Promise.all(contexts.map(async (context2) => {
|
|
3886
|
+
let processors = await Promise.all(context2.fields.map(async (field) => {
|
|
3887
|
+
if (field.processor) {
|
|
3888
|
+
let queueName = void 0;
|
|
3889
|
+
if (field.processor?.config?.queue) {
|
|
3890
|
+
const config2 = await field.processor?.config?.queue;
|
|
3891
|
+
queueName = config2?.queue?.name || void 0;
|
|
3892
|
+
}
|
|
3893
|
+
return {
|
|
3894
|
+
field: field.name,
|
|
3895
|
+
description: field.processor?.description,
|
|
3896
|
+
queue: queueName,
|
|
3897
|
+
trigger: field.processor?.config?.trigger,
|
|
3898
|
+
timeoutInSeconds: field.processor?.config?.timeoutInSeconds || 600,
|
|
3899
|
+
generateEmbeddings: field.processor?.config?.generateEmbeddings || false
|
|
3900
|
+
};
|
|
3901
|
+
}
|
|
3902
|
+
return null;
|
|
3903
|
+
}));
|
|
3904
|
+
processors = processors.filter((processor) => processor !== null);
|
|
3894
3905
|
const sources = await Promise.all(context2.sources.map(async (source) => {
|
|
3895
3906
|
let queueName = void 0;
|
|
3896
3907
|
if (source.config) {
|
|
@@ -3922,6 +3933,7 @@ type PageInfo {
|
|
|
3922
3933
|
slug: "/contexts/" + context2.id,
|
|
3923
3934
|
active: context2.active,
|
|
3924
3935
|
sources,
|
|
3936
|
+
processors,
|
|
3925
3937
|
fields: context2.fields.map((field) => {
|
|
3926
3938
|
return {
|
|
3927
3939
|
...field,
|
|
@@ -3947,6 +3959,25 @@ type PageInfo {
|
|
|
3947
3959
|
if (!data) {
|
|
3948
3960
|
return null;
|
|
3949
3961
|
}
|
|
3962
|
+
let processors = await Promise.all(data.fields.map(async (field) => {
|
|
3963
|
+
if (field.processor) {
|
|
3964
|
+
let queueName = void 0;
|
|
3965
|
+
if (field.processor?.config?.queue) {
|
|
3966
|
+
const config2 = await field.processor?.config?.queue;
|
|
3967
|
+
queueName = config2?.queue?.name || void 0;
|
|
3968
|
+
}
|
|
3969
|
+
return {
|
|
3970
|
+
field: field.name,
|
|
3971
|
+
description: field.processor?.description,
|
|
3972
|
+
queue: queueName,
|
|
3973
|
+
trigger: field.processor?.config?.trigger,
|
|
3974
|
+
timeoutInSeconds: field.processor?.config?.timeoutInSeconds || 600,
|
|
3975
|
+
generateEmbeddings: field.processor?.config?.generateEmbeddings || false
|
|
3976
|
+
};
|
|
3977
|
+
}
|
|
3978
|
+
return null;
|
|
3979
|
+
}));
|
|
3980
|
+
processors = processors.filter((processor) => processor !== null);
|
|
3950
3981
|
const sources = await Promise.all(data.sources.map(async (source) => {
|
|
3951
3982
|
let queueName = void 0;
|
|
3952
3983
|
if (source.config) {
|
|
@@ -3983,6 +4014,7 @@ type PageInfo {
|
|
|
3983
4014
|
slug: "/contexts/" + data.id,
|
|
3984
4015
|
active: data.active,
|
|
3985
4016
|
sources,
|
|
4017
|
+
processors,
|
|
3986
4018
|
fields: await Promise.all(data.fields.map(async (field) => {
|
|
3987
4019
|
const label = field.name?.replace("_s3key", "");
|
|
3988
4020
|
if (field.type === "file" && !field.name.endsWith("_s3key")) {
|
|
@@ -4006,7 +4038,10 @@ type PageInfo {
|
|
|
4006
4038
|
queue: {
|
|
4007
4039
|
name: queue?.queue.name || void 0,
|
|
4008
4040
|
ratelimit: queue?.ratelimit || void 0,
|
|
4009
|
-
concurrency:
|
|
4041
|
+
concurrency: {
|
|
4042
|
+
worker: queue?.concurrency?.worker || void 0,
|
|
4043
|
+
queue: queue?.concurrency?.queue || void 0
|
|
4044
|
+
}
|
|
4010
4045
|
}
|
|
4011
4046
|
},
|
|
4012
4047
|
execute: "function"
|
|
@@ -4079,13 +4114,19 @@ type PageInfo {
|
|
|
4079
4114
|
modelDefs += `
|
|
4080
4115
|
type QueueResult {
|
|
4081
4116
|
name: String!
|
|
4082
|
-
concurrency:
|
|
4117
|
+
concurrency: QueueConcurrency!
|
|
4083
4118
|
ratelimit: Int!
|
|
4084
4119
|
isMaxed: Boolean!
|
|
4085
4120
|
isPaused: Boolean!
|
|
4086
4121
|
jobs: QueueJobsCounts
|
|
4087
4122
|
}
|
|
4088
4123
|
`;
|
|
4124
|
+
modelDefs += `
|
|
4125
|
+
type QueueConcurrency {
|
|
4126
|
+
worker: Int
|
|
4127
|
+
queue: Int
|
|
4128
|
+
}
|
|
4129
|
+
`;
|
|
4089
4130
|
modelDefs += `
|
|
4090
4131
|
type QueueJobsCounts {
|
|
4091
4132
|
paused: Int!
|
|
@@ -4200,7 +4241,8 @@ type Context {
|
|
|
4200
4241
|
active: Boolean
|
|
4201
4242
|
fields: JSON
|
|
4202
4243
|
configuration: JSON
|
|
4203
|
-
sources: [ContextSource
|
|
4244
|
+
sources: [ContextSource]
|
|
4245
|
+
processors: [ContextProcessor]
|
|
4204
4246
|
}
|
|
4205
4247
|
type Embedder {
|
|
4206
4248
|
name: String!
|
|
@@ -4213,6 +4255,14 @@ type EmbedderConfig {
|
|
|
4213
4255
|
description: String
|
|
4214
4256
|
default: String
|
|
4215
4257
|
}
|
|
4258
|
+
type ContextProcessor {
|
|
4259
|
+
field: String!
|
|
4260
|
+
description: String
|
|
4261
|
+
queue: String
|
|
4262
|
+
trigger: String
|
|
4263
|
+
timeoutInSeconds: Int
|
|
4264
|
+
generateEmbeddings: Boolean
|
|
4265
|
+
}
|
|
4216
4266
|
|
|
4217
4267
|
type ContextSource {
|
|
4218
4268
|
id: String!
|
|
@@ -4331,10 +4381,7 @@ async function getJobsByQueueName(queueName, statusses, page, limit) {
|
|
|
4331
4381
|
const config = await queue.use();
|
|
4332
4382
|
const startIndex = (page || 1) - 1;
|
|
4333
4383
|
const endIndex = startIndex - 1 + (limit || 100);
|
|
4334
|
-
console.log("[EXULU] Jobs pagination startIndex", startIndex);
|
|
4335
|
-
console.log("[EXULU] Jobs pagination endIndex", endIndex);
|
|
4336
4384
|
const jobs = await config.queue.getJobs(statusses || [], startIndex, endIndex, false);
|
|
4337
|
-
console.log("[EXULU] Jobs pagination jobs", jobs?.length);
|
|
4338
4385
|
const counts = await config.queue.getJobCounts(...statusses || []);
|
|
4339
4386
|
let total = 0;
|
|
4340
4387
|
if (counts) {
|
|
@@ -4394,21 +4441,10 @@ function getS3Client(config) {
|
|
|
4394
4441
|
});
|
|
4395
4442
|
return s3Client;
|
|
4396
4443
|
}
|
|
4397
|
-
var getPresignedUrl = async (key, config) => {
|
|
4444
|
+
var getPresignedUrl = async (bucket, key, config) => {
|
|
4398
4445
|
if (!config.fileUploads) {
|
|
4399
4446
|
throw new Error("File uploads are not configured");
|
|
4400
4447
|
}
|
|
4401
|
-
let bucket = config.fileUploads.s3Bucket;
|
|
4402
|
-
if (key.includes("[bucket:")) {
|
|
4403
|
-
console.log("[EXULU] key includes [bucket:name]", key);
|
|
4404
|
-
bucket = key.split("[bucket:")[1]?.split("]")[0] || "";
|
|
4405
|
-
if (!bucket?.length) {
|
|
4406
|
-
throw new Error("Invalid key, does not contain a bucket name like '[bucket:name]'.");
|
|
4407
|
-
}
|
|
4408
|
-
key = key.split("]")[1] || "";
|
|
4409
|
-
console.log("[EXULU] bucket", bucket);
|
|
4410
|
-
console.log("[EXULU] key", key);
|
|
4411
|
-
}
|
|
4412
4448
|
const url = await getSignedUrl(
|
|
4413
4449
|
getS3Client(config),
|
|
4414
4450
|
new GetObjectCommand({
|
|
@@ -4419,7 +4455,7 @@ var getPresignedUrl = async (key, config) => {
|
|
|
4419
4455
|
);
|
|
4420
4456
|
return url;
|
|
4421
4457
|
};
|
|
4422
|
-
var
|
|
4458
|
+
var addGeneralPrefixToKey = (keyPath, config) => {
|
|
4423
4459
|
if (!config.fileUploads) {
|
|
4424
4460
|
throw new Error("File uploads are not configured");
|
|
4425
4461
|
}
|
|
@@ -4432,58 +4468,50 @@ var addPrefixToKey = (keyPath, config) => {
|
|
|
4432
4468
|
}
|
|
4433
4469
|
return `${prefix}/${keyPath}`;
|
|
4434
4470
|
};
|
|
4435
|
-
var
|
|
4471
|
+
var addUserPrefixToKey = (key, user) => {
|
|
4472
|
+
if (!user) {
|
|
4473
|
+
return key;
|
|
4474
|
+
}
|
|
4475
|
+
if (key.includes(`/user_${user}/`)) {
|
|
4476
|
+
return key;
|
|
4477
|
+
}
|
|
4478
|
+
return `user_${user}/${key}`;
|
|
4479
|
+
};
|
|
4480
|
+
var addBucketPrefixToKey = (key, bucket) => {
|
|
4481
|
+
if (key.includes(`/${bucket}/`)) {
|
|
4482
|
+
return key;
|
|
4483
|
+
}
|
|
4484
|
+
return `${bucket}/${key}`;
|
|
4485
|
+
};
|
|
4486
|
+
var uploadFile = async (file, fileName, config, options = {}, user, customBucket) => {
|
|
4436
4487
|
if (!config.fileUploads) {
|
|
4437
4488
|
throw new Error("File uploads are not configured (in the exported uploadFile function)");
|
|
4438
4489
|
}
|
|
4439
4490
|
const client2 = getS3Client(config);
|
|
4440
4491
|
let defaultBucket = config.fileUploads.s3Bucket;
|
|
4441
|
-
let
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
if (!customBucket?.length) {
|
|
4446
|
-
throw new Error("Invalid key, does not contain a bucket name like '[bucket:name]'.");
|
|
4447
|
-
}
|
|
4448
|
-
key = key.split("]")[1] || "";
|
|
4449
|
-
console.log("[EXULU] custom bucket", customBucket);
|
|
4450
|
-
}
|
|
4451
|
-
let folder = user ? `${user}/` : "";
|
|
4452
|
-
const fullKey = addPrefixToKey(!key.includes(folder) ? folder + key : key, config);
|
|
4453
|
-
console.log("[EXULU] uploading file to s3 into bucket", customBucket || defaultBucket, "with key", fullKey);
|
|
4492
|
+
let key = fileName;
|
|
4493
|
+
key = addGeneralPrefixToKey(key, config);
|
|
4494
|
+
key = addUserPrefixToKey(key, user || "api");
|
|
4495
|
+
console.log("[EXULU] uploading file to s3 into bucket", defaultBucket, "with key", key);
|
|
4454
4496
|
const command = new PutObjectCommand({
|
|
4455
4497
|
Bucket: customBucket || defaultBucket,
|
|
4456
|
-
Key:
|
|
4498
|
+
Key: key,
|
|
4457
4499
|
Body: file,
|
|
4458
4500
|
ContentType: options.contentType,
|
|
4459
4501
|
Metadata: options.metadata,
|
|
4460
4502
|
ContentLength: file.byteLength
|
|
4461
4503
|
});
|
|
4462
4504
|
await client2.send(command);
|
|
4463
|
-
console.log("[EXULU] file uploaded to s3 into bucket", customBucket || defaultBucket, "with key",
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4505
|
+
console.log("[EXULU] file uploaded to s3 into bucket", customBucket || defaultBucket, "with key", key);
|
|
4506
|
+
return addBucketPrefixToKey(
|
|
4507
|
+
key,
|
|
4508
|
+
customBucket || defaultBucket
|
|
4509
|
+
);
|
|
4468
4510
|
};
|
|
4469
|
-
var createUppyRoutes = async (app, config) => {
|
|
4511
|
+
var createUppyRoutes = async (app, contexts, config) => {
|
|
4470
4512
|
if (!config.fileUploads) {
|
|
4471
4513
|
throw new Error("File uploads are not configured");
|
|
4472
4514
|
}
|
|
4473
|
-
const extractUserPrefix = (key) => {
|
|
4474
|
-
if (!config.fileUploads) {
|
|
4475
|
-
throw new Error("File uploads are not configured");
|
|
4476
|
-
}
|
|
4477
|
-
if (!config.fileUploads.s3prefix) {
|
|
4478
|
-
return key.split("/")[0];
|
|
4479
|
-
}
|
|
4480
|
-
const prefix = config.fileUploads.s3prefix.replace(/\/$/, "");
|
|
4481
|
-
if (key.startsWith(prefix + "/")) {
|
|
4482
|
-
const keyWithoutPrefix = key.slice(prefix.length + 1);
|
|
4483
|
-
return keyWithoutPrefix.split("/")[0];
|
|
4484
|
-
}
|
|
4485
|
-
return key.split("/")[0];
|
|
4486
|
-
};
|
|
4487
4515
|
const policy = {
|
|
4488
4516
|
Version: "2012-10-17",
|
|
4489
4517
|
Statement: [
|
|
@@ -4535,20 +4563,16 @@ var createUppyRoutes = async (app, config) => {
|
|
|
4535
4563
|
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
4536
4564
|
return;
|
|
4537
4565
|
}
|
|
4566
|
+
const user = authenticationResult.user;
|
|
4538
4567
|
let { key } = req.query;
|
|
4539
4568
|
if (typeof key !== "string" || key.trim() === "") {
|
|
4540
4569
|
res.status(400).json({ error: "Missing or invalid `key` query parameter." });
|
|
4541
4570
|
return;
|
|
4542
4571
|
}
|
|
4543
|
-
let bucket =
|
|
4544
|
-
if (key.includes(
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
if (!bucket?.length) {
|
|
4548
|
-
throw new Error("Invalid key, does not contain a bucket name like '[bucket:name]'.");
|
|
4549
|
-
}
|
|
4550
|
-
key = key.split("]")[1] || "";
|
|
4551
|
-
console.log("[EXULU] bucket", bucket);
|
|
4572
|
+
let bucket = key.split("/")[0];
|
|
4573
|
+
if (user.type !== "api" && !key.includes(`/user_${user.id}/`) && !user.super_admin) {
|
|
4574
|
+
res.status(405).json({ error: "Not allowed to access the files in the folder based on authenticated user." });
|
|
4575
|
+
return;
|
|
4552
4576
|
}
|
|
4553
4577
|
const client2 = getS3Client(config);
|
|
4554
4578
|
const command = new DeleteObjectCommand({
|
|
@@ -4576,13 +4600,32 @@ var createUppyRoutes = async (app, config) => {
|
|
|
4576
4600
|
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
4577
4601
|
return;
|
|
4578
4602
|
}
|
|
4579
|
-
const
|
|
4603
|
+
const user = authenticationResult.user;
|
|
4604
|
+
let { key } = req.query;
|
|
4605
|
+
if (!key || typeof key !== "string" || key.trim() === "") {
|
|
4606
|
+
res.status(400).json({ error: "Missing or invalid `key` query parameter." });
|
|
4607
|
+
return;
|
|
4608
|
+
}
|
|
4609
|
+
let bucket = key.split("/")[0];
|
|
4610
|
+
if (!bucket || typeof bucket !== "string" || bucket.trim() === "") {
|
|
4611
|
+
res.status(400).json({ error: "Missing or invalid `bucket` (should be the first part of the key before the first slash)." });
|
|
4612
|
+
return;
|
|
4613
|
+
}
|
|
4614
|
+
key = key.split("/").slice(1).join("/");
|
|
4580
4615
|
if (typeof key !== "string" || key.trim() === "") {
|
|
4581
4616
|
res.status(400).json({ error: "Missing or invalid `key` query parameter." });
|
|
4582
4617
|
return;
|
|
4583
4618
|
}
|
|
4619
|
+
let allowed = false;
|
|
4620
|
+
if (user.type === "api" || user.super_admin || key.includes(`user_${user.id}/`)) {
|
|
4621
|
+
allowed = true;
|
|
4622
|
+
}
|
|
4623
|
+
if (!allowed) {
|
|
4624
|
+
res.status(405).json({ error: "Not allowed to access the file based on authenticated user." });
|
|
4625
|
+
return;
|
|
4626
|
+
}
|
|
4584
4627
|
try {
|
|
4585
|
-
const url = await getPresignedUrl(key, config);
|
|
4628
|
+
const url = await getPresignedUrl(bucket, key, config);
|
|
4586
4629
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
4587
4630
|
res.json({ url, method: "GET", expiresIn });
|
|
4588
4631
|
} catch (err) {
|
|
@@ -4611,16 +4654,15 @@ var createUppyRoutes = async (app, config) => {
|
|
|
4611
4654
|
return;
|
|
4612
4655
|
}
|
|
4613
4656
|
let { key } = req.body;
|
|
4614
|
-
let bucket =
|
|
4615
|
-
if (
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
|
|
4620
|
-
|
|
4621
|
-
}
|
|
4622
|
-
|
|
4623
|
-
console.log("[EXULU] key", key);
|
|
4657
|
+
let bucket = key.split("/")[0];
|
|
4658
|
+
if (!bucket || typeof bucket !== "string" || bucket.trim() === "") {
|
|
4659
|
+
res.status(400).json({ error: "Missing or invalid `bucket` (should be the first part of the key before the first slash)." });
|
|
4660
|
+
return;
|
|
4661
|
+
}
|
|
4662
|
+
key = key.split("/").slice(1).join("/");
|
|
4663
|
+
if (!key || typeof key !== "string" || key.trim() === "") {
|
|
4664
|
+
res.status(400).json({ error: "Missing or invalid `key` query parameter." });
|
|
4665
|
+
return;
|
|
4624
4666
|
}
|
|
4625
4667
|
const client2 = getS3Client(config);
|
|
4626
4668
|
const command = new HeadObjectCommand({
|
|
@@ -4724,11 +4766,12 @@ var createUppyRoutes = async (app, config) => {
|
|
|
4724
4766
|
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
4725
4767
|
return;
|
|
4726
4768
|
}
|
|
4769
|
+
const user = authenticationResult.user;
|
|
4727
4770
|
const { filename, contentType } = extractFileParameters(req);
|
|
4728
4771
|
validateFileParameters(filename, contentType);
|
|
4729
4772
|
const key = generateS3Key2(filename);
|
|
4730
|
-
let
|
|
4731
|
-
|
|
4773
|
+
let fullKey = addGeneralPrefixToKey(key, config);
|
|
4774
|
+
fullKey = addUserPrefixToKey(fullKey, user.type === "api" ? "api" : user.id);
|
|
4732
4775
|
getSignedUrl(
|
|
4733
4776
|
getS3Client(config),
|
|
4734
4777
|
new PutObjectCommand({
|
|
@@ -4772,6 +4815,7 @@ var createUppyRoutes = async (app, config) => {
|
|
|
4772
4815
|
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
4773
4816
|
return;
|
|
4774
4817
|
}
|
|
4818
|
+
const user = authenticationResult.user;
|
|
4775
4819
|
const client2 = getS3Client(config);
|
|
4776
4820
|
const { type, metadata, filename } = req.body;
|
|
4777
4821
|
if (typeof filename !== "string") {
|
|
@@ -4781,13 +4825,8 @@ var createUppyRoutes = async (app, config) => {
|
|
|
4781
4825
|
return res.status(400).json({ error: "s3: content type must be a string" });
|
|
4782
4826
|
}
|
|
4783
4827
|
const key = `${randomUUID()}-_EXULU_${filename}`;
|
|
4784
|
-
let
|
|
4785
|
-
|
|
4786
|
-
folder = `api/`;
|
|
4787
|
-
} else {
|
|
4788
|
-
folder = `${authenticationResult.user.id}/`;
|
|
4789
|
-
}
|
|
4790
|
-
const fullKey = addPrefixToKey(folder + key, config);
|
|
4828
|
+
let fullKey = addGeneralPrefixToKey(key, config);
|
|
4829
|
+
fullKey = addUserPrefixToKey(fullKey, user.type === "api" ? "api" : user.id);
|
|
4791
4830
|
const params = {
|
|
4792
4831
|
Bucket: config.fileUploads.s3Bucket,
|
|
4793
4832
|
Key: fullKey,
|
|
@@ -4990,7 +5029,7 @@ var createProjectRetrievalTool = async ({
|
|
|
4990
5029
|
if (!context) {
|
|
4991
5030
|
throw new Error("The item added to the project does not have a valid gid with the context id as the prefix before the first slash.");
|
|
4992
5031
|
}
|
|
4993
|
-
const id = item.split("/")
|
|
5032
|
+
const id = item.split("/").slice(1).join("/");
|
|
4994
5033
|
if (set[context]) {
|
|
4995
5034
|
set[context].push(id);
|
|
4996
5035
|
} else {
|
|
@@ -5936,12 +5975,20 @@ var ExuluStorage = class {
|
|
|
5936
5975
|
this.config = config;
|
|
5937
5976
|
}
|
|
5938
5977
|
getPresignedUrl = async (key) => {
|
|
5939
|
-
|
|
5978
|
+
const bucket = key.split("/")[0];
|
|
5979
|
+
if (!bucket || typeof bucket !== "string" || bucket.trim() === "") {
|
|
5980
|
+
throw new Error("Invalid S3 key, must be in the format of <bucket>/<key>.");
|
|
5981
|
+
}
|
|
5982
|
+
key = key.split("/").slice(1).join("/");
|
|
5983
|
+
if (!key || typeof key !== "string" || key.trim() === "") {
|
|
5984
|
+
throw new Error("Invalid S3 key, must be in the format of <bucket>/<key>.");
|
|
5985
|
+
}
|
|
5986
|
+
return await getPresignedUrl(bucket, key, this.config);
|
|
5940
5987
|
};
|
|
5941
|
-
uploadFile = async (file,
|
|
5988
|
+
uploadFile = async (file, fileName, type, user, metadata, customBucket) => {
|
|
5942
5989
|
return await uploadFile(
|
|
5943
5990
|
file,
|
|
5944
|
-
|
|
5991
|
+
fileName,
|
|
5945
5992
|
this.config,
|
|
5946
5993
|
{
|
|
5947
5994
|
contentType: type,
|
|
@@ -5950,7 +5997,8 @@ var ExuluStorage = class {
|
|
|
5950
5997
|
type
|
|
5951
5998
|
}
|
|
5952
5999
|
},
|
|
5953
|
-
user
|
|
6000
|
+
user,
|
|
6001
|
+
customBucket
|
|
5954
6002
|
);
|
|
5955
6003
|
};
|
|
5956
6004
|
// todo add upload and delete methods
|
|
@@ -6001,6 +6049,9 @@ var ExuluContext = class {
|
|
|
6001
6049
|
this.resultReranker = resultReranker;
|
|
6002
6050
|
}
|
|
6003
6051
|
processField = async (trigger, item, exuluConfig, user, role) => {
|
|
6052
|
+
if (!item.field) {
|
|
6053
|
+
throw new Error("Field property on item is required for running a specific processor.");
|
|
6054
|
+
}
|
|
6004
6055
|
console.log("[EXULU] processing field", item.field, " in context", this.id);
|
|
6005
6056
|
console.log("[EXULU] fields", this.fields.map((field2) => field2.name));
|
|
6006
6057
|
const field = this.fields.find((field2) => {
|
|
@@ -6032,27 +6083,32 @@ var ExuluContext = class {
|
|
|
6032
6083
|
trigger
|
|
6033
6084
|
});
|
|
6034
6085
|
return {
|
|
6035
|
-
result:
|
|
6086
|
+
result: {},
|
|
6036
6087
|
job: job.id
|
|
6037
6088
|
};
|
|
6038
6089
|
}
|
|
6039
6090
|
console.log("[EXULU] POS 1 -- EXULU CONTEXT PROCESS FIELD");
|
|
6040
|
-
const
|
|
6091
|
+
const processorResult = await field.processor.execute({
|
|
6041
6092
|
item,
|
|
6042
6093
|
user,
|
|
6043
6094
|
role,
|
|
6044
6095
|
utils: {
|
|
6045
|
-
storage: exuluStorage
|
|
6046
|
-
items: {
|
|
6047
|
-
update: this.updateItem,
|
|
6048
|
-
create: this.createItem,
|
|
6049
|
-
delete: this.deleteItem
|
|
6050
|
-
}
|
|
6096
|
+
storage: exuluStorage
|
|
6051
6097
|
},
|
|
6052
6098
|
exuluConfig
|
|
6053
6099
|
});
|
|
6100
|
+
if (!processorResult) {
|
|
6101
|
+
throw new Error("Processor result is required for updating the item in the db.");
|
|
6102
|
+
}
|
|
6103
|
+
const { db: db3 } = await postgresClient();
|
|
6104
|
+
delete processorResult.field;
|
|
6105
|
+
await db3.from(getTableName(this.id)).where({
|
|
6106
|
+
id: processorResult.id
|
|
6107
|
+
}).update({
|
|
6108
|
+
...processorResult
|
|
6109
|
+
});
|
|
6054
6110
|
return {
|
|
6055
|
-
result,
|
|
6111
|
+
result: processorResult,
|
|
6056
6112
|
job: void 0
|
|
6057
6113
|
};
|
|
6058
6114
|
};
|
|
@@ -6137,6 +6193,8 @@ var ExuluContext = class {
|
|
|
6137
6193
|
};
|
|
6138
6194
|
};
|
|
6139
6195
|
createItem = async (item, config, user, role, upsert, generateEmbeddingsOverwrite) => {
|
|
6196
|
+
console.log("[EXULU] creating item", item);
|
|
6197
|
+
console.log("[EXULU] upsert", upsert);
|
|
6140
6198
|
if (upsert && (!item.id && !item.external_id)) {
|
|
6141
6199
|
throw new Error("Item id or external id is required for upsert.");
|
|
6142
6200
|
}
|
|
@@ -6164,7 +6222,7 @@ var ExuluContext = class {
|
|
|
6164
6222
|
}
|
|
6165
6223
|
console.log("[EXULU] context configuration", this.configuration);
|
|
6166
6224
|
let jobs = [];
|
|
6167
|
-
let shouldGenerateEmbeddings = this.embedder && generateEmbeddingsOverwrite !== false && (generateEmbeddingsOverwrite || this.configuration.calculateVectors === "
|
|
6225
|
+
let shouldGenerateEmbeddings = this.embedder && generateEmbeddingsOverwrite !== false && (generateEmbeddingsOverwrite || this.configuration.calculateVectors === "onInsert" || this.configuration.calculateVectors === "always");
|
|
6168
6226
|
for (const [key, value] of Object.entries(item)) {
|
|
6169
6227
|
console.log("[EXULU] Checking for processors for field", key);
|
|
6170
6228
|
const processor = this.fields.find((field) => field.name === key.replace("_s3key", ""))?.processor;
|
|
@@ -6187,8 +6245,13 @@ var ExuluContext = class {
|
|
|
6187
6245
|
if (processorJob) {
|
|
6188
6246
|
jobs.push(processorJob);
|
|
6189
6247
|
}
|
|
6190
|
-
if (!processorJob
|
|
6191
|
-
|
|
6248
|
+
if (!processorJob) {
|
|
6249
|
+
await db3.from(getTableName(this.id)).where({ id: results[0].id }).update({
|
|
6250
|
+
...processorResult
|
|
6251
|
+
});
|
|
6252
|
+
if (processor.config?.generateEmbeddings) {
|
|
6253
|
+
shouldGenerateEmbeddings = true;
|
|
6254
|
+
}
|
|
6192
6255
|
}
|
|
6193
6256
|
}
|
|
6194
6257
|
}
|
|
@@ -6214,6 +6277,7 @@ var ExuluContext = class {
|
|
|
6214
6277
|
};
|
|
6215
6278
|
};
|
|
6216
6279
|
updateItem = async (item, config, user, role, generateEmbeddingsOverwrite) => {
|
|
6280
|
+
console.log("[EXULU] updating item", item);
|
|
6217
6281
|
const { db: db3 } = await postgresClient();
|
|
6218
6282
|
if (item.field) {
|
|
6219
6283
|
delete item.field;
|
|
@@ -6259,8 +6323,13 @@ var ExuluContext = class {
|
|
|
6259
6323
|
if (processorJob) {
|
|
6260
6324
|
jobs.push(processorJob);
|
|
6261
6325
|
}
|
|
6262
|
-
if (!processorJob
|
|
6263
|
-
|
|
6326
|
+
if (!processorJob) {
|
|
6327
|
+
await db3.from(getTableName(this.id)).where({ id: record.id }).update({
|
|
6328
|
+
...processorResult
|
|
6329
|
+
});
|
|
6330
|
+
if (processor.config?.generateEmbeddings) {
|
|
6331
|
+
shouldGenerateEmbeddings = true;
|
|
6332
|
+
}
|
|
6264
6333
|
}
|
|
6265
6334
|
}
|
|
6266
6335
|
}
|
|
@@ -6284,23 +6353,23 @@ var ExuluContext = class {
|
|
|
6284
6353
|
};
|
|
6285
6354
|
deleteItem = async (item, user, role) => {
|
|
6286
6355
|
const { db: db3 } = await postgresClient();
|
|
6356
|
+
if (!item.id && !item.external_id) {
|
|
6357
|
+
throw new Error("Item id or external id is required for deleting an item.");
|
|
6358
|
+
}
|
|
6287
6359
|
if (!item.id?.length && item?.external_id) {
|
|
6288
6360
|
item = await db3.from(getTableName(this.id)).where({ external_id: item.external_id }).first();
|
|
6289
6361
|
if (!item || !item.id) {
|
|
6290
6362
|
throw new Error(`Item not found for external id ${item?.external_id || "undefined"}.`);
|
|
6291
6363
|
}
|
|
6292
6364
|
}
|
|
6293
|
-
|
|
6294
|
-
if (
|
|
6295
|
-
|
|
6296
|
-
|
|
6297
|
-
|
|
6298
|
-
}
|
|
6299
|
-
}
|
|
6300
|
-
const chunks = await db3.from(getChunksTableName(this.id)).where({ source: item.id }).select("id");
|
|
6301
|
-
if (chunks.length > 0) {
|
|
6302
|
-
await db3.from(getChunksTableName(this.id)).where({ source: item.id }).delete();
|
|
6365
|
+
const chunkTableExists = await this.chunksTableExists();
|
|
6366
|
+
if (chunkTableExists) {
|
|
6367
|
+
const chunks = await db3.from(getChunksTableName(this.id)).where({ source: item.id }).select("id");
|
|
6368
|
+
if (chunks.length > 0) {
|
|
6369
|
+
await db3.from(getChunksTableName(this.id)).where({ source: item.id }).delete();
|
|
6370
|
+
}
|
|
6303
6371
|
}
|
|
6372
|
+
await db3.from(getTableName(this.id)).where({ id: item.id }).delete();
|
|
6304
6373
|
return {
|
|
6305
6374
|
id: item.id,
|
|
6306
6375
|
job: void 0
|
|
@@ -7111,7 +7180,7 @@ Mood: friendly and intelligent.
|
|
|
7111
7180
|
});
|
|
7112
7181
|
});
|
|
7113
7182
|
if (config?.fileUploads && config?.fileUploads?.s3region && config?.fileUploads?.s3key && config?.fileUploads?.s3secret && config?.fileUploads?.s3Bucket) {
|
|
7114
|
-
await createUppyRoutes(app, config);
|
|
7183
|
+
await createUppyRoutes(app, contexts ?? [], config);
|
|
7115
7184
|
} else {
|
|
7116
7185
|
console.log("[EXULU] skipping uppy file upload routes, because no S3 compatible region, key or secret is set in ExuluApp instance.");
|
|
7117
7186
|
}
|
|
@@ -7424,6 +7493,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7424
7493
|
async (bullmqJob) => {
|
|
7425
7494
|
console.log("[EXULU] starting execution for job", logMetadata2(bullmqJob.name, {
|
|
7426
7495
|
name: bullmqJob.name,
|
|
7496
|
+
jobId: bullmqJob.id,
|
|
7427
7497
|
status: await bullmqJob.getState(),
|
|
7428
7498
|
type: bullmqJob.data.type
|
|
7429
7499
|
}));
|
|
@@ -7438,6 +7508,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7438
7508
|
});
|
|
7439
7509
|
const workPromise = (async () => {
|
|
7440
7510
|
try {
|
|
7511
|
+
console.log(`[EXULU] Job ${bullmqJob.id} - Log file: logs/jobs/job-${bullmqJob.id}.log`);
|
|
7441
7512
|
bullmq.validate(bullmqJob.id, data);
|
|
7442
7513
|
if (data.type === "embedder") {
|
|
7443
7514
|
console.log("[EXULU] running an embedder job.", logMetadata2(bullmqJob.name));
|
|
@@ -7460,17 +7531,17 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7460
7531
|
if (!embedder) {
|
|
7461
7532
|
throw new Error(`Embedder ${data.embedder} not found in the registry.`);
|
|
7462
7533
|
}
|
|
7463
|
-
const
|
|
7534
|
+
const result2 = await context.createAndUpsertEmbeddings(data.inputs, config, data.user, {
|
|
7464
7535
|
label: embedder.name,
|
|
7465
7536
|
trigger: data.trigger
|
|
7466
7537
|
}, data.role, bullmqJob.id);
|
|
7467
7538
|
return {
|
|
7468
|
-
result,
|
|
7539
|
+
result: result2,
|
|
7469
7540
|
metadata: {}
|
|
7470
7541
|
};
|
|
7471
7542
|
}
|
|
7472
7543
|
if (data.type === "processor") {
|
|
7473
|
-
console.log("[EXULU] running a processor job.",
|
|
7544
|
+
console.log("[EXULU] running a processor job, job name: ", bullmqJob.name, " job id: ", bullmqJob.id, " job data: ", data, " job queue: ", bullmqJob.queueName);
|
|
7474
7545
|
const label = `processor-${bullmqJob.name}`;
|
|
7475
7546
|
await db3.from("job_results").insert({
|
|
7476
7547
|
job_id: bullmqJob.id,
|
|
@@ -7492,35 +7563,42 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7492
7563
|
if (!field.processor) {
|
|
7493
7564
|
throw new Error(`Processor not set for field ${data.inputs.field} in the context ${data.context}.`);
|
|
7494
7565
|
}
|
|
7495
|
-
|
|
7496
|
-
|
|
7497
|
-
throw new Error(`User not set for processor job.`);
|
|
7498
|
-
}
|
|
7499
|
-
if (!data.role) {
|
|
7500
|
-
throw new Error(`Role not set for processor job.`);
|
|
7566
|
+
if (!data.inputs.id) {
|
|
7567
|
+
throw new Error(`[EXULU] Item not set for processor ${field.name} in context ${context.id}, running in job ${bullmqJob.id}.`);
|
|
7501
7568
|
}
|
|
7569
|
+
const exuluStorage = new ExuluStorage({ config });
|
|
7502
7570
|
console.log("[EXULU] POS 2 -- EXULU CONTEXT PROCESS FIELD");
|
|
7503
|
-
const
|
|
7571
|
+
const processorResult = await field.processor.execute({
|
|
7504
7572
|
item: data.inputs,
|
|
7505
7573
|
user: data.user,
|
|
7506
7574
|
role: data.role,
|
|
7507
7575
|
utils: {
|
|
7508
|
-
storage: exuluStorage
|
|
7509
|
-
items: {
|
|
7510
|
-
update: context.updateItem,
|
|
7511
|
-
create: context.createItem,
|
|
7512
|
-
delete: context.deleteItem
|
|
7513
|
-
}
|
|
7576
|
+
storage: exuluStorage
|
|
7514
7577
|
},
|
|
7515
|
-
config
|
|
7578
|
+
exuluConfig: config
|
|
7579
|
+
});
|
|
7580
|
+
if (!processorResult) {
|
|
7581
|
+
throw new Error(`[EXULU] Processor ${field.name} in context ${context.id}, running in job ${bullmqJob.id} did not return an item.`);
|
|
7582
|
+
}
|
|
7583
|
+
delete processorResult.field;
|
|
7584
|
+
await db3.from(getTableName(context.id)).where({
|
|
7585
|
+
id: processorResult.id
|
|
7586
|
+
}).update({
|
|
7587
|
+
...processorResult
|
|
7516
7588
|
});
|
|
7517
7589
|
let jobs = [];
|
|
7518
7590
|
if (field.processor.config?.generateEmbeddings) {
|
|
7591
|
+
const fullItem = await db3.from(getTableName(context.id)).where({
|
|
7592
|
+
id: processorResult.id
|
|
7593
|
+
}).first();
|
|
7594
|
+
if (!fullItem) {
|
|
7595
|
+
throw new Error(`[EXULU] Item ${processorResult.id} not found after processor update in context ${context.id}`);
|
|
7596
|
+
}
|
|
7519
7597
|
const { job: embeddingsJob } = await context.embeddings.generate.one({
|
|
7520
|
-
item:
|
|
7598
|
+
item: fullItem,
|
|
7521
7599
|
user: data.user,
|
|
7522
7600
|
role: data.role,
|
|
7523
|
-
trigger: "
|
|
7601
|
+
trigger: "processor",
|
|
7524
7602
|
config
|
|
7525
7603
|
});
|
|
7526
7604
|
if (embeddingsJob) {
|
|
@@ -7528,7 +7606,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7528
7606
|
}
|
|
7529
7607
|
}
|
|
7530
7608
|
return {
|
|
7531
|
-
result,
|
|
7609
|
+
result: processorResult,
|
|
7532
7610
|
metadata: {
|
|
7533
7611
|
jobs: jobs.length > 0 ? jobs.join(",") : void 0
|
|
7534
7612
|
}
|
|
@@ -7595,9 +7673,9 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7595
7673
|
}
|
|
7596
7674
|
}
|
|
7597
7675
|
});
|
|
7598
|
-
const
|
|
7599
|
-
const messages =
|
|
7600
|
-
const metadata =
|
|
7676
|
+
const result2 = await promise;
|
|
7677
|
+
const messages = result2.messages;
|
|
7678
|
+
const metadata = result2.metadata;
|
|
7601
7679
|
const evalFunctions = evalRun.eval_functions;
|
|
7602
7680
|
let evalFunctionResults = [];
|
|
7603
7681
|
for (const evalFunction of evalFunctions) {
|
|
@@ -7605,7 +7683,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7605
7683
|
if (!evalMethod) {
|
|
7606
7684
|
throw new Error(`Eval function ${evalFunction.id} not found in the registry, check your code and make sure the eval function is registered correctly.`);
|
|
7607
7685
|
}
|
|
7608
|
-
let
|
|
7686
|
+
let result3;
|
|
7609
7687
|
if (evalMethod.queue) {
|
|
7610
7688
|
const queue2 = await evalMethod.queue;
|
|
7611
7689
|
const jobData = {
|
|
@@ -7636,21 +7714,21 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7636
7714
|
if (!job.id) {
|
|
7637
7715
|
throw new Error(`Tried to add job to queue ${queue2.queue.name} but failed to get the job ID.`);
|
|
7638
7716
|
}
|
|
7639
|
-
|
|
7717
|
+
result3 = await pollJobResult({ queue: queue2, jobId: job.id });
|
|
7640
7718
|
const evalFunctionResult = {
|
|
7641
7719
|
test_case_id: testCase.id,
|
|
7642
7720
|
eval_run_id: evalRun.id,
|
|
7643
7721
|
eval_function_id: evalFunction.id,
|
|
7644
7722
|
eval_function_name: evalFunction.name,
|
|
7645
7723
|
eval_function_config: evalFunction.config || {},
|
|
7646
|
-
result:
|
|
7724
|
+
result: result3 || 0
|
|
7647
7725
|
};
|
|
7648
|
-
console.log(`[EXULU] eval function ${evalFunction.id} result: ${
|
|
7649
|
-
result:
|
|
7726
|
+
console.log(`[EXULU] eval function ${evalFunction.id} result: ${result3}`, logMetadata2(bullmqJob.name, {
|
|
7727
|
+
result: result3 || 0
|
|
7650
7728
|
}));
|
|
7651
7729
|
evalFunctionResults.push(evalFunctionResult);
|
|
7652
7730
|
} else {
|
|
7653
|
-
|
|
7731
|
+
result3 = await evalMethod.run(
|
|
7654
7732
|
agentInstance,
|
|
7655
7733
|
agentBackend,
|
|
7656
7734
|
testCase,
|
|
@@ -7661,15 +7739,15 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7661
7739
|
test_case_id: testCase.id,
|
|
7662
7740
|
eval_run_id: evalRun.id,
|
|
7663
7741
|
eval_function_id: evalFunction.id,
|
|
7664
|
-
result:
|
|
7742
|
+
result: result3 || 0
|
|
7665
7743
|
};
|
|
7666
7744
|
evalFunctionResults.push(evalFunctionResult);
|
|
7667
|
-
console.log(`[EXULU] eval function ${evalFunction.id} result: ${
|
|
7668
|
-
result:
|
|
7745
|
+
console.log(`[EXULU] eval function ${evalFunction.id} result: ${result3}`, logMetadata2(bullmqJob.name, {
|
|
7746
|
+
result: result3 || 0
|
|
7669
7747
|
}));
|
|
7670
7748
|
}
|
|
7671
7749
|
}
|
|
7672
|
-
const scores = evalFunctionResults.map((
|
|
7750
|
+
const scores = evalFunctionResults.map((result3) => result3.result);
|
|
7673
7751
|
console.log("[EXULU] Exulu eval run scores for test case: " + testCase.id, scores);
|
|
7674
7752
|
let score = 0;
|
|
7675
7753
|
switch (data.scoring_method?.toLowerCase()) {
|
|
@@ -7734,25 +7812,25 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7734
7812
|
messages: inputMessages
|
|
7735
7813
|
} = await validateEvalPayload(data, agents);
|
|
7736
7814
|
const evalFunctions = evalRun.eval_functions;
|
|
7737
|
-
let
|
|
7815
|
+
let result2;
|
|
7738
7816
|
for (const evalFunction of evalFunctions) {
|
|
7739
7817
|
const evalMethod = evals.find((e) => e.id === evalFunction.id);
|
|
7740
7818
|
if (!evalMethod) {
|
|
7741
7819
|
throw new Error(`Eval function ${evalFunction.id} not found in the registry, check your code and make sure the eval function is registered correctly.`);
|
|
7742
7820
|
}
|
|
7743
|
-
|
|
7821
|
+
result2 = await evalMethod.run(
|
|
7744
7822
|
agentInstance,
|
|
7745
7823
|
backend,
|
|
7746
7824
|
testCase,
|
|
7747
7825
|
inputMessages,
|
|
7748
7826
|
evalFunction.config || {}
|
|
7749
7827
|
);
|
|
7750
|
-
console.log(`[EXULU] eval function ${evalFunction.id} result: ${
|
|
7751
|
-
result:
|
|
7828
|
+
console.log(`[EXULU] eval function ${evalFunction.id} result: ${result2}`, logMetadata2(bullmqJob.name, {
|
|
7829
|
+
result: result2 || 0
|
|
7752
7830
|
}));
|
|
7753
7831
|
}
|
|
7754
7832
|
return {
|
|
7755
|
-
result,
|
|
7833
|
+
result: result2,
|
|
7756
7834
|
metadata: {}
|
|
7757
7835
|
};
|
|
7758
7836
|
}
|
|
@@ -7772,10 +7850,10 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7772
7850
|
if (!source) {
|
|
7773
7851
|
throw new Error(`Source ${data.source} not found in the context ${context.id}.`);
|
|
7774
7852
|
}
|
|
7775
|
-
const
|
|
7853
|
+
const result2 = await source.execute(data.inputs);
|
|
7776
7854
|
let jobs = [];
|
|
7777
7855
|
let items = [];
|
|
7778
|
-
for (const item of
|
|
7856
|
+
for (const item of result2) {
|
|
7779
7857
|
const { item: createdItem, job } = await context.createItem(
|
|
7780
7858
|
item,
|
|
7781
7859
|
config,
|
|
@@ -7807,7 +7885,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7807
7885
|
role: data?.role
|
|
7808
7886
|
});
|
|
7809
7887
|
return {
|
|
7810
|
-
result,
|
|
7888
|
+
result: result2,
|
|
7811
7889
|
metadata: {
|
|
7812
7890
|
jobs,
|
|
7813
7891
|
items
|
|
@@ -7820,11 +7898,13 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7820
7898
|
throw error;
|
|
7821
7899
|
}
|
|
7822
7900
|
})();
|
|
7823
|
-
|
|
7901
|
+
const result = await Promise.race([workPromise, timeoutPromise]);
|
|
7902
|
+
return result;
|
|
7824
7903
|
},
|
|
7825
7904
|
{
|
|
7826
7905
|
autorun: true,
|
|
7827
7906
|
connection: redisConnection,
|
|
7907
|
+
concurrency: queue.concurrency?.worker || 1,
|
|
7828
7908
|
removeOnComplete: { count: 1e3 },
|
|
7829
7909
|
removeOnFail: { count: 5e3 },
|
|
7830
7910
|
...queue.ratelimit && {
|
|
@@ -9028,18 +9108,23 @@ var ExuluQueues = class {
|
|
|
9028
9108
|
// method of ExuluQueues we need to store the desired rate limit on the queue
|
|
9029
9109
|
// here so we can use the value when creating workers for the queue instance
|
|
9030
9110
|
// as there is no way to store a rate limit value natively on a bullm queue.
|
|
9031
|
-
register = (name, concurrency
|
|
9111
|
+
register = (name, concurrency, ratelimit = 1) => {
|
|
9112
|
+
const queueConcurrency = concurrency.queue || 1;
|
|
9113
|
+
const workerConcurrency = concurrency.worker || 1;
|
|
9032
9114
|
const use = async () => {
|
|
9033
9115
|
const existing = this.queues.find((x) => x.queue?.name === name);
|
|
9034
9116
|
if (existing) {
|
|
9035
9117
|
const globalConcurrency = await existing.queue.getGlobalConcurrency();
|
|
9036
|
-
if (globalConcurrency !==
|
|
9037
|
-
await existing.queue.setGlobalConcurrency(
|
|
9118
|
+
if (globalConcurrency !== queueConcurrency) {
|
|
9119
|
+
await existing.queue.setGlobalConcurrency(queueConcurrency);
|
|
9038
9120
|
}
|
|
9039
9121
|
return {
|
|
9040
9122
|
queue: existing.queue,
|
|
9041
9123
|
ratelimit,
|
|
9042
|
-
concurrency
|
|
9124
|
+
concurrency: {
|
|
9125
|
+
worker: workerConcurrency,
|
|
9126
|
+
queue: queueConcurrency
|
|
9127
|
+
}
|
|
9043
9128
|
};
|
|
9044
9129
|
}
|
|
9045
9130
|
if (!redisServer.host?.length || !redisServer.port?.length) {
|
|
@@ -9058,21 +9143,30 @@ var ExuluQueues = class {
|
|
|
9058
9143
|
telemetry: new BullMQOtel("simple-guide")
|
|
9059
9144
|
}
|
|
9060
9145
|
);
|
|
9061
|
-
await newQueue.setGlobalConcurrency(
|
|
9146
|
+
await newQueue.setGlobalConcurrency(queueConcurrency);
|
|
9062
9147
|
this.queues.push({
|
|
9063
9148
|
queue: newQueue,
|
|
9064
9149
|
ratelimit,
|
|
9065
|
-
concurrency
|
|
9150
|
+
concurrency: {
|
|
9151
|
+
worker: workerConcurrency,
|
|
9152
|
+
queue: queueConcurrency
|
|
9153
|
+
}
|
|
9066
9154
|
});
|
|
9067
9155
|
return {
|
|
9068
9156
|
queue: newQueue,
|
|
9069
9157
|
ratelimit,
|
|
9070
|
-
concurrency
|
|
9158
|
+
concurrency: {
|
|
9159
|
+
worker: workerConcurrency,
|
|
9160
|
+
queue: queueConcurrency
|
|
9161
|
+
}
|
|
9071
9162
|
};
|
|
9072
9163
|
};
|
|
9073
9164
|
this.list.set(name, {
|
|
9074
9165
|
name,
|
|
9075
|
-
concurrency
|
|
9166
|
+
concurrency: {
|
|
9167
|
+
worker: workerConcurrency,
|
|
9168
|
+
queue: queueConcurrency
|
|
9169
|
+
},
|
|
9076
9170
|
ratelimit,
|
|
9077
9171
|
use
|
|
9078
9172
|
});
|
|
@@ -9128,7 +9222,10 @@ var llmAsJudgeEval = () => {
|
|
|
9128
9222
|
name: "prompt",
|
|
9129
9223
|
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."
|
|
9130
9224
|
}],
|
|
9131
|
-
queue: queues.register("llm_as_judge",
|
|
9225
|
+
queue: queues.register("llm_as_judge", {
|
|
9226
|
+
worker: 1,
|
|
9227
|
+
queue: 1
|
|
9228
|
+
}, 1).use(),
|
|
9132
9229
|
llm: true
|
|
9133
9230
|
});
|
|
9134
9231
|
}
|
|
@@ -9763,11 +9860,16 @@ var previewPdfTool = new ExuluTool2({
|
|
|
9763
9860
|
type: "function",
|
|
9764
9861
|
config: [],
|
|
9765
9862
|
inputSchema: z5.object({
|
|
9766
|
-
s3key: z5.string().describe("The S3 key of the PDF file to preview
|
|
9863
|
+
s3key: z5.string().describe("The S3 key of the PDF file to preview."),
|
|
9767
9864
|
page: z5.number().describe("The page number to preview, defaults to 1.").optional()
|
|
9768
9865
|
}),
|
|
9769
9866
|
execute: async ({ s3key, page, exuluConfig }) => {
|
|
9770
|
-
const
|
|
9867
|
+
const bucket = s3key.split("/")[0];
|
|
9868
|
+
const key = s3key.split("/").slice(1).join("/");
|
|
9869
|
+
if (!bucket || !key) {
|
|
9870
|
+
throw new Error("Invalid S3 key, must be in the format of <bucket>/<key>.");
|
|
9871
|
+
}
|
|
9872
|
+
const url = await getPresignedUrl(bucket, key, exuluConfig);
|
|
9771
9873
|
if (!url) {
|
|
9772
9874
|
throw new Error("No URL provided for PDF preview");
|
|
9773
9875
|
}
|
|
@@ -10161,7 +10263,10 @@ var ExuluApp = class {
|
|
|
10161
10263
|
}
|
|
10162
10264
|
const queueSet = /* @__PURE__ */ new Set();
|
|
10163
10265
|
if (redisServer.host?.length && redisServer.port?.length) {
|
|
10164
|
-
queues.register(global_queues.eval_runs,
|
|
10266
|
+
queues.register(global_queues.eval_runs, {
|
|
10267
|
+
worker: 1,
|
|
10268
|
+
queue: 1
|
|
10269
|
+
}, 1);
|
|
10165
10270
|
for (const queue of queues.list.values()) {
|
|
10166
10271
|
const config2 = await queue.use();
|
|
10167
10272
|
queueSet.add(config2);
|