@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.cjs
CHANGED
|
@@ -67,7 +67,6 @@ var redisServer = {
|
|
|
67
67
|
// src/redis/client.ts
|
|
68
68
|
var client = {};
|
|
69
69
|
async function redisClient() {
|
|
70
|
-
console.log("[EXULU] redisServer:", redisServer);
|
|
71
70
|
if (!redisServer.host || !redisServer.port) {
|
|
72
71
|
return { client: null };
|
|
73
72
|
}
|
|
@@ -148,7 +147,6 @@ var db = {};
|
|
|
148
147
|
var databaseExistsChecked = false;
|
|
149
148
|
var dbName = process.env.POSTGRES_DB_NAME || "exulu";
|
|
150
149
|
async function ensureDatabaseExists() {
|
|
151
|
-
console.log(`[EXULU] Ensuring ${dbName} database exists...`);
|
|
152
150
|
const defaultKnex = (0, import_knex.default)({
|
|
153
151
|
client: "pg",
|
|
154
152
|
connection: {
|
|
@@ -192,16 +190,7 @@ async function ensureDatabaseExists() {
|
|
|
192
190
|
async function postgresClient() {
|
|
193
191
|
if (!db["exulu"]) {
|
|
194
192
|
try {
|
|
195
|
-
console.log(`[EXULU] Connecting to ${dbName} database.`);
|
|
196
|
-
console.log("[EXULU] POSTGRES_DB_HOST:", process.env.POSTGRES_DB_HOST);
|
|
197
|
-
console.log("[EXULU] POSTGRES_DB_PORT:", process.env.POSTGRES_DB_PORT);
|
|
198
|
-
console.log("[EXULU] POSTGRES_DB_USER:", process.env.POSTGRES_DB_USER);
|
|
199
|
-
console.log("[EXULU] POSTGRES_DB_PASSWORD:", process.env.POSTGRES_DB_PASSWORD);
|
|
200
|
-
console.log("[EXULU] POSTGRES_DB_NAME:", dbName);
|
|
201
|
-
console.log("[EXULU] POSTGRES_DB_SSL:", process.env.POSTGRES_DB_SSL);
|
|
202
|
-
console.log("[EXULU] Database exists checked:", databaseExistsChecked);
|
|
203
193
|
if (!databaseExistsChecked) {
|
|
204
|
-
console.log(`[EXULU] Ensuring ${dbName} database exists...`);
|
|
205
194
|
await ensureDatabaseExists();
|
|
206
195
|
databaseExistsChecked = true;
|
|
207
196
|
}
|
|
@@ -526,7 +515,6 @@ var authentication = async ({
|
|
|
526
515
|
}
|
|
527
516
|
if (authtoken) {
|
|
528
517
|
try {
|
|
529
|
-
console.log("[EXULU] authtoken", authtoken);
|
|
530
518
|
if (!authtoken?.email) {
|
|
531
519
|
return {
|
|
532
520
|
error: true,
|
|
@@ -3730,7 +3718,10 @@ type PageInfo {
|
|
|
3730
3718
|
const config2 = await queue.use();
|
|
3731
3719
|
return {
|
|
3732
3720
|
name: config2.queue.name,
|
|
3733
|
-
concurrency:
|
|
3721
|
+
concurrency: {
|
|
3722
|
+
worker: config2.concurrency?.worker || void 0,
|
|
3723
|
+
queue: config2.concurrency?.queue || void 0
|
|
3724
|
+
},
|
|
3734
3725
|
ratelimit: config2.ratelimit,
|
|
3735
3726
|
isMaxed: await config2.queue.isMaxed(),
|
|
3736
3727
|
isPaused: await config2.queue.isPaused(),
|
|
@@ -3780,7 +3771,10 @@ type PageInfo {
|
|
|
3780
3771
|
if (!agentInstance) {
|
|
3781
3772
|
throw new Error("Agent instance not found for eval run.");
|
|
3782
3773
|
}
|
|
3783
|
-
const evalQueue = await queues.register("eval_runs",
|
|
3774
|
+
const evalQueue = await queues.register("eval_runs", {
|
|
3775
|
+
worker: 1,
|
|
3776
|
+
queue: 1
|
|
3777
|
+
}, 1).use();
|
|
3784
3778
|
const jobIds = [];
|
|
3785
3779
|
for (const testCase of testCases) {
|
|
3786
3780
|
const jobData = {
|
|
@@ -3904,7 +3898,6 @@ type PageInfo {
|
|
|
3904
3898
|
if (!client2) {
|
|
3905
3899
|
throw new Error("Redis client not created properly");
|
|
3906
3900
|
}
|
|
3907
|
-
console.log("[EXULU] Jobs pagination args", args);
|
|
3908
3901
|
const {
|
|
3909
3902
|
jobs,
|
|
3910
3903
|
count
|
|
@@ -3914,7 +3907,6 @@ type PageInfo {
|
|
|
3914
3907
|
args.page || 1,
|
|
3915
3908
|
args.limit || 100
|
|
3916
3909
|
);
|
|
3917
|
-
console.log("[EXULU] jobs", jobs.map((job) => job.name));
|
|
3918
3910
|
const requestedFields = getRequestedFields(info);
|
|
3919
3911
|
return {
|
|
3920
3912
|
items: await Promise.all(jobs.map(async (job) => {
|
|
@@ -3943,6 +3935,25 @@ type PageInfo {
|
|
|
3943
3935
|
};
|
|
3944
3936
|
resolvers.Query["contexts"] = async (_, args, context, info) => {
|
|
3945
3937
|
const data = await Promise.all(contexts.map(async (context2) => {
|
|
3938
|
+
let processors = await Promise.all(context2.fields.map(async (field) => {
|
|
3939
|
+
if (field.processor) {
|
|
3940
|
+
let queueName = void 0;
|
|
3941
|
+
if (field.processor?.config?.queue) {
|
|
3942
|
+
const config2 = await field.processor?.config?.queue;
|
|
3943
|
+
queueName = config2?.queue?.name || void 0;
|
|
3944
|
+
}
|
|
3945
|
+
return {
|
|
3946
|
+
field: field.name,
|
|
3947
|
+
description: field.processor?.description,
|
|
3948
|
+
queue: queueName,
|
|
3949
|
+
trigger: field.processor?.config?.trigger,
|
|
3950
|
+
timeoutInSeconds: field.processor?.config?.timeoutInSeconds || 600,
|
|
3951
|
+
generateEmbeddings: field.processor?.config?.generateEmbeddings || false
|
|
3952
|
+
};
|
|
3953
|
+
}
|
|
3954
|
+
return null;
|
|
3955
|
+
}));
|
|
3956
|
+
processors = processors.filter((processor) => processor !== null);
|
|
3946
3957
|
const sources = await Promise.all(context2.sources.map(async (source) => {
|
|
3947
3958
|
let queueName = void 0;
|
|
3948
3959
|
if (source.config) {
|
|
@@ -3974,6 +3985,7 @@ type PageInfo {
|
|
|
3974
3985
|
slug: "/contexts/" + context2.id,
|
|
3975
3986
|
active: context2.active,
|
|
3976
3987
|
sources,
|
|
3988
|
+
processors,
|
|
3977
3989
|
fields: context2.fields.map((field) => {
|
|
3978
3990
|
return {
|
|
3979
3991
|
...field,
|
|
@@ -3999,6 +4011,25 @@ type PageInfo {
|
|
|
3999
4011
|
if (!data) {
|
|
4000
4012
|
return null;
|
|
4001
4013
|
}
|
|
4014
|
+
let processors = await Promise.all(data.fields.map(async (field) => {
|
|
4015
|
+
if (field.processor) {
|
|
4016
|
+
let queueName = void 0;
|
|
4017
|
+
if (field.processor?.config?.queue) {
|
|
4018
|
+
const config2 = await field.processor?.config?.queue;
|
|
4019
|
+
queueName = config2?.queue?.name || void 0;
|
|
4020
|
+
}
|
|
4021
|
+
return {
|
|
4022
|
+
field: field.name,
|
|
4023
|
+
description: field.processor?.description,
|
|
4024
|
+
queue: queueName,
|
|
4025
|
+
trigger: field.processor?.config?.trigger,
|
|
4026
|
+
timeoutInSeconds: field.processor?.config?.timeoutInSeconds || 600,
|
|
4027
|
+
generateEmbeddings: field.processor?.config?.generateEmbeddings || false
|
|
4028
|
+
};
|
|
4029
|
+
}
|
|
4030
|
+
return null;
|
|
4031
|
+
}));
|
|
4032
|
+
processors = processors.filter((processor) => processor !== null);
|
|
4002
4033
|
const sources = await Promise.all(data.sources.map(async (source) => {
|
|
4003
4034
|
let queueName = void 0;
|
|
4004
4035
|
if (source.config) {
|
|
@@ -4035,6 +4066,7 @@ type PageInfo {
|
|
|
4035
4066
|
slug: "/contexts/" + data.id,
|
|
4036
4067
|
active: data.active,
|
|
4037
4068
|
sources,
|
|
4069
|
+
processors,
|
|
4038
4070
|
fields: await Promise.all(data.fields.map(async (field) => {
|
|
4039
4071
|
const label = field.name?.replace("_s3key", "");
|
|
4040
4072
|
if (field.type === "file" && !field.name.endsWith("_s3key")) {
|
|
@@ -4058,7 +4090,10 @@ type PageInfo {
|
|
|
4058
4090
|
queue: {
|
|
4059
4091
|
name: queue?.queue.name || void 0,
|
|
4060
4092
|
ratelimit: queue?.ratelimit || void 0,
|
|
4061
|
-
concurrency:
|
|
4093
|
+
concurrency: {
|
|
4094
|
+
worker: queue?.concurrency?.worker || void 0,
|
|
4095
|
+
queue: queue?.concurrency?.queue || void 0
|
|
4096
|
+
}
|
|
4062
4097
|
}
|
|
4063
4098
|
},
|
|
4064
4099
|
execute: "function"
|
|
@@ -4131,13 +4166,19 @@ type PageInfo {
|
|
|
4131
4166
|
modelDefs += `
|
|
4132
4167
|
type QueueResult {
|
|
4133
4168
|
name: String!
|
|
4134
|
-
concurrency:
|
|
4169
|
+
concurrency: QueueConcurrency!
|
|
4135
4170
|
ratelimit: Int!
|
|
4136
4171
|
isMaxed: Boolean!
|
|
4137
4172
|
isPaused: Boolean!
|
|
4138
4173
|
jobs: QueueJobsCounts
|
|
4139
4174
|
}
|
|
4140
4175
|
`;
|
|
4176
|
+
modelDefs += `
|
|
4177
|
+
type QueueConcurrency {
|
|
4178
|
+
worker: Int
|
|
4179
|
+
queue: Int
|
|
4180
|
+
}
|
|
4181
|
+
`;
|
|
4141
4182
|
modelDefs += `
|
|
4142
4183
|
type QueueJobsCounts {
|
|
4143
4184
|
paused: Int!
|
|
@@ -4252,7 +4293,8 @@ type Context {
|
|
|
4252
4293
|
active: Boolean
|
|
4253
4294
|
fields: JSON
|
|
4254
4295
|
configuration: JSON
|
|
4255
|
-
sources: [ContextSource
|
|
4296
|
+
sources: [ContextSource]
|
|
4297
|
+
processors: [ContextProcessor]
|
|
4256
4298
|
}
|
|
4257
4299
|
type Embedder {
|
|
4258
4300
|
name: String!
|
|
@@ -4265,6 +4307,14 @@ type EmbedderConfig {
|
|
|
4265
4307
|
description: String
|
|
4266
4308
|
default: String
|
|
4267
4309
|
}
|
|
4310
|
+
type ContextProcessor {
|
|
4311
|
+
field: String!
|
|
4312
|
+
description: String
|
|
4313
|
+
queue: String
|
|
4314
|
+
trigger: String
|
|
4315
|
+
timeoutInSeconds: Int
|
|
4316
|
+
generateEmbeddings: Boolean
|
|
4317
|
+
}
|
|
4268
4318
|
|
|
4269
4319
|
type ContextSource {
|
|
4270
4320
|
id: String!
|
|
@@ -4383,10 +4433,7 @@ async function getJobsByQueueName(queueName, statusses, page, limit) {
|
|
|
4383
4433
|
const config = await queue.use();
|
|
4384
4434
|
const startIndex = (page || 1) - 1;
|
|
4385
4435
|
const endIndex = startIndex - 1 + (limit || 100);
|
|
4386
|
-
console.log("[EXULU] Jobs pagination startIndex", startIndex);
|
|
4387
|
-
console.log("[EXULU] Jobs pagination endIndex", endIndex);
|
|
4388
4436
|
const jobs = await config.queue.getJobs(statusses || [], startIndex, endIndex, false);
|
|
4389
|
-
console.log("[EXULU] Jobs pagination jobs", jobs?.length);
|
|
4390
4437
|
const counts = await config.queue.getJobCounts(...statusses || []);
|
|
4391
4438
|
let total = 0;
|
|
4392
4439
|
if (counts) {
|
|
@@ -4427,21 +4474,10 @@ function getS3Client(config) {
|
|
|
4427
4474
|
});
|
|
4428
4475
|
return s3Client;
|
|
4429
4476
|
}
|
|
4430
|
-
var getPresignedUrl = async (key, config) => {
|
|
4477
|
+
var getPresignedUrl = async (bucket, key, config) => {
|
|
4431
4478
|
if (!config.fileUploads) {
|
|
4432
4479
|
throw new Error("File uploads are not configured");
|
|
4433
4480
|
}
|
|
4434
|
-
let bucket = config.fileUploads.s3Bucket;
|
|
4435
|
-
if (key.includes("[bucket:")) {
|
|
4436
|
-
console.log("[EXULU] key includes [bucket:name]", key);
|
|
4437
|
-
bucket = key.split("[bucket:")[1]?.split("]")[0] || "";
|
|
4438
|
-
if (!bucket?.length) {
|
|
4439
|
-
throw new Error("Invalid key, does not contain a bucket name like '[bucket:name]'.");
|
|
4440
|
-
}
|
|
4441
|
-
key = key.split("]")[1] || "";
|
|
4442
|
-
console.log("[EXULU] bucket", bucket);
|
|
4443
|
-
console.log("[EXULU] key", key);
|
|
4444
|
-
}
|
|
4445
4481
|
const url = await (0, import_s3_request_presigner.getSignedUrl)(
|
|
4446
4482
|
getS3Client(config),
|
|
4447
4483
|
new import_client_s3.GetObjectCommand({
|
|
@@ -4452,7 +4488,7 @@ var getPresignedUrl = async (key, config) => {
|
|
|
4452
4488
|
);
|
|
4453
4489
|
return url;
|
|
4454
4490
|
};
|
|
4455
|
-
var
|
|
4491
|
+
var addGeneralPrefixToKey = (keyPath, config) => {
|
|
4456
4492
|
if (!config.fileUploads) {
|
|
4457
4493
|
throw new Error("File uploads are not configured");
|
|
4458
4494
|
}
|
|
@@ -4465,58 +4501,50 @@ var addPrefixToKey = (keyPath, config) => {
|
|
|
4465
4501
|
}
|
|
4466
4502
|
return `${prefix}/${keyPath}`;
|
|
4467
4503
|
};
|
|
4468
|
-
var
|
|
4504
|
+
var addUserPrefixToKey = (key, user) => {
|
|
4505
|
+
if (!user) {
|
|
4506
|
+
return key;
|
|
4507
|
+
}
|
|
4508
|
+
if (key.includes(`/user_${user}/`)) {
|
|
4509
|
+
return key;
|
|
4510
|
+
}
|
|
4511
|
+
return `user_${user}/${key}`;
|
|
4512
|
+
};
|
|
4513
|
+
var addBucketPrefixToKey = (key, bucket) => {
|
|
4514
|
+
if (key.includes(`/${bucket}/`)) {
|
|
4515
|
+
return key;
|
|
4516
|
+
}
|
|
4517
|
+
return `${bucket}/${key}`;
|
|
4518
|
+
};
|
|
4519
|
+
var uploadFile = async (file, fileName, config, options = {}, user, customBucket) => {
|
|
4469
4520
|
if (!config.fileUploads) {
|
|
4470
4521
|
throw new Error("File uploads are not configured (in the exported uploadFile function)");
|
|
4471
4522
|
}
|
|
4472
4523
|
const client2 = getS3Client(config);
|
|
4473
4524
|
let defaultBucket = config.fileUploads.s3Bucket;
|
|
4474
|
-
let
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
if (!customBucket?.length) {
|
|
4479
|
-
throw new Error("Invalid key, does not contain a bucket name like '[bucket:name]'.");
|
|
4480
|
-
}
|
|
4481
|
-
key = key.split("]")[1] || "";
|
|
4482
|
-
console.log("[EXULU] custom bucket", customBucket);
|
|
4483
|
-
}
|
|
4484
|
-
let folder = user ? `${user}/` : "";
|
|
4485
|
-
const fullKey = addPrefixToKey(!key.includes(folder) ? folder + key : key, config);
|
|
4486
|
-
console.log("[EXULU] uploading file to s3 into bucket", customBucket || defaultBucket, "with key", fullKey);
|
|
4525
|
+
let key = fileName;
|
|
4526
|
+
key = addGeneralPrefixToKey(key, config);
|
|
4527
|
+
key = addUserPrefixToKey(key, user || "api");
|
|
4528
|
+
console.log("[EXULU] uploading file to s3 into bucket", defaultBucket, "with key", key);
|
|
4487
4529
|
const command = new import_client_s3.PutObjectCommand({
|
|
4488
4530
|
Bucket: customBucket || defaultBucket,
|
|
4489
|
-
Key:
|
|
4531
|
+
Key: key,
|
|
4490
4532
|
Body: file,
|
|
4491
4533
|
ContentType: options.contentType,
|
|
4492
4534
|
Metadata: options.metadata,
|
|
4493
4535
|
ContentLength: file.byteLength
|
|
4494
4536
|
});
|
|
4495
4537
|
await client2.send(command);
|
|
4496
|
-
console.log("[EXULU] file uploaded to s3 into bucket", customBucket || defaultBucket, "with key",
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4538
|
+
console.log("[EXULU] file uploaded to s3 into bucket", customBucket || defaultBucket, "with key", key);
|
|
4539
|
+
return addBucketPrefixToKey(
|
|
4540
|
+
key,
|
|
4541
|
+
customBucket || defaultBucket
|
|
4542
|
+
);
|
|
4501
4543
|
};
|
|
4502
|
-
var createUppyRoutes = async (app, config) => {
|
|
4544
|
+
var createUppyRoutes = async (app, contexts, config) => {
|
|
4503
4545
|
if (!config.fileUploads) {
|
|
4504
4546
|
throw new Error("File uploads are not configured");
|
|
4505
4547
|
}
|
|
4506
|
-
const extractUserPrefix = (key) => {
|
|
4507
|
-
if (!config.fileUploads) {
|
|
4508
|
-
throw new Error("File uploads are not configured");
|
|
4509
|
-
}
|
|
4510
|
-
if (!config.fileUploads.s3prefix) {
|
|
4511
|
-
return key.split("/")[0];
|
|
4512
|
-
}
|
|
4513
|
-
const prefix = config.fileUploads.s3prefix.replace(/\/$/, "");
|
|
4514
|
-
if (key.startsWith(prefix + "/")) {
|
|
4515
|
-
const keyWithoutPrefix = key.slice(prefix.length + 1);
|
|
4516
|
-
return keyWithoutPrefix.split("/")[0];
|
|
4517
|
-
}
|
|
4518
|
-
return key.split("/")[0];
|
|
4519
|
-
};
|
|
4520
4548
|
const policy = {
|
|
4521
4549
|
Version: "2012-10-17",
|
|
4522
4550
|
Statement: [
|
|
@@ -4568,20 +4596,16 @@ var createUppyRoutes = async (app, config) => {
|
|
|
4568
4596
|
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
4569
4597
|
return;
|
|
4570
4598
|
}
|
|
4599
|
+
const user = authenticationResult.user;
|
|
4571
4600
|
let { key } = req.query;
|
|
4572
4601
|
if (typeof key !== "string" || key.trim() === "") {
|
|
4573
4602
|
res.status(400).json({ error: "Missing or invalid `key` query parameter." });
|
|
4574
4603
|
return;
|
|
4575
4604
|
}
|
|
4576
|
-
let bucket =
|
|
4577
|
-
if (key.includes(
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
if (!bucket?.length) {
|
|
4581
|
-
throw new Error("Invalid key, does not contain a bucket name like '[bucket:name]'.");
|
|
4582
|
-
}
|
|
4583
|
-
key = key.split("]")[1] || "";
|
|
4584
|
-
console.log("[EXULU] bucket", bucket);
|
|
4605
|
+
let bucket = key.split("/")[0];
|
|
4606
|
+
if (user.type !== "api" && !key.includes(`/user_${user.id}/`) && !user.super_admin) {
|
|
4607
|
+
res.status(405).json({ error: "Not allowed to access the files in the folder based on authenticated user." });
|
|
4608
|
+
return;
|
|
4585
4609
|
}
|
|
4586
4610
|
const client2 = getS3Client(config);
|
|
4587
4611
|
const command = new import_client_s3.DeleteObjectCommand({
|
|
@@ -4609,13 +4633,32 @@ var createUppyRoutes = async (app, config) => {
|
|
|
4609
4633
|
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
4610
4634
|
return;
|
|
4611
4635
|
}
|
|
4612
|
-
const
|
|
4636
|
+
const user = authenticationResult.user;
|
|
4637
|
+
let { key } = req.query;
|
|
4638
|
+
if (!key || typeof key !== "string" || key.trim() === "") {
|
|
4639
|
+
res.status(400).json({ error: "Missing or invalid `key` query parameter." });
|
|
4640
|
+
return;
|
|
4641
|
+
}
|
|
4642
|
+
let bucket = key.split("/")[0];
|
|
4643
|
+
if (!bucket || typeof bucket !== "string" || bucket.trim() === "") {
|
|
4644
|
+
res.status(400).json({ error: "Missing or invalid `bucket` (should be the first part of the key before the first slash)." });
|
|
4645
|
+
return;
|
|
4646
|
+
}
|
|
4647
|
+
key = key.split("/").slice(1).join("/");
|
|
4613
4648
|
if (typeof key !== "string" || key.trim() === "") {
|
|
4614
4649
|
res.status(400).json({ error: "Missing or invalid `key` query parameter." });
|
|
4615
4650
|
return;
|
|
4616
4651
|
}
|
|
4652
|
+
let allowed = false;
|
|
4653
|
+
if (user.type === "api" || user.super_admin || key.includes(`user_${user.id}/`)) {
|
|
4654
|
+
allowed = true;
|
|
4655
|
+
}
|
|
4656
|
+
if (!allowed) {
|
|
4657
|
+
res.status(405).json({ error: "Not allowed to access the file based on authenticated user." });
|
|
4658
|
+
return;
|
|
4659
|
+
}
|
|
4617
4660
|
try {
|
|
4618
|
-
const url = await getPresignedUrl(key, config);
|
|
4661
|
+
const url = await getPresignedUrl(bucket, key, config);
|
|
4619
4662
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
4620
4663
|
res.json({ url, method: "GET", expiresIn });
|
|
4621
4664
|
} catch (err) {
|
|
@@ -4644,16 +4687,15 @@ var createUppyRoutes = async (app, config) => {
|
|
|
4644
4687
|
return;
|
|
4645
4688
|
}
|
|
4646
4689
|
let { key } = req.body;
|
|
4647
|
-
let bucket =
|
|
4648
|
-
if (
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
}
|
|
4655
|
-
|
|
4656
|
-
console.log("[EXULU] key", key);
|
|
4690
|
+
let bucket = key.split("/")[0];
|
|
4691
|
+
if (!bucket || typeof bucket !== "string" || bucket.trim() === "") {
|
|
4692
|
+
res.status(400).json({ error: "Missing or invalid `bucket` (should be the first part of the key before the first slash)." });
|
|
4693
|
+
return;
|
|
4694
|
+
}
|
|
4695
|
+
key = key.split("/").slice(1).join("/");
|
|
4696
|
+
if (!key || typeof key !== "string" || key.trim() === "") {
|
|
4697
|
+
res.status(400).json({ error: "Missing or invalid `key` query parameter." });
|
|
4698
|
+
return;
|
|
4657
4699
|
}
|
|
4658
4700
|
const client2 = getS3Client(config);
|
|
4659
4701
|
const command = new import_client_s3.HeadObjectCommand({
|
|
@@ -4757,11 +4799,12 @@ var createUppyRoutes = async (app, config) => {
|
|
|
4757
4799
|
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
4758
4800
|
return;
|
|
4759
4801
|
}
|
|
4802
|
+
const user = authenticationResult.user;
|
|
4760
4803
|
const { filename, contentType } = extractFileParameters(req);
|
|
4761
4804
|
validateFileParameters(filename, contentType);
|
|
4762
4805
|
const key = generateS3Key2(filename);
|
|
4763
|
-
let
|
|
4764
|
-
|
|
4806
|
+
let fullKey = addGeneralPrefixToKey(key, config);
|
|
4807
|
+
fullKey = addUserPrefixToKey(fullKey, user.type === "api" ? "api" : user.id);
|
|
4765
4808
|
(0, import_s3_request_presigner.getSignedUrl)(
|
|
4766
4809
|
getS3Client(config),
|
|
4767
4810
|
new import_client_s3.PutObjectCommand({
|
|
@@ -4805,6 +4848,7 @@ var createUppyRoutes = async (app, config) => {
|
|
|
4805
4848
|
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
4806
4849
|
return;
|
|
4807
4850
|
}
|
|
4851
|
+
const user = authenticationResult.user;
|
|
4808
4852
|
const client2 = getS3Client(config);
|
|
4809
4853
|
const { type, metadata, filename } = req.body;
|
|
4810
4854
|
if (typeof filename !== "string") {
|
|
@@ -4814,13 +4858,8 @@ var createUppyRoutes = async (app, config) => {
|
|
|
4814
4858
|
return res.status(400).json({ error: "s3: content type must be a string" });
|
|
4815
4859
|
}
|
|
4816
4860
|
const key = `${(0, import_node_crypto.randomUUID)()}-_EXULU_${filename}`;
|
|
4817
|
-
let
|
|
4818
|
-
|
|
4819
|
-
folder = `api/`;
|
|
4820
|
-
} else {
|
|
4821
|
-
folder = `${authenticationResult.user.id}/`;
|
|
4822
|
-
}
|
|
4823
|
-
const fullKey = addPrefixToKey(folder + key, config);
|
|
4861
|
+
let fullKey = addGeneralPrefixToKey(key, config);
|
|
4862
|
+
fullKey = addUserPrefixToKey(fullKey, user.type === "api" ? "api" : user.id);
|
|
4824
4863
|
const params = {
|
|
4825
4864
|
Bucket: config.fileUploads.s3Bucket,
|
|
4826
4865
|
Key: fullKey,
|
|
@@ -5023,7 +5062,7 @@ var createProjectRetrievalTool = async ({
|
|
|
5023
5062
|
if (!context) {
|
|
5024
5063
|
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.");
|
|
5025
5064
|
}
|
|
5026
|
-
const id = item.split("/")
|
|
5065
|
+
const id = item.split("/").slice(1).join("/");
|
|
5027
5066
|
if (set[context]) {
|
|
5028
5067
|
set[context].push(id);
|
|
5029
5068
|
} else {
|
|
@@ -5969,12 +6008,20 @@ var ExuluStorage = class {
|
|
|
5969
6008
|
this.config = config;
|
|
5970
6009
|
}
|
|
5971
6010
|
getPresignedUrl = async (key) => {
|
|
5972
|
-
|
|
6011
|
+
const bucket = key.split("/")[0];
|
|
6012
|
+
if (!bucket || typeof bucket !== "string" || bucket.trim() === "") {
|
|
6013
|
+
throw new Error("Invalid S3 key, must be in the format of <bucket>/<key>.");
|
|
6014
|
+
}
|
|
6015
|
+
key = key.split("/").slice(1).join("/");
|
|
6016
|
+
if (!key || typeof key !== "string" || key.trim() === "") {
|
|
6017
|
+
throw new Error("Invalid S3 key, must be in the format of <bucket>/<key>.");
|
|
6018
|
+
}
|
|
6019
|
+
return await getPresignedUrl(bucket, key, this.config);
|
|
5973
6020
|
};
|
|
5974
|
-
uploadFile = async (file,
|
|
6021
|
+
uploadFile = async (file, fileName, type, user, metadata, customBucket) => {
|
|
5975
6022
|
return await uploadFile(
|
|
5976
6023
|
file,
|
|
5977
|
-
|
|
6024
|
+
fileName,
|
|
5978
6025
|
this.config,
|
|
5979
6026
|
{
|
|
5980
6027
|
contentType: type,
|
|
@@ -5983,7 +6030,8 @@ var ExuluStorage = class {
|
|
|
5983
6030
|
type
|
|
5984
6031
|
}
|
|
5985
6032
|
},
|
|
5986
|
-
user
|
|
6033
|
+
user,
|
|
6034
|
+
customBucket
|
|
5987
6035
|
);
|
|
5988
6036
|
};
|
|
5989
6037
|
// todo add upload and delete methods
|
|
@@ -6034,6 +6082,9 @@ var ExuluContext = class {
|
|
|
6034
6082
|
this.resultReranker = resultReranker;
|
|
6035
6083
|
}
|
|
6036
6084
|
processField = async (trigger, item, exuluConfig, user, role) => {
|
|
6085
|
+
if (!item.field) {
|
|
6086
|
+
throw new Error("Field property on item is required for running a specific processor.");
|
|
6087
|
+
}
|
|
6037
6088
|
console.log("[EXULU] processing field", item.field, " in context", this.id);
|
|
6038
6089
|
console.log("[EXULU] fields", this.fields.map((field2) => field2.name));
|
|
6039
6090
|
const field = this.fields.find((field2) => {
|
|
@@ -6065,27 +6116,32 @@ var ExuluContext = class {
|
|
|
6065
6116
|
trigger
|
|
6066
6117
|
});
|
|
6067
6118
|
return {
|
|
6068
|
-
result:
|
|
6119
|
+
result: {},
|
|
6069
6120
|
job: job.id
|
|
6070
6121
|
};
|
|
6071
6122
|
}
|
|
6072
6123
|
console.log("[EXULU] POS 1 -- EXULU CONTEXT PROCESS FIELD");
|
|
6073
|
-
const
|
|
6124
|
+
const processorResult = await field.processor.execute({
|
|
6074
6125
|
item,
|
|
6075
6126
|
user,
|
|
6076
6127
|
role,
|
|
6077
6128
|
utils: {
|
|
6078
|
-
storage: exuluStorage
|
|
6079
|
-
items: {
|
|
6080
|
-
update: this.updateItem,
|
|
6081
|
-
create: this.createItem,
|
|
6082
|
-
delete: this.deleteItem
|
|
6083
|
-
}
|
|
6129
|
+
storage: exuluStorage
|
|
6084
6130
|
},
|
|
6085
6131
|
exuluConfig
|
|
6086
6132
|
});
|
|
6133
|
+
if (!processorResult) {
|
|
6134
|
+
throw new Error("Processor result is required for updating the item in the db.");
|
|
6135
|
+
}
|
|
6136
|
+
const { db: db3 } = await postgresClient();
|
|
6137
|
+
delete processorResult.field;
|
|
6138
|
+
await db3.from(getTableName(this.id)).where({
|
|
6139
|
+
id: processorResult.id
|
|
6140
|
+
}).update({
|
|
6141
|
+
...processorResult
|
|
6142
|
+
});
|
|
6087
6143
|
return {
|
|
6088
|
-
result,
|
|
6144
|
+
result: processorResult,
|
|
6089
6145
|
job: void 0
|
|
6090
6146
|
};
|
|
6091
6147
|
};
|
|
@@ -6170,6 +6226,8 @@ var ExuluContext = class {
|
|
|
6170
6226
|
};
|
|
6171
6227
|
};
|
|
6172
6228
|
createItem = async (item, config, user, role, upsert, generateEmbeddingsOverwrite) => {
|
|
6229
|
+
console.log("[EXULU] creating item", item);
|
|
6230
|
+
console.log("[EXULU] upsert", upsert);
|
|
6173
6231
|
if (upsert && (!item.id && !item.external_id)) {
|
|
6174
6232
|
throw new Error("Item id or external id is required for upsert.");
|
|
6175
6233
|
}
|
|
@@ -6197,7 +6255,7 @@ var ExuluContext = class {
|
|
|
6197
6255
|
}
|
|
6198
6256
|
console.log("[EXULU] context configuration", this.configuration);
|
|
6199
6257
|
let jobs = [];
|
|
6200
|
-
let shouldGenerateEmbeddings = this.embedder && generateEmbeddingsOverwrite !== false && (generateEmbeddingsOverwrite || this.configuration.calculateVectors === "
|
|
6258
|
+
let shouldGenerateEmbeddings = this.embedder && generateEmbeddingsOverwrite !== false && (generateEmbeddingsOverwrite || this.configuration.calculateVectors === "onInsert" || this.configuration.calculateVectors === "always");
|
|
6201
6259
|
for (const [key, value] of Object.entries(item)) {
|
|
6202
6260
|
console.log("[EXULU] Checking for processors for field", key);
|
|
6203
6261
|
const processor = this.fields.find((field) => field.name === key.replace("_s3key", ""))?.processor;
|
|
@@ -6220,8 +6278,13 @@ var ExuluContext = class {
|
|
|
6220
6278
|
if (processorJob) {
|
|
6221
6279
|
jobs.push(processorJob);
|
|
6222
6280
|
}
|
|
6223
|
-
if (!processorJob
|
|
6224
|
-
|
|
6281
|
+
if (!processorJob) {
|
|
6282
|
+
await db3.from(getTableName(this.id)).where({ id: results[0].id }).update({
|
|
6283
|
+
...processorResult
|
|
6284
|
+
});
|
|
6285
|
+
if (processor.config?.generateEmbeddings) {
|
|
6286
|
+
shouldGenerateEmbeddings = true;
|
|
6287
|
+
}
|
|
6225
6288
|
}
|
|
6226
6289
|
}
|
|
6227
6290
|
}
|
|
@@ -6247,6 +6310,7 @@ var ExuluContext = class {
|
|
|
6247
6310
|
};
|
|
6248
6311
|
};
|
|
6249
6312
|
updateItem = async (item, config, user, role, generateEmbeddingsOverwrite) => {
|
|
6313
|
+
console.log("[EXULU] updating item", item);
|
|
6250
6314
|
const { db: db3 } = await postgresClient();
|
|
6251
6315
|
if (item.field) {
|
|
6252
6316
|
delete item.field;
|
|
@@ -6292,8 +6356,13 @@ var ExuluContext = class {
|
|
|
6292
6356
|
if (processorJob) {
|
|
6293
6357
|
jobs.push(processorJob);
|
|
6294
6358
|
}
|
|
6295
|
-
if (!processorJob
|
|
6296
|
-
|
|
6359
|
+
if (!processorJob) {
|
|
6360
|
+
await db3.from(getTableName(this.id)).where({ id: record.id }).update({
|
|
6361
|
+
...processorResult
|
|
6362
|
+
});
|
|
6363
|
+
if (processor.config?.generateEmbeddings) {
|
|
6364
|
+
shouldGenerateEmbeddings = true;
|
|
6365
|
+
}
|
|
6297
6366
|
}
|
|
6298
6367
|
}
|
|
6299
6368
|
}
|
|
@@ -6317,23 +6386,23 @@ var ExuluContext = class {
|
|
|
6317
6386
|
};
|
|
6318
6387
|
deleteItem = async (item, user, role) => {
|
|
6319
6388
|
const { db: db3 } = await postgresClient();
|
|
6389
|
+
if (!item.id && !item.external_id) {
|
|
6390
|
+
throw new Error("Item id or external id is required for deleting an item.");
|
|
6391
|
+
}
|
|
6320
6392
|
if (!item.id?.length && item?.external_id) {
|
|
6321
6393
|
item = await db3.from(getTableName(this.id)).where({ external_id: item.external_id }).first();
|
|
6322
6394
|
if (!item || !item.id) {
|
|
6323
6395
|
throw new Error(`Item not found for external id ${item?.external_id || "undefined"}.`);
|
|
6324
6396
|
}
|
|
6325
6397
|
}
|
|
6326
|
-
|
|
6327
|
-
if (
|
|
6328
|
-
|
|
6329
|
-
|
|
6330
|
-
|
|
6331
|
-
}
|
|
6332
|
-
}
|
|
6333
|
-
const chunks = await db3.from(getChunksTableName(this.id)).where({ source: item.id }).select("id");
|
|
6334
|
-
if (chunks.length > 0) {
|
|
6335
|
-
await db3.from(getChunksTableName(this.id)).where({ source: item.id }).delete();
|
|
6398
|
+
const chunkTableExists = await this.chunksTableExists();
|
|
6399
|
+
if (chunkTableExists) {
|
|
6400
|
+
const chunks = await db3.from(getChunksTableName(this.id)).where({ source: item.id }).select("id");
|
|
6401
|
+
if (chunks.length > 0) {
|
|
6402
|
+
await db3.from(getChunksTableName(this.id)).where({ source: item.id }).delete();
|
|
6403
|
+
}
|
|
6336
6404
|
}
|
|
6405
|
+
await db3.from(getTableName(this.id)).where({ id: item.id }).delete();
|
|
6337
6406
|
return {
|
|
6338
6407
|
id: item.id,
|
|
6339
6408
|
job: void 0
|
|
@@ -7144,7 +7213,7 @@ Mood: friendly and intelligent.
|
|
|
7144
7213
|
});
|
|
7145
7214
|
});
|
|
7146
7215
|
if (config?.fileUploads && config?.fileUploads?.s3region && config?.fileUploads?.s3key && config?.fileUploads?.s3secret && config?.fileUploads?.s3Bucket) {
|
|
7147
|
-
await createUppyRoutes(app, config);
|
|
7216
|
+
await createUppyRoutes(app, contexts ?? [], config);
|
|
7148
7217
|
} else {
|
|
7149
7218
|
console.log("[EXULU] skipping uppy file upload routes, because no S3 compatible region, key or secret is set in ExuluApp instance.");
|
|
7150
7219
|
}
|
|
@@ -7457,6 +7526,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7457
7526
|
async (bullmqJob) => {
|
|
7458
7527
|
console.log("[EXULU] starting execution for job", logMetadata2(bullmqJob.name, {
|
|
7459
7528
|
name: bullmqJob.name,
|
|
7529
|
+
jobId: bullmqJob.id,
|
|
7460
7530
|
status: await bullmqJob.getState(),
|
|
7461
7531
|
type: bullmqJob.data.type
|
|
7462
7532
|
}));
|
|
@@ -7471,6 +7541,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7471
7541
|
});
|
|
7472
7542
|
const workPromise = (async () => {
|
|
7473
7543
|
try {
|
|
7544
|
+
console.log(`[EXULU] Job ${bullmqJob.id} - Log file: logs/jobs/job-${bullmqJob.id}.log`);
|
|
7474
7545
|
bullmq.validate(bullmqJob.id, data);
|
|
7475
7546
|
if (data.type === "embedder") {
|
|
7476
7547
|
console.log("[EXULU] running an embedder job.", logMetadata2(bullmqJob.name));
|
|
@@ -7493,17 +7564,17 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7493
7564
|
if (!embedder) {
|
|
7494
7565
|
throw new Error(`Embedder ${data.embedder} not found in the registry.`);
|
|
7495
7566
|
}
|
|
7496
|
-
const
|
|
7567
|
+
const result2 = await context.createAndUpsertEmbeddings(data.inputs, config, data.user, {
|
|
7497
7568
|
label: embedder.name,
|
|
7498
7569
|
trigger: data.trigger
|
|
7499
7570
|
}, data.role, bullmqJob.id);
|
|
7500
7571
|
return {
|
|
7501
|
-
result,
|
|
7572
|
+
result: result2,
|
|
7502
7573
|
metadata: {}
|
|
7503
7574
|
};
|
|
7504
7575
|
}
|
|
7505
7576
|
if (data.type === "processor") {
|
|
7506
|
-
console.log("[EXULU] running a processor job.",
|
|
7577
|
+
console.log("[EXULU] running a processor job, job name: ", bullmqJob.name, " job id: ", bullmqJob.id, " job data: ", data, " job queue: ", bullmqJob.queueName);
|
|
7507
7578
|
const label = `processor-${bullmqJob.name}`;
|
|
7508
7579
|
await db3.from("job_results").insert({
|
|
7509
7580
|
job_id: bullmqJob.id,
|
|
@@ -7525,35 +7596,42 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7525
7596
|
if (!field.processor) {
|
|
7526
7597
|
throw new Error(`Processor not set for field ${data.inputs.field} in the context ${data.context}.`);
|
|
7527
7598
|
}
|
|
7528
|
-
|
|
7529
|
-
|
|
7530
|
-
throw new Error(`User not set for processor job.`);
|
|
7531
|
-
}
|
|
7532
|
-
if (!data.role) {
|
|
7533
|
-
throw new Error(`Role not set for processor job.`);
|
|
7599
|
+
if (!data.inputs.id) {
|
|
7600
|
+
throw new Error(`[EXULU] Item not set for processor ${field.name} in context ${context.id}, running in job ${bullmqJob.id}.`);
|
|
7534
7601
|
}
|
|
7602
|
+
const exuluStorage = new ExuluStorage({ config });
|
|
7535
7603
|
console.log("[EXULU] POS 2 -- EXULU CONTEXT PROCESS FIELD");
|
|
7536
|
-
const
|
|
7604
|
+
const processorResult = await field.processor.execute({
|
|
7537
7605
|
item: data.inputs,
|
|
7538
7606
|
user: data.user,
|
|
7539
7607
|
role: data.role,
|
|
7540
7608
|
utils: {
|
|
7541
|
-
storage: exuluStorage
|
|
7542
|
-
items: {
|
|
7543
|
-
update: context.updateItem,
|
|
7544
|
-
create: context.createItem,
|
|
7545
|
-
delete: context.deleteItem
|
|
7546
|
-
}
|
|
7609
|
+
storage: exuluStorage
|
|
7547
7610
|
},
|
|
7548
|
-
config
|
|
7611
|
+
exuluConfig: config
|
|
7612
|
+
});
|
|
7613
|
+
if (!processorResult) {
|
|
7614
|
+
throw new Error(`[EXULU] Processor ${field.name} in context ${context.id}, running in job ${bullmqJob.id} did not return an item.`);
|
|
7615
|
+
}
|
|
7616
|
+
delete processorResult.field;
|
|
7617
|
+
await db3.from(getTableName(context.id)).where({
|
|
7618
|
+
id: processorResult.id
|
|
7619
|
+
}).update({
|
|
7620
|
+
...processorResult
|
|
7549
7621
|
});
|
|
7550
7622
|
let jobs = [];
|
|
7551
7623
|
if (field.processor.config?.generateEmbeddings) {
|
|
7624
|
+
const fullItem = await db3.from(getTableName(context.id)).where({
|
|
7625
|
+
id: processorResult.id
|
|
7626
|
+
}).first();
|
|
7627
|
+
if (!fullItem) {
|
|
7628
|
+
throw new Error(`[EXULU] Item ${processorResult.id} not found after processor update in context ${context.id}`);
|
|
7629
|
+
}
|
|
7552
7630
|
const { job: embeddingsJob } = await context.embeddings.generate.one({
|
|
7553
|
-
item:
|
|
7631
|
+
item: fullItem,
|
|
7554
7632
|
user: data.user,
|
|
7555
7633
|
role: data.role,
|
|
7556
|
-
trigger: "
|
|
7634
|
+
trigger: "processor",
|
|
7557
7635
|
config
|
|
7558
7636
|
});
|
|
7559
7637
|
if (embeddingsJob) {
|
|
@@ -7561,7 +7639,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7561
7639
|
}
|
|
7562
7640
|
}
|
|
7563
7641
|
return {
|
|
7564
|
-
result,
|
|
7642
|
+
result: processorResult,
|
|
7565
7643
|
metadata: {
|
|
7566
7644
|
jobs: jobs.length > 0 ? jobs.join(",") : void 0
|
|
7567
7645
|
}
|
|
@@ -7628,9 +7706,9 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7628
7706
|
}
|
|
7629
7707
|
}
|
|
7630
7708
|
});
|
|
7631
|
-
const
|
|
7632
|
-
const messages =
|
|
7633
|
-
const metadata =
|
|
7709
|
+
const result2 = await promise;
|
|
7710
|
+
const messages = result2.messages;
|
|
7711
|
+
const metadata = result2.metadata;
|
|
7634
7712
|
const evalFunctions = evalRun.eval_functions;
|
|
7635
7713
|
let evalFunctionResults = [];
|
|
7636
7714
|
for (const evalFunction of evalFunctions) {
|
|
@@ -7638,7 +7716,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7638
7716
|
if (!evalMethod) {
|
|
7639
7717
|
throw new Error(`Eval function ${evalFunction.id} not found in the registry, check your code and make sure the eval function is registered correctly.`);
|
|
7640
7718
|
}
|
|
7641
|
-
let
|
|
7719
|
+
let result3;
|
|
7642
7720
|
if (evalMethod.queue) {
|
|
7643
7721
|
const queue2 = await evalMethod.queue;
|
|
7644
7722
|
const jobData = {
|
|
@@ -7669,21 +7747,21 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7669
7747
|
if (!job.id) {
|
|
7670
7748
|
throw new Error(`Tried to add job to queue ${queue2.queue.name} but failed to get the job ID.`);
|
|
7671
7749
|
}
|
|
7672
|
-
|
|
7750
|
+
result3 = await pollJobResult({ queue: queue2, jobId: job.id });
|
|
7673
7751
|
const evalFunctionResult = {
|
|
7674
7752
|
test_case_id: testCase.id,
|
|
7675
7753
|
eval_run_id: evalRun.id,
|
|
7676
7754
|
eval_function_id: evalFunction.id,
|
|
7677
7755
|
eval_function_name: evalFunction.name,
|
|
7678
7756
|
eval_function_config: evalFunction.config || {},
|
|
7679
|
-
result:
|
|
7757
|
+
result: result3 || 0
|
|
7680
7758
|
};
|
|
7681
|
-
console.log(`[EXULU] eval function ${evalFunction.id} result: ${
|
|
7682
|
-
result:
|
|
7759
|
+
console.log(`[EXULU] eval function ${evalFunction.id} result: ${result3}`, logMetadata2(bullmqJob.name, {
|
|
7760
|
+
result: result3 || 0
|
|
7683
7761
|
}));
|
|
7684
7762
|
evalFunctionResults.push(evalFunctionResult);
|
|
7685
7763
|
} else {
|
|
7686
|
-
|
|
7764
|
+
result3 = await evalMethod.run(
|
|
7687
7765
|
agentInstance,
|
|
7688
7766
|
agentBackend,
|
|
7689
7767
|
testCase,
|
|
@@ -7694,15 +7772,15 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7694
7772
|
test_case_id: testCase.id,
|
|
7695
7773
|
eval_run_id: evalRun.id,
|
|
7696
7774
|
eval_function_id: evalFunction.id,
|
|
7697
|
-
result:
|
|
7775
|
+
result: result3 || 0
|
|
7698
7776
|
};
|
|
7699
7777
|
evalFunctionResults.push(evalFunctionResult);
|
|
7700
|
-
console.log(`[EXULU] eval function ${evalFunction.id} result: ${
|
|
7701
|
-
result:
|
|
7778
|
+
console.log(`[EXULU] eval function ${evalFunction.id} result: ${result3}`, logMetadata2(bullmqJob.name, {
|
|
7779
|
+
result: result3 || 0
|
|
7702
7780
|
}));
|
|
7703
7781
|
}
|
|
7704
7782
|
}
|
|
7705
|
-
const scores = evalFunctionResults.map((
|
|
7783
|
+
const scores = evalFunctionResults.map((result3) => result3.result);
|
|
7706
7784
|
console.log("[EXULU] Exulu eval run scores for test case: " + testCase.id, scores);
|
|
7707
7785
|
let score = 0;
|
|
7708
7786
|
switch (data.scoring_method?.toLowerCase()) {
|
|
@@ -7767,25 +7845,25 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7767
7845
|
messages: inputMessages
|
|
7768
7846
|
} = await validateEvalPayload(data, agents);
|
|
7769
7847
|
const evalFunctions = evalRun.eval_functions;
|
|
7770
|
-
let
|
|
7848
|
+
let result2;
|
|
7771
7849
|
for (const evalFunction of evalFunctions) {
|
|
7772
7850
|
const evalMethod = evals.find((e) => e.id === evalFunction.id);
|
|
7773
7851
|
if (!evalMethod) {
|
|
7774
7852
|
throw new Error(`Eval function ${evalFunction.id} not found in the registry, check your code and make sure the eval function is registered correctly.`);
|
|
7775
7853
|
}
|
|
7776
|
-
|
|
7854
|
+
result2 = await evalMethod.run(
|
|
7777
7855
|
agentInstance,
|
|
7778
7856
|
backend,
|
|
7779
7857
|
testCase,
|
|
7780
7858
|
inputMessages,
|
|
7781
7859
|
evalFunction.config || {}
|
|
7782
7860
|
);
|
|
7783
|
-
console.log(`[EXULU] eval function ${evalFunction.id} result: ${
|
|
7784
|
-
result:
|
|
7861
|
+
console.log(`[EXULU] eval function ${evalFunction.id} result: ${result2}`, logMetadata2(bullmqJob.name, {
|
|
7862
|
+
result: result2 || 0
|
|
7785
7863
|
}));
|
|
7786
7864
|
}
|
|
7787
7865
|
return {
|
|
7788
|
-
result,
|
|
7866
|
+
result: result2,
|
|
7789
7867
|
metadata: {}
|
|
7790
7868
|
};
|
|
7791
7869
|
}
|
|
@@ -7805,10 +7883,10 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7805
7883
|
if (!source) {
|
|
7806
7884
|
throw new Error(`Source ${data.source} not found in the context ${context.id}.`);
|
|
7807
7885
|
}
|
|
7808
|
-
const
|
|
7886
|
+
const result2 = await source.execute(data.inputs);
|
|
7809
7887
|
let jobs = [];
|
|
7810
7888
|
let items = [];
|
|
7811
|
-
for (const item of
|
|
7889
|
+
for (const item of result2) {
|
|
7812
7890
|
const { item: createdItem, job } = await context.createItem(
|
|
7813
7891
|
item,
|
|
7814
7892
|
config,
|
|
@@ -7840,7 +7918,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7840
7918
|
role: data?.role
|
|
7841
7919
|
});
|
|
7842
7920
|
return {
|
|
7843
|
-
result,
|
|
7921
|
+
result: result2,
|
|
7844
7922
|
metadata: {
|
|
7845
7923
|
jobs,
|
|
7846
7924
|
items
|
|
@@ -7853,11 +7931,13 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7853
7931
|
throw error;
|
|
7854
7932
|
}
|
|
7855
7933
|
})();
|
|
7856
|
-
|
|
7934
|
+
const result = await Promise.race([workPromise, timeoutPromise]);
|
|
7935
|
+
return result;
|
|
7857
7936
|
},
|
|
7858
7937
|
{
|
|
7859
7938
|
autorun: true,
|
|
7860
7939
|
connection: redisConnection,
|
|
7940
|
+
concurrency: queue.concurrency?.worker || 1,
|
|
7861
7941
|
removeOnComplete: { count: 1e3 },
|
|
7862
7942
|
removeOnFail: { count: 5e3 },
|
|
7863
7943
|
...queue.ratelimit && {
|
|
@@ -9061,18 +9141,23 @@ var ExuluQueues = class {
|
|
|
9061
9141
|
// method of ExuluQueues we need to store the desired rate limit on the queue
|
|
9062
9142
|
// here so we can use the value when creating workers for the queue instance
|
|
9063
9143
|
// as there is no way to store a rate limit value natively on a bullm queue.
|
|
9064
|
-
register = (name, concurrency
|
|
9144
|
+
register = (name, concurrency, ratelimit = 1) => {
|
|
9145
|
+
const queueConcurrency = concurrency.queue || 1;
|
|
9146
|
+
const workerConcurrency = concurrency.worker || 1;
|
|
9065
9147
|
const use = async () => {
|
|
9066
9148
|
const existing = this.queues.find((x) => x.queue?.name === name);
|
|
9067
9149
|
if (existing) {
|
|
9068
9150
|
const globalConcurrency = await existing.queue.getGlobalConcurrency();
|
|
9069
|
-
if (globalConcurrency !==
|
|
9070
|
-
await existing.queue.setGlobalConcurrency(
|
|
9151
|
+
if (globalConcurrency !== queueConcurrency) {
|
|
9152
|
+
await existing.queue.setGlobalConcurrency(queueConcurrency);
|
|
9071
9153
|
}
|
|
9072
9154
|
return {
|
|
9073
9155
|
queue: existing.queue,
|
|
9074
9156
|
ratelimit,
|
|
9075
|
-
concurrency
|
|
9157
|
+
concurrency: {
|
|
9158
|
+
worker: workerConcurrency,
|
|
9159
|
+
queue: queueConcurrency
|
|
9160
|
+
}
|
|
9076
9161
|
};
|
|
9077
9162
|
}
|
|
9078
9163
|
if (!redisServer.host?.length || !redisServer.port?.length) {
|
|
@@ -9091,21 +9176,30 @@ var ExuluQueues = class {
|
|
|
9091
9176
|
telemetry: new import_bullmq_otel.BullMQOtel("simple-guide")
|
|
9092
9177
|
}
|
|
9093
9178
|
);
|
|
9094
|
-
await newQueue.setGlobalConcurrency(
|
|
9179
|
+
await newQueue.setGlobalConcurrency(queueConcurrency);
|
|
9095
9180
|
this.queues.push({
|
|
9096
9181
|
queue: newQueue,
|
|
9097
9182
|
ratelimit,
|
|
9098
|
-
concurrency
|
|
9183
|
+
concurrency: {
|
|
9184
|
+
worker: workerConcurrency,
|
|
9185
|
+
queue: queueConcurrency
|
|
9186
|
+
}
|
|
9099
9187
|
});
|
|
9100
9188
|
return {
|
|
9101
9189
|
queue: newQueue,
|
|
9102
9190
|
ratelimit,
|
|
9103
|
-
concurrency
|
|
9191
|
+
concurrency: {
|
|
9192
|
+
worker: workerConcurrency,
|
|
9193
|
+
queue: queueConcurrency
|
|
9194
|
+
}
|
|
9104
9195
|
};
|
|
9105
9196
|
};
|
|
9106
9197
|
this.list.set(name, {
|
|
9107
9198
|
name,
|
|
9108
|
-
concurrency
|
|
9199
|
+
concurrency: {
|
|
9200
|
+
worker: workerConcurrency,
|
|
9201
|
+
queue: queueConcurrency
|
|
9202
|
+
},
|
|
9109
9203
|
ratelimit,
|
|
9110
9204
|
use
|
|
9111
9205
|
});
|
|
@@ -9161,7 +9255,10 @@ var llmAsJudgeEval = () => {
|
|
|
9161
9255
|
name: "prompt",
|
|
9162
9256
|
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."
|
|
9163
9257
|
}],
|
|
9164
|
-
queue: queues.register("llm_as_judge",
|
|
9258
|
+
queue: queues.register("llm_as_judge", {
|
|
9259
|
+
worker: 1,
|
|
9260
|
+
queue: 1
|
|
9261
|
+
}, 1).use(),
|
|
9165
9262
|
llm: true
|
|
9166
9263
|
});
|
|
9167
9264
|
}
|
|
@@ -9796,11 +9893,16 @@ var previewPdfTool = new ExuluTool2({
|
|
|
9796
9893
|
type: "function",
|
|
9797
9894
|
config: [],
|
|
9798
9895
|
inputSchema: import_zod5.z.object({
|
|
9799
|
-
s3key: import_zod5.z.string().describe("The S3 key of the PDF file to preview
|
|
9896
|
+
s3key: import_zod5.z.string().describe("The S3 key of the PDF file to preview."),
|
|
9800
9897
|
page: import_zod5.z.number().describe("The page number to preview, defaults to 1.").optional()
|
|
9801
9898
|
}),
|
|
9802
9899
|
execute: async ({ s3key, page, exuluConfig }) => {
|
|
9803
|
-
const
|
|
9900
|
+
const bucket = s3key.split("/")[0];
|
|
9901
|
+
const key = s3key.split("/").slice(1).join("/");
|
|
9902
|
+
if (!bucket || !key) {
|
|
9903
|
+
throw new Error("Invalid S3 key, must be in the format of <bucket>/<key>.");
|
|
9904
|
+
}
|
|
9905
|
+
const url = await getPresignedUrl(bucket, key, exuluConfig);
|
|
9804
9906
|
if (!url) {
|
|
9805
9907
|
throw new Error("No URL provided for PDF preview");
|
|
9806
9908
|
}
|
|
@@ -10194,7 +10296,10 @@ var ExuluApp = class {
|
|
|
10194
10296
|
}
|
|
10195
10297
|
const queueSet = /* @__PURE__ */ new Set();
|
|
10196
10298
|
if (redisServer.host?.length && redisServer.port?.length) {
|
|
10197
|
-
queues.register(global_queues.eval_runs,
|
|
10299
|
+
queues.register(global_queues.eval_runs, {
|
|
10300
|
+
worker: 1,
|
|
10301
|
+
queue: 1
|
|
10302
|
+
}, 1);
|
|
10198
10303
|
for (const queue of queues.list.values()) {
|
|
10199
10304
|
const config2 = await queue.use();
|
|
10200
10305
|
queueSet.add(config2);
|