@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.cjs
CHANGED
|
@@ -121,15 +121,15 @@ var import_ai = require("ai");
|
|
|
121
121
|
|
|
122
122
|
// types/enums/statistics.ts
|
|
123
123
|
var STATISTICS_TYPE_ENUM = {
|
|
124
|
-
CONTEXT_RETRIEVE: "
|
|
125
|
-
SOURCE_UPDATE: "
|
|
126
|
-
EMBEDDER_UPSERT: "
|
|
127
|
-
EMBEDDER_GENERATE: "
|
|
128
|
-
EMBEDDER_DELETE: "
|
|
129
|
-
WORKFLOW_RUN: "
|
|
130
|
-
CONTEXT_UPSERT: "
|
|
131
|
-
TOOL_CALL: "
|
|
132
|
-
AGENT_RUN: "
|
|
124
|
+
CONTEXT_RETRIEVE: "CONTEXT_RETRIEVE",
|
|
125
|
+
SOURCE_UPDATE: "SOURCE_UPDATE",
|
|
126
|
+
EMBEDDER_UPSERT: "EMBEDDER_UPSERT",
|
|
127
|
+
EMBEDDER_GENERATE: "EMBEDDER_GENERATE",
|
|
128
|
+
EMBEDDER_DELETE: "EMBEDDER_DELETE",
|
|
129
|
+
WORKFLOW_RUN: "WORKFLOW_RUN",
|
|
130
|
+
CONTEXT_UPSERT: "CONTEXT_UPSERT",
|
|
131
|
+
TOOL_CALL: "TOOL_CALL",
|
|
132
|
+
AGENT_RUN: "AGENT_RUN"
|
|
133
133
|
};
|
|
134
134
|
|
|
135
135
|
// types/enums/eval-types.ts
|
|
@@ -304,58 +304,81 @@ var bullmqDecorator = async ({
|
|
|
304
304
|
|
|
305
305
|
// src/registry/utils/map-types.ts
|
|
306
306
|
var mapType = (t, type, name, defaultValue, unique) => {
|
|
307
|
-
if (type === "text") {
|
|
308
|
-
|
|
307
|
+
if (type === "text" || type === "enum") {
|
|
308
|
+
if (defaultValue) {
|
|
309
|
+
t.text(name).defaultTo(defaultValue);
|
|
310
|
+
} else {
|
|
311
|
+
t.text(name);
|
|
312
|
+
}
|
|
309
313
|
if (unique) t.unique(name);
|
|
310
|
-
if (defaultValue) t.defaultTo(defaultValue);
|
|
311
314
|
return;
|
|
312
315
|
}
|
|
313
316
|
if (type === "longText") {
|
|
314
|
-
|
|
317
|
+
if (defaultValue) {
|
|
318
|
+
t.text(name).defaultTo(defaultValue);
|
|
319
|
+
} else {
|
|
320
|
+
t.text(name);
|
|
321
|
+
}
|
|
315
322
|
if (unique) t.unique(name);
|
|
316
|
-
if (defaultValue) t.defaultTo(defaultValue);
|
|
317
323
|
return;
|
|
318
324
|
}
|
|
319
325
|
if (type === "shortText") {
|
|
320
|
-
|
|
326
|
+
if (defaultValue) {
|
|
327
|
+
t.string(name, 100).defaultTo(defaultValue);
|
|
328
|
+
} else {
|
|
329
|
+
t.string(name, 100);
|
|
330
|
+
}
|
|
321
331
|
if (unique) t.unique(name);
|
|
322
|
-
if (defaultValue) t.defaultTo(defaultValue);
|
|
323
332
|
return;
|
|
324
333
|
}
|
|
325
334
|
if (type === "number") {
|
|
326
|
-
|
|
335
|
+
if (defaultValue) {
|
|
336
|
+
t.float(name).defaultTo(defaultValue);
|
|
337
|
+
} else {
|
|
338
|
+
t.float(name);
|
|
339
|
+
}
|
|
327
340
|
if (unique) t.unique(name);
|
|
328
|
-
if (defaultValue) t.defaultTo(defaultValue);
|
|
329
341
|
return;
|
|
330
342
|
}
|
|
331
343
|
if (type === "boolean") {
|
|
332
344
|
t.boolean(name).defaultTo(defaultValue || false);
|
|
333
345
|
if (unique) t.unique(name);
|
|
334
|
-
if (defaultValue) t.defaultTo(defaultValue);
|
|
335
346
|
return;
|
|
336
347
|
}
|
|
337
348
|
if (type === "code") {
|
|
338
|
-
|
|
349
|
+
if (defaultValue) {
|
|
350
|
+
t.text(name).defaultTo(defaultValue);
|
|
351
|
+
} else {
|
|
352
|
+
t.text(name);
|
|
353
|
+
}
|
|
339
354
|
if (unique) t.unique(name);
|
|
340
|
-
if (defaultValue) t.defaultTo(defaultValue);
|
|
341
355
|
return;
|
|
342
356
|
}
|
|
343
357
|
if (type === "json") {
|
|
344
|
-
|
|
358
|
+
if (defaultValue) {
|
|
359
|
+
t.jsonb(name).defaultTo(defaultValue);
|
|
360
|
+
} else {
|
|
361
|
+
t.jsonb(name);
|
|
362
|
+
}
|
|
345
363
|
if (unique) t.unique(name);
|
|
346
|
-
if (defaultValue) t.defaultTo(defaultValue);
|
|
347
364
|
return;
|
|
348
365
|
}
|
|
349
366
|
if (type === "date") {
|
|
350
|
-
|
|
367
|
+
if (defaultValue) {
|
|
368
|
+
t.timestamp(name).defaultTo(defaultValue);
|
|
369
|
+
} else {
|
|
370
|
+
t.timestamp(name);
|
|
371
|
+
}
|
|
351
372
|
if (unique) t.unique(name);
|
|
352
|
-
if (defaultValue) t.defaultTo(defaultValue);
|
|
353
373
|
return;
|
|
354
374
|
}
|
|
355
375
|
if (type === "uuid") {
|
|
356
|
-
|
|
376
|
+
if (defaultValue) {
|
|
377
|
+
t.uuid(name).defaultTo(defaultValue);
|
|
378
|
+
} else {
|
|
379
|
+
t.uuid(name);
|
|
380
|
+
}
|
|
357
381
|
if (unique) t.unique(name);
|
|
358
|
-
if (defaultValue) t.defaultTo(defaultValue);
|
|
359
382
|
return;
|
|
360
383
|
}
|
|
361
384
|
throw new Error("Invalid type: " + type);
|
|
@@ -445,6 +468,7 @@ var ExuluEvalUtils = {
|
|
|
445
468
|
|
|
446
469
|
// src/registry/classes.ts
|
|
447
470
|
var import_crypto_js = __toESM(require("crypto-js"), 1);
|
|
471
|
+
var import_express = require("express");
|
|
448
472
|
function sanitizeToolName(name) {
|
|
449
473
|
if (typeof name !== "string") return "";
|
|
450
474
|
let sanitized = name.replace(/[^a-zA-Z0-9_-]+/g, "_");
|
|
@@ -463,7 +487,7 @@ var convertToolsArrayToObject = (tools, configs, providerApiKey) => {
|
|
|
463
487
|
console.log("[EXULU] Sanitized tools", sanitizedTools);
|
|
464
488
|
const askForConfirmation = {
|
|
465
489
|
description: "Ask the user for confirmation.",
|
|
466
|
-
|
|
490
|
+
inputSchema: import_zod2.z.object({
|
|
467
491
|
message: import_zod2.z.string().describe("The message to ask for confirmation.")
|
|
468
492
|
})
|
|
469
493
|
};
|
|
@@ -574,10 +598,12 @@ var ExuluAgent = class {
|
|
|
574
598
|
this.model = this.config?.model;
|
|
575
599
|
}
|
|
576
600
|
get providerName() {
|
|
577
|
-
|
|
601
|
+
const model = this.config?.model?.create({ apiKey: "" });
|
|
602
|
+
return typeof model === "string" ? model : model?.provider || "";
|
|
578
603
|
}
|
|
579
604
|
get modelName() {
|
|
580
|
-
|
|
605
|
+
const model = this.config?.model?.create({ apiKey: "" });
|
|
606
|
+
return typeof model === "string" ? model : model?.modelId || "";
|
|
581
607
|
}
|
|
582
608
|
// Exports the agent as a tool that can be used by another agent
|
|
583
609
|
// todo test this
|
|
@@ -603,28 +629,44 @@ var ExuluAgent = class {
|
|
|
603
629
|
}
|
|
604
630
|
});
|
|
605
631
|
};
|
|
606
|
-
generateSync = async ({
|
|
632
|
+
generateSync = async ({ prompt, user, session, message, tools, statistics, toolConfigs, providerApiKey }) => {
|
|
607
633
|
if (!this.model) {
|
|
608
634
|
throw new Error("Model is required for streaming.");
|
|
609
635
|
}
|
|
610
636
|
if (!this.config) {
|
|
611
637
|
throw new Error("Config is required for generating.");
|
|
612
638
|
}
|
|
613
|
-
if (prompt &&
|
|
614
|
-
throw new Error("
|
|
639
|
+
if (prompt && message) {
|
|
640
|
+
throw new Error("Message and prompt cannot be provided at the same time.");
|
|
615
641
|
}
|
|
616
642
|
const model = this.model.create({
|
|
617
643
|
apiKey: providerApiKey
|
|
618
644
|
});
|
|
645
|
+
let messages = [];
|
|
646
|
+
if (message && session && user) {
|
|
647
|
+
const previousMessages = await getAgentMessages({
|
|
648
|
+
session,
|
|
649
|
+
user,
|
|
650
|
+
limit: 50,
|
|
651
|
+
page: 1
|
|
652
|
+
});
|
|
653
|
+
const previousMessagesContent = previousMessages.map((message2) => JSON.parse(message2.content));
|
|
654
|
+
messages = await (0, import_ai.validateUIMessages)({
|
|
655
|
+
// append the new message to the previous messages:
|
|
656
|
+
messages: [...previousMessagesContent, message]
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
console.log("[EXULU] Model provider key", providerApiKey);
|
|
660
|
+
console.log("[EXULU] Tool configs", toolConfigs);
|
|
619
661
|
const { text } = await (0, import_ai.generateText)({
|
|
620
662
|
model,
|
|
621
663
|
// Should be a LanguageModelV1
|
|
622
664
|
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.",
|
|
623
|
-
messages,
|
|
665
|
+
messages: messages ? (0, import_ai.convertToModelMessages)(messages) : void 0,
|
|
624
666
|
prompt,
|
|
625
667
|
maxRetries: 2,
|
|
626
668
|
tools: convertToolsArrayToObject(tools, toolConfigs, providerApiKey),
|
|
627
|
-
|
|
669
|
+
stopWhen: [(0, import_ai.stepCountIs)(5)]
|
|
628
670
|
});
|
|
629
671
|
if (statistics) {
|
|
630
672
|
await updateStatistic({
|
|
@@ -637,36 +679,61 @@ var ExuluAgent = class {
|
|
|
637
679
|
}
|
|
638
680
|
return text;
|
|
639
681
|
};
|
|
640
|
-
generateStream = ({
|
|
682
|
+
generateStream = async ({ express: express3, user, session, message, tools, statistics, toolConfigs, providerApiKey }) => {
|
|
641
683
|
if (!this.model) {
|
|
642
684
|
throw new Error("Model is required for streaming.");
|
|
643
685
|
}
|
|
644
686
|
if (!this.config) {
|
|
645
687
|
throw new Error("Config is required for generating.");
|
|
646
688
|
}
|
|
647
|
-
if (
|
|
648
|
-
throw new Error("
|
|
689
|
+
if (!message) {
|
|
690
|
+
throw new Error("Message is required for streaming.");
|
|
649
691
|
}
|
|
650
692
|
const model = this.model.create({
|
|
651
693
|
apiKey: providerApiKey
|
|
652
694
|
});
|
|
695
|
+
let messages = [];
|
|
696
|
+
const previousMessages = await getAgentMessages({
|
|
697
|
+
session,
|
|
698
|
+
user,
|
|
699
|
+
limit: 50,
|
|
700
|
+
page: 1
|
|
701
|
+
});
|
|
702
|
+
const previousMessagesContent = previousMessages.map((message2) => JSON.parse(message2.content));
|
|
703
|
+
messages = await (0, import_ai.validateUIMessages)({
|
|
704
|
+
// append the new message to the previous messages:
|
|
705
|
+
messages: [...previousMessagesContent, message]
|
|
706
|
+
});
|
|
653
707
|
console.log("[EXULU] Model provider key", providerApiKey);
|
|
654
708
|
console.log("[EXULU] Tool configs", toolConfigs);
|
|
655
|
-
|
|
709
|
+
const result = (0, import_ai.streamText)({
|
|
656
710
|
model,
|
|
657
711
|
// Should be a LanguageModelV1
|
|
658
|
-
messages,
|
|
659
|
-
prompt,
|
|
712
|
+
messages: messages ? (0, import_ai.convertToModelMessages)(messages) : void 0,
|
|
660
713
|
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.",
|
|
661
714
|
maxRetries: 2,
|
|
662
715
|
tools: convertToolsArrayToObject(tools, toolConfigs, providerApiKey),
|
|
663
|
-
maxSteps: 5,
|
|
664
716
|
onError: (error) => console.error("[EXULU] chat stream error.", error),
|
|
665
|
-
|
|
717
|
+
stopWhen: [(0, import_ai.stepCountIs)(5)]
|
|
718
|
+
});
|
|
719
|
+
result.consumeStream();
|
|
720
|
+
result.pipeUIMessageStreamToResponse(express3.res, {
|
|
721
|
+
originalMessages: messages,
|
|
722
|
+
sendReasoning: true,
|
|
723
|
+
generateMessageId: (0, import_ai.createIdGenerator)({
|
|
724
|
+
prefix: "msg_",
|
|
725
|
+
size: 16
|
|
726
|
+
}),
|
|
727
|
+
onFinish: async ({ messages: messages2 }) => {
|
|
666
728
|
console.info(
|
|
667
729
|
"[EXULU] chat stream finished.",
|
|
668
|
-
|
|
730
|
+
messages2
|
|
669
731
|
);
|
|
732
|
+
await saveChat({
|
|
733
|
+
session,
|
|
734
|
+
user,
|
|
735
|
+
messages: messages2
|
|
736
|
+
});
|
|
670
737
|
if (statistics) {
|
|
671
738
|
await updateStatistic({
|
|
672
739
|
name: "count",
|
|
@@ -678,8 +745,26 @@ var ExuluAgent = class {
|
|
|
678
745
|
}
|
|
679
746
|
}
|
|
680
747
|
});
|
|
748
|
+
return;
|
|
681
749
|
};
|
|
682
750
|
};
|
|
751
|
+
var getAgentMessages = async ({ session, user, limit, page }) => {
|
|
752
|
+
const { db: db3 } = await postgresClient();
|
|
753
|
+
const messages = await db3.from("agent_messages").where({ session, user }).limit(limit).offset(page * limit);
|
|
754
|
+
return messages;
|
|
755
|
+
};
|
|
756
|
+
var saveChat = async ({ session, user, messages }) => {
|
|
757
|
+
const { db: db3 } = await postgresClient();
|
|
758
|
+
const promises2 = messages.map((message) => {
|
|
759
|
+
return db3.from("agent_messages").insert({
|
|
760
|
+
session,
|
|
761
|
+
user,
|
|
762
|
+
content: JSON.stringify(message),
|
|
763
|
+
title: message.role === "user" ? "User" : "Assistant"
|
|
764
|
+
});
|
|
765
|
+
});
|
|
766
|
+
await Promise.all(promises2);
|
|
767
|
+
};
|
|
683
768
|
var ExuluEmbedder = class {
|
|
684
769
|
id;
|
|
685
770
|
name;
|
|
@@ -930,7 +1015,7 @@ var ExuluTool = class {
|
|
|
930
1015
|
this.type = type;
|
|
931
1016
|
this.tool = (0, import_ai.tool)({
|
|
932
1017
|
description,
|
|
933
|
-
|
|
1018
|
+
inputSchema: inputSchema || import_zod2.z.object({}),
|
|
934
1019
|
execute: execute2
|
|
935
1020
|
});
|
|
936
1021
|
}
|
|
@@ -1419,22 +1504,27 @@ var ExuluSource = class {
|
|
|
1419
1504
|
var updateStatistic = async (statistic) => {
|
|
1420
1505
|
const currentDate = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
1421
1506
|
const { db: db3 } = await postgresClient();
|
|
1422
|
-
const existing = await db3.from("
|
|
1507
|
+
const existing = await db3.from("tracking").where({
|
|
1508
|
+
...statistic.user ? { user: statistic.user } : {},
|
|
1509
|
+
...statistic.role ? { role: statistic.role } : {},
|
|
1423
1510
|
name: statistic.name,
|
|
1424
1511
|
label: statistic.label,
|
|
1425
1512
|
type: statistic.type,
|
|
1426
1513
|
createdAt: currentDate
|
|
1427
1514
|
}).first();
|
|
1515
|
+
console.log("!!! existing !!!", existing);
|
|
1428
1516
|
if (!existing) {
|
|
1429
|
-
await db3.from("
|
|
1517
|
+
await db3.from("tracking").insert({
|
|
1430
1518
|
name: statistic.name,
|
|
1431
1519
|
label: statistic.label,
|
|
1432
1520
|
type: statistic.type,
|
|
1433
1521
|
total: statistic.count ?? 1,
|
|
1434
|
-
createdAt: currentDate
|
|
1522
|
+
createdAt: currentDate,
|
|
1523
|
+
...statistic.user ? { user: statistic.user } : {},
|
|
1524
|
+
...statistic.role ? { role: statistic.role } : {}
|
|
1435
1525
|
});
|
|
1436
1526
|
} else {
|
|
1437
|
-
await db3.from("
|
|
1527
|
+
await db3.from("tracking").update({
|
|
1438
1528
|
total: db3.raw("total + ?", [statistic.count ?? 1])
|
|
1439
1529
|
}).where({
|
|
1440
1530
|
name: statistic.name,
|
|
@@ -1446,10 +1536,10 @@ var updateStatistic = async (statistic) => {
|
|
|
1446
1536
|
};
|
|
1447
1537
|
|
|
1448
1538
|
// src/registry/index.ts
|
|
1449
|
-
var
|
|
1539
|
+
var import_express7 = require("express");
|
|
1450
1540
|
|
|
1451
1541
|
// src/registry/routes.ts
|
|
1452
|
-
var
|
|
1542
|
+
var import_express3 = require("express");
|
|
1453
1543
|
|
|
1454
1544
|
// src/registry/rate-limiter.ts
|
|
1455
1545
|
var rateLimiter = async (key, windowSeconds, limit, points) => {
|
|
@@ -1773,25 +1863,25 @@ var requestValidators = {
|
|
|
1773
1863
|
message: "Missing body."
|
|
1774
1864
|
};
|
|
1775
1865
|
}
|
|
1776
|
-
if (!req.
|
|
1866
|
+
if (!req.headers["user"]) {
|
|
1777
1867
|
return {
|
|
1778
1868
|
error: true,
|
|
1779
1869
|
code: 400,
|
|
1780
|
-
message:
|
|
1870
|
+
message: 'Missing "user" property in headers.'
|
|
1781
1871
|
};
|
|
1782
1872
|
}
|
|
1783
|
-
if (!req.
|
|
1873
|
+
if (!req.headers["session"]) {
|
|
1784
1874
|
return {
|
|
1785
1875
|
error: true,
|
|
1786
1876
|
code: 400,
|
|
1787
|
-
message:
|
|
1877
|
+
message: 'Missing "session" property in headers.'
|
|
1788
1878
|
};
|
|
1789
1879
|
}
|
|
1790
|
-
if (!req.body.
|
|
1880
|
+
if (!req.body.message) {
|
|
1791
1881
|
return {
|
|
1792
1882
|
error: true,
|
|
1793
1883
|
code: 400,
|
|
1794
|
-
message: 'Missing "
|
|
1884
|
+
message: 'Missing "message" property in body.'
|
|
1795
1885
|
};
|
|
1796
1886
|
}
|
|
1797
1887
|
return {
|
|
@@ -1840,7 +1930,7 @@ var VectorMethodEnum = {
|
|
|
1840
1930
|
};
|
|
1841
1931
|
|
|
1842
1932
|
// src/registry/routes.ts
|
|
1843
|
-
var
|
|
1933
|
+
var import_express4 = __toESM(require("express"), 1);
|
|
1844
1934
|
var import_server3 = require("@apollo/server");
|
|
1845
1935
|
var Papa = __toESM(require("papaparse"), 1);
|
|
1846
1936
|
var import_cors = __toESM(require("cors"), 1);
|
|
@@ -1895,6 +1985,9 @@ var map = (field) => {
|
|
|
1895
1985
|
case "code":
|
|
1896
1986
|
type = "String";
|
|
1897
1987
|
break;
|
|
1988
|
+
case "enum":
|
|
1989
|
+
type = field.enumValues ? `${field.name}Enum` : "String";
|
|
1990
|
+
break;
|
|
1898
1991
|
case "number":
|
|
1899
1992
|
type = "Float";
|
|
1900
1993
|
break;
|
|
@@ -1913,6 +2006,16 @@ var map = (field) => {
|
|
|
1913
2006
|
return type;
|
|
1914
2007
|
};
|
|
1915
2008
|
function createTypeDefs(table) {
|
|
2009
|
+
const enumDefs = table.fields.filter((field) => field.type === "enum" && field.enumValues).map((field) => {
|
|
2010
|
+
const enumValues = field.enumValues.map((value) => {
|
|
2011
|
+
const sanitized = String(value).replace(/[^a-zA-Z0-9_]/g, "_").replace(/^[0-9]/, "_$&").toUpperCase();
|
|
2012
|
+
return ` ${sanitized}`;
|
|
2013
|
+
}).join("\n");
|
|
2014
|
+
return `
|
|
2015
|
+
enum ${field.name}Enum {
|
|
2016
|
+
${enumValues}
|
|
2017
|
+
}`;
|
|
2018
|
+
}).join("\n");
|
|
1916
2019
|
const fields = table.fields.map((field) => {
|
|
1917
2020
|
let type;
|
|
1918
2021
|
type = map(field);
|
|
@@ -1924,8 +2027,6 @@ function createTypeDefs(table) {
|
|
|
1924
2027
|
type ${table.name.singular} {
|
|
1925
2028
|
${fields.join("\n")}
|
|
1926
2029
|
${table.fields.find((field) => field.name === "id") ? "" : "id: ID!"}
|
|
1927
|
-
createdAt: Date!
|
|
1928
|
-
updatedAt: Date!
|
|
1929
2030
|
${rbacField}
|
|
1930
2031
|
}
|
|
1931
2032
|
`;
|
|
@@ -1936,39 +2037,62 @@ ${table.fields.map((f) => ` ${f.name}: ${map(f)}`).join("\n")}
|
|
|
1936
2037
|
${rbacInputField}
|
|
1937
2038
|
}
|
|
1938
2039
|
`;
|
|
1939
|
-
return typeDef + inputDef;
|
|
2040
|
+
return enumDefs + typeDef + inputDef;
|
|
1940
2041
|
}
|
|
1941
2042
|
function createFilterTypeDefs(table) {
|
|
1942
2043
|
const fieldFilters = table.fields.map((field) => {
|
|
1943
2044
|
let type;
|
|
1944
|
-
type
|
|
2045
|
+
if (field.type === "enum" && field.enumValues) {
|
|
2046
|
+
type = `${field.name}Enum`;
|
|
2047
|
+
} else {
|
|
2048
|
+
type = map(field);
|
|
2049
|
+
}
|
|
1945
2050
|
return `
|
|
1946
2051
|
${field.name}: FilterOperator${type}`;
|
|
1947
2052
|
});
|
|
1948
2053
|
const tableNameSingularUpperCaseFirst = table.name.singular.charAt(0).toUpperCase() + table.name.singular.slice(1);
|
|
2054
|
+
const enumFilterOperators = table.fields.filter((field) => field.type === "enum" && field.enumValues).map((field) => {
|
|
2055
|
+
const enumTypeName = `${field.name}Enum`;
|
|
2056
|
+
return `
|
|
2057
|
+
input FilterOperator${enumTypeName} {
|
|
2058
|
+
eq: ${enumTypeName}
|
|
2059
|
+
ne: ${enumTypeName}
|
|
2060
|
+
in: [${enumTypeName}]
|
|
2061
|
+
and: [FilterOperator${enumTypeName}]
|
|
2062
|
+
or: [FilterOperator${enumTypeName}]
|
|
2063
|
+
}`;
|
|
2064
|
+
}).join("\n");
|
|
1949
2065
|
const operatorTypes = `
|
|
1950
2066
|
input FilterOperatorString {
|
|
1951
2067
|
eq: String
|
|
1952
2068
|
ne: String
|
|
1953
2069
|
in: [String]
|
|
1954
2070
|
contains: String
|
|
2071
|
+
and: [FilterOperatorString]
|
|
2072
|
+
or: [FilterOperatorString]
|
|
1955
2073
|
}
|
|
1956
2074
|
|
|
1957
2075
|
input FilterOperatorDate {
|
|
1958
2076
|
lte: Date
|
|
1959
2077
|
gte: Date
|
|
2078
|
+
and: [FilterOperatorDate]
|
|
2079
|
+
or: [FilterOperatorDate]
|
|
1960
2080
|
}
|
|
1961
2081
|
|
|
1962
2082
|
input FilterOperatorFloat {
|
|
1963
2083
|
eq: Float
|
|
1964
2084
|
ne: Float
|
|
1965
2085
|
in: [Float]
|
|
2086
|
+
and: [FilterOperatorFloat]
|
|
2087
|
+
or: [FilterOperatorFloat]
|
|
1966
2088
|
}
|
|
1967
2089
|
|
|
1968
2090
|
input FilterOperatorBoolean {
|
|
1969
2091
|
eq: Boolean
|
|
1970
2092
|
ne: Boolean
|
|
1971
2093
|
in: [Boolean]
|
|
2094
|
+
and: [FilterOperatorBoolean]
|
|
2095
|
+
or: [FilterOperatorBoolean]
|
|
1972
2096
|
}
|
|
1973
2097
|
|
|
1974
2098
|
input FilterOperatorJSON {
|
|
@@ -1987,6 +2111,8 @@ enum SortDirection {
|
|
|
1987
2111
|
DESC
|
|
1988
2112
|
}
|
|
1989
2113
|
|
|
2114
|
+
${enumFilterOperators}
|
|
2115
|
+
|
|
1990
2116
|
input Filter${tableNameSingularUpperCaseFirst} {
|
|
1991
2117
|
${fieldFilters.join("\n")}
|
|
1992
2118
|
}`;
|
|
@@ -2058,6 +2184,17 @@ function createMutations(table) {
|
|
|
2058
2184
|
const validateWriteAccess = async (id, context) => {
|
|
2059
2185
|
try {
|
|
2060
2186
|
const { db: db3, req, user } = context;
|
|
2187
|
+
if (user.super_admin === true) {
|
|
2188
|
+
return true;
|
|
2189
|
+
}
|
|
2190
|
+
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")) {
|
|
2191
|
+
console.error("Access control error: no role found for current user or no access to entity type.");
|
|
2192
|
+
throw new Error("Access control error: no role found for current user or no access to entity type.");
|
|
2193
|
+
}
|
|
2194
|
+
const hasRBAC = table.RBAC === true;
|
|
2195
|
+
if (!hasRBAC) {
|
|
2196
|
+
return true;
|
|
2197
|
+
}
|
|
2061
2198
|
const record = await db3.from(tableNamePlural).select(["rights_mode", "created_by"]).where({ id }).first();
|
|
2062
2199
|
if (!record) {
|
|
2063
2200
|
throw new Error("Record not found");
|
|
@@ -2067,17 +2204,6 @@ function createMutations(table) {
|
|
|
2067
2204
|
throw new Error("You are not authorized to edit this record");
|
|
2068
2205
|
}
|
|
2069
2206
|
}
|
|
2070
|
-
const hasRBAC = table.RBAC === true;
|
|
2071
|
-
if (!hasRBAC) {
|
|
2072
|
-
return true;
|
|
2073
|
-
}
|
|
2074
|
-
if (user.super_admin === true) {
|
|
2075
|
-
return true;
|
|
2076
|
-
}
|
|
2077
|
-
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")) {
|
|
2078
|
-
console.error("Access control error: no role found for current user or no access to entity type.");
|
|
2079
|
-
throw new Error("Access control error: no role found for current user or no access to entity type.");
|
|
2080
|
-
}
|
|
2081
2207
|
if (record.rights_mode === "public") {
|
|
2082
2208
|
return true;
|
|
2083
2209
|
}
|
|
@@ -2258,15 +2384,15 @@ function createMutations(table) {
|
|
|
2258
2384
|
var applyAccessControl = (table, user, query) => {
|
|
2259
2385
|
console.log("table", table);
|
|
2260
2386
|
const tableNamePlural = table.name.plural.toLowerCase();
|
|
2261
|
-
|
|
2262
|
-
|
|
2387
|
+
if (!user.super_admin && table.name.plural === "jobs") {
|
|
2388
|
+
query = query.where("created_by", user.id);
|
|
2263
2389
|
return query;
|
|
2264
2390
|
}
|
|
2265
|
-
|
|
2391
|
+
const hasRBAC = table.RBAC === true;
|
|
2392
|
+
if (!hasRBAC) {
|
|
2266
2393
|
return query;
|
|
2267
2394
|
}
|
|
2268
|
-
if (table.name.plural
|
|
2269
|
-
query = query.where("created_by", user.id);
|
|
2395
|
+
if (table.name.plural !== "agent_sessions" && user.super_admin === true) {
|
|
2270
2396
|
return query;
|
|
2271
2397
|
}
|
|
2272
2398
|
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"))) {
|
|
@@ -2297,6 +2423,27 @@ var applyAccessControl = (table, user, query) => {
|
|
|
2297
2423
|
}
|
|
2298
2424
|
return query;
|
|
2299
2425
|
};
|
|
2426
|
+
var converOperatorToQuery = (query, fieldName, operators) => {
|
|
2427
|
+
if (operators.eq !== void 0) {
|
|
2428
|
+
query = query.where(fieldName, operators.eq);
|
|
2429
|
+
}
|
|
2430
|
+
if (operators.ne !== void 0) {
|
|
2431
|
+
query = query.whereRaw(`?? IS DISTINCT FROM ?`, [fieldName, operators.ne]);
|
|
2432
|
+
}
|
|
2433
|
+
if (operators.in !== void 0) {
|
|
2434
|
+
query = query.whereIn(fieldName, operators.in);
|
|
2435
|
+
}
|
|
2436
|
+
if (operators.contains !== void 0) {
|
|
2437
|
+
query = query.where(fieldName, "like", `%${operators.contains}%`);
|
|
2438
|
+
}
|
|
2439
|
+
if (operators.lte !== void 0) {
|
|
2440
|
+
query = query.where(fieldName, "<=", operators.lte);
|
|
2441
|
+
}
|
|
2442
|
+
if (operators.gte !== void 0) {
|
|
2443
|
+
query = query.where(fieldName, ">=", operators.gte);
|
|
2444
|
+
}
|
|
2445
|
+
return query;
|
|
2446
|
+
};
|
|
2300
2447
|
function createQueries(table) {
|
|
2301
2448
|
const tableNamePlural = table.name.plural.toLowerCase();
|
|
2302
2449
|
const tableNameSingular = table.name.singular.toLowerCase();
|
|
@@ -2304,18 +2451,19 @@ function createQueries(table) {
|
|
|
2304
2451
|
filters.forEach((filter) => {
|
|
2305
2452
|
Object.entries(filter).forEach(([fieldName, operators]) => {
|
|
2306
2453
|
if (operators) {
|
|
2307
|
-
if (operators.
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
}
|
|
2313
|
-
if (operators.in !== void 0) {
|
|
2314
|
-
query = query.whereIn(fieldName, operators.in);
|
|
2454
|
+
if (operators.and !== void 0) {
|
|
2455
|
+
console.log("operators.and", operators.and);
|
|
2456
|
+
operators.and.forEach((operator) => {
|
|
2457
|
+
query = converOperatorToQuery(query, fieldName, operator);
|
|
2458
|
+
});
|
|
2315
2459
|
}
|
|
2316
|
-
if (operators.
|
|
2317
|
-
|
|
2460
|
+
if (operators.or !== void 0) {
|
|
2461
|
+
operators.or.forEach((operator) => {
|
|
2462
|
+
query = converOperatorToQuery(query, fieldName, operator);
|
|
2463
|
+
});
|
|
2318
2464
|
}
|
|
2465
|
+
query = converOperatorToQuery(query, fieldName, operators);
|
|
2466
|
+
console.log("query", query);
|
|
2319
2467
|
}
|
|
2320
2468
|
});
|
|
2321
2469
|
});
|
|
@@ -2388,57 +2536,36 @@ function createQueries(table) {
|
|
|
2388
2536
|
query = applyFilters(query, filters);
|
|
2389
2537
|
query = applyAccessControl(table, context.user, query);
|
|
2390
2538
|
if (groupBy) {
|
|
2391
|
-
|
|
2539
|
+
query = query.select(groupBy).groupBy(groupBy);
|
|
2540
|
+
if (tableNamePlural === "tracking") {
|
|
2541
|
+
query = query.sum("total as count");
|
|
2542
|
+
} else {
|
|
2543
|
+
query = query.count("* as count");
|
|
2544
|
+
}
|
|
2545
|
+
const results = await query;
|
|
2546
|
+
console.log("!!! results !!!", results);
|
|
2392
2547
|
return results.map((r) => ({
|
|
2393
2548
|
group: r[groupBy],
|
|
2394
|
-
count: Number(r.count)
|
|
2549
|
+
count: r.count ? Number(r.count) : 0
|
|
2395
2550
|
}));
|
|
2396
2551
|
} else {
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
count
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
query = query.where("user", user);
|
|
2412
|
-
}
|
|
2413
|
-
if (agent) {
|
|
2414
|
-
query = query.where("agent", agent);
|
|
2415
|
-
}
|
|
2416
|
-
if (from) {
|
|
2417
|
-
query = query.where("createdAt", ">=", from);
|
|
2418
|
-
}
|
|
2419
|
-
if (to) {
|
|
2420
|
-
query = query.where("createdAt", "<=", to);
|
|
2552
|
+
if (tableNamePlural === "tracking") {
|
|
2553
|
+
query = query.sum("total as count");
|
|
2554
|
+
const [{ count }] = await query.sum("total as count");
|
|
2555
|
+
console.log("!!! count !!!", count);
|
|
2556
|
+
return [{
|
|
2557
|
+
group: "total",
|
|
2558
|
+
count: count ? Number(count) : 0
|
|
2559
|
+
}];
|
|
2560
|
+
} else {
|
|
2561
|
+
const [{ count }] = await query.count("* as count");
|
|
2562
|
+
return [{
|
|
2563
|
+
group: "total",
|
|
2564
|
+
count: count ? Number(count) : 0
|
|
2565
|
+
}];
|
|
2421
2566
|
}
|
|
2422
|
-
query = applyAccessControl(table, context.user, query);
|
|
2423
|
-
const runningQuery = query.clone().whereIn("status", ["active", "waiting", "delayed", "paused"]);
|
|
2424
|
-
const [{ runningCount }] = await runningQuery.count("* as runningCount");
|
|
2425
|
-
const erroredQuery = query.clone().whereIn("status", ["failed", "stuck"]);
|
|
2426
|
-
const [{ erroredCount }] = await erroredQuery.count("* as erroredCount");
|
|
2427
|
-
const completedQuery = query.clone().where("status", "completed");
|
|
2428
|
-
const [{ completedCount }] = await completedQuery.count("* as completedCount");
|
|
2429
|
-
const failedQuery = query.clone().where("status", "failed");
|
|
2430
|
-
const [{ failedCount }] = await failedQuery.count("* as failedCount");
|
|
2431
|
-
const durationQuery = query.clone().where("status", "completed").whereNotNull("duration").select(db3.raw('AVG("duration") as averageDuration'));
|
|
2432
|
-
const [{ averageDuration }] = await durationQuery;
|
|
2433
|
-
return {
|
|
2434
|
-
runningCount: Number(runningCount),
|
|
2435
|
-
erroredCount: Number(erroredCount),
|
|
2436
|
-
completedCount: Number(completedCount),
|
|
2437
|
-
failedCount: Number(failedCount),
|
|
2438
|
-
averageDuration: averageDuration ? Number(averageDuration) : 0
|
|
2439
|
-
};
|
|
2440
2567
|
}
|
|
2441
|
-
}
|
|
2568
|
+
}
|
|
2442
2569
|
};
|
|
2443
2570
|
}
|
|
2444
2571
|
var RBACResolver = async (db3, table, entityName, resourceId, rights_mode) => {
|
|
@@ -2458,6 +2585,16 @@ var RBACResolver = async (db3, table, entityName, resourceId, rights_mode) => {
|
|
|
2458
2585
|
};
|
|
2459
2586
|
};
|
|
2460
2587
|
function createSDL(tables) {
|
|
2588
|
+
tables.forEach((table) => {
|
|
2589
|
+
table.fields.push({
|
|
2590
|
+
name: "createdAt",
|
|
2591
|
+
type: "date"
|
|
2592
|
+
});
|
|
2593
|
+
table.fields.push({
|
|
2594
|
+
name: "updatedAt",
|
|
2595
|
+
type: "date"
|
|
2596
|
+
});
|
|
2597
|
+
});
|
|
2461
2598
|
console.log("[EXULU] Creating SDL");
|
|
2462
2599
|
let typeDefs = `
|
|
2463
2600
|
scalar JSON
|
|
@@ -2514,7 +2651,6 @@ function createSDL(tables) {
|
|
|
2514
2651
|
${tableNamePlural}Pagination(limit: Int, page: Int, filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingularUpperCaseFirst}PaginationResult
|
|
2515
2652
|
${tableNameSingular}One(filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingular}
|
|
2516
2653
|
${tableNamePlural}Statistics(filters: [Filter${tableNameSingularUpperCaseFirst}], groupBy: String): [StatisticsResult]!
|
|
2517
|
-
${tableNamePlural === "jobs" ? `jobStatistics(user: ID, agent: String, from: String, to: String): JobStatistics` : ""}
|
|
2518
2654
|
`;
|
|
2519
2655
|
mutationDefs += `
|
|
2520
2656
|
${tableNamePlural}CreateOne(input: ${tableNameSingular}Input!): ${tableNameSingular}
|
|
@@ -2539,17 +2675,6 @@ type PageInfo {
|
|
|
2539
2675
|
hasNextPage: Boolean!
|
|
2540
2676
|
}
|
|
2541
2677
|
`;
|
|
2542
|
-
if (tableNamePlural === "jobs") {
|
|
2543
|
-
modelDefs += `
|
|
2544
|
-
type JobStatistics {
|
|
2545
|
-
runningCount: Int!
|
|
2546
|
-
erroredCount: Int!
|
|
2547
|
-
completedCount: Int!
|
|
2548
|
-
failedCount: Int!
|
|
2549
|
-
averageDuration: Float!
|
|
2550
|
-
}
|
|
2551
|
-
`;
|
|
2552
|
-
}
|
|
2553
2678
|
Object.assign(resolvers.Query, createQueries(table));
|
|
2554
2679
|
Object.assign(resolvers.Mutation, createMutations(table));
|
|
2555
2680
|
if (table.RBAC) {
|
|
@@ -2641,6 +2766,10 @@ var agentMessagesSchema = {
|
|
|
2641
2766
|
name: "title",
|
|
2642
2767
|
type: "text"
|
|
2643
2768
|
},
|
|
2769
|
+
{
|
|
2770
|
+
name: "user",
|
|
2771
|
+
type: "number"
|
|
2772
|
+
},
|
|
2644
2773
|
{
|
|
2645
2774
|
name: "session",
|
|
2646
2775
|
type: "text"
|
|
@@ -2877,6 +3006,10 @@ var rolesSchema = {
|
|
|
2877
3006
|
type: "text"
|
|
2878
3007
|
// write | read access to agents
|
|
2879
3008
|
},
|
|
3009
|
+
{
|
|
3010
|
+
name: "api",
|
|
3011
|
+
type: "text"
|
|
3012
|
+
},
|
|
2880
3013
|
{
|
|
2881
3014
|
name: "workflows",
|
|
2882
3015
|
type: "text"
|
|
@@ -2896,8 +3029,8 @@ var rolesSchema = {
|
|
|
2896
3029
|
};
|
|
2897
3030
|
var statisticsSchema = {
|
|
2898
3031
|
name: {
|
|
2899
|
-
plural: "
|
|
2900
|
-
singular: "
|
|
3032
|
+
plural: "tracking",
|
|
3033
|
+
singular: "tracking"
|
|
2901
3034
|
},
|
|
2902
3035
|
fields: [
|
|
2903
3036
|
{
|
|
@@ -2910,11 +3043,20 @@ var statisticsSchema = {
|
|
|
2910
3043
|
},
|
|
2911
3044
|
{
|
|
2912
3045
|
name: "type",
|
|
2913
|
-
type: "
|
|
3046
|
+
type: "enum",
|
|
3047
|
+
enumValues: Object.values(STATISTICS_TYPE_ENUM)
|
|
2914
3048
|
},
|
|
2915
3049
|
{
|
|
2916
3050
|
name: "total",
|
|
2917
3051
|
type: "number"
|
|
3052
|
+
},
|
|
3053
|
+
{
|
|
3054
|
+
name: "user",
|
|
3055
|
+
type: "number"
|
|
3056
|
+
},
|
|
3057
|
+
{
|
|
3058
|
+
name: "role",
|
|
3059
|
+
type: "uuid"
|
|
2918
3060
|
}
|
|
2919
3061
|
]
|
|
2920
3062
|
};
|
|
@@ -2975,6 +3117,7 @@ var jobsSchema = {
|
|
|
2975
3117
|
plural: "jobs",
|
|
2976
3118
|
singular: "job"
|
|
2977
3119
|
},
|
|
3120
|
+
RBAC: true,
|
|
2978
3121
|
fields: [
|
|
2979
3122
|
{
|
|
2980
3123
|
name: "redis",
|
|
@@ -2984,10 +3127,6 @@ var jobsSchema = {
|
|
|
2984
3127
|
name: "session",
|
|
2985
3128
|
type: "text"
|
|
2986
3129
|
},
|
|
2987
|
-
{
|
|
2988
|
-
name: "created_by",
|
|
2989
|
-
type: "number"
|
|
2990
|
-
},
|
|
2991
3130
|
{
|
|
2992
3131
|
name: "status",
|
|
2993
3132
|
type: "text"
|
|
@@ -3113,7 +3252,7 @@ var coreSchemas = {
|
|
|
3113
3252
|
};
|
|
3114
3253
|
|
|
3115
3254
|
// src/registry/uppy.ts
|
|
3116
|
-
var
|
|
3255
|
+
var import_express2 = require("express");
|
|
3117
3256
|
var createUppyRoutes = async (app) => {
|
|
3118
3257
|
const {
|
|
3119
3258
|
S3Client,
|
|
@@ -3587,7 +3726,7 @@ var createExpressRoutes = async (app, agents, tools, contexts) => {
|
|
|
3587
3726
|
optionsSuccessStatus: 200
|
|
3588
3727
|
// some legacy browsers (IE11, various SmartTVs) choke on 204
|
|
3589
3728
|
};
|
|
3590
|
-
app.use(
|
|
3729
|
+
app.use(import_express4.default.json({ limit: REQUEST_SIZE_LIMIT }));
|
|
3591
3730
|
app.use((0, import_cors.default)(corsOptions));
|
|
3592
3731
|
app.use(import_body_parser.default.urlencoded({ extended: true, limit: REQUEST_SIZE_LIMIT }));
|
|
3593
3732
|
app.use(import_body_parser.default.json({ limit: REQUEST_SIZE_LIMIT }));
|
|
@@ -3630,11 +3769,8 @@ var createExpressRoutes = async (app, agents, tools, contexts) => {
|
|
|
3630
3769
|
{ route: "/agents/:id", method: "GET", note: "Get specific agent" },
|
|
3631
3770
|
{ route: "/contexts", method: "GET", note: "List all contexts" },
|
|
3632
3771
|
{ route: "/contexts/:id", method: "GET", note: "Get specific context" },
|
|
3633
|
-
{ route: "/contexts/statistics", method: "GET", note: "Get context statistics" },
|
|
3634
3772
|
{ route: "/tools", method: "GET", note: "List all tools" },
|
|
3635
3773
|
{ route: "/tools/:id", method: "GET", note: "Get specific tool" },
|
|
3636
|
-
{ route: "/statistics/timeseries", method: "POST", note: "Get time series statistics" },
|
|
3637
|
-
{ route: "/statistics/totals", method: "POST", note: "Get totals statistics" },
|
|
3638
3774
|
{ route: "/items/:context", method: "POST", note: "Create new item in context" },
|
|
3639
3775
|
{ route: "/items/:context", method: "GET", note: "Get items from context" },
|
|
3640
3776
|
{ route: "/items/export/:context", method: "GET", note: "Export items from context" },
|
|
@@ -3672,7 +3808,7 @@ var createExpressRoutes = async (app, agents, tools, contexts) => {
|
|
|
3672
3808
|
app.use(
|
|
3673
3809
|
"/graphql",
|
|
3674
3810
|
(0, import_cors.default)(corsOptions),
|
|
3675
|
-
|
|
3811
|
+
import_express4.default.json({ limit: REQUEST_SIZE_LIMIT }),
|
|
3676
3812
|
(0, import_express5.expressMiddleware)(server, {
|
|
3677
3813
|
context: async ({ req }) => {
|
|
3678
3814
|
const authenticationResult = await requestValidators.authenticate(req);
|
|
@@ -4167,115 +4303,6 @@ var createExpressRoutes = async (app, agents, tools, contexts) => {
|
|
|
4167
4303
|
authenticated: true
|
|
4168
4304
|
});
|
|
4169
4305
|
});
|
|
4170
|
-
console.log("[EXULU] statistics timeseries");
|
|
4171
|
-
app.post("/statistics/timeseries", async (req, res) => {
|
|
4172
|
-
const authenticationResult = await requestValidators.authenticate(req);
|
|
4173
|
-
if (!authenticationResult.user?.id) {
|
|
4174
|
-
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
4175
|
-
return;
|
|
4176
|
-
}
|
|
4177
|
-
const { db: db3 } = await postgresClient();
|
|
4178
|
-
const type = req.body.type;
|
|
4179
|
-
if (!Object.values(STATISTICS_TYPE_ENUM).includes(type)) {
|
|
4180
|
-
res.status(400).json({
|
|
4181
|
-
message: "Invalid type, must be one of: " + Object.values(STATISTICS_TYPE_ENUM).join(", ")
|
|
4182
|
-
});
|
|
4183
|
-
return;
|
|
4184
|
-
}
|
|
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
|
-
const query = db3.from("statistics").select("*");
|
|
4192
|
-
query.where("name", "count");
|
|
4193
|
-
query.andWhere("type", type);
|
|
4194
|
-
query.andWhere("createdAt", ">=", from);
|
|
4195
|
-
query.andWhere("createdAt", "<=", to);
|
|
4196
|
-
const results = await query;
|
|
4197
|
-
const dates = [];
|
|
4198
|
-
for (let i = 0; i < (to.getTime() - from.getTime()) / (1e3 * 60 * 60 * 24); i++) {
|
|
4199
|
-
dates.push(new Date(from.getTime() + i * (1e3 * 60 * 60 * 24)));
|
|
4200
|
-
}
|
|
4201
|
-
const data = dates.map((date) => {
|
|
4202
|
-
const result = results.find((result2) => result2.date === date);
|
|
4203
|
-
if (result) {
|
|
4204
|
-
return result;
|
|
4205
|
-
}
|
|
4206
|
-
return {
|
|
4207
|
-
date,
|
|
4208
|
-
count: 0
|
|
4209
|
-
};
|
|
4210
|
-
});
|
|
4211
|
-
res.status(200).json({
|
|
4212
|
-
data,
|
|
4213
|
-
filter: {
|
|
4214
|
-
from,
|
|
4215
|
-
to
|
|
4216
|
-
}
|
|
4217
|
-
});
|
|
4218
|
-
});
|
|
4219
|
-
console.log("[EXULU] statistics totals");
|
|
4220
|
-
app.post("/statistics/totals", async (req, res) => {
|
|
4221
|
-
const authenticationResult = await requestValidators.authenticate(req);
|
|
4222
|
-
if (!authenticationResult.user?.id) {
|
|
4223
|
-
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
4224
|
-
return;
|
|
4225
|
-
}
|
|
4226
|
-
const { db: db3 } = await postgresClient();
|
|
4227
|
-
let from = new Date(req.body.from);
|
|
4228
|
-
let to = new Date(req.body.to);
|
|
4229
|
-
if (!from || !to) {
|
|
4230
|
-
from = new Date(Date.now() - 7 * 24 * 60 * 60 * 1e3);
|
|
4231
|
-
to = /* @__PURE__ */ new Date();
|
|
4232
|
-
}
|
|
4233
|
-
let promises2 = Object.values(STATISTICS_TYPE_ENUM).map(async (type) => {
|
|
4234
|
-
const result = await db3.from("statistics").where("name", "count").andWhere("type", type).andWhere("createdAt", ">=", from).andWhere("createdAt", "<=", to).sum("total as total");
|
|
4235
|
-
return {
|
|
4236
|
-
[type]: result[0]?.total || 0
|
|
4237
|
-
};
|
|
4238
|
-
});
|
|
4239
|
-
const results = await Promise.all(promises2);
|
|
4240
|
-
res.status(200).json({
|
|
4241
|
-
data: { ...Object.assign({}, ...results) },
|
|
4242
|
-
filter: {
|
|
4243
|
-
from,
|
|
4244
|
-
to
|
|
4245
|
-
}
|
|
4246
|
-
});
|
|
4247
|
-
});
|
|
4248
|
-
console.log("[EXULU] contexts statistics");
|
|
4249
|
-
app.get("/contexts/statistics", async (req, res) => {
|
|
4250
|
-
const authenticationResult = await requestValidators.authenticate(req);
|
|
4251
|
-
if (!authenticationResult.user?.id) {
|
|
4252
|
-
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
4253
|
-
return;
|
|
4254
|
-
}
|
|
4255
|
-
const { db: db3 } = await postgresClient();
|
|
4256
|
-
const statistics = await db3("statistics").where("name", "count").andWhere("type", "context.retrieve").sum("total as total").first();
|
|
4257
|
-
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) => ({
|
|
4258
|
-
jobs: rows
|
|
4259
|
-
}));
|
|
4260
|
-
let jobs = [];
|
|
4261
|
-
if (response[0]) {
|
|
4262
|
-
jobs = response[0].jobs.map((job) => ({
|
|
4263
|
-
date: job.id,
|
|
4264
|
-
count: job.count
|
|
4265
|
-
}));
|
|
4266
|
-
}
|
|
4267
|
-
const embeddingsCountResult = await db3("jobs").where("type", "embedder").count("* as count").first();
|
|
4268
|
-
res.status(200).json({
|
|
4269
|
-
active: contexts.filter((context) => context.active).length,
|
|
4270
|
-
inactive: contexts.filter((context) => !context.active).length,
|
|
4271
|
-
sources: contexts.reduce((acc, context) => acc + context.sources.get().length, 0),
|
|
4272
|
-
queries: statistics?.total || 0,
|
|
4273
|
-
jobs,
|
|
4274
|
-
totals: {
|
|
4275
|
-
embeddings: embeddingsCountResult?.count || 0
|
|
4276
|
-
}
|
|
4277
|
-
});
|
|
4278
|
-
});
|
|
4279
4306
|
console.log("[EXULU] context by id");
|
|
4280
4307
|
app.get(`/contexts/:id`, async (req, res) => {
|
|
4281
4308
|
const authenticationResult = await requestValidators.authenticate(req);
|
|
@@ -4446,7 +4473,11 @@ var createExpressRoutes = async (app, agents, tools, contexts) => {
|
|
|
4446
4473
|
return;
|
|
4447
4474
|
}
|
|
4448
4475
|
}
|
|
4449
|
-
const
|
|
4476
|
+
const headers = {
|
|
4477
|
+
stream: req.headers["stream"] === "true" || false,
|
|
4478
|
+
user: req.headers["user"] || null,
|
|
4479
|
+
session: req.headers["session"] || null
|
|
4480
|
+
};
|
|
4450
4481
|
const requestValidationResult = requestValidators.agents(req);
|
|
4451
4482
|
if (requestValidationResult.error) {
|
|
4452
4483
|
res.status(requestValidationResult.code || 500).json({ detail: `${requestValidationResult.message}` });
|
|
@@ -4457,6 +4488,13 @@ var createExpressRoutes = async (app, agents, tools, contexts) => {
|
|
|
4457
4488
|
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
4458
4489
|
return;
|
|
4459
4490
|
}
|
|
4491
|
+
const user = authenticationResult.user;
|
|
4492
|
+
if (user.type !== "api" && !user.super_admin && req.body.resourceId !== user.id) {
|
|
4493
|
+
res.status(400).json({
|
|
4494
|
+
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."
|
|
4495
|
+
});
|
|
4496
|
+
return;
|
|
4497
|
+
}
|
|
4460
4498
|
console.log("[EXULU] agent tools", agentInstance.tools);
|
|
4461
4499
|
const enabledTools = agentInstance.tools.map(({ config, toolId }) => tools.find(({ id }) => id === toolId)).filter(Boolean);
|
|
4462
4500
|
console.log("[EXULU] enabled tools", enabledTools);
|
|
@@ -4479,9 +4517,15 @@ var createExpressRoutes = async (app, agents, tools, contexts) => {
|
|
|
4479
4517
|
const bytes = import_crypto_js3.default.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
|
|
4480
4518
|
providerApiKey = bytes.toString(import_crypto_js3.default.enc.Utf8);
|
|
4481
4519
|
}
|
|
4482
|
-
if (!!stream) {
|
|
4483
|
-
|
|
4484
|
-
|
|
4520
|
+
if (!!headers.stream) {
|
|
4521
|
+
await agent.generateStream({
|
|
4522
|
+
express: {
|
|
4523
|
+
res,
|
|
4524
|
+
req
|
|
4525
|
+
},
|
|
4526
|
+
user: headers.user,
|
|
4527
|
+
session: headers.session,
|
|
4528
|
+
message: req.body.message,
|
|
4485
4529
|
tools: enabledTools,
|
|
4486
4530
|
providerApiKey,
|
|
4487
4531
|
toolConfigs: agentInstance.tools,
|
|
@@ -4490,11 +4534,12 @@ var createExpressRoutes = async (app, agents, tools, contexts) => {
|
|
|
4490
4534
|
trigger: "agent"
|
|
4491
4535
|
}
|
|
4492
4536
|
});
|
|
4493
|
-
result.pipeDataStreamToResponse(res);
|
|
4494
4537
|
return;
|
|
4495
4538
|
} else {
|
|
4496
4539
|
const response = await agent.generateSync({
|
|
4497
|
-
|
|
4540
|
+
user: headers.user,
|
|
4541
|
+
session: headers.session,
|
|
4542
|
+
message: req.body.message,
|
|
4498
4543
|
tools: enabledTools.map((tool2) => tool2.tool),
|
|
4499
4544
|
providerApiKey,
|
|
4500
4545
|
toolConfigs: agentInstance.tools,
|
|
@@ -4516,7 +4561,7 @@ var createExpressRoutes = async (app, agents, tools, contexts) => {
|
|
|
4516
4561
|
console.log("Routes:");
|
|
4517
4562
|
console.table(routeLogs);
|
|
4518
4563
|
const TARGET_API = "https://api.anthropic.com";
|
|
4519
|
-
app.use("/gateway/anthropic/:id",
|
|
4564
|
+
app.use("/gateway/anthropic/:id", import_express4.default.raw({ type: "*/*", limit: REQUEST_SIZE_LIMIT }), async (req, res) => {
|
|
4520
4565
|
const path3 = req.url;
|
|
4521
4566
|
const url = `${TARGET_API}${path3}`;
|
|
4522
4567
|
try {
|
|
@@ -4861,7 +4906,7 @@ var import_node_crypto = require("crypto");
|
|
|
4861
4906
|
var import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
|
|
4862
4907
|
var import_types = require("@modelcontextprotocol/sdk/types.js");
|
|
4863
4908
|
var import_zod3 = require("zod");
|
|
4864
|
-
var
|
|
4909
|
+
var import_express6 = require("express");
|
|
4865
4910
|
var SESSION_ID_HEADER = "mcp-session-id";
|
|
4866
4911
|
var ExuluMCP = class {
|
|
4867
4912
|
server;
|
|
@@ -4999,7 +5044,7 @@ ${code}`
|
|
|
4999
5044
|
};
|
|
5000
5045
|
|
|
5001
5046
|
// src/registry/index.ts
|
|
5002
|
-
var
|
|
5047
|
+
var import_express8 = __toESM(require("express"), 1);
|
|
5003
5048
|
|
|
5004
5049
|
// src/templates/agents/claude-code.ts
|
|
5005
5050
|
var agentId = "0832-5178-1145-2194";
|
|
@@ -5019,7 +5064,6 @@ var defaultAgent = new ExuluAgent({
|
|
|
5019
5064
|
description: `Basic agent without any defined tools, that can support MCP's.`,
|
|
5020
5065
|
type: "agent",
|
|
5021
5066
|
capabilities: {
|
|
5022
|
-
tools: false,
|
|
5023
5067
|
images: [],
|
|
5024
5068
|
files: [],
|
|
5025
5069
|
audio: [],
|
|
@@ -5031,10 +5075,10 @@ var defaultAgent = new ExuluAgent({
|
|
|
5031
5075
|
instructions: "You are a helpful assistant.",
|
|
5032
5076
|
model: {
|
|
5033
5077
|
create: ({ apiKey }) => {
|
|
5034
|
-
const
|
|
5078
|
+
const anthropic = (0, import_anthropic.createAnthropic)({
|
|
5035
5079
|
apiKey
|
|
5036
5080
|
});
|
|
5037
|
-
return
|
|
5081
|
+
return anthropic.languageModel("claude-4-opus-20250514");
|
|
5038
5082
|
}
|
|
5039
5083
|
// todo add a field of type string that adds a dropdown list from which the user can select the model
|
|
5040
5084
|
// todo for each model, check which provider is used, and require the admin to add one or multiple
|
|
@@ -5080,7 +5124,7 @@ var ExuluApp = class {
|
|
|
5080
5124
|
];
|
|
5081
5125
|
this._queues = [...new Set(queues2.filter((o) => !!o))];
|
|
5082
5126
|
if (!this._expressApp) {
|
|
5083
|
-
this._expressApp = (0,
|
|
5127
|
+
this._expressApp = (0, import_express8.default)();
|
|
5084
5128
|
await this.server.express.init();
|
|
5085
5129
|
console.log("[EXULU] Express app initialized.");
|
|
5086
5130
|
}
|
|
@@ -6575,6 +6619,13 @@ var ExuluJobs = {
|
|
|
6575
6619
|
var db2 = {
|
|
6576
6620
|
init: async () => {
|
|
6577
6621
|
await execute();
|
|
6622
|
+
},
|
|
6623
|
+
api: {
|
|
6624
|
+
key: {
|
|
6625
|
+
generate: async (name, email) => {
|
|
6626
|
+
return await generateApiKey(name, email);
|
|
6627
|
+
}
|
|
6628
|
+
}
|
|
6578
6629
|
}
|
|
6579
6630
|
};
|
|
6580
6631
|
var ExuluChunkers = {
|