@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.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 {
|
|
@@ -411,7 +417,9 @@ var getToken = async (authHeader) => {
|
|
|
411
417
|
}
|
|
412
418
|
try {
|
|
413
419
|
const secret = process.env.NEXTAUTH_SECRET;
|
|
414
|
-
const
|
|
420
|
+
const secretBuffer = Buffer.from(secret, "utf-8");
|
|
421
|
+
const base64Secret = secretBuffer.toString("base64url");
|
|
422
|
+
const jwk = await importJWK({ k: base64Secret, alg: "HS256", kty: "oct" });
|
|
415
423
|
const { payload } = await jwtVerify(token, jwk);
|
|
416
424
|
return payload;
|
|
417
425
|
} catch (error) {
|
|
@@ -450,6 +458,8 @@ var authentication = async ({
|
|
|
450
458
|
type: "api",
|
|
451
459
|
id: 192837465,
|
|
452
460
|
email: "internal@exulu.com",
|
|
461
|
+
firstname: "API",
|
|
462
|
+
lastname: "User",
|
|
453
463
|
role: {
|
|
454
464
|
id: "internal",
|
|
455
465
|
name: "Internal",
|
|
@@ -1283,10 +1293,10 @@ var rbacSchema = {
|
|
|
1283
1293
|
name: "user_id",
|
|
1284
1294
|
type: "number"
|
|
1285
1295
|
},
|
|
1286
|
-
{
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
},
|
|
1296
|
+
/* {
|
|
1297
|
+
name: "project_id",
|
|
1298
|
+
type: "uuid"
|
|
1299
|
+
}, */
|
|
1290
1300
|
{
|
|
1291
1301
|
name: "rights",
|
|
1292
1302
|
type: "text",
|
|
@@ -2196,7 +2206,7 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2196
2206
|
});
|
|
2197
2207
|
return {
|
|
2198
2208
|
// Filter result to only include requested fields
|
|
2199
|
-
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 }),
|
|
2200
2210
|
job
|
|
2201
2211
|
};
|
|
2202
2212
|
},
|
|
@@ -2244,7 +2254,7 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2244
2254
|
}
|
|
2245
2255
|
const { job } = await postprocessUpdate({ table, requestedFields, agents, contexts, tools, result, user: context.user.id, role: context.user.role?.id, config });
|
|
2246
2256
|
return {
|
|
2247
|
-
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 }),
|
|
2248
2258
|
job
|
|
2249
2259
|
};
|
|
2250
2260
|
},
|
|
@@ -2285,7 +2295,7 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2285
2295
|
const result = await db3.from(tableNamePlural).select(Object.keys(columns)).where({ id }).first();
|
|
2286
2296
|
const { job } = await postprocessUpdate({ table, requestedFields, agents, contexts, tools, result, user: context.user.id, role: context.user.role?.id, config });
|
|
2287
2297
|
return {
|
|
2288
|
-
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 }),
|
|
2289
2299
|
job
|
|
2290
2300
|
};
|
|
2291
2301
|
},
|
|
@@ -2317,7 +2327,7 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2317
2327
|
}).del();
|
|
2318
2328
|
}
|
|
2319
2329
|
await postprocessDeletion({ table, requestedFields, agents, contexts, tools, result });
|
|
2320
|
-
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 });
|
|
2321
2331
|
},
|
|
2322
2332
|
[`${tableNamePlural}RemoveOne`]: async (_, args, context, info) => {
|
|
2323
2333
|
const { where } = args;
|
|
@@ -2341,7 +2351,7 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2341
2351
|
}
|
|
2342
2352
|
await db3(tableNamePlural).where(where).del();
|
|
2343
2353
|
await postprocessDeletion({ table, requestedFields, agents, contexts, tools, result });
|
|
2344
|
-
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 });
|
|
2345
2355
|
}
|
|
2346
2356
|
};
|
|
2347
2357
|
if (table.type === "items") {
|
|
@@ -2374,20 +2384,20 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2374
2384
|
}
|
|
2375
2385
|
const { db: db3 } = context;
|
|
2376
2386
|
let query = db3.from(tableNamePlural).select("*").where({ id: args.item });
|
|
2377
|
-
query = applyAccessControl(table, context.user
|
|
2387
|
+
query = applyAccessControl(table, query, context.user);
|
|
2378
2388
|
const item = await query.first();
|
|
2379
2389
|
if (!item) {
|
|
2380
2390
|
throw new Error("Item not found, or your user does not have access to it.");
|
|
2381
2391
|
}
|
|
2382
2392
|
const { job, result } = await exists.processField(
|
|
2383
2393
|
"api",
|
|
2384
|
-
context.user.id,
|
|
2385
|
-
context.user.role?.id,
|
|
2386
2394
|
{
|
|
2387
2395
|
...item,
|
|
2388
2396
|
field: args.field
|
|
2389
2397
|
},
|
|
2390
|
-
config
|
|
2398
|
+
config,
|
|
2399
|
+
context.user.id,
|
|
2400
|
+
context.user.role?.id
|
|
2391
2401
|
);
|
|
2392
2402
|
return {
|
|
2393
2403
|
message: job ? "Processing job scheduled." : "Item processed successfully.",
|
|
@@ -2434,7 +2444,10 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2434
2444
|
};
|
|
2435
2445
|
}
|
|
2436
2446
|
console.log("[EXULU] Executing source function directly");
|
|
2437
|
-
const result = await source.execute(
|
|
2447
|
+
const result = await source.execute({
|
|
2448
|
+
...args.inputs,
|
|
2449
|
+
exuluConfig: config
|
|
2450
|
+
});
|
|
2438
2451
|
let jobs = [];
|
|
2439
2452
|
let items = [];
|
|
2440
2453
|
for (const item of result) {
|
|
@@ -2558,14 +2571,14 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2558
2571
|
}
|
|
2559
2572
|
return mutations;
|
|
2560
2573
|
}
|
|
2561
|
-
var applyAccessControl = (table,
|
|
2574
|
+
var applyAccessControl = (table, query, user) => {
|
|
2562
2575
|
const tableNamePlural = table.name.plural.toLowerCase();
|
|
2563
|
-
if (table.name.plural !== "agent_sessions" && user
|
|
2576
|
+
if (table.name.plural !== "agent_sessions" && user?.super_admin === true) {
|
|
2564
2577
|
return query;
|
|
2565
2578
|
}
|
|
2566
|
-
console.log("[EXULU] user.role", user
|
|
2579
|
+
console.log("[EXULU] user.role", user?.role);
|
|
2567
2580
|
console.log("[EXULU] table.name.plural", table.name.plural);
|
|
2568
|
-
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")))) {
|
|
2569
2582
|
console.error("==== Access control error: no role found or no access to entity type. ====");
|
|
2570
2583
|
throw new Error("Access control error: no role found or no access to entity type.");
|
|
2571
2584
|
}
|
|
@@ -2654,7 +2667,7 @@ var removeAgentFields = (requestedFields) => {
|
|
|
2654
2667
|
filtered.push("backend");
|
|
2655
2668
|
return filtered;
|
|
2656
2669
|
};
|
|
2657
|
-
var addAgentFields = async (requestedFields, agents, result, tools, user) => {
|
|
2670
|
+
var addAgentFields = async (args, requestedFields, agents, result, tools, user, contexts) => {
|
|
2658
2671
|
let backend = agents.find((a) => a.id === result?.backend);
|
|
2659
2672
|
if (requestedFields.includes("providerName")) {
|
|
2660
2673
|
result.providerName = backend?.providerName || "";
|
|
@@ -2701,6 +2714,17 @@ var addAgentFields = async (requestedFields, agents, result, tools, user) => {
|
|
|
2701
2714
|
console.log("[EXULU] hydratedTool", hydratedTool);
|
|
2702
2715
|
return hydratedTool;
|
|
2703
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
|
+
}
|
|
2704
2728
|
result.tools = result.tools.filter((tool2) => tool2 !== null);
|
|
2705
2729
|
} else {
|
|
2706
2730
|
result.tools = [];
|
|
@@ -2839,6 +2863,7 @@ var postprocessDeletion = async ({
|
|
|
2839
2863
|
return result;
|
|
2840
2864
|
};
|
|
2841
2865
|
var finalizeRequestedFields = async ({
|
|
2866
|
+
args,
|
|
2842
2867
|
table,
|
|
2843
2868
|
requestedFields,
|
|
2844
2869
|
agents,
|
|
@@ -2855,11 +2880,28 @@ var finalizeRequestedFields = async ({
|
|
|
2855
2880
|
}
|
|
2856
2881
|
if (Array.isArray(result)) {
|
|
2857
2882
|
result = result.map((item) => {
|
|
2858
|
-
return finalizeRequestedFields({
|
|
2883
|
+
return finalizeRequestedFields({
|
|
2884
|
+
args,
|
|
2885
|
+
table,
|
|
2886
|
+
requestedFields,
|
|
2887
|
+
agents,
|
|
2888
|
+
contexts,
|
|
2889
|
+
tools,
|
|
2890
|
+
result: item,
|
|
2891
|
+
user
|
|
2892
|
+
});
|
|
2859
2893
|
});
|
|
2860
2894
|
} else {
|
|
2861
2895
|
if (table.name.singular === "agent") {
|
|
2862
|
-
result = await addAgentFields(
|
|
2896
|
+
result = await addAgentFields(
|
|
2897
|
+
args,
|
|
2898
|
+
requestedFields,
|
|
2899
|
+
agents,
|
|
2900
|
+
result,
|
|
2901
|
+
tools,
|
|
2902
|
+
user,
|
|
2903
|
+
contexts
|
|
2904
|
+
);
|
|
2863
2905
|
if (!requestedFields.includes("backend")) {
|
|
2864
2906
|
delete result.backend;
|
|
2865
2907
|
}
|
|
@@ -2936,18 +2978,18 @@ function createQueries(table, agents, tools, contexts) {
|
|
|
2936
2978
|
const requestedFields = getRequestedFields(info);
|
|
2937
2979
|
const sanitizedFields = sanitizeRequestedFields(table, requestedFields);
|
|
2938
2980
|
let query = db3.from(tableNamePlural).select(sanitizedFields).where({ id: args.id });
|
|
2939
|
-
query = applyAccessControl(table, context.user
|
|
2981
|
+
query = applyAccessControl(table, query, context.user);
|
|
2940
2982
|
let result = await query.first();
|
|
2941
|
-
return finalizeRequestedFields({ table, requestedFields, agents, contexts, tools, result, user: context.user });
|
|
2983
|
+
return finalizeRequestedFields({ args, table, requestedFields, agents, contexts, tools, result, user: context.user });
|
|
2942
2984
|
},
|
|
2943
2985
|
[`${tableNameSingular}ByIds`]: async (_, args, context, info) => {
|
|
2944
2986
|
const { db: db3 } = context;
|
|
2945
2987
|
const requestedFields = getRequestedFields(info);
|
|
2946
2988
|
const sanitizedFields = sanitizeRequestedFields(table, requestedFields);
|
|
2947
2989
|
let query = db3.from(tableNamePlural).select(sanitizedFields).whereIn("id", args.ids);
|
|
2948
|
-
query = applyAccessControl(table, context.user
|
|
2990
|
+
query = applyAccessControl(table, query, context.user);
|
|
2949
2991
|
let result = await query;
|
|
2950
|
-
return finalizeRequestedFields({ table, requestedFields, agents, contexts, tools, result, user: context.user });
|
|
2992
|
+
return finalizeRequestedFields({ args, table, requestedFields, agents, contexts, tools, result, user: context.user });
|
|
2951
2993
|
},
|
|
2952
2994
|
[`${tableNameSingular}One`]: async (_, args, context, info) => {
|
|
2953
2995
|
const { filters = [], sort } = args;
|
|
@@ -2956,10 +2998,10 @@ function createQueries(table, agents, tools, contexts) {
|
|
|
2956
2998
|
const sanitizedFields = sanitizeRequestedFields(table, requestedFields);
|
|
2957
2999
|
let query = db3.from(tableNamePlural).select(sanitizedFields);
|
|
2958
3000
|
query = applyFilters(query, filters, table);
|
|
2959
|
-
query = applyAccessControl(table, context.user
|
|
3001
|
+
query = applyAccessControl(table, query, context.user);
|
|
2960
3002
|
query = applySorting(query, sort);
|
|
2961
3003
|
let result = await query.first();
|
|
2962
|
-
return finalizeRequestedFields({ table, requestedFields, agents, contexts, tools, result, user: context.user });
|
|
3004
|
+
return finalizeRequestedFields({ args, table, requestedFields, agents, contexts, tools, result, user: context.user });
|
|
2963
3005
|
},
|
|
2964
3006
|
[`${tableNamePlural}Pagination`]: async (_, args, context, info) => {
|
|
2965
3007
|
const { limit = 10, page = 0, filters = [], sort } = args;
|
|
@@ -2969,7 +3011,7 @@ function createQueries(table, agents, tools, contexts) {
|
|
|
2969
3011
|
}
|
|
2970
3012
|
let countQuery = db3(tableNamePlural);
|
|
2971
3013
|
countQuery = applyFilters(countQuery, filters, table);
|
|
2972
|
-
countQuery = applyAccessControl(table, context.user
|
|
3014
|
+
countQuery = applyAccessControl(table, countQuery, context.user);
|
|
2973
3015
|
const countResult = await countQuery.count("* as count");
|
|
2974
3016
|
const itemCount = Number(countResult[0]?.count || 0);
|
|
2975
3017
|
const pageCount = Math.ceil(itemCount / limit);
|
|
@@ -2978,7 +3020,7 @@ function createQueries(table, agents, tools, contexts) {
|
|
|
2978
3020
|
const hasNextPage = currentPage < pageCount - 1;
|
|
2979
3021
|
let dataQuery = db3(tableNamePlural);
|
|
2980
3022
|
dataQuery = applyFilters(dataQuery, filters, table);
|
|
2981
|
-
dataQuery = applyAccessControl(table, context.user
|
|
3023
|
+
dataQuery = applyAccessControl(table, dataQuery, context.user);
|
|
2982
3024
|
const requestedFields = getRequestedFields(info);
|
|
2983
3025
|
dataQuery = applySorting(dataQuery, sort);
|
|
2984
3026
|
if (page > 1) {
|
|
@@ -2994,7 +3036,7 @@ function createQueries(table, agents, tools, contexts) {
|
|
|
2994
3036
|
hasPreviousPage,
|
|
2995
3037
|
hasNextPage
|
|
2996
3038
|
},
|
|
2997
|
-
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 })
|
|
2998
3040
|
};
|
|
2999
3041
|
},
|
|
3000
3042
|
// Add generic statistics query for all tables
|
|
@@ -3003,7 +3045,7 @@ function createQueries(table, agents, tools, contexts) {
|
|
|
3003
3045
|
const { db: db3 } = context;
|
|
3004
3046
|
let query = db3(tableNamePlural);
|
|
3005
3047
|
query = applyFilters(query, filters, table);
|
|
3006
|
-
query = applyAccessControl(table, context.user
|
|
3048
|
+
query = applyAccessControl(table, query, context.user);
|
|
3007
3049
|
if (groupBy) {
|
|
3008
3050
|
query = query.select(groupBy).groupBy(groupBy);
|
|
3009
3051
|
if (tableNamePlural === "tracking") {
|
|
@@ -3103,11 +3145,11 @@ var vectorSearch = async ({
|
|
|
3103
3145
|
const chunksTable = getChunksTableName(id);
|
|
3104
3146
|
let countQuery = db3(mainTable);
|
|
3105
3147
|
countQuery = applyFilters(countQuery, filters, table);
|
|
3106
|
-
countQuery = applyAccessControl(table,
|
|
3148
|
+
countQuery = applyAccessControl(table, countQuery, user);
|
|
3107
3149
|
const columns = await db3(mainTable).columnInfo();
|
|
3108
3150
|
let itemsQuery = db3(mainTable).select(Object.keys(columns).map((column) => mainTable + "." + column));
|
|
3109
3151
|
itemsQuery = applyFilters(itemsQuery, filters, table);
|
|
3110
|
-
itemsQuery = applyAccessControl(table,
|
|
3152
|
+
itemsQuery = applyAccessControl(table, itemsQuery, user);
|
|
3111
3153
|
itemsQuery = applySorting(itemsQuery, sort);
|
|
3112
3154
|
if (queryRewriter) {
|
|
3113
3155
|
query = await queryRewriter(query);
|
|
@@ -3126,7 +3168,7 @@ var vectorSearch = async ({
|
|
|
3126
3168
|
const { chunks } = await embedder.generateFromQuery(context.id, query, {
|
|
3127
3169
|
label: table.name.singular,
|
|
3128
3170
|
trigger
|
|
3129
|
-
}, user
|
|
3171
|
+
}, user?.id, role);
|
|
3130
3172
|
if (!chunks?.[0]?.vector) {
|
|
3131
3173
|
throw new Error("No vector generated for query.");
|
|
3132
3174
|
}
|
|
@@ -3516,7 +3558,8 @@ function createSDL(tables, contexts, agents, tools, config, evals, queues2) {
|
|
|
3516
3558
|
const tableNameSingularUpperCaseFirst = table.name.singular.charAt(0).toUpperCase() + table.name.singular.slice(1);
|
|
3517
3559
|
const processorFields = table.fields.filter((field) => field.processor?.execute);
|
|
3518
3560
|
typeDefs += `
|
|
3519
|
-
|
|
3561
|
+
${tableNameSingular === "agent" ? `${tableNameSingular}ById(id: ID!, project: ID): ${tableNameSingular}` : `${tableNameSingular}ById(id: ID!): ${tableNameSingular}`}
|
|
3562
|
+
|
|
3520
3563
|
${tableNameSingular}ByIds(ids: [ID!]!): [${tableNameSingular}]!
|
|
3521
3564
|
${tableNamePlural}Pagination(limit: Int, page: Int, filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingularUpperCaseFirst}PaginationResult
|
|
3522
3565
|
${tableNameSingular}One(filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingular}
|
|
@@ -4448,16 +4491,27 @@ var addPrefixToKey = (keyPath, config) => {
|
|
|
4448
4491
|
}
|
|
4449
4492
|
return `${prefix}/${keyPath}`;
|
|
4450
4493
|
};
|
|
4451
|
-
var uploadFile = async (
|
|
4452
|
-
console.log("[EXULU] Uploading file to S3", key);
|
|
4494
|
+
var uploadFile = async (file, key, config, options = {}, user) => {
|
|
4453
4495
|
if (!config.fileUploads) {
|
|
4454
|
-
throw new Error("File uploads are not configured");
|
|
4496
|
+
throw new Error("File uploads are not configured (in the exported uploadFile function)");
|
|
4455
4497
|
}
|
|
4456
4498
|
const client2 = getS3Client(config);
|
|
4457
|
-
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}/` : "";
|
|
4458
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);
|
|
4459
4513
|
const command = new PutObjectCommand({
|
|
4460
|
-
Bucket:
|
|
4514
|
+
Bucket: customBucket || defaultBucket,
|
|
4461
4515
|
Key: fullKey,
|
|
4462
4516
|
Body: file,
|
|
4463
4517
|
ContentType: options.contentType,
|
|
@@ -4465,6 +4519,10 @@ var uploadFile = async (user, file, key, config, options = {}) => {
|
|
|
4465
4519
|
ContentLength: file.byteLength
|
|
4466
4520
|
});
|
|
4467
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
|
+
}
|
|
4468
4526
|
return fullKey;
|
|
4469
4527
|
};
|
|
4470
4528
|
var createUppyRoutes = async (app, config) => {
|
|
@@ -4623,16 +4681,12 @@ var createUppyRoutes = async (app, config) => {
|
|
|
4623
4681
|
key = key.split("]")[1] || "";
|
|
4624
4682
|
console.log("[EXULU] key", key);
|
|
4625
4683
|
}
|
|
4626
|
-
console.log("[EXULU] Getting object metadata from s3", key);
|
|
4627
|
-
console.log("[EXULU] bucket", bucket);
|
|
4628
|
-
console.log("[EXULU] key", key);
|
|
4629
4684
|
const client2 = getS3Client(config);
|
|
4630
4685
|
const command = new HeadObjectCommand({
|
|
4631
4686
|
Bucket: bucket,
|
|
4632
4687
|
Key: key
|
|
4633
4688
|
});
|
|
4634
4689
|
const response = await client2.send(command);
|
|
4635
|
-
console.log("[EXULU] Object metadata from s3", response);
|
|
4636
4690
|
res.json(response);
|
|
4637
4691
|
res.end();
|
|
4638
4692
|
});
|
|
@@ -4936,6 +4990,7 @@ var createUppyRoutes = async (app, config) => {
|
|
|
4936
4990
|
};
|
|
4937
4991
|
|
|
4938
4992
|
// src/registry/classes.ts
|
|
4993
|
+
import { parseOfficeAsync } from "officeparser";
|
|
4939
4994
|
var s3Client2;
|
|
4940
4995
|
function sanitizeToolName(name) {
|
|
4941
4996
|
if (typeof name !== "string") return "";
|
|
@@ -4946,12 +5001,117 @@ function sanitizeToolName(name) {
|
|
|
4946
5001
|
}
|
|
4947
5002
|
return sanitized;
|
|
4948
5003
|
}
|
|
4949
|
-
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) => {
|
|
4950
5099
|
if (!currentTools) return {};
|
|
4951
5100
|
if (!allExuluTools) return {};
|
|
4952
5101
|
if (!contexts) {
|
|
4953
5102
|
contexts = [];
|
|
4954
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
|
+
}
|
|
4955
5115
|
const sanitizedTools = currentTools ? currentTools.map((tool2) => ({
|
|
4956
5116
|
...tool2,
|
|
4957
5117
|
name: sanitizeToolName(tool2.name)
|
|
@@ -4960,9 +5120,9 @@ var convertToolsArrayToObject = (currentTools, allExuluTools, configs, providera
|
|
|
4960
5120
|
return {
|
|
4961
5121
|
...sanitizedTools?.reduce(
|
|
4962
5122
|
(prev, cur) => {
|
|
4963
|
-
let
|
|
4964
|
-
const userDefinedConfigDescription =
|
|
4965
|
-
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;
|
|
4966
5126
|
const toolDescription = cur.description;
|
|
4967
5127
|
const description = userDefinedConfigDescription || defaultConfigDescription || toolDescription;
|
|
4968
5128
|
return {
|
|
@@ -4975,8 +5135,8 @@ var convertToolsArrayToObject = (currentTools, allExuluTools, configs, providera
|
|
|
4975
5135
|
console.error("[EXULU] Tool execute function is undefined.", cur.tool);
|
|
4976
5136
|
throw new Error("Tool execute function is undefined.");
|
|
4977
5137
|
}
|
|
4978
|
-
if (
|
|
4979
|
-
|
|
5138
|
+
if (toolVariableConfig) {
|
|
5139
|
+
toolVariableConfig = await hydrateVariables(toolVariableConfig || []);
|
|
4980
5140
|
}
|
|
4981
5141
|
let upload = void 0;
|
|
4982
5142
|
if (exuluConfig?.fileUploads?.s3endpoint && exuluConfig?.fileUploads?.s3key && exuluConfig?.fileUploads?.s3secret && exuluConfig?.fileUploads?.s3Bucket) {
|
|
@@ -5029,7 +5189,6 @@ var convertToolsArrayToObject = (currentTools, allExuluTools, configs, providera
|
|
|
5029
5189
|
acc[curr.id] = curr;
|
|
5030
5190
|
return acc;
|
|
5031
5191
|
}, {});
|
|
5032
|
-
console.log("[EXULU] Config", config);
|
|
5033
5192
|
const response = await cur.tool.execute({
|
|
5034
5193
|
...inputs,
|
|
5035
5194
|
sessionID,
|
|
@@ -5044,7 +5203,7 @@ var convertToolsArrayToObject = (currentTools, allExuluTools, configs, providera
|
|
|
5044
5203
|
contexts: contextsMap,
|
|
5045
5204
|
upload,
|
|
5046
5205
|
exuluConfig,
|
|
5047
|
-
|
|
5206
|
+
toolVariablesConfig: toolVariableConfig ? toolVariableConfig.config.reduce((acc, curr) => {
|
|
5048
5207
|
acc[curr.name] = curr.value;
|
|
5049
5208
|
return acc;
|
|
5050
5209
|
}, {}) : {}
|
|
@@ -5078,7 +5237,6 @@ var hydrateVariables = async (tool2) => {
|
|
|
5078
5237
|
const variableName = toolConfig.variable;
|
|
5079
5238
|
const variable = await db3.from("variables").where({ name: variableName }).first();
|
|
5080
5239
|
if (!variable) {
|
|
5081
|
-
console.error("[EXULU] Variable " + variableName + " not found.");
|
|
5082
5240
|
throw new Error("Variable " + variableName + " not found.");
|
|
5083
5241
|
}
|
|
5084
5242
|
let value = variable.value;
|
|
@@ -5090,7 +5248,6 @@ var hydrateVariables = async (tool2) => {
|
|
|
5090
5248
|
return toolConfig;
|
|
5091
5249
|
});
|
|
5092
5250
|
await Promise.all(promises);
|
|
5093
|
-
console.log("[EXULU] Variable values retrieved and added to tool config.");
|
|
5094
5251
|
return tool2;
|
|
5095
5252
|
};
|
|
5096
5253
|
function generateSlug(name) {
|
|
@@ -5314,7 +5471,21 @@ var ExuluAgent2 = class {
|
|
|
5314
5471
|
});
|
|
5315
5472
|
}
|
|
5316
5473
|
console.log("[EXULU] Message count for agent: " + this.name, "loaded for generating sync.", messages.length);
|
|
5317
|
-
|
|
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.`;
|
|
5318
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.";
|
|
5319
5490
|
system += "\n\n" + genericContext;
|
|
5320
5491
|
if (prompt) {
|
|
@@ -5339,7 +5510,7 @@ var ExuluAgent2 = class {
|
|
|
5339
5510
|
system,
|
|
5340
5511
|
prompt,
|
|
5341
5512
|
maxRetries: 2,
|
|
5342
|
-
tools: convertToolsArrayToObject(
|
|
5513
|
+
tools: await convertToolsArrayToObject(
|
|
5343
5514
|
currentTools,
|
|
5344
5515
|
allExuluTools,
|
|
5345
5516
|
toolConfigs,
|
|
@@ -5348,7 +5519,8 @@ var ExuluAgent2 = class {
|
|
|
5348
5519
|
user,
|
|
5349
5520
|
exuluConfig,
|
|
5350
5521
|
session,
|
|
5351
|
-
req
|
|
5522
|
+
req,
|
|
5523
|
+
project
|
|
5352
5524
|
),
|
|
5353
5525
|
stopWhen: [stepCountIs(2)]
|
|
5354
5526
|
});
|
|
@@ -5399,7 +5571,7 @@ var ExuluAgent2 = class {
|
|
|
5399
5571
|
ignoreIncompleteToolCalls: true
|
|
5400
5572
|
}),
|
|
5401
5573
|
maxRetries: 2,
|
|
5402
|
-
tools: convertToolsArrayToObject(
|
|
5574
|
+
tools: await convertToolsArrayToObject(
|
|
5403
5575
|
currentTools,
|
|
5404
5576
|
allExuluTools,
|
|
5405
5577
|
toolConfigs,
|
|
@@ -5408,7 +5580,8 @@ var ExuluAgent2 = class {
|
|
|
5408
5580
|
user,
|
|
5409
5581
|
exuluConfig,
|
|
5410
5582
|
session,
|
|
5411
|
-
req
|
|
5583
|
+
req,
|
|
5584
|
+
project
|
|
5412
5585
|
),
|
|
5413
5586
|
stopWhen: [stepCountIs(2)]
|
|
5414
5587
|
});
|
|
@@ -5449,6 +5622,70 @@ var ExuluAgent2 = class {
|
|
|
5449
5622
|
}
|
|
5450
5623
|
return "";
|
|
5451
5624
|
};
|
|
5625
|
+
/**
|
|
5626
|
+
* Convert file parts in messages to OpenAI Responses API compatible format.
|
|
5627
|
+
* The OpenAI Responses API doesn't support inline file parts with type 'file'.
|
|
5628
|
+
* This function converts:
|
|
5629
|
+
* - Document files (PDF, DOCX, etc.) -> text parts with extracted content using officeparser
|
|
5630
|
+
* - Image files -> image parts (which ARE supported by Responses API)
|
|
5631
|
+
*/
|
|
5632
|
+
async processFilePartsInMessages(messages) {
|
|
5633
|
+
const processedMessages = await Promise.all(messages.map(async (message) => {
|
|
5634
|
+
if (message.role !== "user" || !Array.isArray(message.parts)) {
|
|
5635
|
+
return message;
|
|
5636
|
+
}
|
|
5637
|
+
const processedParts = await Promise.all(message.parts.map(async (part) => {
|
|
5638
|
+
if (part.type !== "file") {
|
|
5639
|
+
return part;
|
|
5640
|
+
}
|
|
5641
|
+
const { mediaType, url, filename } = part;
|
|
5642
|
+
const imageTypes = ["image/png", "image/jpeg", "image/jpg", "image/gif", "image/webp"];
|
|
5643
|
+
if (imageTypes.includes(mediaType)) {
|
|
5644
|
+
console.log(`[EXULU] Converting file part to image part: ${filename} `);
|
|
5645
|
+
return {
|
|
5646
|
+
type: "image",
|
|
5647
|
+
image: url,
|
|
5648
|
+
mimeType: mediaType
|
|
5649
|
+
};
|
|
5650
|
+
}
|
|
5651
|
+
console.log(`[EXULU] Converting file part to text using officeparser: ${filename}`);
|
|
5652
|
+
try {
|
|
5653
|
+
const response = await fetch(url);
|
|
5654
|
+
if (!response.ok) {
|
|
5655
|
+
console.error(`[EXULU] Failed to fetch file: ${filename}, status: ${response.status} `);
|
|
5656
|
+
return {
|
|
5657
|
+
type: "text",
|
|
5658
|
+
text: `[Error: Could not load file ${filename}]`
|
|
5659
|
+
};
|
|
5660
|
+
}
|
|
5661
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
5662
|
+
const extractedText = await parseOfficeAsync(arrayBuffer, {
|
|
5663
|
+
outputErrorToConsole: false,
|
|
5664
|
+
newlineDelimiter: "\n"
|
|
5665
|
+
});
|
|
5666
|
+
return {
|
|
5667
|
+
type: "text",
|
|
5668
|
+
text: `<file file name = "${filename}" >
|
|
5669
|
+
${extractedText}
|
|
5670
|
+
</file>`
|
|
5671
|
+
};
|
|
5672
|
+
} catch (error) {
|
|
5673
|
+
console.error(`[EXULU] Error processing file ${filename}:`, error);
|
|
5674
|
+
return {
|
|
5675
|
+
type: "text",
|
|
5676
|
+
text: `[Error extracting text from file ${filename}: ${error instanceof Error ? error.message : "Unknown error"}]`
|
|
5677
|
+
};
|
|
5678
|
+
}
|
|
5679
|
+
}));
|
|
5680
|
+
const result = {
|
|
5681
|
+
...message,
|
|
5682
|
+
parts: processedParts
|
|
5683
|
+
};
|
|
5684
|
+
console.log("[EXULU] Result: " + JSON.stringify(result, null, 2));
|
|
5685
|
+
return result;
|
|
5686
|
+
}));
|
|
5687
|
+
return processedMessages;
|
|
5688
|
+
}
|
|
5452
5689
|
generateStream = async ({
|
|
5453
5690
|
user,
|
|
5454
5691
|
session,
|
|
@@ -5480,7 +5717,10 @@ var ExuluAgent2 = class {
|
|
|
5480
5717
|
});
|
|
5481
5718
|
let messages = [];
|
|
5482
5719
|
let previousMessagesContent = previousMessages || [];
|
|
5720
|
+
let project;
|
|
5483
5721
|
if (session) {
|
|
5722
|
+
const sessionData = await getSession({ sessionID: session });
|
|
5723
|
+
project = sessionData.project;
|
|
5484
5724
|
console.log("[EXULU] loading previous messages from session: " + session);
|
|
5485
5725
|
const previousMessages2 = await getAgentMessages({
|
|
5486
5726
|
session,
|
|
@@ -5496,18 +5736,21 @@ var ExuluAgent2 = class {
|
|
|
5496
5736
|
// append the new message to the previous messages:
|
|
5497
5737
|
messages: [...previousMessagesContent, message]
|
|
5498
5738
|
});
|
|
5739
|
+
messages = messages.filter(
|
|
5740
|
+
(message2, index, self) => index === self.findIndex((t) => t.id === message2.id)
|
|
5741
|
+
);
|
|
5742
|
+
messages = await this.processFilePartsInMessages(messages);
|
|
5499
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.";
|
|
5500
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.";
|
|
5501
5745
|
system += "\n\n" + genericContext;
|
|
5502
|
-
console.log("[EXULU] tools for agent: " + this.name, currentTools?.map((x) => x.name + " (" + x.id + ")"));
|
|
5503
|
-
console.log("[EXULU] system", system.slice(0, 100) + "...");
|
|
5504
5746
|
const result = streamText({
|
|
5505
5747
|
model,
|
|
5506
5748
|
// Should be a LanguageModelV1
|
|
5507
5749
|
messages: convertToModelMessages(messages, {
|
|
5508
5750
|
ignoreIncompleteToolCalls: true
|
|
5509
5751
|
}),
|
|
5510
|
-
//
|
|
5752
|
+
// PrepareStep could be used here to set the model
|
|
5753
|
+
// for the first step or change other parameters.
|
|
5511
5754
|
system,
|
|
5512
5755
|
maxRetries: 2,
|
|
5513
5756
|
providerOptions: {
|
|
@@ -5515,7 +5758,7 @@ var ExuluAgent2 = class {
|
|
|
5515
5758
|
reasoningSummary: "auto"
|
|
5516
5759
|
}
|
|
5517
5760
|
},
|
|
5518
|
-
tools: convertToolsArrayToObject(
|
|
5761
|
+
tools: await convertToolsArrayToObject(
|
|
5519
5762
|
currentTools,
|
|
5520
5763
|
allExuluTools,
|
|
5521
5764
|
toolConfigs,
|
|
@@ -5524,7 +5767,8 @@ var ExuluAgent2 = class {
|
|
|
5524
5767
|
user,
|
|
5525
5768
|
exuluConfig,
|
|
5526
5769
|
session,
|
|
5527
|
-
req
|
|
5770
|
+
req,
|
|
5771
|
+
project
|
|
5528
5772
|
),
|
|
5529
5773
|
onError: (error) => {
|
|
5530
5774
|
console.error("[EXULU] chat stream error.", error);
|
|
@@ -5560,7 +5804,7 @@ var getSession = async ({ sessionID }) => {
|
|
|
5560
5804
|
};
|
|
5561
5805
|
var saveChat = async ({ session, user, messages }) => {
|
|
5562
5806
|
const { db: db3 } = await postgresClient();
|
|
5563
|
-
const
|
|
5807
|
+
for (const message of messages) {
|
|
5564
5808
|
const mutation = db3.from("agent_messages").insert({
|
|
5565
5809
|
session,
|
|
5566
5810
|
user,
|
|
@@ -5569,9 +5813,8 @@ var saveChat = async ({ session, user, messages }) => {
|
|
|
5569
5813
|
title: message.role === "user" ? "User" : "Assistant"
|
|
5570
5814
|
}).returning("id");
|
|
5571
5815
|
mutation.onConflict("message_id").merge();
|
|
5572
|
-
|
|
5573
|
-
}
|
|
5574
|
-
await Promise.all(promises);
|
|
5816
|
+
await mutation;
|
|
5817
|
+
}
|
|
5575
5818
|
};
|
|
5576
5819
|
var ExuluEmbedder = class {
|
|
5577
5820
|
id;
|
|
@@ -5621,12 +5864,10 @@ var ExuluEmbedder = class {
|
|
|
5621
5864
|
id
|
|
5622
5865
|
} = setting;
|
|
5623
5866
|
let value = "";
|
|
5624
|
-
console.log("[EXULU] variable name", variableName);
|
|
5625
5867
|
const variable = await db3.from("variables").where({ name: variableName }).first();
|
|
5626
5868
|
if (!variable) {
|
|
5627
5869
|
throw new Error("Variable not found for embedder setting: " + name + " in context: " + context + " and embedder: " + this.id);
|
|
5628
5870
|
}
|
|
5629
|
-
console.log("[EXULU] variable", variable);
|
|
5630
5871
|
if (variable.encrypted) {
|
|
5631
5872
|
if (!process.env.NEXTAUTH_SECRET) {
|
|
5632
5873
|
throw new Error("NEXTAUTH_SECRET environment variable is not set, cannot decrypt variable: " + name);
|
|
@@ -5638,14 +5879,12 @@ var ExuluEmbedder = class {
|
|
|
5638
5879
|
throw new Error("Decryption returned empty string - invalid key or corrupted data");
|
|
5639
5880
|
}
|
|
5640
5881
|
value = decrypted;
|
|
5641
|
-
console.log("[EXULU] successfully decrypted value for", name);
|
|
5642
5882
|
} catch (error) {
|
|
5643
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.`);
|
|
5644
5884
|
}
|
|
5645
5885
|
} else {
|
|
5646
5886
|
value = variable.value;
|
|
5647
5887
|
}
|
|
5648
|
-
console.log("[EXULU] variable value", value);
|
|
5649
5888
|
hydrated.push({
|
|
5650
5889
|
id: id || "",
|
|
5651
5890
|
name,
|
|
@@ -5753,14 +5992,20 @@ var ExuluStorage = class {
|
|
|
5753
5992
|
getPresignedUrl = async (key) => {
|
|
5754
5993
|
return await getPresignedUrl(key, this.config);
|
|
5755
5994
|
};
|
|
5756
|
-
uploadFile = async (
|
|
5757
|
-
return await uploadFile(
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
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
|
+
);
|
|
5764
6009
|
};
|
|
5765
6010
|
// todo add upload and delete methods
|
|
5766
6011
|
};
|
|
@@ -5780,7 +6025,19 @@ var ExuluContext = class {
|
|
|
5780
6025
|
// todo typings
|
|
5781
6026
|
configuration;
|
|
5782
6027
|
sources = [];
|
|
5783
|
-
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
|
+
}) {
|
|
5784
6041
|
this.id = id;
|
|
5785
6042
|
this.name = name;
|
|
5786
6043
|
this.fields = fields || [];
|
|
@@ -5797,7 +6054,7 @@ var ExuluContext = class {
|
|
|
5797
6054
|
this.queryRewriter = queryRewriter;
|
|
5798
6055
|
this.resultReranker = resultReranker;
|
|
5799
6056
|
}
|
|
5800
|
-
processField = async (trigger,
|
|
6057
|
+
processField = async (trigger, item, exuluConfig, user, role) => {
|
|
5801
6058
|
console.log("[EXULU] processing field", item.field, " in context", this.id);
|
|
5802
6059
|
console.log("[EXULU] fields", this.fields.map((field2) => field2.name));
|
|
5803
6060
|
const field = this.fields.find((field2) => {
|
|
@@ -5807,7 +6064,7 @@ var ExuluContext = class {
|
|
|
5807
6064
|
console.error("[EXULU] field not found or processor not set for field", item.field, " in context", this.id);
|
|
5808
6065
|
throw new Error("Field not found or processor not set for field " + item.field + " in context " + this.id);
|
|
5809
6066
|
}
|
|
5810
|
-
const exuluStorage = new ExuluStorage({ config });
|
|
6067
|
+
const exuluStorage = new ExuluStorage({ config: exuluConfig });
|
|
5811
6068
|
const queue = await field.processor.config?.queue;
|
|
5812
6069
|
if (queue?.queue.name) {
|
|
5813
6070
|
console.log("[EXULU] processor is in queue mode, scheduling job.");
|
|
@@ -5833,6 +6090,7 @@ var ExuluContext = class {
|
|
|
5833
6090
|
job: job.id
|
|
5834
6091
|
};
|
|
5835
6092
|
}
|
|
6093
|
+
console.log("[EXULU] POS 1 -- EXULU CONTEXT PROCESS FIELD");
|
|
5836
6094
|
const result = await field.processor.execute({
|
|
5837
6095
|
item,
|
|
5838
6096
|
user,
|
|
@@ -5845,13 +6103,24 @@ var ExuluContext = class {
|
|
|
5845
6103
|
delete: this.deleteItem
|
|
5846
6104
|
}
|
|
5847
6105
|
},
|
|
5848
|
-
|
|
6106
|
+
exuluConfig
|
|
5849
6107
|
});
|
|
5850
6108
|
return {
|
|
5851
6109
|
result,
|
|
5852
6110
|
job: void 0
|
|
5853
6111
|
};
|
|
5854
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
|
+
};
|
|
5855
6124
|
deleteAll = async () => {
|
|
5856
6125
|
const { db: db3 } = await postgresClient();
|
|
5857
6126
|
await db3.from(getTableName(this.id)).delete();
|
|
@@ -5861,8 +6130,11 @@ var ExuluContext = class {
|
|
|
5861
6130
|
results: []
|
|
5862
6131
|
};
|
|
5863
6132
|
};
|
|
5864
|
-
executeSource = async (source, inputs) => {
|
|
5865
|
-
return await source.execute(
|
|
6133
|
+
executeSource = async (source, inputs, exuluConfig) => {
|
|
6134
|
+
return await source.execute({
|
|
6135
|
+
...inputs,
|
|
6136
|
+
exuluConfig
|
|
6137
|
+
});
|
|
5866
6138
|
};
|
|
5867
6139
|
tableExists = async () => {
|
|
5868
6140
|
const { db: db3 } = await postgresClient();
|
|
@@ -5918,7 +6190,7 @@ var ExuluContext = class {
|
|
|
5918
6190
|
job
|
|
5919
6191
|
};
|
|
5920
6192
|
};
|
|
5921
|
-
createItem = async (item, config, user, role, upsert) => {
|
|
6193
|
+
createItem = async (item, config, user, role, upsert, generateEmbeddingsOverwrite) => {
|
|
5922
6194
|
if (upsert && (!item.id && !item.external_id)) {
|
|
5923
6195
|
throw new Error("Item id or external id is required for upsert.");
|
|
5924
6196
|
}
|
|
@@ -5945,12 +6217,40 @@ var ExuluContext = class {
|
|
|
5945
6217
|
throw new Error("Failed to create item.");
|
|
5946
6218
|
}
|
|
5947
6219
|
console.log("[EXULU] context configuration", this.configuration);
|
|
5948
|
-
|
|
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) {
|
|
5949
6250
|
console.log("[EXULU] generating embeddings for item", results[0].id);
|
|
5950
|
-
const { job } = await this.embeddings.generate.one({
|
|
6251
|
+
const { job: embeddingsJob } = await this.embeddings.generate.one({
|
|
5951
6252
|
item: {
|
|
5952
6253
|
...item,
|
|
5953
|
-
// important we need to full record here with all fields for the embedder
|
|
5954
6254
|
id: results[0].id
|
|
5955
6255
|
},
|
|
5956
6256
|
user,
|
|
@@ -5958,17 +6258,16 @@ var ExuluContext = class {
|
|
|
5958
6258
|
trigger: "api",
|
|
5959
6259
|
config
|
|
5960
6260
|
});
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
5964
|
-
};
|
|
6261
|
+
if (embeddingsJob) {
|
|
6262
|
+
jobs.push(embeddingsJob);
|
|
6263
|
+
}
|
|
5965
6264
|
}
|
|
5966
6265
|
return {
|
|
5967
6266
|
item: results[0],
|
|
5968
|
-
job: void 0
|
|
6267
|
+
job: jobs.length > 0 ? jobs.join(",") : void 0
|
|
5969
6268
|
};
|
|
5970
6269
|
};
|
|
5971
|
-
updateItem = async (item, config, user, role) => {
|
|
6270
|
+
updateItem = async (item, config, user, role, generateEmbeddingsOverwrite) => {
|
|
5972
6271
|
const { db: db3 } = await postgresClient();
|
|
5973
6272
|
if (item.field) {
|
|
5974
6273
|
delete item.field;
|
|
@@ -5992,8 +6291,35 @@ var ExuluContext = class {
|
|
|
5992
6291
|
}
|
|
5993
6292
|
).returning("id");
|
|
5994
6293
|
await mutation;
|
|
5995
|
-
|
|
5996
|
-
|
|
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({
|
|
5997
6323
|
item: record,
|
|
5998
6324
|
// important we need to full record here with all fields for the embedder
|
|
5999
6325
|
user,
|
|
@@ -6001,14 +6327,13 @@ var ExuluContext = class {
|
|
|
6001
6327
|
trigger: "api",
|
|
6002
6328
|
config
|
|
6003
6329
|
});
|
|
6004
|
-
|
|
6005
|
-
|
|
6006
|
-
|
|
6007
|
-
};
|
|
6330
|
+
if (embeddingsJob) {
|
|
6331
|
+
jobs.push(embeddingsJob);
|
|
6332
|
+
}
|
|
6008
6333
|
}
|
|
6009
6334
|
return {
|
|
6010
6335
|
item: record,
|
|
6011
|
-
job: void 0
|
|
6336
|
+
job: jobs.length > 0 ? jobs.join(",") : void 0
|
|
6012
6337
|
};
|
|
6013
6338
|
};
|
|
6014
6339
|
deleteItem = async (item, user, role) => {
|
|
@@ -6035,6 +6360,34 @@ var ExuluContext = class {
|
|
|
6035
6360
|
job: void 0
|
|
6036
6361
|
};
|
|
6037
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
|
+
};
|
|
6038
6391
|
embeddings = {
|
|
6039
6392
|
generate: {
|
|
6040
6393
|
one: async ({
|
|
@@ -6051,6 +6404,12 @@ var ExuluContext = class {
|
|
|
6051
6404
|
if (!item.id) {
|
|
6052
6405
|
throw new Error("Item id is required for generating embeddings.");
|
|
6053
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;
|
|
6054
6413
|
const queue = await this.embedder.queue;
|
|
6055
6414
|
if (queue?.queue.name) {
|
|
6056
6415
|
console.log("[EXULU] embedder is in queue mode, scheduling job.");
|
|
@@ -6816,7 +7175,7 @@ Mood: friendly and intelligent.
|
|
|
6816
7175
|
const user = authenticationResult.user;
|
|
6817
7176
|
let agentQuery = db3("agents");
|
|
6818
7177
|
agentQuery.select("*");
|
|
6819
|
-
agentQuery = applyAccessControl(agentsSchema2(), authenticationResult.user
|
|
7178
|
+
agentQuery = applyAccessControl(agentsSchema2(), agentQuery, authenticationResult.user);
|
|
6820
7179
|
agentQuery.where({ id: req.params.agent });
|
|
6821
7180
|
const agent = await agentQuery.first();
|
|
6822
7181
|
if (!agent) {
|
|
@@ -6833,7 +7192,7 @@ Mood: friendly and intelligent.
|
|
|
6833
7192
|
} else {
|
|
6834
7193
|
let projectQuery = db3("projects");
|
|
6835
7194
|
projectQuery.select("*");
|
|
6836
|
-
projectQuery = applyAccessControl(projectsSchema2(), authenticationResult.user
|
|
7195
|
+
projectQuery = applyAccessControl(projectsSchema2(), projectQuery, authenticationResult.user);
|
|
6837
7196
|
projectQuery.where({ id: req.params.project });
|
|
6838
7197
|
project = await projectQuery.first();
|
|
6839
7198
|
if (!project) {
|
|
@@ -7175,6 +7534,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7175
7534
|
if (!data.role) {
|
|
7176
7535
|
throw new Error(`Role not set for processor job.`);
|
|
7177
7536
|
}
|
|
7537
|
+
console.log("[EXULU] POS 2 -- EXULU CONTEXT PROCESS FIELD");
|
|
7178
7538
|
const result = await field.processor.execute({
|
|
7179
7539
|
item: data.inputs,
|
|
7180
7540
|
user: data.user,
|
|
@@ -7189,9 +7549,24 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
7189
7549
|
},
|
|
7190
7550
|
config
|
|
7191
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
|
+
}
|
|
7192
7565
|
return {
|
|
7193
7566
|
result,
|
|
7194
|
-
metadata: {
|
|
7567
|
+
metadata: {
|
|
7568
|
+
jobs: jobs.length > 0 ? jobs.join(",") : void 0
|
|
7569
|
+
}
|
|
7195
7570
|
};
|
|
7196
7571
|
}
|
|
7197
7572
|
if (data.type === "eval_run") {
|
|
@@ -7877,7 +8252,7 @@ var ExuluMCP = class {
|
|
|
7877
8252
|
console.log("[EXULU] MCP tool inputs", inputs);
|
|
7878
8253
|
console.log("[EXULU] MCP tool args", args);
|
|
7879
8254
|
const configValues = agentInstance.tools;
|
|
7880
|
-
const tools = convertToolsArrayToObject(
|
|
8255
|
+
const tools = await convertToolsArrayToObject(
|
|
7881
8256
|
[tool2],
|
|
7882
8257
|
allTools,
|
|
7883
8258
|
configValues,
|