@exulu/backend 1.36.1 → 1.38.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 +3 -3
- package/dist/index.cjs +347 -129
- package/dist/index.d.cts +15 -4
- package/dist/index.d.ts +15 -4
- package/dist/index.js +347 -129
- package/package.json +4 -2
- package/types/models/context.ts +1 -0
package/dist/index.js
CHANGED
|
@@ -411,7 +411,9 @@ var getToken = async (authHeader) => {
|
|
|
411
411
|
}
|
|
412
412
|
try {
|
|
413
413
|
const secret = process.env.NEXTAUTH_SECRET;
|
|
414
|
-
const
|
|
414
|
+
const secretBuffer = Buffer.from(secret, "utf-8");
|
|
415
|
+
const base64Secret = secretBuffer.toString("base64url");
|
|
416
|
+
const jwk = await importJWK({ k: base64Secret, alg: "HS256", kty: "oct" });
|
|
415
417
|
const { payload } = await jwtVerify(token, jwk);
|
|
416
418
|
return payload;
|
|
417
419
|
} catch (error) {
|
|
@@ -687,25 +689,11 @@ var requestValidators = {
|
|
|
687
689
|
message: "Missing body."
|
|
688
690
|
};
|
|
689
691
|
}
|
|
690
|
-
if (!req.
|
|
692
|
+
if (!req.body.message && !req.body.messages) {
|
|
691
693
|
return {
|
|
692
694
|
error: true,
|
|
693
695
|
code: 400,
|
|
694
|
-
message: 'Missing "
|
|
695
|
-
};
|
|
696
|
-
}
|
|
697
|
-
if (!req.headers["session"]) {
|
|
698
|
-
return {
|
|
699
|
-
error: true,
|
|
700
|
-
code: 400,
|
|
701
|
-
message: 'Missing "session" property in headers.'
|
|
702
|
-
};
|
|
703
|
-
}
|
|
704
|
-
if (!req.body.message) {
|
|
705
|
-
return {
|
|
706
|
-
error: true,
|
|
707
|
-
code: 400,
|
|
708
|
-
message: 'Missing "message" property in body.'
|
|
696
|
+
message: 'Missing "message" or "messages" property in body.'
|
|
709
697
|
};
|
|
710
698
|
}
|
|
711
699
|
return {
|
|
@@ -737,6 +725,12 @@ var agentMessagesSchema = {
|
|
|
737
725
|
name: "user",
|
|
738
726
|
type: "number"
|
|
739
727
|
},
|
|
728
|
+
{
|
|
729
|
+
name: "message_id",
|
|
730
|
+
type: "text",
|
|
731
|
+
index: true,
|
|
732
|
+
unique: true
|
|
733
|
+
},
|
|
740
734
|
{
|
|
741
735
|
name: "session",
|
|
742
736
|
type: "text"
|
|
@@ -2371,9 +2365,11 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2371
2365
|
const name = args.field?.replace("_s3key", "");
|
|
2372
2366
|
console.log("[EXULU] name", name);
|
|
2373
2367
|
console.log("[EXULU] fields", exists.fields.map((field2) => field2.name));
|
|
2374
|
-
const field = exists.fields.find((field2) =>
|
|
2368
|
+
const field = exists.fields.find((field2) => {
|
|
2369
|
+
return field2.name.replace("_s3key", "") === name;
|
|
2370
|
+
});
|
|
2375
2371
|
if (!field) {
|
|
2376
|
-
throw new Error(`Field ${name} not found in context ${exists.id}.`);
|
|
2372
|
+
throw new Error(`Field ${name} not found in context ${exists.id}].`);
|
|
2377
2373
|
}
|
|
2378
2374
|
if (!field.processor) {
|
|
2379
2375
|
throw new Error(`Processor not set for field ${args.field} in context ${exists.id}.`);
|
|
@@ -2448,7 +2444,8 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2448
2444
|
item,
|
|
2449
2445
|
config,
|
|
2450
2446
|
context.user.id,
|
|
2451
|
-
context.user.role?.id
|
|
2447
|
+
context.user.role?.id,
|
|
2448
|
+
item.external_id || item.id ? true : false
|
|
2452
2449
|
);
|
|
2453
2450
|
if (job) {
|
|
2454
2451
|
jobs.push(job);
|
|
@@ -2535,22 +2532,30 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2535
2532
|
if (!id) {
|
|
2536
2533
|
throw new Error(`Context ${table.id} not found.`);
|
|
2537
2534
|
}
|
|
2538
|
-
let query = db3.from(getTableName(id)).select("id");
|
|
2539
2535
|
if (args.where) {
|
|
2536
|
+
let query = db3.from(getTableName(id)).select("id");
|
|
2540
2537
|
query = applyFilters(query, args.where, table);
|
|
2538
|
+
const items = await query;
|
|
2539
|
+
if (items.length === 0) {
|
|
2540
|
+
throw new Error("No items found to delete chunks for.");
|
|
2541
|
+
}
|
|
2542
|
+
for (const item of items) {
|
|
2543
|
+
await db3.from(getChunksTableName(id)).where({ source: item.id }).delete();
|
|
2544
|
+
}
|
|
2545
|
+
return {
|
|
2546
|
+
message: "Chunks deleted successfully.",
|
|
2547
|
+
items: items.length,
|
|
2548
|
+
jobs: []
|
|
2549
|
+
};
|
|
2550
|
+
} else {
|
|
2551
|
+
const count = await db3.from(getChunksTableName(id)).count();
|
|
2552
|
+
await db3.from(getChunksTableName(id)).truncate();
|
|
2553
|
+
return {
|
|
2554
|
+
message: "Chunks deleted successfully.",
|
|
2555
|
+
items: parseInt(count[0].count),
|
|
2556
|
+
jobs: []
|
|
2557
|
+
};
|
|
2541
2558
|
}
|
|
2542
|
-
const items = await query;
|
|
2543
|
-
if (items.length === 0) {
|
|
2544
|
-
throw new Error("No items found to delete chunks for.");
|
|
2545
|
-
}
|
|
2546
|
-
for (const item of items) {
|
|
2547
|
-
await db3.from(getChunksTableName(id)).where({ source: item.id }).delete();
|
|
2548
|
-
}
|
|
2549
|
-
return {
|
|
2550
|
-
message: "Chunks deleted successfully.",
|
|
2551
|
-
items: items.length,
|
|
2552
|
-
jobs: []
|
|
2553
|
-
};
|
|
2554
2559
|
};
|
|
2555
2560
|
}
|
|
2556
2561
|
return mutations;
|
|
@@ -2825,6 +2830,13 @@ var postprocessDeletion = async ({
|
|
|
2825
2830
|
}
|
|
2826
2831
|
return result;
|
|
2827
2832
|
}
|
|
2833
|
+
if (table.type === "agent_sessions") {
|
|
2834
|
+
if (!result.id) {
|
|
2835
|
+
return result;
|
|
2836
|
+
}
|
|
2837
|
+
const { db: db3 } = await postgresClient();
|
|
2838
|
+
await db3.from("agent_messages").where({ session: result.id }).where({ session: result.id }).delete();
|
|
2839
|
+
}
|
|
2828
2840
|
}
|
|
2829
2841
|
return result;
|
|
2830
2842
|
};
|
|
@@ -3148,7 +3160,7 @@ var vectorSearch = async ({
|
|
|
3148
3160
|
items = await itemsQuery;
|
|
3149
3161
|
break;
|
|
3150
3162
|
case "hybridSearch":
|
|
3151
|
-
const matchCount = Math.min(limit * 5,
|
|
3163
|
+
const matchCount = Math.min(limit * 5, 100);
|
|
3152
3164
|
const fullTextWeight = 1;
|
|
3153
3165
|
const semanticWeight = 1;
|
|
3154
3166
|
const rrfK = 50;
|
|
@@ -3175,7 +3187,7 @@ var vectorSearch = async ({
|
|
|
3175
3187
|
FROM ${chunksTable} c
|
|
3176
3188
|
WHERE c.embedding IS NOT NULL
|
|
3177
3189
|
ORDER BY rank_ix
|
|
3178
|
-
LIMIT LEAST(?,
|
|
3190
|
+
LIMIT LEAST(?, 50) * 2
|
|
3179
3191
|
)
|
|
3180
3192
|
SELECT
|
|
3181
3193
|
m.*,
|
|
@@ -3183,6 +3195,7 @@ var vectorSearch = async ({
|
|
|
3183
3195
|
c.source,
|
|
3184
3196
|
c.content,
|
|
3185
3197
|
c.chunk_index,
|
|
3198
|
+
c.metadata,
|
|
3186
3199
|
c."createdAt" AS chunk_created_at,
|
|
3187
3200
|
c."updatedAt" AS chunk_updated_at,
|
|
3188
3201
|
vector_dims(c.embedding) as embedding_size,
|
|
@@ -3206,7 +3219,7 @@ var vectorSearch = async ({
|
|
|
3206
3219
|
JOIN ${mainTable} m
|
|
3207
3220
|
ON m.id = c.source
|
|
3208
3221
|
ORDER BY hybrid_score DESC
|
|
3209
|
-
LIMIT LEAST(?,
|
|
3222
|
+
LIMIT LEAST(?, 50)
|
|
3210
3223
|
OFFSET 0
|
|
3211
3224
|
`;
|
|
3212
3225
|
const bindings = [
|
|
@@ -3232,6 +3245,7 @@ var vectorSearch = async ({
|
|
|
3232
3245
|
];
|
|
3233
3246
|
items = await db3.raw(hybridSQL, bindings).then((r) => r.rows ?? r);
|
|
3234
3247
|
}
|
|
3248
|
+
console.log("[EXULU] Vector search results:", items?.length);
|
|
3235
3249
|
const seenSources = /* @__PURE__ */ new Map();
|
|
3236
3250
|
items = items.reduce((acc, item) => {
|
|
3237
3251
|
if (!seenSources.has(item.source)) {
|
|
@@ -3249,6 +3263,7 @@ var vectorSearch = async ({
|
|
|
3249
3263
|
chunk_index: item.chunk_index,
|
|
3250
3264
|
chunk_id: item.chunk_id,
|
|
3251
3265
|
source: item.source,
|
|
3266
|
+
metadata: item.metadata,
|
|
3252
3267
|
chunk_created_at: item.chunk_created_at,
|
|
3253
3268
|
chunk_updated_at: item.chunk_updated_at,
|
|
3254
3269
|
embedding_size: item.embedding_size,
|
|
@@ -3265,6 +3280,7 @@ var vectorSearch = async ({
|
|
|
3265
3280
|
chunk_id: item.chunk_id,
|
|
3266
3281
|
chunk_created_at: item.chunk_created_at,
|
|
3267
3282
|
embedding_size: item.embedding_size,
|
|
3283
|
+
metadata: item.metadata,
|
|
3268
3284
|
source: item.source,
|
|
3269
3285
|
chunk_updated_at: item.chunk_updated_at,
|
|
3270
3286
|
...method === "cosineDistance" && { cosine_distance: item.cosine_distance },
|
|
@@ -3274,6 +3290,7 @@ var vectorSearch = async ({
|
|
|
3274
3290
|
}
|
|
3275
3291
|
return acc;
|
|
3276
3292
|
}, []);
|
|
3293
|
+
console.log("[EXULU] Vector search results after deduplication:", items?.length);
|
|
3277
3294
|
items.forEach((item) => {
|
|
3278
3295
|
if (!item.chunks?.length) {
|
|
3279
3296
|
return;
|
|
@@ -3305,7 +3322,7 @@ var vectorSearch = async ({
|
|
|
3305
3322
|
if (resultReranker && query) {
|
|
3306
3323
|
items = await resultReranker(items);
|
|
3307
3324
|
}
|
|
3308
|
-
|
|
3325
|
+
console.log("[EXULU] Vector search results after slicing:", items?.length);
|
|
3309
3326
|
await updateStatistic({
|
|
3310
3327
|
name: "count",
|
|
3311
3328
|
label: table.name.singular,
|
|
@@ -3967,6 +3984,10 @@ type PageInfo {
|
|
|
3967
3984
|
}
|
|
3968
3985
|
};
|
|
3969
3986
|
}));
|
|
3987
|
+
let embedderQueue = void 0;
|
|
3988
|
+
if (data.embedder?.queue) {
|
|
3989
|
+
embedderQueue = await data.embedder.queue;
|
|
3990
|
+
}
|
|
3970
3991
|
const clean = {
|
|
3971
3992
|
id: data.id,
|
|
3972
3993
|
name: data.name,
|
|
@@ -3974,7 +3995,8 @@ type PageInfo {
|
|
|
3974
3995
|
embedder: data.embedder ? {
|
|
3975
3996
|
name: data.embedder.name,
|
|
3976
3997
|
id: data.embedder.id,
|
|
3977
|
-
config: data.embedder?.config || void 0
|
|
3998
|
+
config: data.embedder?.config || void 0,
|
|
3999
|
+
queue: embedderQueue?.queue.name || void 0
|
|
3978
4000
|
} : void 0,
|
|
3979
4001
|
slug: "/contexts/" + data.id,
|
|
3980
4002
|
active: data.active,
|
|
@@ -4161,6 +4183,7 @@ type ItemChunks {
|
|
|
4161
4183
|
chunk_created_at: Date
|
|
4162
4184
|
chunk_updated_at: Date
|
|
4163
4185
|
embedding_size: Float
|
|
4186
|
+
metadata: JSON
|
|
4164
4187
|
}
|
|
4165
4188
|
|
|
4166
4189
|
type Provider {
|
|
@@ -4201,6 +4224,7 @@ type Embedder {
|
|
|
4201
4224
|
name: String!
|
|
4202
4225
|
id: ID!
|
|
4203
4226
|
config: [EmbedderConfig!]
|
|
4227
|
+
queue: String
|
|
4204
4228
|
}
|
|
4205
4229
|
type EmbedderConfig {
|
|
4206
4230
|
name: String!
|
|
@@ -4392,10 +4416,21 @@ var getPresignedUrl = async (key, config) => {
|
|
|
4392
4416
|
if (!config.fileUploads) {
|
|
4393
4417
|
throw new Error("File uploads are not configured");
|
|
4394
4418
|
}
|
|
4419
|
+
let bucket = config.fileUploads.s3Bucket;
|
|
4420
|
+
if (key.includes("[bucket:")) {
|
|
4421
|
+
console.log("[EXULU] key includes [bucket:name]", key);
|
|
4422
|
+
bucket = key.split("[bucket:")[1]?.split("]")[0] || "";
|
|
4423
|
+
if (!bucket?.length) {
|
|
4424
|
+
throw new Error("Invalid key, does not contain a bucket name like '[bucket:name]'.");
|
|
4425
|
+
}
|
|
4426
|
+
key = key.split("]")[1] || "";
|
|
4427
|
+
console.log("[EXULU] bucket", bucket);
|
|
4428
|
+
console.log("[EXULU] key", key);
|
|
4429
|
+
}
|
|
4395
4430
|
const url = await getSignedUrl(
|
|
4396
4431
|
getS3Client(config),
|
|
4397
4432
|
new GetObjectCommand({
|
|
4398
|
-
Bucket:
|
|
4433
|
+
Bucket: bucket,
|
|
4399
4434
|
Key: key
|
|
4400
4435
|
}),
|
|
4401
4436
|
{ expiresIn }
|
|
@@ -4432,7 +4467,7 @@ var uploadFile = async (user, file, key, config, options = {}) => {
|
|
|
4432
4467
|
ContentLength: file.byteLength
|
|
4433
4468
|
});
|
|
4434
4469
|
await client2.send(command);
|
|
4435
|
-
return
|
|
4470
|
+
return fullKey;
|
|
4436
4471
|
};
|
|
4437
4472
|
var createUppyRoutes = async (app, config) => {
|
|
4438
4473
|
if (!config.fileUploads) {
|
|
@@ -4503,29 +4538,24 @@ var createUppyRoutes = async (app, config) => {
|
|
|
4503
4538
|
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
4504
4539
|
return;
|
|
4505
4540
|
}
|
|
4506
|
-
|
|
4541
|
+
let { key } = req.query;
|
|
4507
4542
|
if (typeof key !== "string" || key.trim() === "") {
|
|
4508
4543
|
res.status(400).json({ error: "Missing or invalid `key` query parameter." });
|
|
4509
4544
|
return;
|
|
4510
4545
|
}
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
return;
|
|
4521
|
-
}
|
|
4522
|
-
if (authenticationResult.user.type !== "api" && !key.includes(authenticationResult.user.id.toString())) {
|
|
4523
|
-
res.status(405).json({ error: "Not allowed to access the files in the folder based on authenticated user." });
|
|
4524
|
-
return;
|
|
4546
|
+
let bucket = config.fileUploads.s3Bucket;
|
|
4547
|
+
if (key.includes("[bucket:")) {
|
|
4548
|
+
console.log("[EXULU] key includes [bucket:name]", key);
|
|
4549
|
+
bucket = key.split("[bucket:")[1]?.split("]")[0] || "";
|
|
4550
|
+
if (!bucket?.length) {
|
|
4551
|
+
throw new Error("Invalid key, does not contain a bucket name like '[bucket:name]'.");
|
|
4552
|
+
}
|
|
4553
|
+
key = key.split("]")[1] || "";
|
|
4554
|
+
console.log("[EXULU] bucket", bucket);
|
|
4525
4555
|
}
|
|
4526
4556
|
const client2 = getS3Client(config);
|
|
4527
4557
|
const command = new DeleteObjectCommand({
|
|
4528
|
-
Bucket:
|
|
4558
|
+
Bucket: bucket,
|
|
4529
4559
|
Key: key
|
|
4530
4560
|
});
|
|
4531
4561
|
await client2.send(command);
|
|
@@ -4554,19 +4584,6 @@ var createUppyRoutes = async (app, config) => {
|
|
|
4554
4584
|
res.status(400).json({ error: "Missing or invalid `key` query parameter." });
|
|
4555
4585
|
return;
|
|
4556
4586
|
}
|
|
4557
|
-
const userPrefix = extractUserPrefix(key);
|
|
4558
|
-
if (!userPrefix) {
|
|
4559
|
-
res.status(405).json({ error: 'Invalid key, does not contain a user prefix like "<user_id>/<key>.' });
|
|
4560
|
-
return;
|
|
4561
|
-
}
|
|
4562
|
-
if (userPrefix !== authenticationResult.user.id.toString()) {
|
|
4563
|
-
res.status(405).json({ error: "Not allowed to access the files in the folder based on authenticated user." });
|
|
4564
|
-
return;
|
|
4565
|
-
}
|
|
4566
|
-
if (authenticationResult.user.type !== "api" && !key.includes(authenticationResult.user.id.toString())) {
|
|
4567
|
-
res.status(405).json({ error: "Not allowed to access the files in the folder based on authenticated user." });
|
|
4568
|
-
return;
|
|
4569
|
-
}
|
|
4570
4587
|
try {
|
|
4571
4588
|
const url = await getPresignedUrl(key, config);
|
|
4572
4589
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
@@ -4596,11 +4613,24 @@ var createUppyRoutes = async (app, config) => {
|
|
|
4596
4613
|
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
4597
4614
|
return;
|
|
4598
4615
|
}
|
|
4599
|
-
|
|
4616
|
+
let { key } = req.body;
|
|
4617
|
+
let bucket = config.fileUploads.s3Bucket;
|
|
4618
|
+
if (key.includes("[bucket:")) {
|
|
4619
|
+
console.log("[EXULU] key includes [bucket:name]", key);
|
|
4620
|
+
bucket = key.split("[bucket:")[1]?.split("]")[0] || "";
|
|
4621
|
+
console.log("[EXULU] bucket", bucket);
|
|
4622
|
+
if (!bucket?.length) {
|
|
4623
|
+
throw new Error("Invalid key, does not contain a bucket name like '[bucket:name]'.");
|
|
4624
|
+
}
|
|
4625
|
+
key = key.split("]")[1] || "";
|
|
4626
|
+
console.log("[EXULU] key", key);
|
|
4627
|
+
}
|
|
4600
4628
|
console.log("[EXULU] Getting object metadata from s3", key);
|
|
4629
|
+
console.log("[EXULU] bucket", bucket);
|
|
4630
|
+
console.log("[EXULU] key", key);
|
|
4601
4631
|
const client2 = getS3Client(config);
|
|
4602
4632
|
const command = new HeadObjectCommand({
|
|
4603
|
-
Bucket:
|
|
4633
|
+
Bucket: bucket,
|
|
4604
4634
|
Key: key
|
|
4605
4635
|
});
|
|
4606
4636
|
const response = await client2.send(command);
|
|
@@ -4908,6 +4938,7 @@ var createUppyRoutes = async (app, config) => {
|
|
|
4908
4938
|
};
|
|
4909
4939
|
|
|
4910
4940
|
// src/registry/classes.ts
|
|
4941
|
+
import { parseOfficeAsync } from "officeparser";
|
|
4911
4942
|
var s3Client2;
|
|
4912
4943
|
function sanitizeToolName(name) {
|
|
4913
4944
|
if (typeof name !== "string") return "";
|
|
@@ -4918,7 +4949,7 @@ function sanitizeToolName(name) {
|
|
|
4918
4949
|
}
|
|
4919
4950
|
return sanitized;
|
|
4920
4951
|
}
|
|
4921
|
-
var convertToolsArrayToObject = (currentTools, allExuluTools, configs, providerapikey, contexts, user, exuluConfig, sessionID) => {
|
|
4952
|
+
var convertToolsArrayToObject = (currentTools, allExuluTools, configs, providerapikey, contexts, user, exuluConfig, sessionID, req) => {
|
|
4922
4953
|
if (!currentTools) return {};
|
|
4923
4954
|
if (!allExuluTools) return {};
|
|
4924
4955
|
if (!contexts) {
|
|
@@ -5005,6 +5036,7 @@ var convertToolsArrayToObject = (currentTools, allExuluTools, configs, providera
|
|
|
5005
5036
|
const response = await cur.tool.execute({
|
|
5006
5037
|
...inputs,
|
|
5007
5038
|
sessionID,
|
|
5039
|
+
req,
|
|
5008
5040
|
// Convert config to object format if a config object
|
|
5009
5041
|
// is available, after we added the .value property
|
|
5010
5042
|
// by hydrating it from the variables table.
|
|
@@ -5014,6 +5046,7 @@ var convertToolsArrayToObject = (currentTools, allExuluTools, configs, providera
|
|
|
5014
5046
|
user,
|
|
5015
5047
|
contexts: contextsMap,
|
|
5016
5048
|
upload,
|
|
5049
|
+
exuluConfig,
|
|
5017
5050
|
config: config ? config.config.reduce((acc, curr) => {
|
|
5018
5051
|
acc[curr.name] = curr.value;
|
|
5019
5052
|
return acc;
|
|
@@ -5235,6 +5268,7 @@ var ExuluAgent2 = class {
|
|
|
5235
5268
|
};
|
|
5236
5269
|
generateSync = async ({
|
|
5237
5270
|
prompt,
|
|
5271
|
+
req,
|
|
5238
5272
|
user,
|
|
5239
5273
|
session,
|
|
5240
5274
|
inputMessages,
|
|
@@ -5316,7 +5350,8 @@ var ExuluAgent2 = class {
|
|
|
5316
5350
|
contexts,
|
|
5317
5351
|
user,
|
|
5318
5352
|
exuluConfig,
|
|
5319
|
-
session
|
|
5353
|
+
session,
|
|
5354
|
+
req
|
|
5320
5355
|
),
|
|
5321
5356
|
stopWhen: [stepCountIs(2)]
|
|
5322
5357
|
});
|
|
@@ -5375,7 +5410,8 @@ var ExuluAgent2 = class {
|
|
|
5375
5410
|
contexts,
|
|
5376
5411
|
user,
|
|
5377
5412
|
exuluConfig,
|
|
5378
|
-
session
|
|
5413
|
+
session,
|
|
5414
|
+
req
|
|
5379
5415
|
),
|
|
5380
5416
|
stopWhen: [stepCountIs(2)]
|
|
5381
5417
|
});
|
|
@@ -5416,6 +5452,71 @@ var ExuluAgent2 = class {
|
|
|
5416
5452
|
}
|
|
5417
5453
|
return "";
|
|
5418
5454
|
};
|
|
5455
|
+
/**
|
|
5456
|
+
* Convert file parts in messages to OpenAI Responses API compatible format.
|
|
5457
|
+
* The OpenAI Responses API doesn't support inline file parts with type 'file'.
|
|
5458
|
+
* This function converts:
|
|
5459
|
+
* - Document files (PDF, DOCX, etc.) -> text parts with extracted content using officeparser
|
|
5460
|
+
* - Image files -> image parts (which ARE supported by Responses API)
|
|
5461
|
+
*/
|
|
5462
|
+
async processFilePartsInMessages(messages) {
|
|
5463
|
+
const processedMessages = await Promise.all(messages.map(async (message) => {
|
|
5464
|
+
console.log("[EXULU] Processing file parts in messages: " + JSON.stringify(message, null, 2));
|
|
5465
|
+
if (message.role !== "user" || !Array.isArray(message.parts)) {
|
|
5466
|
+
return message;
|
|
5467
|
+
}
|
|
5468
|
+
const processedParts = await Promise.all(message.parts.map(async (part) => {
|
|
5469
|
+
if (part.type !== "file") {
|
|
5470
|
+
return part;
|
|
5471
|
+
}
|
|
5472
|
+
const { mediaType, url, filename } = part;
|
|
5473
|
+
const imageTypes = ["image/png", "image/jpeg", "image/jpg", "image/gif", "image/webp"];
|
|
5474
|
+
if (imageTypes.includes(mediaType)) {
|
|
5475
|
+
console.log(`[EXULU] Converting file part to image part: ${filename}`);
|
|
5476
|
+
return {
|
|
5477
|
+
type: "image",
|
|
5478
|
+
image: url,
|
|
5479
|
+
mimeType: mediaType
|
|
5480
|
+
};
|
|
5481
|
+
}
|
|
5482
|
+
console.log(`[EXULU] Converting file part to text using officeparser: ${filename}`);
|
|
5483
|
+
try {
|
|
5484
|
+
const response = await fetch(url);
|
|
5485
|
+
if (!response.ok) {
|
|
5486
|
+
console.error(`[EXULU] Failed to fetch file: ${filename}, status: ${response.status}`);
|
|
5487
|
+
return {
|
|
5488
|
+
type: "text",
|
|
5489
|
+
text: `[Error: Could not load file ${filename}]`
|
|
5490
|
+
};
|
|
5491
|
+
}
|
|
5492
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
5493
|
+
const extractedText = await parseOfficeAsync(arrayBuffer, {
|
|
5494
|
+
outputErrorToConsole: false,
|
|
5495
|
+
newlineDelimiter: "\n"
|
|
5496
|
+
});
|
|
5497
|
+
return {
|
|
5498
|
+
type: "text",
|
|
5499
|
+
text: `<file name="${filename}">
|
|
5500
|
+
${extractedText}
|
|
5501
|
+
</file>`
|
|
5502
|
+
};
|
|
5503
|
+
} catch (error) {
|
|
5504
|
+
console.error(`[EXULU] Error processing file ${filename}:`, error);
|
|
5505
|
+
return {
|
|
5506
|
+
type: "text",
|
|
5507
|
+
text: `[Error extracting text from file ${filename}: ${error instanceof Error ? error.message : "Unknown error"}]`
|
|
5508
|
+
};
|
|
5509
|
+
}
|
|
5510
|
+
}));
|
|
5511
|
+
const result = {
|
|
5512
|
+
...message,
|
|
5513
|
+
parts: processedParts
|
|
5514
|
+
};
|
|
5515
|
+
console.log("[EXULU] Result: " + JSON.stringify(result, null, 2));
|
|
5516
|
+
return result;
|
|
5517
|
+
}));
|
|
5518
|
+
return processedMessages;
|
|
5519
|
+
}
|
|
5419
5520
|
generateStream = async ({
|
|
5420
5521
|
user,
|
|
5421
5522
|
session,
|
|
@@ -5427,7 +5528,8 @@ var ExuluAgent2 = class {
|
|
|
5427
5528
|
providerapikey,
|
|
5428
5529
|
contexts,
|
|
5429
5530
|
exuluConfig,
|
|
5430
|
-
instructions
|
|
5531
|
+
instructions,
|
|
5532
|
+
req
|
|
5431
5533
|
}) => {
|
|
5432
5534
|
if (!this.model) {
|
|
5433
5535
|
console.error("[EXULU] Model is required for streaming.");
|
|
@@ -5450,7 +5552,7 @@ var ExuluAgent2 = class {
|
|
|
5450
5552
|
console.log("[EXULU] loading previous messages from session: " + session);
|
|
5451
5553
|
const previousMessages2 = await getAgentMessages({
|
|
5452
5554
|
session,
|
|
5453
|
-
user: user
|
|
5555
|
+
user: user?.id,
|
|
5454
5556
|
limit: 50,
|
|
5455
5557
|
page: 1
|
|
5456
5558
|
});
|
|
@@ -5462,6 +5564,11 @@ var ExuluAgent2 = class {
|
|
|
5462
5564
|
// append the new message to the previous messages:
|
|
5463
5565
|
messages: [...previousMessagesContent, message]
|
|
5464
5566
|
});
|
|
5567
|
+
messages = messages.filter(
|
|
5568
|
+
(message2, index, self) => index === self.findIndex((t) => t.id === message2.id)
|
|
5569
|
+
);
|
|
5570
|
+
console.log("[EXULU] Processing file parts in messages for OpenAI Responses API compatibility");
|
|
5571
|
+
messages = await this.processFilePartsInMessages(messages);
|
|
5465
5572
|
const genericContext = "IMPORTANT: \n\n The current date is " + (/* @__PURE__ */ new Date()).toLocaleDateString() + " and the current time is " + (/* @__PURE__ */ new Date()).toLocaleTimeString() + ". If the user does not explicitly provide the current date, for examle when saying ' this weekend', you should assume they are talking with the current date in mind as a reference.";
|
|
5466
5573
|
let system = instructions || "You are a helpful assistant. When you use a tool to answer a question do not explicitly comment on the result of the tool call unless the user has explicitly you to do something with the result.";
|
|
5467
5574
|
system += "\n\n" + genericContext;
|
|
@@ -5489,7 +5596,8 @@ var ExuluAgent2 = class {
|
|
|
5489
5596
|
contexts,
|
|
5490
5597
|
user,
|
|
5491
5598
|
exuluConfig,
|
|
5492
|
-
session
|
|
5599
|
+
session,
|
|
5600
|
+
req
|
|
5493
5601
|
),
|
|
5494
5602
|
onError: (error) => {
|
|
5495
5603
|
console.error("[EXULU] chat stream error.", error);
|
|
@@ -5507,7 +5615,7 @@ var ExuluAgent2 = class {
|
|
|
5507
5615
|
var getAgentMessages = async ({ session, user, limit, page }) => {
|
|
5508
5616
|
const { db: db3 } = await postgresClient();
|
|
5509
5617
|
console.log("[EXULU] getting agent messages for session: " + session + " and user: " + user + " and page: " + page);
|
|
5510
|
-
const query = db3.from("agent_messages").where({ session, user }).limit(limit);
|
|
5618
|
+
const query = db3.from("agent_messages").where({ session, user: user || null }).limit(limit);
|
|
5511
5619
|
if (page > 0) {
|
|
5512
5620
|
query.offset((page - 1) * limit);
|
|
5513
5621
|
}
|
|
@@ -5525,15 +5633,17 @@ var getSession = async ({ sessionID }) => {
|
|
|
5525
5633
|
};
|
|
5526
5634
|
var saveChat = async ({ session, user, messages }) => {
|
|
5527
5635
|
const { db: db3 } = await postgresClient();
|
|
5528
|
-
const
|
|
5529
|
-
|
|
5636
|
+
for (const message of messages) {
|
|
5637
|
+
const mutation = db3.from("agent_messages").insert({
|
|
5530
5638
|
session,
|
|
5531
5639
|
user,
|
|
5532
5640
|
content: JSON.stringify(message),
|
|
5641
|
+
message_id: message.id,
|
|
5533
5642
|
title: message.role === "user" ? "User" : "Assistant"
|
|
5534
|
-
});
|
|
5535
|
-
|
|
5536
|
-
|
|
5643
|
+
}).returning("id");
|
|
5644
|
+
mutation.onConflict("message_id").merge();
|
|
5645
|
+
await mutation;
|
|
5646
|
+
}
|
|
5537
5647
|
};
|
|
5538
5648
|
var ExuluEmbedder = class {
|
|
5539
5649
|
id;
|
|
@@ -5574,19 +5684,36 @@ var ExuluEmbedder = class {
|
|
|
5574
5684
|
});
|
|
5575
5685
|
for (const config of this.config || []) {
|
|
5576
5686
|
const name = config.name;
|
|
5687
|
+
const setting = variables.find((v) => v.name === name);
|
|
5688
|
+
if (!setting) {
|
|
5689
|
+
throw new Error("Setting value not found for embedder setting: " + name + ", for context: " + context + " and embedder: " + this.id + ". Make sure to set the value for this setting in the embedder settings.");
|
|
5690
|
+
}
|
|
5577
5691
|
const {
|
|
5578
5692
|
value: variableName,
|
|
5579
5693
|
id
|
|
5580
|
-
} =
|
|
5694
|
+
} = setting;
|
|
5581
5695
|
let value = "";
|
|
5582
5696
|
console.log("[EXULU] variable name", variableName);
|
|
5583
5697
|
const variable = await db3.from("variables").where({ name: variableName }).first();
|
|
5584
5698
|
if (!variable) {
|
|
5585
5699
|
throw new Error("Variable not found for embedder setting: " + name + " in context: " + context + " and embedder: " + this.id);
|
|
5586
5700
|
}
|
|
5701
|
+
console.log("[EXULU] variable", variable);
|
|
5587
5702
|
if (variable.encrypted) {
|
|
5588
|
-
|
|
5589
|
-
|
|
5703
|
+
if (!process.env.NEXTAUTH_SECRET) {
|
|
5704
|
+
throw new Error("NEXTAUTH_SECRET environment variable is not set, cannot decrypt variable: " + name);
|
|
5705
|
+
}
|
|
5706
|
+
try {
|
|
5707
|
+
const bytes = CryptoJS2.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
|
|
5708
|
+
const decrypted = bytes.toString(CryptoJS2.enc.Utf8);
|
|
5709
|
+
if (!decrypted) {
|
|
5710
|
+
throw new Error("Decryption returned empty string - invalid key or corrupted data");
|
|
5711
|
+
}
|
|
5712
|
+
value = decrypted;
|
|
5713
|
+
console.log("[EXULU] successfully decrypted value for", name);
|
|
5714
|
+
} catch (error) {
|
|
5715
|
+
throw new Error(`Failed to decrypt variable "${name}" for embedder setting in context "${context}": ${error instanceof Error ? error.message : "Unknown error"}. Verify that NEXTAUTH_SECRET matches the key used during encryption.`);
|
|
5716
|
+
}
|
|
5590
5717
|
} else {
|
|
5591
5718
|
value = variable.value;
|
|
5592
5719
|
}
|
|
@@ -5745,7 +5872,9 @@ var ExuluContext = class {
|
|
|
5745
5872
|
processField = async (trigger, user, role, item, config) => {
|
|
5746
5873
|
console.log("[EXULU] processing field", item.field, " in context", this.id);
|
|
5747
5874
|
console.log("[EXULU] fields", this.fields.map((field2) => field2.name));
|
|
5748
|
-
const field = this.fields.find((field2) =>
|
|
5875
|
+
const field = this.fields.find((field2) => {
|
|
5876
|
+
return field2.name.replace("_s3key", "") === item.field.replace("_s3key", "");
|
|
5877
|
+
});
|
|
5749
5878
|
if (!field || !field.processor) {
|
|
5750
5879
|
console.error("[EXULU] field not found or processor not set for field", item.field, " in context", this.id);
|
|
5751
5880
|
throw new Error("Field not found or processor not set for field " + item.field + " in context " + this.id);
|
|
@@ -5755,7 +5884,7 @@ var ExuluContext = class {
|
|
|
5755
5884
|
if (queue?.queue.name) {
|
|
5756
5885
|
console.log("[EXULU] processor is in queue mode, scheduling job.");
|
|
5757
5886
|
const job = await bullmqDecorator({
|
|
5758
|
-
timeoutInSeconds: field.processor?.config?.timeoutInSeconds ||
|
|
5887
|
+
timeoutInSeconds: field.processor?.config?.timeoutInSeconds || 600,
|
|
5759
5888
|
label: `${this.name} ${field.name} data processor`,
|
|
5760
5889
|
processor: `${this.id}-${field.name}`,
|
|
5761
5890
|
context: this.id,
|
|
@@ -5846,6 +5975,7 @@ var ExuluContext = class {
|
|
|
5846
5975
|
if (chunks?.length) {
|
|
5847
5976
|
await db3.from(getChunksTableName(this.id)).insert(chunks.map((chunk) => ({
|
|
5848
5977
|
source,
|
|
5978
|
+
metadata: chunk.metadata,
|
|
5849
5979
|
content: chunk.content,
|
|
5850
5980
|
chunk_index: chunk.index,
|
|
5851
5981
|
embedding: pgvector2.toSql(chunk.vector)
|
|
@@ -5886,9 +6016,15 @@ var ExuluContext = class {
|
|
|
5886
6016
|
if (!results[0]) {
|
|
5887
6017
|
throw new Error("Failed to create item.");
|
|
5888
6018
|
}
|
|
6019
|
+
console.log("[EXULU] context configuration", this.configuration);
|
|
5889
6020
|
if (this.embedder && (this.configuration.calculateVectors === "onUpdate" || this.configuration.calculateVectors === "always")) {
|
|
6021
|
+
console.log("[EXULU] generating embeddings for item", results[0].id);
|
|
5890
6022
|
const { job } = await this.embeddings.generate.one({
|
|
5891
|
-
item:
|
|
6023
|
+
item: {
|
|
6024
|
+
...item,
|
|
6025
|
+
// important we need to full record here with all fields for the embedder
|
|
6026
|
+
id: results[0].id
|
|
6027
|
+
},
|
|
5892
6028
|
user,
|
|
5893
6029
|
role,
|
|
5894
6030
|
trigger: "api",
|
|
@@ -5931,7 +6067,7 @@ var ExuluContext = class {
|
|
|
5931
6067
|
if (this.embedder && (this.configuration.calculateVectors === "onUpdate" || this.configuration.calculateVectors === "always")) {
|
|
5932
6068
|
const { job } = await this.embeddings.generate.one({
|
|
5933
6069
|
item: record,
|
|
5934
|
-
// important we need to full record here with all fields
|
|
6070
|
+
// important we need to full record here with all fields for the embedder
|
|
5935
6071
|
user,
|
|
5936
6072
|
role,
|
|
5937
6073
|
trigger: "api",
|
|
@@ -5948,10 +6084,13 @@ var ExuluContext = class {
|
|
|
5948
6084
|
};
|
|
5949
6085
|
};
|
|
5950
6086
|
deleteItem = async (item, user, role) => {
|
|
5951
|
-
if (!item.id) {
|
|
5952
|
-
throw new Error("Item id is required for deleting item.");
|
|
5953
|
-
}
|
|
5954
6087
|
const { db: db3 } = await postgresClient();
|
|
6088
|
+
if (!item.id?.length && item?.external_id) {
|
|
6089
|
+
item = await db3.from(getTableName(this.id)).where({ external_id: item.external_id }).first();
|
|
6090
|
+
if (!item || !item.id) {
|
|
6091
|
+
throw new Error(`Item not found for external id ${item?.external_id || "undefined"}.`);
|
|
6092
|
+
}
|
|
6093
|
+
}
|
|
5955
6094
|
await db3.from(getTableName(this.id)).where({ id: item.id }).delete();
|
|
5956
6095
|
if (!this.embedder) {
|
|
5957
6096
|
return {
|
|
@@ -6125,7 +6264,7 @@ var ExuluContext = class {
|
|
|
6125
6264
|
const { db: db3 } = await postgresClient();
|
|
6126
6265
|
const result = await vectorSearch({
|
|
6127
6266
|
page: 1,
|
|
6128
|
-
limit:
|
|
6267
|
+
limit: 50,
|
|
6129
6268
|
query,
|
|
6130
6269
|
filters: [],
|
|
6131
6270
|
user,
|
|
@@ -6153,7 +6292,6 @@ var ExuluContext = class {
|
|
|
6153
6292
|
};
|
|
6154
6293
|
};
|
|
6155
6294
|
var updateStatistic = async (statistic) => {
|
|
6156
|
-
console.log("[EXULU] updating statistic", statistic);
|
|
6157
6295
|
const currentDate = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
6158
6296
|
const { db: db3 } = await postgresClient();
|
|
6159
6297
|
const existing = await db3.from("tracking").where({
|
|
@@ -6278,6 +6416,7 @@ var CLAUDE_MESSAGES = {
|
|
|
6278
6416
|
|
|
6279
6417
|
// src/registry/routes.ts
|
|
6280
6418
|
import { createIdGenerator } from "ai";
|
|
6419
|
+
import cookieParser from "cookie-parser";
|
|
6281
6420
|
var REQUEST_SIZE_LIMIT = "50mb";
|
|
6282
6421
|
var global_queues = {
|
|
6283
6422
|
eval_runs: "eval_runs"
|
|
@@ -6314,6 +6453,7 @@ var createExpressRoutes = async (app, agents, tools, contexts, config, evals, tr
|
|
|
6314
6453
|
app.use(cors(corsOptions));
|
|
6315
6454
|
app.use(bodyParser.urlencoded({ extended: true, limit: REQUEST_SIZE_LIMIT }));
|
|
6316
6455
|
app.use(bodyParser.json({ limit: REQUEST_SIZE_LIMIT }));
|
|
6456
|
+
app.use(cookieParser());
|
|
6317
6457
|
console.log(`
|
|
6318
6458
|
\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
|
|
6319
6459
|
\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
|
|
@@ -6535,8 +6675,9 @@ Mood: friendly and intelligent.
|
|
|
6535
6675
|
res.status(requestValidationResult.code || 500).json({ detail: `${requestValidationResult.message}` });
|
|
6536
6676
|
return;
|
|
6537
6677
|
}
|
|
6678
|
+
console.log("[EXULU] agentInstance.rights_mode", agentInstance.rights_mode);
|
|
6538
6679
|
const authenticationResult = await requestValidators.authenticate(req);
|
|
6539
|
-
if (!authenticationResult.user?.id) {
|
|
6680
|
+
if (!authenticationResult.user?.id && agentInstance.rights_mode !== "public") {
|
|
6540
6681
|
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
6541
6682
|
return;
|
|
6542
6683
|
}
|
|
@@ -6560,7 +6701,7 @@ Mood: friendly and intelligent.
|
|
|
6560
6701
|
return;
|
|
6561
6702
|
}
|
|
6562
6703
|
}
|
|
6563
|
-
if (user
|
|
6704
|
+
if (user?.type !== "api" && !user?.super_admin && req.body.resourceId !== user?.id) {
|
|
6564
6705
|
res.status(400).json({
|
|
6565
6706
|
message: "The provided user id in the resourceId field is not the same as the authenticated user. Only super admins and API users can impersonate other users."
|
|
6566
6707
|
});
|
|
@@ -6581,7 +6722,6 @@ Mood: friendly and intelligent.
|
|
|
6581
6722
|
return;
|
|
6582
6723
|
}
|
|
6583
6724
|
providerapikey = variable.value;
|
|
6584
|
-
console.log("[EXULU] encrypted value", providerapikey);
|
|
6585
6725
|
if (!variable.encrypted) {
|
|
6586
6726
|
res.status(400).json({
|
|
6587
6727
|
message: "Provider API key variable not encrypted, for security reasons you are only allowed to use encrypted variables for provider API keys."
|
|
@@ -6591,7 +6731,6 @@ Mood: friendly and intelligent.
|
|
|
6591
6731
|
if (variable.encrypted) {
|
|
6592
6732
|
const bytes = CryptoJS3.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
|
|
6593
6733
|
providerapikey = bytes.toString(CryptoJS3.enc.Utf8);
|
|
6594
|
-
console.log("[EXULU] decrypted value", providerapikey);
|
|
6595
6734
|
}
|
|
6596
6735
|
}
|
|
6597
6736
|
if (!!headers.stream) {
|
|
@@ -6599,17 +6738,27 @@ Mood: friendly and intelligent.
|
|
|
6599
6738
|
label: agent.name,
|
|
6600
6739
|
trigger: "agent"
|
|
6601
6740
|
};
|
|
6741
|
+
let previousMessages = [];
|
|
6742
|
+
let message;
|
|
6743
|
+
if (!req.body.message && !headers.session && req.body.messages) {
|
|
6744
|
+
message = req.body.messages[req.body.messages.length - 1];
|
|
6745
|
+
previousMessages = req.body.messages.slice(0, -1);
|
|
6746
|
+
} else {
|
|
6747
|
+
message = req.body.message;
|
|
6748
|
+
}
|
|
6602
6749
|
const result = await agent.generateStream({
|
|
6603
6750
|
contexts,
|
|
6604
6751
|
user,
|
|
6605
6752
|
instructions: agentInstance.instructions,
|
|
6606
6753
|
session: headers.session,
|
|
6607
|
-
message
|
|
6754
|
+
message,
|
|
6755
|
+
previousMessages,
|
|
6608
6756
|
currentTools: enabledTools,
|
|
6609
6757
|
allExuluTools: tools,
|
|
6610
6758
|
providerapikey,
|
|
6611
6759
|
toolConfigs: agentInstance.tools,
|
|
6612
|
-
exuluConfig: config
|
|
6760
|
+
exuluConfig: config,
|
|
6761
|
+
req
|
|
6613
6762
|
});
|
|
6614
6763
|
result.stream.consumeStream();
|
|
6615
6764
|
result.stream.pipeUIMessageStreamToResponse(res, {
|
|
@@ -6636,11 +6785,12 @@ Mood: friendly and intelligent.
|
|
|
6636
6785
|
size: 16
|
|
6637
6786
|
}),
|
|
6638
6787
|
onFinish: async ({ messages, isContinuation, isAborted, responseMessage }) => {
|
|
6639
|
-
|
|
6788
|
+
console.log("[EXULU] onFinish", messages?.map((msg) => msg.parts?.map((part) => part.type === "text" ? part.text : null)).join("\n"));
|
|
6789
|
+
if (headers.session && user?.id) {
|
|
6640
6790
|
await saveChat({
|
|
6641
6791
|
session: headers.session,
|
|
6642
6792
|
user: user.id,
|
|
6643
|
-
messages
|
|
6793
|
+
messages
|
|
6644
6794
|
});
|
|
6645
6795
|
}
|
|
6646
6796
|
const metadata = messages[messages.length - 1]?.metadata;
|
|
@@ -6657,7 +6807,7 @@ Mood: friendly and intelligent.
|
|
|
6657
6807
|
type: STATISTICS_TYPE_ENUM.AGENT_RUN,
|
|
6658
6808
|
trigger: statistics.trigger,
|
|
6659
6809
|
count: 1,
|
|
6660
|
-
user: user
|
|
6810
|
+
user: user?.id,
|
|
6661
6811
|
role: user?.role?.id
|
|
6662
6812
|
}),
|
|
6663
6813
|
...metadata?.inputTokens ? [
|
|
@@ -6667,7 +6817,7 @@ Mood: friendly and intelligent.
|
|
|
6667
6817
|
type: STATISTICS_TYPE_ENUM.AGENT_RUN,
|
|
6668
6818
|
trigger: statistics.trigger,
|
|
6669
6819
|
count: metadata?.inputTokens,
|
|
6670
|
-
user: user
|
|
6820
|
+
user: user?.id,
|
|
6671
6821
|
role: user?.role?.id
|
|
6672
6822
|
})
|
|
6673
6823
|
] : [],
|
|
@@ -6688,6 +6838,7 @@ Mood: friendly and intelligent.
|
|
|
6688
6838
|
} else {
|
|
6689
6839
|
const response = await agent.generateSync({
|
|
6690
6840
|
user,
|
|
6841
|
+
req,
|
|
6691
6842
|
instructions: agentInstance.instructions,
|
|
6692
6843
|
session: headers.session,
|
|
6693
6844
|
inputMessages: [req.body.message],
|
|
@@ -7026,7 +7177,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7026
7177
|
}));
|
|
7027
7178
|
const { db: db3 } = await postgresClient();
|
|
7028
7179
|
const data = bullmqJob.data;
|
|
7029
|
-
const timeoutInSeconds = data.timeoutInSeconds ||
|
|
7180
|
+
const timeoutInSeconds = data.timeoutInSeconds || 600;
|
|
7030
7181
|
const timeoutMs = timeoutInSeconds * 1e3;
|
|
7031
7182
|
const timeoutPromise = new Promise((_, reject) => {
|
|
7032
7183
|
setTimeout(() => {
|
|
@@ -7080,7 +7231,9 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7080
7231
|
if (!context) {
|
|
7081
7232
|
throw new Error(`Context ${data.context} not found in the registry.`);
|
|
7082
7233
|
}
|
|
7083
|
-
const field = context.fields.find((field2) =>
|
|
7234
|
+
const field = context.fields.find((field2) => {
|
|
7235
|
+
return field2.name.replace("_s3key", "") === data.inputs.field.replace("_s3key", "");
|
|
7236
|
+
});
|
|
7084
7237
|
if (!field) {
|
|
7085
7238
|
throw new Error(`Field ${data.inputs.field} not found in the context ${data.context}.`);
|
|
7086
7239
|
}
|
|
@@ -7355,7 +7508,13 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7355
7508
|
let jobs = [];
|
|
7356
7509
|
let items = [];
|
|
7357
7510
|
for (const item of result) {
|
|
7358
|
-
const { item: createdItem, job } = await context.createItem(
|
|
7511
|
+
const { item: createdItem, job } = await context.createItem(
|
|
7512
|
+
item,
|
|
7513
|
+
config,
|
|
7514
|
+
data.user,
|
|
7515
|
+
data.role,
|
|
7516
|
+
item.external_id || item.id ? true : false
|
|
7517
|
+
);
|
|
7359
7518
|
if (job) {
|
|
7360
7519
|
jobs.push(job);
|
|
7361
7520
|
console.log(`[EXULU] Scheduled job through source update job for item ${createdItem.id} (Job ID: ${job})`, logMetadata2(bullmqJob.name, {
|
|
@@ -7409,14 +7568,12 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7409
7568
|
}
|
|
7410
7569
|
);
|
|
7411
7570
|
worker.on("completed", async (job, returnvalue) => {
|
|
7412
|
-
console.log(`[EXULU] completed job ${job.id}.`,
|
|
7413
|
-
result: returnvalue
|
|
7414
|
-
}));
|
|
7571
|
+
console.log(`[EXULU] completed job ${job.id}.`, returnvalue);
|
|
7415
7572
|
const { db: db3 } = await postgresClient();
|
|
7416
7573
|
await db3.from("job_results").where({ job_id: job.id }).update({
|
|
7417
7574
|
state: JOB_STATUS_ENUM.completed,
|
|
7418
|
-
result: returnvalue.result,
|
|
7419
|
-
metadata: returnvalue.metadata
|
|
7575
|
+
result: returnvalue.result ? JSON.stringify(returnvalue.result) : null,
|
|
7576
|
+
metadata: returnvalue.metadata ? JSON.stringify(returnvalue.metadata) : null
|
|
7420
7577
|
});
|
|
7421
7578
|
});
|
|
7422
7579
|
worker.on("failed", async (job, error, prev) => {
|
|
@@ -8100,7 +8257,6 @@ var claudeSonnet45Agent = new ExuluAgent2({
|
|
|
8100
8257
|
|
|
8101
8258
|
// src/templates/agents/google/vertex/index.ts
|
|
8102
8259
|
import { createVertex } from "@ai-sdk/google-vertex";
|
|
8103
|
-
import "@ai-sdk/google-vertex";
|
|
8104
8260
|
var vertexAuthenticationInformation = `
|
|
8105
8261
|
### Vertex Authentication Setup (Google Auth)
|
|
8106
8262
|
|
|
@@ -8155,7 +8311,6 @@ var vertexGemini25FlashAgent = new ExuluAgent2({
|
|
|
8155
8311
|
instructions: "",
|
|
8156
8312
|
model: {
|
|
8157
8313
|
create: ({ apiKey }) => {
|
|
8158
|
-
console.log("[EXULU] apiKey", apiKey);
|
|
8159
8314
|
if (!apiKey) {
|
|
8160
8315
|
throw new Error("Auth credentials not found for Google Vertex agent, make sure you have set the provider api key to a valid google authentication json.");
|
|
8161
8316
|
}
|
|
@@ -8166,8 +8321,45 @@ var vertexGemini25FlashAgent = new ExuluAgent2({
|
|
|
8166
8321
|
if (!googleAuthPayload.location) {
|
|
8167
8322
|
throw new Error("Location not set in authentication json for Google Vertex Gemini 2.5 Flash agent, should be for example 'europe-west1'");
|
|
8168
8323
|
}
|
|
8169
|
-
const
|
|
8170
|
-
const model =
|
|
8324
|
+
const vertex = createVertex(googleAuthPayload);
|
|
8325
|
+
const model = vertex("gemini-2.5-flash");
|
|
8326
|
+
return model;
|
|
8327
|
+
}
|
|
8328
|
+
}
|
|
8329
|
+
}
|
|
8330
|
+
});
|
|
8331
|
+
var vertexGemini20FlashAgent = new ExuluAgent2({
|
|
8332
|
+
id: `default_vertex_gemini_2_0_flash_agent`,
|
|
8333
|
+
name: `GEMINI-2.0-FLASH`,
|
|
8334
|
+
provider: "vertex",
|
|
8335
|
+
description: `Google Vertex Gemini 2.0 Flash model. High intelligence and capability. Moderately Fast.`,
|
|
8336
|
+
type: "agent",
|
|
8337
|
+
capabilities: {
|
|
8338
|
+
text: true,
|
|
8339
|
+
images: [".png", ".jpg", ".jpeg", ".webp"],
|
|
8340
|
+
files: [".pdf", ".txt"],
|
|
8341
|
+
audio: [".mpeg", ".mp3", ".m4a", ".wav", ".mp4"],
|
|
8342
|
+
video: [".mp4", ".mpeg"]
|
|
8343
|
+
},
|
|
8344
|
+
authenticationInformation: vertexAuthenticationInformation,
|
|
8345
|
+
maxContextLength: 1048576,
|
|
8346
|
+
config: {
|
|
8347
|
+
name: `GEMINI-2.0-FLASH`,
|
|
8348
|
+
instructions: "",
|
|
8349
|
+
model: {
|
|
8350
|
+
create: ({ apiKey }) => {
|
|
8351
|
+
if (!apiKey) {
|
|
8352
|
+
throw new Error("Auth credentials not found for Google Vertex agent, make sure you have set the provider api key to a valid google authentication json.");
|
|
8353
|
+
}
|
|
8354
|
+
const googleAuthPayload = JSON.parse(apiKey || "{}");
|
|
8355
|
+
if (!googleAuthPayload) {
|
|
8356
|
+
throw new Error("API key not found for Google Vertex Gemini 2.0 Flash agent.");
|
|
8357
|
+
}
|
|
8358
|
+
if (!googleAuthPayload.location) {
|
|
8359
|
+
throw new Error("Location not set in authentication json for Google Vertex Gemini 2.0 Flash agent, should be for example 'europe-west1'");
|
|
8360
|
+
}
|
|
8361
|
+
const vertex = createVertex(googleAuthPayload);
|
|
8362
|
+
const model = vertex("gemini-2.0-flash");
|
|
8171
8363
|
return model;
|
|
8172
8364
|
}
|
|
8173
8365
|
}
|
|
@@ -8193,7 +8385,6 @@ var vertexGemini3ProAgent = new ExuluAgent2({
|
|
|
8193
8385
|
instructions: "",
|
|
8194
8386
|
model: {
|
|
8195
8387
|
create: ({ apiKey }) => {
|
|
8196
|
-
console.log("[EXULU] apiKey", apiKey);
|
|
8197
8388
|
if (!apiKey) {
|
|
8198
8389
|
throw new Error("Auth credentials not found for Google Vertex agent, make sure you have set the provider api key to a valid google authentication json.");
|
|
8199
8390
|
}
|
|
@@ -8204,8 +8395,8 @@ var vertexGemini3ProAgent = new ExuluAgent2({
|
|
|
8204
8395
|
if (!googleAuthPayload.location) {
|
|
8205
8396
|
throw new Error("Location not set in authentication json for Google Vertex Gemini 3 Pro agent, should be for example 'europe-west1'");
|
|
8206
8397
|
}
|
|
8207
|
-
const
|
|
8208
|
-
const model =
|
|
8398
|
+
const vertex = createVertex(googleAuthPayload);
|
|
8399
|
+
const model = vertex("gemini-3-pro-preview");
|
|
8209
8400
|
return model;
|
|
8210
8401
|
}
|
|
8211
8402
|
}
|
|
@@ -9295,6 +9486,32 @@ var mathTools = [
|
|
|
9295
9486
|
degreesToRadiansTool
|
|
9296
9487
|
];
|
|
9297
9488
|
|
|
9489
|
+
// src/templates/tools/preview-pdf.ts
|
|
9490
|
+
import { z as z5 } from "zod";
|
|
9491
|
+
var previewPdfTool = new ExuluTool2({
|
|
9492
|
+
id: "preview_pdf",
|
|
9493
|
+
name: "Preview PDF",
|
|
9494
|
+
description: "Used to display a PDF file in an iframe web view",
|
|
9495
|
+
type: "function",
|
|
9496
|
+
config: [],
|
|
9497
|
+
inputSchema: z5.object({
|
|
9498
|
+
s3key: z5.string().describe("The S3 key of the PDF file to preview, can also optionally include a [bucket:name] to specify the bucket."),
|
|
9499
|
+
page: z5.number().describe("The page number to preview, defaults to 1.").optional()
|
|
9500
|
+
}),
|
|
9501
|
+
execute: async ({ s3key, page, exuluConfig }) => {
|
|
9502
|
+
const url = await getPresignedUrl(s3key, exuluConfig);
|
|
9503
|
+
if (!url) {
|
|
9504
|
+
throw new Error("No URL provided for PDF preview");
|
|
9505
|
+
}
|
|
9506
|
+
return {
|
|
9507
|
+
result: JSON.stringify({
|
|
9508
|
+
url,
|
|
9509
|
+
page: page ?? 1
|
|
9510
|
+
})
|
|
9511
|
+
};
|
|
9512
|
+
}
|
|
9513
|
+
});
|
|
9514
|
+
|
|
9298
9515
|
// src/templates/tools/todo/todowrite.txt
|
|
9299
9516
|
var todowrite_default = `Use this tool to create and manage a structured task list for your current coding session. This helps you track progress, organize complex tasks, and demonstrate thoroughness to the user.
|
|
9300
9517
|
It also helps the user understand the progress of the task and overall progress of their requests.
|
|
@@ -9481,12 +9698,12 @@ Usage:
|
|
|
9481
9698
|
- If no todos exist yet, an empty list will be returned`;
|
|
9482
9699
|
|
|
9483
9700
|
// src/templates/tools/todo/todo.ts
|
|
9484
|
-
import
|
|
9485
|
-
var TodoSchema =
|
|
9486
|
-
content:
|
|
9487
|
-
status:
|
|
9488
|
-
priority:
|
|
9489
|
-
id:
|
|
9701
|
+
import z6 from "zod";
|
|
9702
|
+
var TodoSchema = z6.object({
|
|
9703
|
+
content: z6.string().describe("Brief description of the task"),
|
|
9704
|
+
status: z6.string().describe("Current status of the task: pending, in_progress, completed, cancelled"),
|
|
9705
|
+
priority: z6.string().describe("Priority level of the task: high, medium, low"),
|
|
9706
|
+
id: z6.string().describe("Unique identifier for the todo item")
|
|
9490
9707
|
});
|
|
9491
9708
|
var TodoWriteTool = new ExuluTool2({
|
|
9492
9709
|
id: "todo_write",
|
|
@@ -9499,8 +9716,8 @@ var TodoWriteTool = new ExuluTool2({
|
|
|
9499
9716
|
description: "The description of the todo list, if set overwrites the default description.",
|
|
9500
9717
|
default: todowrite_default
|
|
9501
9718
|
}],
|
|
9502
|
-
inputSchema:
|
|
9503
|
-
todos:
|
|
9719
|
+
inputSchema: z6.object({
|
|
9720
|
+
todos: z6.array(TodoSchema).describe("The updated todo list")
|
|
9504
9721
|
}),
|
|
9505
9722
|
execute: async (inputs) => {
|
|
9506
9723
|
const { sessionID, todos, user } = inputs;
|
|
@@ -9531,7 +9748,7 @@ var TodoReadTool = new ExuluTool2({
|
|
|
9531
9748
|
id: "todo_read",
|
|
9532
9749
|
name: "Todo Read",
|
|
9533
9750
|
description: "Use this tool to read your todo list",
|
|
9534
|
-
inputSchema:
|
|
9751
|
+
inputSchema: z6.object({}),
|
|
9535
9752
|
type: "function",
|
|
9536
9753
|
category: "todo",
|
|
9537
9754
|
config: [{
|
|
@@ -9643,6 +9860,7 @@ var ExuluApp = class {
|
|
|
9643
9860
|
this._tools = [
|
|
9644
9861
|
...tools ?? [],
|
|
9645
9862
|
...mathTools,
|
|
9863
|
+
...[previewPdfTool],
|
|
9646
9864
|
...todoTools,
|
|
9647
9865
|
// Add contexts as tools
|
|
9648
9866
|
...Object.values(contexts || {}).map((context) => context.tool())
|