@exulu/backend 1.12.0 → 1.14.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 +301 -265
- package/dist/index.d.cts +27 -15
- package/dist/index.d.ts +27 -15
- package/dist/index.js +292 -256
- package/package.json +3 -3
- package/types/enums/field-types.ts +1 -1
- package/types/enums/statistics.ts +10 -10
- package/types/models/agent.ts +0 -1
- package/types/models/user-role.ts +5 -2
package/dist/index.js
CHANGED
|
@@ -75,19 +75,19 @@ import "bullmq";
|
|
|
75
75
|
import { z } from "zod";
|
|
76
76
|
import * as fs from "fs";
|
|
77
77
|
import * as path from "path";
|
|
78
|
-
import { generateObject, generateText, streamText, tool } from "ai";
|
|
78
|
+
import { convertToModelMessages, createIdGenerator, generateObject, generateText, streamText, tool, validateUIMessages, stepCountIs } from "ai";
|
|
79
79
|
|
|
80
80
|
// types/enums/statistics.ts
|
|
81
81
|
var STATISTICS_TYPE_ENUM = {
|
|
82
|
-
CONTEXT_RETRIEVE: "
|
|
83
|
-
SOURCE_UPDATE: "
|
|
84
|
-
EMBEDDER_UPSERT: "
|
|
85
|
-
EMBEDDER_GENERATE: "
|
|
86
|
-
EMBEDDER_DELETE: "
|
|
87
|
-
WORKFLOW_RUN: "
|
|
88
|
-
CONTEXT_UPSERT: "
|
|
89
|
-
TOOL_CALL: "
|
|
90
|
-
AGENT_RUN: "
|
|
82
|
+
CONTEXT_RETRIEVE: "CONTEXT_RETRIEVE",
|
|
83
|
+
SOURCE_UPDATE: "SOURCE_UPDATE",
|
|
84
|
+
EMBEDDER_UPSERT: "EMBEDDER_UPSERT",
|
|
85
|
+
EMBEDDER_GENERATE: "EMBEDDER_GENERATE",
|
|
86
|
+
EMBEDDER_DELETE: "EMBEDDER_DELETE",
|
|
87
|
+
WORKFLOW_RUN: "WORKFLOW_RUN",
|
|
88
|
+
CONTEXT_UPSERT: "CONTEXT_UPSERT",
|
|
89
|
+
TOOL_CALL: "TOOL_CALL",
|
|
90
|
+
AGENT_RUN: "AGENT_RUN"
|
|
91
91
|
};
|
|
92
92
|
|
|
93
93
|
// types/enums/eval-types.ts
|
|
@@ -262,7 +262,7 @@ var bullmqDecorator = async ({
|
|
|
262
262
|
|
|
263
263
|
// src/registry/utils/map-types.ts
|
|
264
264
|
var mapType = (t, type, name, defaultValue, unique) => {
|
|
265
|
-
if (type === "text") {
|
|
265
|
+
if (type === "text" || type === "enum") {
|
|
266
266
|
t.text(name);
|
|
267
267
|
if (unique) t.unique(name);
|
|
268
268
|
if (defaultValue) t.defaultTo(defaultValue);
|
|
@@ -403,6 +403,7 @@ var ExuluEvalUtils = {
|
|
|
403
403
|
|
|
404
404
|
// src/registry/classes.ts
|
|
405
405
|
import CryptoJS from "crypto-js";
|
|
406
|
+
import "express";
|
|
406
407
|
function sanitizeToolName(name) {
|
|
407
408
|
if (typeof name !== "string") return "";
|
|
408
409
|
let sanitized = name.replace(/[^a-zA-Z0-9_-]+/g, "_");
|
|
@@ -421,7 +422,7 @@ var convertToolsArrayToObject = (tools, configs, providerApiKey) => {
|
|
|
421
422
|
console.log("[EXULU] Sanitized tools", sanitizedTools);
|
|
422
423
|
const askForConfirmation = {
|
|
423
424
|
description: "Ask the user for confirmation.",
|
|
424
|
-
|
|
425
|
+
inputSchema: z.object({
|
|
425
426
|
message: z.string().describe("The message to ask for confirmation.")
|
|
426
427
|
})
|
|
427
428
|
};
|
|
@@ -532,10 +533,12 @@ var ExuluAgent = class {
|
|
|
532
533
|
this.model = this.config?.model;
|
|
533
534
|
}
|
|
534
535
|
get providerName() {
|
|
535
|
-
|
|
536
|
+
const model = this.config?.model?.create({ apiKey: "" });
|
|
537
|
+
return typeof model === "string" ? model : model?.provider || "";
|
|
536
538
|
}
|
|
537
539
|
get modelName() {
|
|
538
|
-
|
|
540
|
+
const model = this.config?.model?.create({ apiKey: "" });
|
|
541
|
+
return typeof model === "string" ? model : model?.modelId || "";
|
|
539
542
|
}
|
|
540
543
|
// Exports the agent as a tool that can be used by another agent
|
|
541
544
|
// todo test this
|
|
@@ -561,28 +564,44 @@ var ExuluAgent = class {
|
|
|
561
564
|
}
|
|
562
565
|
});
|
|
563
566
|
};
|
|
564
|
-
generateSync = async ({
|
|
567
|
+
generateSync = async ({ prompt, user, session, message, tools, statistics, toolConfigs, providerApiKey }) => {
|
|
565
568
|
if (!this.model) {
|
|
566
569
|
throw new Error("Model is required for streaming.");
|
|
567
570
|
}
|
|
568
571
|
if (!this.config) {
|
|
569
572
|
throw new Error("Config is required for generating.");
|
|
570
573
|
}
|
|
571
|
-
if (prompt &&
|
|
572
|
-
throw new Error("
|
|
574
|
+
if (prompt && message) {
|
|
575
|
+
throw new Error("Message and prompt cannot be provided at the same time.");
|
|
573
576
|
}
|
|
574
577
|
const model = this.model.create({
|
|
575
578
|
apiKey: providerApiKey
|
|
576
579
|
});
|
|
580
|
+
let messages = [];
|
|
581
|
+
if (message && session && user) {
|
|
582
|
+
const previousMessages = await getAgentMessages({
|
|
583
|
+
session,
|
|
584
|
+
user,
|
|
585
|
+
limit: 50,
|
|
586
|
+
page: 1
|
|
587
|
+
});
|
|
588
|
+
const previousMessagesContent = previousMessages.map((message2) => JSON.parse(message2.content));
|
|
589
|
+
messages = await validateUIMessages({
|
|
590
|
+
// append the new message to the previous messages:
|
|
591
|
+
messages: [...previousMessagesContent, message]
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
console.log("[EXULU] Model provider key", providerApiKey);
|
|
595
|
+
console.log("[EXULU] Tool configs", toolConfigs);
|
|
577
596
|
const { text } = await generateText({
|
|
578
597
|
model,
|
|
579
598
|
// Should be a LanguageModelV1
|
|
580
599
|
system: "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.",
|
|
581
|
-
messages,
|
|
600
|
+
messages: messages ? convertToModelMessages(messages) : void 0,
|
|
582
601
|
prompt,
|
|
583
602
|
maxRetries: 2,
|
|
584
603
|
tools: convertToolsArrayToObject(tools, toolConfigs, providerApiKey),
|
|
585
|
-
|
|
604
|
+
stopWhen: [stepCountIs(5)]
|
|
586
605
|
});
|
|
587
606
|
if (statistics) {
|
|
588
607
|
await updateStatistic({
|
|
@@ -595,36 +614,61 @@ var ExuluAgent = class {
|
|
|
595
614
|
}
|
|
596
615
|
return text;
|
|
597
616
|
};
|
|
598
|
-
generateStream = ({
|
|
617
|
+
generateStream = async ({ express: express3, user, session, message, tools, statistics, toolConfigs, providerApiKey }) => {
|
|
599
618
|
if (!this.model) {
|
|
600
619
|
throw new Error("Model is required for streaming.");
|
|
601
620
|
}
|
|
602
621
|
if (!this.config) {
|
|
603
622
|
throw new Error("Config is required for generating.");
|
|
604
623
|
}
|
|
605
|
-
if (
|
|
606
|
-
throw new Error("
|
|
624
|
+
if (!message) {
|
|
625
|
+
throw new Error("Message is required for streaming.");
|
|
607
626
|
}
|
|
608
627
|
const model = this.model.create({
|
|
609
628
|
apiKey: providerApiKey
|
|
610
629
|
});
|
|
630
|
+
let messages = [];
|
|
631
|
+
const previousMessages = await getAgentMessages({
|
|
632
|
+
session,
|
|
633
|
+
user,
|
|
634
|
+
limit: 50,
|
|
635
|
+
page: 1
|
|
636
|
+
});
|
|
637
|
+
const previousMessagesContent = previousMessages.map((message2) => JSON.parse(message2.content));
|
|
638
|
+
messages = await validateUIMessages({
|
|
639
|
+
// append the new message to the previous messages:
|
|
640
|
+
messages: [...previousMessagesContent, message]
|
|
641
|
+
});
|
|
611
642
|
console.log("[EXULU] Model provider key", providerApiKey);
|
|
612
643
|
console.log("[EXULU] Tool configs", toolConfigs);
|
|
613
|
-
|
|
644
|
+
const result = streamText({
|
|
614
645
|
model,
|
|
615
646
|
// Should be a LanguageModelV1
|
|
616
|
-
messages,
|
|
617
|
-
prompt,
|
|
647
|
+
messages: messages ? convertToModelMessages(messages) : void 0,
|
|
618
648
|
system: "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.",
|
|
619
649
|
maxRetries: 2,
|
|
620
650
|
tools: convertToolsArrayToObject(tools, toolConfigs, providerApiKey),
|
|
621
|
-
maxSteps: 5,
|
|
622
651
|
onError: (error) => console.error("[EXULU] chat stream error.", error),
|
|
623
|
-
|
|
652
|
+
stopWhen: [stepCountIs(5)]
|
|
653
|
+
});
|
|
654
|
+
result.consumeStream();
|
|
655
|
+
result.pipeUIMessageStreamToResponse(express3.res, {
|
|
656
|
+
originalMessages: messages,
|
|
657
|
+
sendReasoning: true,
|
|
658
|
+
generateMessageId: createIdGenerator({
|
|
659
|
+
prefix: "msg_",
|
|
660
|
+
size: 16
|
|
661
|
+
}),
|
|
662
|
+
onFinish: async ({ messages: messages2 }) => {
|
|
624
663
|
console.info(
|
|
625
664
|
"[EXULU] chat stream finished.",
|
|
626
|
-
|
|
665
|
+
messages2
|
|
627
666
|
);
|
|
667
|
+
await saveChat({
|
|
668
|
+
session,
|
|
669
|
+
user,
|
|
670
|
+
messages: messages2
|
|
671
|
+
});
|
|
628
672
|
if (statistics) {
|
|
629
673
|
await updateStatistic({
|
|
630
674
|
name: "count",
|
|
@@ -636,8 +680,26 @@ var ExuluAgent = class {
|
|
|
636
680
|
}
|
|
637
681
|
}
|
|
638
682
|
});
|
|
683
|
+
return;
|
|
639
684
|
};
|
|
640
685
|
};
|
|
686
|
+
var getAgentMessages = async ({ session, user, limit, page }) => {
|
|
687
|
+
const { db: db3 } = await postgresClient();
|
|
688
|
+
const messages = await db3.from("agent_messages").where({ session, user }).limit(limit).offset(page * limit);
|
|
689
|
+
return messages;
|
|
690
|
+
};
|
|
691
|
+
var saveChat = async ({ session, user, messages }) => {
|
|
692
|
+
const { db: db3 } = await postgresClient();
|
|
693
|
+
const promises2 = messages.map((message) => {
|
|
694
|
+
return db3.from("agent_messages").insert({
|
|
695
|
+
session,
|
|
696
|
+
user,
|
|
697
|
+
content: JSON.stringify(message),
|
|
698
|
+
title: message.role === "user" ? "User" : "Assistant"
|
|
699
|
+
});
|
|
700
|
+
});
|
|
701
|
+
await Promise.all(promises2);
|
|
702
|
+
};
|
|
641
703
|
var ExuluEmbedder = class {
|
|
642
704
|
id;
|
|
643
705
|
name;
|
|
@@ -888,7 +950,7 @@ var ExuluTool = class {
|
|
|
888
950
|
this.type = type;
|
|
889
951
|
this.tool = tool({
|
|
890
952
|
description,
|
|
891
|
-
|
|
953
|
+
inputSchema: inputSchema || z.object({}),
|
|
892
954
|
execute: execute2
|
|
893
955
|
});
|
|
894
956
|
}
|
|
@@ -1377,22 +1439,27 @@ var ExuluSource = class {
|
|
|
1377
1439
|
var updateStatistic = async (statistic) => {
|
|
1378
1440
|
const currentDate = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
1379
1441
|
const { db: db3 } = await postgresClient();
|
|
1380
|
-
const existing = await db3.from("
|
|
1442
|
+
const existing = await db3.from("tracking").where({
|
|
1443
|
+
...statistic.user ? { user: statistic.user } : {},
|
|
1444
|
+
...statistic.role ? { role: statistic.role } : {},
|
|
1381
1445
|
name: statistic.name,
|
|
1382
1446
|
label: statistic.label,
|
|
1383
1447
|
type: statistic.type,
|
|
1384
1448
|
createdAt: currentDate
|
|
1385
1449
|
}).first();
|
|
1450
|
+
console.log("!!! existing !!!", existing);
|
|
1386
1451
|
if (!existing) {
|
|
1387
|
-
await db3.from("
|
|
1452
|
+
await db3.from("tracking").insert({
|
|
1388
1453
|
name: statistic.name,
|
|
1389
1454
|
label: statistic.label,
|
|
1390
1455
|
type: statistic.type,
|
|
1391
1456
|
total: statistic.count ?? 1,
|
|
1392
|
-
createdAt: currentDate
|
|
1457
|
+
createdAt: currentDate,
|
|
1458
|
+
...statistic.user ? { user: statistic.user } : {},
|
|
1459
|
+
...statistic.role ? { role: statistic.role } : {}
|
|
1393
1460
|
});
|
|
1394
1461
|
} else {
|
|
1395
|
-
await db3.from("
|
|
1462
|
+
await db3.from("tracking").update({
|
|
1396
1463
|
total: db3.raw("total + ?", [statistic.count ?? 1])
|
|
1397
1464
|
}).where({
|
|
1398
1465
|
name: statistic.name,
|
|
@@ -1731,25 +1798,25 @@ var requestValidators = {
|
|
|
1731
1798
|
message: "Missing body."
|
|
1732
1799
|
};
|
|
1733
1800
|
}
|
|
1734
|
-
if (!req.
|
|
1801
|
+
if (!req.headers["user"]) {
|
|
1735
1802
|
return {
|
|
1736
1803
|
error: true,
|
|
1737
1804
|
code: 400,
|
|
1738
|
-
message:
|
|
1805
|
+
message: 'Missing "user" property in headers.'
|
|
1739
1806
|
};
|
|
1740
1807
|
}
|
|
1741
|
-
if (!req.
|
|
1808
|
+
if (!req.headers["session"]) {
|
|
1742
1809
|
return {
|
|
1743
1810
|
error: true,
|
|
1744
1811
|
code: 400,
|
|
1745
|
-
message:
|
|
1812
|
+
message: 'Missing "session" property in headers.'
|
|
1746
1813
|
};
|
|
1747
1814
|
}
|
|
1748
|
-
if (!req.body.
|
|
1815
|
+
if (!req.body.message) {
|
|
1749
1816
|
return {
|
|
1750
1817
|
error: true,
|
|
1751
1818
|
code: 400,
|
|
1752
|
-
message: 'Missing "
|
|
1819
|
+
message: 'Missing "message" property in body.'
|
|
1753
1820
|
};
|
|
1754
1821
|
}
|
|
1755
1822
|
return {
|
|
@@ -1853,6 +1920,9 @@ var map = (field) => {
|
|
|
1853
1920
|
case "code":
|
|
1854
1921
|
type = "String";
|
|
1855
1922
|
break;
|
|
1923
|
+
case "enum":
|
|
1924
|
+
type = field.enumValues ? `${field.name}Enum` : "String";
|
|
1925
|
+
break;
|
|
1856
1926
|
case "number":
|
|
1857
1927
|
type = "Float";
|
|
1858
1928
|
break;
|
|
@@ -1871,6 +1941,16 @@ var map = (field) => {
|
|
|
1871
1941
|
return type;
|
|
1872
1942
|
};
|
|
1873
1943
|
function createTypeDefs(table) {
|
|
1944
|
+
const enumDefs = table.fields.filter((field) => field.type === "enum" && field.enumValues).map((field) => {
|
|
1945
|
+
const enumValues = field.enumValues.map((value) => {
|
|
1946
|
+
const sanitized = String(value).replace(/[^a-zA-Z0-9_]/g, "_").replace(/^[0-9]/, "_$&").toUpperCase();
|
|
1947
|
+
return ` ${sanitized}`;
|
|
1948
|
+
}).join("\n");
|
|
1949
|
+
return `
|
|
1950
|
+
enum ${field.name}Enum {
|
|
1951
|
+
${enumValues}
|
|
1952
|
+
}`;
|
|
1953
|
+
}).join("\n");
|
|
1874
1954
|
const fields = table.fields.map((field) => {
|
|
1875
1955
|
let type;
|
|
1876
1956
|
type = map(field);
|
|
@@ -1882,8 +1962,6 @@ function createTypeDefs(table) {
|
|
|
1882
1962
|
type ${table.name.singular} {
|
|
1883
1963
|
${fields.join("\n")}
|
|
1884
1964
|
${table.fields.find((field) => field.name === "id") ? "" : "id: ID!"}
|
|
1885
|
-
createdAt: Date!
|
|
1886
|
-
updatedAt: Date!
|
|
1887
1965
|
${rbacField}
|
|
1888
1966
|
}
|
|
1889
1967
|
`;
|
|
@@ -1894,39 +1972,62 @@ ${table.fields.map((f) => ` ${f.name}: ${map(f)}`).join("\n")}
|
|
|
1894
1972
|
${rbacInputField}
|
|
1895
1973
|
}
|
|
1896
1974
|
`;
|
|
1897
|
-
return typeDef + inputDef;
|
|
1975
|
+
return enumDefs + typeDef + inputDef;
|
|
1898
1976
|
}
|
|
1899
1977
|
function createFilterTypeDefs(table) {
|
|
1900
1978
|
const fieldFilters = table.fields.map((field) => {
|
|
1901
1979
|
let type;
|
|
1902
|
-
type
|
|
1980
|
+
if (field.type === "enum" && field.enumValues) {
|
|
1981
|
+
type = `${field.name}Enum`;
|
|
1982
|
+
} else {
|
|
1983
|
+
type = map(field);
|
|
1984
|
+
}
|
|
1903
1985
|
return `
|
|
1904
1986
|
${field.name}: FilterOperator${type}`;
|
|
1905
1987
|
});
|
|
1906
1988
|
const tableNameSingularUpperCaseFirst = table.name.singular.charAt(0).toUpperCase() + table.name.singular.slice(1);
|
|
1989
|
+
const enumFilterOperators = table.fields.filter((field) => field.type === "enum" && field.enumValues).map((field) => {
|
|
1990
|
+
const enumTypeName = `${field.name}Enum`;
|
|
1991
|
+
return `
|
|
1992
|
+
input FilterOperator${enumTypeName} {
|
|
1993
|
+
eq: ${enumTypeName}
|
|
1994
|
+
ne: ${enumTypeName}
|
|
1995
|
+
in: [${enumTypeName}]
|
|
1996
|
+
and: [FilterOperator${enumTypeName}]
|
|
1997
|
+
or: [FilterOperator${enumTypeName}]
|
|
1998
|
+
}`;
|
|
1999
|
+
}).join("\n");
|
|
1907
2000
|
const operatorTypes = `
|
|
1908
2001
|
input FilterOperatorString {
|
|
1909
2002
|
eq: String
|
|
1910
2003
|
ne: String
|
|
1911
2004
|
in: [String]
|
|
1912
2005
|
contains: String
|
|
2006
|
+
and: [FilterOperatorString]
|
|
2007
|
+
or: [FilterOperatorString]
|
|
1913
2008
|
}
|
|
1914
2009
|
|
|
1915
2010
|
input FilterOperatorDate {
|
|
1916
2011
|
lte: Date
|
|
1917
2012
|
gte: Date
|
|
2013
|
+
and: [FilterOperatorDate]
|
|
2014
|
+
or: [FilterOperatorDate]
|
|
1918
2015
|
}
|
|
1919
2016
|
|
|
1920
2017
|
input FilterOperatorFloat {
|
|
1921
2018
|
eq: Float
|
|
1922
2019
|
ne: Float
|
|
1923
2020
|
in: [Float]
|
|
2021
|
+
and: [FilterOperatorFloat]
|
|
2022
|
+
or: [FilterOperatorFloat]
|
|
1924
2023
|
}
|
|
1925
2024
|
|
|
1926
2025
|
input FilterOperatorBoolean {
|
|
1927
2026
|
eq: Boolean
|
|
1928
2027
|
ne: Boolean
|
|
1929
2028
|
in: [Boolean]
|
|
2029
|
+
and: [FilterOperatorBoolean]
|
|
2030
|
+
or: [FilterOperatorBoolean]
|
|
1930
2031
|
}
|
|
1931
2032
|
|
|
1932
2033
|
input FilterOperatorJSON {
|
|
@@ -1945,6 +2046,8 @@ enum SortDirection {
|
|
|
1945
2046
|
DESC
|
|
1946
2047
|
}
|
|
1947
2048
|
|
|
2049
|
+
${enumFilterOperators}
|
|
2050
|
+
|
|
1948
2051
|
input Filter${tableNameSingularUpperCaseFirst} {
|
|
1949
2052
|
${fieldFilters.join("\n")}
|
|
1950
2053
|
}`;
|
|
@@ -2014,33 +2117,33 @@ var handleRBACUpdate = async (db3, entityName, resourceId, rbacData, existingRba
|
|
|
2014
2117
|
function createMutations(table) {
|
|
2015
2118
|
const tableNamePlural = table.name.plural.toLowerCase();
|
|
2016
2119
|
const validateWriteAccess = async (id, context) => {
|
|
2017
|
-
const { db: db3, req, user } = context;
|
|
2018
|
-
const hasRBAC = table.RBAC === true;
|
|
2019
|
-
if (!hasRBAC) {
|
|
2020
|
-
return true;
|
|
2021
|
-
}
|
|
2022
|
-
if (user.super_admin === true) {
|
|
2023
|
-
return true;
|
|
2024
|
-
}
|
|
2025
|
-
if (!user.role || !(table.name.plural.includes("agent") && user.role.agents === "write") && !(table.name.plural.includes("workflow") && user.role.workflows === "write") && !(table.name.plural.includes("variable") && user.role.variables === "write") && !(table.name.plural.includes("user") && user.role.users === "write")) {
|
|
2026
|
-
console.error("Access control error: no role found for current user or no access to entity type.");
|
|
2027
|
-
throw new Error("Access control error: no role found for current user or no access to entity type.");
|
|
2028
|
-
}
|
|
2029
2120
|
try {
|
|
2030
|
-
const
|
|
2031
|
-
if (
|
|
2032
|
-
|
|
2121
|
+
const { db: db3, req, user } = context;
|
|
2122
|
+
if (user.super_admin === true) {
|
|
2123
|
+
return true;
|
|
2124
|
+
}
|
|
2125
|
+
if (!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")) {
|
|
2126
|
+
console.error("Access control error: no role found for current user or no access to entity type.");
|
|
2127
|
+
throw new Error("Access control error: no role found for current user or no access to entity type.");
|
|
2128
|
+
}
|
|
2129
|
+
const hasRBAC = table.RBAC === true;
|
|
2130
|
+
if (!hasRBAC) {
|
|
2131
|
+
return true;
|
|
2033
2132
|
}
|
|
2034
|
-
const user2 = authResult.user;
|
|
2035
2133
|
const record = await db3.from(tableNamePlural).select(["rights_mode", "created_by"]).where({ id }).first();
|
|
2036
2134
|
if (!record) {
|
|
2037
2135
|
throw new Error("Record not found");
|
|
2038
2136
|
}
|
|
2137
|
+
if (tableNamePlural === "jobs") {
|
|
2138
|
+
if (!user.super_admin && record.created_by !== user.id) {
|
|
2139
|
+
throw new Error("You are not authorized to edit this record");
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2039
2142
|
if (record.rights_mode === "public") {
|
|
2040
2143
|
return true;
|
|
2041
2144
|
}
|
|
2042
2145
|
if (record.rights_mode === "private") {
|
|
2043
|
-
if (record.created_by ===
|
|
2146
|
+
if (record.created_by === user.id) {
|
|
2044
2147
|
return true;
|
|
2045
2148
|
}
|
|
2046
2149
|
throw new Error("Only the creator can edit this private record");
|
|
@@ -2050,7 +2153,7 @@ function createMutations(table) {
|
|
|
2050
2153
|
entity: table.name.singular,
|
|
2051
2154
|
target_resource_id: id,
|
|
2052
2155
|
access_type: "User",
|
|
2053
|
-
user_id:
|
|
2156
|
+
user_id: user.id,
|
|
2054
2157
|
rights: "write"
|
|
2055
2158
|
}).first();
|
|
2056
2159
|
if (rbacRecord) {
|
|
@@ -2058,12 +2161,12 @@ function createMutations(table) {
|
|
|
2058
2161
|
}
|
|
2059
2162
|
throw new Error("Insufficient user permissions to edit this record");
|
|
2060
2163
|
}
|
|
2061
|
-
if (record.rights_mode === "roles" &&
|
|
2164
|
+
if (record.rights_mode === "roles" && user.role) {
|
|
2062
2165
|
const rbacRecord = await db3.from("rbac").where({
|
|
2063
2166
|
entity: table.name.singular,
|
|
2064
2167
|
target_resource_id: id,
|
|
2065
2168
|
access_type: "Role",
|
|
2066
|
-
role_id:
|
|
2169
|
+
role_id: user.role,
|
|
2067
2170
|
rights: "write"
|
|
2068
2171
|
}).first();
|
|
2069
2172
|
if (rbacRecord) {
|
|
@@ -2216,14 +2319,18 @@ function createMutations(table) {
|
|
|
2216
2319
|
var applyAccessControl = (table, user, query) => {
|
|
2217
2320
|
console.log("table", table);
|
|
2218
2321
|
const tableNamePlural = table.name.plural.toLowerCase();
|
|
2322
|
+
if (!user.super_admin && table.name.plural === "jobs") {
|
|
2323
|
+
query = query.where("created_by", user.id);
|
|
2324
|
+
return query;
|
|
2325
|
+
}
|
|
2219
2326
|
const hasRBAC = table.RBAC === true;
|
|
2220
2327
|
if (!hasRBAC) {
|
|
2221
2328
|
return query;
|
|
2222
2329
|
}
|
|
2223
|
-
if (user.super_admin === true) {
|
|
2330
|
+
if (table.name.plural !== "agent_sessions" && user.super_admin === true) {
|
|
2224
2331
|
return query;
|
|
2225
2332
|
}
|
|
2226
|
-
if (!user.role || !(table.name.plural
|
|
2333
|
+
if (!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"))) {
|
|
2227
2334
|
console.error("Access control error: no role found or no access to entity type.");
|
|
2228
2335
|
return query.where("1", "=", "0");
|
|
2229
2336
|
}
|
|
@@ -2251,6 +2358,27 @@ var applyAccessControl = (table, user, query) => {
|
|
|
2251
2358
|
}
|
|
2252
2359
|
return query;
|
|
2253
2360
|
};
|
|
2361
|
+
var converOperatorToQuery = (query, fieldName, operators) => {
|
|
2362
|
+
if (operators.eq !== void 0) {
|
|
2363
|
+
query = query.where(fieldName, operators.eq);
|
|
2364
|
+
}
|
|
2365
|
+
if (operators.ne !== void 0) {
|
|
2366
|
+
query = query.whereRaw(`?? IS DISTINCT FROM ?`, [fieldName, operators.ne]);
|
|
2367
|
+
}
|
|
2368
|
+
if (operators.in !== void 0) {
|
|
2369
|
+
query = query.whereIn(fieldName, operators.in);
|
|
2370
|
+
}
|
|
2371
|
+
if (operators.contains !== void 0) {
|
|
2372
|
+
query = query.where(fieldName, "like", `%${operators.contains}%`);
|
|
2373
|
+
}
|
|
2374
|
+
if (operators.lte !== void 0) {
|
|
2375
|
+
query = query.where(fieldName, "<=", operators.lte);
|
|
2376
|
+
}
|
|
2377
|
+
if (operators.gte !== void 0) {
|
|
2378
|
+
query = query.where(fieldName, ">=", operators.gte);
|
|
2379
|
+
}
|
|
2380
|
+
return query;
|
|
2381
|
+
};
|
|
2254
2382
|
function createQueries(table) {
|
|
2255
2383
|
const tableNamePlural = table.name.plural.toLowerCase();
|
|
2256
2384
|
const tableNameSingular = table.name.singular.toLowerCase();
|
|
@@ -2258,18 +2386,19 @@ function createQueries(table) {
|
|
|
2258
2386
|
filters.forEach((filter) => {
|
|
2259
2387
|
Object.entries(filter).forEach(([fieldName, operators]) => {
|
|
2260
2388
|
if (operators) {
|
|
2261
|
-
if (operators.
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
}
|
|
2267
|
-
if (operators.in !== void 0) {
|
|
2268
|
-
query = query.whereIn(fieldName, operators.in);
|
|
2389
|
+
if (operators.and !== void 0) {
|
|
2390
|
+
console.log("operators.and", operators.and);
|
|
2391
|
+
operators.and.forEach((operator) => {
|
|
2392
|
+
query = converOperatorToQuery(query, fieldName, operator);
|
|
2393
|
+
});
|
|
2269
2394
|
}
|
|
2270
|
-
if (operators.
|
|
2271
|
-
|
|
2395
|
+
if (operators.or !== void 0) {
|
|
2396
|
+
operators.or.forEach((operator) => {
|
|
2397
|
+
query = converOperatorToQuery(query, fieldName, operator);
|
|
2398
|
+
});
|
|
2272
2399
|
}
|
|
2400
|
+
query = converOperatorToQuery(query, fieldName, operators);
|
|
2401
|
+
console.log("query", query);
|
|
2273
2402
|
}
|
|
2274
2403
|
});
|
|
2275
2404
|
});
|
|
@@ -2342,57 +2471,36 @@ function createQueries(table) {
|
|
|
2342
2471
|
query = applyFilters(query, filters);
|
|
2343
2472
|
query = applyAccessControl(table, context.user, query);
|
|
2344
2473
|
if (groupBy) {
|
|
2345
|
-
|
|
2474
|
+
query = query.select(groupBy).groupBy(groupBy);
|
|
2475
|
+
if (tableNamePlural === "tracking") {
|
|
2476
|
+
query = query.sum("total as count");
|
|
2477
|
+
} else {
|
|
2478
|
+
query = query.count("* as count");
|
|
2479
|
+
}
|
|
2480
|
+
const results = await query;
|
|
2481
|
+
console.log("!!! results !!!", results);
|
|
2346
2482
|
return results.map((r) => ({
|
|
2347
2483
|
group: r[groupBy],
|
|
2348
|
-
count: Number(r.count)
|
|
2484
|
+
count: r.count ? Number(r.count) : 0
|
|
2349
2485
|
}));
|
|
2350
2486
|
} else {
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
count
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
query = query.where("user", user);
|
|
2366
|
-
}
|
|
2367
|
-
if (agent) {
|
|
2368
|
-
query = query.where("agent", agent);
|
|
2369
|
-
}
|
|
2370
|
-
if (from) {
|
|
2371
|
-
query = query.where("createdAt", ">=", from);
|
|
2372
|
-
}
|
|
2373
|
-
if (to) {
|
|
2374
|
-
query = query.where("createdAt", "<=", to);
|
|
2487
|
+
if (tableNamePlural === "tracking") {
|
|
2488
|
+
query = query.sum("total as count");
|
|
2489
|
+
const [{ count }] = await query.sum("total as count");
|
|
2490
|
+
console.log("!!! count !!!", count);
|
|
2491
|
+
return [{
|
|
2492
|
+
group: "total",
|
|
2493
|
+
count: count ? Number(count) : 0
|
|
2494
|
+
}];
|
|
2495
|
+
} else {
|
|
2496
|
+
const [{ count }] = await query.count("* as count");
|
|
2497
|
+
return [{
|
|
2498
|
+
group: "total",
|
|
2499
|
+
count: count ? Number(count) : 0
|
|
2500
|
+
}];
|
|
2375
2501
|
}
|
|
2376
|
-
query = applyAccessControl(table, context.user, query);
|
|
2377
|
-
const runningQuery = query.clone().whereIn("status", ["active", "waiting", "delayed", "paused"]);
|
|
2378
|
-
const [{ runningCount }] = await runningQuery.count("* as runningCount");
|
|
2379
|
-
const erroredQuery = query.clone().whereIn("status", ["failed", "stuck"]);
|
|
2380
|
-
const [{ erroredCount }] = await erroredQuery.count("* as erroredCount");
|
|
2381
|
-
const completedQuery = query.clone().where("status", "completed");
|
|
2382
|
-
const [{ completedCount }] = await completedQuery.count("* as completedCount");
|
|
2383
|
-
const failedQuery = query.clone().where("status", "failed");
|
|
2384
|
-
const [{ failedCount }] = await failedQuery.count("* as failedCount");
|
|
2385
|
-
const durationQuery = query.clone().where("status", "completed").whereNotNull("duration").select(db3.raw('AVG("duration") as averageDuration'));
|
|
2386
|
-
const [{ averageDuration }] = await durationQuery;
|
|
2387
|
-
return {
|
|
2388
|
-
runningCount: Number(runningCount),
|
|
2389
|
-
erroredCount: Number(erroredCount),
|
|
2390
|
-
completedCount: Number(completedCount),
|
|
2391
|
-
failedCount: Number(failedCount),
|
|
2392
|
-
averageDuration: averageDuration ? Number(averageDuration) : 0
|
|
2393
|
-
};
|
|
2394
2502
|
}
|
|
2395
|
-
}
|
|
2503
|
+
}
|
|
2396
2504
|
};
|
|
2397
2505
|
}
|
|
2398
2506
|
var RBACResolver = async (db3, table, entityName, resourceId, rights_mode) => {
|
|
@@ -2412,6 +2520,16 @@ var RBACResolver = async (db3, table, entityName, resourceId, rights_mode) => {
|
|
|
2412
2520
|
};
|
|
2413
2521
|
};
|
|
2414
2522
|
function createSDL(tables) {
|
|
2523
|
+
tables.forEach((table) => {
|
|
2524
|
+
table.fields.push({
|
|
2525
|
+
name: "createdAt",
|
|
2526
|
+
type: "date"
|
|
2527
|
+
});
|
|
2528
|
+
table.fields.push({
|
|
2529
|
+
name: "updatedAt",
|
|
2530
|
+
type: "date"
|
|
2531
|
+
});
|
|
2532
|
+
});
|
|
2415
2533
|
console.log("[EXULU] Creating SDL");
|
|
2416
2534
|
let typeDefs = `
|
|
2417
2535
|
scalar JSON
|
|
@@ -2468,7 +2586,6 @@ function createSDL(tables) {
|
|
|
2468
2586
|
${tableNamePlural}Pagination(limit: Int, page: Int, filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingularUpperCaseFirst}PaginationResult
|
|
2469
2587
|
${tableNameSingular}One(filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingular}
|
|
2470
2588
|
${tableNamePlural}Statistics(filters: [Filter${tableNameSingularUpperCaseFirst}], groupBy: String): [StatisticsResult]!
|
|
2471
|
-
${tableNamePlural === "jobs" ? `jobStatistics(user: ID, agent: String, from: String, to: String): JobStatistics` : ""}
|
|
2472
2589
|
`;
|
|
2473
2590
|
mutationDefs += `
|
|
2474
2591
|
${tableNamePlural}CreateOne(input: ${tableNameSingular}Input!): ${tableNameSingular}
|
|
@@ -2493,17 +2610,6 @@ type PageInfo {
|
|
|
2493
2610
|
hasNextPage: Boolean!
|
|
2494
2611
|
}
|
|
2495
2612
|
`;
|
|
2496
|
-
if (tableNamePlural === "jobs") {
|
|
2497
|
-
modelDefs += `
|
|
2498
|
-
type JobStatistics {
|
|
2499
|
-
runningCount: Int!
|
|
2500
|
-
erroredCount: Int!
|
|
2501
|
-
completedCount: Int!
|
|
2502
|
-
failedCount: Int!
|
|
2503
|
-
averageDuration: Float!
|
|
2504
|
-
}
|
|
2505
|
-
`;
|
|
2506
|
-
}
|
|
2507
2613
|
Object.assign(resolvers.Query, createQueries(table));
|
|
2508
2614
|
Object.assign(resolvers.Mutation, createMutations(table));
|
|
2509
2615
|
if (table.RBAC) {
|
|
@@ -2595,6 +2701,10 @@ var agentMessagesSchema = {
|
|
|
2595
2701
|
name: "title",
|
|
2596
2702
|
type: "text"
|
|
2597
2703
|
},
|
|
2704
|
+
{
|
|
2705
|
+
name: "user",
|
|
2706
|
+
type: "number"
|
|
2707
|
+
},
|
|
2598
2708
|
{
|
|
2599
2709
|
name: "session",
|
|
2600
2710
|
type: "text"
|
|
@@ -2831,6 +2941,10 @@ var rolesSchema = {
|
|
|
2831
2941
|
type: "text"
|
|
2832
2942
|
// write | read access to agents
|
|
2833
2943
|
},
|
|
2944
|
+
{
|
|
2945
|
+
name: "api",
|
|
2946
|
+
type: "text"
|
|
2947
|
+
},
|
|
2834
2948
|
{
|
|
2835
2949
|
name: "workflows",
|
|
2836
2950
|
type: "text"
|
|
@@ -2850,8 +2964,8 @@ var rolesSchema = {
|
|
|
2850
2964
|
};
|
|
2851
2965
|
var statisticsSchema = {
|
|
2852
2966
|
name: {
|
|
2853
|
-
plural: "
|
|
2854
|
-
singular: "
|
|
2967
|
+
plural: "tracking",
|
|
2968
|
+
singular: "tracking"
|
|
2855
2969
|
},
|
|
2856
2970
|
fields: [
|
|
2857
2971
|
{
|
|
@@ -2864,11 +2978,20 @@ var statisticsSchema = {
|
|
|
2864
2978
|
},
|
|
2865
2979
|
{
|
|
2866
2980
|
name: "type",
|
|
2867
|
-
type: "
|
|
2981
|
+
type: "enum",
|
|
2982
|
+
enumValues: Object.values(STATISTICS_TYPE_ENUM)
|
|
2868
2983
|
},
|
|
2869
2984
|
{
|
|
2870
2985
|
name: "total",
|
|
2871
2986
|
type: "number"
|
|
2987
|
+
},
|
|
2988
|
+
{
|
|
2989
|
+
name: "user",
|
|
2990
|
+
type: "number"
|
|
2991
|
+
},
|
|
2992
|
+
{
|
|
2993
|
+
name: "role",
|
|
2994
|
+
type: "uuid"
|
|
2872
2995
|
}
|
|
2873
2996
|
]
|
|
2874
2997
|
};
|
|
@@ -2929,6 +3052,7 @@ var jobsSchema = {
|
|
|
2929
3052
|
plural: "jobs",
|
|
2930
3053
|
singular: "job"
|
|
2931
3054
|
},
|
|
3055
|
+
RBAC: true,
|
|
2932
3056
|
fields: [
|
|
2933
3057
|
{
|
|
2934
3058
|
name: "redis",
|
|
@@ -3580,11 +3704,8 @@ var createExpressRoutes = async (app, agents, tools, contexts) => {
|
|
|
3580
3704
|
{ route: "/agents/:id", method: "GET", note: "Get specific agent" },
|
|
3581
3705
|
{ route: "/contexts", method: "GET", note: "List all contexts" },
|
|
3582
3706
|
{ route: "/contexts/:id", method: "GET", note: "Get specific context" },
|
|
3583
|
-
{ route: "/contexts/statistics", method: "GET", note: "Get context statistics" },
|
|
3584
3707
|
{ route: "/tools", method: "GET", note: "List all tools" },
|
|
3585
3708
|
{ route: "/tools/:id", method: "GET", note: "Get specific tool" },
|
|
3586
|
-
{ route: "/statistics/timeseries", method: "POST", note: "Get time series statistics" },
|
|
3587
|
-
{ route: "/statistics/totals", method: "POST", note: "Get totals statistics" },
|
|
3588
3709
|
{ route: "/items/:context", method: "POST", note: "Create new item in context" },
|
|
3589
3710
|
{ route: "/items/:context", method: "GET", note: "Get items from context" },
|
|
3590
3711
|
{ route: "/items/export/:context", method: "GET", note: "Export items from context" },
|
|
@@ -4117,115 +4238,6 @@ var createExpressRoutes = async (app, agents, tools, contexts) => {
|
|
|
4117
4238
|
authenticated: true
|
|
4118
4239
|
});
|
|
4119
4240
|
});
|
|
4120
|
-
console.log("[EXULU] statistics timeseries");
|
|
4121
|
-
app.post("/statistics/timeseries", async (req, res) => {
|
|
4122
|
-
const authenticationResult = await requestValidators.authenticate(req);
|
|
4123
|
-
if (!authenticationResult.user?.id) {
|
|
4124
|
-
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
4125
|
-
return;
|
|
4126
|
-
}
|
|
4127
|
-
const { db: db3 } = await postgresClient();
|
|
4128
|
-
const type = req.body.type;
|
|
4129
|
-
if (!Object.values(STATISTICS_TYPE_ENUM).includes(type)) {
|
|
4130
|
-
res.status(400).json({
|
|
4131
|
-
message: "Invalid type, must be one of: " + Object.values(STATISTICS_TYPE_ENUM).join(", ")
|
|
4132
|
-
});
|
|
4133
|
-
return;
|
|
4134
|
-
}
|
|
4135
|
-
let from = new Date(req.body.from);
|
|
4136
|
-
let to = new Date(req.body.to);
|
|
4137
|
-
if (!from || !to) {
|
|
4138
|
-
from = new Date(Date.now() - 7 * 24 * 60 * 60 * 1e3);
|
|
4139
|
-
to = /* @__PURE__ */ new Date();
|
|
4140
|
-
}
|
|
4141
|
-
const query = db3.from("statistics").select("*");
|
|
4142
|
-
query.where("name", "count");
|
|
4143
|
-
query.andWhere("type", type);
|
|
4144
|
-
query.andWhere("createdAt", ">=", from);
|
|
4145
|
-
query.andWhere("createdAt", "<=", to);
|
|
4146
|
-
const results = await query;
|
|
4147
|
-
const dates = [];
|
|
4148
|
-
for (let i = 0; i < (to.getTime() - from.getTime()) / (1e3 * 60 * 60 * 24); i++) {
|
|
4149
|
-
dates.push(new Date(from.getTime() + i * (1e3 * 60 * 60 * 24)));
|
|
4150
|
-
}
|
|
4151
|
-
const data = dates.map((date) => {
|
|
4152
|
-
const result = results.find((result2) => result2.date === date);
|
|
4153
|
-
if (result) {
|
|
4154
|
-
return result;
|
|
4155
|
-
}
|
|
4156
|
-
return {
|
|
4157
|
-
date,
|
|
4158
|
-
count: 0
|
|
4159
|
-
};
|
|
4160
|
-
});
|
|
4161
|
-
res.status(200).json({
|
|
4162
|
-
data,
|
|
4163
|
-
filter: {
|
|
4164
|
-
from,
|
|
4165
|
-
to
|
|
4166
|
-
}
|
|
4167
|
-
});
|
|
4168
|
-
});
|
|
4169
|
-
console.log("[EXULU] statistics totals");
|
|
4170
|
-
app.post("/statistics/totals", async (req, res) => {
|
|
4171
|
-
const authenticationResult = await requestValidators.authenticate(req);
|
|
4172
|
-
if (!authenticationResult.user?.id) {
|
|
4173
|
-
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
4174
|
-
return;
|
|
4175
|
-
}
|
|
4176
|
-
const { db: db3 } = await postgresClient();
|
|
4177
|
-
let from = new Date(req.body.from);
|
|
4178
|
-
let to = new Date(req.body.to);
|
|
4179
|
-
if (!from || !to) {
|
|
4180
|
-
from = new Date(Date.now() - 7 * 24 * 60 * 60 * 1e3);
|
|
4181
|
-
to = /* @__PURE__ */ new Date();
|
|
4182
|
-
}
|
|
4183
|
-
let promises2 = Object.values(STATISTICS_TYPE_ENUM).map(async (type) => {
|
|
4184
|
-
const result = await db3.from("statistics").where("name", "count").andWhere("type", type).andWhere("createdAt", ">=", from).andWhere("createdAt", "<=", to).sum("total as total");
|
|
4185
|
-
return {
|
|
4186
|
-
[type]: result[0]?.total || 0
|
|
4187
|
-
};
|
|
4188
|
-
});
|
|
4189
|
-
const results = await Promise.all(promises2);
|
|
4190
|
-
res.status(200).json({
|
|
4191
|
-
data: { ...Object.assign({}, ...results) },
|
|
4192
|
-
filter: {
|
|
4193
|
-
from,
|
|
4194
|
-
to
|
|
4195
|
-
}
|
|
4196
|
-
});
|
|
4197
|
-
});
|
|
4198
|
-
console.log("[EXULU] contexts statistics");
|
|
4199
|
-
app.get("/contexts/statistics", async (req, res) => {
|
|
4200
|
-
const authenticationResult = await requestValidators.authenticate(req);
|
|
4201
|
-
if (!authenticationResult.user?.id) {
|
|
4202
|
-
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
4203
|
-
return;
|
|
4204
|
-
}
|
|
4205
|
-
const { db: db3 } = await postgresClient();
|
|
4206
|
-
const statistics = await db3("statistics").where("name", "count").andWhere("type", "context.retrieve").sum("total as total").first();
|
|
4207
|
-
const response = await db3("jobs").select(db3.raw(`to_char("createdAt", 'YYYY-MM-DD') as date`)).count("* as count").where("type", "embedder").groupByRaw(`to_char("createdAt", 'YYYY-MM-DD')`).then((rows) => ({
|
|
4208
|
-
jobs: rows
|
|
4209
|
-
}));
|
|
4210
|
-
let jobs = [];
|
|
4211
|
-
if (response[0]) {
|
|
4212
|
-
jobs = response[0].jobs.map((job) => ({
|
|
4213
|
-
date: job.id,
|
|
4214
|
-
count: job.count
|
|
4215
|
-
}));
|
|
4216
|
-
}
|
|
4217
|
-
const embeddingsCountResult = await db3("jobs").where("type", "embedder").count("* as count").first();
|
|
4218
|
-
res.status(200).json({
|
|
4219
|
-
active: contexts.filter((context) => context.active).length,
|
|
4220
|
-
inactive: contexts.filter((context) => !context.active).length,
|
|
4221
|
-
sources: contexts.reduce((acc, context) => acc + context.sources.get().length, 0),
|
|
4222
|
-
queries: statistics?.total || 0,
|
|
4223
|
-
jobs,
|
|
4224
|
-
totals: {
|
|
4225
|
-
embeddings: embeddingsCountResult?.count || 0
|
|
4226
|
-
}
|
|
4227
|
-
});
|
|
4228
|
-
});
|
|
4229
4241
|
console.log("[EXULU] context by id");
|
|
4230
4242
|
app.get(`/contexts/:id`, async (req, res) => {
|
|
4231
4243
|
const authenticationResult = await requestValidators.authenticate(req);
|
|
@@ -4396,7 +4408,11 @@ var createExpressRoutes = async (app, agents, tools, contexts) => {
|
|
|
4396
4408
|
return;
|
|
4397
4409
|
}
|
|
4398
4410
|
}
|
|
4399
|
-
const
|
|
4411
|
+
const headers = {
|
|
4412
|
+
stream: req.headers["stream"] === "true" || false,
|
|
4413
|
+
user: req.headers["user"] || null,
|
|
4414
|
+
session: req.headers["session"] || null
|
|
4415
|
+
};
|
|
4400
4416
|
const requestValidationResult = requestValidators.agents(req);
|
|
4401
4417
|
if (requestValidationResult.error) {
|
|
4402
4418
|
res.status(requestValidationResult.code || 500).json({ detail: `${requestValidationResult.message}` });
|
|
@@ -4407,6 +4423,13 @@ var createExpressRoutes = async (app, agents, tools, contexts) => {
|
|
|
4407
4423
|
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
4408
4424
|
return;
|
|
4409
4425
|
}
|
|
4426
|
+
const user = authenticationResult.user;
|
|
4427
|
+
if (user.type !== "api" && !user.super_admin && req.body.resourceId !== user.id) {
|
|
4428
|
+
res.status(400).json({
|
|
4429
|
+
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."
|
|
4430
|
+
});
|
|
4431
|
+
return;
|
|
4432
|
+
}
|
|
4410
4433
|
console.log("[EXULU] agent tools", agentInstance.tools);
|
|
4411
4434
|
const enabledTools = agentInstance.tools.map(({ config, toolId }) => tools.find(({ id }) => id === toolId)).filter(Boolean);
|
|
4412
4435
|
console.log("[EXULU] enabled tools", enabledTools);
|
|
@@ -4429,9 +4452,15 @@ var createExpressRoutes = async (app, agents, tools, contexts) => {
|
|
|
4429
4452
|
const bytes = CryptoJS3.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
|
|
4430
4453
|
providerApiKey = bytes.toString(CryptoJS3.enc.Utf8);
|
|
4431
4454
|
}
|
|
4432
|
-
if (!!stream) {
|
|
4433
|
-
|
|
4434
|
-
|
|
4455
|
+
if (!!headers.stream) {
|
|
4456
|
+
await agent.generateStream({
|
|
4457
|
+
express: {
|
|
4458
|
+
res,
|
|
4459
|
+
req
|
|
4460
|
+
},
|
|
4461
|
+
user: headers.user,
|
|
4462
|
+
session: headers.session,
|
|
4463
|
+
message: req.body.message,
|
|
4435
4464
|
tools: enabledTools,
|
|
4436
4465
|
providerApiKey,
|
|
4437
4466
|
toolConfigs: agentInstance.tools,
|
|
@@ -4440,11 +4469,12 @@ var createExpressRoutes = async (app, agents, tools, contexts) => {
|
|
|
4440
4469
|
trigger: "agent"
|
|
4441
4470
|
}
|
|
4442
4471
|
});
|
|
4443
|
-
result.pipeDataStreamToResponse(res);
|
|
4444
4472
|
return;
|
|
4445
4473
|
} else {
|
|
4446
4474
|
const response = await agent.generateSync({
|
|
4447
|
-
|
|
4475
|
+
user: headers.user,
|
|
4476
|
+
session: headers.session,
|
|
4477
|
+
message: req.body.message,
|
|
4448
4478
|
tools: enabledTools.map((tool2) => tool2.tool),
|
|
4449
4479
|
providerApiKey,
|
|
4450
4480
|
toolConfigs: agentInstance.tools,
|
|
@@ -4969,7 +4999,6 @@ var defaultAgent = new ExuluAgent({
|
|
|
4969
4999
|
description: `Basic agent without any defined tools, that can support MCP's.`,
|
|
4970
5000
|
type: "agent",
|
|
4971
5001
|
capabilities: {
|
|
4972
|
-
tools: false,
|
|
4973
5002
|
images: [],
|
|
4974
5003
|
files: [],
|
|
4975
5004
|
audio: [],
|
|
@@ -4981,10 +5010,10 @@ var defaultAgent = new ExuluAgent({
|
|
|
4981
5010
|
instructions: "You are a helpful assistant.",
|
|
4982
5011
|
model: {
|
|
4983
5012
|
create: ({ apiKey }) => {
|
|
4984
|
-
const
|
|
5013
|
+
const anthropic = createAnthropic({
|
|
4985
5014
|
apiKey
|
|
4986
5015
|
});
|
|
4987
|
-
return
|
|
5016
|
+
return anthropic.languageModel("claude-4-opus-20250514");
|
|
4988
5017
|
}
|
|
4989
5018
|
// todo add a field of type string that adds a dropdown list from which the user can select the model
|
|
4990
5019
|
// todo for each model, check which provider is used, and require the admin to add one or multiple
|
|
@@ -6525,6 +6554,13 @@ var ExuluJobs = {
|
|
|
6525
6554
|
var db2 = {
|
|
6526
6555
|
init: async () => {
|
|
6527
6556
|
await execute();
|
|
6557
|
+
},
|
|
6558
|
+
api: {
|
|
6559
|
+
key: {
|
|
6560
|
+
generate: async (name, email) => {
|
|
6561
|
+
return await generateApiKey(name, email);
|
|
6562
|
+
}
|
|
6563
|
+
}
|
|
6528
6564
|
}
|
|
6529
6565
|
};
|
|
6530
6566
|
var ExuluChunkers = {
|