@exulu/backend 1.37.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 +484 -109
- package/dist/index.d.cts +64 -12
- package/dist/index.d.ts +64 -12
- package/dist/index.js +484 -109
- package/package.json +2 -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 {
|
|
@@ -463,7 +469,9 @@ var getToken = async (authHeader) => {
|
|
|
463
469
|
}
|
|
464
470
|
try {
|
|
465
471
|
const secret = process.env.NEXTAUTH_SECRET;
|
|
466
|
-
const
|
|
472
|
+
const secretBuffer = Buffer.from(secret, "utf-8");
|
|
473
|
+
const base64Secret = secretBuffer.toString("base64url");
|
|
474
|
+
const jwk = await (0, import_jose.importJWK)({ k: base64Secret, alg: "HS256", kty: "oct" });
|
|
467
475
|
const { payload } = await (0, import_jose.jwtVerify)(token, jwk);
|
|
468
476
|
return payload;
|
|
469
477
|
} catch (error) {
|
|
@@ -502,6 +510,8 @@ var authentication = async ({
|
|
|
502
510
|
type: "api",
|
|
503
511
|
id: 192837465,
|
|
504
512
|
email: "internal@exulu.com",
|
|
513
|
+
firstname: "API",
|
|
514
|
+
lastname: "User",
|
|
505
515
|
role: {
|
|
506
516
|
id: "internal",
|
|
507
517
|
name: "Internal",
|
|
@@ -1335,10 +1345,10 @@ var rbacSchema = {
|
|
|
1335
1345
|
name: "user_id",
|
|
1336
1346
|
type: "number"
|
|
1337
1347
|
},
|
|
1338
|
-
{
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
},
|
|
1348
|
+
/* {
|
|
1349
|
+
name: "project_id",
|
|
1350
|
+
type: "uuid"
|
|
1351
|
+
}, */
|
|
1342
1352
|
{
|
|
1343
1353
|
name: "rights",
|
|
1344
1354
|
type: "text",
|
|
@@ -2248,7 +2258,7 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2248
2258
|
});
|
|
2249
2259
|
return {
|
|
2250
2260
|
// Filter result to only include requested fields
|
|
2251
|
-
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 }),
|
|
2252
2262
|
job
|
|
2253
2263
|
};
|
|
2254
2264
|
},
|
|
@@ -2296,7 +2306,7 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2296
2306
|
}
|
|
2297
2307
|
const { job } = await postprocessUpdate({ table, requestedFields, agents, contexts, tools, result, user: context.user.id, role: context.user.role?.id, config });
|
|
2298
2308
|
return {
|
|
2299
|
-
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 }),
|
|
2300
2310
|
job
|
|
2301
2311
|
};
|
|
2302
2312
|
},
|
|
@@ -2337,7 +2347,7 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2337
2347
|
const result = await db3.from(tableNamePlural).select(Object.keys(columns)).where({ id }).first();
|
|
2338
2348
|
const { job } = await postprocessUpdate({ table, requestedFields, agents, contexts, tools, result, user: context.user.id, role: context.user.role?.id, config });
|
|
2339
2349
|
return {
|
|
2340
|
-
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 }),
|
|
2341
2351
|
job
|
|
2342
2352
|
};
|
|
2343
2353
|
},
|
|
@@ -2369,7 +2379,7 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2369
2379
|
}).del();
|
|
2370
2380
|
}
|
|
2371
2381
|
await postprocessDeletion({ table, requestedFields, agents, contexts, tools, result });
|
|
2372
|
-
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 });
|
|
2373
2383
|
},
|
|
2374
2384
|
[`${tableNamePlural}RemoveOne`]: async (_, args, context, info) => {
|
|
2375
2385
|
const { where } = args;
|
|
@@ -2393,7 +2403,7 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2393
2403
|
}
|
|
2394
2404
|
await db3(tableNamePlural).where(where).del();
|
|
2395
2405
|
await postprocessDeletion({ table, requestedFields, agents, contexts, tools, result });
|
|
2396
|
-
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 });
|
|
2397
2407
|
}
|
|
2398
2408
|
};
|
|
2399
2409
|
if (table.type === "items") {
|
|
@@ -2426,20 +2436,20 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2426
2436
|
}
|
|
2427
2437
|
const { db: db3 } = context;
|
|
2428
2438
|
let query = db3.from(tableNamePlural).select("*").where({ id: args.item });
|
|
2429
|
-
query = applyAccessControl(table, context.user
|
|
2439
|
+
query = applyAccessControl(table, query, context.user);
|
|
2430
2440
|
const item = await query.first();
|
|
2431
2441
|
if (!item) {
|
|
2432
2442
|
throw new Error("Item not found, or your user does not have access to it.");
|
|
2433
2443
|
}
|
|
2434
2444
|
const { job, result } = await exists.processField(
|
|
2435
2445
|
"api",
|
|
2436
|
-
context.user.id,
|
|
2437
|
-
context.user.role?.id,
|
|
2438
2446
|
{
|
|
2439
2447
|
...item,
|
|
2440
2448
|
field: args.field
|
|
2441
2449
|
},
|
|
2442
|
-
config
|
|
2450
|
+
config,
|
|
2451
|
+
context.user.id,
|
|
2452
|
+
context.user.role?.id
|
|
2443
2453
|
);
|
|
2444
2454
|
return {
|
|
2445
2455
|
message: job ? "Processing job scheduled." : "Item processed successfully.",
|
|
@@ -2486,7 +2496,10 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2486
2496
|
};
|
|
2487
2497
|
}
|
|
2488
2498
|
console.log("[EXULU] Executing source function directly");
|
|
2489
|
-
const result = await source.execute(
|
|
2499
|
+
const result = await source.execute({
|
|
2500
|
+
...args.inputs,
|
|
2501
|
+
exuluConfig: config
|
|
2502
|
+
});
|
|
2490
2503
|
let jobs = [];
|
|
2491
2504
|
let items = [];
|
|
2492
2505
|
for (const item of result) {
|
|
@@ -2610,14 +2623,14 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2610
2623
|
}
|
|
2611
2624
|
return mutations;
|
|
2612
2625
|
}
|
|
2613
|
-
var applyAccessControl = (table,
|
|
2626
|
+
var applyAccessControl = (table, query, user) => {
|
|
2614
2627
|
const tableNamePlural = table.name.plural.toLowerCase();
|
|
2615
|
-
if (table.name.plural !== "agent_sessions" && user
|
|
2628
|
+
if (table.name.plural !== "agent_sessions" && user?.super_admin === true) {
|
|
2616
2629
|
return query;
|
|
2617
2630
|
}
|
|
2618
|
-
console.log("[EXULU] user.role", user
|
|
2631
|
+
console.log("[EXULU] user.role", user?.role);
|
|
2619
2632
|
console.log("[EXULU] table.name.plural", table.name.plural);
|
|
2620
|
-
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")))) {
|
|
2621
2634
|
console.error("==== Access control error: no role found or no access to entity type. ====");
|
|
2622
2635
|
throw new Error("Access control error: no role found or no access to entity type.");
|
|
2623
2636
|
}
|
|
@@ -2706,7 +2719,7 @@ var removeAgentFields = (requestedFields) => {
|
|
|
2706
2719
|
filtered.push("backend");
|
|
2707
2720
|
return filtered;
|
|
2708
2721
|
};
|
|
2709
|
-
var addAgentFields = async (requestedFields, agents, result, tools, user) => {
|
|
2722
|
+
var addAgentFields = async (args, requestedFields, agents, result, tools, user, contexts) => {
|
|
2710
2723
|
let backend = agents.find((a) => a.id === result?.backend);
|
|
2711
2724
|
if (requestedFields.includes("providerName")) {
|
|
2712
2725
|
result.providerName = backend?.providerName || "";
|
|
@@ -2753,6 +2766,17 @@ var addAgentFields = async (requestedFields, agents, result, tools, user) => {
|
|
|
2753
2766
|
console.log("[EXULU] hydratedTool", hydratedTool);
|
|
2754
2767
|
return hydratedTool;
|
|
2755
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
|
+
}
|
|
2756
2780
|
result.tools = result.tools.filter((tool2) => tool2 !== null);
|
|
2757
2781
|
} else {
|
|
2758
2782
|
result.tools = [];
|
|
@@ -2891,6 +2915,7 @@ var postprocessDeletion = async ({
|
|
|
2891
2915
|
return result;
|
|
2892
2916
|
};
|
|
2893
2917
|
var finalizeRequestedFields = async ({
|
|
2918
|
+
args,
|
|
2894
2919
|
table,
|
|
2895
2920
|
requestedFields,
|
|
2896
2921
|
agents,
|
|
@@ -2907,11 +2932,28 @@ var finalizeRequestedFields = async ({
|
|
|
2907
2932
|
}
|
|
2908
2933
|
if (Array.isArray(result)) {
|
|
2909
2934
|
result = result.map((item) => {
|
|
2910
|
-
return finalizeRequestedFields({
|
|
2935
|
+
return finalizeRequestedFields({
|
|
2936
|
+
args,
|
|
2937
|
+
table,
|
|
2938
|
+
requestedFields,
|
|
2939
|
+
agents,
|
|
2940
|
+
contexts,
|
|
2941
|
+
tools,
|
|
2942
|
+
result: item,
|
|
2943
|
+
user
|
|
2944
|
+
});
|
|
2911
2945
|
});
|
|
2912
2946
|
} else {
|
|
2913
2947
|
if (table.name.singular === "agent") {
|
|
2914
|
-
result = await addAgentFields(
|
|
2948
|
+
result = await addAgentFields(
|
|
2949
|
+
args,
|
|
2950
|
+
requestedFields,
|
|
2951
|
+
agents,
|
|
2952
|
+
result,
|
|
2953
|
+
tools,
|
|
2954
|
+
user,
|
|
2955
|
+
contexts
|
|
2956
|
+
);
|
|
2915
2957
|
if (!requestedFields.includes("backend")) {
|
|
2916
2958
|
delete result.backend;
|
|
2917
2959
|
}
|
|
@@ -2988,18 +3030,18 @@ function createQueries(table, agents, tools, contexts) {
|
|
|
2988
3030
|
const requestedFields = getRequestedFields(info);
|
|
2989
3031
|
const sanitizedFields = sanitizeRequestedFields(table, requestedFields);
|
|
2990
3032
|
let query = db3.from(tableNamePlural).select(sanitizedFields).where({ id: args.id });
|
|
2991
|
-
query = applyAccessControl(table, context.user
|
|
3033
|
+
query = applyAccessControl(table, query, context.user);
|
|
2992
3034
|
let result = await query.first();
|
|
2993
|
-
return finalizeRequestedFields({ table, requestedFields, agents, contexts, tools, result, user: context.user });
|
|
3035
|
+
return finalizeRequestedFields({ args, table, requestedFields, agents, contexts, tools, result, user: context.user });
|
|
2994
3036
|
},
|
|
2995
3037
|
[`${tableNameSingular}ByIds`]: async (_, args, context, info) => {
|
|
2996
3038
|
const { db: db3 } = context;
|
|
2997
3039
|
const requestedFields = getRequestedFields(info);
|
|
2998
3040
|
const sanitizedFields = sanitizeRequestedFields(table, requestedFields);
|
|
2999
3041
|
let query = db3.from(tableNamePlural).select(sanitizedFields).whereIn("id", args.ids);
|
|
3000
|
-
query = applyAccessControl(table, context.user
|
|
3042
|
+
query = applyAccessControl(table, query, context.user);
|
|
3001
3043
|
let result = await query;
|
|
3002
|
-
return finalizeRequestedFields({ table, requestedFields, agents, contexts, tools, result, user: context.user });
|
|
3044
|
+
return finalizeRequestedFields({ args, table, requestedFields, agents, contexts, tools, result, user: context.user });
|
|
3003
3045
|
},
|
|
3004
3046
|
[`${tableNameSingular}One`]: async (_, args, context, info) => {
|
|
3005
3047
|
const { filters = [], sort } = args;
|
|
@@ -3008,10 +3050,10 @@ function createQueries(table, agents, tools, contexts) {
|
|
|
3008
3050
|
const sanitizedFields = sanitizeRequestedFields(table, requestedFields);
|
|
3009
3051
|
let query = db3.from(tableNamePlural).select(sanitizedFields);
|
|
3010
3052
|
query = applyFilters(query, filters, table);
|
|
3011
|
-
query = applyAccessControl(table, context.user
|
|
3053
|
+
query = applyAccessControl(table, query, context.user);
|
|
3012
3054
|
query = applySorting(query, sort);
|
|
3013
3055
|
let result = await query.first();
|
|
3014
|
-
return finalizeRequestedFields({ table, requestedFields, agents, contexts, tools, result, user: context.user });
|
|
3056
|
+
return finalizeRequestedFields({ args, table, requestedFields, agents, contexts, tools, result, user: context.user });
|
|
3015
3057
|
},
|
|
3016
3058
|
[`${tableNamePlural}Pagination`]: async (_, args, context, info) => {
|
|
3017
3059
|
const { limit = 10, page = 0, filters = [], sort } = args;
|
|
@@ -3021,7 +3063,7 @@ function createQueries(table, agents, tools, contexts) {
|
|
|
3021
3063
|
}
|
|
3022
3064
|
let countQuery = db3(tableNamePlural);
|
|
3023
3065
|
countQuery = applyFilters(countQuery, filters, table);
|
|
3024
|
-
countQuery = applyAccessControl(table, context.user
|
|
3066
|
+
countQuery = applyAccessControl(table, countQuery, context.user);
|
|
3025
3067
|
const countResult = await countQuery.count("* as count");
|
|
3026
3068
|
const itemCount = Number(countResult[0]?.count || 0);
|
|
3027
3069
|
const pageCount = Math.ceil(itemCount / limit);
|
|
@@ -3030,7 +3072,7 @@ function createQueries(table, agents, tools, contexts) {
|
|
|
3030
3072
|
const hasNextPage = currentPage < pageCount - 1;
|
|
3031
3073
|
let dataQuery = db3(tableNamePlural);
|
|
3032
3074
|
dataQuery = applyFilters(dataQuery, filters, table);
|
|
3033
|
-
dataQuery = applyAccessControl(table, context.user
|
|
3075
|
+
dataQuery = applyAccessControl(table, dataQuery, context.user);
|
|
3034
3076
|
const requestedFields = getRequestedFields(info);
|
|
3035
3077
|
dataQuery = applySorting(dataQuery, sort);
|
|
3036
3078
|
if (page > 1) {
|
|
@@ -3046,7 +3088,7 @@ function createQueries(table, agents, tools, contexts) {
|
|
|
3046
3088
|
hasPreviousPage,
|
|
3047
3089
|
hasNextPage
|
|
3048
3090
|
},
|
|
3049
|
-
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 })
|
|
3050
3092
|
};
|
|
3051
3093
|
},
|
|
3052
3094
|
// Add generic statistics query for all tables
|
|
@@ -3055,7 +3097,7 @@ function createQueries(table, agents, tools, contexts) {
|
|
|
3055
3097
|
const { db: db3 } = context;
|
|
3056
3098
|
let query = db3(tableNamePlural);
|
|
3057
3099
|
query = applyFilters(query, filters, table);
|
|
3058
|
-
query = applyAccessControl(table, context.user
|
|
3100
|
+
query = applyAccessControl(table, query, context.user);
|
|
3059
3101
|
if (groupBy) {
|
|
3060
3102
|
query = query.select(groupBy).groupBy(groupBy);
|
|
3061
3103
|
if (tableNamePlural === "tracking") {
|
|
@@ -3155,11 +3197,11 @@ var vectorSearch = async ({
|
|
|
3155
3197
|
const chunksTable = getChunksTableName(id);
|
|
3156
3198
|
let countQuery = db3(mainTable);
|
|
3157
3199
|
countQuery = applyFilters(countQuery, filters, table);
|
|
3158
|
-
countQuery = applyAccessControl(table,
|
|
3200
|
+
countQuery = applyAccessControl(table, countQuery, user);
|
|
3159
3201
|
const columns = await db3(mainTable).columnInfo();
|
|
3160
3202
|
let itemsQuery = db3(mainTable).select(Object.keys(columns).map((column) => mainTable + "." + column));
|
|
3161
3203
|
itemsQuery = applyFilters(itemsQuery, filters, table);
|
|
3162
|
-
itemsQuery = applyAccessControl(table,
|
|
3204
|
+
itemsQuery = applyAccessControl(table, itemsQuery, user);
|
|
3163
3205
|
itemsQuery = applySorting(itemsQuery, sort);
|
|
3164
3206
|
if (queryRewriter) {
|
|
3165
3207
|
query = await queryRewriter(query);
|
|
@@ -3178,7 +3220,7 @@ var vectorSearch = async ({
|
|
|
3178
3220
|
const { chunks } = await embedder.generateFromQuery(context.id, query, {
|
|
3179
3221
|
label: table.name.singular,
|
|
3180
3222
|
trigger
|
|
3181
|
-
}, user
|
|
3223
|
+
}, user?.id, role);
|
|
3182
3224
|
if (!chunks?.[0]?.vector) {
|
|
3183
3225
|
throw new Error("No vector generated for query.");
|
|
3184
3226
|
}
|
|
@@ -3568,7 +3610,8 @@ function createSDL(tables, contexts, agents, tools, config, evals, queues2) {
|
|
|
3568
3610
|
const tableNameSingularUpperCaseFirst = table.name.singular.charAt(0).toUpperCase() + table.name.singular.slice(1);
|
|
3569
3611
|
const processorFields = table.fields.filter((field) => field.processor?.execute);
|
|
3570
3612
|
typeDefs += `
|
|
3571
|
-
|
|
3613
|
+
${tableNameSingular === "agent" ? `${tableNameSingular}ById(id: ID!, project: ID): ${tableNameSingular}` : `${tableNameSingular}ById(id: ID!): ${tableNameSingular}`}
|
|
3614
|
+
|
|
3572
3615
|
${tableNameSingular}ByIds(ids: [ID!]!): [${tableNameSingular}]!
|
|
3573
3616
|
${tableNamePlural}Pagination(limit: Int, page: Int, filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingularUpperCaseFirst}PaginationResult
|
|
3574
3617
|
${tableNameSingular}One(filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingular}
|
|
@@ -4481,16 +4524,27 @@ var addPrefixToKey = (keyPath, config) => {
|
|
|
4481
4524
|
}
|
|
4482
4525
|
return `${prefix}/${keyPath}`;
|
|
4483
4526
|
};
|
|
4484
|
-
var uploadFile = async (
|
|
4485
|
-
console.log("[EXULU] Uploading file to S3", key);
|
|
4527
|
+
var uploadFile = async (file, key, config, options = {}, user) => {
|
|
4486
4528
|
if (!config.fileUploads) {
|
|
4487
|
-
throw new Error("File uploads are not configured");
|
|
4529
|
+
throw new Error("File uploads are not configured (in the exported uploadFile function)");
|
|
4488
4530
|
}
|
|
4489
4531
|
const client2 = getS3Client(config);
|
|
4490
|
-
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}/` : "";
|
|
4491
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);
|
|
4492
4546
|
const command = new import_client_s3.PutObjectCommand({
|
|
4493
|
-
Bucket:
|
|
4547
|
+
Bucket: customBucket || defaultBucket,
|
|
4494
4548
|
Key: fullKey,
|
|
4495
4549
|
Body: file,
|
|
4496
4550
|
ContentType: options.contentType,
|
|
@@ -4498,6 +4552,10 @@ var uploadFile = async (user, file, key, config, options = {}) => {
|
|
|
4498
4552
|
ContentLength: file.byteLength
|
|
4499
4553
|
});
|
|
4500
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
|
+
}
|
|
4501
4559
|
return fullKey;
|
|
4502
4560
|
};
|
|
4503
4561
|
var createUppyRoutes = async (app, config) => {
|
|
@@ -4656,16 +4714,12 @@ var createUppyRoutes = async (app, config) => {
|
|
|
4656
4714
|
key = key.split("]")[1] || "";
|
|
4657
4715
|
console.log("[EXULU] key", key);
|
|
4658
4716
|
}
|
|
4659
|
-
console.log("[EXULU] Getting object metadata from s3", key);
|
|
4660
|
-
console.log("[EXULU] bucket", bucket);
|
|
4661
|
-
console.log("[EXULU] key", key);
|
|
4662
4717
|
const client2 = getS3Client(config);
|
|
4663
4718
|
const command = new import_client_s3.HeadObjectCommand({
|
|
4664
4719
|
Bucket: bucket,
|
|
4665
4720
|
Key: key
|
|
4666
4721
|
});
|
|
4667
4722
|
const response = await client2.send(command);
|
|
4668
|
-
console.log("[EXULU] Object metadata from s3", response);
|
|
4669
4723
|
res.json(response);
|
|
4670
4724
|
res.end();
|
|
4671
4725
|
});
|
|
@@ -4969,6 +5023,7 @@ var createUppyRoutes = async (app, config) => {
|
|
|
4969
5023
|
};
|
|
4970
5024
|
|
|
4971
5025
|
// src/registry/classes.ts
|
|
5026
|
+
var import_officeparser = require("officeparser");
|
|
4972
5027
|
var s3Client2;
|
|
4973
5028
|
function sanitizeToolName(name) {
|
|
4974
5029
|
if (typeof name !== "string") return "";
|
|
@@ -4979,12 +5034,117 @@ function sanitizeToolName(name) {
|
|
|
4979
5034
|
}
|
|
4980
5035
|
return sanitized;
|
|
4981
5036
|
}
|
|
4982
|
-
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) => {
|
|
4983
5132
|
if (!currentTools) return {};
|
|
4984
5133
|
if (!allExuluTools) return {};
|
|
4985
5134
|
if (!contexts) {
|
|
4986
5135
|
contexts = [];
|
|
4987
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
|
+
}
|
|
4988
5148
|
const sanitizedTools = currentTools ? currentTools.map((tool2) => ({
|
|
4989
5149
|
...tool2,
|
|
4990
5150
|
name: sanitizeToolName(tool2.name)
|
|
@@ -4993,9 +5153,9 @@ var convertToolsArrayToObject = (currentTools, allExuluTools, configs, providera
|
|
|
4993
5153
|
return {
|
|
4994
5154
|
...sanitizedTools?.reduce(
|
|
4995
5155
|
(prev, cur) => {
|
|
4996
|
-
let
|
|
4997
|
-
const userDefinedConfigDescription =
|
|
4998
|
-
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;
|
|
4999
5159
|
const toolDescription = cur.description;
|
|
5000
5160
|
const description = userDefinedConfigDescription || defaultConfigDescription || toolDescription;
|
|
5001
5161
|
return {
|
|
@@ -5008,8 +5168,8 @@ var convertToolsArrayToObject = (currentTools, allExuluTools, configs, providera
|
|
|
5008
5168
|
console.error("[EXULU] Tool execute function is undefined.", cur.tool);
|
|
5009
5169
|
throw new Error("Tool execute function is undefined.");
|
|
5010
5170
|
}
|
|
5011
|
-
if (
|
|
5012
|
-
|
|
5171
|
+
if (toolVariableConfig) {
|
|
5172
|
+
toolVariableConfig = await hydrateVariables(toolVariableConfig || []);
|
|
5013
5173
|
}
|
|
5014
5174
|
let upload = void 0;
|
|
5015
5175
|
if (exuluConfig?.fileUploads?.s3endpoint && exuluConfig?.fileUploads?.s3key && exuluConfig?.fileUploads?.s3secret && exuluConfig?.fileUploads?.s3Bucket) {
|
|
@@ -5062,7 +5222,6 @@ var convertToolsArrayToObject = (currentTools, allExuluTools, configs, providera
|
|
|
5062
5222
|
acc[curr.id] = curr;
|
|
5063
5223
|
return acc;
|
|
5064
5224
|
}, {});
|
|
5065
|
-
console.log("[EXULU] Config", config);
|
|
5066
5225
|
const response = await cur.tool.execute({
|
|
5067
5226
|
...inputs,
|
|
5068
5227
|
sessionID,
|
|
@@ -5077,7 +5236,7 @@ var convertToolsArrayToObject = (currentTools, allExuluTools, configs, providera
|
|
|
5077
5236
|
contexts: contextsMap,
|
|
5078
5237
|
upload,
|
|
5079
5238
|
exuluConfig,
|
|
5080
|
-
|
|
5239
|
+
toolVariablesConfig: toolVariableConfig ? toolVariableConfig.config.reduce((acc, curr) => {
|
|
5081
5240
|
acc[curr.name] = curr.value;
|
|
5082
5241
|
return acc;
|
|
5083
5242
|
}, {}) : {}
|
|
@@ -5111,7 +5270,6 @@ var hydrateVariables = async (tool2) => {
|
|
|
5111
5270
|
const variableName = toolConfig.variable;
|
|
5112
5271
|
const variable = await db3.from("variables").where({ name: variableName }).first();
|
|
5113
5272
|
if (!variable) {
|
|
5114
|
-
console.error("[EXULU] Variable " + variableName + " not found.");
|
|
5115
5273
|
throw new Error("Variable " + variableName + " not found.");
|
|
5116
5274
|
}
|
|
5117
5275
|
let value = variable.value;
|
|
@@ -5123,7 +5281,6 @@ var hydrateVariables = async (tool2) => {
|
|
|
5123
5281
|
return toolConfig;
|
|
5124
5282
|
});
|
|
5125
5283
|
await Promise.all(promises);
|
|
5126
|
-
console.log("[EXULU] Variable values retrieved and added to tool config.");
|
|
5127
5284
|
return tool2;
|
|
5128
5285
|
};
|
|
5129
5286
|
function generateSlug(name) {
|
|
@@ -5347,7 +5504,21 @@ var ExuluAgent2 = class {
|
|
|
5347
5504
|
});
|
|
5348
5505
|
}
|
|
5349
5506
|
console.log("[EXULU] Message count for agent: " + this.name, "loaded for generating sync.", messages.length);
|
|
5350
|
-
|
|
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.`;
|
|
5351
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.";
|
|
5352
5523
|
system += "\n\n" + genericContext;
|
|
5353
5524
|
if (prompt) {
|
|
@@ -5372,7 +5543,7 @@ var ExuluAgent2 = class {
|
|
|
5372
5543
|
system,
|
|
5373
5544
|
prompt,
|
|
5374
5545
|
maxRetries: 2,
|
|
5375
|
-
tools: convertToolsArrayToObject(
|
|
5546
|
+
tools: await convertToolsArrayToObject(
|
|
5376
5547
|
currentTools,
|
|
5377
5548
|
allExuluTools,
|
|
5378
5549
|
toolConfigs,
|
|
@@ -5381,7 +5552,8 @@ var ExuluAgent2 = class {
|
|
|
5381
5552
|
user,
|
|
5382
5553
|
exuluConfig,
|
|
5383
5554
|
session,
|
|
5384
|
-
req
|
|
5555
|
+
req,
|
|
5556
|
+
project
|
|
5385
5557
|
),
|
|
5386
5558
|
stopWhen: [(0, import_ai.stepCountIs)(2)]
|
|
5387
5559
|
});
|
|
@@ -5432,7 +5604,7 @@ var ExuluAgent2 = class {
|
|
|
5432
5604
|
ignoreIncompleteToolCalls: true
|
|
5433
5605
|
}),
|
|
5434
5606
|
maxRetries: 2,
|
|
5435
|
-
tools: convertToolsArrayToObject(
|
|
5607
|
+
tools: await convertToolsArrayToObject(
|
|
5436
5608
|
currentTools,
|
|
5437
5609
|
allExuluTools,
|
|
5438
5610
|
toolConfigs,
|
|
@@ -5441,7 +5613,8 @@ var ExuluAgent2 = class {
|
|
|
5441
5613
|
user,
|
|
5442
5614
|
exuluConfig,
|
|
5443
5615
|
session,
|
|
5444
|
-
req
|
|
5616
|
+
req,
|
|
5617
|
+
project
|
|
5445
5618
|
),
|
|
5446
5619
|
stopWhen: [(0, import_ai.stepCountIs)(2)]
|
|
5447
5620
|
});
|
|
@@ -5482,6 +5655,70 @@ var ExuluAgent2 = class {
|
|
|
5482
5655
|
}
|
|
5483
5656
|
return "";
|
|
5484
5657
|
};
|
|
5658
|
+
/**
|
|
5659
|
+
* Convert file parts in messages to OpenAI Responses API compatible format.
|
|
5660
|
+
* The OpenAI Responses API doesn't support inline file parts with type 'file'.
|
|
5661
|
+
* This function converts:
|
|
5662
|
+
* - Document files (PDF, DOCX, etc.) -> text parts with extracted content using officeparser
|
|
5663
|
+
* - Image files -> image parts (which ARE supported by Responses API)
|
|
5664
|
+
*/
|
|
5665
|
+
async processFilePartsInMessages(messages) {
|
|
5666
|
+
const processedMessages = await Promise.all(messages.map(async (message) => {
|
|
5667
|
+
if (message.role !== "user" || !Array.isArray(message.parts)) {
|
|
5668
|
+
return message;
|
|
5669
|
+
}
|
|
5670
|
+
const processedParts = await Promise.all(message.parts.map(async (part) => {
|
|
5671
|
+
if (part.type !== "file") {
|
|
5672
|
+
return part;
|
|
5673
|
+
}
|
|
5674
|
+
const { mediaType, url, filename } = part;
|
|
5675
|
+
const imageTypes = ["image/png", "image/jpeg", "image/jpg", "image/gif", "image/webp"];
|
|
5676
|
+
if (imageTypes.includes(mediaType)) {
|
|
5677
|
+
console.log(`[EXULU] Converting file part to image part: ${filename} `);
|
|
5678
|
+
return {
|
|
5679
|
+
type: "image",
|
|
5680
|
+
image: url,
|
|
5681
|
+
mimeType: mediaType
|
|
5682
|
+
};
|
|
5683
|
+
}
|
|
5684
|
+
console.log(`[EXULU] Converting file part to text using officeparser: ${filename}`);
|
|
5685
|
+
try {
|
|
5686
|
+
const response = await fetch(url);
|
|
5687
|
+
if (!response.ok) {
|
|
5688
|
+
console.error(`[EXULU] Failed to fetch file: ${filename}, status: ${response.status} `);
|
|
5689
|
+
return {
|
|
5690
|
+
type: "text",
|
|
5691
|
+
text: `[Error: Could not load file ${filename}]`
|
|
5692
|
+
};
|
|
5693
|
+
}
|
|
5694
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
5695
|
+
const extractedText = await (0, import_officeparser.parseOfficeAsync)(arrayBuffer, {
|
|
5696
|
+
outputErrorToConsole: false,
|
|
5697
|
+
newlineDelimiter: "\n"
|
|
5698
|
+
});
|
|
5699
|
+
return {
|
|
5700
|
+
type: "text",
|
|
5701
|
+
text: `<file file name = "${filename}" >
|
|
5702
|
+
${extractedText}
|
|
5703
|
+
</file>`
|
|
5704
|
+
};
|
|
5705
|
+
} catch (error) {
|
|
5706
|
+
console.error(`[EXULU] Error processing file ${filename}:`, error);
|
|
5707
|
+
return {
|
|
5708
|
+
type: "text",
|
|
5709
|
+
text: `[Error extracting text from file ${filename}: ${error instanceof Error ? error.message : "Unknown error"}]`
|
|
5710
|
+
};
|
|
5711
|
+
}
|
|
5712
|
+
}));
|
|
5713
|
+
const result = {
|
|
5714
|
+
...message,
|
|
5715
|
+
parts: processedParts
|
|
5716
|
+
};
|
|
5717
|
+
console.log("[EXULU] Result: " + JSON.stringify(result, null, 2));
|
|
5718
|
+
return result;
|
|
5719
|
+
}));
|
|
5720
|
+
return processedMessages;
|
|
5721
|
+
}
|
|
5485
5722
|
generateStream = async ({
|
|
5486
5723
|
user,
|
|
5487
5724
|
session,
|
|
@@ -5513,7 +5750,10 @@ var ExuluAgent2 = class {
|
|
|
5513
5750
|
});
|
|
5514
5751
|
let messages = [];
|
|
5515
5752
|
let previousMessagesContent = previousMessages || [];
|
|
5753
|
+
let project;
|
|
5516
5754
|
if (session) {
|
|
5755
|
+
const sessionData = await getSession({ sessionID: session });
|
|
5756
|
+
project = sessionData.project;
|
|
5517
5757
|
console.log("[EXULU] loading previous messages from session: " + session);
|
|
5518
5758
|
const previousMessages2 = await getAgentMessages({
|
|
5519
5759
|
session,
|
|
@@ -5529,18 +5769,21 @@ var ExuluAgent2 = class {
|
|
|
5529
5769
|
// append the new message to the previous messages:
|
|
5530
5770
|
messages: [...previousMessagesContent, message]
|
|
5531
5771
|
});
|
|
5772
|
+
messages = messages.filter(
|
|
5773
|
+
(message2, index, self) => index === self.findIndex((t) => t.id === message2.id)
|
|
5774
|
+
);
|
|
5775
|
+
messages = await this.processFilePartsInMessages(messages);
|
|
5532
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.";
|
|
5533
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.";
|
|
5534
5778
|
system += "\n\n" + genericContext;
|
|
5535
|
-
console.log("[EXULU] tools for agent: " + this.name, currentTools?.map((x) => x.name + " (" + x.id + ")"));
|
|
5536
|
-
console.log("[EXULU] system", system.slice(0, 100) + "...");
|
|
5537
5779
|
const result = (0, import_ai.streamText)({
|
|
5538
5780
|
model,
|
|
5539
5781
|
// Should be a LanguageModelV1
|
|
5540
5782
|
messages: (0, import_ai.convertToModelMessages)(messages, {
|
|
5541
5783
|
ignoreIncompleteToolCalls: true
|
|
5542
5784
|
}),
|
|
5543
|
-
//
|
|
5785
|
+
// PrepareStep could be used here to set the model
|
|
5786
|
+
// for the first step or change other parameters.
|
|
5544
5787
|
system,
|
|
5545
5788
|
maxRetries: 2,
|
|
5546
5789
|
providerOptions: {
|
|
@@ -5548,7 +5791,7 @@ var ExuluAgent2 = class {
|
|
|
5548
5791
|
reasoningSummary: "auto"
|
|
5549
5792
|
}
|
|
5550
5793
|
},
|
|
5551
|
-
tools: convertToolsArrayToObject(
|
|
5794
|
+
tools: await convertToolsArrayToObject(
|
|
5552
5795
|
currentTools,
|
|
5553
5796
|
allExuluTools,
|
|
5554
5797
|
toolConfigs,
|
|
@@ -5557,7 +5800,8 @@ var ExuluAgent2 = class {
|
|
|
5557
5800
|
user,
|
|
5558
5801
|
exuluConfig,
|
|
5559
5802
|
session,
|
|
5560
|
-
req
|
|
5803
|
+
req,
|
|
5804
|
+
project
|
|
5561
5805
|
),
|
|
5562
5806
|
onError: (error) => {
|
|
5563
5807
|
console.error("[EXULU] chat stream error.", error);
|
|
@@ -5593,7 +5837,7 @@ var getSession = async ({ sessionID }) => {
|
|
|
5593
5837
|
};
|
|
5594
5838
|
var saveChat = async ({ session, user, messages }) => {
|
|
5595
5839
|
const { db: db3 } = await postgresClient();
|
|
5596
|
-
const
|
|
5840
|
+
for (const message of messages) {
|
|
5597
5841
|
const mutation = db3.from("agent_messages").insert({
|
|
5598
5842
|
session,
|
|
5599
5843
|
user,
|
|
@@ -5602,9 +5846,8 @@ var saveChat = async ({ session, user, messages }) => {
|
|
|
5602
5846
|
title: message.role === "user" ? "User" : "Assistant"
|
|
5603
5847
|
}).returning("id");
|
|
5604
5848
|
mutation.onConflict("message_id").merge();
|
|
5605
|
-
|
|
5606
|
-
}
|
|
5607
|
-
await Promise.all(promises);
|
|
5849
|
+
await mutation;
|
|
5850
|
+
}
|
|
5608
5851
|
};
|
|
5609
5852
|
var ExuluEmbedder = class {
|
|
5610
5853
|
id;
|
|
@@ -5654,12 +5897,10 @@ var ExuluEmbedder = class {
|
|
|
5654
5897
|
id
|
|
5655
5898
|
} = setting;
|
|
5656
5899
|
let value = "";
|
|
5657
|
-
console.log("[EXULU] variable name", variableName);
|
|
5658
5900
|
const variable = await db3.from("variables").where({ name: variableName }).first();
|
|
5659
5901
|
if (!variable) {
|
|
5660
5902
|
throw new Error("Variable not found for embedder setting: " + name + " in context: " + context + " and embedder: " + this.id);
|
|
5661
5903
|
}
|
|
5662
|
-
console.log("[EXULU] variable", variable);
|
|
5663
5904
|
if (variable.encrypted) {
|
|
5664
5905
|
if (!process.env.NEXTAUTH_SECRET) {
|
|
5665
5906
|
throw new Error("NEXTAUTH_SECRET environment variable is not set, cannot decrypt variable: " + name);
|
|
@@ -5671,14 +5912,12 @@ var ExuluEmbedder = class {
|
|
|
5671
5912
|
throw new Error("Decryption returned empty string - invalid key or corrupted data");
|
|
5672
5913
|
}
|
|
5673
5914
|
value = decrypted;
|
|
5674
|
-
console.log("[EXULU] successfully decrypted value for", name);
|
|
5675
5915
|
} catch (error) {
|
|
5676
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.`);
|
|
5677
5917
|
}
|
|
5678
5918
|
} else {
|
|
5679
5919
|
value = variable.value;
|
|
5680
5920
|
}
|
|
5681
|
-
console.log("[EXULU] variable value", value);
|
|
5682
5921
|
hydrated.push({
|
|
5683
5922
|
id: id || "",
|
|
5684
5923
|
name,
|
|
@@ -5786,14 +6025,20 @@ var ExuluStorage = class {
|
|
|
5786
6025
|
getPresignedUrl = async (key) => {
|
|
5787
6026
|
return await getPresignedUrl(key, this.config);
|
|
5788
6027
|
};
|
|
5789
|
-
uploadFile = async (
|
|
5790
|
-
return await uploadFile(
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
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
|
+
);
|
|
5797
6042
|
};
|
|
5798
6043
|
// todo add upload and delete methods
|
|
5799
6044
|
};
|
|
@@ -5813,7 +6058,19 @@ var ExuluContext = class {
|
|
|
5813
6058
|
// todo typings
|
|
5814
6059
|
configuration;
|
|
5815
6060
|
sources = [];
|
|
5816
|
-
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
|
+
}) {
|
|
5817
6074
|
this.id = id;
|
|
5818
6075
|
this.name = name;
|
|
5819
6076
|
this.fields = fields || [];
|
|
@@ -5830,7 +6087,7 @@ var ExuluContext = class {
|
|
|
5830
6087
|
this.queryRewriter = queryRewriter;
|
|
5831
6088
|
this.resultReranker = resultReranker;
|
|
5832
6089
|
}
|
|
5833
|
-
processField = async (trigger,
|
|
6090
|
+
processField = async (trigger, item, exuluConfig, user, role) => {
|
|
5834
6091
|
console.log("[EXULU] processing field", item.field, " in context", this.id);
|
|
5835
6092
|
console.log("[EXULU] fields", this.fields.map((field2) => field2.name));
|
|
5836
6093
|
const field = this.fields.find((field2) => {
|
|
@@ -5840,7 +6097,7 @@ var ExuluContext = class {
|
|
|
5840
6097
|
console.error("[EXULU] field not found or processor not set for field", item.field, " in context", this.id);
|
|
5841
6098
|
throw new Error("Field not found or processor not set for field " + item.field + " in context " + this.id);
|
|
5842
6099
|
}
|
|
5843
|
-
const exuluStorage = new ExuluStorage({ config });
|
|
6100
|
+
const exuluStorage = new ExuluStorage({ config: exuluConfig });
|
|
5844
6101
|
const queue = await field.processor.config?.queue;
|
|
5845
6102
|
if (queue?.queue.name) {
|
|
5846
6103
|
console.log("[EXULU] processor is in queue mode, scheduling job.");
|
|
@@ -5866,6 +6123,7 @@ var ExuluContext = class {
|
|
|
5866
6123
|
job: job.id
|
|
5867
6124
|
};
|
|
5868
6125
|
}
|
|
6126
|
+
console.log("[EXULU] POS 1 -- EXULU CONTEXT PROCESS FIELD");
|
|
5869
6127
|
const result = await field.processor.execute({
|
|
5870
6128
|
item,
|
|
5871
6129
|
user,
|
|
@@ -5878,13 +6136,24 @@ var ExuluContext = class {
|
|
|
5878
6136
|
delete: this.deleteItem
|
|
5879
6137
|
}
|
|
5880
6138
|
},
|
|
5881
|
-
|
|
6139
|
+
exuluConfig
|
|
5882
6140
|
});
|
|
5883
6141
|
return {
|
|
5884
6142
|
result,
|
|
5885
6143
|
job: void 0
|
|
5886
6144
|
};
|
|
5887
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
|
+
};
|
|
5888
6157
|
deleteAll = async () => {
|
|
5889
6158
|
const { db: db3 } = await postgresClient();
|
|
5890
6159
|
await db3.from(getTableName(this.id)).delete();
|
|
@@ -5894,8 +6163,11 @@ var ExuluContext = class {
|
|
|
5894
6163
|
results: []
|
|
5895
6164
|
};
|
|
5896
6165
|
};
|
|
5897
|
-
executeSource = async (source, inputs) => {
|
|
5898
|
-
return await source.execute(
|
|
6166
|
+
executeSource = async (source, inputs, exuluConfig) => {
|
|
6167
|
+
return await source.execute({
|
|
6168
|
+
...inputs,
|
|
6169
|
+
exuluConfig
|
|
6170
|
+
});
|
|
5899
6171
|
};
|
|
5900
6172
|
tableExists = async () => {
|
|
5901
6173
|
const { db: db3 } = await postgresClient();
|
|
@@ -5951,7 +6223,7 @@ var ExuluContext = class {
|
|
|
5951
6223
|
job
|
|
5952
6224
|
};
|
|
5953
6225
|
};
|
|
5954
|
-
createItem = async (item, config, user, role, upsert) => {
|
|
6226
|
+
createItem = async (item, config, user, role, upsert, generateEmbeddingsOverwrite) => {
|
|
5955
6227
|
if (upsert && (!item.id && !item.external_id)) {
|
|
5956
6228
|
throw new Error("Item id or external id is required for upsert.");
|
|
5957
6229
|
}
|
|
@@ -5978,12 +6250,40 @@ var ExuluContext = class {
|
|
|
5978
6250
|
throw new Error("Failed to create item.");
|
|
5979
6251
|
}
|
|
5980
6252
|
console.log("[EXULU] context configuration", this.configuration);
|
|
5981
|
-
|
|
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) {
|
|
5982
6283
|
console.log("[EXULU] generating embeddings for item", results[0].id);
|
|
5983
|
-
const { job } = await this.embeddings.generate.one({
|
|
6284
|
+
const { job: embeddingsJob } = await this.embeddings.generate.one({
|
|
5984
6285
|
item: {
|
|
5985
6286
|
...item,
|
|
5986
|
-
// important we need to full record here with all fields for the embedder
|
|
5987
6287
|
id: results[0].id
|
|
5988
6288
|
},
|
|
5989
6289
|
user,
|
|
@@ -5991,17 +6291,16 @@ var ExuluContext = class {
|
|
|
5991
6291
|
trigger: "api",
|
|
5992
6292
|
config
|
|
5993
6293
|
});
|
|
5994
|
-
|
|
5995
|
-
|
|
5996
|
-
|
|
5997
|
-
};
|
|
6294
|
+
if (embeddingsJob) {
|
|
6295
|
+
jobs.push(embeddingsJob);
|
|
6296
|
+
}
|
|
5998
6297
|
}
|
|
5999
6298
|
return {
|
|
6000
6299
|
item: results[0],
|
|
6001
|
-
job: void 0
|
|
6300
|
+
job: jobs.length > 0 ? jobs.join(",") : void 0
|
|
6002
6301
|
};
|
|
6003
6302
|
};
|
|
6004
|
-
updateItem = async (item, config, user, role) => {
|
|
6303
|
+
updateItem = async (item, config, user, role, generateEmbeddingsOverwrite) => {
|
|
6005
6304
|
const { db: db3 } = await postgresClient();
|
|
6006
6305
|
if (item.field) {
|
|
6007
6306
|
delete item.field;
|
|
@@ -6025,8 +6324,35 @@ var ExuluContext = class {
|
|
|
6025
6324
|
}
|
|
6026
6325
|
).returning("id");
|
|
6027
6326
|
await mutation;
|
|
6028
|
-
|
|
6029
|
-
|
|
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({
|
|
6030
6356
|
item: record,
|
|
6031
6357
|
// important we need to full record here with all fields for the embedder
|
|
6032
6358
|
user,
|
|
@@ -6034,14 +6360,13 @@ var ExuluContext = class {
|
|
|
6034
6360
|
trigger: "api",
|
|
6035
6361
|
config
|
|
6036
6362
|
});
|
|
6037
|
-
|
|
6038
|
-
|
|
6039
|
-
|
|
6040
|
-
};
|
|
6363
|
+
if (embeddingsJob) {
|
|
6364
|
+
jobs.push(embeddingsJob);
|
|
6365
|
+
}
|
|
6041
6366
|
}
|
|
6042
6367
|
return {
|
|
6043
6368
|
item: record,
|
|
6044
|
-
job: void 0
|
|
6369
|
+
job: jobs.length > 0 ? jobs.join(",") : void 0
|
|
6045
6370
|
};
|
|
6046
6371
|
};
|
|
6047
6372
|
deleteItem = async (item, user, role) => {
|
|
@@ -6068,6 +6393,34 @@ var ExuluContext = class {
|
|
|
6068
6393
|
job: void 0
|
|
6069
6394
|
};
|
|
6070
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
|
+
};
|
|
6071
6424
|
embeddings = {
|
|
6072
6425
|
generate: {
|
|
6073
6426
|
one: async ({
|
|
@@ -6084,6 +6437,12 @@ var ExuluContext = class {
|
|
|
6084
6437
|
if (!item.id) {
|
|
6085
6438
|
throw new Error("Item id is required for generating embeddings.");
|
|
6086
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;
|
|
6087
6446
|
const queue = await this.embedder.queue;
|
|
6088
6447
|
if (queue?.queue.name) {
|
|
6089
6448
|
console.log("[EXULU] embedder is in queue mode, scheduling job.");
|
|
@@ -6849,7 +7208,7 @@ Mood: friendly and intelligent.
|
|
|
6849
7208
|
const user = authenticationResult.user;
|
|
6850
7209
|
let agentQuery = db3("agents");
|
|
6851
7210
|
agentQuery.select("*");
|
|
6852
|
-
agentQuery = applyAccessControl(agentsSchema2(), authenticationResult.user
|
|
7211
|
+
agentQuery = applyAccessControl(agentsSchema2(), agentQuery, authenticationResult.user);
|
|
6853
7212
|
agentQuery.where({ id: req.params.agent });
|
|
6854
7213
|
const agent = await agentQuery.first();
|
|
6855
7214
|
if (!agent) {
|
|
@@ -6866,7 +7225,7 @@ Mood: friendly and intelligent.
|
|
|
6866
7225
|
} else {
|
|
6867
7226
|
let projectQuery = db3("projects");
|
|
6868
7227
|
projectQuery.select("*");
|
|
6869
|
-
projectQuery = applyAccessControl(projectsSchema2(), authenticationResult.user
|
|
7228
|
+
projectQuery = applyAccessControl(projectsSchema2(), projectQuery, authenticationResult.user);
|
|
6870
7229
|
projectQuery.where({ id: req.params.project });
|
|
6871
7230
|
project = await projectQuery.first();
|
|
6872
7231
|
if (!project) {
|
|
@@ -7208,6 +7567,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7208
7567
|
if (!data.role) {
|
|
7209
7568
|
throw new Error(`Role not set for processor job.`);
|
|
7210
7569
|
}
|
|
7570
|
+
console.log("[EXULU] POS 2 -- EXULU CONTEXT PROCESS FIELD");
|
|
7211
7571
|
const result = await field.processor.execute({
|
|
7212
7572
|
item: data.inputs,
|
|
7213
7573
|
user: data.user,
|
|
@@ -7222,9 +7582,24 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7222
7582
|
},
|
|
7223
7583
|
config
|
|
7224
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
|
+
}
|
|
7225
7598
|
return {
|
|
7226
7599
|
result,
|
|
7227
|
-
metadata: {
|
|
7600
|
+
metadata: {
|
|
7601
|
+
jobs: jobs.length > 0 ? jobs.join(",") : void 0
|
|
7602
|
+
}
|
|
7228
7603
|
};
|
|
7229
7604
|
}
|
|
7230
7605
|
if (data.type === "eval_run") {
|
|
@@ -7910,7 +8285,7 @@ var ExuluMCP = class {
|
|
|
7910
8285
|
console.log("[EXULU] MCP tool inputs", inputs);
|
|
7911
8286
|
console.log("[EXULU] MCP tool args", args);
|
|
7912
8287
|
const configValues = agentInstance.tools;
|
|
7913
|
-
const tools = convertToolsArrayToObject(
|
|
8288
|
+
const tools = await convertToolsArrayToObject(
|
|
7914
8289
|
[tool2],
|
|
7915
8290
|
allTools,
|
|
7916
8291
|
configValues,
|