@exulu/backend 1.13.0 → 1.14.1
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 +331 -280
- package/dist/index.d.cts +27 -15
- package/dist/index.d.ts +27 -15
- package/dist/index.js +322 -271
- 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/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,58 +262,81 @@ 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") {
|
|
266
|
-
|
|
265
|
+
if (type === "text" || type === "enum") {
|
|
266
|
+
if (defaultValue) {
|
|
267
|
+
t.text(name).defaultTo(defaultValue);
|
|
268
|
+
} else {
|
|
269
|
+
t.text(name);
|
|
270
|
+
}
|
|
267
271
|
if (unique) t.unique(name);
|
|
268
|
-
if (defaultValue) t.defaultTo(defaultValue);
|
|
269
272
|
return;
|
|
270
273
|
}
|
|
271
274
|
if (type === "longText") {
|
|
272
|
-
|
|
275
|
+
if (defaultValue) {
|
|
276
|
+
t.text(name).defaultTo(defaultValue);
|
|
277
|
+
} else {
|
|
278
|
+
t.text(name);
|
|
279
|
+
}
|
|
273
280
|
if (unique) t.unique(name);
|
|
274
|
-
if (defaultValue) t.defaultTo(defaultValue);
|
|
275
281
|
return;
|
|
276
282
|
}
|
|
277
283
|
if (type === "shortText") {
|
|
278
|
-
|
|
284
|
+
if (defaultValue) {
|
|
285
|
+
t.string(name, 100).defaultTo(defaultValue);
|
|
286
|
+
} else {
|
|
287
|
+
t.string(name, 100);
|
|
288
|
+
}
|
|
279
289
|
if (unique) t.unique(name);
|
|
280
|
-
if (defaultValue) t.defaultTo(defaultValue);
|
|
281
290
|
return;
|
|
282
291
|
}
|
|
283
292
|
if (type === "number") {
|
|
284
|
-
|
|
293
|
+
if (defaultValue) {
|
|
294
|
+
t.float(name).defaultTo(defaultValue);
|
|
295
|
+
} else {
|
|
296
|
+
t.float(name);
|
|
297
|
+
}
|
|
285
298
|
if (unique) t.unique(name);
|
|
286
|
-
if (defaultValue) t.defaultTo(defaultValue);
|
|
287
299
|
return;
|
|
288
300
|
}
|
|
289
301
|
if (type === "boolean") {
|
|
290
302
|
t.boolean(name).defaultTo(defaultValue || false);
|
|
291
303
|
if (unique) t.unique(name);
|
|
292
|
-
if (defaultValue) t.defaultTo(defaultValue);
|
|
293
304
|
return;
|
|
294
305
|
}
|
|
295
306
|
if (type === "code") {
|
|
296
|
-
|
|
307
|
+
if (defaultValue) {
|
|
308
|
+
t.text(name).defaultTo(defaultValue);
|
|
309
|
+
} else {
|
|
310
|
+
t.text(name);
|
|
311
|
+
}
|
|
297
312
|
if (unique) t.unique(name);
|
|
298
|
-
if (defaultValue) t.defaultTo(defaultValue);
|
|
299
313
|
return;
|
|
300
314
|
}
|
|
301
315
|
if (type === "json") {
|
|
302
|
-
|
|
316
|
+
if (defaultValue) {
|
|
317
|
+
t.jsonb(name).defaultTo(defaultValue);
|
|
318
|
+
} else {
|
|
319
|
+
t.jsonb(name);
|
|
320
|
+
}
|
|
303
321
|
if (unique) t.unique(name);
|
|
304
|
-
if (defaultValue) t.defaultTo(defaultValue);
|
|
305
322
|
return;
|
|
306
323
|
}
|
|
307
324
|
if (type === "date") {
|
|
308
|
-
|
|
325
|
+
if (defaultValue) {
|
|
326
|
+
t.timestamp(name).defaultTo(defaultValue);
|
|
327
|
+
} else {
|
|
328
|
+
t.timestamp(name);
|
|
329
|
+
}
|
|
309
330
|
if (unique) t.unique(name);
|
|
310
|
-
if (defaultValue) t.defaultTo(defaultValue);
|
|
311
331
|
return;
|
|
312
332
|
}
|
|
313
333
|
if (type === "uuid") {
|
|
314
|
-
|
|
334
|
+
if (defaultValue) {
|
|
335
|
+
t.uuid(name).defaultTo(defaultValue);
|
|
336
|
+
} else {
|
|
337
|
+
t.uuid(name);
|
|
338
|
+
}
|
|
315
339
|
if (unique) t.unique(name);
|
|
316
|
-
if (defaultValue) t.defaultTo(defaultValue);
|
|
317
340
|
return;
|
|
318
341
|
}
|
|
319
342
|
throw new Error("Invalid type: " + type);
|
|
@@ -403,6 +426,7 @@ var ExuluEvalUtils = {
|
|
|
403
426
|
|
|
404
427
|
// src/registry/classes.ts
|
|
405
428
|
import CryptoJS from "crypto-js";
|
|
429
|
+
import "express";
|
|
406
430
|
function sanitizeToolName(name) {
|
|
407
431
|
if (typeof name !== "string") return "";
|
|
408
432
|
let sanitized = name.replace(/[^a-zA-Z0-9_-]+/g, "_");
|
|
@@ -421,7 +445,7 @@ var convertToolsArrayToObject = (tools, configs, providerApiKey) => {
|
|
|
421
445
|
console.log("[EXULU] Sanitized tools", sanitizedTools);
|
|
422
446
|
const askForConfirmation = {
|
|
423
447
|
description: "Ask the user for confirmation.",
|
|
424
|
-
|
|
448
|
+
inputSchema: z.object({
|
|
425
449
|
message: z.string().describe("The message to ask for confirmation.")
|
|
426
450
|
})
|
|
427
451
|
};
|
|
@@ -532,10 +556,12 @@ var ExuluAgent = class {
|
|
|
532
556
|
this.model = this.config?.model;
|
|
533
557
|
}
|
|
534
558
|
get providerName() {
|
|
535
|
-
|
|
559
|
+
const model = this.config?.model?.create({ apiKey: "" });
|
|
560
|
+
return typeof model === "string" ? model : model?.provider || "";
|
|
536
561
|
}
|
|
537
562
|
get modelName() {
|
|
538
|
-
|
|
563
|
+
const model = this.config?.model?.create({ apiKey: "" });
|
|
564
|
+
return typeof model === "string" ? model : model?.modelId || "";
|
|
539
565
|
}
|
|
540
566
|
// Exports the agent as a tool that can be used by another agent
|
|
541
567
|
// todo test this
|
|
@@ -561,28 +587,44 @@ var ExuluAgent = class {
|
|
|
561
587
|
}
|
|
562
588
|
});
|
|
563
589
|
};
|
|
564
|
-
generateSync = async ({
|
|
590
|
+
generateSync = async ({ prompt, user, session, message, tools, statistics, toolConfigs, providerApiKey }) => {
|
|
565
591
|
if (!this.model) {
|
|
566
592
|
throw new Error("Model is required for streaming.");
|
|
567
593
|
}
|
|
568
594
|
if (!this.config) {
|
|
569
595
|
throw new Error("Config is required for generating.");
|
|
570
596
|
}
|
|
571
|
-
if (prompt &&
|
|
572
|
-
throw new Error("
|
|
597
|
+
if (prompt && message) {
|
|
598
|
+
throw new Error("Message and prompt cannot be provided at the same time.");
|
|
573
599
|
}
|
|
574
600
|
const model = this.model.create({
|
|
575
601
|
apiKey: providerApiKey
|
|
576
602
|
});
|
|
603
|
+
let messages = [];
|
|
604
|
+
if (message && session && user) {
|
|
605
|
+
const previousMessages = await getAgentMessages({
|
|
606
|
+
session,
|
|
607
|
+
user,
|
|
608
|
+
limit: 50,
|
|
609
|
+
page: 1
|
|
610
|
+
});
|
|
611
|
+
const previousMessagesContent = previousMessages.map((message2) => JSON.parse(message2.content));
|
|
612
|
+
messages = await validateUIMessages({
|
|
613
|
+
// append the new message to the previous messages:
|
|
614
|
+
messages: [...previousMessagesContent, message]
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
console.log("[EXULU] Model provider key", providerApiKey);
|
|
618
|
+
console.log("[EXULU] Tool configs", toolConfigs);
|
|
577
619
|
const { text } = await generateText({
|
|
578
620
|
model,
|
|
579
621
|
// Should be a LanguageModelV1
|
|
580
622
|
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,
|
|
623
|
+
messages: messages ? convertToModelMessages(messages) : void 0,
|
|
582
624
|
prompt,
|
|
583
625
|
maxRetries: 2,
|
|
584
626
|
tools: convertToolsArrayToObject(tools, toolConfigs, providerApiKey),
|
|
585
|
-
|
|
627
|
+
stopWhen: [stepCountIs(5)]
|
|
586
628
|
});
|
|
587
629
|
if (statistics) {
|
|
588
630
|
await updateStatistic({
|
|
@@ -595,36 +637,61 @@ var ExuluAgent = class {
|
|
|
595
637
|
}
|
|
596
638
|
return text;
|
|
597
639
|
};
|
|
598
|
-
generateStream = ({
|
|
640
|
+
generateStream = async ({ express: express3, user, session, message, tools, statistics, toolConfigs, providerApiKey }) => {
|
|
599
641
|
if (!this.model) {
|
|
600
642
|
throw new Error("Model is required for streaming.");
|
|
601
643
|
}
|
|
602
644
|
if (!this.config) {
|
|
603
645
|
throw new Error("Config is required for generating.");
|
|
604
646
|
}
|
|
605
|
-
if (
|
|
606
|
-
throw new Error("
|
|
647
|
+
if (!message) {
|
|
648
|
+
throw new Error("Message is required for streaming.");
|
|
607
649
|
}
|
|
608
650
|
const model = this.model.create({
|
|
609
651
|
apiKey: providerApiKey
|
|
610
652
|
});
|
|
653
|
+
let messages = [];
|
|
654
|
+
const previousMessages = await getAgentMessages({
|
|
655
|
+
session,
|
|
656
|
+
user,
|
|
657
|
+
limit: 50,
|
|
658
|
+
page: 1
|
|
659
|
+
});
|
|
660
|
+
const previousMessagesContent = previousMessages.map((message2) => JSON.parse(message2.content));
|
|
661
|
+
messages = await validateUIMessages({
|
|
662
|
+
// append the new message to the previous messages:
|
|
663
|
+
messages: [...previousMessagesContent, message]
|
|
664
|
+
});
|
|
611
665
|
console.log("[EXULU] Model provider key", providerApiKey);
|
|
612
666
|
console.log("[EXULU] Tool configs", toolConfigs);
|
|
613
|
-
|
|
667
|
+
const result = streamText({
|
|
614
668
|
model,
|
|
615
669
|
// Should be a LanguageModelV1
|
|
616
|
-
messages,
|
|
617
|
-
prompt,
|
|
670
|
+
messages: messages ? convertToModelMessages(messages) : void 0,
|
|
618
671
|
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
672
|
maxRetries: 2,
|
|
620
673
|
tools: convertToolsArrayToObject(tools, toolConfigs, providerApiKey),
|
|
621
|
-
maxSteps: 5,
|
|
622
674
|
onError: (error) => console.error("[EXULU] chat stream error.", error),
|
|
623
|
-
|
|
675
|
+
stopWhen: [stepCountIs(5)]
|
|
676
|
+
});
|
|
677
|
+
result.consumeStream();
|
|
678
|
+
result.pipeUIMessageStreamToResponse(express3.res, {
|
|
679
|
+
originalMessages: messages,
|
|
680
|
+
sendReasoning: true,
|
|
681
|
+
generateMessageId: createIdGenerator({
|
|
682
|
+
prefix: "msg_",
|
|
683
|
+
size: 16
|
|
684
|
+
}),
|
|
685
|
+
onFinish: async ({ messages: messages2 }) => {
|
|
624
686
|
console.info(
|
|
625
687
|
"[EXULU] chat stream finished.",
|
|
626
|
-
|
|
688
|
+
messages2
|
|
627
689
|
);
|
|
690
|
+
await saveChat({
|
|
691
|
+
session,
|
|
692
|
+
user,
|
|
693
|
+
messages: messages2
|
|
694
|
+
});
|
|
628
695
|
if (statistics) {
|
|
629
696
|
await updateStatistic({
|
|
630
697
|
name: "count",
|
|
@@ -636,8 +703,26 @@ var ExuluAgent = class {
|
|
|
636
703
|
}
|
|
637
704
|
}
|
|
638
705
|
});
|
|
706
|
+
return;
|
|
639
707
|
};
|
|
640
708
|
};
|
|
709
|
+
var getAgentMessages = async ({ session, user, limit, page }) => {
|
|
710
|
+
const { db: db3 } = await postgresClient();
|
|
711
|
+
const messages = await db3.from("agent_messages").where({ session, user }).limit(limit).offset(page * limit);
|
|
712
|
+
return messages;
|
|
713
|
+
};
|
|
714
|
+
var saveChat = async ({ session, user, messages }) => {
|
|
715
|
+
const { db: db3 } = await postgresClient();
|
|
716
|
+
const promises2 = messages.map((message) => {
|
|
717
|
+
return db3.from("agent_messages").insert({
|
|
718
|
+
session,
|
|
719
|
+
user,
|
|
720
|
+
content: JSON.stringify(message),
|
|
721
|
+
title: message.role === "user" ? "User" : "Assistant"
|
|
722
|
+
});
|
|
723
|
+
});
|
|
724
|
+
await Promise.all(promises2);
|
|
725
|
+
};
|
|
641
726
|
var ExuluEmbedder = class {
|
|
642
727
|
id;
|
|
643
728
|
name;
|
|
@@ -888,7 +973,7 @@ var ExuluTool = class {
|
|
|
888
973
|
this.type = type;
|
|
889
974
|
this.tool = tool({
|
|
890
975
|
description,
|
|
891
|
-
|
|
976
|
+
inputSchema: inputSchema || z.object({}),
|
|
892
977
|
execute: execute2
|
|
893
978
|
});
|
|
894
979
|
}
|
|
@@ -1377,22 +1462,27 @@ var ExuluSource = class {
|
|
|
1377
1462
|
var updateStatistic = async (statistic) => {
|
|
1378
1463
|
const currentDate = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
1379
1464
|
const { db: db3 } = await postgresClient();
|
|
1380
|
-
const existing = await db3.from("
|
|
1465
|
+
const existing = await db3.from("tracking").where({
|
|
1466
|
+
...statistic.user ? { user: statistic.user } : {},
|
|
1467
|
+
...statistic.role ? { role: statistic.role } : {},
|
|
1381
1468
|
name: statistic.name,
|
|
1382
1469
|
label: statistic.label,
|
|
1383
1470
|
type: statistic.type,
|
|
1384
1471
|
createdAt: currentDate
|
|
1385
1472
|
}).first();
|
|
1473
|
+
console.log("!!! existing !!!", existing);
|
|
1386
1474
|
if (!existing) {
|
|
1387
|
-
await db3.from("
|
|
1475
|
+
await db3.from("tracking").insert({
|
|
1388
1476
|
name: statistic.name,
|
|
1389
1477
|
label: statistic.label,
|
|
1390
1478
|
type: statistic.type,
|
|
1391
1479
|
total: statistic.count ?? 1,
|
|
1392
|
-
createdAt: currentDate
|
|
1480
|
+
createdAt: currentDate,
|
|
1481
|
+
...statistic.user ? { user: statistic.user } : {},
|
|
1482
|
+
...statistic.role ? { role: statistic.role } : {}
|
|
1393
1483
|
});
|
|
1394
1484
|
} else {
|
|
1395
|
-
await db3.from("
|
|
1485
|
+
await db3.from("tracking").update({
|
|
1396
1486
|
total: db3.raw("total + ?", [statistic.count ?? 1])
|
|
1397
1487
|
}).where({
|
|
1398
1488
|
name: statistic.name,
|
|
@@ -1731,25 +1821,25 @@ var requestValidators = {
|
|
|
1731
1821
|
message: "Missing body."
|
|
1732
1822
|
};
|
|
1733
1823
|
}
|
|
1734
|
-
if (!req.
|
|
1824
|
+
if (!req.headers["user"]) {
|
|
1735
1825
|
return {
|
|
1736
1826
|
error: true,
|
|
1737
1827
|
code: 400,
|
|
1738
|
-
message:
|
|
1828
|
+
message: 'Missing "user" property in headers.'
|
|
1739
1829
|
};
|
|
1740
1830
|
}
|
|
1741
|
-
if (!req.
|
|
1831
|
+
if (!req.headers["session"]) {
|
|
1742
1832
|
return {
|
|
1743
1833
|
error: true,
|
|
1744
1834
|
code: 400,
|
|
1745
|
-
message:
|
|
1835
|
+
message: 'Missing "session" property in headers.'
|
|
1746
1836
|
};
|
|
1747
1837
|
}
|
|
1748
|
-
if (!req.body.
|
|
1838
|
+
if (!req.body.message) {
|
|
1749
1839
|
return {
|
|
1750
1840
|
error: true,
|
|
1751
1841
|
code: 400,
|
|
1752
|
-
message: 'Missing "
|
|
1842
|
+
message: 'Missing "message" property in body.'
|
|
1753
1843
|
};
|
|
1754
1844
|
}
|
|
1755
1845
|
return {
|
|
@@ -1853,6 +1943,9 @@ var map = (field) => {
|
|
|
1853
1943
|
case "code":
|
|
1854
1944
|
type = "String";
|
|
1855
1945
|
break;
|
|
1946
|
+
case "enum":
|
|
1947
|
+
type = field.enumValues ? `${field.name}Enum` : "String";
|
|
1948
|
+
break;
|
|
1856
1949
|
case "number":
|
|
1857
1950
|
type = "Float";
|
|
1858
1951
|
break;
|
|
@@ -1871,6 +1964,16 @@ var map = (field) => {
|
|
|
1871
1964
|
return type;
|
|
1872
1965
|
};
|
|
1873
1966
|
function createTypeDefs(table) {
|
|
1967
|
+
const enumDefs = table.fields.filter((field) => field.type === "enum" && field.enumValues).map((field) => {
|
|
1968
|
+
const enumValues = field.enumValues.map((value) => {
|
|
1969
|
+
const sanitized = String(value).replace(/[^a-zA-Z0-9_]/g, "_").replace(/^[0-9]/, "_$&").toUpperCase();
|
|
1970
|
+
return ` ${sanitized}`;
|
|
1971
|
+
}).join("\n");
|
|
1972
|
+
return `
|
|
1973
|
+
enum ${field.name}Enum {
|
|
1974
|
+
${enumValues}
|
|
1975
|
+
}`;
|
|
1976
|
+
}).join("\n");
|
|
1874
1977
|
const fields = table.fields.map((field) => {
|
|
1875
1978
|
let type;
|
|
1876
1979
|
type = map(field);
|
|
@@ -1882,8 +1985,6 @@ function createTypeDefs(table) {
|
|
|
1882
1985
|
type ${table.name.singular} {
|
|
1883
1986
|
${fields.join("\n")}
|
|
1884
1987
|
${table.fields.find((field) => field.name === "id") ? "" : "id: ID!"}
|
|
1885
|
-
createdAt: Date!
|
|
1886
|
-
updatedAt: Date!
|
|
1887
1988
|
${rbacField}
|
|
1888
1989
|
}
|
|
1889
1990
|
`;
|
|
@@ -1894,39 +1995,62 @@ ${table.fields.map((f) => ` ${f.name}: ${map(f)}`).join("\n")}
|
|
|
1894
1995
|
${rbacInputField}
|
|
1895
1996
|
}
|
|
1896
1997
|
`;
|
|
1897
|
-
return typeDef + inputDef;
|
|
1998
|
+
return enumDefs + typeDef + inputDef;
|
|
1898
1999
|
}
|
|
1899
2000
|
function createFilterTypeDefs(table) {
|
|
1900
2001
|
const fieldFilters = table.fields.map((field) => {
|
|
1901
2002
|
let type;
|
|
1902
|
-
type
|
|
2003
|
+
if (field.type === "enum" && field.enumValues) {
|
|
2004
|
+
type = `${field.name}Enum`;
|
|
2005
|
+
} else {
|
|
2006
|
+
type = map(field);
|
|
2007
|
+
}
|
|
1903
2008
|
return `
|
|
1904
2009
|
${field.name}: FilterOperator${type}`;
|
|
1905
2010
|
});
|
|
1906
2011
|
const tableNameSingularUpperCaseFirst = table.name.singular.charAt(0).toUpperCase() + table.name.singular.slice(1);
|
|
2012
|
+
const enumFilterOperators = table.fields.filter((field) => field.type === "enum" && field.enumValues).map((field) => {
|
|
2013
|
+
const enumTypeName = `${field.name}Enum`;
|
|
2014
|
+
return `
|
|
2015
|
+
input FilterOperator${enumTypeName} {
|
|
2016
|
+
eq: ${enumTypeName}
|
|
2017
|
+
ne: ${enumTypeName}
|
|
2018
|
+
in: [${enumTypeName}]
|
|
2019
|
+
and: [FilterOperator${enumTypeName}]
|
|
2020
|
+
or: [FilterOperator${enumTypeName}]
|
|
2021
|
+
}`;
|
|
2022
|
+
}).join("\n");
|
|
1907
2023
|
const operatorTypes = `
|
|
1908
2024
|
input FilterOperatorString {
|
|
1909
2025
|
eq: String
|
|
1910
2026
|
ne: String
|
|
1911
2027
|
in: [String]
|
|
1912
2028
|
contains: String
|
|
2029
|
+
and: [FilterOperatorString]
|
|
2030
|
+
or: [FilterOperatorString]
|
|
1913
2031
|
}
|
|
1914
2032
|
|
|
1915
2033
|
input FilterOperatorDate {
|
|
1916
2034
|
lte: Date
|
|
1917
2035
|
gte: Date
|
|
2036
|
+
and: [FilterOperatorDate]
|
|
2037
|
+
or: [FilterOperatorDate]
|
|
1918
2038
|
}
|
|
1919
2039
|
|
|
1920
2040
|
input FilterOperatorFloat {
|
|
1921
2041
|
eq: Float
|
|
1922
2042
|
ne: Float
|
|
1923
2043
|
in: [Float]
|
|
2044
|
+
and: [FilterOperatorFloat]
|
|
2045
|
+
or: [FilterOperatorFloat]
|
|
1924
2046
|
}
|
|
1925
2047
|
|
|
1926
2048
|
input FilterOperatorBoolean {
|
|
1927
2049
|
eq: Boolean
|
|
1928
2050
|
ne: Boolean
|
|
1929
2051
|
in: [Boolean]
|
|
2052
|
+
and: [FilterOperatorBoolean]
|
|
2053
|
+
or: [FilterOperatorBoolean]
|
|
1930
2054
|
}
|
|
1931
2055
|
|
|
1932
2056
|
input FilterOperatorJSON {
|
|
@@ -1945,6 +2069,8 @@ enum SortDirection {
|
|
|
1945
2069
|
DESC
|
|
1946
2070
|
}
|
|
1947
2071
|
|
|
2072
|
+
${enumFilterOperators}
|
|
2073
|
+
|
|
1948
2074
|
input Filter${tableNameSingularUpperCaseFirst} {
|
|
1949
2075
|
${fieldFilters.join("\n")}
|
|
1950
2076
|
}`;
|
|
@@ -2016,6 +2142,17 @@ function createMutations(table) {
|
|
|
2016
2142
|
const validateWriteAccess = async (id, context) => {
|
|
2017
2143
|
try {
|
|
2018
2144
|
const { db: db3, req, user } = context;
|
|
2145
|
+
if (user.super_admin === true) {
|
|
2146
|
+
return true;
|
|
2147
|
+
}
|
|
2148
|
+
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")) {
|
|
2149
|
+
console.error("Access control error: no role found for current user or no access to entity type.");
|
|
2150
|
+
throw new Error("Access control error: no role found for current user or no access to entity type.");
|
|
2151
|
+
}
|
|
2152
|
+
const hasRBAC = table.RBAC === true;
|
|
2153
|
+
if (!hasRBAC) {
|
|
2154
|
+
return true;
|
|
2155
|
+
}
|
|
2019
2156
|
const record = await db3.from(tableNamePlural).select(["rights_mode", "created_by"]).where({ id }).first();
|
|
2020
2157
|
if (!record) {
|
|
2021
2158
|
throw new Error("Record not found");
|
|
@@ -2025,17 +2162,6 @@ function createMutations(table) {
|
|
|
2025
2162
|
throw new Error("You are not authorized to edit this record");
|
|
2026
2163
|
}
|
|
2027
2164
|
}
|
|
2028
|
-
const hasRBAC = table.RBAC === true;
|
|
2029
|
-
if (!hasRBAC) {
|
|
2030
|
-
return true;
|
|
2031
|
-
}
|
|
2032
|
-
if (user.super_admin === true) {
|
|
2033
|
-
return true;
|
|
2034
|
-
}
|
|
2035
|
-
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")) {
|
|
2036
|
-
console.error("Access control error: no role found for current user or no access to entity type.");
|
|
2037
|
-
throw new Error("Access control error: no role found for current user or no access to entity type.");
|
|
2038
|
-
}
|
|
2039
2165
|
if (record.rights_mode === "public") {
|
|
2040
2166
|
return true;
|
|
2041
2167
|
}
|
|
@@ -2216,15 +2342,15 @@ function createMutations(table) {
|
|
|
2216
2342
|
var applyAccessControl = (table, user, query) => {
|
|
2217
2343
|
console.log("table", table);
|
|
2218
2344
|
const tableNamePlural = table.name.plural.toLowerCase();
|
|
2345
|
+
if (!user.super_admin && table.name.plural === "jobs") {
|
|
2346
|
+
query = query.where("created_by", user.id);
|
|
2347
|
+
return query;
|
|
2348
|
+
}
|
|
2219
2349
|
const hasRBAC = table.RBAC === true;
|
|
2220
2350
|
if (!hasRBAC) {
|
|
2221
2351
|
return query;
|
|
2222
2352
|
}
|
|
2223
|
-
if (user.super_admin === true) {
|
|
2224
|
-
return query;
|
|
2225
|
-
}
|
|
2226
|
-
if (table.name.plural === "jobs") {
|
|
2227
|
-
query = query.where("created_by", user.id);
|
|
2353
|
+
if (table.name.plural !== "agent_sessions" && user.super_admin === true) {
|
|
2228
2354
|
return query;
|
|
2229
2355
|
}
|
|
2230
2356
|
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"))) {
|
|
@@ -2255,6 +2381,27 @@ var applyAccessControl = (table, user, query) => {
|
|
|
2255
2381
|
}
|
|
2256
2382
|
return query;
|
|
2257
2383
|
};
|
|
2384
|
+
var converOperatorToQuery = (query, fieldName, operators) => {
|
|
2385
|
+
if (operators.eq !== void 0) {
|
|
2386
|
+
query = query.where(fieldName, operators.eq);
|
|
2387
|
+
}
|
|
2388
|
+
if (operators.ne !== void 0) {
|
|
2389
|
+
query = query.whereRaw(`?? IS DISTINCT FROM ?`, [fieldName, operators.ne]);
|
|
2390
|
+
}
|
|
2391
|
+
if (operators.in !== void 0) {
|
|
2392
|
+
query = query.whereIn(fieldName, operators.in);
|
|
2393
|
+
}
|
|
2394
|
+
if (operators.contains !== void 0) {
|
|
2395
|
+
query = query.where(fieldName, "like", `%${operators.contains}%`);
|
|
2396
|
+
}
|
|
2397
|
+
if (operators.lte !== void 0) {
|
|
2398
|
+
query = query.where(fieldName, "<=", operators.lte);
|
|
2399
|
+
}
|
|
2400
|
+
if (operators.gte !== void 0) {
|
|
2401
|
+
query = query.where(fieldName, ">=", operators.gte);
|
|
2402
|
+
}
|
|
2403
|
+
return query;
|
|
2404
|
+
};
|
|
2258
2405
|
function createQueries(table) {
|
|
2259
2406
|
const tableNamePlural = table.name.plural.toLowerCase();
|
|
2260
2407
|
const tableNameSingular = table.name.singular.toLowerCase();
|
|
@@ -2262,18 +2409,19 @@ function createQueries(table) {
|
|
|
2262
2409
|
filters.forEach((filter) => {
|
|
2263
2410
|
Object.entries(filter).forEach(([fieldName, operators]) => {
|
|
2264
2411
|
if (operators) {
|
|
2265
|
-
if (operators.
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
}
|
|
2271
|
-
if (operators.in !== void 0) {
|
|
2272
|
-
query = query.whereIn(fieldName, operators.in);
|
|
2412
|
+
if (operators.and !== void 0) {
|
|
2413
|
+
console.log("operators.and", operators.and);
|
|
2414
|
+
operators.and.forEach((operator) => {
|
|
2415
|
+
query = converOperatorToQuery(query, fieldName, operator);
|
|
2416
|
+
});
|
|
2273
2417
|
}
|
|
2274
|
-
if (operators.
|
|
2275
|
-
|
|
2418
|
+
if (operators.or !== void 0) {
|
|
2419
|
+
operators.or.forEach((operator) => {
|
|
2420
|
+
query = converOperatorToQuery(query, fieldName, operator);
|
|
2421
|
+
});
|
|
2276
2422
|
}
|
|
2423
|
+
query = converOperatorToQuery(query, fieldName, operators);
|
|
2424
|
+
console.log("query", query);
|
|
2277
2425
|
}
|
|
2278
2426
|
});
|
|
2279
2427
|
});
|
|
@@ -2346,57 +2494,36 @@ function createQueries(table) {
|
|
|
2346
2494
|
query = applyFilters(query, filters);
|
|
2347
2495
|
query = applyAccessControl(table, context.user, query);
|
|
2348
2496
|
if (groupBy) {
|
|
2349
|
-
|
|
2497
|
+
query = query.select(groupBy).groupBy(groupBy);
|
|
2498
|
+
if (tableNamePlural === "tracking") {
|
|
2499
|
+
query = query.sum("total as count");
|
|
2500
|
+
} else {
|
|
2501
|
+
query = query.count("* as count");
|
|
2502
|
+
}
|
|
2503
|
+
const results = await query;
|
|
2504
|
+
console.log("!!! results !!!", results);
|
|
2350
2505
|
return results.map((r) => ({
|
|
2351
2506
|
group: r[groupBy],
|
|
2352
|
-
count: Number(r.count)
|
|
2507
|
+
count: r.count ? Number(r.count) : 0
|
|
2353
2508
|
}));
|
|
2354
2509
|
} else {
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
count
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
query = query.where("user", user);
|
|
2370
|
-
}
|
|
2371
|
-
if (agent) {
|
|
2372
|
-
query = query.where("agent", agent);
|
|
2373
|
-
}
|
|
2374
|
-
if (from) {
|
|
2375
|
-
query = query.where("createdAt", ">=", from);
|
|
2376
|
-
}
|
|
2377
|
-
if (to) {
|
|
2378
|
-
query = query.where("createdAt", "<=", to);
|
|
2510
|
+
if (tableNamePlural === "tracking") {
|
|
2511
|
+
query = query.sum("total as count");
|
|
2512
|
+
const [{ count }] = await query.sum("total as count");
|
|
2513
|
+
console.log("!!! count !!!", count);
|
|
2514
|
+
return [{
|
|
2515
|
+
group: "total",
|
|
2516
|
+
count: count ? Number(count) : 0
|
|
2517
|
+
}];
|
|
2518
|
+
} else {
|
|
2519
|
+
const [{ count }] = await query.count("* as count");
|
|
2520
|
+
return [{
|
|
2521
|
+
group: "total",
|
|
2522
|
+
count: count ? Number(count) : 0
|
|
2523
|
+
}];
|
|
2379
2524
|
}
|
|
2380
|
-
query = applyAccessControl(table, context.user, query);
|
|
2381
|
-
const runningQuery = query.clone().whereIn("status", ["active", "waiting", "delayed", "paused"]);
|
|
2382
|
-
const [{ runningCount }] = await runningQuery.count("* as runningCount");
|
|
2383
|
-
const erroredQuery = query.clone().whereIn("status", ["failed", "stuck"]);
|
|
2384
|
-
const [{ erroredCount }] = await erroredQuery.count("* as erroredCount");
|
|
2385
|
-
const completedQuery = query.clone().where("status", "completed");
|
|
2386
|
-
const [{ completedCount }] = await completedQuery.count("* as completedCount");
|
|
2387
|
-
const failedQuery = query.clone().where("status", "failed");
|
|
2388
|
-
const [{ failedCount }] = await failedQuery.count("* as failedCount");
|
|
2389
|
-
const durationQuery = query.clone().where("status", "completed").whereNotNull("duration").select(db3.raw('AVG("duration") as averageDuration'));
|
|
2390
|
-
const [{ averageDuration }] = await durationQuery;
|
|
2391
|
-
return {
|
|
2392
|
-
runningCount: Number(runningCount),
|
|
2393
|
-
erroredCount: Number(erroredCount),
|
|
2394
|
-
completedCount: Number(completedCount),
|
|
2395
|
-
failedCount: Number(failedCount),
|
|
2396
|
-
averageDuration: averageDuration ? Number(averageDuration) : 0
|
|
2397
|
-
};
|
|
2398
2525
|
}
|
|
2399
|
-
}
|
|
2526
|
+
}
|
|
2400
2527
|
};
|
|
2401
2528
|
}
|
|
2402
2529
|
var RBACResolver = async (db3, table, entityName, resourceId, rights_mode) => {
|
|
@@ -2416,6 +2543,16 @@ var RBACResolver = async (db3, table, entityName, resourceId, rights_mode) => {
|
|
|
2416
2543
|
};
|
|
2417
2544
|
};
|
|
2418
2545
|
function createSDL(tables) {
|
|
2546
|
+
tables.forEach((table) => {
|
|
2547
|
+
table.fields.push({
|
|
2548
|
+
name: "createdAt",
|
|
2549
|
+
type: "date"
|
|
2550
|
+
});
|
|
2551
|
+
table.fields.push({
|
|
2552
|
+
name: "updatedAt",
|
|
2553
|
+
type: "date"
|
|
2554
|
+
});
|
|
2555
|
+
});
|
|
2419
2556
|
console.log("[EXULU] Creating SDL");
|
|
2420
2557
|
let typeDefs = `
|
|
2421
2558
|
scalar JSON
|
|
@@ -2472,7 +2609,6 @@ function createSDL(tables) {
|
|
|
2472
2609
|
${tableNamePlural}Pagination(limit: Int, page: Int, filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingularUpperCaseFirst}PaginationResult
|
|
2473
2610
|
${tableNameSingular}One(filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingular}
|
|
2474
2611
|
${tableNamePlural}Statistics(filters: [Filter${tableNameSingularUpperCaseFirst}], groupBy: String): [StatisticsResult]!
|
|
2475
|
-
${tableNamePlural === "jobs" ? `jobStatistics(user: ID, agent: String, from: String, to: String): JobStatistics` : ""}
|
|
2476
2612
|
`;
|
|
2477
2613
|
mutationDefs += `
|
|
2478
2614
|
${tableNamePlural}CreateOne(input: ${tableNameSingular}Input!): ${tableNameSingular}
|
|
@@ -2497,17 +2633,6 @@ type PageInfo {
|
|
|
2497
2633
|
hasNextPage: Boolean!
|
|
2498
2634
|
}
|
|
2499
2635
|
`;
|
|
2500
|
-
if (tableNamePlural === "jobs") {
|
|
2501
|
-
modelDefs += `
|
|
2502
|
-
type JobStatistics {
|
|
2503
|
-
runningCount: Int!
|
|
2504
|
-
erroredCount: Int!
|
|
2505
|
-
completedCount: Int!
|
|
2506
|
-
failedCount: Int!
|
|
2507
|
-
averageDuration: Float!
|
|
2508
|
-
}
|
|
2509
|
-
`;
|
|
2510
|
-
}
|
|
2511
2636
|
Object.assign(resolvers.Query, createQueries(table));
|
|
2512
2637
|
Object.assign(resolvers.Mutation, createMutations(table));
|
|
2513
2638
|
if (table.RBAC) {
|
|
@@ -2599,6 +2724,10 @@ var agentMessagesSchema = {
|
|
|
2599
2724
|
name: "title",
|
|
2600
2725
|
type: "text"
|
|
2601
2726
|
},
|
|
2727
|
+
{
|
|
2728
|
+
name: "user",
|
|
2729
|
+
type: "number"
|
|
2730
|
+
},
|
|
2602
2731
|
{
|
|
2603
2732
|
name: "session",
|
|
2604
2733
|
type: "text"
|
|
@@ -2835,6 +2964,10 @@ var rolesSchema = {
|
|
|
2835
2964
|
type: "text"
|
|
2836
2965
|
// write | read access to agents
|
|
2837
2966
|
},
|
|
2967
|
+
{
|
|
2968
|
+
name: "api",
|
|
2969
|
+
type: "text"
|
|
2970
|
+
},
|
|
2838
2971
|
{
|
|
2839
2972
|
name: "workflows",
|
|
2840
2973
|
type: "text"
|
|
@@ -2854,8 +2987,8 @@ var rolesSchema = {
|
|
|
2854
2987
|
};
|
|
2855
2988
|
var statisticsSchema = {
|
|
2856
2989
|
name: {
|
|
2857
|
-
plural: "
|
|
2858
|
-
singular: "
|
|
2990
|
+
plural: "tracking",
|
|
2991
|
+
singular: "tracking"
|
|
2859
2992
|
},
|
|
2860
2993
|
fields: [
|
|
2861
2994
|
{
|
|
@@ -2868,11 +3001,20 @@ var statisticsSchema = {
|
|
|
2868
3001
|
},
|
|
2869
3002
|
{
|
|
2870
3003
|
name: "type",
|
|
2871
|
-
type: "
|
|
3004
|
+
type: "enum",
|
|
3005
|
+
enumValues: Object.values(STATISTICS_TYPE_ENUM)
|
|
2872
3006
|
},
|
|
2873
3007
|
{
|
|
2874
3008
|
name: "total",
|
|
2875
3009
|
type: "number"
|
|
3010
|
+
},
|
|
3011
|
+
{
|
|
3012
|
+
name: "user",
|
|
3013
|
+
type: "number"
|
|
3014
|
+
},
|
|
3015
|
+
{
|
|
3016
|
+
name: "role",
|
|
3017
|
+
type: "uuid"
|
|
2876
3018
|
}
|
|
2877
3019
|
]
|
|
2878
3020
|
};
|
|
@@ -2933,6 +3075,7 @@ var jobsSchema = {
|
|
|
2933
3075
|
plural: "jobs",
|
|
2934
3076
|
singular: "job"
|
|
2935
3077
|
},
|
|
3078
|
+
RBAC: true,
|
|
2936
3079
|
fields: [
|
|
2937
3080
|
{
|
|
2938
3081
|
name: "redis",
|
|
@@ -2942,10 +3085,6 @@ var jobsSchema = {
|
|
|
2942
3085
|
name: "session",
|
|
2943
3086
|
type: "text"
|
|
2944
3087
|
},
|
|
2945
|
-
{
|
|
2946
|
-
name: "created_by",
|
|
2947
|
-
type: "number"
|
|
2948
|
-
},
|
|
2949
3088
|
{
|
|
2950
3089
|
name: "status",
|
|
2951
3090
|
type: "text"
|
|
@@ -3588,11 +3727,8 @@ var createExpressRoutes = async (app, agents, tools, contexts) => {
|
|
|
3588
3727
|
{ route: "/agents/:id", method: "GET", note: "Get specific agent" },
|
|
3589
3728
|
{ route: "/contexts", method: "GET", note: "List all contexts" },
|
|
3590
3729
|
{ route: "/contexts/:id", method: "GET", note: "Get specific context" },
|
|
3591
|
-
{ route: "/contexts/statistics", method: "GET", note: "Get context statistics" },
|
|
3592
3730
|
{ route: "/tools", method: "GET", note: "List all tools" },
|
|
3593
3731
|
{ route: "/tools/:id", method: "GET", note: "Get specific tool" },
|
|
3594
|
-
{ route: "/statistics/timeseries", method: "POST", note: "Get time series statistics" },
|
|
3595
|
-
{ route: "/statistics/totals", method: "POST", note: "Get totals statistics" },
|
|
3596
3732
|
{ route: "/items/:context", method: "POST", note: "Create new item in context" },
|
|
3597
3733
|
{ route: "/items/:context", method: "GET", note: "Get items from context" },
|
|
3598
3734
|
{ route: "/items/export/:context", method: "GET", note: "Export items from context" },
|
|
@@ -4125,115 +4261,6 @@ var createExpressRoutes = async (app, agents, tools, contexts) => {
|
|
|
4125
4261
|
authenticated: true
|
|
4126
4262
|
});
|
|
4127
4263
|
});
|
|
4128
|
-
console.log("[EXULU] statistics timeseries");
|
|
4129
|
-
app.post("/statistics/timeseries", async (req, res) => {
|
|
4130
|
-
const authenticationResult = await requestValidators.authenticate(req);
|
|
4131
|
-
if (!authenticationResult.user?.id) {
|
|
4132
|
-
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
4133
|
-
return;
|
|
4134
|
-
}
|
|
4135
|
-
const { db: db3 } = await postgresClient();
|
|
4136
|
-
const type = req.body.type;
|
|
4137
|
-
if (!Object.values(STATISTICS_TYPE_ENUM).includes(type)) {
|
|
4138
|
-
res.status(400).json({
|
|
4139
|
-
message: "Invalid type, must be one of: " + Object.values(STATISTICS_TYPE_ENUM).join(", ")
|
|
4140
|
-
});
|
|
4141
|
-
return;
|
|
4142
|
-
}
|
|
4143
|
-
let from = new Date(req.body.from);
|
|
4144
|
-
let to = new Date(req.body.to);
|
|
4145
|
-
if (!from || !to) {
|
|
4146
|
-
from = new Date(Date.now() - 7 * 24 * 60 * 60 * 1e3);
|
|
4147
|
-
to = /* @__PURE__ */ new Date();
|
|
4148
|
-
}
|
|
4149
|
-
const query = db3.from("statistics").select("*");
|
|
4150
|
-
query.where("name", "count");
|
|
4151
|
-
query.andWhere("type", type);
|
|
4152
|
-
query.andWhere("createdAt", ">=", from);
|
|
4153
|
-
query.andWhere("createdAt", "<=", to);
|
|
4154
|
-
const results = await query;
|
|
4155
|
-
const dates = [];
|
|
4156
|
-
for (let i = 0; i < (to.getTime() - from.getTime()) / (1e3 * 60 * 60 * 24); i++) {
|
|
4157
|
-
dates.push(new Date(from.getTime() + i * (1e3 * 60 * 60 * 24)));
|
|
4158
|
-
}
|
|
4159
|
-
const data = dates.map((date) => {
|
|
4160
|
-
const result = results.find((result2) => result2.date === date);
|
|
4161
|
-
if (result) {
|
|
4162
|
-
return result;
|
|
4163
|
-
}
|
|
4164
|
-
return {
|
|
4165
|
-
date,
|
|
4166
|
-
count: 0
|
|
4167
|
-
};
|
|
4168
|
-
});
|
|
4169
|
-
res.status(200).json({
|
|
4170
|
-
data,
|
|
4171
|
-
filter: {
|
|
4172
|
-
from,
|
|
4173
|
-
to
|
|
4174
|
-
}
|
|
4175
|
-
});
|
|
4176
|
-
});
|
|
4177
|
-
console.log("[EXULU] statistics totals");
|
|
4178
|
-
app.post("/statistics/totals", async (req, res) => {
|
|
4179
|
-
const authenticationResult = await requestValidators.authenticate(req);
|
|
4180
|
-
if (!authenticationResult.user?.id) {
|
|
4181
|
-
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
4182
|
-
return;
|
|
4183
|
-
}
|
|
4184
|
-
const { db: db3 } = await postgresClient();
|
|
4185
|
-
let from = new Date(req.body.from);
|
|
4186
|
-
let to = new Date(req.body.to);
|
|
4187
|
-
if (!from || !to) {
|
|
4188
|
-
from = new Date(Date.now() - 7 * 24 * 60 * 60 * 1e3);
|
|
4189
|
-
to = /* @__PURE__ */ new Date();
|
|
4190
|
-
}
|
|
4191
|
-
let promises2 = Object.values(STATISTICS_TYPE_ENUM).map(async (type) => {
|
|
4192
|
-
const result = await db3.from("statistics").where("name", "count").andWhere("type", type).andWhere("createdAt", ">=", from).andWhere("createdAt", "<=", to).sum("total as total");
|
|
4193
|
-
return {
|
|
4194
|
-
[type]: result[0]?.total || 0
|
|
4195
|
-
};
|
|
4196
|
-
});
|
|
4197
|
-
const results = await Promise.all(promises2);
|
|
4198
|
-
res.status(200).json({
|
|
4199
|
-
data: { ...Object.assign({}, ...results) },
|
|
4200
|
-
filter: {
|
|
4201
|
-
from,
|
|
4202
|
-
to
|
|
4203
|
-
}
|
|
4204
|
-
});
|
|
4205
|
-
});
|
|
4206
|
-
console.log("[EXULU] contexts statistics");
|
|
4207
|
-
app.get("/contexts/statistics", async (req, res) => {
|
|
4208
|
-
const authenticationResult = await requestValidators.authenticate(req);
|
|
4209
|
-
if (!authenticationResult.user?.id) {
|
|
4210
|
-
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
4211
|
-
return;
|
|
4212
|
-
}
|
|
4213
|
-
const { db: db3 } = await postgresClient();
|
|
4214
|
-
const statistics = await db3("statistics").where("name", "count").andWhere("type", "context.retrieve").sum("total as total").first();
|
|
4215
|
-
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) => ({
|
|
4216
|
-
jobs: rows
|
|
4217
|
-
}));
|
|
4218
|
-
let jobs = [];
|
|
4219
|
-
if (response[0]) {
|
|
4220
|
-
jobs = response[0].jobs.map((job) => ({
|
|
4221
|
-
date: job.id,
|
|
4222
|
-
count: job.count
|
|
4223
|
-
}));
|
|
4224
|
-
}
|
|
4225
|
-
const embeddingsCountResult = await db3("jobs").where("type", "embedder").count("* as count").first();
|
|
4226
|
-
res.status(200).json({
|
|
4227
|
-
active: contexts.filter((context) => context.active).length,
|
|
4228
|
-
inactive: contexts.filter((context) => !context.active).length,
|
|
4229
|
-
sources: contexts.reduce((acc, context) => acc + context.sources.get().length, 0),
|
|
4230
|
-
queries: statistics?.total || 0,
|
|
4231
|
-
jobs,
|
|
4232
|
-
totals: {
|
|
4233
|
-
embeddings: embeddingsCountResult?.count || 0
|
|
4234
|
-
}
|
|
4235
|
-
});
|
|
4236
|
-
});
|
|
4237
4264
|
console.log("[EXULU] context by id");
|
|
4238
4265
|
app.get(`/contexts/:id`, async (req, res) => {
|
|
4239
4266
|
const authenticationResult = await requestValidators.authenticate(req);
|
|
@@ -4404,7 +4431,11 @@ var createExpressRoutes = async (app, agents, tools, contexts) => {
|
|
|
4404
4431
|
return;
|
|
4405
4432
|
}
|
|
4406
4433
|
}
|
|
4407
|
-
const
|
|
4434
|
+
const headers = {
|
|
4435
|
+
stream: req.headers["stream"] === "true" || false,
|
|
4436
|
+
user: req.headers["user"] || null,
|
|
4437
|
+
session: req.headers["session"] || null
|
|
4438
|
+
};
|
|
4408
4439
|
const requestValidationResult = requestValidators.agents(req);
|
|
4409
4440
|
if (requestValidationResult.error) {
|
|
4410
4441
|
res.status(requestValidationResult.code || 500).json({ detail: `${requestValidationResult.message}` });
|
|
@@ -4415,6 +4446,13 @@ var createExpressRoutes = async (app, agents, tools, contexts) => {
|
|
|
4415
4446
|
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
4416
4447
|
return;
|
|
4417
4448
|
}
|
|
4449
|
+
const user = authenticationResult.user;
|
|
4450
|
+
if (user.type !== "api" && !user.super_admin && req.body.resourceId !== user.id) {
|
|
4451
|
+
res.status(400).json({
|
|
4452
|
+
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."
|
|
4453
|
+
});
|
|
4454
|
+
return;
|
|
4455
|
+
}
|
|
4418
4456
|
console.log("[EXULU] agent tools", agentInstance.tools);
|
|
4419
4457
|
const enabledTools = agentInstance.tools.map(({ config, toolId }) => tools.find(({ id }) => id === toolId)).filter(Boolean);
|
|
4420
4458
|
console.log("[EXULU] enabled tools", enabledTools);
|
|
@@ -4437,9 +4475,15 @@ var createExpressRoutes = async (app, agents, tools, contexts) => {
|
|
|
4437
4475
|
const bytes = CryptoJS3.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
|
|
4438
4476
|
providerApiKey = bytes.toString(CryptoJS3.enc.Utf8);
|
|
4439
4477
|
}
|
|
4440
|
-
if (!!stream) {
|
|
4441
|
-
|
|
4442
|
-
|
|
4478
|
+
if (!!headers.stream) {
|
|
4479
|
+
await agent.generateStream({
|
|
4480
|
+
express: {
|
|
4481
|
+
res,
|
|
4482
|
+
req
|
|
4483
|
+
},
|
|
4484
|
+
user: headers.user,
|
|
4485
|
+
session: headers.session,
|
|
4486
|
+
message: req.body.message,
|
|
4443
4487
|
tools: enabledTools,
|
|
4444
4488
|
providerApiKey,
|
|
4445
4489
|
toolConfigs: agentInstance.tools,
|
|
@@ -4448,11 +4492,12 @@ var createExpressRoutes = async (app, agents, tools, contexts) => {
|
|
|
4448
4492
|
trigger: "agent"
|
|
4449
4493
|
}
|
|
4450
4494
|
});
|
|
4451
|
-
result.pipeDataStreamToResponse(res);
|
|
4452
4495
|
return;
|
|
4453
4496
|
} else {
|
|
4454
4497
|
const response = await agent.generateSync({
|
|
4455
|
-
|
|
4498
|
+
user: headers.user,
|
|
4499
|
+
session: headers.session,
|
|
4500
|
+
message: req.body.message,
|
|
4456
4501
|
tools: enabledTools.map((tool2) => tool2.tool),
|
|
4457
4502
|
providerApiKey,
|
|
4458
4503
|
toolConfigs: agentInstance.tools,
|
|
@@ -4977,7 +5022,6 @@ var defaultAgent = new ExuluAgent({
|
|
|
4977
5022
|
description: `Basic agent without any defined tools, that can support MCP's.`,
|
|
4978
5023
|
type: "agent",
|
|
4979
5024
|
capabilities: {
|
|
4980
|
-
tools: false,
|
|
4981
5025
|
images: [],
|
|
4982
5026
|
files: [],
|
|
4983
5027
|
audio: [],
|
|
@@ -4989,10 +5033,10 @@ var defaultAgent = new ExuluAgent({
|
|
|
4989
5033
|
instructions: "You are a helpful assistant.",
|
|
4990
5034
|
model: {
|
|
4991
5035
|
create: ({ apiKey }) => {
|
|
4992
|
-
const
|
|
5036
|
+
const anthropic = createAnthropic({
|
|
4993
5037
|
apiKey
|
|
4994
5038
|
});
|
|
4995
|
-
return
|
|
5039
|
+
return anthropic.languageModel("claude-4-opus-20250514");
|
|
4996
5040
|
}
|
|
4997
5041
|
// todo add a field of type string that adds a dropdown list from which the user can select the model
|
|
4998
5042
|
// todo for each model, check which provider is used, and require the admin to add one or multiple
|
|
@@ -6533,6 +6577,13 @@ var ExuluJobs = {
|
|
|
6533
6577
|
var db2 = {
|
|
6534
6578
|
init: async () => {
|
|
6535
6579
|
await execute();
|
|
6580
|
+
},
|
|
6581
|
+
api: {
|
|
6582
|
+
key: {
|
|
6583
|
+
generate: async (name, email) => {
|
|
6584
|
+
return await generateApiKey(name, email);
|
|
6585
|
+
}
|
|
6586
|
+
}
|
|
6536
6587
|
}
|
|
6537
6588
|
};
|
|
6538
6589
|
var ExuluChunkers = {
|