@exulu/backend 1.38.0 → 1.39.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 +414 -111
- package/dist/index.d.cts +56 -12
- package/dist/index.d.ts +56 -12
- package/dist/index.js +414 -111
- package/package.json +1 -1
- package/types/models/agent-session.ts +1 -0
- package/types/models/user.ts +2 -0
package/dist/index.cjs
CHANGED
|
@@ -219,11 +219,17 @@ async function postgresClient() {
|
|
|
219
219
|
pool: {
|
|
220
220
|
min: 2,
|
|
221
221
|
max: 20,
|
|
222
|
+
// Increased from 20 to handle more concurrent operations
|
|
222
223
|
acquireTimeoutMillis: 3e4,
|
|
223
224
|
createTimeoutMillis: 3e4,
|
|
224
225
|
idleTimeoutMillis: 3e4,
|
|
225
226
|
reapIntervalMillis: 1e3,
|
|
226
|
-
createRetryIntervalMillis: 200
|
|
227
|
+
createRetryIntervalMillis: 200,
|
|
228
|
+
// Log pool events to help debug connection issues
|
|
229
|
+
afterCreate: (conn, done) => {
|
|
230
|
+
console.log("[EXULU] New database connection created");
|
|
231
|
+
done(null, conn);
|
|
232
|
+
}
|
|
227
233
|
}
|
|
228
234
|
});
|
|
229
235
|
try {
|
|
@@ -504,6 +510,8 @@ var authentication = async ({
|
|
|
504
510
|
type: "api",
|
|
505
511
|
id: 192837465,
|
|
506
512
|
email: "internal@exulu.com",
|
|
513
|
+
firstname: "API",
|
|
514
|
+
lastname: "User",
|
|
507
515
|
role: {
|
|
508
516
|
id: "internal",
|
|
509
517
|
name: "Internal",
|
|
@@ -1337,10 +1345,10 @@ var rbacSchema = {
|
|
|
1337
1345
|
name: "user_id",
|
|
1338
1346
|
type: "number"
|
|
1339
1347
|
},
|
|
1340
|
-
{
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
},
|
|
1348
|
+
/* {
|
|
1349
|
+
name: "project_id",
|
|
1350
|
+
type: "uuid"
|
|
1351
|
+
}, */
|
|
1344
1352
|
{
|
|
1345
1353
|
name: "rights",
|
|
1346
1354
|
type: "text",
|
|
@@ -2250,7 +2258,7 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2250
2258
|
});
|
|
2251
2259
|
return {
|
|
2252
2260
|
// Filter result to only include requested fields
|
|
2253
|
-
item: finalizeRequestedFields({ table, requestedFields, agents, contexts, tools, result: results[0], user: context.user }),
|
|
2261
|
+
item: finalizeRequestedFields({ args, table, requestedFields, agents, contexts, tools, result: results[0], user: context.user }),
|
|
2254
2262
|
job
|
|
2255
2263
|
};
|
|
2256
2264
|
},
|
|
@@ -2298,7 +2306,7 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2298
2306
|
}
|
|
2299
2307
|
const { job } = await postprocessUpdate({ table, requestedFields, agents, contexts, tools, result, user: context.user.id, role: context.user.role?.id, config });
|
|
2300
2308
|
return {
|
|
2301
|
-
item: finalizeRequestedFields({ table, requestedFields, agents, contexts, tools, result, user: context.user.id }),
|
|
2309
|
+
item: finalizeRequestedFields({ args, table, requestedFields, agents, contexts, tools, result, user: context.user.id }),
|
|
2302
2310
|
job
|
|
2303
2311
|
};
|
|
2304
2312
|
},
|
|
@@ -2339,7 +2347,7 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2339
2347
|
const result = await db3.from(tableNamePlural).select(Object.keys(columns)).where({ id }).first();
|
|
2340
2348
|
const { job } = await postprocessUpdate({ table, requestedFields, agents, contexts, tools, result, user: context.user.id, role: context.user.role?.id, config });
|
|
2341
2349
|
return {
|
|
2342
|
-
item: finalizeRequestedFields({ table, requestedFields, agents, contexts, tools, result, user: context.user.id }),
|
|
2350
|
+
item: finalizeRequestedFields({ args, table, requestedFields, agents, contexts, tools, result, user: context.user.id }),
|
|
2343
2351
|
job
|
|
2344
2352
|
};
|
|
2345
2353
|
},
|
|
@@ -2371,7 +2379,7 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2371
2379
|
}).del();
|
|
2372
2380
|
}
|
|
2373
2381
|
await postprocessDeletion({ table, requestedFields, agents, contexts, tools, result });
|
|
2374
|
-
return finalizeRequestedFields({ table, requestedFields, agents, contexts, tools, result, user: context.user.id });
|
|
2382
|
+
return finalizeRequestedFields({ args, table, requestedFields, agents, contexts, tools, result, user: context.user.id });
|
|
2375
2383
|
},
|
|
2376
2384
|
[`${tableNamePlural}RemoveOne`]: async (_, args, context, info) => {
|
|
2377
2385
|
const { where } = args;
|
|
@@ -2395,7 +2403,7 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2395
2403
|
}
|
|
2396
2404
|
await db3(tableNamePlural).where(where).del();
|
|
2397
2405
|
await postprocessDeletion({ table, requestedFields, agents, contexts, tools, result });
|
|
2398
|
-
return finalizeRequestedFields({ table, requestedFields, agents, contexts, tools, result, user: context.user.id });
|
|
2406
|
+
return finalizeRequestedFields({ args, table, requestedFields, agents, contexts, tools, result, user: context.user.id });
|
|
2399
2407
|
}
|
|
2400
2408
|
};
|
|
2401
2409
|
if (table.type === "items") {
|
|
@@ -2428,20 +2436,20 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2428
2436
|
}
|
|
2429
2437
|
const { db: db3 } = context;
|
|
2430
2438
|
let query = db3.from(tableNamePlural).select("*").where({ id: args.item });
|
|
2431
|
-
query = applyAccessControl(table, context.user
|
|
2439
|
+
query = applyAccessControl(table, query, context.user);
|
|
2432
2440
|
const item = await query.first();
|
|
2433
2441
|
if (!item) {
|
|
2434
2442
|
throw new Error("Item not found, or your user does not have access to it.");
|
|
2435
2443
|
}
|
|
2436
2444
|
const { job, result } = await exists.processField(
|
|
2437
2445
|
"api",
|
|
2438
|
-
context.user.id,
|
|
2439
|
-
context.user.role?.id,
|
|
2440
2446
|
{
|
|
2441
2447
|
...item,
|
|
2442
2448
|
field: args.field
|
|
2443
2449
|
},
|
|
2444
|
-
config
|
|
2450
|
+
config,
|
|
2451
|
+
context.user.id,
|
|
2452
|
+
context.user.role?.id
|
|
2445
2453
|
);
|
|
2446
2454
|
return {
|
|
2447
2455
|
message: job ? "Processing job scheduled." : "Item processed successfully.",
|
|
@@ -2488,7 +2496,10 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2488
2496
|
};
|
|
2489
2497
|
}
|
|
2490
2498
|
console.log("[EXULU] Executing source function directly");
|
|
2491
|
-
const result = await source.execute(
|
|
2499
|
+
const result = await source.execute({
|
|
2500
|
+
...args.inputs,
|
|
2501
|
+
exuluConfig: config
|
|
2502
|
+
});
|
|
2492
2503
|
let jobs = [];
|
|
2493
2504
|
let items = [];
|
|
2494
2505
|
for (const item of result) {
|
|
@@ -2612,14 +2623,14 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2612
2623
|
}
|
|
2613
2624
|
return mutations;
|
|
2614
2625
|
}
|
|
2615
|
-
var applyAccessControl = (table,
|
|
2626
|
+
var applyAccessControl = (table, query, user) => {
|
|
2616
2627
|
const tableNamePlural = table.name.plural.toLowerCase();
|
|
2617
|
-
if (table.name.plural !== "agent_sessions" && user
|
|
2628
|
+
if (table.name.plural !== "agent_sessions" && user?.super_admin === true) {
|
|
2618
2629
|
return query;
|
|
2619
2630
|
}
|
|
2620
|
-
console.log("[EXULU] user.role", user
|
|
2631
|
+
console.log("[EXULU] user.role", user?.role);
|
|
2621
2632
|
console.log("[EXULU] table.name.plural", table.name.plural);
|
|
2622
|
-
if (!user
|
|
2633
|
+
if (!user?.super_admin && (!user?.role || !(table.name.plural === "agents" && (user.role.agents === "read" || user.role.agents === "write")) && !(table.name.plural === "workflow_templates" && (user.role.workflows === "read" || user.role.workflows === "write")) && !(table.name.plural === "variables" && (user.role.variables === "read" || user.role.variables === "write")) && !(table.name.plural === "users" && (user.role.users === "read" || user.role.users === "write")) && !((table.name.plural === "test_cases" || table.name.plural === "eval_sets" || table.name.plural === "eval_runs") && (user.role.evals === "read" || user.role.evals === "write")))) {
|
|
2623
2634
|
console.error("==== Access control error: no role found or no access to entity type. ====");
|
|
2624
2635
|
throw new Error("Access control error: no role found or no access to entity type.");
|
|
2625
2636
|
}
|
|
@@ -2708,7 +2719,7 @@ var removeAgentFields = (requestedFields) => {
|
|
|
2708
2719
|
filtered.push("backend");
|
|
2709
2720
|
return filtered;
|
|
2710
2721
|
};
|
|
2711
|
-
var addAgentFields = async (requestedFields, agents, result, tools, user) => {
|
|
2722
|
+
var addAgentFields = async (args, requestedFields, agents, result, tools, user, contexts) => {
|
|
2712
2723
|
let backend = agents.find((a) => a.id === result?.backend);
|
|
2713
2724
|
if (requestedFields.includes("providerName")) {
|
|
2714
2725
|
result.providerName = backend?.providerName || "";
|
|
@@ -2755,6 +2766,17 @@ var addAgentFields = async (requestedFields, agents, result, tools, user) => {
|
|
|
2755
2766
|
console.log("[EXULU] hydratedTool", hydratedTool);
|
|
2756
2767
|
return hydratedTool;
|
|
2757
2768
|
}));
|
|
2769
|
+
if (args.project) {
|
|
2770
|
+
const projectTool = await createProjectRetrievalTool({
|
|
2771
|
+
projectId: args.project,
|
|
2772
|
+
user,
|
|
2773
|
+
role: user.role?.id,
|
|
2774
|
+
contexts
|
|
2775
|
+
});
|
|
2776
|
+
if (projectTool) {
|
|
2777
|
+
result.tools.unshift(projectTool);
|
|
2778
|
+
}
|
|
2779
|
+
}
|
|
2758
2780
|
result.tools = result.tools.filter((tool2) => tool2 !== null);
|
|
2759
2781
|
} else {
|
|
2760
2782
|
result.tools = [];
|
|
@@ -2893,6 +2915,7 @@ var postprocessDeletion = async ({
|
|
|
2893
2915
|
return result;
|
|
2894
2916
|
};
|
|
2895
2917
|
var finalizeRequestedFields = async ({
|
|
2918
|
+
args,
|
|
2896
2919
|
table,
|
|
2897
2920
|
requestedFields,
|
|
2898
2921
|
agents,
|
|
@@ -2909,11 +2932,28 @@ var finalizeRequestedFields = async ({
|
|
|
2909
2932
|
}
|
|
2910
2933
|
if (Array.isArray(result)) {
|
|
2911
2934
|
result = result.map((item) => {
|
|
2912
|
-
return finalizeRequestedFields({
|
|
2935
|
+
return finalizeRequestedFields({
|
|
2936
|
+
args,
|
|
2937
|
+
table,
|
|
2938
|
+
requestedFields,
|
|
2939
|
+
agents,
|
|
2940
|
+
contexts,
|
|
2941
|
+
tools,
|
|
2942
|
+
result: item,
|
|
2943
|
+
user
|
|
2944
|
+
});
|
|
2913
2945
|
});
|
|
2914
2946
|
} else {
|
|
2915
2947
|
if (table.name.singular === "agent") {
|
|
2916
|
-
result = await addAgentFields(
|
|
2948
|
+
result = await addAgentFields(
|
|
2949
|
+
args,
|
|
2950
|
+
requestedFields,
|
|
2951
|
+
agents,
|
|
2952
|
+
result,
|
|
2953
|
+
tools,
|
|
2954
|
+
user,
|
|
2955
|
+
contexts
|
|
2956
|
+
);
|
|
2917
2957
|
if (!requestedFields.includes("backend")) {
|
|
2918
2958
|
delete result.backend;
|
|
2919
2959
|
}
|
|
@@ -2990,18 +3030,18 @@ function createQueries(table, agents, tools, contexts) {
|
|
|
2990
3030
|
const requestedFields = getRequestedFields(info);
|
|
2991
3031
|
const sanitizedFields = sanitizeRequestedFields(table, requestedFields);
|
|
2992
3032
|
let query = db3.from(tableNamePlural).select(sanitizedFields).where({ id: args.id });
|
|
2993
|
-
query = applyAccessControl(table, context.user
|
|
3033
|
+
query = applyAccessControl(table, query, context.user);
|
|
2994
3034
|
let result = await query.first();
|
|
2995
|
-
return finalizeRequestedFields({ table, requestedFields, agents, contexts, tools, result, user: context.user });
|
|
3035
|
+
return finalizeRequestedFields({ args, table, requestedFields, agents, contexts, tools, result, user: context.user });
|
|
2996
3036
|
},
|
|
2997
3037
|
[`${tableNameSingular}ByIds`]: async (_, args, context, info) => {
|
|
2998
3038
|
const { db: db3 } = context;
|
|
2999
3039
|
const requestedFields = getRequestedFields(info);
|
|
3000
3040
|
const sanitizedFields = sanitizeRequestedFields(table, requestedFields);
|
|
3001
3041
|
let query = db3.from(tableNamePlural).select(sanitizedFields).whereIn("id", args.ids);
|
|
3002
|
-
query = applyAccessControl(table, context.user
|
|
3042
|
+
query = applyAccessControl(table, query, context.user);
|
|
3003
3043
|
let result = await query;
|
|
3004
|
-
return finalizeRequestedFields({ table, requestedFields, agents, contexts, tools, result, user: context.user });
|
|
3044
|
+
return finalizeRequestedFields({ args, table, requestedFields, agents, contexts, tools, result, user: context.user });
|
|
3005
3045
|
},
|
|
3006
3046
|
[`${tableNameSingular}One`]: async (_, args, context, info) => {
|
|
3007
3047
|
const { filters = [], sort } = args;
|
|
@@ -3010,10 +3050,10 @@ function createQueries(table, agents, tools, contexts) {
|
|
|
3010
3050
|
const sanitizedFields = sanitizeRequestedFields(table, requestedFields);
|
|
3011
3051
|
let query = db3.from(tableNamePlural).select(sanitizedFields);
|
|
3012
3052
|
query = applyFilters(query, filters, table);
|
|
3013
|
-
query = applyAccessControl(table, context.user
|
|
3053
|
+
query = applyAccessControl(table, query, context.user);
|
|
3014
3054
|
query = applySorting(query, sort);
|
|
3015
3055
|
let result = await query.first();
|
|
3016
|
-
return finalizeRequestedFields({ table, requestedFields, agents, contexts, tools, result, user: context.user });
|
|
3056
|
+
return finalizeRequestedFields({ args, table, requestedFields, agents, contexts, tools, result, user: context.user });
|
|
3017
3057
|
},
|
|
3018
3058
|
[`${tableNamePlural}Pagination`]: async (_, args, context, info) => {
|
|
3019
3059
|
const { limit = 10, page = 0, filters = [], sort } = args;
|
|
@@ -3023,7 +3063,7 @@ function createQueries(table, agents, tools, contexts) {
|
|
|
3023
3063
|
}
|
|
3024
3064
|
let countQuery = db3(tableNamePlural);
|
|
3025
3065
|
countQuery = applyFilters(countQuery, filters, table);
|
|
3026
|
-
countQuery = applyAccessControl(table, context.user
|
|
3066
|
+
countQuery = applyAccessControl(table, countQuery, context.user);
|
|
3027
3067
|
const countResult = await countQuery.count("* as count");
|
|
3028
3068
|
const itemCount = Number(countResult[0]?.count || 0);
|
|
3029
3069
|
const pageCount = Math.ceil(itemCount / limit);
|
|
@@ -3032,7 +3072,7 @@ function createQueries(table, agents, tools, contexts) {
|
|
|
3032
3072
|
const hasNextPage = currentPage < pageCount - 1;
|
|
3033
3073
|
let dataQuery = db3(tableNamePlural);
|
|
3034
3074
|
dataQuery = applyFilters(dataQuery, filters, table);
|
|
3035
|
-
dataQuery = applyAccessControl(table, context.user
|
|
3075
|
+
dataQuery = applyAccessControl(table, dataQuery, context.user);
|
|
3036
3076
|
const requestedFields = getRequestedFields(info);
|
|
3037
3077
|
dataQuery = applySorting(dataQuery, sort);
|
|
3038
3078
|
if (page > 1) {
|
|
@@ -3048,7 +3088,7 @@ function createQueries(table, agents, tools, contexts) {
|
|
|
3048
3088
|
hasPreviousPage,
|
|
3049
3089
|
hasNextPage
|
|
3050
3090
|
},
|
|
3051
|
-
items: finalizeRequestedFields({ table, requestedFields, agents, contexts, tools, result: items, user: context.user })
|
|
3091
|
+
items: finalizeRequestedFields({ args, table, requestedFields, agents, contexts, tools, result: items, user: context.user })
|
|
3052
3092
|
};
|
|
3053
3093
|
},
|
|
3054
3094
|
// Add generic statistics query for all tables
|
|
@@ -3057,7 +3097,7 @@ function createQueries(table, agents, tools, contexts) {
|
|
|
3057
3097
|
const { db: db3 } = context;
|
|
3058
3098
|
let query = db3(tableNamePlural);
|
|
3059
3099
|
query = applyFilters(query, filters, table);
|
|
3060
|
-
query = applyAccessControl(table, context.user
|
|
3100
|
+
query = applyAccessControl(table, query, context.user);
|
|
3061
3101
|
if (groupBy) {
|
|
3062
3102
|
query = query.select(groupBy).groupBy(groupBy);
|
|
3063
3103
|
if (tableNamePlural === "tracking") {
|
|
@@ -3157,11 +3197,11 @@ var vectorSearch = async ({
|
|
|
3157
3197
|
const chunksTable = getChunksTableName(id);
|
|
3158
3198
|
let countQuery = db3(mainTable);
|
|
3159
3199
|
countQuery = applyFilters(countQuery, filters, table);
|
|
3160
|
-
countQuery = applyAccessControl(table,
|
|
3200
|
+
countQuery = applyAccessControl(table, countQuery, user);
|
|
3161
3201
|
const columns = await db3(mainTable).columnInfo();
|
|
3162
3202
|
let itemsQuery = db3(mainTable).select(Object.keys(columns).map((column) => mainTable + "." + column));
|
|
3163
3203
|
itemsQuery = applyFilters(itemsQuery, filters, table);
|
|
3164
|
-
itemsQuery = applyAccessControl(table,
|
|
3204
|
+
itemsQuery = applyAccessControl(table, itemsQuery, user);
|
|
3165
3205
|
itemsQuery = applySorting(itemsQuery, sort);
|
|
3166
3206
|
if (queryRewriter) {
|
|
3167
3207
|
query = await queryRewriter(query);
|
|
@@ -3180,7 +3220,7 @@ var vectorSearch = async ({
|
|
|
3180
3220
|
const { chunks } = await embedder.generateFromQuery(context.id, query, {
|
|
3181
3221
|
label: table.name.singular,
|
|
3182
3222
|
trigger
|
|
3183
|
-
}, user
|
|
3223
|
+
}, user?.id, role);
|
|
3184
3224
|
if (!chunks?.[0]?.vector) {
|
|
3185
3225
|
throw new Error("No vector generated for query.");
|
|
3186
3226
|
}
|
|
@@ -3570,7 +3610,8 @@ function createSDL(tables, contexts, agents, tools, config, evals, queues2) {
|
|
|
3570
3610
|
const tableNameSingularUpperCaseFirst = table.name.singular.charAt(0).toUpperCase() + table.name.singular.slice(1);
|
|
3571
3611
|
const processorFields = table.fields.filter((field) => field.processor?.execute);
|
|
3572
3612
|
typeDefs += `
|
|
3573
|
-
|
|
3613
|
+
${tableNameSingular === "agent" ? `${tableNameSingular}ById(id: ID!, project: ID): ${tableNameSingular}` : `${tableNameSingular}ById(id: ID!): ${tableNameSingular}`}
|
|
3614
|
+
|
|
3574
3615
|
${tableNameSingular}ByIds(ids: [ID!]!): [${tableNameSingular}]!
|
|
3575
3616
|
${tableNamePlural}Pagination(limit: Int, page: Int, filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingularUpperCaseFirst}PaginationResult
|
|
3576
3617
|
${tableNameSingular}One(filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingular}
|
|
@@ -4483,16 +4524,27 @@ var addPrefixToKey = (keyPath, config) => {
|
|
|
4483
4524
|
}
|
|
4484
4525
|
return `${prefix}/${keyPath}`;
|
|
4485
4526
|
};
|
|
4486
|
-
var uploadFile = async (
|
|
4487
|
-
console.log("[EXULU] Uploading file to S3", key);
|
|
4527
|
+
var uploadFile = async (file, key, config, options = {}, user) => {
|
|
4488
4528
|
if (!config.fileUploads) {
|
|
4489
|
-
throw new Error("File uploads are not configured");
|
|
4529
|
+
throw new Error("File uploads are not configured (in the exported uploadFile function)");
|
|
4490
4530
|
}
|
|
4491
4531
|
const client2 = getS3Client(config);
|
|
4492
|
-
let
|
|
4532
|
+
let defaultBucket = config.fileUploads.s3Bucket;
|
|
4533
|
+
let customBucket = false;
|
|
4534
|
+
if (key.includes("[bucket:")) {
|
|
4535
|
+
console.log("[EXULU] key includes [bucket:name]", key);
|
|
4536
|
+
customBucket = key.split("[bucket:")[1]?.split("]")[0] || "";
|
|
4537
|
+
if (!customBucket?.length) {
|
|
4538
|
+
throw new Error("Invalid key, does not contain a bucket name like '[bucket:name]'.");
|
|
4539
|
+
}
|
|
4540
|
+
key = key.split("]")[1] || "";
|
|
4541
|
+
console.log("[EXULU] custom bucket", customBucket);
|
|
4542
|
+
}
|
|
4543
|
+
let folder = user ? `${user}/` : "";
|
|
4493
4544
|
const fullKey = addPrefixToKey(!key.includes(folder) ? folder + key : key, config);
|
|
4545
|
+
console.log("[EXULU] uploading file to s3 into bucket", customBucket || defaultBucket, "with key", fullKey);
|
|
4494
4546
|
const command = new import_client_s3.PutObjectCommand({
|
|
4495
|
-
Bucket:
|
|
4547
|
+
Bucket: customBucket || defaultBucket,
|
|
4496
4548
|
Key: fullKey,
|
|
4497
4549
|
Body: file,
|
|
4498
4550
|
ContentType: options.contentType,
|
|
@@ -4500,6 +4552,10 @@ var uploadFile = async (user, file, key, config, options = {}) => {
|
|
|
4500
4552
|
ContentLength: file.byteLength
|
|
4501
4553
|
});
|
|
4502
4554
|
await client2.send(command);
|
|
4555
|
+
console.log("[EXULU] file uploaded to s3 into bucket", customBucket || defaultBucket, "with key", fullKey);
|
|
4556
|
+
if (customBucket) {
|
|
4557
|
+
return "[bucket:" + customBucket + "]" + fullKey;
|
|
4558
|
+
}
|
|
4503
4559
|
return fullKey;
|
|
4504
4560
|
};
|
|
4505
4561
|
var createUppyRoutes = async (app, config) => {
|
|
@@ -4658,16 +4714,12 @@ var createUppyRoutes = async (app, config) => {
|
|
|
4658
4714
|
key = key.split("]")[1] || "";
|
|
4659
4715
|
console.log("[EXULU] key", key);
|
|
4660
4716
|
}
|
|
4661
|
-
console.log("[EXULU] Getting object metadata from s3", key);
|
|
4662
|
-
console.log("[EXULU] bucket", bucket);
|
|
4663
|
-
console.log("[EXULU] key", key);
|
|
4664
4717
|
const client2 = getS3Client(config);
|
|
4665
4718
|
const command = new import_client_s3.HeadObjectCommand({
|
|
4666
4719
|
Bucket: bucket,
|
|
4667
4720
|
Key: key
|
|
4668
4721
|
});
|
|
4669
4722
|
const response = await client2.send(command);
|
|
4670
|
-
console.log("[EXULU] Object metadata from s3", response);
|
|
4671
4723
|
res.json(response);
|
|
4672
4724
|
res.end();
|
|
4673
4725
|
});
|
|
@@ -4982,12 +5034,117 @@ function sanitizeToolName(name) {
|
|
|
4982
5034
|
}
|
|
4983
5035
|
return sanitized;
|
|
4984
5036
|
}
|
|
4985
|
-
var
|
|
5037
|
+
var projectsCache = /* @__PURE__ */ new Map();
|
|
5038
|
+
var createProjectRetrievalTool = async ({
|
|
5039
|
+
user,
|
|
5040
|
+
role,
|
|
5041
|
+
contexts,
|
|
5042
|
+
projectId
|
|
5043
|
+
}) => {
|
|
5044
|
+
let project;
|
|
5045
|
+
const cachedProject = projectsCache.get(projectId);
|
|
5046
|
+
const OneMinuteAgo = new Date(Date.now() - 1e3 * 60);
|
|
5047
|
+
if (cachedProject && cachedProject.age > OneMinuteAgo) {
|
|
5048
|
+
project = cachedProject.project;
|
|
5049
|
+
} else {
|
|
5050
|
+
const { db: db3 } = await postgresClient();
|
|
5051
|
+
project = await db3.from("projects").where("id", projectId).first();
|
|
5052
|
+
if (project) {
|
|
5053
|
+
projectsCache.set(projectId, {
|
|
5054
|
+
age: /* @__PURE__ */ new Date(),
|
|
5055
|
+
project
|
|
5056
|
+
});
|
|
5057
|
+
} else {
|
|
5058
|
+
return;
|
|
5059
|
+
}
|
|
5060
|
+
}
|
|
5061
|
+
console.log("[EXULU] Project search tool created for project", project);
|
|
5062
|
+
if (!project.project_items?.length) {
|
|
5063
|
+
return;
|
|
5064
|
+
}
|
|
5065
|
+
const projectRetrievalTool = new ExuluTool2({
|
|
5066
|
+
id: "project_information_retrieval_tool_" + projectId,
|
|
5067
|
+
name: "Project information retrieval tool for project " + project.name,
|
|
5068
|
+
description: "This tool retrieves information about a project from conversations and items that were added to the project " + project.name + ".",
|
|
5069
|
+
inputSchema: import_zod.z.object({
|
|
5070
|
+
query: import_zod.z.string().describe("The query to retrieve information about the project " + project.name + "."),
|
|
5071
|
+
keywords: import_zod.z.array(import_zod.z.string()).describe("The most relevant keywords in the query, such as names of people, companies, products, etc. in the project " + project.name + ".")
|
|
5072
|
+
}),
|
|
5073
|
+
type: "function",
|
|
5074
|
+
category: "project",
|
|
5075
|
+
config: [],
|
|
5076
|
+
execute: async ({ query, keywords }) => {
|
|
5077
|
+
console.log("[EXULU] Project search tool searching for project", project);
|
|
5078
|
+
const items = project.project_items;
|
|
5079
|
+
const set = {};
|
|
5080
|
+
for (const item of items) {
|
|
5081
|
+
const context = item.split("/")[0];
|
|
5082
|
+
if (!context) {
|
|
5083
|
+
throw new Error("The item added to the project does not have a valid gid with the context id as the prefix before the first slash.");
|
|
5084
|
+
}
|
|
5085
|
+
const id = item.split("/")[1];
|
|
5086
|
+
if (set[context]) {
|
|
5087
|
+
set[context].push(id);
|
|
5088
|
+
} else {
|
|
5089
|
+
set[context] = [id];
|
|
5090
|
+
}
|
|
5091
|
+
}
|
|
5092
|
+
console.log("[EXULU] Project search tool searching through contexts", Object.keys(set));
|
|
5093
|
+
const results = await Promise.all(Object.keys(set).map(async (contextName, index) => {
|
|
5094
|
+
const context = contexts.find((context2) => context2.id === contextName);
|
|
5095
|
+
if (!context) {
|
|
5096
|
+
console.error("[EXULU] Context not found for project information retrieval tool.", contextName);
|
|
5097
|
+
return [];
|
|
5098
|
+
}
|
|
5099
|
+
const itemIds = set[contextName];
|
|
5100
|
+
console.log("[EXULU] Project search tool searching through items", itemIds);
|
|
5101
|
+
return await context.search({
|
|
5102
|
+
// todo check if it is more performant to use a concatenation of
|
|
5103
|
+
// the query and keywords, or just the keywords, instead of the
|
|
5104
|
+
// query itself.
|
|
5105
|
+
query,
|
|
5106
|
+
filters: [{
|
|
5107
|
+
id: {
|
|
5108
|
+
in: itemIds
|
|
5109
|
+
}
|
|
5110
|
+
}],
|
|
5111
|
+
user,
|
|
5112
|
+
role,
|
|
5113
|
+
method: "hybridSearch",
|
|
5114
|
+
sort: {
|
|
5115
|
+
field: "updatedAt",
|
|
5116
|
+
direction: "desc"
|
|
5117
|
+
},
|
|
5118
|
+
trigger: "tool",
|
|
5119
|
+
limit: 10,
|
|
5120
|
+
page: 1
|
|
5121
|
+
});
|
|
5122
|
+
}));
|
|
5123
|
+
console.log("[EXULU] Project search tool results", results);
|
|
5124
|
+
return {
|
|
5125
|
+
result: JSON.stringify(results.flat())
|
|
5126
|
+
};
|
|
5127
|
+
}
|
|
5128
|
+
});
|
|
5129
|
+
return projectRetrievalTool;
|
|
5130
|
+
};
|
|
5131
|
+
var convertToolsArrayToObject = async (currentTools, allExuluTools, configs, providerapikey, contexts, user, exuluConfig, sessionID, req, project) => {
|
|
4986
5132
|
if (!currentTools) return {};
|
|
4987
5133
|
if (!allExuluTools) return {};
|
|
4988
5134
|
if (!contexts) {
|
|
4989
5135
|
contexts = [];
|
|
4990
5136
|
}
|
|
5137
|
+
if (project) {
|
|
5138
|
+
const projectRetrievalTool = await createProjectRetrievalTool({
|
|
5139
|
+
user,
|
|
5140
|
+
role: user?.role?.id,
|
|
5141
|
+
contexts,
|
|
5142
|
+
projectId: project
|
|
5143
|
+
});
|
|
5144
|
+
if (projectRetrievalTool) {
|
|
5145
|
+
currentTools.push(projectRetrievalTool);
|
|
5146
|
+
}
|
|
5147
|
+
}
|
|
4991
5148
|
const sanitizedTools = currentTools ? currentTools.map((tool2) => ({
|
|
4992
5149
|
...tool2,
|
|
4993
5150
|
name: sanitizeToolName(tool2.name)
|
|
@@ -4996,9 +5153,9 @@ var convertToolsArrayToObject = (currentTools, allExuluTools, configs, providera
|
|
|
4996
5153
|
return {
|
|
4997
5154
|
...sanitizedTools?.reduce(
|
|
4998
5155
|
(prev, cur) => {
|
|
4999
|
-
let
|
|
5000
|
-
const userDefinedConfigDescription =
|
|
5001
|
-
const defaultConfigDescription =
|
|
5156
|
+
let toolVariableConfig = configs?.find((config) => config.id === cur.id);
|
|
5157
|
+
const userDefinedConfigDescription = toolVariableConfig?.config.find((config) => config.name === "description")?.value;
|
|
5158
|
+
const defaultConfigDescription = toolVariableConfig?.config.find((config) => config.name === "description")?.default;
|
|
5002
5159
|
const toolDescription = cur.description;
|
|
5003
5160
|
const description = userDefinedConfigDescription || defaultConfigDescription || toolDescription;
|
|
5004
5161
|
return {
|
|
@@ -5011,8 +5168,8 @@ var convertToolsArrayToObject = (currentTools, allExuluTools, configs, providera
|
|
|
5011
5168
|
console.error("[EXULU] Tool execute function is undefined.", cur.tool);
|
|
5012
5169
|
throw new Error("Tool execute function is undefined.");
|
|
5013
5170
|
}
|
|
5014
|
-
if (
|
|
5015
|
-
|
|
5171
|
+
if (toolVariableConfig) {
|
|
5172
|
+
toolVariableConfig = await hydrateVariables(toolVariableConfig || []);
|
|
5016
5173
|
}
|
|
5017
5174
|
let upload = void 0;
|
|
5018
5175
|
if (exuluConfig?.fileUploads?.s3endpoint && exuluConfig?.fileUploads?.s3key && exuluConfig?.fileUploads?.s3secret && exuluConfig?.fileUploads?.s3Bucket) {
|
|
@@ -5065,7 +5222,6 @@ var convertToolsArrayToObject = (currentTools, allExuluTools, configs, providera
|
|
|
5065
5222
|
acc[curr.id] = curr;
|
|
5066
5223
|
return acc;
|
|
5067
5224
|
}, {});
|
|
5068
|
-
console.log("[EXULU] Config", config);
|
|
5069
5225
|
const response = await cur.tool.execute({
|
|
5070
5226
|
...inputs,
|
|
5071
5227
|
sessionID,
|
|
@@ -5080,7 +5236,7 @@ var convertToolsArrayToObject = (currentTools, allExuluTools, configs, providera
|
|
|
5080
5236
|
contexts: contextsMap,
|
|
5081
5237
|
upload,
|
|
5082
5238
|
exuluConfig,
|
|
5083
|
-
|
|
5239
|
+
toolVariablesConfig: toolVariableConfig ? toolVariableConfig.config.reduce((acc, curr) => {
|
|
5084
5240
|
acc[curr.name] = curr.value;
|
|
5085
5241
|
return acc;
|
|
5086
5242
|
}, {}) : {}
|
|
@@ -5114,7 +5270,6 @@ var hydrateVariables = async (tool2) => {
|
|
|
5114
5270
|
const variableName = toolConfig.variable;
|
|
5115
5271
|
const variable = await db3.from("variables").where({ name: variableName }).first();
|
|
5116
5272
|
if (!variable) {
|
|
5117
|
-
console.error("[EXULU] Variable " + variableName + " not found.");
|
|
5118
5273
|
throw new Error("Variable " + variableName + " not found.");
|
|
5119
5274
|
}
|
|
5120
5275
|
let value = variable.value;
|
|
@@ -5126,7 +5281,6 @@ var hydrateVariables = async (tool2) => {
|
|
|
5126
5281
|
return toolConfig;
|
|
5127
5282
|
});
|
|
5128
5283
|
await Promise.all(promises);
|
|
5129
|
-
console.log("[EXULU] Variable values retrieved and added to tool config.");
|
|
5130
5284
|
return tool2;
|
|
5131
5285
|
};
|
|
5132
5286
|
function generateSlug(name) {
|
|
@@ -5350,7 +5504,21 @@ var ExuluAgent2 = class {
|
|
|
5350
5504
|
});
|
|
5351
5505
|
}
|
|
5352
5506
|
console.log("[EXULU] Message count for agent: " + this.name, "loaded for generating sync.", messages.length);
|
|
5353
|
-
|
|
5507
|
+
let project;
|
|
5508
|
+
if (session) {
|
|
5509
|
+
const sessionData = await getSession({ sessionID: session });
|
|
5510
|
+
project = sessionData.project;
|
|
5511
|
+
}
|
|
5512
|
+
const personalizationInformation = exuluConfig?.privacy?.systemPromptPersonalization !== false ? `
|
|
5513
|
+
${user?.firstname ? `The users first name is "${user.firstname}"` : ""}
|
|
5514
|
+
${user?.lastname ? `The users last name is "${user.lastname}"` : ""}
|
|
5515
|
+
${user?.email ? `The users email is "${user.email}"` : ""}
|
|
5516
|
+
` : "";
|
|
5517
|
+
const genericContext = `IMPORTANT general information:
|
|
5518
|
+
${personalizationInformation}
|
|
5519
|
+
The current date is "${(/* @__PURE__ */ new Date()).toLocaleDateString()}" and the current time is "${(/* @__PURE__ */ new Date()).toLocaleTimeString()}".
|
|
5520
|
+
If the user does not explicitly provide the current date, for examle when saying ' this weekend', you should assume
|
|
5521
|
+
they are talking with the current date in mind as a reference.`;
|
|
5354
5522
|
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.";
|
|
5355
5523
|
system += "\n\n" + genericContext;
|
|
5356
5524
|
if (prompt) {
|
|
@@ -5375,7 +5543,7 @@ var ExuluAgent2 = class {
|
|
|
5375
5543
|
system,
|
|
5376
5544
|
prompt,
|
|
5377
5545
|
maxRetries: 2,
|
|
5378
|
-
tools: convertToolsArrayToObject(
|
|
5546
|
+
tools: await convertToolsArrayToObject(
|
|
5379
5547
|
currentTools,
|
|
5380
5548
|
allExuluTools,
|
|
5381
5549
|
toolConfigs,
|
|
@@ -5384,7 +5552,8 @@ var ExuluAgent2 = class {
|
|
|
5384
5552
|
user,
|
|
5385
5553
|
exuluConfig,
|
|
5386
5554
|
session,
|
|
5387
|
-
req
|
|
5555
|
+
req,
|
|
5556
|
+
project
|
|
5388
5557
|
),
|
|
5389
5558
|
stopWhen: [(0, import_ai.stepCountIs)(2)]
|
|
5390
5559
|
});
|
|
@@ -5435,7 +5604,7 @@ var ExuluAgent2 = class {
|
|
|
5435
5604
|
ignoreIncompleteToolCalls: true
|
|
5436
5605
|
}),
|
|
5437
5606
|
maxRetries: 2,
|
|
5438
|
-
tools: convertToolsArrayToObject(
|
|
5607
|
+
tools: await convertToolsArrayToObject(
|
|
5439
5608
|
currentTools,
|
|
5440
5609
|
allExuluTools,
|
|
5441
5610
|
toolConfigs,
|
|
@@ -5444,7 +5613,8 @@ var ExuluAgent2 = class {
|
|
|
5444
5613
|
user,
|
|
5445
5614
|
exuluConfig,
|
|
5446
5615
|
session,
|
|
5447
|
-
req
|
|
5616
|
+
req,
|
|
5617
|
+
project
|
|
5448
5618
|
),
|
|
5449
5619
|
stopWhen: [(0, import_ai.stepCountIs)(2)]
|
|
5450
5620
|
});
|
|
@@ -5494,7 +5664,6 @@ var ExuluAgent2 = class {
|
|
|
5494
5664
|
*/
|
|
5495
5665
|
async processFilePartsInMessages(messages) {
|
|
5496
5666
|
const processedMessages = await Promise.all(messages.map(async (message) => {
|
|
5497
|
-
console.log("[EXULU] Processing file parts in messages: " + JSON.stringify(message, null, 2));
|
|
5498
5667
|
if (message.role !== "user" || !Array.isArray(message.parts)) {
|
|
5499
5668
|
return message;
|
|
5500
5669
|
}
|
|
@@ -5505,7 +5674,7 @@ var ExuluAgent2 = class {
|
|
|
5505
5674
|
const { mediaType, url, filename } = part;
|
|
5506
5675
|
const imageTypes = ["image/png", "image/jpeg", "image/jpg", "image/gif", "image/webp"];
|
|
5507
5676
|
if (imageTypes.includes(mediaType)) {
|
|
5508
|
-
console.log(`[EXULU] Converting file part to image part: ${filename}`);
|
|
5677
|
+
console.log(`[EXULU] Converting file part to image part: ${filename} `);
|
|
5509
5678
|
return {
|
|
5510
5679
|
type: "image",
|
|
5511
5680
|
image: url,
|
|
@@ -5516,7 +5685,7 @@ var ExuluAgent2 = class {
|
|
|
5516
5685
|
try {
|
|
5517
5686
|
const response = await fetch(url);
|
|
5518
5687
|
if (!response.ok) {
|
|
5519
|
-
console.error(`[EXULU] Failed to fetch file: ${filename}, status: ${response.status}`);
|
|
5688
|
+
console.error(`[EXULU] Failed to fetch file: ${filename}, status: ${response.status} `);
|
|
5520
5689
|
return {
|
|
5521
5690
|
type: "text",
|
|
5522
5691
|
text: `[Error: Could not load file ${filename}]`
|
|
@@ -5529,9 +5698,9 @@ var ExuluAgent2 = class {
|
|
|
5529
5698
|
});
|
|
5530
5699
|
return {
|
|
5531
5700
|
type: "text",
|
|
5532
|
-
text: `<file name="${filename}">
|
|
5533
|
-
${extractedText}
|
|
5534
|
-
</file>`
|
|
5701
|
+
text: `<file file name = "${filename}" >
|
|
5702
|
+
${extractedText}
|
|
5703
|
+
</file>`
|
|
5535
5704
|
};
|
|
5536
5705
|
} catch (error) {
|
|
5537
5706
|
console.error(`[EXULU] Error processing file ${filename}:`, error);
|
|
@@ -5581,7 +5750,10 @@ ${extractedText}
|
|
|
5581
5750
|
});
|
|
5582
5751
|
let messages = [];
|
|
5583
5752
|
let previousMessagesContent = previousMessages || [];
|
|
5753
|
+
let project;
|
|
5584
5754
|
if (session) {
|
|
5755
|
+
const sessionData = await getSession({ sessionID: session });
|
|
5756
|
+
project = sessionData.project;
|
|
5585
5757
|
console.log("[EXULU] loading previous messages from session: " + session);
|
|
5586
5758
|
const previousMessages2 = await getAgentMessages({
|
|
5587
5759
|
session,
|
|
@@ -5600,20 +5772,18 @@ ${extractedText}
|
|
|
5600
5772
|
messages = messages.filter(
|
|
5601
5773
|
(message2, index, self) => index === self.findIndex((t) => t.id === message2.id)
|
|
5602
5774
|
);
|
|
5603
|
-
console.log("[EXULU] Processing file parts in messages for OpenAI Responses API compatibility");
|
|
5604
5775
|
messages = await this.processFilePartsInMessages(messages);
|
|
5605
5776
|
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.";
|
|
5606
5777
|
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.";
|
|
5607
5778
|
system += "\n\n" + genericContext;
|
|
5608
|
-
console.log("[EXULU] tools for agent: " + this.name, currentTools?.map((x) => x.name + " (" + x.id + ")"));
|
|
5609
|
-
console.log("[EXULU] system", system.slice(0, 100) + "...");
|
|
5610
5779
|
const result = (0, import_ai.streamText)({
|
|
5611
5780
|
model,
|
|
5612
5781
|
// Should be a LanguageModelV1
|
|
5613
5782
|
messages: (0, import_ai.convertToModelMessages)(messages, {
|
|
5614
5783
|
ignoreIncompleteToolCalls: true
|
|
5615
5784
|
}),
|
|
5616
|
-
//
|
|
5785
|
+
// PrepareStep could be used here to set the model
|
|
5786
|
+
// for the first step or change other parameters.
|
|
5617
5787
|
system,
|
|
5618
5788
|
maxRetries: 2,
|
|
5619
5789
|
providerOptions: {
|
|
@@ -5621,7 +5791,7 @@ ${extractedText}
|
|
|
5621
5791
|
reasoningSummary: "auto"
|
|
5622
5792
|
}
|
|
5623
5793
|
},
|
|
5624
|
-
tools: convertToolsArrayToObject(
|
|
5794
|
+
tools: await convertToolsArrayToObject(
|
|
5625
5795
|
currentTools,
|
|
5626
5796
|
allExuluTools,
|
|
5627
5797
|
toolConfigs,
|
|
@@ -5630,7 +5800,8 @@ ${extractedText}
|
|
|
5630
5800
|
user,
|
|
5631
5801
|
exuluConfig,
|
|
5632
5802
|
session,
|
|
5633
|
-
req
|
|
5803
|
+
req,
|
|
5804
|
+
project
|
|
5634
5805
|
),
|
|
5635
5806
|
onError: (error) => {
|
|
5636
5807
|
console.error("[EXULU] chat stream error.", error);
|
|
@@ -5726,12 +5897,10 @@ var ExuluEmbedder = class {
|
|
|
5726
5897
|
id
|
|
5727
5898
|
} = setting;
|
|
5728
5899
|
let value = "";
|
|
5729
|
-
console.log("[EXULU] variable name", variableName);
|
|
5730
5900
|
const variable = await db3.from("variables").where({ name: variableName }).first();
|
|
5731
5901
|
if (!variable) {
|
|
5732
5902
|
throw new Error("Variable not found for embedder setting: " + name + " in context: " + context + " and embedder: " + this.id);
|
|
5733
5903
|
}
|
|
5734
|
-
console.log("[EXULU] variable", variable);
|
|
5735
5904
|
if (variable.encrypted) {
|
|
5736
5905
|
if (!process.env.NEXTAUTH_SECRET) {
|
|
5737
5906
|
throw new Error("NEXTAUTH_SECRET environment variable is not set, cannot decrypt variable: " + name);
|
|
@@ -5743,14 +5912,12 @@ var ExuluEmbedder = class {
|
|
|
5743
5912
|
throw new Error("Decryption returned empty string - invalid key or corrupted data");
|
|
5744
5913
|
}
|
|
5745
5914
|
value = decrypted;
|
|
5746
|
-
console.log("[EXULU] successfully decrypted value for", name);
|
|
5747
5915
|
} catch (error) {
|
|
5748
5916
|
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.`);
|
|
5749
5917
|
}
|
|
5750
5918
|
} else {
|
|
5751
5919
|
value = variable.value;
|
|
5752
5920
|
}
|
|
5753
|
-
console.log("[EXULU] variable value", value);
|
|
5754
5921
|
hydrated.push({
|
|
5755
5922
|
id: id || "",
|
|
5756
5923
|
name,
|
|
@@ -5858,14 +6025,20 @@ var ExuluStorage = class {
|
|
|
5858
6025
|
getPresignedUrl = async (key) => {
|
|
5859
6026
|
return await getPresignedUrl(key, this.config);
|
|
5860
6027
|
};
|
|
5861
|
-
uploadFile = async (
|
|
5862
|
-
return await uploadFile(
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
6028
|
+
uploadFile = async (file, key, type, user, metadata) => {
|
|
6029
|
+
return await uploadFile(
|
|
6030
|
+
file,
|
|
6031
|
+
key,
|
|
6032
|
+
this.config,
|
|
6033
|
+
{
|
|
6034
|
+
contentType: type,
|
|
6035
|
+
metadata: {
|
|
6036
|
+
...metadata,
|
|
6037
|
+
type
|
|
6038
|
+
}
|
|
6039
|
+
},
|
|
6040
|
+
user
|
|
6041
|
+
);
|
|
5869
6042
|
};
|
|
5870
6043
|
// todo add upload and delete methods
|
|
5871
6044
|
};
|
|
@@ -5885,7 +6058,19 @@ var ExuluContext = class {
|
|
|
5885
6058
|
// todo typings
|
|
5886
6059
|
configuration;
|
|
5887
6060
|
sources = [];
|
|
5888
|
-
constructor({
|
|
6061
|
+
constructor({
|
|
6062
|
+
id,
|
|
6063
|
+
name,
|
|
6064
|
+
description,
|
|
6065
|
+
embedder,
|
|
6066
|
+
active,
|
|
6067
|
+
rateLimit,
|
|
6068
|
+
fields,
|
|
6069
|
+
queryRewriter,
|
|
6070
|
+
resultReranker,
|
|
6071
|
+
configuration,
|
|
6072
|
+
sources
|
|
6073
|
+
}) {
|
|
5889
6074
|
this.id = id;
|
|
5890
6075
|
this.name = name;
|
|
5891
6076
|
this.fields = fields || [];
|
|
@@ -5902,7 +6087,7 @@ var ExuluContext = class {
|
|
|
5902
6087
|
this.queryRewriter = queryRewriter;
|
|
5903
6088
|
this.resultReranker = resultReranker;
|
|
5904
6089
|
}
|
|
5905
|
-
processField = async (trigger,
|
|
6090
|
+
processField = async (trigger, item, exuluConfig, user, role) => {
|
|
5906
6091
|
console.log("[EXULU] processing field", item.field, " in context", this.id);
|
|
5907
6092
|
console.log("[EXULU] fields", this.fields.map((field2) => field2.name));
|
|
5908
6093
|
const field = this.fields.find((field2) => {
|
|
@@ -5912,7 +6097,7 @@ var ExuluContext = class {
|
|
|
5912
6097
|
console.error("[EXULU] field not found or processor not set for field", item.field, " in context", this.id);
|
|
5913
6098
|
throw new Error("Field not found or processor not set for field " + item.field + " in context " + this.id);
|
|
5914
6099
|
}
|
|
5915
|
-
const exuluStorage = new ExuluStorage({ config });
|
|
6100
|
+
const exuluStorage = new ExuluStorage({ config: exuluConfig });
|
|
5916
6101
|
const queue = await field.processor.config?.queue;
|
|
5917
6102
|
if (queue?.queue.name) {
|
|
5918
6103
|
console.log("[EXULU] processor is in queue mode, scheduling job.");
|
|
@@ -5938,6 +6123,7 @@ var ExuluContext = class {
|
|
|
5938
6123
|
job: job.id
|
|
5939
6124
|
};
|
|
5940
6125
|
}
|
|
6126
|
+
console.log("[EXULU] POS 1 -- EXULU CONTEXT PROCESS FIELD");
|
|
5941
6127
|
const result = await field.processor.execute({
|
|
5942
6128
|
item,
|
|
5943
6129
|
user,
|
|
@@ -5950,13 +6136,24 @@ var ExuluContext = class {
|
|
|
5950
6136
|
delete: this.deleteItem
|
|
5951
6137
|
}
|
|
5952
6138
|
},
|
|
5953
|
-
|
|
6139
|
+
exuluConfig
|
|
5954
6140
|
});
|
|
5955
6141
|
return {
|
|
5956
6142
|
result,
|
|
5957
6143
|
job: void 0
|
|
5958
6144
|
};
|
|
5959
6145
|
};
|
|
6146
|
+
search = async (options) => {
|
|
6147
|
+
const { db: db3 } = await postgresClient();
|
|
6148
|
+
const result = await vectorSearch({
|
|
6149
|
+
...options,
|
|
6150
|
+
user: options.user,
|
|
6151
|
+
role: options.role,
|
|
6152
|
+
context: this,
|
|
6153
|
+
db: db3
|
|
6154
|
+
});
|
|
6155
|
+
return result;
|
|
6156
|
+
};
|
|
5960
6157
|
deleteAll = async () => {
|
|
5961
6158
|
const { db: db3 } = await postgresClient();
|
|
5962
6159
|
await db3.from(getTableName(this.id)).delete();
|
|
@@ -5966,8 +6163,11 @@ var ExuluContext = class {
|
|
|
5966
6163
|
results: []
|
|
5967
6164
|
};
|
|
5968
6165
|
};
|
|
5969
|
-
executeSource = async (source, inputs) => {
|
|
5970
|
-
return await source.execute(
|
|
6166
|
+
executeSource = async (source, inputs, exuluConfig) => {
|
|
6167
|
+
return await source.execute({
|
|
6168
|
+
...inputs,
|
|
6169
|
+
exuluConfig
|
|
6170
|
+
});
|
|
5971
6171
|
};
|
|
5972
6172
|
tableExists = async () => {
|
|
5973
6173
|
const { db: db3 } = await postgresClient();
|
|
@@ -6023,7 +6223,7 @@ var ExuluContext = class {
|
|
|
6023
6223
|
job
|
|
6024
6224
|
};
|
|
6025
6225
|
};
|
|
6026
|
-
createItem = async (item, config, user, role, upsert) => {
|
|
6226
|
+
createItem = async (item, config, user, role, upsert, generateEmbeddingsOverwrite) => {
|
|
6027
6227
|
if (upsert && (!item.id && !item.external_id)) {
|
|
6028
6228
|
throw new Error("Item id or external id is required for upsert.");
|
|
6029
6229
|
}
|
|
@@ -6050,12 +6250,40 @@ var ExuluContext = class {
|
|
|
6050
6250
|
throw new Error("Failed to create item.");
|
|
6051
6251
|
}
|
|
6052
6252
|
console.log("[EXULU] context configuration", this.configuration);
|
|
6053
|
-
|
|
6253
|
+
let jobs = [];
|
|
6254
|
+
let shouldGenerateEmbeddings = this.embedder && generateEmbeddingsOverwrite !== false && (generateEmbeddingsOverwrite || this.configuration.calculateVectors === "onUpdate" || this.configuration.calculateVectors === "onInsert" || this.configuration.calculateVectors === "always");
|
|
6255
|
+
for (const [key, value] of Object.entries(item)) {
|
|
6256
|
+
console.log("[EXULU] Checking for processors for field", key);
|
|
6257
|
+
const processor = this.fields.find((field) => field.name === key.replace("_s3key", ""))?.processor;
|
|
6258
|
+
console.log("[EXULU] Processor found", processor);
|
|
6259
|
+
if (processor && (processor?.config?.trigger === "onInsert" || processor?.config?.trigger === "onUpdate" || processor?.config?.trigger === "always")) {
|
|
6260
|
+
const {
|
|
6261
|
+
job: processorJob,
|
|
6262
|
+
result: processorResult
|
|
6263
|
+
} = await this.processField(
|
|
6264
|
+
"api",
|
|
6265
|
+
{
|
|
6266
|
+
...item,
|
|
6267
|
+
id: results[0].id,
|
|
6268
|
+
field: key
|
|
6269
|
+
},
|
|
6270
|
+
config,
|
|
6271
|
+
user,
|
|
6272
|
+
role
|
|
6273
|
+
);
|
|
6274
|
+
if (processorJob) {
|
|
6275
|
+
jobs.push(processorJob);
|
|
6276
|
+
}
|
|
6277
|
+
if (!processorJob && processor.config?.generateEmbeddings) {
|
|
6278
|
+
shouldGenerateEmbeddings = true;
|
|
6279
|
+
}
|
|
6280
|
+
}
|
|
6281
|
+
}
|
|
6282
|
+
if (shouldGenerateEmbeddings) {
|
|
6054
6283
|
console.log("[EXULU] generating embeddings for item", results[0].id);
|
|
6055
|
-
const { job } = await this.embeddings.generate.one({
|
|
6284
|
+
const { job: embeddingsJob } = await this.embeddings.generate.one({
|
|
6056
6285
|
item: {
|
|
6057
6286
|
...item,
|
|
6058
|
-
// important we need to full record here with all fields for the embedder
|
|
6059
6287
|
id: results[0].id
|
|
6060
6288
|
},
|
|
6061
6289
|
user,
|
|
@@ -6063,17 +6291,16 @@ var ExuluContext = class {
|
|
|
6063
6291
|
trigger: "api",
|
|
6064
6292
|
config
|
|
6065
6293
|
});
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
};
|
|
6294
|
+
if (embeddingsJob) {
|
|
6295
|
+
jobs.push(embeddingsJob);
|
|
6296
|
+
}
|
|
6070
6297
|
}
|
|
6071
6298
|
return {
|
|
6072
6299
|
item: results[0],
|
|
6073
|
-
job: void 0
|
|
6300
|
+
job: jobs.length > 0 ? jobs.join(",") : void 0
|
|
6074
6301
|
};
|
|
6075
6302
|
};
|
|
6076
|
-
updateItem = async (item, config, user, role) => {
|
|
6303
|
+
updateItem = async (item, config, user, role, generateEmbeddingsOverwrite) => {
|
|
6077
6304
|
const { db: db3 } = await postgresClient();
|
|
6078
6305
|
if (item.field) {
|
|
6079
6306
|
delete item.field;
|
|
@@ -6097,8 +6324,35 @@ var ExuluContext = class {
|
|
|
6097
6324
|
}
|
|
6098
6325
|
).returning("id");
|
|
6099
6326
|
await mutation;
|
|
6100
|
-
|
|
6101
|
-
|
|
6327
|
+
let jobs = [];
|
|
6328
|
+
let shouldGenerateEmbeddings = this.embedder && generateEmbeddingsOverwrite !== false && (generateEmbeddingsOverwrite || this.configuration.calculateVectors === "onUpdate" || this.configuration.calculateVectors === "always");
|
|
6329
|
+
for (const [key, value] of Object.entries(item)) {
|
|
6330
|
+
const processor = this.fields.find((field) => field.name === key.replace("_s3key", ""))?.processor;
|
|
6331
|
+
if (processor && (processor?.config?.trigger === "onInsert" || processor?.config?.trigger === "onUpdate" || processor?.config?.trigger === "always")) {
|
|
6332
|
+
const {
|
|
6333
|
+
job: processorJob,
|
|
6334
|
+
result: processorResult
|
|
6335
|
+
} = await this.processField(
|
|
6336
|
+
"api",
|
|
6337
|
+
{
|
|
6338
|
+
...item,
|
|
6339
|
+
id: record.id,
|
|
6340
|
+
field: key
|
|
6341
|
+
},
|
|
6342
|
+
config,
|
|
6343
|
+
user,
|
|
6344
|
+
role
|
|
6345
|
+
);
|
|
6346
|
+
if (processorJob) {
|
|
6347
|
+
jobs.push(processorJob);
|
|
6348
|
+
}
|
|
6349
|
+
if (!processorJob && processor.config?.generateEmbeddings) {
|
|
6350
|
+
shouldGenerateEmbeddings = true;
|
|
6351
|
+
}
|
|
6352
|
+
}
|
|
6353
|
+
}
|
|
6354
|
+
if (shouldGenerateEmbeddings) {
|
|
6355
|
+
const { job: embeddingsJob } = await this.embeddings.generate.one({
|
|
6102
6356
|
item: record,
|
|
6103
6357
|
// important we need to full record here with all fields for the embedder
|
|
6104
6358
|
user,
|
|
@@ -6106,14 +6360,13 @@ var ExuluContext = class {
|
|
|
6106
6360
|
trigger: "api",
|
|
6107
6361
|
config
|
|
6108
6362
|
});
|
|
6109
|
-
|
|
6110
|
-
|
|
6111
|
-
|
|
6112
|
-
};
|
|
6363
|
+
if (embeddingsJob) {
|
|
6364
|
+
jobs.push(embeddingsJob);
|
|
6365
|
+
}
|
|
6113
6366
|
}
|
|
6114
6367
|
return {
|
|
6115
6368
|
item: record,
|
|
6116
|
-
job: void 0
|
|
6369
|
+
job: jobs.length > 0 ? jobs.join(",") : void 0
|
|
6117
6370
|
};
|
|
6118
6371
|
};
|
|
6119
6372
|
deleteItem = async (item, user, role) => {
|
|
@@ -6140,6 +6393,34 @@ var ExuluContext = class {
|
|
|
6140
6393
|
job: void 0
|
|
6141
6394
|
};
|
|
6142
6395
|
};
|
|
6396
|
+
getItem = async ({ item }) => {
|
|
6397
|
+
const { db: db3 } = await postgresClient();
|
|
6398
|
+
if (!item.id && !item.external_id) {
|
|
6399
|
+
throw new Error("Item id or external id is required to get an item.");
|
|
6400
|
+
}
|
|
6401
|
+
const result = await db3.from(getTableName(this.id)).where({
|
|
6402
|
+
...item.id ? { id: item.id } : {},
|
|
6403
|
+
...item.external_id ? { external_id: item.external_id } : {}
|
|
6404
|
+
}).first();
|
|
6405
|
+
if (result) {
|
|
6406
|
+
const chunksCount = await db3.from(getChunksTableName(this.id)).where(
|
|
6407
|
+
{ source: result.id }
|
|
6408
|
+
).count("id");
|
|
6409
|
+
result.chunksCount = Number(chunksCount[0].count) || 0;
|
|
6410
|
+
}
|
|
6411
|
+
return result;
|
|
6412
|
+
};
|
|
6413
|
+
getItems = async ({
|
|
6414
|
+
filters,
|
|
6415
|
+
fields
|
|
6416
|
+
}) => {
|
|
6417
|
+
const { db: db3 } = await postgresClient();
|
|
6418
|
+
let query = db3.from(getTableName(this.id)).select(fields || "*");
|
|
6419
|
+
const tableDefinition = contextToTableDefinition(this);
|
|
6420
|
+
query = applyFilters(query, filters || [], tableDefinition);
|
|
6421
|
+
const items = await query;
|
|
6422
|
+
return items;
|
|
6423
|
+
};
|
|
6143
6424
|
embeddings = {
|
|
6144
6425
|
generate: {
|
|
6145
6426
|
one: async ({
|
|
@@ -6156,6 +6437,12 @@ var ExuluContext = class {
|
|
|
6156
6437
|
if (!item.id) {
|
|
6157
6438
|
throw new Error("Item id is required for generating embeddings.");
|
|
6158
6439
|
}
|
|
6440
|
+
const { db: db3 } = await postgresClient();
|
|
6441
|
+
const record = await db3.from(getTableName(this.id)).where({ id: item.id }).first();
|
|
6442
|
+
if (!record) {
|
|
6443
|
+
throw new Error("Item not found.");
|
|
6444
|
+
}
|
|
6445
|
+
item = record;
|
|
6159
6446
|
const queue = await this.embedder.queue;
|
|
6160
6447
|
if (queue?.queue.name) {
|
|
6161
6448
|
console.log("[EXULU] embedder is in queue mode, scheduling job.");
|
|
@@ -6921,7 +7208,7 @@ Mood: friendly and intelligent.
|
|
|
6921
7208
|
const user = authenticationResult.user;
|
|
6922
7209
|
let agentQuery = db3("agents");
|
|
6923
7210
|
agentQuery.select("*");
|
|
6924
|
-
agentQuery = applyAccessControl(agentsSchema2(), authenticationResult.user
|
|
7211
|
+
agentQuery = applyAccessControl(agentsSchema2(), agentQuery, authenticationResult.user);
|
|
6925
7212
|
agentQuery.where({ id: req.params.agent });
|
|
6926
7213
|
const agent = await agentQuery.first();
|
|
6927
7214
|
if (!agent) {
|
|
@@ -6938,7 +7225,7 @@ Mood: friendly and intelligent.
|
|
|
6938
7225
|
} else {
|
|
6939
7226
|
let projectQuery = db3("projects");
|
|
6940
7227
|
projectQuery.select("*");
|
|
6941
|
-
projectQuery = applyAccessControl(projectsSchema2(), authenticationResult.user
|
|
7228
|
+
projectQuery = applyAccessControl(projectsSchema2(), projectQuery, authenticationResult.user);
|
|
6942
7229
|
projectQuery.where({ id: req.params.project });
|
|
6943
7230
|
project = await projectQuery.first();
|
|
6944
7231
|
if (!project) {
|
|
@@ -7280,6 +7567,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7280
7567
|
if (!data.role) {
|
|
7281
7568
|
throw new Error(`Role not set for processor job.`);
|
|
7282
7569
|
}
|
|
7570
|
+
console.log("[EXULU] POS 2 -- EXULU CONTEXT PROCESS FIELD");
|
|
7283
7571
|
const result = await field.processor.execute({
|
|
7284
7572
|
item: data.inputs,
|
|
7285
7573
|
user: data.user,
|
|
@@ -7294,9 +7582,24 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7294
7582
|
},
|
|
7295
7583
|
config
|
|
7296
7584
|
});
|
|
7585
|
+
let jobs = [];
|
|
7586
|
+
if (field.processor.config?.generateEmbeddings) {
|
|
7587
|
+
const { job: embeddingsJob } = await context.embeddings.generate.one({
|
|
7588
|
+
item: data.inputs,
|
|
7589
|
+
user: data.user,
|
|
7590
|
+
role: data.role,
|
|
7591
|
+
trigger: "api",
|
|
7592
|
+
config
|
|
7593
|
+
});
|
|
7594
|
+
if (embeddingsJob) {
|
|
7595
|
+
jobs.push(embeddingsJob);
|
|
7596
|
+
}
|
|
7597
|
+
}
|
|
7297
7598
|
return {
|
|
7298
7599
|
result,
|
|
7299
|
-
metadata: {
|
|
7600
|
+
metadata: {
|
|
7601
|
+
jobs: jobs.length > 0 ? jobs.join(",") : void 0
|
|
7602
|
+
}
|
|
7300
7603
|
};
|
|
7301
7604
|
}
|
|
7302
7605
|
if (data.type === "eval_run") {
|
|
@@ -7982,7 +8285,7 @@ var ExuluMCP = class {
|
|
|
7982
8285
|
console.log("[EXULU] MCP tool inputs", inputs);
|
|
7983
8286
|
console.log("[EXULU] MCP tool args", args);
|
|
7984
8287
|
const configValues = agentInstance.tools;
|
|
7985
|
-
const tools = convertToolsArrayToObject(
|
|
8288
|
+
const tools = await convertToolsArrayToObject(
|
|
7986
8289
|
[tool2],
|
|
7987
8290
|
allTools,
|
|
7988
8291
|
configValues,
|