@exulu/backend 1.42.2 → 1.43.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 +3 -3
- package/dist/index.cjs +328 -219
- package/dist/index.d.cts +4 -7
- package/dist/index.d.ts +4 -7
- package/dist/index.js +328 -218
- package/package.json +1 -1
- package/types/models/agent.ts +1 -0
package/dist/index.cjs
CHANGED
|
@@ -38,7 +38,6 @@ __export(index_exports, {
|
|
|
38
38
|
ExuluChunkers: () => ExuluChunkers,
|
|
39
39
|
ExuluContext: () => ExuluContext,
|
|
40
40
|
ExuluDefaultAgents: () => ExuluDefaultAgents,
|
|
41
|
-
ExuluDefaultContexts: () => ExuluDefaultContexts,
|
|
42
41
|
ExuluEmbedder: () => ExuluEmbedder,
|
|
43
42
|
ExuluEval: () => ExuluEval2,
|
|
44
43
|
ExuluJobs: () => ExuluJobs,
|
|
@@ -1487,6 +1486,10 @@ var addCoreFields = (schema) => {
|
|
|
1487
1486
|
name: "last_processed_at",
|
|
1488
1487
|
type: "date"
|
|
1489
1488
|
});
|
|
1489
|
+
schema.fields.push({
|
|
1490
|
+
name: "embeddings_updated_at",
|
|
1491
|
+
type: "date"
|
|
1492
|
+
});
|
|
1490
1493
|
if (schema.RBAC) {
|
|
1491
1494
|
if (!schema.fields.some((field) => field.name === "rights_mode")) {
|
|
1492
1495
|
schema.fields.push({
|
|
@@ -1700,7 +1703,8 @@ var checkRecordAccess = async (record, request, user) => {
|
|
|
1700
1703
|
const isPublic = record.rights_mode === "public";
|
|
1701
1704
|
const byUsers = record.rights_mode === "users";
|
|
1702
1705
|
const byRoles = record.rights_mode === "roles";
|
|
1703
|
-
const
|
|
1706
|
+
const createdBy = typeof record.created_by === "string" ? record.created_by : record.created_by?.toString();
|
|
1707
|
+
const isCreator = user ? createdBy === user.id.toString() : false;
|
|
1704
1708
|
const isAdmin = user ? user.super_admin : false;
|
|
1705
1709
|
const isApi = user ? user.type === "api" : false;
|
|
1706
1710
|
let hasAccess = "none";
|
|
@@ -1907,6 +1911,7 @@ ${enumValues}
|
|
|
1907
1911
|
fields.push(" maxContextLength: Int");
|
|
1908
1912
|
fields.push(" provider: String");
|
|
1909
1913
|
fields.push(" authenticationInformation: String");
|
|
1914
|
+
fields.push(" systemInstructions: String");
|
|
1910
1915
|
fields.push(" slug: String");
|
|
1911
1916
|
}
|
|
1912
1917
|
const rbacField = table.RBAC ? " RBAC: RBACData" : "";
|
|
@@ -1971,6 +1976,8 @@ input FilterOperatorDate {
|
|
|
1971
1976
|
input FilterOperatorFloat {
|
|
1972
1977
|
eq: Float
|
|
1973
1978
|
ne: Float
|
|
1979
|
+
lte: Float
|
|
1980
|
+
gte: Float
|
|
1974
1981
|
in: [Float]
|
|
1975
1982
|
and: [FilterOperatorFloat]
|
|
1976
1983
|
or: [FilterOperatorFloat]
|
|
@@ -2034,7 +2041,11 @@ var getRequestedFields = (info) => {
|
|
|
2034
2041
|
return fields.filter((field) => field !== "pageInfo" && field !== "items" && field !== "RBAC");
|
|
2035
2042
|
};
|
|
2036
2043
|
var handleRBACUpdate = async (db3, entityName, resourceId, rbacData, existingRbacRecords) => {
|
|
2037
|
-
const {
|
|
2044
|
+
const {
|
|
2045
|
+
users = [],
|
|
2046
|
+
roles = []
|
|
2047
|
+
/* projects = [] */
|
|
2048
|
+
} = rbacData;
|
|
2038
2049
|
if (!existingRbacRecords) {
|
|
2039
2050
|
existingRbacRecords = await db3.from("rbac").where({
|
|
2040
2051
|
entity: entityName,
|
|
@@ -2043,25 +2054,19 @@ var handleRBACUpdate = async (db3, entityName, resourceId, rbacData, existingRba
|
|
|
2043
2054
|
}
|
|
2044
2055
|
const newUserRecords = new Set(users.map((u) => `${u.id}:${u.rights}`));
|
|
2045
2056
|
const newRoleRecords = new Set(roles.map((r) => `${r.id}:${r.rights}`));
|
|
2046
|
-
const newProjectRecords = new Set(projects.map((p) => `${p.id}:${p.rights}`));
|
|
2047
2057
|
const existingUserRecords = new Set(existingRbacRecords.filter((r) => r.access_type === "User").map((r) => `${r.user_id}:${r.rights}`));
|
|
2048
2058
|
const existingRoleRecords = new Set(existingRbacRecords.filter((r) => r.access_type === "Role").map((r) => `${r.role_id}:${r.rights}`));
|
|
2049
2059
|
const existingProjectRecords = new Set(existingRbacRecords.filter((r) => r.access_type === "Project").map((r) => `${r.project_id}:${r.rights}`));
|
|
2050
2060
|
const usersToCreate = users.filter((u) => !existingUserRecords.has(`${u.id}:${u.rights}`));
|
|
2051
2061
|
const rolesToCreate = roles.filter((r) => !existingRoleRecords.has(`${r.id}:${r.rights}`));
|
|
2052
|
-
const projectsToCreate = projects.filter((p) => !existingProjectRecords.has(`${p.id}:${p.rights}`));
|
|
2053
2062
|
const usersToRemove = existingRbacRecords.filter((r) => r.access_type === "User" && !newUserRecords.has(`${r.user_id}:${r.rights}`));
|
|
2054
2063
|
const rolesToRemove = existingRbacRecords.filter((r) => r.access_type === "Role" && !newRoleRecords.has(`${r.role_id}:${r.rights}`));
|
|
2055
|
-
const projectsToRemove = existingRbacRecords.filter((r) => r.access_type === "Project" && !newProjectRecords.has(`${r.project_id}:${r.rights}`));
|
|
2056
2064
|
if (usersToRemove.length > 0) {
|
|
2057
2065
|
await db3.from("rbac").whereIn("id", usersToRemove.map((r) => r.id)).del();
|
|
2058
2066
|
}
|
|
2059
2067
|
if (rolesToRemove.length > 0) {
|
|
2060
2068
|
await db3.from("rbac").whereIn("id", rolesToRemove.map((r) => r.id)).del();
|
|
2061
2069
|
}
|
|
2062
|
-
if (projectsToRemove.length > 0) {
|
|
2063
|
-
await db3.from("rbac").whereIn("id", projectsToRemove.map((r) => r.id)).del();
|
|
2064
|
-
}
|
|
2065
2070
|
const recordsToInsert = [];
|
|
2066
2071
|
usersToCreate.forEach((user) => {
|
|
2067
2072
|
recordsToInsert.push({
|
|
@@ -2085,17 +2090,6 @@ var handleRBACUpdate = async (db3, entityName, resourceId, rbacData, existingRba
|
|
|
2085
2090
|
updatedAt: /* @__PURE__ */ new Date()
|
|
2086
2091
|
});
|
|
2087
2092
|
});
|
|
2088
|
-
projectsToCreate.forEach((project) => {
|
|
2089
|
-
recordsToInsert.push({
|
|
2090
|
-
entity: entityName,
|
|
2091
|
-
access_type: "Project",
|
|
2092
|
-
target_resource_id: resourceId,
|
|
2093
|
-
project_id: project.id,
|
|
2094
|
-
rights: project.rights,
|
|
2095
|
-
createdAt: /* @__PURE__ */ new Date(),
|
|
2096
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
2097
|
-
});
|
|
2098
|
-
});
|
|
2099
2093
|
if (recordsToInsert.length > 0) {
|
|
2100
2094
|
await db3.from("rbac").insert(recordsToInsert);
|
|
2101
2095
|
}
|
|
@@ -2109,7 +2103,7 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2109
2103
|
if (user.super_admin === true) {
|
|
2110
2104
|
return true;
|
|
2111
2105
|
}
|
|
2112
|
-
if (!user.super_admin && (!user.role || !(table.name.plural === "agents" && user.role.agents === "write") && !(table.name.plural === "workflow_templates" && user.role.workflows === "write") && !(table.name.plural === "variables" && user.role.variables === "write") && !(table.name.plural === "users" && user.role.users === "write") && !((table.name.plural === "test_cases" || table.name.plural === "eval_sets" || table.name.plural === "eval_runs") && user.role.evals === "write"))) {
|
|
2106
|
+
if (!user.super_admin && (table.name.plural === "agents" || table.name.plural === "workflow_templates" || table.name.plural === "variables" || table.name.plural === "users" || table.name.plural === "test_cases" || table.name.plural === "eval_sets" || table.name.plural === "eval_runs") && (!user.role || !(table.name.plural === "agents" && user.role.agents === "write") && !(table.name.plural === "workflow_templates" && user.role.workflows === "write") && !(table.name.plural === "variables" && user.role.variables === "write") && !(table.name.plural === "users" && user.role.users === "write") && !((table.name.plural === "test_cases" || table.name.plural === "eval_sets" || table.name.plural === "eval_runs") && user.role.evals === "write"))) {
|
|
2113
2107
|
console.error("Access control error: no role found for current user or no access to entity type.");
|
|
2114
2108
|
throw new Error("Access control error: no role found for current user or no access to entity type.");
|
|
2115
2109
|
}
|
|
@@ -2163,6 +2157,61 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2163
2157
|
}
|
|
2164
2158
|
};
|
|
2165
2159
|
const mutations = {
|
|
2160
|
+
[`${tableNamePlural}CopyOneById`]: async (_, args, context, info) => {
|
|
2161
|
+
const { db: db3 } = context;
|
|
2162
|
+
const requestedFields = getRequestedFields(info);
|
|
2163
|
+
let { id } = args;
|
|
2164
|
+
if (!id) {
|
|
2165
|
+
throw new Error("ID is required for copying a record.");
|
|
2166
|
+
}
|
|
2167
|
+
await validateWriteAccess(id, context);
|
|
2168
|
+
const item = await db3.from(tableNamePlural).select("*").where({ id }).first();
|
|
2169
|
+
if (!item) {
|
|
2170
|
+
throw new Error("Record not found");
|
|
2171
|
+
}
|
|
2172
|
+
if (item.rights_mode) {
|
|
2173
|
+
item.rights_mode = "private";
|
|
2174
|
+
}
|
|
2175
|
+
if (item.created_at) {
|
|
2176
|
+
item.created_at = /* @__PURE__ */ new Date();
|
|
2177
|
+
}
|
|
2178
|
+
if (item.createdAt) {
|
|
2179
|
+
item.createdAt = /* @__PURE__ */ new Date();
|
|
2180
|
+
}
|
|
2181
|
+
if (item.updated_at) {
|
|
2182
|
+
item.updated_at = /* @__PURE__ */ new Date();
|
|
2183
|
+
}
|
|
2184
|
+
if (item.updatedAt) {
|
|
2185
|
+
item.updatedAt = /* @__PURE__ */ new Date();
|
|
2186
|
+
}
|
|
2187
|
+
if (item.created_by) {
|
|
2188
|
+
item.created_by = context.user.id;
|
|
2189
|
+
}
|
|
2190
|
+
if (item.createdBy) {
|
|
2191
|
+
item.createdBy = context.user.id;
|
|
2192
|
+
}
|
|
2193
|
+
if (item.name) {
|
|
2194
|
+
item.name = item.name + " (Copy)";
|
|
2195
|
+
}
|
|
2196
|
+
Object.keys(item).forEach((key) => {
|
|
2197
|
+
if (table.fields.find((field) => field.name === key)?.type === "json") {
|
|
2198
|
+
if (typeof item[key] === "object" || Array.isArray(item[key])) {
|
|
2199
|
+
item[key] = JSON.stringify(item[key]);
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
});
|
|
2203
|
+
const insert = db3(tableNamePlural).insert({
|
|
2204
|
+
...item,
|
|
2205
|
+
id: db3.fn.uuid()
|
|
2206
|
+
}).returning("*");
|
|
2207
|
+
const result = await insert;
|
|
2208
|
+
if (!result[0]) {
|
|
2209
|
+
throw new Error("Failed to copy record.");
|
|
2210
|
+
}
|
|
2211
|
+
return {
|
|
2212
|
+
item: finalizeRequestedFields({ args, table, requestedFields, agents, contexts, tools, result: result[0], user: context.user })
|
|
2213
|
+
};
|
|
2214
|
+
},
|
|
2166
2215
|
[`${tableNamePlural}CreateOne`]: async (_, args, context, info) => {
|
|
2167
2216
|
const { db: db3 } = context;
|
|
2168
2217
|
const requestedFields = getRequestedFields(info);
|
|
@@ -2420,7 +2469,7 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2420
2469
|
}
|
|
2421
2470
|
const { limit = 10, filters = [], sort } = args;
|
|
2422
2471
|
const { db: db3 } = context;
|
|
2423
|
-
const { items } = await
|
|
2472
|
+
const { items } = await itemsPaginationRequest({
|
|
2424
2473
|
db: db3,
|
|
2425
2474
|
limit,
|
|
2426
2475
|
page: 0,
|
|
@@ -2519,9 +2568,6 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2519
2568
|
};
|
|
2520
2569
|
};
|
|
2521
2570
|
mutations[`${tableNameSingular}GenerateChunks`] = async (_, args, context, info) => {
|
|
2522
|
-
if (!context.user?.super_admin) {
|
|
2523
|
-
throw new Error("You are not authorized to generate chunks via API, user must be super admin.");
|
|
2524
|
-
}
|
|
2525
2571
|
const { db: db3 } = await postgresClient();
|
|
2526
2572
|
const exists = contexts.find((context2) => context2.id === table.id);
|
|
2527
2573
|
if (!exists) {
|
|
@@ -2532,13 +2578,17 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2532
2578
|
const columns = await db3(mainTable).columnInfo();
|
|
2533
2579
|
let query = db3.from(mainTable).select(Object.keys(columns));
|
|
2534
2580
|
if (!args.where) {
|
|
2581
|
+
if (!context.user?.super_admin) {
|
|
2582
|
+
throw new Error("You are not authorized to generate all chunks via API, user must be super admin.");
|
|
2583
|
+
}
|
|
2535
2584
|
const {
|
|
2536
2585
|
jobs: jobs2,
|
|
2537
2586
|
items: items2
|
|
2538
2587
|
} = await embeddings.generate.all(
|
|
2539
2588
|
config,
|
|
2540
2589
|
context.user.id,
|
|
2541
|
-
context.user.role?.id
|
|
2590
|
+
context.user.role?.id,
|
|
2591
|
+
args.limit
|
|
2542
2592
|
);
|
|
2543
2593
|
return {
|
|
2544
2594
|
message: "Chunks generated successfully.",
|
|
@@ -2547,6 +2597,9 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2547
2597
|
};
|
|
2548
2598
|
}
|
|
2549
2599
|
query = applyFilters(query, args.where, table);
|
|
2600
|
+
if (args.limit) {
|
|
2601
|
+
query = query.limit(args.limit);
|
|
2602
|
+
}
|
|
2550
2603
|
const items = await query;
|
|
2551
2604
|
if (items.length === 0) {
|
|
2552
2605
|
throw new Error("No items found to generate chunks for.");
|
|
@@ -2571,9 +2624,6 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2571
2624
|
};
|
|
2572
2625
|
};
|
|
2573
2626
|
mutations[`${tableNameSingular}DeleteChunks`] = async (_, args, context, info) => {
|
|
2574
|
-
if (!context.user?.super_admin) {
|
|
2575
|
-
throw new Error("You are not authorized to delete chunks via API, user must be super admin.");
|
|
2576
|
-
}
|
|
2577
2627
|
const { db: db3 } = await postgresClient();
|
|
2578
2628
|
const id = contexts.find((context2) => context2.id === table.id)?.id;
|
|
2579
2629
|
if (!id) {
|
|
@@ -2582,6 +2632,10 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2582
2632
|
if (args.where) {
|
|
2583
2633
|
let query = db3.from(getTableName(id)).select("id");
|
|
2584
2634
|
query = applyFilters(query, args.where, table);
|
|
2635
|
+
query = applyAccessControl(table, query, context.user);
|
|
2636
|
+
if (args.limit) {
|
|
2637
|
+
query = query.limit(args.limit);
|
|
2638
|
+
}
|
|
2585
2639
|
const items = await query;
|
|
2586
2640
|
if (items.length === 0) {
|
|
2587
2641
|
throw new Error("No items found to delete chunks for.");
|
|
@@ -2595,11 +2649,20 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
2595
2649
|
jobs: []
|
|
2596
2650
|
};
|
|
2597
2651
|
} else {
|
|
2598
|
-
|
|
2599
|
-
|
|
2652
|
+
if (!context.user?.super_admin) {
|
|
2653
|
+
throw new Error("You are not authorized to delete all chunks via API, user must be super admin.");
|
|
2654
|
+
}
|
|
2655
|
+
let count = 0;
|
|
2656
|
+
if (!args.limit) {
|
|
2657
|
+
const result = await db3.from(getChunksTableName(id)).count();
|
|
2658
|
+
count = parseInt(result[0].count);
|
|
2659
|
+
await db3.from(getChunksTableName(id)).truncate();
|
|
2660
|
+
} else {
|
|
2661
|
+
count = await db3.from(getChunksTableName(id)).limit(args.limit).delete();
|
|
2662
|
+
}
|
|
2600
2663
|
return {
|
|
2601
2664
|
message: "Chunks deleted successfully.",
|
|
2602
|
-
items:
|
|
2665
|
+
items: count,
|
|
2603
2666
|
jobs: []
|
|
2604
2667
|
};
|
|
2605
2668
|
}
|
|
@@ -2614,8 +2677,8 @@ var applyAccessControl = (table, query, user, field_prefix) => {
|
|
|
2614
2677
|
}
|
|
2615
2678
|
console.log("[EXULU] user.role", user?.role);
|
|
2616
2679
|
console.log("[EXULU] table.name.plural", table.name.plural);
|
|
2617
|
-
if (user && !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")))) {
|
|
2618
|
-
console.error("==== Access control error: no role found or no access to entity type. ====");
|
|
2680
|
+
if (user && !user?.super_admin && (table.name.plural === "agents" || table.name.plural === "workflow_templates" || table.name.plural === "variables" || table.name.plural === "users" || table.name.plural === "test_cases" || table.name.plural === "eval_sets" || table.name.plural === "eval_runs") && (!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")))) {
|
|
2681
|
+
console.error("==== Access control error: no role found or no access to entity type. ====", user, table.name.plural);
|
|
2619
2682
|
throw new Error("Access control error: no role found or no access to entity type.");
|
|
2620
2683
|
}
|
|
2621
2684
|
const hasRBAC = table.RBAC === true;
|
|
@@ -2658,6 +2721,7 @@ var converOperatorToQuery = (query, fieldName, operators, table, field_prefix) =
|
|
|
2658
2721
|
const isJsonField = field?.type === "json";
|
|
2659
2722
|
const prefix = field_prefix ? field_prefix + "." : "";
|
|
2660
2723
|
fieldName = prefix + fieldName;
|
|
2724
|
+
console.log("[EXULU] operators", operators);
|
|
2661
2725
|
if (operators.eq !== void 0) {
|
|
2662
2726
|
if (isJsonField) {
|
|
2663
2727
|
query = query.whereRaw(`?? = ?::jsonb`, [fieldName, JSON.stringify(operators.eq)]);
|
|
@@ -2689,7 +2753,13 @@ var converOperatorToQuery = (query, fieldName, operators, table, field_prefix) =
|
|
|
2689
2753
|
}
|
|
2690
2754
|
}
|
|
2691
2755
|
if (operators.lte !== void 0) {
|
|
2692
|
-
|
|
2756
|
+
console.log("[EXULU] operators.lte", operators.lte);
|
|
2757
|
+
console.log("[EXULU] fieldName", fieldName);
|
|
2758
|
+
if (operators.lte === 0 || operators.lte === "0") {
|
|
2759
|
+
query = query.whereNull(fieldName).orWhere(fieldName, "=", 0);
|
|
2760
|
+
} else {
|
|
2761
|
+
query = query.where(fieldName, "<=", operators.lte);
|
|
2762
|
+
}
|
|
2693
2763
|
}
|
|
2694
2764
|
if (operators.gte !== void 0) {
|
|
2695
2765
|
query = query.where(fieldName, ">=", operators.gte);
|
|
@@ -2705,7 +2775,8 @@ var backendAgentFields = [
|
|
|
2705
2775
|
"capabilities",
|
|
2706
2776
|
"maxContextLength",
|
|
2707
2777
|
"provider",
|
|
2708
|
-
"authenticationInformation"
|
|
2778
|
+
"authenticationInformation",
|
|
2779
|
+
"systemInstructions"
|
|
2709
2780
|
];
|
|
2710
2781
|
var removeAgentFields = (requestedFields) => {
|
|
2711
2782
|
const filtered = requestedFields.filter((field) => !backendAgentFields.includes(field));
|
|
@@ -2790,6 +2861,9 @@ var addAgentFields = async (args, requestedFields, agents, result, tools, user,
|
|
|
2790
2861
|
if (requestedFields.includes("provider")) {
|
|
2791
2862
|
result.provider = backend?.provider || "";
|
|
2792
2863
|
}
|
|
2864
|
+
if (requestedFields.includes("systemInstructions")) {
|
|
2865
|
+
result.systemInstructions = backend?.config?.instructions || void 0;
|
|
2866
|
+
}
|
|
2793
2867
|
if (!requestedFields.includes("backend")) {
|
|
2794
2868
|
delete result.backend;
|
|
2795
2869
|
}
|
|
@@ -3014,7 +3088,7 @@ var applySorting = (query, sort, field_prefix) => {
|
|
|
3014
3088
|
}
|
|
3015
3089
|
return query;
|
|
3016
3090
|
};
|
|
3017
|
-
var
|
|
3091
|
+
var itemsPaginationRequest = async ({
|
|
3018
3092
|
db: db3,
|
|
3019
3093
|
limit,
|
|
3020
3094
|
page,
|
|
@@ -3044,7 +3118,8 @@ var paginationRequest = async ({
|
|
|
3044
3118
|
if (page > 1) {
|
|
3045
3119
|
dataQuery = dataQuery.offset((page - 1) * limit);
|
|
3046
3120
|
}
|
|
3047
|
-
|
|
3121
|
+
dataQuery = dataQuery.select(fields ? fields : "*").limit(limit);
|
|
3122
|
+
let items = await dataQuery;
|
|
3048
3123
|
return {
|
|
3049
3124
|
items,
|
|
3050
3125
|
pageInfo: {
|
|
@@ -3095,7 +3170,7 @@ function createQueries(table, agents, tools, contexts) {
|
|
|
3095
3170
|
const { limit = 10, page = 0, filters = [], sort } = args;
|
|
3096
3171
|
const requestedFields = getRequestedFields(info);
|
|
3097
3172
|
const sanitizedFields = sanitizeRequestedFields(table, requestedFields);
|
|
3098
|
-
const { items, pageInfo } = await
|
|
3173
|
+
const { items, pageInfo } = await itemsPaginationRequest({
|
|
3099
3174
|
db: db3,
|
|
3100
3175
|
limit,
|
|
3101
3176
|
page,
|
|
@@ -3170,6 +3245,46 @@ function createQueries(table, agents, tools, contexts) {
|
|
|
3170
3245
|
expand: args.expand
|
|
3171
3246
|
});
|
|
3172
3247
|
};
|
|
3248
|
+
queries[`${tableNameSingular}ChunkById`] = async (_, args, context, info) => {
|
|
3249
|
+
const exists = contexts.find((ctx) => ctx.id === table.id);
|
|
3250
|
+
if (!exists) {
|
|
3251
|
+
throw new Error("Context " + table.id + " not found in registry.");
|
|
3252
|
+
}
|
|
3253
|
+
const { db: db3 } = context;
|
|
3254
|
+
const chunksTable = getChunksTableName(exists.id);
|
|
3255
|
+
const mainTable = getTableName(exists.id);
|
|
3256
|
+
const chunk = await db3(chunksTable + " as chunks").select([
|
|
3257
|
+
"chunks.id as chunk_id",
|
|
3258
|
+
"chunks.source as chunk_source",
|
|
3259
|
+
"chunks.content as chunk_content",
|
|
3260
|
+
"chunks.chunk_index",
|
|
3261
|
+
"chunks.metadata as chunk_metadata",
|
|
3262
|
+
db3.raw('chunks."createdAt" as chunk_created_at'),
|
|
3263
|
+
db3.raw('chunks."updatedAt" as chunk_updated_at'),
|
|
3264
|
+
"items.id as item_id",
|
|
3265
|
+
"items.name as item_name",
|
|
3266
|
+
"items.external_id as item_external_id",
|
|
3267
|
+
db3.raw('items."updatedAt" as item_updated_at'),
|
|
3268
|
+
db3.raw('items."createdAt" as item_created_at')
|
|
3269
|
+
]).leftJoin(mainTable + " as items", "chunks.source", "items.id").where("chunks.id", args.id).first();
|
|
3270
|
+
if (!chunk) {
|
|
3271
|
+
return null;
|
|
3272
|
+
}
|
|
3273
|
+
return {
|
|
3274
|
+
chunk_content: chunk.chunk_content,
|
|
3275
|
+
chunk_index: chunk.chunk_index,
|
|
3276
|
+
chunk_id: chunk.chunk_id,
|
|
3277
|
+
chunk_source: chunk.chunk_source,
|
|
3278
|
+
chunk_metadata: chunk.chunk_metadata,
|
|
3279
|
+
chunk_created_at: chunk.chunk_created_at,
|
|
3280
|
+
chunk_updated_at: chunk.chunk_updated_at,
|
|
3281
|
+
item_id: chunk.item_id,
|
|
3282
|
+
item_name: chunk.item_name,
|
|
3283
|
+
item_external_id: chunk.item_external_id,
|
|
3284
|
+
item_updated_at: chunk.item_updated_at,
|
|
3285
|
+
item_created_at: chunk.item_created_at
|
|
3286
|
+
};
|
|
3287
|
+
};
|
|
3173
3288
|
}
|
|
3174
3289
|
return queries;
|
|
3175
3290
|
}
|
|
@@ -3302,120 +3417,52 @@ var vectorSearch = async ({
|
|
|
3302
3417
|
const fullTextWeight = 2;
|
|
3303
3418
|
const semanticWeight = 1;
|
|
3304
3419
|
const rrfK = 50;
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
(1 - (chunks.embedding <=> ${vectorExpr})) AS cosine_distance,
|
|
3352
|
-
|
|
3353
|
-
/* Hybrid RRF score */
|
|
3354
|
-
(
|
|
3355
|
-
COALESCE(1.0 / (? + ft.rank_ix), 0.0) * ?
|
|
3356
|
-
+
|
|
3357
|
-
COALESCE(1.0 / (? + se.rank_ix), 0.0) * ?
|
|
3358
|
-
)::float AS hybrid_score
|
|
3359
|
-
|
|
3360
|
-
FROM full_text ft
|
|
3361
|
-
FULL OUTER JOIN semantic se
|
|
3362
|
-
ON ft.id = se.id
|
|
3363
|
-
JOIN ${chunksTable} as chunks
|
|
3364
|
-
ON COALESCE(ft.id, se.id) = chunks.id
|
|
3365
|
-
JOIN ${mainTable} as items
|
|
3366
|
-
ON items.id = chunks.source
|
|
3367
|
-
WHERE (
|
|
3368
|
-
COALESCE(1.0 / (? + ft.rank_ix), 0.0) * ?
|
|
3369
|
-
+
|
|
3370
|
-
COALESCE(1.0 / (? + se.rank_ix), 0.0) * ?
|
|
3371
|
-
) >= ?
|
|
3372
|
-
AND (chunks.fts IS NULL OR ts_rank(chunks.fts, plainto_tsquery(?, ?)) > ?)
|
|
3373
|
-
AND (chunks.embedding IS NULL OR (1 - (chunks.embedding <=> ${vectorExpr})) >= ?)
|
|
3374
|
-
ORDER BY hybrid_score DESC
|
|
3375
|
-
LIMIT LEAST(?, 250)
|
|
3376
|
-
OFFSET 0
|
|
3377
|
-
`;
|
|
3378
|
-
const bindings = [
|
|
3379
|
-
// full_text: plainto_tsquery(lang, query) in rank and where
|
|
3380
|
-
language,
|
|
3381
|
-
query,
|
|
3382
|
-
language,
|
|
3383
|
-
query,
|
|
3384
|
-
language,
|
|
3385
|
-
query,
|
|
3386
|
-
cutoffs?.tsvector || 0,
|
|
3387
|
-
// full_text tsvector cutoff
|
|
3388
|
-
matchCount,
|
|
3389
|
-
// full_text limit
|
|
3390
|
-
cutoffs?.cosineDistance || 0,
|
|
3391
|
-
// semantic cosine distance cutoff
|
|
3392
|
-
matchCount,
|
|
3393
|
-
// semantic limit
|
|
3394
|
-
// fts_rank (ts_rank) call
|
|
3395
|
-
language,
|
|
3396
|
-
query,
|
|
3397
|
-
// RRF fusion parameters
|
|
3398
|
-
rrfK,
|
|
3399
|
-
fullTextWeight,
|
|
3400
|
-
rrfK,
|
|
3401
|
-
semanticWeight,
|
|
3402
|
-
// WHERE clause hybrid_score filter
|
|
3403
|
-
rrfK,
|
|
3404
|
-
fullTextWeight,
|
|
3405
|
-
rrfK,
|
|
3406
|
-
semanticWeight,
|
|
3407
|
-
cutoffs?.hybrid || 0,
|
|
3408
|
-
// Additional cutoff filters in main WHERE clause
|
|
3409
|
-
language,
|
|
3410
|
-
query,
|
|
3411
|
-
cutoffs?.tsvector || 0,
|
|
3412
|
-
// tsvector cutoff for results from semantic CTE
|
|
3413
|
-
cutoffs?.cosineDistance || 0,
|
|
3414
|
-
// cosine distance cutoff for results from full_text CTE
|
|
3415
|
-
matchCount
|
|
3416
|
-
// final limit
|
|
3417
|
-
];
|
|
3418
|
-
resultChunks = await db3.raw(hybridSQL, bindings).then((r) => r.rows ?? r);
|
|
3420
|
+
let fullTextQuery = db3(chunksTable + " as chunks").select([
|
|
3421
|
+
"chunks.id",
|
|
3422
|
+
"chunks.source",
|
|
3423
|
+
db3.raw(`row_number() OVER (ORDER BY ts_rank(chunks.fts, plainto_tsquery(?, ?)) DESC) AS rank_ix`, [language, query])
|
|
3424
|
+
]).leftJoin(mainTable + " as items", "items.id", "chunks.source").whereRaw(`chunks.fts @@ plainto_tsquery(?, ?)`, [language, query]).whereRaw(`ts_rank(chunks.fts, plainto_tsquery(?, ?)) > ?`, [language, query, cutoffs?.tsvector || 0]).whereRaw(`(items.archived IS FALSE OR items.archived IS NULL)`).limit(Math.min(matchCount * 2, 500));
|
|
3425
|
+
fullTextQuery = applyFilters(fullTextQuery, filters, table, "items");
|
|
3426
|
+
fullTextQuery = applyAccessControl(table, fullTextQuery, user, "items");
|
|
3427
|
+
let semanticQuery = db3(chunksTable + " as chunks").select([
|
|
3428
|
+
"chunks.id",
|
|
3429
|
+
"chunks.source",
|
|
3430
|
+
db3.raw(`row_number() OVER (ORDER BY chunks.embedding <=> ${vectorExpr} ASC) AS rank_ix`)
|
|
3431
|
+
]).leftJoin(mainTable + " as items", "items.id", "chunks.source").whereNotNull("chunks.embedding").whereRaw(`(1 - (chunks.embedding <=> ${vectorExpr})) >= ?`, [cutoffs?.cosineDistance || 0]).whereRaw(`(items.archived IS FALSE OR items.archived IS NULL)`).limit(Math.min(matchCount * 2, 500));
|
|
3432
|
+
semanticQuery = applyFilters(semanticQuery, filters, table, "items");
|
|
3433
|
+
semanticQuery = applyAccessControl(table, semanticQuery, user, "items");
|
|
3434
|
+
let hybridQuery = db3.with("full_text", fullTextQuery).with("semantic", semanticQuery).select([
|
|
3435
|
+
"items.id as item_id",
|
|
3436
|
+
"items.name as item_name",
|
|
3437
|
+
"items.external_id as item_external_id",
|
|
3438
|
+
"chunks.id as chunk_id",
|
|
3439
|
+
"chunks.source",
|
|
3440
|
+
"chunks.content",
|
|
3441
|
+
"chunks.chunk_index",
|
|
3442
|
+
"chunks.metadata",
|
|
3443
|
+
db3.raw('chunks."createdAt" as chunk_created_at'),
|
|
3444
|
+
db3.raw('chunks."updatedAt" as chunk_updated_at'),
|
|
3445
|
+
db3.raw('items."updatedAt" as item_updated_at'),
|
|
3446
|
+
db3.raw('items."createdAt" as item_created_at'),
|
|
3447
|
+
db3.raw(`ts_rank(chunks.fts, plainto_tsquery(?, ?)) AS fts_rank`, [language, query]),
|
|
3448
|
+
db3.raw(`(1 - (chunks.embedding <=> ${vectorExpr})) AS cosine_distance`),
|
|
3449
|
+
db3.raw(`
|
|
3450
|
+
(
|
|
3451
|
+
COALESCE(1.0 / (? + ft.rank_ix), 0.0) * ?
|
|
3452
|
+
+
|
|
3453
|
+
COALESCE(1.0 / (? + se.rank_ix), 0.0) * ?
|
|
3454
|
+
)::float AS hybrid_score
|
|
3455
|
+
`, [rrfK, fullTextWeight, rrfK, semanticWeight])
|
|
3456
|
+
]).from("full_text as ft").fullOuterJoin("semantic as se", "ft.id", "se.id").join(chunksTable + " as chunks", function() {
|
|
3457
|
+
this.on(db3.raw("COALESCE(ft.id, se.id)"), "=", "chunks.id");
|
|
3458
|
+
}).join(mainTable + " as items", "items.id", "chunks.source").whereRaw(`
|
|
3459
|
+
(
|
|
3460
|
+
COALESCE(1.0 / (? + ft.rank_ix), 0.0) * ?
|
|
3461
|
+
+
|
|
3462
|
+
COALESCE(1.0 / (? + se.rank_ix), 0.0) * ?
|
|
3463
|
+
) >= ?
|
|
3464
|
+
`, [rrfK, fullTextWeight, rrfK, semanticWeight, cutoffs?.hybrid || 0]).whereRaw(`(chunks.fts IS NULL OR ts_rank(chunks.fts, plainto_tsquery(?, ?)) > ?)`, [language, query, cutoffs?.tsvector || 0]).whereRaw(`(chunks.embedding IS NULL OR (1 - (chunks.embedding <=> ${vectorExpr})) >= ?)`, [cutoffs?.cosineDistance || 0]).orderByRaw("hybrid_score DESC").limit(Math.min(matchCount, 250));
|
|
3465
|
+
resultChunks = await hybridQuery;
|
|
3419
3466
|
}
|
|
3420
3467
|
console.log("[EXULU] Vector search chunk results:", resultChunks?.length);
|
|
3421
3468
|
let results = resultChunks.map((chunk) => ({
|
|
@@ -3651,8 +3698,8 @@ var contextToTableDefinition = (context) => {
|
|
|
3651
3698
|
type: "text"
|
|
3652
3699
|
});
|
|
3653
3700
|
definition.fields.push({
|
|
3654
|
-
name: "
|
|
3655
|
-
type: "
|
|
3701
|
+
name: "chunks_count",
|
|
3702
|
+
type: "number"
|
|
3656
3703
|
});
|
|
3657
3704
|
definition.fields.push({
|
|
3658
3705
|
name: "name",
|
|
@@ -3755,10 +3802,13 @@ function createSDL(tables, contexts, agents, tools, config, evals, queues2) {
|
|
|
3755
3802
|
if (table.type === "items") {
|
|
3756
3803
|
typeDefs += `
|
|
3757
3804
|
${tableNamePlural}VectorSearch(query: String!, method: VectorMethodEnum!, filters: [Filter${tableNameSingularUpperCaseFirst}], cutoffs: SearchCutoffs, expand: SearchExpand): ${tableNameSingular}VectorSearchResult
|
|
3805
|
+
${tableNameSingular}ChunkById(id: ID!): ${tableNameSingular}VectorSearchChunk
|
|
3758
3806
|
`;
|
|
3759
3807
|
}
|
|
3760
3808
|
mutationDefs += `
|
|
3761
3809
|
${tableNamePlural}CreateOne(input: ${tableNameSingular}Input!, upsert: Boolean): ${tableNameSingular}MutationPayload
|
|
3810
|
+
${tableNamePlural}CopyOneById(id: ID!): ${tableNameSingular}MutationPayload
|
|
3811
|
+
|
|
3762
3812
|
${tableNamePlural}UpdateOne(where: [Filter${tableNameSingularUpperCaseFirst}], input: ${tableNameSingular}Input!): ${tableNameSingular}MutationPayload
|
|
3763
3813
|
${tableNamePlural}UpdateOneById(id: ID!, input: ${tableNameSingular}Input!): ${tableNameSingular}MutationPayload
|
|
3764
3814
|
${tableNamePlural}RemoveOneById(id: ID!): ${tableNameSingular}
|
|
@@ -3766,9 +3816,9 @@ function createSDL(tables, contexts, agents, tools, config, evals, queues2) {
|
|
|
3766
3816
|
`;
|
|
3767
3817
|
if (table.type === "items") {
|
|
3768
3818
|
mutationDefs += `
|
|
3769
|
-
${tableNameSingular}GenerateChunks(where: [Filter${tableNameSingularUpperCaseFirst}]): ${tableNameSingular}GenerateChunksReturnPayload
|
|
3819
|
+
${tableNameSingular}GenerateChunks(where: [Filter${tableNameSingularUpperCaseFirst}], limit: Int): ${tableNameSingular}GenerateChunksReturnPayload
|
|
3770
3820
|
${tableNameSingular}ExecuteSource(source: ID!, inputs: JSON!): ${tableNameSingular}ExecuteSourceReturnPayload
|
|
3771
|
-
${tableNameSingular}DeleteChunks(where: [Filter${tableNameSingularUpperCaseFirst}]): ${tableNameSingular}DeleteChunksReturnPayload
|
|
3821
|
+
${tableNameSingular}DeleteChunks(where: [Filter${tableNameSingularUpperCaseFirst}], limit: Int): ${tableNameSingular}DeleteChunksReturnPayload
|
|
3772
3822
|
`;
|
|
3773
3823
|
if (table.processor) {
|
|
3774
3824
|
mutationDefs += `
|
|
@@ -3902,6 +3952,9 @@ type PageInfo {
|
|
|
3902
3952
|
typeDefs += `
|
|
3903
3953
|
contextById(id: ID!): Context
|
|
3904
3954
|
`;
|
|
3955
|
+
typeDefs += `
|
|
3956
|
+
getUniquePromptTags: [String!]!
|
|
3957
|
+
`;
|
|
3905
3958
|
mutationDefs += `
|
|
3906
3959
|
runEval(id: ID!, test_case_ids: [ID!]): RunEvalReturnPayload
|
|
3907
3960
|
`;
|
|
@@ -4359,6 +4412,38 @@ type PageInfo {
|
|
|
4359
4412
|
array.push("agents");
|
|
4360
4413
|
return [...new Set(array)].sort();
|
|
4361
4414
|
};
|
|
4415
|
+
resolvers.Query["getUniquePromptTags"] = async (_, args, context, info) => {
|
|
4416
|
+
const { db: db3 } = context;
|
|
4417
|
+
const user = context.user;
|
|
4418
|
+
const promptTable = tables.find((t) => t.name.plural === "prompt_library");
|
|
4419
|
+
if (!promptTable) {
|
|
4420
|
+
throw new Error("Prompt library table not found");
|
|
4421
|
+
}
|
|
4422
|
+
let query = db3.from("prompt_library").select("tags");
|
|
4423
|
+
query = applyAccessControl(promptTable, query, user);
|
|
4424
|
+
const results = await query;
|
|
4425
|
+
const allTags = [];
|
|
4426
|
+
for (const row of results) {
|
|
4427
|
+
if (row.tags) {
|
|
4428
|
+
let tags = [];
|
|
4429
|
+
if (typeof row.tags === "string") {
|
|
4430
|
+
try {
|
|
4431
|
+
tags = JSON.parse(row.tags);
|
|
4432
|
+
} catch (e) {
|
|
4433
|
+
tags = [row.tags];
|
|
4434
|
+
}
|
|
4435
|
+
} else if (Array.isArray(row.tags)) {
|
|
4436
|
+
tags = row.tags;
|
|
4437
|
+
}
|
|
4438
|
+
tags.forEach((tag) => {
|
|
4439
|
+
if (tag && typeof tag === "string" && tag.trim()) {
|
|
4440
|
+
allTags.push(tag.trim().toLowerCase());
|
|
4441
|
+
}
|
|
4442
|
+
});
|
|
4443
|
+
}
|
|
4444
|
+
}
|
|
4445
|
+
return [...new Set(allTags)].sort();
|
|
4446
|
+
};
|
|
4362
4447
|
modelDefs += `
|
|
4363
4448
|
type ProviderPaginationResult {
|
|
4364
4449
|
items: [Provider]!
|
|
@@ -4730,14 +4815,16 @@ var addBucketPrefixToKey = (key, bucket) => {
|
|
|
4730
4815
|
}
|
|
4731
4816
|
return `${bucket}/${key}`;
|
|
4732
4817
|
};
|
|
4733
|
-
var uploadFile = async (file, fileName, config, options = {}, user, customBucket) => {
|
|
4818
|
+
var uploadFile = async (file, fileName, config, options = {}, user, customBucket, global) => {
|
|
4734
4819
|
if (!config.fileUploads) {
|
|
4735
4820
|
throw new Error("File uploads are not configured (in the exported uploadFile function)");
|
|
4736
4821
|
}
|
|
4737
4822
|
const client2 = getS3Client(config);
|
|
4738
4823
|
let defaultBucket = config.fileUploads.s3Bucket;
|
|
4739
4824
|
let key = fileName;
|
|
4740
|
-
|
|
4825
|
+
if (!global) {
|
|
4826
|
+
key = addUserPrefixToKey(key, user || "api");
|
|
4827
|
+
}
|
|
4741
4828
|
key = addGeneralPrefixToKey(key, config);
|
|
4742
4829
|
const sanitizedMetadata = sanitizeMetadata(options.metadata);
|
|
4743
4830
|
const command = new import_client_s3.PutObjectCommand({
|
|
@@ -4888,7 +4975,7 @@ var createUppyRoutes = async (app, contexts, config) => {
|
|
|
4888
4975
|
return;
|
|
4889
4976
|
}
|
|
4890
4977
|
let allowed = false;
|
|
4891
|
-
if (user.type === "api" || user.super_admin || key.includes(`user_${user.id}/`)) {
|
|
4978
|
+
if (user.type === "api" || user.super_admin || !key.includes(`user_`) || key.includes(`user_${user.id}/`)) {
|
|
4892
4979
|
allowed = true;
|
|
4893
4980
|
}
|
|
4894
4981
|
if (!allowed) {
|
|
@@ -4966,9 +5053,16 @@ var createUppyRoutes = async (app, contexts, config) => {
|
|
|
4966
5053
|
return;
|
|
4967
5054
|
}
|
|
4968
5055
|
const client2 = getS3Client(config);
|
|
5056
|
+
let prefix = `${config.fileUploads.s3prefix ? config.fileUploads.s3prefix.replace(/\/$/, "") + "/" : ""}`;
|
|
5057
|
+
if (!req.headers.global) {
|
|
5058
|
+
prefix += `user_${authenticationResult.user.id}`;
|
|
5059
|
+
} else {
|
|
5060
|
+
prefix += "global";
|
|
5061
|
+
}
|
|
5062
|
+
console.log("[EXULU] prefix", prefix);
|
|
4969
5063
|
const command = new import_client_s3.ListObjectsV2Command({
|
|
4970
5064
|
Bucket: config.fileUploads.s3Bucket,
|
|
4971
|
-
Prefix:
|
|
5065
|
+
Prefix: prefix,
|
|
4972
5066
|
MaxKeys: 9,
|
|
4973
5067
|
...req.query.continuationToken && { ContinuationToken: req.query.continuationToken }
|
|
4974
5068
|
});
|
|
@@ -5051,7 +5145,13 @@ var createUppyRoutes = async (app, contexts, config) => {
|
|
|
5051
5145
|
const { filename, contentType } = extractFileParameters(req);
|
|
5052
5146
|
validateFileParameters(filename, contentType);
|
|
5053
5147
|
const key = generateS3Key2(filename);
|
|
5054
|
-
let fullKey =
|
|
5148
|
+
let fullKey = key;
|
|
5149
|
+
console.log("[EXULU] global", req.headers.global);
|
|
5150
|
+
if (!req.headers.global) {
|
|
5151
|
+
fullKey = addUserPrefixToKey(key, user.type === "api" ? "api" : user.id);
|
|
5152
|
+
} else {
|
|
5153
|
+
fullKey = "global/" + key;
|
|
5154
|
+
}
|
|
5055
5155
|
fullKey = addGeneralPrefixToKey(fullKey, config);
|
|
5056
5156
|
console.log("[EXULU] signing on server for user", user.id, "with key", fullKey);
|
|
5057
5157
|
(0, import_s3_request_presigner.getSignedUrl)(
|
|
@@ -5107,7 +5207,13 @@ var createUppyRoutes = async (app, contexts, config) => {
|
|
|
5107
5207
|
return res.status(400).json({ error: "s3: content type must be a string" });
|
|
5108
5208
|
}
|
|
5109
5209
|
const key = `${(0, import_node_crypto.randomUUID)()}-_EXULU_${filename}`;
|
|
5110
|
-
let fullKey =
|
|
5210
|
+
let fullKey = key;
|
|
5211
|
+
console.log("[EXULU] global", req.headers.global);
|
|
5212
|
+
if (!req.headers.global) {
|
|
5213
|
+
fullKey = addUserPrefixToKey(key, user.type === "api" ? "api" : user.id);
|
|
5214
|
+
} else {
|
|
5215
|
+
fullKey = "global/" + key;
|
|
5216
|
+
}
|
|
5111
5217
|
fullKey = addGeneralPrefixToKey(fullKey, config);
|
|
5112
5218
|
console.log("[EXULU] signing on server for user", user.id, "with key", fullKey);
|
|
5113
5219
|
const params = {
|
|
@@ -5755,6 +5861,31 @@ var ExuluAgent2 = class {
|
|
|
5755
5861
|
they are talking with the current date in mind as a reference.`;
|
|
5756
5862
|
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.";
|
|
5757
5863
|
system += "\n\n" + genericContext;
|
|
5864
|
+
const includesContextSearchTool = currentTools?.some((tool2) => tool2.name.toLowerCase().includes("context_search") || tool2.id.includes("context_search"));
|
|
5865
|
+
console.log("[EXULU] Current tools: " + currentTools?.map((tool2) => tool2.name));
|
|
5866
|
+
console.log("[EXULU] Includes context search tool: " + includesContextSearchTool);
|
|
5867
|
+
if (includesContextSearchTool) {
|
|
5868
|
+
system += `
|
|
5869
|
+
|
|
5870
|
+
|
|
5871
|
+
|
|
5872
|
+
When you use a context search tool, you will include references to the items
|
|
5873
|
+
retrieved from the context search tool inline in the response using this exact JSON format
|
|
5874
|
+
(all on one line, no line breaks):
|
|
5875
|
+
{item_name: <item_name>, item_id: <item_id>, context: <context_id>, chunk_id: <chunk_id>, chunk_index: <chunk_index>}
|
|
5876
|
+
|
|
5877
|
+
IMPORTANT formatting rules:
|
|
5878
|
+
- Use the exact format shown above, all on ONE line
|
|
5879
|
+
- Do NOT use quotes around field names or values
|
|
5880
|
+
- Use the context ID (e.g., "dx-newlift-newton-knowledge-g3y6r1") from the tool result
|
|
5881
|
+
- Include the file/item name, not the full path
|
|
5882
|
+
- Separate multiple citations with spaces
|
|
5883
|
+
|
|
5884
|
+
Example: {item_name: document.pdf, item_id: abc123, context: my-context-id, chunk_id: chunk_456, chunk_index: 0}
|
|
5885
|
+
|
|
5886
|
+
The citations will be rendered as interactive badges in the UI.
|
|
5887
|
+
`;
|
|
5888
|
+
}
|
|
5758
5889
|
if (prompt) {
|
|
5759
5890
|
let result = { object: null, text: "" };
|
|
5760
5891
|
let inputTokens = 0;
|
|
@@ -6015,6 +6146,31 @@ ${extractedText}
|
|
|
6015
6146
|
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.";
|
|
6016
6147
|
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.";
|
|
6017
6148
|
system += "\n\n" + genericContext;
|
|
6149
|
+
const includesContextSearchTool = currentTools?.some((tool2) => tool2.name.toLowerCase().includes("context_search") || tool2.id.includes("context_search"));
|
|
6150
|
+
console.log("[EXULU] Current tools: " + currentTools?.map((tool2) => tool2.name));
|
|
6151
|
+
console.log("[EXULU] Includes context search tool: " + includesContextSearchTool);
|
|
6152
|
+
if (includesContextSearchTool) {
|
|
6153
|
+
system += `
|
|
6154
|
+
|
|
6155
|
+
|
|
6156
|
+
|
|
6157
|
+
When you use a context search tool, you will include references to the items
|
|
6158
|
+
retrieved from the context search tool inline in the response using this exact JSON format
|
|
6159
|
+
(all on one line, no line breaks):
|
|
6160
|
+
{item_name: <item_name>, item_id: <item_id>, context: <context_id>, chunk_id: <chunk_id>, chunk_index: <chunk_index>}
|
|
6161
|
+
|
|
6162
|
+
IMPORTANT formatting rules:
|
|
6163
|
+
- Use the exact format shown above, all on ONE line
|
|
6164
|
+
- Do NOT use quotes around field names or values
|
|
6165
|
+
- Use the context ID (e.g., "dx-newlift-newton-knowledge-g3y6r1") from the tool result
|
|
6166
|
+
- Include the file/item name, not the full path
|
|
6167
|
+
- Separate multiple citations with spaces
|
|
6168
|
+
|
|
6169
|
+
Example: {item_name: document.pdf, item_id: abc123, context: my-context-id, chunk_id: chunk_456, chunk_index: 0}
|
|
6170
|
+
|
|
6171
|
+
The citations will be rendered as interactive badges in the UI.
|
|
6172
|
+
`;
|
|
6173
|
+
}
|
|
6018
6174
|
const result = (0, import_ai.streamText)({
|
|
6019
6175
|
model,
|
|
6020
6176
|
// Should be a LanguageModelV1
|
|
@@ -6558,6 +6714,7 @@ var ExuluContext = class {
|
|
|
6558
6714
|
})));
|
|
6559
6715
|
}
|
|
6560
6716
|
await db3.from(getTableName(this.id)).where({ id: item.id }).update({
|
|
6717
|
+
chunks_count: chunks?.length || 0,
|
|
6561
6718
|
embeddings_updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
6562
6719
|
}).returning("id");
|
|
6563
6720
|
return {
|
|
@@ -6827,9 +6984,13 @@ var ExuluContext = class {
|
|
|
6827
6984
|
trigger: trigger || "agent"
|
|
6828
6985
|
}, role, void 0);
|
|
6829
6986
|
},
|
|
6830
|
-
all: async (config, userId, roleId) => {
|
|
6987
|
+
all: async (config, userId, roleId, limit) => {
|
|
6831
6988
|
const { db: db3 } = await postgresClient();
|
|
6832
|
-
|
|
6989
|
+
let query = db3.from(getTableName(this.id)).select("*");
|
|
6990
|
+
if (limit) {
|
|
6991
|
+
query = query.limit(limit);
|
|
6992
|
+
}
|
|
6993
|
+
const items = await query;
|
|
6833
6994
|
const jobs = [];
|
|
6834
6995
|
const queue = await this.embedder?.queue;
|
|
6835
6996
|
if (!queue?.queue.name && items.length > 2e3) {
|
|
@@ -6874,7 +7035,7 @@ var ExuluContext = class {
|
|
|
6874
7035
|
table.text("rights_mode").defaultTo(this.configuration?.defaultRightsMode ?? "private");
|
|
6875
7036
|
table.integer("textlength");
|
|
6876
7037
|
table.text("source");
|
|
6877
|
-
table.
|
|
7038
|
+
table.integer("chunks_count").defaultTo(0);
|
|
6878
7039
|
for (const field of this.fields) {
|
|
6879
7040
|
let { type, name, unique } = field;
|
|
6880
7041
|
if (!type || !name) {
|
|
@@ -7403,12 +7564,6 @@ Mood: friendly and intelligent.
|
|
|
7403
7564
|
return;
|
|
7404
7565
|
}
|
|
7405
7566
|
}
|
|
7406
|
-
if (user?.type !== "api" && !user?.super_admin && req.body.resourceId !== user?.id) {
|
|
7407
|
-
res.status(400).json({
|
|
7408
|
-
message: "The provided user id in the resourceId field is not the same as the authenticated user. Only super admins and API users can impersonate other users."
|
|
7409
|
-
});
|
|
7410
|
-
return;
|
|
7411
|
-
}
|
|
7412
7567
|
console.log("[EXULU] agent tools", agentInstance.tools?.map((x) => x.name + " (" + x.id + ")"));
|
|
7413
7568
|
const disabledTools = req.body.disabledTools ? req.body.disabledTools : [];
|
|
7414
7569
|
let enabledTools = await getEnabledTools(agentInstance, tools, disabledTools, agents, user);
|
|
@@ -9453,45 +9608,6 @@ var createLogger = ({
|
|
|
9453
9608
|
};
|
|
9454
9609
|
var logger_default = createLogger;
|
|
9455
9610
|
|
|
9456
|
-
// src/templates/contexts/code-standards.ts
|
|
9457
|
-
var codeStandardsContext = new ExuluContext({
|
|
9458
|
-
id: "code_standards",
|
|
9459
|
-
name: "Code Standards",
|
|
9460
|
-
description: "Code standards that can be used with the Exulu CLI.",
|
|
9461
|
-
configuration: {
|
|
9462
|
-
defaultRightsMode: "public"
|
|
9463
|
-
},
|
|
9464
|
-
fields: [{
|
|
9465
|
-
name: "Best practices",
|
|
9466
|
-
type: "longText"
|
|
9467
|
-
}, {
|
|
9468
|
-
name: "Code style",
|
|
9469
|
-
type: "longText"
|
|
9470
|
-
}, {
|
|
9471
|
-
name: "Tech stack",
|
|
9472
|
-
type: "longText"
|
|
9473
|
-
}],
|
|
9474
|
-
active: true
|
|
9475
|
-
});
|
|
9476
|
-
|
|
9477
|
-
// src/templates/contexts/outputs.ts
|
|
9478
|
-
var outputsContext = new ExuluContext({
|
|
9479
|
-
id: "outputs_default_context",
|
|
9480
|
-
name: "Outputs",
|
|
9481
|
-
description: "Outputs from agent sessions that you have saved for re-used later.",
|
|
9482
|
-
configuration: {
|
|
9483
|
-
defaultRightsMode: "private",
|
|
9484
|
-
calculateVectors: "manual"
|
|
9485
|
-
},
|
|
9486
|
-
fields: [
|
|
9487
|
-
{
|
|
9488
|
-
name: "content",
|
|
9489
|
-
type: "longText"
|
|
9490
|
-
}
|
|
9491
|
-
],
|
|
9492
|
-
active: true
|
|
9493
|
-
});
|
|
9494
|
-
|
|
9495
9611
|
// src/registry/index.ts
|
|
9496
9612
|
var import_winston2 = __toESM(require("winston"), 1);
|
|
9497
9613
|
var import_util = __toESM(require("util"), 1);
|
|
@@ -10618,9 +10734,7 @@ var ExuluApp = class {
|
|
|
10618
10734
|
...evals ?? []
|
|
10619
10735
|
] : [];
|
|
10620
10736
|
this._contexts = {
|
|
10621
|
-
...contexts
|
|
10622
|
-
codeStandardsContext,
|
|
10623
|
-
outputsContext
|
|
10737
|
+
...contexts
|
|
10624
10738
|
};
|
|
10625
10739
|
this._agents = [
|
|
10626
10740
|
claudeSonnet4Agent,
|
|
@@ -12356,10 +12470,6 @@ var ExuluJobs = {
|
|
|
12356
12470
|
validate: validateJob
|
|
12357
12471
|
}
|
|
12358
12472
|
};
|
|
12359
|
-
var ExuluDefaultContexts = {
|
|
12360
|
-
codeStandards: codeStandardsContext,
|
|
12361
|
-
outputs: outputsContext
|
|
12362
|
-
};
|
|
12363
12473
|
var ExuluDefaultAgents = {
|
|
12364
12474
|
anthropic: {
|
|
12365
12475
|
opus4: claudeOpus4Agent,
|
|
@@ -12518,7 +12628,6 @@ var ExuluChunkers = {
|
|
|
12518
12628
|
ExuluChunkers,
|
|
12519
12629
|
ExuluContext,
|
|
12520
12630
|
ExuluDefaultAgents,
|
|
12521
|
-
ExuluDefaultContexts,
|
|
12522
12631
|
ExuluEmbedder,
|
|
12523
12632
|
ExuluEval,
|
|
12524
12633
|
ExuluJobs,
|