@exulu/backend 1.32.0 → 1.33.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 +2 -2
- package/dist/index.cjs +672 -214
- package/dist/index.d.cts +7 -4
- package/dist/index.d.ts +7 -4
- package/dist/index.js +672 -214
- package/package.json +1 -1
- package/types/models/context.ts +26 -12
package/dist/index.js
CHANGED
|
@@ -6,10 +6,10 @@ import { createClient } from "redis";
|
|
|
6
6
|
|
|
7
7
|
// src/bullmq/server.ts
|
|
8
8
|
var redisServer = {
|
|
9
|
-
host:
|
|
10
|
-
port: process.env.REDIS_PORT,
|
|
9
|
+
host: process.env.REDIS_HOST ?? "",
|
|
10
|
+
port: process.env.REDIS_PORT ?? "",
|
|
11
11
|
password: process.env.REDIS_PASSWORD || void 0,
|
|
12
|
-
username: process.env.REDIS_USER ||
|
|
12
|
+
username: process.env.REDIS_USER || ""
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
// src/redis/client.ts
|
|
@@ -22,7 +22,7 @@ async function redisClient() {
|
|
|
22
22
|
if (!client["exulu"]) {
|
|
23
23
|
try {
|
|
24
24
|
let url = "";
|
|
25
|
-
if (redisServer.
|
|
25
|
+
if (redisServer.password) {
|
|
26
26
|
url = `redis://${redisServer.username}:${redisServer.password}@${redisServer.host}:${redisServer.port}`;
|
|
27
27
|
} else {
|
|
28
28
|
url = `redis://${redisServer.host}:${redisServer.port}`;
|
|
@@ -382,7 +382,7 @@ var mapType = (t, type, name, defaultValue, unique) => {
|
|
|
382
382
|
if (unique) t.unique(name);
|
|
383
383
|
return;
|
|
384
384
|
}
|
|
385
|
-
throw new Error("Invalid type: " + type);
|
|
385
|
+
throw new Error("Invalid field type for database: " + type);
|
|
386
386
|
};
|
|
387
387
|
|
|
388
388
|
// src/registry/utils/sanitize-name.ts
|
|
@@ -772,6 +772,10 @@ var agentSessionsSchema = {
|
|
|
772
772
|
name: "project",
|
|
773
773
|
type: "uuid",
|
|
774
774
|
required: false
|
|
775
|
+
},
|
|
776
|
+
{
|
|
777
|
+
name: "metadata",
|
|
778
|
+
type: "json"
|
|
775
779
|
}
|
|
776
780
|
]
|
|
777
781
|
};
|
|
@@ -1429,11 +1433,11 @@ var bullmq = {
|
|
|
1429
1433
|
if (!data.inputs) {
|
|
1430
1434
|
throw new Error(`Missing property "inputs" in data for job ${id}.`);
|
|
1431
1435
|
}
|
|
1432
|
-
if (data.type !== "embedder" && data.type !== "workflow" && data.type !== "processor" && data.type !== "eval_run" && data.type !== "eval_function") {
|
|
1433
|
-
throw new Error(`Property "type" in data for job ${id} must be of value "embedder", "workflow", "processor", "eval_run" or "
|
|
1436
|
+
if (data.type !== "embedder" && data.type !== "workflow" && data.type !== "processor" && data.type !== "eval_run" && data.type !== "eval_function" && data.type !== "source") {
|
|
1437
|
+
throw new Error(`Property "type" in data for job ${id} must be of value "embedder", "workflow", "processor", "eval_run", "eval_function" or "source".`);
|
|
1434
1438
|
}
|
|
1435
|
-
if (!data.workflow && !data.embedder && !data.processor && !data.eval_run_id && !data.eval_functions?.length) {
|
|
1436
|
-
throw new Error(`Either a workflow, embedder, processor, eval_run or
|
|
1439
|
+
if (!data.workflow && !data.embedder && !data.processor && !data.eval_run_id && !data.eval_functions?.length && !data.source) {
|
|
1440
|
+
throw new Error(`Either a workflow, embedder, processor, eval_run, eval_functions or source must be set for job ${id}.`);
|
|
1437
1441
|
}
|
|
1438
1442
|
}
|
|
1439
1443
|
};
|
|
@@ -1939,6 +1943,7 @@ var handleRBACUpdate = async (db3, entityName, resourceId, rbacData, existingRba
|
|
|
1939
1943
|
};
|
|
1940
1944
|
function createMutations(table, agents, contexts, tools, config) {
|
|
1941
1945
|
const tableNamePlural = table.name.plural.toLowerCase();
|
|
1946
|
+
const tableNameSingular = table.name.singular.toLowerCase();
|
|
1942
1947
|
const validateWriteAccess = async (id, context) => {
|
|
1943
1948
|
try {
|
|
1944
1949
|
const { db: db3, req, user } = context;
|
|
@@ -2250,56 +2255,120 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2250
2255
|
return finalizeRequestedFields({ table, requestedFields, agents, contexts, tools, result, user: context.user.id });
|
|
2251
2256
|
}
|
|
2252
2257
|
};
|
|
2253
|
-
if (table.type === "items"
|
|
2254
|
-
|
|
2258
|
+
if (table.type === "items") {
|
|
2259
|
+
if (table.fields.some((field) => field.processor?.execute)) {
|
|
2260
|
+
mutations[`${tableNameSingular}ProcessItemField`] = async (_, args, context, info) => {
|
|
2261
|
+
if (!context.user?.super_admin) {
|
|
2262
|
+
throw new Error("You are not authorized to process fields via API, user must be super admin.");
|
|
2263
|
+
}
|
|
2264
|
+
const exists = contexts.find((context2) => context2.id === table.id);
|
|
2265
|
+
if (!exists) {
|
|
2266
|
+
throw new Error(`Context ${table.id} not found.`);
|
|
2267
|
+
}
|
|
2268
|
+
if (!args.field) {
|
|
2269
|
+
throw new Error("Field argument missing, the field argument is required.");
|
|
2270
|
+
}
|
|
2271
|
+
if (!args.item) {
|
|
2272
|
+
throw new Error("Item argument missing, the item argument is required.");
|
|
2273
|
+
}
|
|
2274
|
+
const name = args.field?.replace("_s3key", "");
|
|
2275
|
+
console.log("[EXULU] name", name);
|
|
2276
|
+
console.log("[EXULU] fields", exists.fields.map((field2) => field2.name));
|
|
2277
|
+
const field = exists.fields.find((field2) => field2.name === name);
|
|
2278
|
+
if (!field) {
|
|
2279
|
+
throw new Error(`Field ${name} not found in context ${exists.id}.`);
|
|
2280
|
+
}
|
|
2281
|
+
if (!field.processor) {
|
|
2282
|
+
throw new Error(`Processor not set for field ${args.field} in context ${exists.id}.`);
|
|
2283
|
+
}
|
|
2284
|
+
const { db: db3 } = context;
|
|
2285
|
+
let query = db3.from(tableNamePlural).select("*").where({ id: args.item });
|
|
2286
|
+
query = applyAccessControl(table, context.user, query);
|
|
2287
|
+
const item = await query.first();
|
|
2288
|
+
if (!item) {
|
|
2289
|
+
throw new Error("Item not found, or your user does not have access to it.");
|
|
2290
|
+
}
|
|
2291
|
+
const { job, result } = await exists.processField(
|
|
2292
|
+
"api",
|
|
2293
|
+
context.user.id,
|
|
2294
|
+
context.user.role?.id,
|
|
2295
|
+
{
|
|
2296
|
+
...item,
|
|
2297
|
+
field: args.field
|
|
2298
|
+
},
|
|
2299
|
+
config
|
|
2300
|
+
);
|
|
2301
|
+
return {
|
|
2302
|
+
message: job ? "Processing job scheduled." : "Item processed successfully.",
|
|
2303
|
+
result,
|
|
2304
|
+
job
|
|
2305
|
+
};
|
|
2306
|
+
};
|
|
2307
|
+
}
|
|
2308
|
+
mutations[`${tableNameSingular}ExecuteSource`] = async (_, args, context, info) => {
|
|
2309
|
+
console.log("[EXULU] Executing source", args);
|
|
2255
2310
|
if (!context.user?.super_admin) {
|
|
2256
|
-
throw new Error("You are not authorized to
|
|
2311
|
+
throw new Error("You are not authorized to execute sources via API, user must be super admin.");
|
|
2312
|
+
}
|
|
2313
|
+
if (!args.source) {
|
|
2314
|
+
throw new Error("Source argument missing, the source argument is required.");
|
|
2257
2315
|
}
|
|
2258
2316
|
const exists = contexts.find((context2) => context2.id === table.id);
|
|
2259
2317
|
if (!exists) {
|
|
2260
2318
|
throw new Error(`Context ${table.id} not found.`);
|
|
2261
2319
|
}
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
if (!args.item) {
|
|
2266
|
-
throw new Error("Item argument missing, the item argument is required.");
|
|
2320
|
+
const source = exists.sources.find((source2) => source2.id === args.source);
|
|
2321
|
+
if (!source) {
|
|
2322
|
+
throw new Error(`Source ${args.source} not found in context ${exists.id}.`);
|
|
2267
2323
|
}
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2324
|
+
if (source?.config?.queue) {
|
|
2325
|
+
console.log("[EXULU] Executing source function in queue mode");
|
|
2326
|
+
const queue = await source.config.queue;
|
|
2327
|
+
if (!queue) {
|
|
2328
|
+
throw new Error(`Queue not found for source ${source.id}.`);
|
|
2329
|
+
}
|
|
2330
|
+
const job = await queue.queue?.add(source.id, {
|
|
2331
|
+
source: source.id,
|
|
2332
|
+
context: exists.id,
|
|
2333
|
+
type: "source",
|
|
2334
|
+
inputs: args.inputs
|
|
2335
|
+
});
|
|
2336
|
+
console.log("[EXULU] Source function job scheduled", job.id);
|
|
2337
|
+
return {
|
|
2338
|
+
message: "Job scheduled for source execution.",
|
|
2339
|
+
jobs: [job?.id],
|
|
2340
|
+
items: []
|
|
2341
|
+
};
|
|
2277
2342
|
}
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2343
|
+
console.log("[EXULU] Executing source function directly");
|
|
2344
|
+
const result = await source.execute(args.inputs);
|
|
2345
|
+
let jobs = [];
|
|
2346
|
+
let items = [];
|
|
2347
|
+
for (const item of result) {
|
|
2348
|
+
const { item: createdItem, job } = await exists.createItem(
|
|
2349
|
+
item,
|
|
2350
|
+
config,
|
|
2351
|
+
context.user.id,
|
|
2352
|
+
context.user.role?.id
|
|
2353
|
+
);
|
|
2354
|
+
if (job) {
|
|
2355
|
+
jobs.push(job);
|
|
2356
|
+
console.log(`[EXULU] Scheduled job through source update job for item ${createdItem.id} (Job ID: ${job})`);
|
|
2357
|
+
}
|
|
2358
|
+
if (createdItem.id) {
|
|
2359
|
+
items.push(createdItem.id);
|
|
2360
|
+
console.log(`[EXULU] created item through source update job ${createdItem.id}`);
|
|
2361
|
+
}
|
|
2284
2362
|
}
|
|
2285
|
-
const { job, result } = await exists.processField(
|
|
2286
|
-
"api",
|
|
2287
|
-
context.user.id,
|
|
2288
|
-
context.user.role?.id,
|
|
2289
|
-
{
|
|
2290
|
-
...item,
|
|
2291
|
-
field: args.field
|
|
2292
|
-
},
|
|
2293
|
-
config
|
|
2294
|
-
);
|
|
2295
2363
|
return {
|
|
2296
|
-
message:
|
|
2297
|
-
|
|
2298
|
-
|
|
2364
|
+
message: "Items created successfully.",
|
|
2365
|
+
jobs,
|
|
2366
|
+
items
|
|
2299
2367
|
};
|
|
2300
|
-
}
|
|
2368
|
+
};
|
|
2369
|
+
mutations[`${tableNameSingular}GenerateChunks`] = async (_, args, context, info) => {
|
|
2301
2370
|
if (!context.user?.super_admin) {
|
|
2302
|
-
throw new Error("You are not authorized to
|
|
2371
|
+
throw new Error("You are not authorized to generate chunks via API, user must be super admin.");
|
|
2303
2372
|
}
|
|
2304
2373
|
const { db: db3 } = await postgresClient();
|
|
2305
2374
|
const exists = contexts.find((context2) => context2.id === table.id);
|
|
@@ -2344,11 +2413,12 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2344
2413
|
}
|
|
2345
2414
|
}
|
|
2346
2415
|
return {
|
|
2347
|
-
message: "Chunks
|
|
2416
|
+
message: "Chunks generated successfully.",
|
|
2348
2417
|
items: items.length,
|
|
2349
2418
|
jobs: jobs.slice(0, 100)
|
|
2350
2419
|
};
|
|
2351
|
-
}
|
|
2420
|
+
};
|
|
2421
|
+
mutations[`${tableNameSingular}DeleteChunks`] = async (_, args, context, info) => {
|
|
2352
2422
|
if (!context.user?.super_admin) {
|
|
2353
2423
|
throw new Error("You are not authorized to delete chunks via API, user must be super admin.");
|
|
2354
2424
|
}
|
|
@@ -3320,6 +3390,7 @@ function createSDL(tables, contexts, agents, tools, config, evals, queues2) {
|
|
|
3320
3390
|
if (table.type === "items") {
|
|
3321
3391
|
mutationDefs += `
|
|
3322
3392
|
${tableNameSingular}GenerateChunks(where: [Filter${tableNameSingularUpperCaseFirst}]): ${tableNameSingular}GenerateChunksReturnPayload
|
|
3393
|
+
${tableNameSingular}ExecuteSource(source: ID!, inputs: JSON!): ${tableNameSingular}ExecuteSourceReturnPayload
|
|
3323
3394
|
${tableNameSingular}DeleteChunks(where: [Filter${tableNameSingularUpperCaseFirst}]): ${tableNameSingular}DeleteChunksReturnPayload
|
|
3324
3395
|
`;
|
|
3325
3396
|
if (processorFields?.length > 0) {
|
|
@@ -3334,6 +3405,12 @@ function createSDL(tables, contexts, agents, tools, config, evals, queues2) {
|
|
|
3334
3405
|
jobs: [String!]
|
|
3335
3406
|
}
|
|
3336
3407
|
|
|
3408
|
+
type ${tableNameSingular}ExecuteSourceReturnPayload {
|
|
3409
|
+
message: String!
|
|
3410
|
+
jobs: [String!]
|
|
3411
|
+
items: [String!]
|
|
3412
|
+
}
|
|
3413
|
+
|
|
3337
3414
|
type ${tableNameSingular}ProcessItemFieldReturnPayload {
|
|
3338
3415
|
message: String!
|
|
3339
3416
|
result: String!
|
|
@@ -3445,7 +3522,7 @@ type PageInfo {
|
|
|
3445
3522
|
toolCategories: [String!]!
|
|
3446
3523
|
`;
|
|
3447
3524
|
typeDefs += `
|
|
3448
|
-
jobs(queue: QueueEnum!, statusses: [JobStateEnum!]): JobPaginationResult
|
|
3525
|
+
jobs(queue: QueueEnum!, statusses: [JobStateEnum!], page: Int, limit: Int): JobPaginationResult
|
|
3449
3526
|
`;
|
|
3450
3527
|
resolvers.Query["providers"] = async (_, args, context, info) => {
|
|
3451
3528
|
const requestedFields = getRequestedFields(info);
|
|
@@ -3644,6 +3721,7 @@ type PageInfo {
|
|
|
3644
3721
|
if (!client2) {
|
|
3645
3722
|
throw new Error("Redis client not created properly");
|
|
3646
3723
|
}
|
|
3724
|
+
console.log("[EXULU] Jobs pagination args", args);
|
|
3647
3725
|
const {
|
|
3648
3726
|
jobs,
|
|
3649
3727
|
count
|
|
@@ -3676,25 +3754,46 @@ type PageInfo {
|
|
|
3676
3754
|
itemCount: count,
|
|
3677
3755
|
currentPage: args.page || 1,
|
|
3678
3756
|
hasPreviousPage: args.page && args.page > 1 ? true : false,
|
|
3679
|
-
hasNextPage: args.page && args.page < Math.ceil(
|
|
3757
|
+
hasNextPage: args.page && args.page < Math.ceil(count / (args.limit || 100)) ? true : false
|
|
3680
3758
|
}
|
|
3681
3759
|
};
|
|
3682
3760
|
};
|
|
3683
3761
|
resolvers.Query["contexts"] = async (_, args, context, info) => {
|
|
3684
|
-
const data = contexts.map((context2) =>
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
fields: context2.fields.map((field) => {
|
|
3762
|
+
const data = await Promise.all(contexts.map(async (context2) => {
|
|
3763
|
+
const sources = await Promise.all(context2.sources.map(async (source) => {
|
|
3764
|
+
let queueName = void 0;
|
|
3765
|
+
if (source.config) {
|
|
3766
|
+
const config2 = await source.config.queue;
|
|
3767
|
+
queueName = config2?.queue?.name || void 0;
|
|
3768
|
+
}
|
|
3692
3769
|
return {
|
|
3693
|
-
|
|
3694
|
-
name:
|
|
3695
|
-
|
|
3770
|
+
id: source.id,
|
|
3771
|
+
name: source.name,
|
|
3772
|
+
description: source.description,
|
|
3773
|
+
config: {
|
|
3774
|
+
schedule: source.config?.schedule,
|
|
3775
|
+
queue: queueName,
|
|
3776
|
+
retries: source.config?.retries,
|
|
3777
|
+
backoff: source.config?.backoff
|
|
3778
|
+
}
|
|
3696
3779
|
};
|
|
3697
|
-
})
|
|
3780
|
+
}));
|
|
3781
|
+
return {
|
|
3782
|
+
id: context2.id,
|
|
3783
|
+
name: context2.name,
|
|
3784
|
+
description: context2.description,
|
|
3785
|
+
embedder: context2.embedder?.name || void 0,
|
|
3786
|
+
slug: "/contexts/" + context2.id,
|
|
3787
|
+
active: context2.active,
|
|
3788
|
+
sources,
|
|
3789
|
+
fields: context2.fields.map((field) => {
|
|
3790
|
+
return {
|
|
3791
|
+
...field,
|
|
3792
|
+
name: sanitizeName(field.name),
|
|
3793
|
+
label: field.name?.replace("_s3key", "")
|
|
3794
|
+
};
|
|
3795
|
+
})
|
|
3796
|
+
};
|
|
3698
3797
|
}));
|
|
3699
3798
|
const requestedFields = getRequestedFields(info);
|
|
3700
3799
|
return {
|
|
@@ -3712,6 +3811,24 @@ type PageInfo {
|
|
|
3712
3811
|
if (!data) {
|
|
3713
3812
|
return null;
|
|
3714
3813
|
}
|
|
3814
|
+
const sources = await Promise.all(data.sources.map(async (source) => {
|
|
3815
|
+
let queueName = void 0;
|
|
3816
|
+
if (source.config) {
|
|
3817
|
+
const config2 = await source.config.queue;
|
|
3818
|
+
queueName = config2?.queue?.name || void 0;
|
|
3819
|
+
}
|
|
3820
|
+
return {
|
|
3821
|
+
id: source.id,
|
|
3822
|
+
name: source.name,
|
|
3823
|
+
description: source.description,
|
|
3824
|
+
config: {
|
|
3825
|
+
schedule: source.config?.schedule,
|
|
3826
|
+
queue: queueName,
|
|
3827
|
+
retries: source.config?.retries,
|
|
3828
|
+
backoff: source.config?.backoff
|
|
3829
|
+
}
|
|
3830
|
+
};
|
|
3831
|
+
}));
|
|
3715
3832
|
const clean = {
|
|
3716
3833
|
id: data.id,
|
|
3717
3834
|
name: data.name,
|
|
@@ -3719,6 +3836,7 @@ type PageInfo {
|
|
|
3719
3836
|
embedder: data.embedder?.name || void 0,
|
|
3720
3837
|
slug: "/contexts/" + data.id,
|
|
3721
3838
|
active: data.active,
|
|
3839
|
+
sources,
|
|
3722
3840
|
fields: await Promise.all(data.fields.map(async (field) => {
|
|
3723
3841
|
const label = field.name?.replace("_s3key", "");
|
|
3724
3842
|
if (field.type === "file" && !field.name.endsWith("_s3key")) {
|
|
@@ -3935,6 +4053,26 @@ type Context {
|
|
|
3935
4053
|
active: Boolean
|
|
3936
4054
|
fields: JSON
|
|
3937
4055
|
configuration: JSON
|
|
4056
|
+
sources: [ContextSource!]
|
|
4057
|
+
}
|
|
4058
|
+
|
|
4059
|
+
type ContextSource {
|
|
4060
|
+
id: String!
|
|
4061
|
+
name: String!
|
|
4062
|
+
description: String!
|
|
4063
|
+
config: ContextSourceConfig!
|
|
4064
|
+
}
|
|
4065
|
+
|
|
4066
|
+
type ContextSourceConfig {
|
|
4067
|
+
schedule: String
|
|
4068
|
+
queue: String
|
|
4069
|
+
retries: Int
|
|
4070
|
+
backoff: ContextSourceBackoff
|
|
4071
|
+
}
|
|
4072
|
+
|
|
4073
|
+
type ContextSourceBackoff {
|
|
4074
|
+
type: String
|
|
4075
|
+
delay: Int
|
|
3938
4076
|
}
|
|
3939
4077
|
|
|
3940
4078
|
type RunEvalReturnPayload {
|
|
@@ -4027,8 +4165,11 @@ async function getJobsByQueueAndName(queueName, statusses, page, limit) {
|
|
|
4027
4165
|
}
|
|
4028
4166
|
const config = await queue.use();
|
|
4029
4167
|
const startIndex = (page || 1) - 1;
|
|
4030
|
-
const endIndex = startIndex + (limit || 100);
|
|
4168
|
+
const endIndex = startIndex - 1 + (limit || 100);
|
|
4169
|
+
console.log("[EXULU] Jobs pagination startIndex", startIndex);
|
|
4170
|
+
console.log("[EXULU] Jobs pagination endIndex", endIndex);
|
|
4031
4171
|
const jobs = await config.queue.getJobs(statusses || [], startIndex, endIndex, false);
|
|
4172
|
+
console.log("[EXULU] Jobs pagination jobs", jobs?.length);
|
|
4032
4173
|
const counts = await config.queue.getJobCounts(...statusses || []);
|
|
4033
4174
|
let total = 0;
|
|
4034
4175
|
if (counts) {
|
|
@@ -4618,7 +4759,7 @@ function sanitizeToolName(name) {
|
|
|
4618
4759
|
}
|
|
4619
4760
|
return sanitized;
|
|
4620
4761
|
}
|
|
4621
|
-
var convertToolsArrayToObject = (currentTools, allExuluTools, configs, providerapikey, contexts, user, exuluConfig) => {
|
|
4762
|
+
var convertToolsArrayToObject = (currentTools, allExuluTools, configs, providerapikey, contexts, user, exuluConfig, sessionID) => {
|
|
4622
4763
|
if (!currentTools) return {};
|
|
4623
4764
|
if (!allExuluTools) return {};
|
|
4624
4765
|
const sanitizedTools = currentTools ? currentTools.map((tool2) => ({
|
|
@@ -4628,101 +4769,109 @@ var convertToolsArrayToObject = (currentTools, allExuluTools, configs, providera
|
|
|
4628
4769
|
console.log("[EXULU] Sanitized tools", sanitizedTools.map((x) => x.name + " (" + x.id + ")"));
|
|
4629
4770
|
return {
|
|
4630
4771
|
...sanitizedTools?.reduce(
|
|
4631
|
-
(prev, cur) =>
|
|
4632
|
-
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
|
|
4643
|
-
|
|
4644
|
-
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
}) => {
|
|
4663
|
-
const mime = getMimeType(type);
|
|
4664
|
-
const prefix = exuluConfig?.fileUploads?.s3prefix ? `${exuluConfig.fileUploads.s3prefix.replace(/\/$/, "")}/` : "";
|
|
4665
|
-
const key = `${prefix}${user}/${generateS3Key(name)}${type}`;
|
|
4666
|
-
const command = new PutObjectCommand2({
|
|
4667
|
-
Bucket: exuluConfig?.fileUploads?.s3Bucket,
|
|
4668
|
-
Key: key,
|
|
4669
|
-
Body: data,
|
|
4670
|
-
ContentType: mime
|
|
4772
|
+
(prev, cur) => {
|
|
4773
|
+
let config = configs?.find((config2) => config2.id === cur.id);
|
|
4774
|
+
const userDefinedConfigDescription = config?.config.find((config2) => config2.name === "description")?.value;
|
|
4775
|
+
const defaultConfigDescription = config?.config.find((config2) => config2.name === "description")?.default;
|
|
4776
|
+
const toolDescription = cur.description;
|
|
4777
|
+
const description = userDefinedConfigDescription || defaultConfigDescription || toolDescription;
|
|
4778
|
+
return {
|
|
4779
|
+
...prev,
|
|
4780
|
+
[cur.name]: {
|
|
4781
|
+
...cur.tool,
|
|
4782
|
+
description,
|
|
4783
|
+
async *execute(inputs, options) {
|
|
4784
|
+
if (!cur.tool?.execute) {
|
|
4785
|
+
console.error("[EXULU] Tool execute function is undefined.", cur.tool);
|
|
4786
|
+
throw new Error("Tool execute function is undefined.");
|
|
4787
|
+
}
|
|
4788
|
+
if (config) {
|
|
4789
|
+
config = await hydrateVariables(config || []);
|
|
4790
|
+
}
|
|
4791
|
+
let upload = void 0;
|
|
4792
|
+
if (exuluConfig?.fileUploads?.s3endpoint && exuluConfig?.fileUploads?.s3key && exuluConfig?.fileUploads?.s3secret && exuluConfig?.fileUploads?.s3Bucket) {
|
|
4793
|
+
s3Client2 ??= new S3Client2({
|
|
4794
|
+
region: exuluConfig?.fileUploads?.s3region,
|
|
4795
|
+
...exuluConfig?.fileUploads?.s3endpoint && {
|
|
4796
|
+
forcePathStyle: true,
|
|
4797
|
+
endpoint: exuluConfig?.fileUploads?.s3endpoint
|
|
4798
|
+
},
|
|
4799
|
+
credentials: {
|
|
4800
|
+
accessKeyId: exuluConfig?.fileUploads?.s3key ?? "",
|
|
4801
|
+
secretAccessKey: exuluConfig?.fileUploads?.s3secret ?? ""
|
|
4802
|
+
}
|
|
4671
4803
|
});
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
|
|
4686
|
-
|
|
4804
|
+
upload = async ({
|
|
4805
|
+
name,
|
|
4806
|
+
data,
|
|
4807
|
+
type,
|
|
4808
|
+
tags
|
|
4809
|
+
}) => {
|
|
4810
|
+
const mime = getMimeType(type);
|
|
4811
|
+
const prefix = exuluConfig?.fileUploads?.s3prefix ? `${exuluConfig.fileUploads.s3prefix.replace(/\/$/, "")}/` : "";
|
|
4812
|
+
const key = `${prefix}${user}/${generateS3Key(name)}${type}`;
|
|
4813
|
+
const command = new PutObjectCommand2({
|
|
4814
|
+
Bucket: exuluConfig?.fileUploads?.s3Bucket,
|
|
4815
|
+
Key: key,
|
|
4816
|
+
Body: data,
|
|
4817
|
+
ContentType: mime
|
|
4818
|
+
});
|
|
4819
|
+
try {
|
|
4820
|
+
const response2 = await s3Client2.send(command);
|
|
4821
|
+
console.log(response2);
|
|
4822
|
+
return response2;
|
|
4823
|
+
} catch (caught) {
|
|
4824
|
+
if (caught instanceof S3ServiceException && caught.name === "EntityTooLarge") {
|
|
4825
|
+
console.error(
|
|
4826
|
+
`[EXULU] Error from S3 while uploading object to ${exuluConfig?.fileUploads?.s3Bucket}. The object was too large. To upload objects larger than 5GB, use the S3 console (160GB max) or the multipart upload API (5TB max).`
|
|
4827
|
+
);
|
|
4828
|
+
} else if (caught instanceof S3ServiceException) {
|
|
4829
|
+
console.error(
|
|
4830
|
+
`[EXULU] Error from S3 while uploading object to ${exuluConfig?.fileUploads?.s3Bucket}. ${caught.name}: ${caught.message}`
|
|
4831
|
+
);
|
|
4832
|
+
} else {
|
|
4833
|
+
throw caught;
|
|
4834
|
+
}
|
|
4687
4835
|
}
|
|
4688
|
-
}
|
|
4689
|
-
}
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
acc[curr.id] = curr;
|
|
4693
|
-
return acc;
|
|
4694
|
-
}, {});
|
|
4695
|
-
console.log("[EXULU] Config", config);
|
|
4696
|
-
const response = await cur.tool.execute({
|
|
4697
|
-
...inputs,
|
|
4698
|
-
// Convert config to object format if a config object
|
|
4699
|
-
// is available, after we added the .value property
|
|
4700
|
-
// by hydrating it from the variables table.
|
|
4701
|
-
providerapikey,
|
|
4702
|
-
allExuluTools,
|
|
4703
|
-
currentTools,
|
|
4704
|
-
user,
|
|
4705
|
-
contexts: contextsMap,
|
|
4706
|
-
upload,
|
|
4707
|
-
config: config ? config.config.reduce((acc, curr) => {
|
|
4708
|
-
acc[curr.name] = curr.value;
|
|
4836
|
+
};
|
|
4837
|
+
}
|
|
4838
|
+
const contextsMap = contexts?.reduce((acc, curr) => {
|
|
4839
|
+
acc[curr.id] = curr;
|
|
4709
4840
|
return acc;
|
|
4710
|
-
}, {})
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4841
|
+
}, {});
|
|
4842
|
+
console.log("[EXULU] Config", config);
|
|
4843
|
+
const response = await cur.tool.execute({
|
|
4844
|
+
...inputs,
|
|
4845
|
+
sessionID,
|
|
4846
|
+
// Convert config to object format if a config object
|
|
4847
|
+
// is available, after we added the .value property
|
|
4848
|
+
// by hydrating it from the variables table.
|
|
4849
|
+
providerapikey,
|
|
4850
|
+
allExuluTools,
|
|
4851
|
+
currentTools,
|
|
4852
|
+
user,
|
|
4853
|
+
contexts: contextsMap,
|
|
4854
|
+
upload,
|
|
4855
|
+
config: config ? config.config.reduce((acc, curr) => {
|
|
4856
|
+
acc[curr.name] = curr.value;
|
|
4857
|
+
return acc;
|
|
4858
|
+
}, {}) : {}
|
|
4859
|
+
}, options);
|
|
4860
|
+
await updateStatistic({
|
|
4861
|
+
name: "count",
|
|
4862
|
+
label: cur.name,
|
|
4863
|
+
type: STATISTICS_TYPE_ENUM.TOOL_CALL,
|
|
4864
|
+
trigger: "agent",
|
|
4865
|
+
count: 1,
|
|
4866
|
+
user: user?.id,
|
|
4867
|
+
role: user?.role?.id
|
|
4868
|
+
});
|
|
4869
|
+
yield response;
|
|
4870
|
+
return response;
|
|
4871
|
+
}
|
|
4723
4872
|
}
|
|
4724
|
-
}
|
|
4725
|
-
}
|
|
4873
|
+
};
|
|
4874
|
+
},
|
|
4726
4875
|
{}
|
|
4727
4876
|
)
|
|
4728
4877
|
// askForConfirmation
|
|
@@ -4731,6 +4880,9 @@ var convertToolsArrayToObject = (currentTools, allExuluTools, configs, providera
|
|
|
4731
4880
|
var hydrateVariables = async (tool2) => {
|
|
4732
4881
|
const { db: db3 } = await postgresClient();
|
|
4733
4882
|
const promises = tool2.config.map(async (toolConfig) => {
|
|
4883
|
+
if (!toolConfig.variable) {
|
|
4884
|
+
return toolConfig;
|
|
4885
|
+
}
|
|
4734
4886
|
const variableName = toolConfig.variable;
|
|
4735
4887
|
const variable = await db3.from("variables").where({ name: variableName }).first();
|
|
4736
4888
|
if (!variable) {
|
|
@@ -4743,6 +4895,7 @@ var hydrateVariables = async (tool2) => {
|
|
|
4743
4895
|
value = bytes.toString(CryptoJS2.enc.Utf8);
|
|
4744
4896
|
}
|
|
4745
4897
|
toolConfig.value = value;
|
|
4898
|
+
return toolConfig;
|
|
4746
4899
|
});
|
|
4747
4900
|
await Promise.all(promises);
|
|
4748
4901
|
console.log("[EXULU] Variable values retrieved and added to tool config.");
|
|
@@ -5000,7 +5153,8 @@ var ExuluAgent2 = class {
|
|
|
5000
5153
|
providerapikey,
|
|
5001
5154
|
contexts,
|
|
5002
5155
|
user,
|
|
5003
|
-
exuluConfig
|
|
5156
|
+
exuluConfig,
|
|
5157
|
+
session
|
|
5004
5158
|
),
|
|
5005
5159
|
stopWhen: [stepCountIs(2)]
|
|
5006
5160
|
});
|
|
@@ -5058,7 +5212,8 @@ var ExuluAgent2 = class {
|
|
|
5058
5212
|
providerapikey,
|
|
5059
5213
|
contexts,
|
|
5060
5214
|
user,
|
|
5061
|
-
exuluConfig
|
|
5215
|
+
exuluConfig,
|
|
5216
|
+
session
|
|
5062
5217
|
),
|
|
5063
5218
|
stopWhen: [stepCountIs(2)]
|
|
5064
5219
|
});
|
|
@@ -5171,7 +5326,8 @@ var ExuluAgent2 = class {
|
|
|
5171
5326
|
providerapikey,
|
|
5172
5327
|
contexts,
|
|
5173
5328
|
user,
|
|
5174
|
-
exuluConfig
|
|
5329
|
+
exuluConfig,
|
|
5330
|
+
session
|
|
5175
5331
|
),
|
|
5176
5332
|
onError: (error) => {
|
|
5177
5333
|
console.error("[EXULU] chat stream error.", error);
|
|
@@ -5196,6 +5352,15 @@ var getAgentMessages = async ({ session, user, limit, page }) => {
|
|
|
5196
5352
|
const messages = await query;
|
|
5197
5353
|
return messages;
|
|
5198
5354
|
};
|
|
5355
|
+
var getSession = async ({ sessionID }) => {
|
|
5356
|
+
const { db: db3 } = await postgresClient();
|
|
5357
|
+
console.log("[EXULU] getting session for session ID: " + sessionID);
|
|
5358
|
+
const session = await db3.from("agent_sessions").where({ id: sessionID }).first();
|
|
5359
|
+
if (!session) {
|
|
5360
|
+
throw new Error("Session not found for session ID: " + sessionID);
|
|
5361
|
+
}
|
|
5362
|
+
return session;
|
|
5363
|
+
};
|
|
5199
5364
|
var saveChat = async ({ session, user, messages }) => {
|
|
5200
5365
|
const { db: db3 } = await postgresClient();
|
|
5201
5366
|
const promises = messages.map((message) => {
|
|
@@ -5963,25 +6128,13 @@ var createExpressRoutes = async (app, agents, tools, contexts, config, evals, tr
|
|
|
5963
6128
|
express.json({ limit: REQUEST_SIZE_LIMIT }),
|
|
5964
6129
|
expressMiddleware(server, {
|
|
5965
6130
|
context: async ({ req }) => {
|
|
5966
|
-
console.info("[EXULU] Incoming graphql request", {
|
|
5967
|
-
message: "Incoming Request",
|
|
5968
|
-
method: req.method,
|
|
5969
|
-
path: req.path,
|
|
5970
|
-
requestId: "req-" + Date.now(),
|
|
5971
|
-
ipAddress: req.ip,
|
|
5972
|
-
userAgent: req.get("User-Agent"),
|
|
5973
|
-
headers: {
|
|
5974
|
-
"authorization": req.headers["authorization"],
|
|
5975
|
-
"exulu-api-key": req.headers["exulu-api-key"],
|
|
5976
|
-
"origin": req.headers["origin"],
|
|
5977
|
-
"...": "..."
|
|
5978
|
-
}
|
|
5979
|
-
});
|
|
5980
6131
|
const authenticationResult = await requestValidators.authenticate(req);
|
|
5981
6132
|
if (!authenticationResult.user?.id) {
|
|
6133
|
+
console.error("[EXULU] Authentication failed", authenticationResult);
|
|
5982
6134
|
throw new Error(authenticationResult.message);
|
|
5983
6135
|
}
|
|
5984
6136
|
const { db: db3 } = await postgresClient();
|
|
6137
|
+
console.log("[EXULU] Graphql call");
|
|
5985
6138
|
return {
|
|
5986
6139
|
req,
|
|
5987
6140
|
db: db3,
|
|
@@ -6595,7 +6748,7 @@ import "ai";
|
|
|
6595
6748
|
import CryptoJS4 from "crypto-js";
|
|
6596
6749
|
|
|
6597
6750
|
// src/registry/log-metadata.ts
|
|
6598
|
-
function
|
|
6751
|
+
function logMetadata2(id, additionalMetadata) {
|
|
6599
6752
|
return {
|
|
6600
6753
|
__logMetadata: true,
|
|
6601
6754
|
id,
|
|
@@ -6606,16 +6759,6 @@ function logMetadata(id, additionalMetadata) {
|
|
|
6606
6759
|
// src/registry/workers.ts
|
|
6607
6760
|
var redisConnection;
|
|
6608
6761
|
var createWorkers = async (agents, queues2, config, contexts, evals, tools, tracer) => {
|
|
6609
|
-
console.log(`
|
|
6610
|
-
\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
|
|
6611
|
-
\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
|
|
6612
|
-
\u2588\u2588\u2588\u2588\u2588\u2557 \u255A\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
|
|
6613
|
-
\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
|
|
6614
|
-
\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2554\u255D \u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D
|
|
6615
|
-
\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D
|
|
6616
|
-
Intelligence Management Platform - Workers
|
|
6617
|
-
|
|
6618
|
-
`);
|
|
6619
6762
|
console.log("[EXULU] creating workers for " + queues2?.length + " queues.");
|
|
6620
6763
|
console.log("[EXULU] queues", queues2.map((q) => q.queue.name));
|
|
6621
6764
|
if (!redisServer.host || !redisServer.port) {
|
|
@@ -6642,7 +6785,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
6642
6785
|
const worker = new Worker(
|
|
6643
6786
|
`${queue.queue.name}`,
|
|
6644
6787
|
async (bullmqJob) => {
|
|
6645
|
-
console.log("[EXULU] starting execution for job",
|
|
6788
|
+
console.log("[EXULU] starting execution for job", logMetadata2(bullmqJob.name, {
|
|
6646
6789
|
name: bullmqJob.name,
|
|
6647
6790
|
status: await bullmqJob.getState(),
|
|
6648
6791
|
type: bullmqJob.data.type
|
|
@@ -6659,7 +6802,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
6659
6802
|
try {
|
|
6660
6803
|
bullmq.validate(bullmqJob.id, data);
|
|
6661
6804
|
if (data.type === "embedder") {
|
|
6662
|
-
console.log("[EXULU] running an embedder job.",
|
|
6805
|
+
console.log("[EXULU] running an embedder job.", logMetadata2(bullmqJob.name));
|
|
6663
6806
|
const label = `embedder-${bullmqJob.name}`;
|
|
6664
6807
|
await db3.from("job_results").insert({
|
|
6665
6808
|
job_id: bullmqJob.id,
|
|
@@ -6689,7 +6832,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
6689
6832
|
};
|
|
6690
6833
|
}
|
|
6691
6834
|
if (data.type === "processor") {
|
|
6692
|
-
console.log("[EXULU] running a processor job.",
|
|
6835
|
+
console.log("[EXULU] running a processor job.", logMetadata2(bullmqJob.name));
|
|
6693
6836
|
const label = `processor-${bullmqJob.name}`;
|
|
6694
6837
|
await db3.from("job_results").insert({
|
|
6695
6838
|
job_id: bullmqJob.id,
|
|
@@ -6736,7 +6879,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
6736
6879
|
};
|
|
6737
6880
|
}
|
|
6738
6881
|
if (data.type === "eval_run") {
|
|
6739
|
-
console.log("[EXULU] running an eval run job.",
|
|
6882
|
+
console.log("[EXULU] running an eval run job.", logMetadata2(bullmqJob.name));
|
|
6740
6883
|
const label = `eval-run-${data.eval_run_id}-${data.test_case_id}`;
|
|
6741
6884
|
const existingResult = await db3.from("job_results").where({ label }).first();
|
|
6742
6885
|
if (existingResult) {
|
|
@@ -6784,7 +6927,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
6784
6927
|
resolve(messages2);
|
|
6785
6928
|
break;
|
|
6786
6929
|
} catch (error) {
|
|
6787
|
-
console.error(`[EXULU] error processing UI messages flow for agent ${agentInstance.name} (${agentInstance.id}).`,
|
|
6930
|
+
console.error(`[EXULU] error processing UI messages flow for agent ${agentInstance.name} (${agentInstance.id}).`, logMetadata2(bullmqJob.name, {
|
|
6788
6931
|
error: error instanceof Error ? error.message : String(error)
|
|
6789
6932
|
}));
|
|
6790
6933
|
attempts++;
|
|
@@ -6843,7 +6986,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
6843
6986
|
eval_function_id: evalFunction.id,
|
|
6844
6987
|
result: result2 || 0
|
|
6845
6988
|
};
|
|
6846
|
-
console.log(`[EXULU] eval function ${evalFunction.id} result: ${result2}`,
|
|
6989
|
+
console.log(`[EXULU] eval function ${evalFunction.id} result: ${result2}`, logMetadata2(bullmqJob.name, {
|
|
6847
6990
|
result: result2 || 0
|
|
6848
6991
|
}));
|
|
6849
6992
|
evalFunctionResults.push(evalFunctionResult);
|
|
@@ -6862,7 +7005,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
6862
7005
|
result: result2 || 0
|
|
6863
7006
|
};
|
|
6864
7007
|
evalFunctionResults.push(evalFunctionResult);
|
|
6865
|
-
console.log(`[EXULU] eval function ${evalFunction.id} result: ${result2}`,
|
|
7008
|
+
console.log(`[EXULU] eval function ${evalFunction.id} result: ${result2}`, logMetadata2(bullmqJob.name, {
|
|
6866
7009
|
result: result2 || 0
|
|
6867
7010
|
}));
|
|
6868
7011
|
}
|
|
@@ -6889,7 +7032,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
6889
7032
|
};
|
|
6890
7033
|
}
|
|
6891
7034
|
if (data.type === "eval_function") {
|
|
6892
|
-
console.log("[EXULU] running an eval function job.",
|
|
7035
|
+
console.log("[EXULU] running an eval function job.", logMetadata2(bullmqJob.name));
|
|
6893
7036
|
if (data.eval_functions?.length !== 1) {
|
|
6894
7037
|
throw new Error(`Expected 1 eval function for eval function job, got ${data.eval_functions?.length}.`);
|
|
6895
7038
|
}
|
|
@@ -6935,7 +7078,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
6935
7078
|
inputMessages,
|
|
6936
7079
|
evalFunction.config || {}
|
|
6937
7080
|
);
|
|
6938
|
-
console.log(`[EXULU] eval function ${evalFunction.id} result: ${result}`,
|
|
7081
|
+
console.log(`[EXULU] eval function ${evalFunction.id} result: ${result}`, logMetadata2(bullmqJob.name, {
|
|
6939
7082
|
result: result || 0
|
|
6940
7083
|
}));
|
|
6941
7084
|
}
|
|
@@ -6945,7 +7088,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
6945
7088
|
};
|
|
6946
7089
|
}
|
|
6947
7090
|
if (data.type === "source") {
|
|
6948
|
-
console.log("[EXULU] running a source job.",
|
|
7091
|
+
console.log("[EXULU] running a source job.", logMetadata2(bullmqJob.name));
|
|
6949
7092
|
if (!data.source) {
|
|
6950
7093
|
throw new Error(`No source id set for source job.`);
|
|
6951
7094
|
}
|
|
@@ -6967,14 +7110,14 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
6967
7110
|
const { item: createdItem, job } = await context.createItem(item, config, data.user, data.role);
|
|
6968
7111
|
if (job) {
|
|
6969
7112
|
jobs.push(job);
|
|
6970
|
-
console.log(`[EXULU] Scheduled job through source update job for item ${createdItem.id} (Job ID: ${job})`,
|
|
7113
|
+
console.log(`[EXULU] Scheduled job through source update job for item ${createdItem.id} (Job ID: ${job})`, logMetadata2(bullmqJob.name, {
|
|
6971
7114
|
item: createdItem,
|
|
6972
7115
|
job
|
|
6973
7116
|
}));
|
|
6974
7117
|
}
|
|
6975
7118
|
if (createdItem.id) {
|
|
6976
7119
|
items.push(createdItem.id);
|
|
6977
|
-
console.log(`[EXULU] created item through source update job ${createdItem.id}`,
|
|
7120
|
+
console.log(`[EXULU] created item through source update job ${createdItem.id}`, logMetadata2(bullmqJob.name, {
|
|
6978
7121
|
item: createdItem
|
|
6979
7122
|
}));
|
|
6980
7123
|
}
|
|
@@ -7009,7 +7152,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7009
7152
|
}
|
|
7010
7153
|
);
|
|
7011
7154
|
worker.on("completed", async (job, returnvalue) => {
|
|
7012
|
-
console.log(`[EXULU] completed job ${job.id}.`,
|
|
7155
|
+
console.log(`[EXULU] completed job ${job.id}.`, logMetadata2(job.name, {
|
|
7013
7156
|
result: returnvalue
|
|
7014
7157
|
}));
|
|
7015
7158
|
const { db: db3 } = await postgresClient();
|
|
@@ -7029,7 +7172,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7029
7172
|
});
|
|
7030
7173
|
return;
|
|
7031
7174
|
}
|
|
7032
|
-
console.error(`[EXULU] job failed.`, job?.name ?
|
|
7175
|
+
console.error(`[EXULU] job failed.`, job?.name ? logMetadata2(job.name, {
|
|
7033
7176
|
error: error instanceof Error ? error.message : String(error)
|
|
7034
7177
|
}) : error);
|
|
7035
7178
|
});
|
|
@@ -7037,7 +7180,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7037
7180
|
console.error(`[EXULU] worker error.`, error);
|
|
7038
7181
|
});
|
|
7039
7182
|
worker.on("progress", (job, progress) => {
|
|
7040
|
-
console.log(`[EXULU] job progress ${job.id}.`,
|
|
7183
|
+
console.log(`[EXULU] job progress ${job.id}.`, logMetadata2(job.name, {
|
|
7041
7184
|
progress
|
|
7042
7185
|
}));
|
|
7043
7186
|
});
|
|
@@ -8689,6 +8832,280 @@ var mathTools = [
|
|
|
8689
8832
|
degreesToRadiansTool
|
|
8690
8833
|
];
|
|
8691
8834
|
|
|
8835
|
+
// src/templates/tools/todo/todowrite.txt
|
|
8836
|
+
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.
|
|
8837
|
+
It also helps the user understand the progress of the task and overall progress of their requests.
|
|
8838
|
+
|
|
8839
|
+
## When to Use This Tool
|
|
8840
|
+
Use this tool proactively in these scenarios:
|
|
8841
|
+
|
|
8842
|
+
1. Complex multi-step tasks - When a task requires 3 or more distinct steps or actions
|
|
8843
|
+
2. Non-trivial and complex tasks - Tasks that require careful planning or multiple operations
|
|
8844
|
+
3. User explicitly requests todo list - When the user directly asks you to use the todo list
|
|
8845
|
+
4. User provides multiple tasks - When users provide a list of things to be done (numbered or comma-separated)
|
|
8846
|
+
5. After receiving new instructions - Immediately capture user requirements as todos. Feel free to edit the todo list based on new information.
|
|
8847
|
+
6. After completing a task - Mark it complete and add any new follow-up tasks
|
|
8848
|
+
7. When you start working on a new task, mark the todo as in_progress. Ideally you should only have one todo as in_progress at a time. Complete existing tasks before starting new ones.
|
|
8849
|
+
|
|
8850
|
+
## When NOT to Use This Tool
|
|
8851
|
+
|
|
8852
|
+
Skip using this tool when:
|
|
8853
|
+
1. There is only a single, straightforward task
|
|
8854
|
+
2. The task is trivial and tracking it provides no organizational benefit
|
|
8855
|
+
3. The task can be completed in less than 3 trivial steps
|
|
8856
|
+
4. The task is purely conversational or informational
|
|
8857
|
+
|
|
8858
|
+
NOTE that you should not use this tool if there is only one trivial task to do. In this case you are better off just doing the task directly.
|
|
8859
|
+
|
|
8860
|
+
## Examples of When to Use the Todo List
|
|
8861
|
+
|
|
8862
|
+
<example>
|
|
8863
|
+
User: I want to add a dark mode toggle to the application settings. Make sure you run the tests and build when you're done!
|
|
8864
|
+
Assistant: I'll help add a dark mode toggle to your application settings. Let me create a todo list to track this implementation.
|
|
8865
|
+
*Creates todo list with the following items:*
|
|
8866
|
+
1. Create dark mode toggle component in Settings page
|
|
8867
|
+
2. Add dark mode state management (context/store)
|
|
8868
|
+
3. Implement CSS-in-JS styles for dark theme
|
|
8869
|
+
4. Update existing components to support theme switching
|
|
8870
|
+
5. Run tests and build process, addressing any failures or errors that occur
|
|
8871
|
+
*Begins working on the first task*
|
|
8872
|
+
|
|
8873
|
+
<reasoning>
|
|
8874
|
+
The assistant used the todo list because:
|
|
8875
|
+
1. Adding dark mode is a multi-step feature requiring UI, state management, and styling changes
|
|
8876
|
+
2. The user explicitly requested tests and build be run afterward
|
|
8877
|
+
3. The assistant inferred that tests and build need to pass by adding "Ensure tests and build succeed" as the final task
|
|
8878
|
+
</reasoning>
|
|
8879
|
+
</example>
|
|
8880
|
+
|
|
8881
|
+
<example>
|
|
8882
|
+
User: Help me rename the function getCwd to getCurrentWorkingDirectory across my project
|
|
8883
|
+
Assistant: Let me first search through your codebase to find all occurrences of 'getCwd'.
|
|
8884
|
+
*Uses grep or search tools to locate all instances of getCwd in the codebase*
|
|
8885
|
+
Assistant: I've found 15 instances of 'getCwd' across 8 different files. Let me create a todo list to track these changes.
|
|
8886
|
+
*Creates todo list with specific items for each file that needs updating*
|
|
8887
|
+
|
|
8888
|
+
<reasoning>
|
|
8889
|
+
The assistant used the todo list because:
|
|
8890
|
+
1. First, the assistant searched to understand the scope of the task
|
|
8891
|
+
2. Upon finding multiple occurrences across different files, it determined this was a complex task with multiple steps
|
|
8892
|
+
3. The todo list helps ensure every instance is tracked and updated systematically
|
|
8893
|
+
4. This approach prevents missing any occurrences and maintains code consistency
|
|
8894
|
+
</reasoning>
|
|
8895
|
+
</example>
|
|
8896
|
+
|
|
8897
|
+
|
|
8898
|
+
<example>
|
|
8899
|
+
User: I need to implement these features for my e-commerce site: user registration, product catalog, shopping cart, and checkout flow.
|
|
8900
|
+
Assistant: I'll help implement these features. First, let's add all the features to the todo list.
|
|
8901
|
+
*Creates a todo list breaking down each feature into specific tasks based on the project architecture*
|
|
8902
|
+
Assistant: Let's start with user registration. This will involve creating database models, API endpoints, and frontend forms that integrate with your existing authentication system.
|
|
8903
|
+
|
|
8904
|
+
<reasoning>
|
|
8905
|
+
The assistant used the todo list because:
|
|
8906
|
+
1. The user provided multiple complex features to implement in a comma separated list
|
|
8907
|
+
2. The todo list helps organize these large features into manageable tasks
|
|
8908
|
+
3. This approach allows for tracking progress across the entire implementation
|
|
8909
|
+
</reasoning>
|
|
8910
|
+
</example>
|
|
8911
|
+
|
|
8912
|
+
<example>
|
|
8913
|
+
User: Can you help optimize my React application? It's rendering slowly and has performance issues.</user>
|
|
8914
|
+
Assistant: I'll help optimize your React application. First, let me examine your codebase to identify potential performance bottlenecks.
|
|
8915
|
+
*Reviews component structure, render patterns, state management, and data fetching*
|
|
8916
|
+
Assistant: After analyzing your codebase, I've identified several performance issues. Let me create a todo list to track our optimization efforts.
|
|
8917
|
+
*Creates todo list with items like: 1) Implement memoization for expensive calculations in ProductList, 2) Add virtualization for long lists in Dashboard, 3) Optimize image loading in Gallery component, 4) Fix state update loops in ShoppingCart, 5) Review bundle size and implement code splitting*
|
|
8918
|
+
Let's start by implementing memoization for the expensive calculations in your ProductList component.</assistant>
|
|
8919
|
+
|
|
8920
|
+
<reasoning>
|
|
8921
|
+
The assistant used the todo list because:
|
|
8922
|
+
1. First, the assistant examined the codebase to identify specific performance issues
|
|
8923
|
+
2. Based on this analysis, it identified multiple optimization opportunities
|
|
8924
|
+
3. Performance optimization is a non-trivial task requiring multiple steps
|
|
8925
|
+
4. The todo list helps methodically track improvements across different components
|
|
8926
|
+
5. This systematic approach ensures all performance bottlenecks are addressed
|
|
8927
|
+
</reasoning>
|
|
8928
|
+
</example>
|
|
8929
|
+
|
|
8930
|
+
## Examples of When NOT to Use the Todo List
|
|
8931
|
+
|
|
8932
|
+
<example>
|
|
8933
|
+
User: How do I print 'Hello World' in Python?
|
|
8934
|
+
Assistant: In Python, you can print "Hello World" with this simple code:
|
|
8935
|
+
|
|
8936
|
+
python
|
|
8937
|
+
print("Hello World")
|
|
8938
|
+
|
|
8939
|
+
This will output the text "Hello World" to the console when executed.</assistant>
|
|
8940
|
+
|
|
8941
|
+
<reasoning>
|
|
8942
|
+
The assistant did not use the todo list because this is a single, trivial task that can be completed in one step. There's no need to track multiple tasks or steps for such a straightforward request.
|
|
8943
|
+
</reasoning>
|
|
8944
|
+
</example>
|
|
8945
|
+
|
|
8946
|
+
<example>
|
|
8947
|
+
User: What does the git status command do?
|
|
8948
|
+
Assistant: The git status command shows the current state of your working directory and staging area. It displays which changes have been staged, which haven't, and which files aren't being tracked by Git. This is a fundamental command for understanding the current state of your repository before making commits.
|
|
8949
|
+
|
|
8950
|
+
<reasoning>
|
|
8951
|
+
The assistant did not use the todo list because this is an informational request with no actual coding task to complete. The user is simply asking for an explanation, not for the assistant to perform multiple steps or tasks.
|
|
8952
|
+
</reasoning>
|
|
8953
|
+
</example>
|
|
8954
|
+
|
|
8955
|
+
<example>
|
|
8956
|
+
User: Can you add a comment to the calculateTotal function to explain what it does?
|
|
8957
|
+
Assistant: Sure, let me add a comment to the calculateTotal function to explain what it does.
|
|
8958
|
+
* Uses the Edit tool to add a comment to the calculateTotal function *
|
|
8959
|
+
|
|
8960
|
+
<reasoning>
|
|
8961
|
+
The assistant did not use the todo list because this is a single, straightforward task confined to one location in the code. Adding a comment doesn't require tracking multiple steps or systematic organization.
|
|
8962
|
+
</reasoning>
|
|
8963
|
+
</example>
|
|
8964
|
+
|
|
8965
|
+
<example>
|
|
8966
|
+
User: Run npm install for me and tell me what happens.
|
|
8967
|
+
Assistant: I'll run the npm install command for you.
|
|
8968
|
+
|
|
8969
|
+
*Executes: npm install*
|
|
8970
|
+
|
|
8971
|
+
The command completed successfully. Here's the output:
|
|
8972
|
+
[Output of npm install command]
|
|
8973
|
+
|
|
8974
|
+
All dependencies have been installed according to your package.json file.
|
|
8975
|
+
|
|
8976
|
+
<reasoning>
|
|
8977
|
+
The assistant did not use the todo list because this is a single command execution with immediate results. There are no multiple steps to track or organize, making the todo list unnecessary for this straightforward task.
|
|
8978
|
+
</reasoning>
|
|
8979
|
+
</example>
|
|
8980
|
+
|
|
8981
|
+
## Task States and Management
|
|
8982
|
+
|
|
8983
|
+
1. **Task States**: Use these states to track progress:
|
|
8984
|
+
- pending: Task not yet started
|
|
8985
|
+
- in_progress: Currently working on (limit to ONE task at a time)
|
|
8986
|
+
- completed: Task finished successfully
|
|
8987
|
+
- cancelled: Task no longer needed
|
|
8988
|
+
|
|
8989
|
+
2. **Task Management**:
|
|
8990
|
+
- Update task status in real-time as you work
|
|
8991
|
+
- Mark tasks complete IMMEDIATELY after finishing (don't batch completions)
|
|
8992
|
+
- Only have ONE task in_progress at any time
|
|
8993
|
+
- Complete current tasks before starting new ones
|
|
8994
|
+
- Cancel tasks that become irrelevant
|
|
8995
|
+
|
|
8996
|
+
3. **Task Breakdown**:
|
|
8997
|
+
- Create specific, actionable items
|
|
8998
|
+
- Break complex tasks into smaller, manageable steps
|
|
8999
|
+
- Use clear, descriptive task names
|
|
9000
|
+
|
|
9001
|
+
When in doubt, use this tool. Being proactive with task management demonstrates attentiveness and ensures you complete all requirements successfully.
|
|
9002
|
+
`;
|
|
9003
|
+
|
|
9004
|
+
// src/templates/tools/todo/todoread.txt
|
|
9005
|
+
var todoread_default = `Use this tool to read the current to-do list for the session. This tool should be used proactively and frequently to ensure that you are aware of
|
|
9006
|
+
the status of the current task list. You should make use of this tool as often as possible, especially in the following situations:
|
|
9007
|
+
- At the beginning of conversations to see what's pending
|
|
9008
|
+
- Before starting new tasks to prioritize work
|
|
9009
|
+
- When the user asks about previous tasks or plans
|
|
9010
|
+
- Whenever you're uncertain about what to do next
|
|
9011
|
+
- After completing tasks to update your understanding of remaining work
|
|
9012
|
+
- After every few messages to ensure you're on track
|
|
9013
|
+
|
|
9014
|
+
Usage:
|
|
9015
|
+
- This tool takes in no parameters. So leave the input blank or empty. DO NOT include a dummy object, placeholder string or a key like "input" or "empty". LEAVE IT BLANK.
|
|
9016
|
+
- Returns a list of todo items with their status, priority, and content
|
|
9017
|
+
- Use this information to track progress and plan next steps
|
|
9018
|
+
- If no todos exist yet, an empty list will be returned`;
|
|
9019
|
+
|
|
9020
|
+
// src/templates/tools/todo/todo.ts
|
|
9021
|
+
import z5 from "zod";
|
|
9022
|
+
var TodoSchema = z5.object({
|
|
9023
|
+
content: z5.string().describe("Brief description of the task"),
|
|
9024
|
+
status: z5.string().describe("Current status of the task: pending, in_progress, completed, cancelled"),
|
|
9025
|
+
priority: z5.string().describe("Priority level of the task: high, medium, low"),
|
|
9026
|
+
id: z5.string().describe("Unique identifier for the todo item")
|
|
9027
|
+
});
|
|
9028
|
+
var TodoWriteTool = new ExuluTool2({
|
|
9029
|
+
id: "todo_write",
|
|
9030
|
+
name: "Todo Write",
|
|
9031
|
+
description: "Use this tool to write your todo list",
|
|
9032
|
+
type: "function",
|
|
9033
|
+
category: "todo",
|
|
9034
|
+
config: [{
|
|
9035
|
+
name: "description",
|
|
9036
|
+
description: "The description of the todo list, if set overwrites the default description.",
|
|
9037
|
+
default: todowrite_default
|
|
9038
|
+
}],
|
|
9039
|
+
inputSchema: z5.object({
|
|
9040
|
+
todos: z5.array(TodoSchema).describe("The updated todo list")
|
|
9041
|
+
}),
|
|
9042
|
+
execute: async (inputs) => {
|
|
9043
|
+
const { sessionID, todos, user } = inputs;
|
|
9044
|
+
if (!user) {
|
|
9045
|
+
throw new Error("No authenticated user available, a user is required for the todo write tool, this likely means the tool was called outside a session like in an MCP or API call instead of as part of an authenticated session.");
|
|
9046
|
+
}
|
|
9047
|
+
if (!sessionID) {
|
|
9048
|
+
throw new Error("Session ID is required for the todo write tool, this likely means the tool was called outside a session like in an MCP or API call instead of as part of a conversation.");
|
|
9049
|
+
}
|
|
9050
|
+
const session = await getSession({ sessionID });
|
|
9051
|
+
if (!session?.id) {
|
|
9052
|
+
throw new Error("Session with ID " + sessionID + " not found in the todo write tool.");
|
|
9053
|
+
}
|
|
9054
|
+
const hasAccessToSession = await checkRecordAccess(session, "read", user);
|
|
9055
|
+
if (!hasAccessToSession) {
|
|
9056
|
+
throw new Error("You don't have access to this session " + session.id + ".");
|
|
9057
|
+
}
|
|
9058
|
+
await updateTodos({
|
|
9059
|
+
session,
|
|
9060
|
+
todos
|
|
9061
|
+
});
|
|
9062
|
+
return {
|
|
9063
|
+
result: JSON.stringify(todos, null, 2)
|
|
9064
|
+
};
|
|
9065
|
+
}
|
|
9066
|
+
});
|
|
9067
|
+
var TodoReadTool = new ExuluTool2({
|
|
9068
|
+
id: "todo_read",
|
|
9069
|
+
name: "Todo Read",
|
|
9070
|
+
description: "Use this tool to read your todo list",
|
|
9071
|
+
inputSchema: z5.object({}),
|
|
9072
|
+
type: "function",
|
|
9073
|
+
category: "todo",
|
|
9074
|
+
config: [{
|
|
9075
|
+
name: "description",
|
|
9076
|
+
description: "The description of the todo list, if set overwrites the default description.",
|
|
9077
|
+
default: todoread_default
|
|
9078
|
+
}],
|
|
9079
|
+
execute: async (inputs) => {
|
|
9080
|
+
const { sessionID } = inputs;
|
|
9081
|
+
let todos = await getTodos(sessionID);
|
|
9082
|
+
return {
|
|
9083
|
+
result: JSON.stringify(todos, null, 2)
|
|
9084
|
+
};
|
|
9085
|
+
}
|
|
9086
|
+
});
|
|
9087
|
+
async function updateTodos(input) {
|
|
9088
|
+
const metadata = input.session.metadata || {};
|
|
9089
|
+
metadata["todos"] = input.todos;
|
|
9090
|
+
const { db: db3 } = await postgresClient();
|
|
9091
|
+
await db3.from("agent_sessions").where({ id: input.session.id }).update({
|
|
9092
|
+
metadata
|
|
9093
|
+
});
|
|
9094
|
+
return input.session;
|
|
9095
|
+
}
|
|
9096
|
+
async function getTodos(sessionID) {
|
|
9097
|
+
const { db: db3 } = await postgresClient();
|
|
9098
|
+
const session = await db3.from("agent_sessions").where({ id: sessionID }).first();
|
|
9099
|
+
if (!session) {
|
|
9100
|
+
throw new Error("Session not found for session ID: " + sessionID);
|
|
9101
|
+
}
|
|
9102
|
+
return session.metadata?.todos || [];
|
|
9103
|
+
}
|
|
9104
|
+
var todoTools = [
|
|
9105
|
+
TodoWriteTool,
|
|
9106
|
+
TodoReadTool
|
|
9107
|
+
];
|
|
9108
|
+
|
|
8692
9109
|
// src/registry/index.ts
|
|
8693
9110
|
var isDev = process.env.NODE_ENV !== "production";
|
|
8694
9111
|
var consoleTransport = new winston2.transports.Console({
|
|
@@ -8761,6 +9178,7 @@ var ExuluApp = class {
|
|
|
8761
9178
|
this._tools = [
|
|
8762
9179
|
...tools ?? [],
|
|
8763
9180
|
...mathTools,
|
|
9181
|
+
...todoTools,
|
|
8764
9182
|
// Add contexts as tools
|
|
8765
9183
|
...Object.values(contexts || {}).map((context) => context.tool())
|
|
8766
9184
|
// Because agents are stored in the database, we add those as tools
|
|
@@ -8872,6 +9290,16 @@ var ExuluApp = class {
|
|
|
8872
9290
|
bullmq = {
|
|
8873
9291
|
workers: {
|
|
8874
9292
|
create: async (queues2) => {
|
|
9293
|
+
console.log(`
|
|
9294
|
+
\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
|
|
9295
|
+
\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
|
|
9296
|
+
\u2588\u2588\u2588\u2588\u2588\u2557 \u255A\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
|
|
9297
|
+
\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
|
|
9298
|
+
\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2554\u255D \u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D
|
|
9299
|
+
\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D
|
|
9300
|
+
Intelligence Management Platform - Workers
|
|
9301
|
+
|
|
9302
|
+
`);
|
|
8875
9303
|
if (!this._config) {
|
|
8876
9304
|
throw new Error("Config not initialized, make sure to call await ExuluApp.create() first when starting your server.");
|
|
8877
9305
|
}
|
|
@@ -8893,16 +9321,26 @@ var ExuluApp = class {
|
|
|
8893
9321
|
if (queues2) {
|
|
8894
9322
|
filteredQueues = filteredQueues.filter((q) => queues2.includes(q.queue.name));
|
|
8895
9323
|
}
|
|
8896
|
-
const
|
|
8897
|
-
|
|
8898
|
-
|
|
8899
|
-
|
|
9324
|
+
const contexts = Object.values(this._contexts ?? {});
|
|
9325
|
+
let sources = [];
|
|
9326
|
+
for (const context of contexts) {
|
|
9327
|
+
for (const source of context.sources) {
|
|
9328
|
+
sources.push({
|
|
9329
|
+
...source,
|
|
9330
|
+
context: context.id
|
|
9331
|
+
});
|
|
9332
|
+
}
|
|
9333
|
+
}
|
|
8900
9334
|
if (sources.length > 0) {
|
|
8901
9335
|
console.log("[EXULU] Creating ContextSource schedulers for", sources.length, "sources.");
|
|
8902
9336
|
for (const source of sources) {
|
|
8903
9337
|
const queue = await source.config?.queue;
|
|
9338
|
+
if (!queue) {
|
|
9339
|
+
console.warn("[EXULU] No queue configured for source", source.name);
|
|
9340
|
+
continue;
|
|
9341
|
+
}
|
|
8904
9342
|
if (queue) {
|
|
8905
|
-
if (!source.config
|
|
9343
|
+
if (!source.config?.schedule) {
|
|
8906
9344
|
throw new Error("Schedule is required for source when configuring a queue: " + source.name);
|
|
8907
9345
|
}
|
|
8908
9346
|
console.log("[EXULU] Creating ContextSource scheduler for", source.name, "in queue", queue.queue?.name);
|
|
@@ -8914,7 +9352,8 @@ var ExuluApp = class {
|
|
|
8914
9352
|
data: {
|
|
8915
9353
|
source: source.id,
|
|
8916
9354
|
context: source.context,
|
|
8917
|
-
type: "source"
|
|
9355
|
+
type: "source",
|
|
9356
|
+
inputs: {}
|
|
8918
9357
|
},
|
|
8919
9358
|
opts: {
|
|
8920
9359
|
backoff: {
|
|
@@ -10337,20 +10776,39 @@ var execute = async ({ contexts }) => {
|
|
|
10337
10776
|
await up(db3);
|
|
10338
10777
|
await contextDatabases(contexts);
|
|
10339
10778
|
console.log("[EXULU] Inserting default user and admin role.");
|
|
10340
|
-
const
|
|
10341
|
-
|
|
10342
|
-
|
|
10343
|
-
|
|
10779
|
+
const existingAdminRole = await db3.from("roles").where({ name: "admin" }).first();
|
|
10780
|
+
const existingDefaultRole = await db3.from("roles").where({ name: "default" }).first();
|
|
10781
|
+
let adminRoleId;
|
|
10782
|
+
let defaultRoleId;
|
|
10783
|
+
if (!existingAdminRole) {
|
|
10784
|
+
console.log("[EXULU] Creating admin role.");
|
|
10344
10785
|
const role = await db3.from("roles").insert({
|
|
10345
10786
|
name: "admin",
|
|
10346
10787
|
agents: "write",
|
|
10788
|
+
api: "write",
|
|
10347
10789
|
workflows: "write",
|
|
10348
10790
|
variables: "write",
|
|
10349
|
-
users: "write"
|
|
10791
|
+
users: "write",
|
|
10792
|
+
evals: "write"
|
|
10350
10793
|
}).returning("id");
|
|
10351
|
-
|
|
10794
|
+
adminRoleId = role[0].id;
|
|
10352
10795
|
} else {
|
|
10353
|
-
|
|
10796
|
+
adminRoleId = existingAdminRole.id;
|
|
10797
|
+
}
|
|
10798
|
+
if (!existingDefaultRole) {
|
|
10799
|
+
console.log("[EXULU] Creating default role.");
|
|
10800
|
+
const role = await db3.from("roles").insert({
|
|
10801
|
+
name: "default",
|
|
10802
|
+
agents: "write",
|
|
10803
|
+
api: "read",
|
|
10804
|
+
workflows: "read",
|
|
10805
|
+
variables: "read",
|
|
10806
|
+
users: "read",
|
|
10807
|
+
evals: "read"
|
|
10808
|
+
}).returning("id");
|
|
10809
|
+
defaultRoleId = role[0].id;
|
|
10810
|
+
} else {
|
|
10811
|
+
defaultRoleId = existingDefaultRole.id;
|
|
10354
10812
|
}
|
|
10355
10813
|
const existingUser = await db3.from("users").where({ email: "admin@exulu.com" }).first();
|
|
10356
10814
|
if (!existingUser) {
|
|
@@ -10365,7 +10823,7 @@ var execute = async ({ contexts }) => {
|
|
|
10365
10823
|
updatedAt: /* @__PURE__ */ new Date(),
|
|
10366
10824
|
password,
|
|
10367
10825
|
type: "user",
|
|
10368
|
-
role:
|
|
10826
|
+
role: adminRoleId
|
|
10369
10827
|
});
|
|
10370
10828
|
}
|
|
10371
10829
|
const { key } = await generateApiKey("exulu", "api@exulu.com");
|
|
@@ -10593,5 +11051,5 @@ export {
|
|
|
10593
11051
|
ExuluUtils,
|
|
10594
11052
|
ExuluVariables,
|
|
10595
11053
|
db2 as db,
|
|
10596
|
-
logMetadata
|
|
11054
|
+
logMetadata2 as logMetadata
|
|
10597
11055
|
};
|