@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.js
CHANGED
|
@@ -167,11 +167,17 @@ async function postgresClient() {
|
|
|
167
167
|
pool: {
|
|
168
168
|
min: 2,
|
|
169
169
|
max: 20,
|
|
170
|
+
// Increased from 20 to handle more concurrent operations
|
|
170
171
|
acquireTimeoutMillis: 3e4,
|
|
171
172
|
createTimeoutMillis: 3e4,
|
|
172
173
|
idleTimeoutMillis: 3e4,
|
|
173
174
|
reapIntervalMillis: 1e3,
|
|
174
|
-
createRetryIntervalMillis: 200
|
|
175
|
+
createRetryIntervalMillis: 200,
|
|
176
|
+
// Log pool events to help debug connection issues
|
|
177
|
+
afterCreate: (conn, done) => {
|
|
178
|
+
console.log("[EXULU] New database connection created");
|
|
179
|
+
done(null, conn);
|
|
180
|
+
}
|
|
175
181
|
}
|
|
176
182
|
});
|
|
177
183
|
try {
|
|
@@ -452,6 +458,8 @@ var authentication = async ({
|
|
|
452
458
|
type: "api",
|
|
453
459
|
id: 192837465,
|
|
454
460
|
email: "internal@exulu.com",
|
|
461
|
+
firstname: "API",
|
|
462
|
+
lastname: "User",
|
|
455
463
|
role: {
|
|
456
464
|
id: "internal",
|
|
457
465
|
name: "Internal",
|
|
@@ -1285,10 +1293,10 @@ var rbacSchema = {
|
|
|
1285
1293
|
name: "user_id",
|
|
1286
1294
|
type: "number"
|
|
1287
1295
|
},
|
|
1288
|
-
{
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
},
|
|
1296
|
+
/* {
|
|
1297
|
+
name: "project_id",
|
|
1298
|
+
type: "uuid"
|
|
1299
|
+
}, */
|
|
1292
1300
|
{
|
|
1293
1301
|
name: "rights",
|
|
1294
1302
|
type: "text",
|
|
@@ -2198,7 +2206,7 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2198
2206
|
});
|
|
2199
2207
|
return {
|
|
2200
2208
|
// Filter result to only include requested fields
|
|
2201
|
-
item: finalizeRequestedFields({ table, requestedFields, agents, contexts, tools, result: results[0], user: context.user }),
|
|
2209
|
+
item: finalizeRequestedFields({ args, table, requestedFields, agents, contexts, tools, result: results[0], user: context.user }),
|
|
2202
2210
|
job
|
|
2203
2211
|
};
|
|
2204
2212
|
},
|
|
@@ -2246,7 +2254,7 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2246
2254
|
}
|
|
2247
2255
|
const { job } = await postprocessUpdate({ table, requestedFields, agents, contexts, tools, result, user: context.user.id, role: context.user.role?.id, config });
|
|
2248
2256
|
return {
|
|
2249
|
-
item: finalizeRequestedFields({ table, requestedFields, agents, contexts, tools, result, user: context.user.id }),
|
|
2257
|
+
item: finalizeRequestedFields({ args, table, requestedFields, agents, contexts, tools, result, user: context.user.id }),
|
|
2250
2258
|
job
|
|
2251
2259
|
};
|
|
2252
2260
|
},
|
|
@@ -2287,7 +2295,7 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2287
2295
|
const result = await db3.from(tableNamePlural).select(Object.keys(columns)).where({ id }).first();
|
|
2288
2296
|
const { job } = await postprocessUpdate({ table, requestedFields, agents, contexts, tools, result, user: context.user.id, role: context.user.role?.id, config });
|
|
2289
2297
|
return {
|
|
2290
|
-
item: finalizeRequestedFields({ table, requestedFields, agents, contexts, tools, result, user: context.user.id }),
|
|
2298
|
+
item: finalizeRequestedFields({ args, table, requestedFields, agents, contexts, tools, result, user: context.user.id }),
|
|
2291
2299
|
job
|
|
2292
2300
|
};
|
|
2293
2301
|
},
|
|
@@ -2319,7 +2327,7 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2319
2327
|
}).del();
|
|
2320
2328
|
}
|
|
2321
2329
|
await postprocessDeletion({ table, requestedFields, agents, contexts, tools, result });
|
|
2322
|
-
return finalizeRequestedFields({ table, requestedFields, agents, contexts, tools, result, user: context.user.id });
|
|
2330
|
+
return finalizeRequestedFields({ args, table, requestedFields, agents, contexts, tools, result, user: context.user.id });
|
|
2323
2331
|
},
|
|
2324
2332
|
[`${tableNamePlural}RemoveOne`]: async (_, args, context, info) => {
|
|
2325
2333
|
const { where } = args;
|
|
@@ -2343,7 +2351,7 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2343
2351
|
}
|
|
2344
2352
|
await db3(tableNamePlural).where(where).del();
|
|
2345
2353
|
await postprocessDeletion({ table, requestedFields, agents, contexts, tools, result });
|
|
2346
|
-
return finalizeRequestedFields({ table, requestedFields, agents, contexts, tools, result, user: context.user.id });
|
|
2354
|
+
return finalizeRequestedFields({ args, table, requestedFields, agents, contexts, tools, result, user: context.user.id });
|
|
2347
2355
|
}
|
|
2348
2356
|
};
|
|
2349
2357
|
if (table.type === "items") {
|
|
@@ -2376,20 +2384,20 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2376
2384
|
}
|
|
2377
2385
|
const { db: db3 } = context;
|
|
2378
2386
|
let query = db3.from(tableNamePlural).select("*").where({ id: args.item });
|
|
2379
|
-
query = applyAccessControl(table, context.user
|
|
2387
|
+
query = applyAccessControl(table, query, context.user);
|
|
2380
2388
|
const item = await query.first();
|
|
2381
2389
|
if (!item) {
|
|
2382
2390
|
throw new Error("Item not found, or your user does not have access to it.");
|
|
2383
2391
|
}
|
|
2384
2392
|
const { job, result } = await exists.processField(
|
|
2385
2393
|
"api",
|
|
2386
|
-
context.user.id,
|
|
2387
|
-
context.user.role?.id,
|
|
2388
2394
|
{
|
|
2389
2395
|
...item,
|
|
2390
2396
|
field: args.field
|
|
2391
2397
|
},
|
|
2392
|
-
config
|
|
2398
|
+
config,
|
|
2399
|
+
context.user.id,
|
|
2400
|
+
context.user.role?.id
|
|
2393
2401
|
);
|
|
2394
2402
|
return {
|
|
2395
2403
|
message: job ? "Processing job scheduled." : "Item processed successfully.",
|
|
@@ -2436,7 +2444,10 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2436
2444
|
};
|
|
2437
2445
|
}
|
|
2438
2446
|
console.log("[EXULU] Executing source function directly");
|
|
2439
|
-
const result = await source.execute(
|
|
2447
|
+
const result = await source.execute({
|
|
2448
|
+
...args.inputs,
|
|
2449
|
+
exuluConfig: config
|
|
2450
|
+
});
|
|
2440
2451
|
let jobs = [];
|
|
2441
2452
|
let items = [];
|
|
2442
2453
|
for (const item of result) {
|
|
@@ -2560,14 +2571,14 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2560
2571
|
}
|
|
2561
2572
|
return mutations;
|
|
2562
2573
|
}
|
|
2563
|
-
var applyAccessControl = (table,
|
|
2574
|
+
var applyAccessControl = (table, query, user) => {
|
|
2564
2575
|
const tableNamePlural = table.name.plural.toLowerCase();
|
|
2565
|
-
if (table.name.plural !== "agent_sessions" && user
|
|
2576
|
+
if (table.name.plural !== "agent_sessions" && user?.super_admin === true) {
|
|
2566
2577
|
return query;
|
|
2567
2578
|
}
|
|
2568
|
-
console.log("[EXULU] user.role", user
|
|
2579
|
+
console.log("[EXULU] user.role", user?.role);
|
|
2569
2580
|
console.log("[EXULU] table.name.plural", table.name.plural);
|
|
2570
|
-
if (!user
|
|
2581
|
+
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")))) {
|
|
2571
2582
|
console.error("==== Access control error: no role found or no access to entity type. ====");
|
|
2572
2583
|
throw new Error("Access control error: no role found or no access to entity type.");
|
|
2573
2584
|
}
|
|
@@ -2656,7 +2667,7 @@ var removeAgentFields = (requestedFields) => {
|
|
|
2656
2667
|
filtered.push("backend");
|
|
2657
2668
|
return filtered;
|
|
2658
2669
|
};
|
|
2659
|
-
var addAgentFields = async (requestedFields, agents, result, tools, user) => {
|
|
2670
|
+
var addAgentFields = async (args, requestedFields, agents, result, tools, user, contexts) => {
|
|
2660
2671
|
let backend = agents.find((a) => a.id === result?.backend);
|
|
2661
2672
|
if (requestedFields.includes("providerName")) {
|
|
2662
2673
|
result.providerName = backend?.providerName || "";
|
|
@@ -2703,6 +2714,17 @@ var addAgentFields = async (requestedFields, agents, result, tools, user) => {
|
|
|
2703
2714
|
console.log("[EXULU] hydratedTool", hydratedTool);
|
|
2704
2715
|
return hydratedTool;
|
|
2705
2716
|
}));
|
|
2717
|
+
if (args.project) {
|
|
2718
|
+
const projectTool = await createProjectRetrievalTool({
|
|
2719
|
+
projectId: args.project,
|
|
2720
|
+
user,
|
|
2721
|
+
role: user.role?.id,
|
|
2722
|
+
contexts
|
|
2723
|
+
});
|
|
2724
|
+
if (projectTool) {
|
|
2725
|
+
result.tools.unshift(projectTool);
|
|
2726
|
+
}
|
|
2727
|
+
}
|
|
2706
2728
|
result.tools = result.tools.filter((tool2) => tool2 !== null);
|
|
2707
2729
|
} else {
|
|
2708
2730
|
result.tools = [];
|
|
@@ -2841,6 +2863,7 @@ var postprocessDeletion = async ({
|
|
|
2841
2863
|
return result;
|
|
2842
2864
|
};
|
|
2843
2865
|
var finalizeRequestedFields = async ({
|
|
2866
|
+
args,
|
|
2844
2867
|
table,
|
|
2845
2868
|
requestedFields,
|
|
2846
2869
|
agents,
|
|
@@ -2857,11 +2880,28 @@ var finalizeRequestedFields = async ({
|
|
|
2857
2880
|
}
|
|
2858
2881
|
if (Array.isArray(result)) {
|
|
2859
2882
|
result = result.map((item) => {
|
|
2860
|
-
return finalizeRequestedFields({
|
|
2883
|
+
return finalizeRequestedFields({
|
|
2884
|
+
args,
|
|
2885
|
+
table,
|
|
2886
|
+
requestedFields,
|
|
2887
|
+
agents,
|
|
2888
|
+
contexts,
|
|
2889
|
+
tools,
|
|
2890
|
+
result: item,
|
|
2891
|
+
user
|
|
2892
|
+
});
|
|
2861
2893
|
});
|
|
2862
2894
|
} else {
|
|
2863
2895
|
if (table.name.singular === "agent") {
|
|
2864
|
-
result = await addAgentFields(
|
|
2896
|
+
result = await addAgentFields(
|
|
2897
|
+
args,
|
|
2898
|
+
requestedFields,
|
|
2899
|
+
agents,
|
|
2900
|
+
result,
|
|
2901
|
+
tools,
|
|
2902
|
+
user,
|
|
2903
|
+
contexts
|
|
2904
|
+
);
|
|
2865
2905
|
if (!requestedFields.includes("backend")) {
|
|
2866
2906
|
delete result.backend;
|
|
2867
2907
|
}
|
|
@@ -2938,18 +2978,18 @@ function createQueries(table, agents, tools, contexts) {
|
|
|
2938
2978
|
const requestedFields = getRequestedFields(info);
|
|
2939
2979
|
const sanitizedFields = sanitizeRequestedFields(table, requestedFields);
|
|
2940
2980
|
let query = db3.from(tableNamePlural).select(sanitizedFields).where({ id: args.id });
|
|
2941
|
-
query = applyAccessControl(table, context.user
|
|
2981
|
+
query = applyAccessControl(table, query, context.user);
|
|
2942
2982
|
let result = await query.first();
|
|
2943
|
-
return finalizeRequestedFields({ table, requestedFields, agents, contexts, tools, result, user: context.user });
|
|
2983
|
+
return finalizeRequestedFields({ args, table, requestedFields, agents, contexts, tools, result, user: context.user });
|
|
2944
2984
|
},
|
|
2945
2985
|
[`${tableNameSingular}ByIds`]: async (_, args, context, info) => {
|
|
2946
2986
|
const { db: db3 } = context;
|
|
2947
2987
|
const requestedFields = getRequestedFields(info);
|
|
2948
2988
|
const sanitizedFields = sanitizeRequestedFields(table, requestedFields);
|
|
2949
2989
|
let query = db3.from(tableNamePlural).select(sanitizedFields).whereIn("id", args.ids);
|
|
2950
|
-
query = applyAccessControl(table, context.user
|
|
2990
|
+
query = applyAccessControl(table, query, context.user);
|
|
2951
2991
|
let result = await query;
|
|
2952
|
-
return finalizeRequestedFields({ table, requestedFields, agents, contexts, tools, result, user: context.user });
|
|
2992
|
+
return finalizeRequestedFields({ args, table, requestedFields, agents, contexts, tools, result, user: context.user });
|
|
2953
2993
|
},
|
|
2954
2994
|
[`${tableNameSingular}One`]: async (_, args, context, info) => {
|
|
2955
2995
|
const { filters = [], sort } = args;
|
|
@@ -2958,10 +2998,10 @@ function createQueries(table, agents, tools, contexts) {
|
|
|
2958
2998
|
const sanitizedFields = sanitizeRequestedFields(table, requestedFields);
|
|
2959
2999
|
let query = db3.from(tableNamePlural).select(sanitizedFields);
|
|
2960
3000
|
query = applyFilters(query, filters, table);
|
|
2961
|
-
query = applyAccessControl(table, context.user
|
|
3001
|
+
query = applyAccessControl(table, query, context.user);
|
|
2962
3002
|
query = applySorting(query, sort);
|
|
2963
3003
|
let result = await query.first();
|
|
2964
|
-
return finalizeRequestedFields({ table, requestedFields, agents, contexts, tools, result, user: context.user });
|
|
3004
|
+
return finalizeRequestedFields({ args, table, requestedFields, agents, contexts, tools, result, user: context.user });
|
|
2965
3005
|
},
|
|
2966
3006
|
[`${tableNamePlural}Pagination`]: async (_, args, context, info) => {
|
|
2967
3007
|
const { limit = 10, page = 0, filters = [], sort } = args;
|
|
@@ -2971,7 +3011,7 @@ function createQueries(table, agents, tools, contexts) {
|
|
|
2971
3011
|
}
|
|
2972
3012
|
let countQuery = db3(tableNamePlural);
|
|
2973
3013
|
countQuery = applyFilters(countQuery, filters, table);
|
|
2974
|
-
countQuery = applyAccessControl(table, context.user
|
|
3014
|
+
countQuery = applyAccessControl(table, countQuery, context.user);
|
|
2975
3015
|
const countResult = await countQuery.count("* as count");
|
|
2976
3016
|
const itemCount = Number(countResult[0]?.count || 0);
|
|
2977
3017
|
const pageCount = Math.ceil(itemCount / limit);
|
|
@@ -2980,7 +3020,7 @@ function createQueries(table, agents, tools, contexts) {
|
|
|
2980
3020
|
const hasNextPage = currentPage < pageCount - 1;
|
|
2981
3021
|
let dataQuery = db3(tableNamePlural);
|
|
2982
3022
|
dataQuery = applyFilters(dataQuery, filters, table);
|
|
2983
|
-
dataQuery = applyAccessControl(table, context.user
|
|
3023
|
+
dataQuery = applyAccessControl(table, dataQuery, context.user);
|
|
2984
3024
|
const requestedFields = getRequestedFields(info);
|
|
2985
3025
|
dataQuery = applySorting(dataQuery, sort);
|
|
2986
3026
|
if (page > 1) {
|
|
@@ -2996,7 +3036,7 @@ function createQueries(table, agents, tools, contexts) {
|
|
|
2996
3036
|
hasPreviousPage,
|
|
2997
3037
|
hasNextPage
|
|
2998
3038
|
},
|
|
2999
|
-
items: finalizeRequestedFields({ table, requestedFields, agents, contexts, tools, result: items, user: context.user })
|
|
3039
|
+
items: finalizeRequestedFields({ args, table, requestedFields, agents, contexts, tools, result: items, user: context.user })
|
|
3000
3040
|
};
|
|
3001
3041
|
},
|
|
3002
3042
|
// Add generic statistics query for all tables
|
|
@@ -3005,7 +3045,7 @@ function createQueries(table, agents, tools, contexts) {
|
|
|
3005
3045
|
const { db: db3 } = context;
|
|
3006
3046
|
let query = db3(tableNamePlural);
|
|
3007
3047
|
query = applyFilters(query, filters, table);
|
|
3008
|
-
query = applyAccessControl(table, context.user
|
|
3048
|
+
query = applyAccessControl(table, query, context.user);
|
|
3009
3049
|
if (groupBy) {
|
|
3010
3050
|
query = query.select(groupBy).groupBy(groupBy);
|
|
3011
3051
|
if (tableNamePlural === "tracking") {
|
|
@@ -3105,11 +3145,11 @@ var vectorSearch = async ({
|
|
|
3105
3145
|
const chunksTable = getChunksTableName(id);
|
|
3106
3146
|
let countQuery = db3(mainTable);
|
|
3107
3147
|
countQuery = applyFilters(countQuery, filters, table);
|
|
3108
|
-
countQuery = applyAccessControl(table,
|
|
3148
|
+
countQuery = applyAccessControl(table, countQuery, user);
|
|
3109
3149
|
const columns = await db3(mainTable).columnInfo();
|
|
3110
3150
|
let itemsQuery = db3(mainTable).select(Object.keys(columns).map((column) => mainTable + "." + column));
|
|
3111
3151
|
itemsQuery = applyFilters(itemsQuery, filters, table);
|
|
3112
|
-
itemsQuery = applyAccessControl(table,
|
|
3152
|
+
itemsQuery = applyAccessControl(table, itemsQuery, user);
|
|
3113
3153
|
itemsQuery = applySorting(itemsQuery, sort);
|
|
3114
3154
|
if (queryRewriter) {
|
|
3115
3155
|
query = await queryRewriter(query);
|
|
@@ -3128,7 +3168,7 @@ var vectorSearch = async ({
|
|
|
3128
3168
|
const { chunks } = await embedder.generateFromQuery(context.id, query, {
|
|
3129
3169
|
label: table.name.singular,
|
|
3130
3170
|
trigger
|
|
3131
|
-
}, user
|
|
3171
|
+
}, user?.id, role);
|
|
3132
3172
|
if (!chunks?.[0]?.vector) {
|
|
3133
3173
|
throw new Error("No vector generated for query.");
|
|
3134
3174
|
}
|
|
@@ -3518,7 +3558,8 @@ function createSDL(tables, contexts, agents, tools, config, evals, queues2) {
|
|
|
3518
3558
|
const tableNameSingularUpperCaseFirst = table.name.singular.charAt(0).toUpperCase() + table.name.singular.slice(1);
|
|
3519
3559
|
const processorFields = table.fields.filter((field) => field.processor?.execute);
|
|
3520
3560
|
typeDefs += `
|
|
3521
|
-
|
|
3561
|
+
${tableNameSingular === "agent" ? `${tableNameSingular}ById(id: ID!, project: ID): ${tableNameSingular}` : `${tableNameSingular}ById(id: ID!): ${tableNameSingular}`}
|
|
3562
|
+
|
|
3522
3563
|
${tableNameSingular}ByIds(ids: [ID!]!): [${tableNameSingular}]!
|
|
3523
3564
|
${tableNamePlural}Pagination(limit: Int, page: Int, filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingularUpperCaseFirst}PaginationResult
|
|
3524
3565
|
${tableNameSingular}One(filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingular}
|
|
@@ -4450,16 +4491,27 @@ var addPrefixToKey = (keyPath, config) => {
|
|
|
4450
4491
|
}
|
|
4451
4492
|
return `${prefix}/${keyPath}`;
|
|
4452
4493
|
};
|
|
4453
|
-
var uploadFile = async (
|
|
4454
|
-
console.log("[EXULU] Uploading file to S3", key);
|
|
4494
|
+
var uploadFile = async (file, key, config, options = {}, user) => {
|
|
4455
4495
|
if (!config.fileUploads) {
|
|
4456
|
-
throw new Error("File uploads are not configured");
|
|
4496
|
+
throw new Error("File uploads are not configured (in the exported uploadFile function)");
|
|
4457
4497
|
}
|
|
4458
4498
|
const client2 = getS3Client(config);
|
|
4459
|
-
let
|
|
4499
|
+
let defaultBucket = config.fileUploads.s3Bucket;
|
|
4500
|
+
let customBucket = false;
|
|
4501
|
+
if (key.includes("[bucket:")) {
|
|
4502
|
+
console.log("[EXULU] key includes [bucket:name]", key);
|
|
4503
|
+
customBucket = key.split("[bucket:")[1]?.split("]")[0] || "";
|
|
4504
|
+
if (!customBucket?.length) {
|
|
4505
|
+
throw new Error("Invalid key, does not contain a bucket name like '[bucket:name]'.");
|
|
4506
|
+
}
|
|
4507
|
+
key = key.split("]")[1] || "";
|
|
4508
|
+
console.log("[EXULU] custom bucket", customBucket);
|
|
4509
|
+
}
|
|
4510
|
+
let folder = user ? `${user}/` : "";
|
|
4460
4511
|
const fullKey = addPrefixToKey(!key.includes(folder) ? folder + key : key, config);
|
|
4512
|
+
console.log("[EXULU] uploading file to s3 into bucket", customBucket || defaultBucket, "with key", fullKey);
|
|
4461
4513
|
const command = new PutObjectCommand({
|
|
4462
|
-
Bucket:
|
|
4514
|
+
Bucket: customBucket || defaultBucket,
|
|
4463
4515
|
Key: fullKey,
|
|
4464
4516
|
Body: file,
|
|
4465
4517
|
ContentType: options.contentType,
|
|
@@ -4467,6 +4519,10 @@ var uploadFile = async (user, file, key, config, options = {}) => {
|
|
|
4467
4519
|
ContentLength: file.byteLength
|
|
4468
4520
|
});
|
|
4469
4521
|
await client2.send(command);
|
|
4522
|
+
console.log("[EXULU] file uploaded to s3 into bucket", customBucket || defaultBucket, "with key", fullKey);
|
|
4523
|
+
if (customBucket) {
|
|
4524
|
+
return "[bucket:" + customBucket + "]" + fullKey;
|
|
4525
|
+
}
|
|
4470
4526
|
return fullKey;
|
|
4471
4527
|
};
|
|
4472
4528
|
var createUppyRoutes = async (app, config) => {
|
|
@@ -4625,16 +4681,12 @@ var createUppyRoutes = async (app, config) => {
|
|
|
4625
4681
|
key = key.split("]")[1] || "";
|
|
4626
4682
|
console.log("[EXULU] key", key);
|
|
4627
4683
|
}
|
|
4628
|
-
console.log("[EXULU] Getting object metadata from s3", key);
|
|
4629
|
-
console.log("[EXULU] bucket", bucket);
|
|
4630
|
-
console.log("[EXULU] key", key);
|
|
4631
4684
|
const client2 = getS3Client(config);
|
|
4632
4685
|
const command = new HeadObjectCommand({
|
|
4633
4686
|
Bucket: bucket,
|
|
4634
4687
|
Key: key
|
|
4635
4688
|
});
|
|
4636
4689
|
const response = await client2.send(command);
|
|
4637
|
-
console.log("[EXULU] Object metadata from s3", response);
|
|
4638
4690
|
res.json(response);
|
|
4639
4691
|
res.end();
|
|
4640
4692
|
});
|
|
@@ -4949,12 +5001,117 @@ function sanitizeToolName(name) {
|
|
|
4949
5001
|
}
|
|
4950
5002
|
return sanitized;
|
|
4951
5003
|
}
|
|
4952
|
-
var
|
|
5004
|
+
var projectsCache = /* @__PURE__ */ new Map();
|
|
5005
|
+
var createProjectRetrievalTool = async ({
|
|
5006
|
+
user,
|
|
5007
|
+
role,
|
|
5008
|
+
contexts,
|
|
5009
|
+
projectId
|
|
5010
|
+
}) => {
|
|
5011
|
+
let project;
|
|
5012
|
+
const cachedProject = projectsCache.get(projectId);
|
|
5013
|
+
const OneMinuteAgo = new Date(Date.now() - 1e3 * 60);
|
|
5014
|
+
if (cachedProject && cachedProject.age > OneMinuteAgo) {
|
|
5015
|
+
project = cachedProject.project;
|
|
5016
|
+
} else {
|
|
5017
|
+
const { db: db3 } = await postgresClient();
|
|
5018
|
+
project = await db3.from("projects").where("id", projectId).first();
|
|
5019
|
+
if (project) {
|
|
5020
|
+
projectsCache.set(projectId, {
|
|
5021
|
+
age: /* @__PURE__ */ new Date(),
|
|
5022
|
+
project
|
|
5023
|
+
});
|
|
5024
|
+
} else {
|
|
5025
|
+
return;
|
|
5026
|
+
}
|
|
5027
|
+
}
|
|
5028
|
+
console.log("[EXULU] Project search tool created for project", project);
|
|
5029
|
+
if (!project.project_items?.length) {
|
|
5030
|
+
return;
|
|
5031
|
+
}
|
|
5032
|
+
const projectRetrievalTool = new ExuluTool2({
|
|
5033
|
+
id: "project_information_retrieval_tool_" + projectId,
|
|
5034
|
+
name: "Project information retrieval tool for project " + project.name,
|
|
5035
|
+
description: "This tool retrieves information about a project from conversations and items that were added to the project " + project.name + ".",
|
|
5036
|
+
inputSchema: z.object({
|
|
5037
|
+
query: z.string().describe("The query to retrieve information about the project " + project.name + "."),
|
|
5038
|
+
keywords: z.array(z.string()).describe("The most relevant keywords in the query, such as names of people, companies, products, etc. in the project " + project.name + ".")
|
|
5039
|
+
}),
|
|
5040
|
+
type: "function",
|
|
5041
|
+
category: "project",
|
|
5042
|
+
config: [],
|
|
5043
|
+
execute: async ({ query, keywords }) => {
|
|
5044
|
+
console.log("[EXULU] Project search tool searching for project", project);
|
|
5045
|
+
const items = project.project_items;
|
|
5046
|
+
const set = {};
|
|
5047
|
+
for (const item of items) {
|
|
5048
|
+
const context = item.split("/")[0];
|
|
5049
|
+
if (!context) {
|
|
5050
|
+
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.");
|
|
5051
|
+
}
|
|
5052
|
+
const id = item.split("/")[1];
|
|
5053
|
+
if (set[context]) {
|
|
5054
|
+
set[context].push(id);
|
|
5055
|
+
} else {
|
|
5056
|
+
set[context] = [id];
|
|
5057
|
+
}
|
|
5058
|
+
}
|
|
5059
|
+
console.log("[EXULU] Project search tool searching through contexts", Object.keys(set));
|
|
5060
|
+
const results = await Promise.all(Object.keys(set).map(async (contextName, index) => {
|
|
5061
|
+
const context = contexts.find((context2) => context2.id === contextName);
|
|
5062
|
+
if (!context) {
|
|
5063
|
+
console.error("[EXULU] Context not found for project information retrieval tool.", contextName);
|
|
5064
|
+
return [];
|
|
5065
|
+
}
|
|
5066
|
+
const itemIds = set[contextName];
|
|
5067
|
+
console.log("[EXULU] Project search tool searching through items", itemIds);
|
|
5068
|
+
return await context.search({
|
|
5069
|
+
// todo check if it is more performant to use a concatenation of
|
|
5070
|
+
// the query and keywords, or just the keywords, instead of the
|
|
5071
|
+
// query itself.
|
|
5072
|
+
query,
|
|
5073
|
+
filters: [{
|
|
5074
|
+
id: {
|
|
5075
|
+
in: itemIds
|
|
5076
|
+
}
|
|
5077
|
+
}],
|
|
5078
|
+
user,
|
|
5079
|
+
role,
|
|
5080
|
+
method: "hybridSearch",
|
|
5081
|
+
sort: {
|
|
5082
|
+
field: "updatedAt",
|
|
5083
|
+
direction: "desc"
|
|
5084
|
+
},
|
|
5085
|
+
trigger: "tool",
|
|
5086
|
+
limit: 10,
|
|
5087
|
+
page: 1
|
|
5088
|
+
});
|
|
5089
|
+
}));
|
|
5090
|
+
console.log("[EXULU] Project search tool results", results);
|
|
5091
|
+
return {
|
|
5092
|
+
result: JSON.stringify(results.flat())
|
|
5093
|
+
};
|
|
5094
|
+
}
|
|
5095
|
+
});
|
|
5096
|
+
return projectRetrievalTool;
|
|
5097
|
+
};
|
|
5098
|
+
var convertToolsArrayToObject = async (currentTools, allExuluTools, configs, providerapikey, contexts, user, exuluConfig, sessionID, req, project) => {
|
|
4953
5099
|
if (!currentTools) return {};
|
|
4954
5100
|
if (!allExuluTools) return {};
|
|
4955
5101
|
if (!contexts) {
|
|
4956
5102
|
contexts = [];
|
|
4957
5103
|
}
|
|
5104
|
+
if (project) {
|
|
5105
|
+
const projectRetrievalTool = await createProjectRetrievalTool({
|
|
5106
|
+
user,
|
|
5107
|
+
role: user?.role?.id,
|
|
5108
|
+
contexts,
|
|
5109
|
+
projectId: project
|
|
5110
|
+
});
|
|
5111
|
+
if (projectRetrievalTool) {
|
|
5112
|
+
currentTools.push(projectRetrievalTool);
|
|
5113
|
+
}
|
|
5114
|
+
}
|
|
4958
5115
|
const sanitizedTools = currentTools ? currentTools.map((tool2) => ({
|
|
4959
5116
|
...tool2,
|
|
4960
5117
|
name: sanitizeToolName(tool2.name)
|
|
@@ -4963,9 +5120,9 @@ var convertToolsArrayToObject = (currentTools, allExuluTools, configs, providera
|
|
|
4963
5120
|
return {
|
|
4964
5121
|
...sanitizedTools?.reduce(
|
|
4965
5122
|
(prev, cur) => {
|
|
4966
|
-
let
|
|
4967
|
-
const userDefinedConfigDescription =
|
|
4968
|
-
const defaultConfigDescription =
|
|
5123
|
+
let toolVariableConfig = configs?.find((config) => config.id === cur.id);
|
|
5124
|
+
const userDefinedConfigDescription = toolVariableConfig?.config.find((config) => config.name === "description")?.value;
|
|
5125
|
+
const defaultConfigDescription = toolVariableConfig?.config.find((config) => config.name === "description")?.default;
|
|
4969
5126
|
const toolDescription = cur.description;
|
|
4970
5127
|
const description = userDefinedConfigDescription || defaultConfigDescription || toolDescription;
|
|
4971
5128
|
return {
|
|
@@ -4978,8 +5135,8 @@ var convertToolsArrayToObject = (currentTools, allExuluTools, configs, providera
|
|
|
4978
5135
|
console.error("[EXULU] Tool execute function is undefined.", cur.tool);
|
|
4979
5136
|
throw new Error("Tool execute function is undefined.");
|
|
4980
5137
|
}
|
|
4981
|
-
if (
|
|
4982
|
-
|
|
5138
|
+
if (toolVariableConfig) {
|
|
5139
|
+
toolVariableConfig = await hydrateVariables(toolVariableConfig || []);
|
|
4983
5140
|
}
|
|
4984
5141
|
let upload = void 0;
|
|
4985
5142
|
if (exuluConfig?.fileUploads?.s3endpoint && exuluConfig?.fileUploads?.s3key && exuluConfig?.fileUploads?.s3secret && exuluConfig?.fileUploads?.s3Bucket) {
|
|
@@ -5032,7 +5189,6 @@ var convertToolsArrayToObject = (currentTools, allExuluTools, configs, providera
|
|
|
5032
5189
|
acc[curr.id] = curr;
|
|
5033
5190
|
return acc;
|
|
5034
5191
|
}, {});
|
|
5035
|
-
console.log("[EXULU] Config", config);
|
|
5036
5192
|
const response = await cur.tool.execute({
|
|
5037
5193
|
...inputs,
|
|
5038
5194
|
sessionID,
|
|
@@ -5047,7 +5203,7 @@ var convertToolsArrayToObject = (currentTools, allExuluTools, configs, providera
|
|
|
5047
5203
|
contexts: contextsMap,
|
|
5048
5204
|
upload,
|
|
5049
5205
|
exuluConfig,
|
|
5050
|
-
|
|
5206
|
+
toolVariablesConfig: toolVariableConfig ? toolVariableConfig.config.reduce((acc, curr) => {
|
|
5051
5207
|
acc[curr.name] = curr.value;
|
|
5052
5208
|
return acc;
|
|
5053
5209
|
}, {}) : {}
|
|
@@ -5081,7 +5237,6 @@ var hydrateVariables = async (tool2) => {
|
|
|
5081
5237
|
const variableName = toolConfig.variable;
|
|
5082
5238
|
const variable = await db3.from("variables").where({ name: variableName }).first();
|
|
5083
5239
|
if (!variable) {
|
|
5084
|
-
console.error("[EXULU] Variable " + variableName + " not found.");
|
|
5085
5240
|
throw new Error("Variable " + variableName + " not found.");
|
|
5086
5241
|
}
|
|
5087
5242
|
let value = variable.value;
|
|
@@ -5093,7 +5248,6 @@ var hydrateVariables = async (tool2) => {
|
|
|
5093
5248
|
return toolConfig;
|
|
5094
5249
|
});
|
|
5095
5250
|
await Promise.all(promises);
|
|
5096
|
-
console.log("[EXULU] Variable values retrieved and added to tool config.");
|
|
5097
5251
|
return tool2;
|
|
5098
5252
|
};
|
|
5099
5253
|
function generateSlug(name) {
|
|
@@ -5317,7 +5471,21 @@ var ExuluAgent2 = class {
|
|
|
5317
5471
|
});
|
|
5318
5472
|
}
|
|
5319
5473
|
console.log("[EXULU] Message count for agent: " + this.name, "loaded for generating sync.", messages.length);
|
|
5320
|
-
|
|
5474
|
+
let project;
|
|
5475
|
+
if (session) {
|
|
5476
|
+
const sessionData = await getSession({ sessionID: session });
|
|
5477
|
+
project = sessionData.project;
|
|
5478
|
+
}
|
|
5479
|
+
const personalizationInformation = exuluConfig?.privacy?.systemPromptPersonalization !== false ? `
|
|
5480
|
+
${user?.firstname ? `The users first name is "${user.firstname}"` : ""}
|
|
5481
|
+
${user?.lastname ? `The users last name is "${user.lastname}"` : ""}
|
|
5482
|
+
${user?.email ? `The users email is "${user.email}"` : ""}
|
|
5483
|
+
` : "";
|
|
5484
|
+
const genericContext = `IMPORTANT general information:
|
|
5485
|
+
${personalizationInformation}
|
|
5486
|
+
The current date is "${(/* @__PURE__ */ new Date()).toLocaleDateString()}" and the current time is "${(/* @__PURE__ */ new Date()).toLocaleTimeString()}".
|
|
5487
|
+
If the user does not explicitly provide the current date, for examle when saying ' this weekend', you should assume
|
|
5488
|
+
they are talking with the current date in mind as a reference.`;
|
|
5321
5489
|
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.";
|
|
5322
5490
|
system += "\n\n" + genericContext;
|
|
5323
5491
|
if (prompt) {
|
|
@@ -5342,7 +5510,7 @@ var ExuluAgent2 = class {
|
|
|
5342
5510
|
system,
|
|
5343
5511
|
prompt,
|
|
5344
5512
|
maxRetries: 2,
|
|
5345
|
-
tools: convertToolsArrayToObject(
|
|
5513
|
+
tools: await convertToolsArrayToObject(
|
|
5346
5514
|
currentTools,
|
|
5347
5515
|
allExuluTools,
|
|
5348
5516
|
toolConfigs,
|
|
@@ -5351,7 +5519,8 @@ var ExuluAgent2 = class {
|
|
|
5351
5519
|
user,
|
|
5352
5520
|
exuluConfig,
|
|
5353
5521
|
session,
|
|
5354
|
-
req
|
|
5522
|
+
req,
|
|
5523
|
+
project
|
|
5355
5524
|
),
|
|
5356
5525
|
stopWhen: [stepCountIs(2)]
|
|
5357
5526
|
});
|
|
@@ -5402,7 +5571,7 @@ var ExuluAgent2 = class {
|
|
|
5402
5571
|
ignoreIncompleteToolCalls: true
|
|
5403
5572
|
}),
|
|
5404
5573
|
maxRetries: 2,
|
|
5405
|
-
tools: convertToolsArrayToObject(
|
|
5574
|
+
tools: await convertToolsArrayToObject(
|
|
5406
5575
|
currentTools,
|
|
5407
5576
|
allExuluTools,
|
|
5408
5577
|
toolConfigs,
|
|
@@ -5411,7 +5580,8 @@ var ExuluAgent2 = class {
|
|
|
5411
5580
|
user,
|
|
5412
5581
|
exuluConfig,
|
|
5413
5582
|
session,
|
|
5414
|
-
req
|
|
5583
|
+
req,
|
|
5584
|
+
project
|
|
5415
5585
|
),
|
|
5416
5586
|
stopWhen: [stepCountIs(2)]
|
|
5417
5587
|
});
|
|
@@ -5461,7 +5631,6 @@ var ExuluAgent2 = class {
|
|
|
5461
5631
|
*/
|
|
5462
5632
|
async processFilePartsInMessages(messages) {
|
|
5463
5633
|
const processedMessages = await Promise.all(messages.map(async (message) => {
|
|
5464
|
-
console.log("[EXULU] Processing file parts in messages: " + JSON.stringify(message, null, 2));
|
|
5465
5634
|
if (message.role !== "user" || !Array.isArray(message.parts)) {
|
|
5466
5635
|
return message;
|
|
5467
5636
|
}
|
|
@@ -5472,7 +5641,7 @@ var ExuluAgent2 = class {
|
|
|
5472
5641
|
const { mediaType, url, filename } = part;
|
|
5473
5642
|
const imageTypes = ["image/png", "image/jpeg", "image/jpg", "image/gif", "image/webp"];
|
|
5474
5643
|
if (imageTypes.includes(mediaType)) {
|
|
5475
|
-
console.log(`[EXULU] Converting file part to image part: ${filename}`);
|
|
5644
|
+
console.log(`[EXULU] Converting file part to image part: ${filename} `);
|
|
5476
5645
|
return {
|
|
5477
5646
|
type: "image",
|
|
5478
5647
|
image: url,
|
|
@@ -5483,7 +5652,7 @@ var ExuluAgent2 = class {
|
|
|
5483
5652
|
try {
|
|
5484
5653
|
const response = await fetch(url);
|
|
5485
5654
|
if (!response.ok) {
|
|
5486
|
-
console.error(`[EXULU] Failed to fetch file: ${filename}, status: ${response.status}`);
|
|
5655
|
+
console.error(`[EXULU] Failed to fetch file: ${filename}, status: ${response.status} `);
|
|
5487
5656
|
return {
|
|
5488
5657
|
type: "text",
|
|
5489
5658
|
text: `[Error: Could not load file ${filename}]`
|
|
@@ -5496,9 +5665,9 @@ var ExuluAgent2 = class {
|
|
|
5496
5665
|
});
|
|
5497
5666
|
return {
|
|
5498
5667
|
type: "text",
|
|
5499
|
-
text: `<file name="${filename}">
|
|
5500
|
-
${extractedText}
|
|
5501
|
-
</file>`
|
|
5668
|
+
text: `<file file name = "${filename}" >
|
|
5669
|
+
${extractedText}
|
|
5670
|
+
</file>`
|
|
5502
5671
|
};
|
|
5503
5672
|
} catch (error) {
|
|
5504
5673
|
console.error(`[EXULU] Error processing file ${filename}:`, error);
|
|
@@ -5548,7 +5717,10 @@ ${extractedText}
|
|
|
5548
5717
|
});
|
|
5549
5718
|
let messages = [];
|
|
5550
5719
|
let previousMessagesContent = previousMessages || [];
|
|
5720
|
+
let project;
|
|
5551
5721
|
if (session) {
|
|
5722
|
+
const sessionData = await getSession({ sessionID: session });
|
|
5723
|
+
project = sessionData.project;
|
|
5552
5724
|
console.log("[EXULU] loading previous messages from session: " + session);
|
|
5553
5725
|
const previousMessages2 = await getAgentMessages({
|
|
5554
5726
|
session,
|
|
@@ -5567,20 +5739,18 @@ ${extractedText}
|
|
|
5567
5739
|
messages = messages.filter(
|
|
5568
5740
|
(message2, index, self) => index === self.findIndex((t) => t.id === message2.id)
|
|
5569
5741
|
);
|
|
5570
|
-
console.log("[EXULU] Processing file parts in messages for OpenAI Responses API compatibility");
|
|
5571
5742
|
messages = await this.processFilePartsInMessages(messages);
|
|
5572
5743
|
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.";
|
|
5573
5744
|
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.";
|
|
5574
5745
|
system += "\n\n" + genericContext;
|
|
5575
|
-
console.log("[EXULU] tools for agent: " + this.name, currentTools?.map((x) => x.name + " (" + x.id + ")"));
|
|
5576
|
-
console.log("[EXULU] system", system.slice(0, 100) + "...");
|
|
5577
5746
|
const result = streamText({
|
|
5578
5747
|
model,
|
|
5579
5748
|
// Should be a LanguageModelV1
|
|
5580
5749
|
messages: convertToModelMessages(messages, {
|
|
5581
5750
|
ignoreIncompleteToolCalls: true
|
|
5582
5751
|
}),
|
|
5583
|
-
//
|
|
5752
|
+
// PrepareStep could be used here to set the model
|
|
5753
|
+
// for the first step or change other parameters.
|
|
5584
5754
|
system,
|
|
5585
5755
|
maxRetries: 2,
|
|
5586
5756
|
providerOptions: {
|
|
@@ -5588,7 +5758,7 @@ ${extractedText}
|
|
|
5588
5758
|
reasoningSummary: "auto"
|
|
5589
5759
|
}
|
|
5590
5760
|
},
|
|
5591
|
-
tools: convertToolsArrayToObject(
|
|
5761
|
+
tools: await convertToolsArrayToObject(
|
|
5592
5762
|
currentTools,
|
|
5593
5763
|
allExuluTools,
|
|
5594
5764
|
toolConfigs,
|
|
@@ -5597,7 +5767,8 @@ ${extractedText}
|
|
|
5597
5767
|
user,
|
|
5598
5768
|
exuluConfig,
|
|
5599
5769
|
session,
|
|
5600
|
-
req
|
|
5770
|
+
req,
|
|
5771
|
+
project
|
|
5601
5772
|
),
|
|
5602
5773
|
onError: (error) => {
|
|
5603
5774
|
console.error("[EXULU] chat stream error.", error);
|
|
@@ -5693,12 +5864,10 @@ var ExuluEmbedder = class {
|
|
|
5693
5864
|
id
|
|
5694
5865
|
} = setting;
|
|
5695
5866
|
let value = "";
|
|
5696
|
-
console.log("[EXULU] variable name", variableName);
|
|
5697
5867
|
const variable = await db3.from("variables").where({ name: variableName }).first();
|
|
5698
5868
|
if (!variable) {
|
|
5699
5869
|
throw new Error("Variable not found for embedder setting: " + name + " in context: " + context + " and embedder: " + this.id);
|
|
5700
5870
|
}
|
|
5701
|
-
console.log("[EXULU] variable", variable);
|
|
5702
5871
|
if (variable.encrypted) {
|
|
5703
5872
|
if (!process.env.NEXTAUTH_SECRET) {
|
|
5704
5873
|
throw new Error("NEXTAUTH_SECRET environment variable is not set, cannot decrypt variable: " + name);
|
|
@@ -5710,14 +5879,12 @@ var ExuluEmbedder = class {
|
|
|
5710
5879
|
throw new Error("Decryption returned empty string - invalid key or corrupted data");
|
|
5711
5880
|
}
|
|
5712
5881
|
value = decrypted;
|
|
5713
|
-
console.log("[EXULU] successfully decrypted value for", name);
|
|
5714
5882
|
} catch (error) {
|
|
5715
5883
|
throw new Error(`Failed to decrypt variable "${name}" for embedder setting in context "${context}": ${error instanceof Error ? error.message : "Unknown error"}. Verify that NEXTAUTH_SECRET matches the key used during encryption.`);
|
|
5716
5884
|
}
|
|
5717
5885
|
} else {
|
|
5718
5886
|
value = variable.value;
|
|
5719
5887
|
}
|
|
5720
|
-
console.log("[EXULU] variable value", value);
|
|
5721
5888
|
hydrated.push({
|
|
5722
5889
|
id: id || "",
|
|
5723
5890
|
name,
|
|
@@ -5825,14 +5992,20 @@ var ExuluStorage = class {
|
|
|
5825
5992
|
getPresignedUrl = async (key) => {
|
|
5826
5993
|
return await getPresignedUrl(key, this.config);
|
|
5827
5994
|
};
|
|
5828
|
-
uploadFile = async (
|
|
5829
|
-
return await uploadFile(
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
5995
|
+
uploadFile = async (file, key, type, user, metadata) => {
|
|
5996
|
+
return await uploadFile(
|
|
5997
|
+
file,
|
|
5998
|
+
key,
|
|
5999
|
+
this.config,
|
|
6000
|
+
{
|
|
6001
|
+
contentType: type,
|
|
6002
|
+
metadata: {
|
|
6003
|
+
...metadata,
|
|
6004
|
+
type
|
|
6005
|
+
}
|
|
6006
|
+
},
|
|
6007
|
+
user
|
|
6008
|
+
);
|
|
5836
6009
|
};
|
|
5837
6010
|
// todo add upload and delete methods
|
|
5838
6011
|
};
|
|
@@ -5852,7 +6025,19 @@ var ExuluContext = class {
|
|
|
5852
6025
|
// todo typings
|
|
5853
6026
|
configuration;
|
|
5854
6027
|
sources = [];
|
|
5855
|
-
constructor({
|
|
6028
|
+
constructor({
|
|
6029
|
+
id,
|
|
6030
|
+
name,
|
|
6031
|
+
description,
|
|
6032
|
+
embedder,
|
|
6033
|
+
active,
|
|
6034
|
+
rateLimit,
|
|
6035
|
+
fields,
|
|
6036
|
+
queryRewriter,
|
|
6037
|
+
resultReranker,
|
|
6038
|
+
configuration,
|
|
6039
|
+
sources
|
|
6040
|
+
}) {
|
|
5856
6041
|
this.id = id;
|
|
5857
6042
|
this.name = name;
|
|
5858
6043
|
this.fields = fields || [];
|
|
@@ -5869,7 +6054,7 @@ var ExuluContext = class {
|
|
|
5869
6054
|
this.queryRewriter = queryRewriter;
|
|
5870
6055
|
this.resultReranker = resultReranker;
|
|
5871
6056
|
}
|
|
5872
|
-
processField = async (trigger,
|
|
6057
|
+
processField = async (trigger, item, exuluConfig, user, role) => {
|
|
5873
6058
|
console.log("[EXULU] processing field", item.field, " in context", this.id);
|
|
5874
6059
|
console.log("[EXULU] fields", this.fields.map((field2) => field2.name));
|
|
5875
6060
|
const field = this.fields.find((field2) => {
|
|
@@ -5879,7 +6064,7 @@ var ExuluContext = class {
|
|
|
5879
6064
|
console.error("[EXULU] field not found or processor not set for field", item.field, " in context", this.id);
|
|
5880
6065
|
throw new Error("Field not found or processor not set for field " + item.field + " in context " + this.id);
|
|
5881
6066
|
}
|
|
5882
|
-
const exuluStorage = new ExuluStorage({ config });
|
|
6067
|
+
const exuluStorage = new ExuluStorage({ config: exuluConfig });
|
|
5883
6068
|
const queue = await field.processor.config?.queue;
|
|
5884
6069
|
if (queue?.queue.name) {
|
|
5885
6070
|
console.log("[EXULU] processor is in queue mode, scheduling job.");
|
|
@@ -5905,6 +6090,7 @@ var ExuluContext = class {
|
|
|
5905
6090
|
job: job.id
|
|
5906
6091
|
};
|
|
5907
6092
|
}
|
|
6093
|
+
console.log("[EXULU] POS 1 -- EXULU CONTEXT PROCESS FIELD");
|
|
5908
6094
|
const result = await field.processor.execute({
|
|
5909
6095
|
item,
|
|
5910
6096
|
user,
|
|
@@ -5917,13 +6103,24 @@ var ExuluContext = class {
|
|
|
5917
6103
|
delete: this.deleteItem
|
|
5918
6104
|
}
|
|
5919
6105
|
},
|
|
5920
|
-
|
|
6106
|
+
exuluConfig
|
|
5921
6107
|
});
|
|
5922
6108
|
return {
|
|
5923
6109
|
result,
|
|
5924
6110
|
job: void 0
|
|
5925
6111
|
};
|
|
5926
6112
|
};
|
|
6113
|
+
search = async (options) => {
|
|
6114
|
+
const { db: db3 } = await postgresClient();
|
|
6115
|
+
const result = await vectorSearch({
|
|
6116
|
+
...options,
|
|
6117
|
+
user: options.user,
|
|
6118
|
+
role: options.role,
|
|
6119
|
+
context: this,
|
|
6120
|
+
db: db3
|
|
6121
|
+
});
|
|
6122
|
+
return result;
|
|
6123
|
+
};
|
|
5927
6124
|
deleteAll = async () => {
|
|
5928
6125
|
const { db: db3 } = await postgresClient();
|
|
5929
6126
|
await db3.from(getTableName(this.id)).delete();
|
|
@@ -5933,8 +6130,11 @@ var ExuluContext = class {
|
|
|
5933
6130
|
results: []
|
|
5934
6131
|
};
|
|
5935
6132
|
};
|
|
5936
|
-
executeSource = async (source, inputs) => {
|
|
5937
|
-
return await source.execute(
|
|
6133
|
+
executeSource = async (source, inputs, exuluConfig) => {
|
|
6134
|
+
return await source.execute({
|
|
6135
|
+
...inputs,
|
|
6136
|
+
exuluConfig
|
|
6137
|
+
});
|
|
5938
6138
|
};
|
|
5939
6139
|
tableExists = async () => {
|
|
5940
6140
|
const { db: db3 } = await postgresClient();
|
|
@@ -5990,7 +6190,7 @@ var ExuluContext = class {
|
|
|
5990
6190
|
job
|
|
5991
6191
|
};
|
|
5992
6192
|
};
|
|
5993
|
-
createItem = async (item, config, user, role, upsert) => {
|
|
6193
|
+
createItem = async (item, config, user, role, upsert, generateEmbeddingsOverwrite) => {
|
|
5994
6194
|
if (upsert && (!item.id && !item.external_id)) {
|
|
5995
6195
|
throw new Error("Item id or external id is required for upsert.");
|
|
5996
6196
|
}
|
|
@@ -6017,12 +6217,40 @@ var ExuluContext = class {
|
|
|
6017
6217
|
throw new Error("Failed to create item.");
|
|
6018
6218
|
}
|
|
6019
6219
|
console.log("[EXULU] context configuration", this.configuration);
|
|
6020
|
-
|
|
6220
|
+
let jobs = [];
|
|
6221
|
+
let shouldGenerateEmbeddings = this.embedder && generateEmbeddingsOverwrite !== false && (generateEmbeddingsOverwrite || this.configuration.calculateVectors === "onUpdate" || this.configuration.calculateVectors === "onInsert" || this.configuration.calculateVectors === "always");
|
|
6222
|
+
for (const [key, value] of Object.entries(item)) {
|
|
6223
|
+
console.log("[EXULU] Checking for processors for field", key);
|
|
6224
|
+
const processor = this.fields.find((field) => field.name === key.replace("_s3key", ""))?.processor;
|
|
6225
|
+
console.log("[EXULU] Processor found", processor);
|
|
6226
|
+
if (processor && (processor?.config?.trigger === "onInsert" || processor?.config?.trigger === "onUpdate" || processor?.config?.trigger === "always")) {
|
|
6227
|
+
const {
|
|
6228
|
+
job: processorJob,
|
|
6229
|
+
result: processorResult
|
|
6230
|
+
} = await this.processField(
|
|
6231
|
+
"api",
|
|
6232
|
+
{
|
|
6233
|
+
...item,
|
|
6234
|
+
id: results[0].id,
|
|
6235
|
+
field: key
|
|
6236
|
+
},
|
|
6237
|
+
config,
|
|
6238
|
+
user,
|
|
6239
|
+
role
|
|
6240
|
+
);
|
|
6241
|
+
if (processorJob) {
|
|
6242
|
+
jobs.push(processorJob);
|
|
6243
|
+
}
|
|
6244
|
+
if (!processorJob && processor.config?.generateEmbeddings) {
|
|
6245
|
+
shouldGenerateEmbeddings = true;
|
|
6246
|
+
}
|
|
6247
|
+
}
|
|
6248
|
+
}
|
|
6249
|
+
if (shouldGenerateEmbeddings) {
|
|
6021
6250
|
console.log("[EXULU] generating embeddings for item", results[0].id);
|
|
6022
|
-
const { job } = await this.embeddings.generate.one({
|
|
6251
|
+
const { job: embeddingsJob } = await this.embeddings.generate.one({
|
|
6023
6252
|
item: {
|
|
6024
6253
|
...item,
|
|
6025
|
-
// important we need to full record here with all fields for the embedder
|
|
6026
6254
|
id: results[0].id
|
|
6027
6255
|
},
|
|
6028
6256
|
user,
|
|
@@ -6030,17 +6258,16 @@ var ExuluContext = class {
|
|
|
6030
6258
|
trigger: "api",
|
|
6031
6259
|
config
|
|
6032
6260
|
});
|
|
6033
|
-
|
|
6034
|
-
|
|
6035
|
-
|
|
6036
|
-
};
|
|
6261
|
+
if (embeddingsJob) {
|
|
6262
|
+
jobs.push(embeddingsJob);
|
|
6263
|
+
}
|
|
6037
6264
|
}
|
|
6038
6265
|
return {
|
|
6039
6266
|
item: results[0],
|
|
6040
|
-
job: void 0
|
|
6267
|
+
job: jobs.length > 0 ? jobs.join(",") : void 0
|
|
6041
6268
|
};
|
|
6042
6269
|
};
|
|
6043
|
-
updateItem = async (item, config, user, role) => {
|
|
6270
|
+
updateItem = async (item, config, user, role, generateEmbeddingsOverwrite) => {
|
|
6044
6271
|
const { db: db3 } = await postgresClient();
|
|
6045
6272
|
if (item.field) {
|
|
6046
6273
|
delete item.field;
|
|
@@ -6064,8 +6291,35 @@ var ExuluContext = class {
|
|
|
6064
6291
|
}
|
|
6065
6292
|
).returning("id");
|
|
6066
6293
|
await mutation;
|
|
6067
|
-
|
|
6068
|
-
|
|
6294
|
+
let jobs = [];
|
|
6295
|
+
let shouldGenerateEmbeddings = this.embedder && generateEmbeddingsOverwrite !== false && (generateEmbeddingsOverwrite || this.configuration.calculateVectors === "onUpdate" || this.configuration.calculateVectors === "always");
|
|
6296
|
+
for (const [key, value] of Object.entries(item)) {
|
|
6297
|
+
const processor = this.fields.find((field) => field.name === key.replace("_s3key", ""))?.processor;
|
|
6298
|
+
if (processor && (processor?.config?.trigger === "onInsert" || processor?.config?.trigger === "onUpdate" || processor?.config?.trigger === "always")) {
|
|
6299
|
+
const {
|
|
6300
|
+
job: processorJob,
|
|
6301
|
+
result: processorResult
|
|
6302
|
+
} = await this.processField(
|
|
6303
|
+
"api",
|
|
6304
|
+
{
|
|
6305
|
+
...item,
|
|
6306
|
+
id: record.id,
|
|
6307
|
+
field: key
|
|
6308
|
+
},
|
|
6309
|
+
config,
|
|
6310
|
+
user,
|
|
6311
|
+
role
|
|
6312
|
+
);
|
|
6313
|
+
if (processorJob) {
|
|
6314
|
+
jobs.push(processorJob);
|
|
6315
|
+
}
|
|
6316
|
+
if (!processorJob && processor.config?.generateEmbeddings) {
|
|
6317
|
+
shouldGenerateEmbeddings = true;
|
|
6318
|
+
}
|
|
6319
|
+
}
|
|
6320
|
+
}
|
|
6321
|
+
if (shouldGenerateEmbeddings) {
|
|
6322
|
+
const { job: embeddingsJob } = await this.embeddings.generate.one({
|
|
6069
6323
|
item: record,
|
|
6070
6324
|
// important we need to full record here with all fields for the embedder
|
|
6071
6325
|
user,
|
|
@@ -6073,14 +6327,13 @@ var ExuluContext = class {
|
|
|
6073
6327
|
trigger: "api",
|
|
6074
6328
|
config
|
|
6075
6329
|
});
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
};
|
|
6330
|
+
if (embeddingsJob) {
|
|
6331
|
+
jobs.push(embeddingsJob);
|
|
6332
|
+
}
|
|
6080
6333
|
}
|
|
6081
6334
|
return {
|
|
6082
6335
|
item: record,
|
|
6083
|
-
job: void 0
|
|
6336
|
+
job: jobs.length > 0 ? jobs.join(",") : void 0
|
|
6084
6337
|
};
|
|
6085
6338
|
};
|
|
6086
6339
|
deleteItem = async (item, user, role) => {
|
|
@@ -6107,6 +6360,34 @@ var ExuluContext = class {
|
|
|
6107
6360
|
job: void 0
|
|
6108
6361
|
};
|
|
6109
6362
|
};
|
|
6363
|
+
getItem = async ({ item }) => {
|
|
6364
|
+
const { db: db3 } = await postgresClient();
|
|
6365
|
+
if (!item.id && !item.external_id) {
|
|
6366
|
+
throw new Error("Item id or external id is required to get an item.");
|
|
6367
|
+
}
|
|
6368
|
+
const result = await db3.from(getTableName(this.id)).where({
|
|
6369
|
+
...item.id ? { id: item.id } : {},
|
|
6370
|
+
...item.external_id ? { external_id: item.external_id } : {}
|
|
6371
|
+
}).first();
|
|
6372
|
+
if (result) {
|
|
6373
|
+
const chunksCount = await db3.from(getChunksTableName(this.id)).where(
|
|
6374
|
+
{ source: result.id }
|
|
6375
|
+
).count("id");
|
|
6376
|
+
result.chunksCount = Number(chunksCount[0].count) || 0;
|
|
6377
|
+
}
|
|
6378
|
+
return result;
|
|
6379
|
+
};
|
|
6380
|
+
getItems = async ({
|
|
6381
|
+
filters,
|
|
6382
|
+
fields
|
|
6383
|
+
}) => {
|
|
6384
|
+
const { db: db3 } = await postgresClient();
|
|
6385
|
+
let query = db3.from(getTableName(this.id)).select(fields || "*");
|
|
6386
|
+
const tableDefinition = contextToTableDefinition(this);
|
|
6387
|
+
query = applyFilters(query, filters || [], tableDefinition);
|
|
6388
|
+
const items = await query;
|
|
6389
|
+
return items;
|
|
6390
|
+
};
|
|
6110
6391
|
embeddings = {
|
|
6111
6392
|
generate: {
|
|
6112
6393
|
one: async ({
|
|
@@ -6123,6 +6404,12 @@ var ExuluContext = class {
|
|
|
6123
6404
|
if (!item.id) {
|
|
6124
6405
|
throw new Error("Item id is required for generating embeddings.");
|
|
6125
6406
|
}
|
|
6407
|
+
const { db: db3 } = await postgresClient();
|
|
6408
|
+
const record = await db3.from(getTableName(this.id)).where({ id: item.id }).first();
|
|
6409
|
+
if (!record) {
|
|
6410
|
+
throw new Error("Item not found.");
|
|
6411
|
+
}
|
|
6412
|
+
item = record;
|
|
6126
6413
|
const queue = await this.embedder.queue;
|
|
6127
6414
|
if (queue?.queue.name) {
|
|
6128
6415
|
console.log("[EXULU] embedder is in queue mode, scheduling job.");
|
|
@@ -6888,7 +7175,7 @@ Mood: friendly and intelligent.
|
|
|
6888
7175
|
const user = authenticationResult.user;
|
|
6889
7176
|
let agentQuery = db3("agents");
|
|
6890
7177
|
agentQuery.select("*");
|
|
6891
|
-
agentQuery = applyAccessControl(agentsSchema2(), authenticationResult.user
|
|
7178
|
+
agentQuery = applyAccessControl(agentsSchema2(), agentQuery, authenticationResult.user);
|
|
6892
7179
|
agentQuery.where({ id: req.params.agent });
|
|
6893
7180
|
const agent = await agentQuery.first();
|
|
6894
7181
|
if (!agent) {
|
|
@@ -6905,7 +7192,7 @@ Mood: friendly and intelligent.
|
|
|
6905
7192
|
} else {
|
|
6906
7193
|
let projectQuery = db3("projects");
|
|
6907
7194
|
projectQuery.select("*");
|
|
6908
|
-
projectQuery = applyAccessControl(projectsSchema2(), authenticationResult.user
|
|
7195
|
+
projectQuery = applyAccessControl(projectsSchema2(), projectQuery, authenticationResult.user);
|
|
6909
7196
|
projectQuery.where({ id: req.params.project });
|
|
6910
7197
|
project = await projectQuery.first();
|
|
6911
7198
|
if (!project) {
|
|
@@ -7247,6 +7534,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7247
7534
|
if (!data.role) {
|
|
7248
7535
|
throw new Error(`Role not set for processor job.`);
|
|
7249
7536
|
}
|
|
7537
|
+
console.log("[EXULU] POS 2 -- EXULU CONTEXT PROCESS FIELD");
|
|
7250
7538
|
const result = await field.processor.execute({
|
|
7251
7539
|
item: data.inputs,
|
|
7252
7540
|
user: data.user,
|
|
@@ -7261,9 +7549,24 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7261
7549
|
},
|
|
7262
7550
|
config
|
|
7263
7551
|
});
|
|
7552
|
+
let jobs = [];
|
|
7553
|
+
if (field.processor.config?.generateEmbeddings) {
|
|
7554
|
+
const { job: embeddingsJob } = await context.embeddings.generate.one({
|
|
7555
|
+
item: data.inputs,
|
|
7556
|
+
user: data.user,
|
|
7557
|
+
role: data.role,
|
|
7558
|
+
trigger: "api",
|
|
7559
|
+
config
|
|
7560
|
+
});
|
|
7561
|
+
if (embeddingsJob) {
|
|
7562
|
+
jobs.push(embeddingsJob);
|
|
7563
|
+
}
|
|
7564
|
+
}
|
|
7264
7565
|
return {
|
|
7265
7566
|
result,
|
|
7266
|
-
metadata: {
|
|
7567
|
+
metadata: {
|
|
7568
|
+
jobs: jobs.length > 0 ? jobs.join(",") : void 0
|
|
7569
|
+
}
|
|
7267
7570
|
};
|
|
7268
7571
|
}
|
|
7269
7572
|
if (data.type === "eval_run") {
|
|
@@ -7949,7 +8252,7 @@ var ExuluMCP = class {
|
|
|
7949
8252
|
console.log("[EXULU] MCP tool inputs", inputs);
|
|
7950
8253
|
console.log("[EXULU] MCP tool args", args);
|
|
7951
8254
|
const configValues = agentInstance.tools;
|
|
7952
|
-
const tools = convertToolsArrayToObject(
|
|
8255
|
+
const tools = await convertToolsArrayToObject(
|
|
7953
8256
|
[tool2],
|
|
7954
8257
|
allTools,
|
|
7955
8258
|
configValues,
|