@exulu/backend 0.3.13 → 1.2.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/.github/workflows/release.yml +38 -0
- package/.husky/commit-msg +1 -0
- package/CHANGELOG.md +45 -0
- package/README.md +152 -47
- package/commitlint.config.js +4 -0
- package/dist/index.cjs +799 -459
- package/dist/index.d.cts +46 -42
- package/dist/index.d.ts +46 -42
- package/dist/index.js +799 -458
- package/package.json +98 -88
- package/release.config.cjs +9 -0
- package/types/models/user.ts +1 -0
- package/git-conventional-commits.yaml +0 -43
- package/types/models/embedder-backend.ts +0 -15
package/dist/index.js
CHANGED
|
@@ -102,6 +102,47 @@ async function postgresClient() {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
// src/postgres/core-schema.ts
|
|
105
|
+
var agentMessagesSchema = {
|
|
106
|
+
name: {
|
|
107
|
+
plural: "agent_messages",
|
|
108
|
+
singular: "agent_message"
|
|
109
|
+
},
|
|
110
|
+
fields: [
|
|
111
|
+
{
|
|
112
|
+
name: "content",
|
|
113
|
+
type: "text"
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
name: "title",
|
|
117
|
+
type: "text"
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: "session",
|
|
121
|
+
type: "text"
|
|
122
|
+
}
|
|
123
|
+
]
|
|
124
|
+
};
|
|
125
|
+
var agentSessionsSchema = {
|
|
126
|
+
name: {
|
|
127
|
+
plural: "agent_sessions",
|
|
128
|
+
singular: "agent_session"
|
|
129
|
+
},
|
|
130
|
+
fields: [
|
|
131
|
+
{
|
|
132
|
+
name: "agent",
|
|
133
|
+
type: "uuid"
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: "user",
|
|
137
|
+
// next auth stores users with id type SERIAL, so we need to use number
|
|
138
|
+
type: "number"
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: "title",
|
|
142
|
+
type: "text"
|
|
143
|
+
}
|
|
144
|
+
]
|
|
145
|
+
};
|
|
105
146
|
var usersSchema = {
|
|
106
147
|
name: {
|
|
107
148
|
plural: "users",
|
|
@@ -159,14 +200,13 @@ var usersSchema = {
|
|
|
159
200
|
name: "last_used",
|
|
160
201
|
type: "date"
|
|
161
202
|
},
|
|
203
|
+
{
|
|
204
|
+
name: "anthropic_token",
|
|
205
|
+
type: "text"
|
|
206
|
+
},
|
|
162
207
|
{
|
|
163
208
|
name: "role",
|
|
164
|
-
type: "
|
|
165
|
-
references: {
|
|
166
|
-
table: "roles",
|
|
167
|
-
field: "id",
|
|
168
|
-
onDelete: "CASCADE"
|
|
169
|
-
}
|
|
209
|
+
type: "uuid"
|
|
170
210
|
}
|
|
171
211
|
]
|
|
172
212
|
};
|
|
@@ -212,10 +252,6 @@ var statisticsSchema = {
|
|
|
212
252
|
{
|
|
213
253
|
name: "total",
|
|
214
254
|
type: "number"
|
|
215
|
-
},
|
|
216
|
-
{
|
|
217
|
-
name: "timeseries",
|
|
218
|
-
type: "json"
|
|
219
255
|
}
|
|
220
256
|
]
|
|
221
257
|
};
|
|
@@ -271,11 +307,11 @@ var evalResultsSchema = {
|
|
|
271
307
|
},
|
|
272
308
|
{
|
|
273
309
|
name: "agent_id",
|
|
274
|
-
type: "
|
|
310
|
+
type: "uuid"
|
|
275
311
|
},
|
|
276
312
|
{
|
|
277
313
|
name: "workflow_id",
|
|
278
|
-
type: "
|
|
314
|
+
type: "uuid"
|
|
279
315
|
},
|
|
280
316
|
{
|
|
281
317
|
name: "eval_type",
|
|
@@ -291,50 +327,6 @@ var evalResultsSchema = {
|
|
|
291
327
|
}
|
|
292
328
|
]
|
|
293
329
|
};
|
|
294
|
-
var threadsSchema = {
|
|
295
|
-
name: {
|
|
296
|
-
plural: "threads",
|
|
297
|
-
singular: "thread"
|
|
298
|
-
},
|
|
299
|
-
fields: [
|
|
300
|
-
{
|
|
301
|
-
name: "resourceId",
|
|
302
|
-
type: "text"
|
|
303
|
-
},
|
|
304
|
-
{
|
|
305
|
-
name: "title",
|
|
306
|
-
type: "text"
|
|
307
|
-
},
|
|
308
|
-
{
|
|
309
|
-
name: "metadata",
|
|
310
|
-
type: "text"
|
|
311
|
-
}
|
|
312
|
-
]
|
|
313
|
-
};
|
|
314
|
-
var messagesSchema = {
|
|
315
|
-
name: {
|
|
316
|
-
plural: "messages",
|
|
317
|
-
singular: "message"
|
|
318
|
-
},
|
|
319
|
-
fields: [
|
|
320
|
-
{
|
|
321
|
-
name: "thread_id",
|
|
322
|
-
type: "text"
|
|
323
|
-
},
|
|
324
|
-
{
|
|
325
|
-
name: "content",
|
|
326
|
-
type: "text"
|
|
327
|
-
},
|
|
328
|
-
{
|
|
329
|
-
name: "role",
|
|
330
|
-
type: "text"
|
|
331
|
-
},
|
|
332
|
-
{
|
|
333
|
-
name: "type",
|
|
334
|
-
type: "text"
|
|
335
|
-
}
|
|
336
|
-
]
|
|
337
|
-
};
|
|
338
330
|
var jobsSchema = {
|
|
339
331
|
name: {
|
|
340
332
|
plural: "jobs",
|
|
@@ -367,19 +359,20 @@ var jobsSchema = {
|
|
|
367
359
|
},
|
|
368
360
|
{
|
|
369
361
|
name: "agent",
|
|
370
|
-
type: "
|
|
362
|
+
type: "uuid"
|
|
371
363
|
},
|
|
372
364
|
{
|
|
373
365
|
name: "workflow",
|
|
374
|
-
type: "
|
|
366
|
+
type: "uuid"
|
|
375
367
|
},
|
|
376
368
|
{
|
|
377
369
|
name: "user",
|
|
378
|
-
type
|
|
370
|
+
// next auth stores users with id type SERIAL, so we need to use number
|
|
371
|
+
type: "number"
|
|
379
372
|
},
|
|
380
373
|
{
|
|
381
374
|
name: "item",
|
|
382
|
-
type: "
|
|
375
|
+
type: "uuid"
|
|
383
376
|
},
|
|
384
377
|
{
|
|
385
378
|
name: "steps",
|
|
@@ -473,7 +466,7 @@ var mapType = (t, type, name, defaultValue) => {
|
|
|
473
466
|
return;
|
|
474
467
|
}
|
|
475
468
|
if (type === "date") {
|
|
476
|
-
t.
|
|
469
|
+
t.timestamp(name);
|
|
477
470
|
return;
|
|
478
471
|
}
|
|
479
472
|
if (type === "uuid") {
|
|
@@ -543,23 +536,44 @@ var generateApiKey = async (name, email) => {
|
|
|
543
536
|
|
|
544
537
|
// src/postgres/init-db.ts
|
|
545
538
|
var up = async function(knex) {
|
|
539
|
+
if (!await knex.schema.hasTable("agent_sessions")) {
|
|
540
|
+
await knex.schema.createTable("agent_sessions", (table) => {
|
|
541
|
+
table.uuid("id").primary().defaultTo(knex.fn.uuid());
|
|
542
|
+
table.timestamp("createdAt").defaultTo(knex.fn.now());
|
|
543
|
+
table.timestamp("updatedAt").defaultTo(knex.fn.now());
|
|
544
|
+
for (const field of agentSessionsSchema.fields) {
|
|
545
|
+
const { type, name, default: defaultValue } = field;
|
|
546
|
+
if (!type || !name) {
|
|
547
|
+
continue;
|
|
548
|
+
}
|
|
549
|
+
mapType(table, type, sanitizeName(name), defaultValue);
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
if (!await knex.schema.hasTable("agent_messages")) {
|
|
554
|
+
await knex.schema.createTable("agent_messages", (table) => {
|
|
555
|
+
table.uuid("id").primary().defaultTo(knex.fn.uuid());
|
|
556
|
+
table.timestamp("createdAt").defaultTo(knex.fn.now());
|
|
557
|
+
table.timestamp("updatedAt").defaultTo(knex.fn.now());
|
|
558
|
+
for (const field of agentMessagesSchema.fields) {
|
|
559
|
+
const { type, name, default: defaultValue } = field;
|
|
560
|
+
if (!type || !name) {
|
|
561
|
+
continue;
|
|
562
|
+
}
|
|
563
|
+
mapType(table, type, sanitizeName(name), defaultValue);
|
|
564
|
+
}
|
|
565
|
+
});
|
|
566
|
+
}
|
|
546
567
|
if (!await knex.schema.hasTable("roles")) {
|
|
547
568
|
await knex.schema.createTable("roles", (table) => {
|
|
548
569
|
table.uuid("id").primary().defaultTo(knex.fn.uuid());
|
|
549
|
-
table.
|
|
550
|
-
table.
|
|
570
|
+
table.timestamp("createdAt").defaultTo(knex.fn.now());
|
|
571
|
+
table.timestamp("updatedAt").defaultTo(knex.fn.now());
|
|
551
572
|
for (const field of rolesSchema.fields) {
|
|
552
|
-
const { type, name,
|
|
573
|
+
const { type, name, default: defaultValue } = field;
|
|
553
574
|
if (!type || !name) {
|
|
554
575
|
continue;
|
|
555
576
|
}
|
|
556
|
-
if (type === "reference") {
|
|
557
|
-
if (!references) {
|
|
558
|
-
throw new Error("Field with type reference must have a reference definition.");
|
|
559
|
-
}
|
|
560
|
-
table.uuid(name).references(references.field).inTable(references.table);
|
|
561
|
-
return;
|
|
562
|
-
}
|
|
563
577
|
mapType(table, type, sanitizeName(name), defaultValue);
|
|
564
578
|
}
|
|
565
579
|
});
|
|
@@ -567,20 +581,13 @@ var up = async function(knex) {
|
|
|
567
581
|
if (!await knex.schema.hasTable("eval_results")) {
|
|
568
582
|
await knex.schema.createTable("eval_results", (table) => {
|
|
569
583
|
table.uuid("id").primary().defaultTo(knex.fn.uuid());
|
|
570
|
-
table.
|
|
571
|
-
table.
|
|
584
|
+
table.timestamp("createdAt").defaultTo(knex.fn.now());
|
|
585
|
+
table.timestamp("updatedAt").defaultTo(knex.fn.now());
|
|
572
586
|
for (const field of evalResultsSchema.fields) {
|
|
573
|
-
const { type, name,
|
|
587
|
+
const { type, name, default: defaultValue } = field;
|
|
574
588
|
if (!type || !name) {
|
|
575
589
|
continue;
|
|
576
590
|
}
|
|
577
|
-
if (type === "reference") {
|
|
578
|
-
if (!references) {
|
|
579
|
-
throw new Error("Field with type reference must have a reference definition.");
|
|
580
|
-
}
|
|
581
|
-
table.uuid(name).references(references.field).inTable(references.table);
|
|
582
|
-
return;
|
|
583
|
-
}
|
|
584
591
|
mapType(table, type, sanitizeName(name), defaultValue);
|
|
585
592
|
}
|
|
586
593
|
});
|
|
@@ -588,62 +595,41 @@ var up = async function(knex) {
|
|
|
588
595
|
if (!await knex.schema.hasTable("statistics")) {
|
|
589
596
|
await knex.schema.createTable("statistics", (table) => {
|
|
590
597
|
table.uuid("id").primary().defaultTo(knex.fn.uuid());
|
|
591
|
-
table.
|
|
592
|
-
table.
|
|
598
|
+
table.timestamp("createdAt").defaultTo(knex.fn.now());
|
|
599
|
+
table.timestamp("updatedAt").defaultTo(knex.fn.now());
|
|
593
600
|
for (const field of statisticsSchema.fields) {
|
|
594
|
-
const { type, name,
|
|
601
|
+
const { type, name, default: defaultValue } = field;
|
|
595
602
|
if (!type || !name) {
|
|
596
603
|
continue;
|
|
597
604
|
}
|
|
598
|
-
if (type === "reference") {
|
|
599
|
-
if (!references) {
|
|
600
|
-
throw new Error("Field with type reference must have a reference definition.");
|
|
601
|
-
}
|
|
602
|
-
table.uuid(name).references(references.field).inTable(references.table);
|
|
603
|
-
return;
|
|
604
|
-
}
|
|
605
605
|
mapType(table, type, sanitizeName(name), defaultValue);
|
|
606
606
|
}
|
|
607
607
|
});
|
|
608
608
|
}
|
|
609
609
|
if (!await knex.schema.hasTable("jobs")) {
|
|
610
610
|
await knex.schema.createTable("jobs", (table) => {
|
|
611
|
-
table.
|
|
612
|
-
table.
|
|
613
|
-
table.
|
|
611
|
+
table.uuid("id").primary().defaultTo(knex.fn.uuid());
|
|
612
|
+
table.timestamp("createdAt").defaultTo(knex.fn.now());
|
|
613
|
+
table.timestamp("updatedAt").defaultTo(knex.fn.now());
|
|
614
614
|
for (const field of jobsSchema.fields) {
|
|
615
|
-
const { type, name,
|
|
615
|
+
const { type, name, default: defaultValue } = field;
|
|
616
616
|
if (!type || !name) {
|
|
617
617
|
continue;
|
|
618
618
|
}
|
|
619
|
-
if (type === "reference") {
|
|
620
|
-
if (!references) {
|
|
621
|
-
throw new Error("Field with type reference must have a reference definition.");
|
|
622
|
-
}
|
|
623
|
-
table.uuid(name).references(references.field).inTable(references.table);
|
|
624
|
-
return;
|
|
625
|
-
}
|
|
626
619
|
mapType(table, type, sanitizeName(name), defaultValue);
|
|
627
620
|
}
|
|
628
621
|
});
|
|
629
622
|
}
|
|
630
623
|
if (!await knex.schema.hasTable("agents")) {
|
|
631
624
|
await knex.schema.createTable("agents", (table) => {
|
|
632
|
-
table.
|
|
633
|
-
table.
|
|
634
|
-
table.
|
|
625
|
+
table.uuid("id").primary().defaultTo(knex.fn.uuid());
|
|
626
|
+
table.timestamp("createdAt").defaultTo(knex.fn.now());
|
|
627
|
+
table.timestamp("updatedAt").defaultTo(knex.fn.now());
|
|
635
628
|
for (const field of agentsSchema.fields) {
|
|
636
|
-
const { type, name,
|
|
629
|
+
const { type, name, default: defaultValue } = field;
|
|
637
630
|
if (!type || !name) {
|
|
638
631
|
continue;
|
|
639
632
|
}
|
|
640
|
-
if (type === "reference") {
|
|
641
|
-
if (!references) {
|
|
642
|
-
throw new Error("Field with type reference must have a reference definition.");
|
|
643
|
-
}
|
|
644
|
-
table.uuid(name).references(references.field).inTable(references.table);
|
|
645
|
-
return;
|
|
646
|
-
}
|
|
647
633
|
mapType(table, type, sanitizeName(name), defaultValue);
|
|
648
634
|
}
|
|
649
635
|
});
|
|
@@ -659,8 +645,8 @@ var up = async function(knex) {
|
|
|
659
645
|
if (!await knex.schema.hasTable("users")) {
|
|
660
646
|
await knex.schema.createTable("users", (table) => {
|
|
661
647
|
table.increments("id").primary();
|
|
662
|
-
table.
|
|
663
|
-
table.
|
|
648
|
+
table.timestamp("createdAt").defaultTo(knex.fn.now());
|
|
649
|
+
table.timestamp("updatedAt").defaultTo(knex.fn.now());
|
|
664
650
|
table.string("name", 255);
|
|
665
651
|
table.string("password", 255);
|
|
666
652
|
table.string("email", 255);
|
|
@@ -668,20 +654,13 @@ var up = async function(knex) {
|
|
|
668
654
|
table.text("image");
|
|
669
655
|
for (const field of usersSchema.fields) {
|
|
670
656
|
console.log("[EXULU] field", field);
|
|
671
|
-
const { type, name,
|
|
657
|
+
const { type, name, default: defaultValue } = field;
|
|
672
658
|
if (name === "id" || name === "name" || name === "email" || name === "emailVerified" || name === "image") {
|
|
673
659
|
continue;
|
|
674
660
|
}
|
|
675
661
|
if (!type || !name) {
|
|
676
662
|
continue;
|
|
677
663
|
}
|
|
678
|
-
if (type === "reference") {
|
|
679
|
-
if (!references) {
|
|
680
|
-
throw new Error("Field with type reference must have a reference definition.");
|
|
681
|
-
}
|
|
682
|
-
table.uuid(name).references(references.field).inTable(references.table);
|
|
683
|
-
return;
|
|
684
|
-
}
|
|
685
664
|
mapType(table, type, sanitizeName(name), defaultValue);
|
|
686
665
|
}
|
|
687
666
|
});
|
|
@@ -702,14 +681,6 @@ var up = async function(knex) {
|
|
|
702
681
|
table.text("token_type");
|
|
703
682
|
});
|
|
704
683
|
}
|
|
705
|
-
if (!await knex.schema.hasTable("sessions")) {
|
|
706
|
-
await knex.schema.createTable("sessions", (table) => {
|
|
707
|
-
table.increments("id").primary();
|
|
708
|
-
table.integer("userId").notNullable();
|
|
709
|
-
table.timestamp("expires", { useTz: true }).notNullable();
|
|
710
|
-
table.string("sessionToken", 255).notNullable();
|
|
711
|
-
});
|
|
712
|
-
}
|
|
713
684
|
};
|
|
714
685
|
var execute = async () => {
|
|
715
686
|
console.log("[EXULU] Initializing database.");
|
|
@@ -960,6 +931,35 @@ var ExuluEvalUtils = {
|
|
|
960
931
|
};
|
|
961
932
|
|
|
962
933
|
// src/registry/classes.ts
|
|
934
|
+
function sanitizeToolName(name) {
|
|
935
|
+
if (typeof name !== "string") return "";
|
|
936
|
+
let sanitized = name.replace(/[^a-zA-Z0-9_-]+/g, "_");
|
|
937
|
+
sanitized = sanitized.replace(/^_+|_+$/g, "");
|
|
938
|
+
if (sanitized.length > 128) {
|
|
939
|
+
sanitized = sanitized.substring(0, 128);
|
|
940
|
+
}
|
|
941
|
+
return sanitized;
|
|
942
|
+
}
|
|
943
|
+
var convertToolsArrayToObject = (tools) => {
|
|
944
|
+
if (!tools) return {};
|
|
945
|
+
const sanitizedTools = tools ? tools.map((tool2) => ({
|
|
946
|
+
...tool2,
|
|
947
|
+
name: sanitizeToolName(tool2.name)
|
|
948
|
+
})) : [];
|
|
949
|
+
const askForConfirmation = {
|
|
950
|
+
description: "Ask the user for confirmation.",
|
|
951
|
+
parameters: z.object({
|
|
952
|
+
message: z.string().describe("The message to ask for confirmation.")
|
|
953
|
+
})
|
|
954
|
+
};
|
|
955
|
+
return {
|
|
956
|
+
...sanitizedTools?.reduce(
|
|
957
|
+
(prev, cur) => ({ ...prev, [cur.name]: cur.tool }),
|
|
958
|
+
{}
|
|
959
|
+
),
|
|
960
|
+
askForConfirmation
|
|
961
|
+
};
|
|
962
|
+
};
|
|
963
963
|
function generateSlug(name) {
|
|
964
964
|
const normalized = name.normalize("NFKD").replace(/[\u0300-\u036f]/g, "");
|
|
965
965
|
const lowercase = normalized.toLowerCase();
|
|
@@ -986,54 +986,119 @@ var ExuluAgent = class {
|
|
|
986
986
|
name;
|
|
987
987
|
description = "";
|
|
988
988
|
slug = "";
|
|
989
|
+
type;
|
|
989
990
|
streaming = false;
|
|
990
991
|
rateLimit;
|
|
991
992
|
config;
|
|
992
993
|
// private memory: Memory | undefined; // TODO do own implementation
|
|
993
|
-
tools;
|
|
994
994
|
evals;
|
|
995
995
|
model;
|
|
996
996
|
capabilities;
|
|
997
|
-
constructor({ id, name, description, config, rateLimit, capabilities,
|
|
997
|
+
constructor({ id, name, description, config, rateLimit, capabilities, type, evals }) {
|
|
998
998
|
this.id = id;
|
|
999
999
|
this.name = name;
|
|
1000
1000
|
this.evals = evals;
|
|
1001
1001
|
this.description = description;
|
|
1002
1002
|
this.rateLimit = rateLimit;
|
|
1003
|
-
this.tools = tools;
|
|
1004
1003
|
this.config = config;
|
|
1005
|
-
this.
|
|
1004
|
+
this.type = type;
|
|
1005
|
+
this.capabilities = capabilities || {
|
|
1006
|
+
images: [],
|
|
1007
|
+
files: [],
|
|
1008
|
+
audio: [],
|
|
1009
|
+
video: []
|
|
1010
|
+
};
|
|
1006
1011
|
this.slug = `/agents/${generateSlug(this.name)}/run`;
|
|
1007
|
-
this.model = this.config
|
|
1012
|
+
this.model = this.config?.model;
|
|
1008
1013
|
}
|
|
1009
|
-
|
|
1014
|
+
// Exports the agent as a tool that can be used by another agent
|
|
1015
|
+
// todo test this
|
|
1016
|
+
tool = () => {
|
|
1017
|
+
return new ExuluTool({
|
|
1018
|
+
id: this.id,
|
|
1019
|
+
name: `${this.name} agent`,
|
|
1020
|
+
type: "agent",
|
|
1021
|
+
inputSchema: z.object({
|
|
1022
|
+
prompt: z.string()
|
|
1023
|
+
}),
|
|
1024
|
+
description: `A function that calls an AI agent named: ${this.name}. The agent does the following: ${this.description}.`,
|
|
1025
|
+
execute: async ({ prompt }) => {
|
|
1026
|
+
return await this.generateSync({
|
|
1027
|
+
prompt,
|
|
1028
|
+
statistics: {
|
|
1029
|
+
label: "",
|
|
1030
|
+
trigger: "tool"
|
|
1031
|
+
}
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
1034
|
+
});
|
|
1035
|
+
};
|
|
1036
|
+
generateSync = async ({ messages, prompt, tools, statistics }) => {
|
|
1010
1037
|
if (!this.model) {
|
|
1011
1038
|
throw new Error("Model is required for streaming.");
|
|
1012
1039
|
}
|
|
1013
|
-
if (this.config
|
|
1014
|
-
|
|
1015
|
-
}
|
|
1016
|
-
const { object } = await generateObject({
|
|
1017
|
-
model: this.model,
|
|
1018
|
-
schema: this.config.outputSchema,
|
|
1019
|
-
prompt
|
|
1020
|
-
});
|
|
1021
|
-
return object;
|
|
1040
|
+
if (!this.config) {
|
|
1041
|
+
throw new Error("Config is required for generating.");
|
|
1022
1042
|
}
|
|
1023
|
-
if (
|
|
1024
|
-
|
|
1025
|
-
model: this.model,
|
|
1026
|
-
prompt
|
|
1027
|
-
});
|
|
1028
|
-
const text2 = await result.text;
|
|
1029
|
-
return text2;
|
|
1043
|
+
if (prompt && messages) {
|
|
1044
|
+
throw new Error("Prompt and messages cannot be provided at the same time.");
|
|
1030
1045
|
}
|
|
1031
1046
|
const { text } = await generateText({
|
|
1032
1047
|
model: this.model,
|
|
1033
|
-
|
|
1048
|
+
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.",
|
|
1049
|
+
messages,
|
|
1050
|
+
prompt,
|
|
1051
|
+
maxRetries: 2,
|
|
1052
|
+
tools: convertToolsArrayToObject(tools),
|
|
1053
|
+
maxSteps: 5
|
|
1034
1054
|
});
|
|
1055
|
+
if (statistics) {
|
|
1056
|
+
await updateStatistic({
|
|
1057
|
+
name: "count",
|
|
1058
|
+
label: statistics.label,
|
|
1059
|
+
type: STATISTICS_TYPE_ENUM.AGENT_RUN,
|
|
1060
|
+
trigger: statistics.trigger,
|
|
1061
|
+
count: 1
|
|
1062
|
+
});
|
|
1063
|
+
}
|
|
1035
1064
|
return text;
|
|
1036
1065
|
};
|
|
1066
|
+
generateStream = ({ messages, prompt, tools, statistics }) => {
|
|
1067
|
+
if (!this.model) {
|
|
1068
|
+
throw new Error("Model is required for streaming.");
|
|
1069
|
+
}
|
|
1070
|
+
if (!this.config) {
|
|
1071
|
+
throw new Error("Config is required for generating.");
|
|
1072
|
+
}
|
|
1073
|
+
if (prompt && messages) {
|
|
1074
|
+
throw new Error("Prompt and messages cannot be provided at the same time.");
|
|
1075
|
+
}
|
|
1076
|
+
return streamText({
|
|
1077
|
+
model: this.model,
|
|
1078
|
+
messages,
|
|
1079
|
+
prompt,
|
|
1080
|
+
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.",
|
|
1081
|
+
maxRetries: 2,
|
|
1082
|
+
tools: convertToolsArrayToObject(tools),
|
|
1083
|
+
maxSteps: 5,
|
|
1084
|
+
onError: (error) => console.error("[EXULU] chat stream error.", error),
|
|
1085
|
+
onFinish: async ({ response, usage }) => {
|
|
1086
|
+
console.info(
|
|
1087
|
+
"[EXULU] chat stream finished.",
|
|
1088
|
+
usage
|
|
1089
|
+
);
|
|
1090
|
+
if (statistics) {
|
|
1091
|
+
await updateStatistic({
|
|
1092
|
+
name: "count",
|
|
1093
|
+
label: statistics.label,
|
|
1094
|
+
type: STATISTICS_TYPE_ENUM.AGENT_RUN,
|
|
1095
|
+
trigger: statistics.trigger,
|
|
1096
|
+
count: 1
|
|
1097
|
+
});
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
});
|
|
1101
|
+
};
|
|
1037
1102
|
};
|
|
1038
1103
|
var ExuluEmbedder = class {
|
|
1039
1104
|
id;
|
|
@@ -1056,6 +1121,13 @@ var ExuluEmbedder = class {
|
|
|
1056
1121
|
}
|
|
1057
1122
|
async generateFromQuery(query, statistics) {
|
|
1058
1123
|
if (statistics) {
|
|
1124
|
+
await updateStatistic({
|
|
1125
|
+
name: "count",
|
|
1126
|
+
label: statistics.label,
|
|
1127
|
+
type: STATISTICS_TYPE_ENUM.EMBEDDER_GENERATE,
|
|
1128
|
+
trigger: statistics.trigger,
|
|
1129
|
+
count: 1
|
|
1130
|
+
});
|
|
1059
1131
|
}
|
|
1060
1132
|
return await this.generateEmbeddings({
|
|
1061
1133
|
item: {
|
|
@@ -1069,6 +1141,13 @@ var ExuluEmbedder = class {
|
|
|
1069
1141
|
}
|
|
1070
1142
|
async generateFromDocument(input, statistics) {
|
|
1071
1143
|
if (statistics) {
|
|
1144
|
+
await updateStatistic({
|
|
1145
|
+
name: "count",
|
|
1146
|
+
label: statistics.label,
|
|
1147
|
+
type: STATISTICS_TYPE_ENUM.EMBEDDER_GENERATE,
|
|
1148
|
+
trigger: statistics.trigger,
|
|
1149
|
+
count: 1
|
|
1150
|
+
});
|
|
1072
1151
|
}
|
|
1073
1152
|
if (!this.chunker) {
|
|
1074
1153
|
throw new Error("Chunker not found for embedder " + this.name);
|
|
@@ -1257,9 +1336,8 @@ var ExuluEval = class {
|
|
|
1257
1336
|
if (!data.prompt) {
|
|
1258
1337
|
throw new Error("Prompt is required for running an agent.");
|
|
1259
1338
|
}
|
|
1260
|
-
const result = await runner.agent.
|
|
1261
|
-
prompt: data.prompt
|
|
1262
|
-
stream: false
|
|
1339
|
+
const result = await runner.agent.generateSync({
|
|
1340
|
+
prompt: data.prompt
|
|
1263
1341
|
});
|
|
1264
1342
|
data.result = result;
|
|
1265
1343
|
}
|
|
@@ -1354,18 +1432,18 @@ var ExuluTool = class {
|
|
|
1354
1432
|
id;
|
|
1355
1433
|
name;
|
|
1356
1434
|
description;
|
|
1357
|
-
|
|
1435
|
+
inputSchema;
|
|
1358
1436
|
type;
|
|
1359
1437
|
tool;
|
|
1360
|
-
constructor({ id, name, description,
|
|
1438
|
+
constructor({ id, name, description, inputSchema, type, execute: execute2 }) {
|
|
1361
1439
|
this.id = id;
|
|
1362
1440
|
this.name = name;
|
|
1363
1441
|
this.description = description;
|
|
1364
|
-
this.
|
|
1442
|
+
this.inputSchema = inputSchema;
|
|
1365
1443
|
this.type = type;
|
|
1366
1444
|
this.tool = tool({
|
|
1367
1445
|
description,
|
|
1368
|
-
parameters,
|
|
1446
|
+
parameters: inputSchema || z.object({}),
|
|
1369
1447
|
execute: execute2
|
|
1370
1448
|
});
|
|
1371
1449
|
}
|
|
@@ -1421,7 +1499,7 @@ var ExuluContext = class {
|
|
|
1421
1499
|
}
|
|
1422
1500
|
const { db: db2 } = await postgresClient();
|
|
1423
1501
|
Object.keys(item).forEach((key) => {
|
|
1424
|
-
if (key === "name" || key === "description" || key === "external_id" || key === "tags" || key === "source" || key === "textLength" || key === "upsert") {
|
|
1502
|
+
if (key === "name" || key === "description" || key === "external_id" || key === "tags" || key === "source" || key === "textLength" || key === "upsert" || key === "archived") {
|
|
1425
1503
|
return;
|
|
1426
1504
|
}
|
|
1427
1505
|
const field = this.fields.find((field2) => field2.name === key);
|
|
@@ -1468,6 +1546,9 @@ var ExuluContext = class {
|
|
|
1468
1546
|
chunk_index: chunk.index,
|
|
1469
1547
|
embedding: pgvector2.toSql(chunk.vector)
|
|
1470
1548
|
})));
|
|
1549
|
+
await db2.from(this.getTableName()).where({ id }).update({
|
|
1550
|
+
embeddings_updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1551
|
+
}).returning("id");
|
|
1471
1552
|
}
|
|
1472
1553
|
return {
|
|
1473
1554
|
id: result[0].id,
|
|
@@ -1497,7 +1578,7 @@ var ExuluContext = class {
|
|
|
1497
1578
|
}
|
|
1498
1579
|
}
|
|
1499
1580
|
Object.keys(item).forEach((key) => {
|
|
1500
|
-
if (key === "name" || key === "description" || key === "external_id" || key === "tags" || key === "source" || key === "textLength" || key === "upsert") {
|
|
1581
|
+
if (key === "name" || key === "description" || key === "external_id" || key === "tags" || key === "source" || key === "textLength" || key === "upsert" || key === "archived") {
|
|
1501
1582
|
return;
|
|
1502
1583
|
}
|
|
1503
1584
|
const field = this.fields.find((field2) => field2.name === key);
|
|
@@ -1548,6 +1629,9 @@ var ExuluContext = class {
|
|
|
1548
1629
|
chunk_index: chunk.index,
|
|
1549
1630
|
embedding: pgvector2.toSql(chunk.vector)
|
|
1550
1631
|
})));
|
|
1632
|
+
await db2.from(this.getTableName()).where({ id: result[0].id }).update({
|
|
1633
|
+
embeddings_updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1634
|
+
}).returning("id");
|
|
1551
1635
|
}
|
|
1552
1636
|
return {
|
|
1553
1637
|
id: result[0].id,
|
|
@@ -1557,6 +1641,8 @@ var ExuluContext = class {
|
|
|
1557
1641
|
getItems = async ({
|
|
1558
1642
|
statistics,
|
|
1559
1643
|
limit,
|
|
1644
|
+
sort,
|
|
1645
|
+
order,
|
|
1560
1646
|
page,
|
|
1561
1647
|
name,
|
|
1562
1648
|
archived,
|
|
@@ -1577,6 +1663,9 @@ var ExuluContext = class {
|
|
|
1577
1663
|
const columns = await db2(mainTable).columnInfo();
|
|
1578
1664
|
const totalQuery = db2.count("* as count").from(mainTable).first();
|
|
1579
1665
|
const itemsQuery = db2.select(Object.keys(columns).map((column) => mainTable + "." + column)).from(mainTable).offset(offset).limit(limit);
|
|
1666
|
+
if (sort) {
|
|
1667
|
+
itemsQuery.orderBy(sort, order === "desc" ? "desc" : "asc");
|
|
1668
|
+
}
|
|
1580
1669
|
if (typeof name === "string") {
|
|
1581
1670
|
itemsQuery.whereILike("name", `%${name}%`);
|
|
1582
1671
|
totalQuery.whereILike("name", `%${name}%`);
|
|
@@ -1620,7 +1709,7 @@ var ExuluContext = class {
|
|
|
1620
1709
|
}
|
|
1621
1710
|
itemsQuery.limit(limit * 5);
|
|
1622
1711
|
if (statistics) {
|
|
1623
|
-
updateStatistic({
|
|
1712
|
+
await updateStatistic({
|
|
1624
1713
|
name: "count",
|
|
1625
1714
|
label: statistics.label,
|
|
1626
1715
|
type: STATISTICS_TYPE_ENUM.CONTEXT_RETRIEVE,
|
|
@@ -1753,6 +1842,7 @@ var ExuluContext = class {
|
|
|
1753
1842
|
table.text("external_id");
|
|
1754
1843
|
table.integer("textLength");
|
|
1755
1844
|
table.text("source");
|
|
1845
|
+
table.timestamp("embeddings_updated_at");
|
|
1756
1846
|
for (const field of this.fields) {
|
|
1757
1847
|
const { type, name } = field;
|
|
1758
1848
|
if (!type || !name) {
|
|
@@ -1782,15 +1872,15 @@ var ExuluContext = class {
|
|
|
1782
1872
|
id: this.id,
|
|
1783
1873
|
name: `${this.name} context`,
|
|
1784
1874
|
type: "context",
|
|
1785
|
-
|
|
1875
|
+
inputSchema: z.object({
|
|
1786
1876
|
query: z.string()
|
|
1787
1877
|
}),
|
|
1788
1878
|
description: `Gets information from the context called: ${this.name}. The context description is: ${this.description}.`,
|
|
1789
|
-
execute: async ({
|
|
1879
|
+
execute: async ({ query }) => {
|
|
1790
1880
|
return await this.getItems({
|
|
1791
1881
|
page: 1,
|
|
1792
1882
|
limit: 10,
|
|
1793
|
-
query
|
|
1883
|
+
query,
|
|
1794
1884
|
statistics: {
|
|
1795
1885
|
label: this.name,
|
|
1796
1886
|
trigger: "agent"
|
|
@@ -1839,7 +1929,32 @@ var ExuluSource = class {
|
|
|
1839
1929
|
}
|
|
1840
1930
|
};
|
|
1841
1931
|
var updateStatistic = async (statistic) => {
|
|
1842
|
-
|
|
1932
|
+
const currentDate = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
1933
|
+
const { db: db2 } = await postgresClient();
|
|
1934
|
+
const existing = await db2.from("statistics").where({
|
|
1935
|
+
name: statistic.name,
|
|
1936
|
+
label: statistic.label,
|
|
1937
|
+
type: statistic.type,
|
|
1938
|
+
createdAt: currentDate
|
|
1939
|
+
}).first();
|
|
1940
|
+
if (!existing) {
|
|
1941
|
+
await db2.from("statistics").insert({
|
|
1942
|
+
name: statistic.name,
|
|
1943
|
+
label: statistic.label,
|
|
1944
|
+
type: statistic.type,
|
|
1945
|
+
total: statistic.count ?? 1,
|
|
1946
|
+
createdAt: currentDate
|
|
1947
|
+
});
|
|
1948
|
+
} else {
|
|
1949
|
+
await db2.from("statistics").update({
|
|
1950
|
+
total: db2.raw("total + ?", [statistic.count ?? 1])
|
|
1951
|
+
}).where({
|
|
1952
|
+
name: statistic.name,
|
|
1953
|
+
label: statistic.label,
|
|
1954
|
+
type: statistic.type,
|
|
1955
|
+
createdAt: currentDate
|
|
1956
|
+
});
|
|
1957
|
+
}
|
|
1843
1958
|
};
|
|
1844
1959
|
|
|
1845
1960
|
// src/registry/index.ts
|
|
@@ -1939,6 +2054,7 @@ var authentication = async ({
|
|
|
1939
2054
|
}
|
|
1940
2055
|
if (authtoken) {
|
|
1941
2056
|
try {
|
|
2057
|
+
console.log("[EXULU] authtoken", authtoken);
|
|
1942
2058
|
if (!authtoken?.email) {
|
|
1943
2059
|
return {
|
|
1944
2060
|
error: true,
|
|
@@ -2032,8 +2148,7 @@ var requestValidators = {
|
|
|
2032
2148
|
const { db: db2 } = await postgresClient();
|
|
2033
2149
|
let authtoken = null;
|
|
2034
2150
|
if (typeof apikey !== "string") {
|
|
2035
|
-
|
|
2036
|
-
authtoken = await getToken(req.headers["authorization"] ?? "");
|
|
2151
|
+
authtoken = await getToken((req.headers["authorization"] || req.headers["x-api-key"]) ?? "");
|
|
2037
2152
|
}
|
|
2038
2153
|
return await authentication({
|
|
2039
2154
|
authtoken,
|
|
@@ -2226,6 +2341,42 @@ import "reflect-metadata";
|
|
|
2226
2341
|
// src/registry/utils/graphql.ts
|
|
2227
2342
|
import { makeExecutableSchema } from "@graphql-tools/schema";
|
|
2228
2343
|
import GraphQLJSON from "graphql-type-json";
|
|
2344
|
+
import { GraphQLScalarType, Kind } from "graphql";
|
|
2345
|
+
import CryptoJS from "crypto-js";
|
|
2346
|
+
var GraphQLDate = new GraphQLScalarType({
|
|
2347
|
+
name: "Date",
|
|
2348
|
+
description: "Date custom scalar type",
|
|
2349
|
+
serialize(value) {
|
|
2350
|
+
if (value instanceof Date) {
|
|
2351
|
+
return value.toISOString();
|
|
2352
|
+
}
|
|
2353
|
+
if (typeof value === "number") {
|
|
2354
|
+
return new Date(value).toISOString();
|
|
2355
|
+
}
|
|
2356
|
+
if (typeof value === "string") {
|
|
2357
|
+
return new Date(value).toISOString();
|
|
2358
|
+
}
|
|
2359
|
+
return value;
|
|
2360
|
+
},
|
|
2361
|
+
parseValue(value) {
|
|
2362
|
+
if (typeof value === "string") {
|
|
2363
|
+
return new Date(value);
|
|
2364
|
+
}
|
|
2365
|
+
if (typeof value === "number") {
|
|
2366
|
+
return new Date(value);
|
|
2367
|
+
}
|
|
2368
|
+
return value;
|
|
2369
|
+
},
|
|
2370
|
+
parseLiteral(ast) {
|
|
2371
|
+
if (ast.kind === Kind.STRING) {
|
|
2372
|
+
return new Date(ast.value);
|
|
2373
|
+
}
|
|
2374
|
+
if (ast.kind === Kind.INT) {
|
|
2375
|
+
return new Date(parseInt(ast.value, 10));
|
|
2376
|
+
}
|
|
2377
|
+
return null;
|
|
2378
|
+
}
|
|
2379
|
+
});
|
|
2229
2380
|
var map = (field) => {
|
|
2230
2381
|
let type;
|
|
2231
2382
|
switch (field.type) {
|
|
@@ -2245,7 +2396,7 @@ var map = (field) => {
|
|
|
2245
2396
|
type = "JSON";
|
|
2246
2397
|
break;
|
|
2247
2398
|
case "date":
|
|
2248
|
-
type = "
|
|
2399
|
+
type = "Date";
|
|
2249
2400
|
break;
|
|
2250
2401
|
default:
|
|
2251
2402
|
type = "String";
|
|
@@ -2263,8 +2414,8 @@ function createTypeDefs(table) {
|
|
|
2263
2414
|
type ${table.name.singular} {
|
|
2264
2415
|
${fields.join("\n")}
|
|
2265
2416
|
id: ID!
|
|
2266
|
-
createdAt:
|
|
2267
|
-
updatedAt:
|
|
2417
|
+
createdAt: Date!
|
|
2418
|
+
updatedAt: Date!
|
|
2268
2419
|
}
|
|
2269
2420
|
`;
|
|
2270
2421
|
const inputDef = `
|
|
@@ -2290,6 +2441,11 @@ input FilterOperatorString {
|
|
|
2290
2441
|
contains: String
|
|
2291
2442
|
}
|
|
2292
2443
|
|
|
2444
|
+
input FilterOperatorDate {
|
|
2445
|
+
lte: Date
|
|
2446
|
+
gte: Date
|
|
2447
|
+
}
|
|
2448
|
+
|
|
2293
2449
|
input FilterOperatorFloat {
|
|
2294
2450
|
eq: Float
|
|
2295
2451
|
ne: Float
|
|
@@ -2342,8 +2498,10 @@ function createMutations(table) {
|
|
|
2342
2498
|
[`${tableNamePlural}CreateOne`]: async (_, args, context, info) => {
|
|
2343
2499
|
const { db: db2 } = context;
|
|
2344
2500
|
const requestedFields = getRequestedFields(info);
|
|
2501
|
+
let { input } = args;
|
|
2502
|
+
input = encryptSensitiveFields(input);
|
|
2345
2503
|
const results = await db2(tableNamePlural).insert({
|
|
2346
|
-
...
|
|
2504
|
+
...input,
|
|
2347
2505
|
createdAt: /* @__PURE__ */ new Date(),
|
|
2348
2506
|
updatedAt: /* @__PURE__ */ new Date()
|
|
2349
2507
|
}).returning(requestedFields);
|
|
@@ -2351,7 +2509,8 @@ function createMutations(table) {
|
|
|
2351
2509
|
},
|
|
2352
2510
|
[`${tableNamePlural}UpdateOne`]: async (_, args, context, info) => {
|
|
2353
2511
|
const { db: db2 } = context;
|
|
2354
|
-
|
|
2512
|
+
let { where, input } = args;
|
|
2513
|
+
input = encryptSensitiveFields(input);
|
|
2355
2514
|
await db2(tableNamePlural).where(where).update({
|
|
2356
2515
|
...input,
|
|
2357
2516
|
updatedAt: /* @__PURE__ */ new Date()
|
|
@@ -2361,7 +2520,8 @@ function createMutations(table) {
|
|
|
2361
2520
|
return result;
|
|
2362
2521
|
},
|
|
2363
2522
|
[`${tableNamePlural}UpdateOneById`]: async (_, args, context, info) => {
|
|
2364
|
-
|
|
2523
|
+
let { id, input } = args;
|
|
2524
|
+
input = encryptSensitiveFields(input);
|
|
2365
2525
|
const { db: db2 } = context;
|
|
2366
2526
|
await db2(tableNamePlural).where({ id }).update({
|
|
2367
2527
|
...input,
|
|
@@ -2464,12 +2624,44 @@ function createQueries(table) {
|
|
|
2464
2624
|
},
|
|
2465
2625
|
items
|
|
2466
2626
|
};
|
|
2467
|
-
}
|
|
2627
|
+
},
|
|
2628
|
+
// Add jobStatistics query for jobs table
|
|
2629
|
+
...tableNamePlural === "jobs" ? {
|
|
2630
|
+
jobStatistics: async (_, args, context, info) => {
|
|
2631
|
+
const { user, agent, from, to } = args;
|
|
2632
|
+
const { db: db2 } = context;
|
|
2633
|
+
let query = db2("jobs");
|
|
2634
|
+
if (user) {
|
|
2635
|
+
query = query.where("user", user);
|
|
2636
|
+
}
|
|
2637
|
+
if (agent) {
|
|
2638
|
+
query = query.where("agent", agent);
|
|
2639
|
+
}
|
|
2640
|
+
if (from) {
|
|
2641
|
+
query = query.where("createdAt", ">=", from);
|
|
2642
|
+
}
|
|
2643
|
+
if (to) {
|
|
2644
|
+
query = query.where("createdAt", "<=", to);
|
|
2645
|
+
}
|
|
2646
|
+
const completedQuery = query.clone().where("status", "completed");
|
|
2647
|
+
const [{ completedCount }] = await completedQuery.count("* as completedCount");
|
|
2648
|
+
const failedQuery = query.clone().where("status", "failed");
|
|
2649
|
+
const [{ failedCount }] = await failedQuery.count("* as failedCount");
|
|
2650
|
+
const durationQuery = query.clone().where("status", "completed").whereNotNull("duration").select(db2.raw('AVG("duration") as averageDuration'));
|
|
2651
|
+
const [{ averageDuration }] = await durationQuery;
|
|
2652
|
+
return {
|
|
2653
|
+
completedCount: Number(completedCount),
|
|
2654
|
+
failedCount: Number(failedCount),
|
|
2655
|
+
averageDuration: averageDuration ? Number(averageDuration) : 0
|
|
2656
|
+
};
|
|
2657
|
+
}
|
|
2658
|
+
} : {}
|
|
2468
2659
|
};
|
|
2469
2660
|
}
|
|
2470
2661
|
function createSDL(tables) {
|
|
2471
2662
|
let typeDefs = `
|
|
2472
2663
|
scalar JSON
|
|
2664
|
+
scalar Date
|
|
2473
2665
|
|
|
2474
2666
|
type Query {
|
|
2475
2667
|
`;
|
|
@@ -2477,7 +2669,7 @@ function createSDL(tables) {
|
|
|
2477
2669
|
type Mutation {
|
|
2478
2670
|
`;
|
|
2479
2671
|
let modelDefs = "";
|
|
2480
|
-
const resolvers = { JSON: GraphQLJSON, Query: {}, Mutation: {} };
|
|
2672
|
+
const resolvers = { JSON: GraphQLJSON, Date: GraphQLDate, Query: {}, Mutation: {} };
|
|
2481
2673
|
for (const table of tables) {
|
|
2482
2674
|
const tableNamePlural = table.name.plural.toLowerCase();
|
|
2483
2675
|
const tableNameSingular = table.name.singular.toLowerCase();
|
|
@@ -2486,6 +2678,7 @@ function createSDL(tables) {
|
|
|
2486
2678
|
${tableNameSingular}ById(id: ID!): ${tableNameSingular}
|
|
2487
2679
|
${tableNamePlural}Pagination(limit: Int, page: Int, filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingularUpperCaseFirst}PaginationResult
|
|
2488
2680
|
${tableNameSingular}One(filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingular}
|
|
2681
|
+
${tableNamePlural === "jobs" ? `jobStatistics(user: ID, agent: String, from: String, to: String): JobStatistics` : ""}
|
|
2489
2682
|
`;
|
|
2490
2683
|
mutationDefs += `
|
|
2491
2684
|
${tableNamePlural}CreateOne(input: ${tableNameSingular}Input!): ${tableNameSingular}
|
|
@@ -2510,6 +2703,15 @@ type PageInfo {
|
|
|
2510
2703
|
hasNextPage: Boolean!
|
|
2511
2704
|
}
|
|
2512
2705
|
`;
|
|
2706
|
+
if (tableNamePlural === "jobs") {
|
|
2707
|
+
modelDefs += `
|
|
2708
|
+
type JobStatistics {
|
|
2709
|
+
completedCount: Int!
|
|
2710
|
+
failedCount: Int!
|
|
2711
|
+
averageDuration: Float!
|
|
2712
|
+
}
|
|
2713
|
+
`;
|
|
2714
|
+
}
|
|
2513
2715
|
Object.assign(resolvers.Query, createQueries(table));
|
|
2514
2716
|
Object.assign(resolvers.Mutation, createMutations(table));
|
|
2515
2717
|
}
|
|
@@ -2546,6 +2748,15 @@ type PageInfo {
|
|
|
2546
2748
|
console.log("\n");
|
|
2547
2749
|
return schema;
|
|
2548
2750
|
}
|
|
2751
|
+
var sensitiveFields = ["anthropic_token"];
|
|
2752
|
+
var encryptSensitiveFields = (input) => {
|
|
2753
|
+
sensitiveFields.forEach((field) => {
|
|
2754
|
+
if (input[field]) {
|
|
2755
|
+
input[field] = CryptoJS.AES.encrypt(input[field], process.env.NEXTAUTH_SECRET).toString();
|
|
2756
|
+
}
|
|
2757
|
+
});
|
|
2758
|
+
return input;
|
|
2759
|
+
};
|
|
2549
2760
|
|
|
2550
2761
|
// src/registry/routes.ts
|
|
2551
2762
|
import { expressMiddleware } from "@as-integrations/express5";
|
|
@@ -2944,6 +3155,34 @@ var createUppyRoutes = async (app) => {
|
|
|
2944
3155
|
// src/registry/routes.ts
|
|
2945
3156
|
import { InMemoryLRUCache } from "@apollo/utils.keyvaluecache";
|
|
2946
3157
|
import bodyParser from "body-parser";
|
|
3158
|
+
import CryptoJS2 from "crypto-js";
|
|
3159
|
+
|
|
3160
|
+
// src/registry/utils/claude-messages.ts
|
|
3161
|
+
var CLAUDE_MESSAGES = {
|
|
3162
|
+
authentication_error: `
|
|
3163
|
+
\x1B[41m -- Authentication error please check your IMP token and try again. --
|
|
3164
|
+
\x1B[0m`,
|
|
3165
|
+
missing_body: `
|
|
3166
|
+
\x1B[41m -- Missing body Anthropic response. --
|
|
3167
|
+
\x1B[0m`,
|
|
3168
|
+
missing_nextauth_secret: `
|
|
3169
|
+
\x1B[41m -- Missing NEXTAUTH_SECRET in environment variables on the server. --
|
|
3170
|
+
\x1B[0m`,
|
|
3171
|
+
not_enabled: `
|
|
3172
|
+
\x1B[31m
|
|
3173
|
+
\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557
|
|
3174
|
+
\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u255A\u2588\u2588\u2557\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
|
|
3175
|
+
\u2588\u2588\u2588\u2588\u2588\u2557 \u255A\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
|
|
3176
|
+
\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
|
|
3177
|
+
\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2554\u255D \u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D
|
|
3178
|
+
\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D
|
|
3179
|
+
Intelligence Management Platform
|
|
3180
|
+
\x1B[0m
|
|
3181
|
+
\x1B[41m -- Your account has not been enabled to use Claude Code, please contact your admin or enable Claude Code in the user settings. --
|
|
3182
|
+
\x1B[0m`
|
|
3183
|
+
};
|
|
3184
|
+
|
|
3185
|
+
// src/registry/routes.ts
|
|
2947
3186
|
var REQUEST_SIZE_LIMIT = "50mb";
|
|
2948
3187
|
var global_queues = {
|
|
2949
3188
|
logs_cleaner: "logs-cleaner"
|
|
@@ -2980,7 +3219,7 @@ var createRecurringJobs = async () => {
|
|
|
2980
3219
|
console.table(recurringJobSchedulersLogs);
|
|
2981
3220
|
return queue;
|
|
2982
3221
|
};
|
|
2983
|
-
var createExpressRoutes = async (app, agents,
|
|
3222
|
+
var createExpressRoutes = async (app, agents, tools, workflows, contexts) => {
|
|
2984
3223
|
const routeLogs = [];
|
|
2985
3224
|
var corsOptions = {
|
|
2986
3225
|
origin: "*",
|
|
@@ -3000,6 +3239,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3000
3239
|
\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
|
|
3001
3240
|
\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2554\u255D \u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D
|
|
3002
3241
|
\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D
|
|
3242
|
+
Intelligence Management Platform
|
|
3003
3243
|
|
|
3004
3244
|
`);
|
|
3005
3245
|
console.log("Agents:");
|
|
@@ -3048,7 +3288,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3048
3288
|
} else {
|
|
3049
3289
|
console.log("===========================", "[EXULU] no redis server configured, not setting up recurring jobs.", "===========================");
|
|
3050
3290
|
}
|
|
3051
|
-
const schema = createSDL([usersSchema, rolesSchema, agentsSchema, jobsSchema, workflowSchema, evalResultsSchema,
|
|
3291
|
+
const schema = createSDL([usersSchema, rolesSchema, agentsSchema, jobsSchema, workflowSchema, evalResultsSchema, agentSessionsSchema, agentMessagesSchema]);
|
|
3052
3292
|
console.log("[EXULU] graphql server");
|
|
3053
3293
|
const server = new ApolloServer({
|
|
3054
3294
|
cache: new InMemoryLRUCache(),
|
|
@@ -3076,10 +3316,52 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3076
3316
|
}
|
|
3077
3317
|
})
|
|
3078
3318
|
);
|
|
3079
|
-
app.get(`/
|
|
3319
|
+
app.get(`/providers`, async (req, res) => {
|
|
3320
|
+
const authenticationResult = await requestValidators.authenticate(req);
|
|
3321
|
+
if (!authenticationResult.user?.id) {
|
|
3322
|
+
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
3323
|
+
return;
|
|
3324
|
+
}
|
|
3080
3325
|
res.status(200).json(agents);
|
|
3081
3326
|
});
|
|
3327
|
+
app.get(`/agents`, async (req, res) => {
|
|
3328
|
+
const authenticationResult = await requestValidators.authenticate(req);
|
|
3329
|
+
if (!authenticationResult.user?.id) {
|
|
3330
|
+
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
3331
|
+
return;
|
|
3332
|
+
}
|
|
3333
|
+
const { db: db2 } = await postgresClient();
|
|
3334
|
+
const agentsFromDb = await db2.from("agents").select("*");
|
|
3335
|
+
res.status(200).json(agentsFromDb.map((agent) => {
|
|
3336
|
+
const backend = agents.find((a) => a.id === agent.backend);
|
|
3337
|
+
if (!backend) {
|
|
3338
|
+
return null;
|
|
3339
|
+
}
|
|
3340
|
+
return {
|
|
3341
|
+
name: agent.name,
|
|
3342
|
+
id: agent.id,
|
|
3343
|
+
description: agent.description,
|
|
3344
|
+
provider: backend?.model?.provider,
|
|
3345
|
+
model: backend?.model?.modelId,
|
|
3346
|
+
active: agent.active,
|
|
3347
|
+
public: agent.public,
|
|
3348
|
+
type: agent.type,
|
|
3349
|
+
slug: backend?.slug,
|
|
3350
|
+
rateLimit: backend?.rateLimit,
|
|
3351
|
+
streaming: backend?.streaming,
|
|
3352
|
+
capabilities: backend?.capabilities,
|
|
3353
|
+
// todo add contexts
|
|
3354
|
+
availableTools: tools,
|
|
3355
|
+
enabledTools: agent.tools
|
|
3356
|
+
};
|
|
3357
|
+
}).filter(Boolean));
|
|
3358
|
+
});
|
|
3082
3359
|
app.get(`/agents/:id`, async (req, res) => {
|
|
3360
|
+
const authenticationResult = await requestValidators.authenticate(req);
|
|
3361
|
+
if (!authenticationResult.user?.id) {
|
|
3362
|
+
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
3363
|
+
return;
|
|
3364
|
+
}
|
|
3083
3365
|
const { db: db2 } = await postgresClient();
|
|
3084
3366
|
const id = req.params.id;
|
|
3085
3367
|
if (!id) {
|
|
@@ -3102,6 +3384,8 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3102
3384
|
name: agent.name,
|
|
3103
3385
|
id: agent.id,
|
|
3104
3386
|
description: agent.description,
|
|
3387
|
+
provider: backend?.model?.provider,
|
|
3388
|
+
model: backend?.model?.modelId,
|
|
3105
3389
|
active: agent.active,
|
|
3106
3390
|
public: agent.public,
|
|
3107
3391
|
type: agent.type,
|
|
@@ -3110,7 +3394,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3110
3394
|
streaming: backend?.streaming,
|
|
3111
3395
|
capabilities: backend?.capabilities,
|
|
3112
3396
|
// todo add contexts
|
|
3113
|
-
availableTools:
|
|
3397
|
+
availableTools: tools,
|
|
3114
3398
|
enabledTools: agent.tools
|
|
3115
3399
|
}
|
|
3116
3400
|
});
|
|
@@ -3121,8 +3405,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3121
3405
|
name: tool2.name,
|
|
3122
3406
|
description: tool2.description,
|
|
3123
3407
|
type: tool2.type || "tool",
|
|
3124
|
-
inputSchema: tool2.inputSchema ? zerialize(tool2.inputSchema) : null
|
|
3125
|
-
outputSchema: tool2.outputSchema ? zerialize(tool2.outputSchema) : null
|
|
3408
|
+
inputSchema: tool2.inputSchema ? zerialize(tool2.inputSchema) : null
|
|
3126
3409
|
})));
|
|
3127
3410
|
});
|
|
3128
3411
|
app.get("/tools/:id", async (req, res) => {
|
|
@@ -3240,6 +3523,14 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3240
3523
|
});
|
|
3241
3524
|
return;
|
|
3242
3525
|
}
|
|
3526
|
+
const itemsTableExists = await context.tableExists();
|
|
3527
|
+
if (!itemsTableExists) {
|
|
3528
|
+
await context.createItemsTable();
|
|
3529
|
+
}
|
|
3530
|
+
const chunksTableExists = await db2.schema.hasTable(context.getChunksTableName());
|
|
3531
|
+
if (!chunksTableExists) {
|
|
3532
|
+
await context.createChunksTable();
|
|
3533
|
+
}
|
|
3243
3534
|
const item = await db2.from(context.getTableName()).where({ id: req.params.id }).select("*").first();
|
|
3244
3535
|
if (!item) {
|
|
3245
3536
|
res.status(404).json({
|
|
@@ -3361,6 +3652,20 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3361
3652
|
}
|
|
3362
3653
|
let limit = req.query.limit ? parseInt(req.query.limit) : 10;
|
|
3363
3654
|
let page = req.query.page ? parseInt(req.query.page) : 1;
|
|
3655
|
+
let sort = req.query.sort ? req.query.sort : "created_at";
|
|
3656
|
+
let order = req.query.order ? req.query.order : "desc";
|
|
3657
|
+
if (sort && !["created_at", "embeddings_updated_at"].includes(sort)) {
|
|
3658
|
+
res.status(400).json({
|
|
3659
|
+
message: "Invalid sort field, must be one of: createdAt, embeddings_updated_at"
|
|
3660
|
+
});
|
|
3661
|
+
return;
|
|
3662
|
+
}
|
|
3663
|
+
if (order && !["desc", "asc"].includes(order)) {
|
|
3664
|
+
res.status(400).json({
|
|
3665
|
+
message: "Invalid order, must be one of: desc, asc"
|
|
3666
|
+
});
|
|
3667
|
+
return;
|
|
3668
|
+
}
|
|
3364
3669
|
const authenticationResult = await requestValidators.authenticate(req);
|
|
3365
3670
|
if (!authenticationResult.user?.id) {
|
|
3366
3671
|
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
@@ -3384,6 +3689,8 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3384
3689
|
return;
|
|
3385
3690
|
}
|
|
3386
3691
|
const result = await context.getItems({
|
|
3692
|
+
sort,
|
|
3693
|
+
order,
|
|
3387
3694
|
page,
|
|
3388
3695
|
limit,
|
|
3389
3696
|
archived: req.query.archived === "true",
|
|
@@ -3463,6 +3770,18 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3463
3770
|
message: "Embedding deleted."
|
|
3464
3771
|
});
|
|
3465
3772
|
});
|
|
3773
|
+
app.get("/ping", async (req, res) => {
|
|
3774
|
+
const authenticationResult = await requestValidators.authenticate(req);
|
|
3775
|
+
if (!authenticationResult.user?.id) {
|
|
3776
|
+
res.status(200).json({
|
|
3777
|
+
authenticated: false
|
|
3778
|
+
});
|
|
3779
|
+
return;
|
|
3780
|
+
}
|
|
3781
|
+
res.status(200).json({
|
|
3782
|
+
authenticated: true
|
|
3783
|
+
});
|
|
3784
|
+
});
|
|
3466
3785
|
console.log("[EXULU] statistics timeseries");
|
|
3467
3786
|
app.post("/statistics/timeseries", async (req, res) => {
|
|
3468
3787
|
const authenticationResult = await requestValidators.authenticate(req);
|
|
@@ -3602,6 +3921,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3602
3921
|
slug: "/contexts/" + context.id,
|
|
3603
3922
|
active: context.active,
|
|
3604
3923
|
fields: context.fields,
|
|
3924
|
+
configuration: context.configuration,
|
|
3605
3925
|
sources: context.sources.get().map((source) => ({
|
|
3606
3926
|
id: source.id,
|
|
3607
3927
|
name: source.name,
|
|
@@ -3796,47 +4116,28 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3796
4116
|
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
3797
4117
|
return;
|
|
3798
4118
|
}
|
|
4119
|
+
console.log("[EXULU] agent tools", agentInstance.tools);
|
|
4120
|
+
const enabledTools = agentInstance.tools.map((tool2) => tools.find(({ id }) => id === tool2)).filter(Boolean);
|
|
4121
|
+
console.log("[EXULU] enabled tools", enabledTools);
|
|
3799
4122
|
if (!!stream) {
|
|
3800
|
-
const
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
const { textStream } = await chatClient.stream(req.body.messages, {
|
|
3808
|
-
threadId: `${req.body.threadId}`,
|
|
3809
|
-
// conversation id
|
|
3810
|
-
resourceId: `${req.body.resourceId}`,
|
|
3811
|
-
// user id
|
|
3812
|
-
...agent.outputSchema && { output: agent.outputSchema },
|
|
3813
|
-
maxRetries: 2,
|
|
3814
|
-
// todo make part of ExuluAgent class
|
|
3815
|
-
maxSteps: 5,
|
|
3816
|
-
// todo make part of ExuluAgent class
|
|
3817
|
-
onError: (error) => console.error("[EXULU] chat stream error.", error),
|
|
3818
|
-
onFinish: ({ response, usage }) => console.info(
|
|
3819
|
-
"[EXULU] chat stream finished.",
|
|
3820
|
-
usage
|
|
3821
|
-
)
|
|
4123
|
+
const result = agent.generateStream({
|
|
4124
|
+
messages: req.body.messages,
|
|
4125
|
+
tools: enabledTools,
|
|
4126
|
+
statistics: {
|
|
4127
|
+
label: agent.name,
|
|
4128
|
+
trigger: "agent"
|
|
4129
|
+
}
|
|
3822
4130
|
});
|
|
3823
|
-
|
|
3824
|
-
res.write(`data: ${delta}
|
|
3825
|
-
|
|
3826
|
-
`);
|
|
3827
|
-
}
|
|
3828
|
-
res.end();
|
|
4131
|
+
result.pipeDataStreamToResponse(res);
|
|
3829
4132
|
return;
|
|
3830
4133
|
} else {
|
|
3831
|
-
const response = await agent.
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
maxSteps: 5
|
|
3839
|
-
// todo make part of ExuluAgent class
|
|
4134
|
+
const response = await agent.generateSync({
|
|
4135
|
+
messages: req.body.messages,
|
|
4136
|
+
tools: enabledTools.map(),
|
|
4137
|
+
statistics: {
|
|
4138
|
+
label: agent.name,
|
|
4139
|
+
trigger: "agent"
|
|
4140
|
+
}
|
|
3840
4141
|
});
|
|
3841
4142
|
res.status(200).json(response);
|
|
3842
4143
|
return;
|
|
@@ -3911,7 +4212,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3911
4212
|
console.log("Routes:");
|
|
3912
4213
|
console.table(routeLogs);
|
|
3913
4214
|
const TARGET_API = "https://api.anthropic.com";
|
|
3914
|
-
app.use("/gateway/anthropic", express.raw({ type: "*/*", limit: REQUEST_SIZE_LIMIT }), async (req, res) => {
|
|
4215
|
+
app.use("/gateway/anthropic/:id", express.raw({ type: "*/*", limit: REQUEST_SIZE_LIMIT }), async (req, res) => {
|
|
3915
4216
|
const path3 = req.url;
|
|
3916
4217
|
const url = `${TARGET_API}${path3}`;
|
|
3917
4218
|
console.log("[PROXY] Manual proxy to:", url);
|
|
@@ -3922,57 +4223,100 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3922
4223
|
console.log("[PROXY] Request stream:", req.body.stream);
|
|
3923
4224
|
console.log("[PROXY] Request messages:", req.body.messages?.length);
|
|
3924
4225
|
try {
|
|
3925
|
-
const headers = {
|
|
3926
|
-
"x-api-key": process.env.ANTHROPIC_API_KEY,
|
|
3927
|
-
"anthropic-version": "2023-06-01",
|
|
3928
|
-
"content-type": req.headers["content-type"] || "application/json"
|
|
3929
|
-
};
|
|
3930
|
-
if (req.headers["accept"]) headers["accept"] = req.headers["accept"];
|
|
3931
|
-
if (req.headers["user-agent"]) headers["user-agent"] = req.headers["user-agent"];
|
|
3932
4226
|
console.log("[PROXY] Request body tools array length:", req.body.tools?.length);
|
|
3933
4227
|
if (!req.body.tools) {
|
|
3934
4228
|
req.body.tools = [];
|
|
3935
4229
|
}
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
4230
|
+
const authenticationResult = await requestValidators.authenticate(req);
|
|
4231
|
+
if (!authenticationResult.user?.id) {
|
|
4232
|
+
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
4233
|
+
return;
|
|
4234
|
+
}
|
|
4235
|
+
console.log("[EXULU] authentication result", authenticationResult);
|
|
4236
|
+
const { db: db2 } = await postgresClient();
|
|
4237
|
+
const agent = await db2.from("agents").where({
|
|
4238
|
+
id: req.params.id
|
|
4239
|
+
}).first();
|
|
4240
|
+
if (!agent) {
|
|
4241
|
+
const arrayBuffer = createCustomAnthropicStreamingMessage(`
|
|
4242
|
+
\x1B[41m -- Agent ${req.params.id} not found or you do not have access to it. --
|
|
4243
|
+
\x1B[0m`);
|
|
4244
|
+
res.setHeader("Content-Type", "application/json");
|
|
4245
|
+
res.end(Buffer.from(arrayBuffer));
|
|
4246
|
+
return;
|
|
4247
|
+
}
|
|
4248
|
+
console.log("[EXULU] agent", agent?.name);
|
|
4249
|
+
if (!process.env.NEXTAUTH_SECRET) {
|
|
4250
|
+
const arrayBuffer = createCustomAnthropicStreamingMessage(CLAUDE_MESSAGES.missing_nextauth_secret);
|
|
4251
|
+
res.setHeader("Content-Type", "application/json");
|
|
4252
|
+
res.end(Buffer.from(arrayBuffer));
|
|
3946
4253
|
return;
|
|
3947
4254
|
}
|
|
4255
|
+
if (!authenticationResult.user?.anthropic_token) {
|
|
4256
|
+
const arrayBuffer = createCustomAnthropicStreamingMessage(CLAUDE_MESSAGES.not_enabled);
|
|
4257
|
+
res.setHeader("Content-Type", "application/json");
|
|
4258
|
+
res.end(Buffer.from(arrayBuffer));
|
|
4259
|
+
return;
|
|
4260
|
+
}
|
|
4261
|
+
const bytes = CryptoJS2.AES.decrypt(authenticationResult.user?.anthropic_token, process.env.NEXTAUTH_SECRET);
|
|
4262
|
+
const anthropicApiKey = bytes.toString(CryptoJS2.enc.Utf8);
|
|
4263
|
+
const headers = {
|
|
4264
|
+
"x-api-key": anthropicApiKey,
|
|
4265
|
+
"anthropic-version": "2023-06-01",
|
|
4266
|
+
"content-type": req.headers["content-type"] || "application/json"
|
|
4267
|
+
};
|
|
4268
|
+
if (req.headers["accept"]) headers["accept"] = req.headers["accept"];
|
|
4269
|
+
if (req.headers["user-agent"]) headers["user-agent"] = req.headers["user-agent"];
|
|
4270
|
+
console.log("[EXULU] anthropic api key", anthropicApiKey);
|
|
3948
4271
|
const response = await fetch(url, {
|
|
3949
4272
|
method: req.method,
|
|
3950
4273
|
headers,
|
|
3951
4274
|
body: req.method !== "GET" ? JSON.stringify(req.body) : void 0
|
|
3952
4275
|
});
|
|
3953
|
-
console.log("[PROXY] Response
|
|
4276
|
+
console.log("[PROXY] Response:", response);
|
|
4277
|
+
console.log("[PROXY] Response:", response.body);
|
|
4278
|
+
await updateStatistic({
|
|
4279
|
+
name: "count",
|
|
4280
|
+
label: "Claude Code",
|
|
4281
|
+
type: STATISTICS_TYPE_ENUM.AGENT_RUN,
|
|
4282
|
+
trigger: "claude-code",
|
|
4283
|
+
count: 1
|
|
4284
|
+
});
|
|
3954
4285
|
response.headers.forEach((value, key) => {
|
|
3955
4286
|
res.setHeader(key, value);
|
|
3956
4287
|
});
|
|
3957
4288
|
res.status(response.status);
|
|
3958
|
-
|
|
4289
|
+
const isStreaming = response.headers.get("content-type")?.includes("text/event-stream");
|
|
4290
|
+
if (isStreaming && !response?.body) {
|
|
4291
|
+
const arrayBuffer = createCustomAnthropicStreamingMessage(CLAUDE_MESSAGES.missing_body);
|
|
4292
|
+
res.setHeader("Content-Type", "application/json");
|
|
4293
|
+
res.end(Buffer.from(arrayBuffer));
|
|
4294
|
+
return;
|
|
4295
|
+
}
|
|
4296
|
+
if (isStreaming) {
|
|
3959
4297
|
const reader = response.body.getReader();
|
|
3960
4298
|
const decoder = new TextDecoder();
|
|
3961
4299
|
while (true) {
|
|
3962
4300
|
const { done, value } = await reader.read();
|
|
3963
4301
|
if (done) break;
|
|
3964
4302
|
const chunk = decoder.decode(value, { stream: true });
|
|
4303
|
+
console.log("[PROXY] Chunk:", chunk);
|
|
3965
4304
|
res.write(chunk);
|
|
3966
4305
|
}
|
|
3967
4306
|
res.end();
|
|
3968
|
-
|
|
3969
|
-
const data = await response.arrayBuffer();
|
|
3970
|
-
res.end(Buffer.from(data));
|
|
4307
|
+
return;
|
|
3971
4308
|
}
|
|
4309
|
+
const data = await response.arrayBuffer();
|
|
4310
|
+
console.log("[PROXY] Data:", data);
|
|
4311
|
+
res.end(Buffer.from(data));
|
|
3972
4312
|
} catch (error) {
|
|
3973
4313
|
console.error("[PROXY] Manual proxy error:", error);
|
|
3974
4314
|
if (!res.headersSent) {
|
|
3975
|
-
|
|
4315
|
+
if (error?.message === "Invalid token") {
|
|
4316
|
+
res.status(500).json({ error: "Authentication error, please check your IMP token and try again." });
|
|
4317
|
+
} else {
|
|
4318
|
+
res.status(500).json({ error: error.message });
|
|
4319
|
+
}
|
|
3976
4320
|
}
|
|
3977
4321
|
}
|
|
3978
4322
|
});
|
|
@@ -4020,6 +4364,20 @@ var getPresignedFileUrl = async (key) => {
|
|
|
4020
4364
|
console.log(`[EXULU] presigned url for file with key: ${key}, generated: ${json.url}`);
|
|
4021
4365
|
return json.url;
|
|
4022
4366
|
};
|
|
4367
|
+
var createCustomAnthropicStreamingMessage = (message) => {
|
|
4368
|
+
const responseData = {
|
|
4369
|
+
type: "message",
|
|
4370
|
+
content: [
|
|
4371
|
+
{
|
|
4372
|
+
type: "text",
|
|
4373
|
+
text: message
|
|
4374
|
+
}
|
|
4375
|
+
]
|
|
4376
|
+
};
|
|
4377
|
+
const jsonString = JSON.stringify(responseData);
|
|
4378
|
+
const arrayBuffer = new TextEncoder().encode(jsonString).buffer;
|
|
4379
|
+
return arrayBuffer;
|
|
4380
|
+
};
|
|
4023
4381
|
|
|
4024
4382
|
// src/registry/workers.ts
|
|
4025
4383
|
import IORedis from "ioredis";
|
|
@@ -4072,7 +4430,7 @@ import * as fs2 from "fs";
|
|
|
4072
4430
|
import path2 from "path";
|
|
4073
4431
|
var defaultLogsDir = path2.join(process.cwd(), "logs");
|
|
4074
4432
|
var redisConnection;
|
|
4075
|
-
var createWorkers = async (queues2, contexts,
|
|
4433
|
+
var createWorkers = async (queues2, contexts, workflows, _logsDir) => {
|
|
4076
4434
|
if (!redisServer.host || !redisServer.port) {
|
|
4077
4435
|
console.error("[EXULU] you are trying to start workers, but no redis server is configured in the environment.");
|
|
4078
4436
|
throw new Error("No redis server configured in the environment, so cannot start workers.");
|
|
@@ -4103,7 +4461,7 @@ var createWorkers = async (queues2, contexts, embedders, workflows, _logsDir) =>
|
|
|
4103
4461
|
if (!bullmqJob.data.embedder) {
|
|
4104
4462
|
throw new Error(`No embedder set for embedder job.`);
|
|
4105
4463
|
}
|
|
4106
|
-
const embedder =
|
|
4464
|
+
const embedder = contexts.find((context2) => context2.embedder?.id === bullmqJob.data.embedder);
|
|
4107
4465
|
if (!embedder) {
|
|
4108
4466
|
throw new Error(`Embedder ${bullmqJob.data.embedder} not found in the registry.`);
|
|
4109
4467
|
}
|
|
@@ -4143,6 +4501,9 @@ var createWorkers = async (queues2, contexts, embedders, workflows, _logsDir) =>
|
|
|
4143
4501
|
duration,
|
|
4144
4502
|
result: JSON.stringify(result)
|
|
4145
4503
|
});
|
|
4504
|
+
await db2.from((void 0).getTableName()).where({ id: result[0].id }).update({
|
|
4505
|
+
embeddings_updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
4506
|
+
}).returning("id");
|
|
4146
4507
|
return result;
|
|
4147
4508
|
}
|
|
4148
4509
|
if (bullmqJob.data.type === "workflow") {
|
|
@@ -4240,7 +4601,7 @@ var ExuluMCP = class {
|
|
|
4240
4601
|
express;
|
|
4241
4602
|
constructor() {
|
|
4242
4603
|
}
|
|
4243
|
-
create = async ({ express: express3, contexts,
|
|
4604
|
+
create = async ({ express: express3, contexts, agents, workflows, config, tools }) => {
|
|
4244
4605
|
this.express = express3;
|
|
4245
4606
|
if (!this.server) {
|
|
4246
4607
|
console.log("[EXULU] Creating MCP server.");
|
|
@@ -4371,28 +4732,210 @@ ${code}`
|
|
|
4371
4732
|
|
|
4372
4733
|
// src/registry/index.ts
|
|
4373
4734
|
import express2 from "express";
|
|
4735
|
+
|
|
4736
|
+
// src/templates/agents/claude-code.ts
|
|
4737
|
+
var agentId = "0832-5178-1145-2194";
|
|
4738
|
+
var claudeCodeAgent = new ExuluAgent({
|
|
4739
|
+
id: `${agentId}-claude-code-agent`,
|
|
4740
|
+
name: `Claude Code Agent`,
|
|
4741
|
+
description: `Claude Code agent, enabling the creation of multiple Claude Code Agent instances with different configurations (rate limits, functions, etc).`,
|
|
4742
|
+
type: "custom"
|
|
4743
|
+
});
|
|
4744
|
+
|
|
4745
|
+
// src/templates/agents/claude-opus-4.ts
|
|
4746
|
+
import { anthropic } from "@ai-sdk/anthropic";
|
|
4747
|
+
import { z as z3 } from "zod";
|
|
4748
|
+
var agentId2 = "5434-5678-9143-2590";
|
|
4749
|
+
var defaultAgent = new ExuluAgent({
|
|
4750
|
+
id: `${agentId2}-default-claude-4-opus-agent`,
|
|
4751
|
+
name: `Default Claude 4 Opus Agent`,
|
|
4752
|
+
description: `Basic agent without any defined tools, that can support MCP's.`,
|
|
4753
|
+
type: "agent",
|
|
4754
|
+
capabilities: {
|
|
4755
|
+
tools: false,
|
|
4756
|
+
images: [],
|
|
4757
|
+
files: [],
|
|
4758
|
+
audio: [],
|
|
4759
|
+
video: []
|
|
4760
|
+
},
|
|
4761
|
+
evals: [],
|
|
4762
|
+
config: {
|
|
4763
|
+
name: `Default agent`,
|
|
4764
|
+
instructions: "You are a helpful assistant.",
|
|
4765
|
+
model: anthropic("claude-4-opus-20250514"),
|
|
4766
|
+
// todo add a field of type string that adds a dropdown list from which the user can select the model
|
|
4767
|
+
// todo for each model, check which provider is used, and require the admin to add one or multiple
|
|
4768
|
+
// API keys for the provider (which we can then auto-rotate).
|
|
4769
|
+
// todo also add custom fields for rate limiting, so the admin can set custom rate limits for the agent
|
|
4770
|
+
// and allow him/her to decide if the rate limit is per user or per agent.
|
|
4771
|
+
// todo finally allow switching on or off immutable audit logs on the agent. Which then enables OTEL
|
|
4772
|
+
// and stores the logs into the pre-defined storage.
|
|
4773
|
+
custom: z3.object({
|
|
4774
|
+
apiKey: z3.string()
|
|
4775
|
+
})
|
|
4776
|
+
}
|
|
4777
|
+
});
|
|
4778
|
+
|
|
4779
|
+
// src/templates/tools/browserbase.ts
|
|
4780
|
+
import { z as z4 } from "zod";
|
|
4781
|
+
import { Stagehand } from "@browserbasehq/stagehand";
|
|
4782
|
+
import { Browserbase } from "@browserbasehq/sdk";
|
|
4783
|
+
var PROJECT_ID = "811444dd-6e6d-40b5-bd90-541c93e44be6";
|
|
4784
|
+
process.env.BROWSERBASE_PROJECT_ID = PROJECT_ID;
|
|
4785
|
+
var BB_API_KEY = "bb_live_LwMwNgZB5cIEKcBwMuAugrgNkFM";
|
|
4786
|
+
async function createContext() {
|
|
4787
|
+
const bb = new Browserbase({ apiKey: BB_API_KEY });
|
|
4788
|
+
const context = await bb.contexts.create({
|
|
4789
|
+
projectId: PROJECT_ID
|
|
4790
|
+
});
|
|
4791
|
+
return context;
|
|
4792
|
+
}
|
|
4793
|
+
async function createAuthSession(contextId) {
|
|
4794
|
+
const bb = new Browserbase({ apiKey: BB_API_KEY });
|
|
4795
|
+
const session = await bb.sessions.create({
|
|
4796
|
+
projectId: PROJECT_ID,
|
|
4797
|
+
browserSettings: {
|
|
4798
|
+
context: {
|
|
4799
|
+
id: contextId,
|
|
4800
|
+
persist: true
|
|
4801
|
+
}
|
|
4802
|
+
}
|
|
4803
|
+
/* proxies: [{ // not included in the free tier
|
|
4804
|
+
type: "browserbase",
|
|
4805
|
+
geolocation: {
|
|
4806
|
+
city: CITY,
|
|
4807
|
+
country: COUNTRY
|
|
4808
|
+
}
|
|
4809
|
+
}] */
|
|
4810
|
+
});
|
|
4811
|
+
const liveViewLinks = await bb.sessions.debug(session.id);
|
|
4812
|
+
const liveViewLink = liveViewLinks.debuggerFullscreenUrl;
|
|
4813
|
+
console.log(`\u{1F50D} Live View Link: ${liveViewLink}`);
|
|
4814
|
+
console.log("Session URL: https://browserbase.com/sessions/" + session.id);
|
|
4815
|
+
return {
|
|
4816
|
+
url: liveViewLink,
|
|
4817
|
+
id: session.id
|
|
4818
|
+
};
|
|
4819
|
+
}
|
|
4820
|
+
var createSession = new ExuluTool({
|
|
4821
|
+
id: `1234-5178-9423-4267`,
|
|
4822
|
+
type: "function",
|
|
4823
|
+
name: "Create a browserbase session.",
|
|
4824
|
+
description: `
|
|
4825
|
+
Creates a browserbase session and returns the live view url as well as
|
|
4826
|
+
the session id as a JSON object. A browserbase session is a headless browser
|
|
4827
|
+
that can be used to to visit websites and perform actions.
|
|
4828
|
+
`,
|
|
4829
|
+
execute: async () => {
|
|
4830
|
+
const { id } = await createContext();
|
|
4831
|
+
return await createAuthSession(id);
|
|
4832
|
+
}
|
|
4833
|
+
});
|
|
4834
|
+
var askChatgpt = new ExuluTool({
|
|
4835
|
+
id: `1234-5178-9423-4268`,
|
|
4836
|
+
type: "function",
|
|
4837
|
+
name: "ChatGPT browserbase operation.",
|
|
4838
|
+
inputSchema: z4.object({
|
|
4839
|
+
session: z4.string().describe("The session id of the browserbase session."),
|
|
4840
|
+
question: z4.string().describe("The question to ask ChatGPT.")
|
|
4841
|
+
}),
|
|
4842
|
+
description: `Uses an existing, authenticated browserbase session to visit ChatGPT and perform actions such as asking questions.`,
|
|
4843
|
+
execute: async ({ session, question }) => {
|
|
4844
|
+
const stagehand = new Stagehand({
|
|
4845
|
+
// With npx create-browser-app, this config is found
|
|
4846
|
+
// in a separate stagehand.config.ts file
|
|
4847
|
+
env: "BROWSERBASE",
|
|
4848
|
+
// set to "LOCAL" for local development
|
|
4849
|
+
apiKey: BB_API_KEY,
|
|
4850
|
+
// todo make this a config variable the admin can set in the UI
|
|
4851
|
+
modelName: "openai/gpt-4.1-mini",
|
|
4852
|
+
// todo change to claude || optionally make configurable?
|
|
4853
|
+
browserbaseSessionID: session,
|
|
4854
|
+
modelClientOptions: {
|
|
4855
|
+
apiKey: process.env.OPENAI_API_KEY
|
|
4856
|
+
// todo make this a config variable the admin can set in the UI
|
|
4857
|
+
}
|
|
4858
|
+
});
|
|
4859
|
+
await stagehand.init();
|
|
4860
|
+
const page = stagehand.page;
|
|
4861
|
+
await page.goto("https://chatgpt.com");
|
|
4862
|
+
await page.act(`Type in '${question}' into the search bar`);
|
|
4863
|
+
const { answer } = await page.extract({
|
|
4864
|
+
instruction: "The answer to the question generated by ChatGPT.",
|
|
4865
|
+
schema: z4.object({
|
|
4866
|
+
answer: z4.string()
|
|
4867
|
+
})
|
|
4868
|
+
});
|
|
4869
|
+
console.log(answer);
|
|
4870
|
+
await stagehand.close();
|
|
4871
|
+
return {
|
|
4872
|
+
answer
|
|
4873
|
+
};
|
|
4874
|
+
}
|
|
4875
|
+
});
|
|
4876
|
+
|
|
4877
|
+
// src/templates/tools/jira.ts
|
|
4878
|
+
import { z as z5 } from "zod";
|
|
4879
|
+
var getTicket = new ExuluTool({
|
|
4880
|
+
id: `1414-5179-1423-1269`,
|
|
4881
|
+
name: "JIRA ticket retrieval.",
|
|
4882
|
+
type: "function",
|
|
4883
|
+
inputSchema: z5.object({
|
|
4884
|
+
ticketId: z5.string().describe("The id of the ticket to retrieve.")
|
|
4885
|
+
}),
|
|
4886
|
+
description: `Retrieves a ticket from Jira.`,
|
|
4887
|
+
execute: async ({ session, question }) => {
|
|
4888
|
+
return {
|
|
4889
|
+
name: "BYD-1234",
|
|
4890
|
+
id: "12345678",
|
|
4891
|
+
status: "Open",
|
|
4892
|
+
description: "This is a test ticket",
|
|
4893
|
+
assignee: "John Doe",
|
|
4894
|
+
createdAt: "2021-01-01",
|
|
4895
|
+
updatedAt: "2021-01-01",
|
|
4896
|
+
dueDate: "2021-01-01",
|
|
4897
|
+
priority: "High"
|
|
4898
|
+
};
|
|
4899
|
+
}
|
|
4900
|
+
});
|
|
4901
|
+
|
|
4902
|
+
// src/registry/index.ts
|
|
4374
4903
|
var ExuluApp = class {
|
|
4375
4904
|
_agents = [];
|
|
4376
4905
|
_workflows = [];
|
|
4377
4906
|
_config;
|
|
4378
|
-
_embedders = [];
|
|
4379
4907
|
_queues = [];
|
|
4380
4908
|
_contexts = {};
|
|
4381
4909
|
_tools = [];
|
|
4382
4910
|
_expressApp = null;
|
|
4383
4911
|
constructor() {
|
|
4384
4912
|
}
|
|
4385
|
-
// Factory function so we can async
|
|
4386
|
-
// MCP server if needed.
|
|
4387
|
-
create = async ({ contexts,
|
|
4388
|
-
this._embedders = embedders ?? [];
|
|
4913
|
+
// Factory function so we can async
|
|
4914
|
+
// initialize the MCP server if needed.
|
|
4915
|
+
create = async ({ contexts, agents, workflows, config, tools }) => {
|
|
4389
4916
|
this._workflows = workflows ?? [];
|
|
4390
4917
|
this._contexts = contexts ?? {};
|
|
4391
|
-
this._agents =
|
|
4918
|
+
this._agents = [
|
|
4919
|
+
claudeCodeAgent,
|
|
4920
|
+
defaultAgent,
|
|
4921
|
+
...agents ?? []
|
|
4922
|
+
];
|
|
4392
4923
|
this._config = config;
|
|
4393
|
-
this._tools =
|
|
4924
|
+
this._tools = [
|
|
4925
|
+
...tools ?? [],
|
|
4926
|
+
// Add contexts as tools
|
|
4927
|
+
...Object.values(contexts || {}).map((context) => context.tool()),
|
|
4928
|
+
// Add agents as tools
|
|
4929
|
+
...(agents || []).map((agent) => agent.tool()),
|
|
4930
|
+
...[
|
|
4931
|
+
createSession,
|
|
4932
|
+
askChatgpt,
|
|
4933
|
+
getTicket
|
|
4934
|
+
]
|
|
4935
|
+
];
|
|
4936
|
+
const contextsArray = Object.values(contexts || {});
|
|
4394
4937
|
const queues2 = [
|
|
4395
|
-
...
|
|
4938
|
+
...contextsArray?.length ? contextsArray.map((context) => context.embedder.queue?.name || null) : [],
|
|
4396
4939
|
...workflows?.length ? workflows.map((workflow) => workflow.queue?.name || null) : []
|
|
4397
4940
|
];
|
|
4398
4941
|
this._queues = [...new Set(queues2.filter((o) => !!o))];
|
|
@@ -4409,9 +4952,6 @@ var ExuluApp = class {
|
|
|
4409
4952
|
}
|
|
4410
4953
|
return this._expressApp;
|
|
4411
4954
|
}
|
|
4412
|
-
embedder(id) {
|
|
4413
|
-
return this._embedders.find((x) => x.id === id);
|
|
4414
|
-
}
|
|
4415
4955
|
tool(id) {
|
|
4416
4956
|
return this._tools.find((x) => x.id === id);
|
|
4417
4957
|
}
|
|
@@ -4427,9 +4967,6 @@ var ExuluApp = class {
|
|
|
4427
4967
|
workflow(id) {
|
|
4428
4968
|
return this._workflows.find((x) => x.id === id);
|
|
4429
4969
|
}
|
|
4430
|
-
get embedders() {
|
|
4431
|
-
return this._embedders;
|
|
4432
|
-
}
|
|
4433
4970
|
get contexts() {
|
|
4434
4971
|
return Object.values(this._contexts ?? {});
|
|
4435
4972
|
}
|
|
@@ -4445,7 +4982,6 @@ var ExuluApp = class {
|
|
|
4445
4982
|
return await createWorkers(
|
|
4446
4983
|
this._queues,
|
|
4447
4984
|
Object.values(this._contexts ?? {}),
|
|
4448
|
-
this._embedders,
|
|
4449
4985
|
this._workflows,
|
|
4450
4986
|
this._config?.workers?.logsDir
|
|
4451
4987
|
);
|
|
@@ -4462,7 +4998,6 @@ var ExuluApp = class {
|
|
|
4462
4998
|
await createExpressRoutes(
|
|
4463
4999
|
app,
|
|
4464
5000
|
this._agents,
|
|
4465
|
-
this._embedders,
|
|
4466
5001
|
this._tools,
|
|
4467
5002
|
this._workflows,
|
|
4468
5003
|
Object.values(this._contexts ?? {})
|
|
@@ -4472,7 +5007,6 @@ var ExuluApp = class {
|
|
|
4472
5007
|
await mcp.create({
|
|
4473
5008
|
express: app,
|
|
4474
5009
|
contexts: this._contexts,
|
|
4475
|
-
embedders: this._embedders,
|
|
4476
5010
|
agents: this._agents,
|
|
4477
5011
|
workflows: this._workflows,
|
|
4478
5012
|
config: this._config,
|
|
@@ -5680,198 +6214,6 @@ var SentenceChunker = class _SentenceChunker extends BaseChunker {
|
|
|
5680
6214
|
}
|
|
5681
6215
|
};
|
|
5682
6216
|
|
|
5683
|
-
// src/cli/index.tsx
|
|
5684
|
-
import { useState as useState2 } from "react";
|
|
5685
|
-
import { Box as Box2, Text as Text4, render as render2 } from "ink";
|
|
5686
|
-
import { UnorderedList as UnorderedList3 } from "@inkjs/ui";
|
|
5687
|
-
import patchConsole from "patch-console";
|
|
5688
|
-
|
|
5689
|
-
// src/cli/components/nav.tsx
|
|
5690
|
-
import { Select } from "@inkjs/ui";
|
|
5691
|
-
import { useApp } from "ink";
|
|
5692
|
-
import { jsx } from "react/jsx-runtime";
|
|
5693
|
-
var nav = [
|
|
5694
|
-
{
|
|
5695
|
-
label: "Agents",
|
|
5696
|
-
value: "agents"
|
|
5697
|
-
},
|
|
5698
|
-
{
|
|
5699
|
-
label: "Start Claude Code",
|
|
5700
|
-
value: "claude-code"
|
|
5701
|
-
},
|
|
5702
|
-
{
|
|
5703
|
-
label: "Exit",
|
|
5704
|
-
value: "exit"
|
|
5705
|
-
}
|
|
5706
|
-
];
|
|
5707
|
-
var Nav = ({ setView }) => {
|
|
5708
|
-
const { exit } = useApp();
|
|
5709
|
-
return /* @__PURE__ */ jsx(Select, { options: nav, onChange: (value) => {
|
|
5710
|
-
if (value === "exit") {
|
|
5711
|
-
exit();
|
|
5712
|
-
}
|
|
5713
|
-
setView(value);
|
|
5714
|
-
} });
|
|
5715
|
-
};
|
|
5716
|
-
var nav_default = Nav;
|
|
5717
|
-
|
|
5718
|
-
// src/cli/components/agent-selector.tsx
|
|
5719
|
-
import { Text as Text2 } from "ink";
|
|
5720
|
-
import { Select as Select2 } from "@inkjs/ui";
|
|
5721
|
-
import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
5722
|
-
var AgentSelector = ({ exulu, setAgent, setEvaluations }) => {
|
|
5723
|
-
const agents = exulu.agents.map((agent) => ({
|
|
5724
|
-
label: agent.name,
|
|
5725
|
-
value: agent.id
|
|
5726
|
-
}));
|
|
5727
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5728
|
-
/* @__PURE__ */ jsx2(Text2, { children: "Please select an agent:" }),
|
|
5729
|
-
/* @__PURE__ */ jsx2(Select2, { options: agents, onChange: (value) => {
|
|
5730
|
-
const agent = exulu.agent(value);
|
|
5731
|
-
if (!agent) {
|
|
5732
|
-
console.error("Agent not found", value);
|
|
5733
|
-
return;
|
|
5734
|
-
}
|
|
5735
|
-
setAgent(agent);
|
|
5736
|
-
if (agent) {
|
|
5737
|
-
setEvaluations(agent.evals || []);
|
|
5738
|
-
}
|
|
5739
|
-
} })
|
|
5740
|
-
] });
|
|
5741
|
-
};
|
|
5742
|
-
var agent_selector_default = AgentSelector;
|
|
5743
|
-
|
|
5744
|
-
// src/cli/components/eval-selector.tsx
|
|
5745
|
-
import { Select as Select3 } from "@inkjs/ui";
|
|
5746
|
-
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
5747
|
-
var EvalSelector = ({ evaluations, setEvaluation }) => {
|
|
5748
|
-
return /* @__PURE__ */ jsx3(Select3, { options: evaluations.map((evaluation) => ({
|
|
5749
|
-
label: evaluation.runner.name,
|
|
5750
|
-
value: evaluation.runner.name
|
|
5751
|
-
})), onChange: (value) => {
|
|
5752
|
-
const evaluation = evaluations?.find((evaluation2) => evaluation2.runner.name === value);
|
|
5753
|
-
if (evaluation) {
|
|
5754
|
-
setEvaluation(evaluation);
|
|
5755
|
-
}
|
|
5756
|
-
} });
|
|
5757
|
-
};
|
|
5758
|
-
var eval_selector_default = EvalSelector;
|
|
5759
|
-
|
|
5760
|
-
// src/cli/components/eval-actions.tsx
|
|
5761
|
-
import { useState } from "react";
|
|
5762
|
-
import { ProgressBar as ProgressBar2, Select as Select4, UnorderedList as UnorderedList2 } from "@inkjs/ui";
|
|
5763
|
-
import { Text as Text3 } from "ink";
|
|
5764
|
-
import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
5765
|
-
var EvalActions = ({ agent, evaluation, setEvaluation }) => {
|
|
5766
|
-
const [progress, setProgress] = useState(0);
|
|
5767
|
-
const [results, setResults] = useState([]);
|
|
5768
|
-
const [running, setRunning] = useState();
|
|
5769
|
-
const run = async (evaluation2) => {
|
|
5770
|
-
setRunning({
|
|
5771
|
-
label: evaluation2.runner.name
|
|
5772
|
-
});
|
|
5773
|
-
const testCases = evaluation2.runner.testcases;
|
|
5774
|
-
const total = testCases.length;
|
|
5775
|
-
if (!testCases) {
|
|
5776
|
-
throw new Error("No test cases found");
|
|
5777
|
-
}
|
|
5778
|
-
let i = 0;
|
|
5779
|
-
for (const testCase of testCases) {
|
|
5780
|
-
i++;
|
|
5781
|
-
const result = await evaluation2.runner.run({
|
|
5782
|
-
data: testCase,
|
|
5783
|
-
runner: {
|
|
5784
|
-
agent
|
|
5785
|
-
}
|
|
5786
|
-
});
|
|
5787
|
-
setProgress(Math.round(i / total * 100));
|
|
5788
|
-
setResults([...results, {
|
|
5789
|
-
name: evaluation2.runner.name,
|
|
5790
|
-
prompt: testCase.prompt?.slice(0, 100) + "...",
|
|
5791
|
-
score: result.score,
|
|
5792
|
-
comment: result.comment
|
|
5793
|
-
}]);
|
|
5794
|
-
}
|
|
5795
|
-
setRunning(void 0);
|
|
5796
|
-
};
|
|
5797
|
-
if (progress === 100) {
|
|
5798
|
-
return /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
5799
|
-
/* @__PURE__ */ jsx4(Text3, { children: "Evaluations completed." }),
|
|
5800
|
-
/* @__PURE__ */ jsx4(UnorderedList2, { children: results.map((result) => /* @__PURE__ */ jsx4(UnorderedList2.Item, { children: /* @__PURE__ */ jsxs2(Text3, { children: [
|
|
5801
|
-
result.name,
|
|
5802
|
-
": ",
|
|
5803
|
-
result.score,
|
|
5804
|
-
" - ",
|
|
5805
|
-
result.comment
|
|
5806
|
-
] }) })) })
|
|
5807
|
-
] });
|
|
5808
|
-
}
|
|
5809
|
-
if (running) {
|
|
5810
|
-
return /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
5811
|
-
/* @__PURE__ */ jsxs2(Text3, { children: [
|
|
5812
|
-
"Running ",
|
|
5813
|
-
running.label,
|
|
5814
|
-
"..."
|
|
5815
|
-
] }),
|
|
5816
|
-
/* @__PURE__ */ jsx4(ProgressBar2, { value: progress })
|
|
5817
|
-
] });
|
|
5818
|
-
}
|
|
5819
|
-
return /* @__PURE__ */ jsx4(Select4, { options: [{
|
|
5820
|
-
label: "Run evaluation",
|
|
5821
|
-
value: "run"
|
|
5822
|
-
}, {
|
|
5823
|
-
label: "Go back",
|
|
5824
|
-
value: "back"
|
|
5825
|
-
}], onChange: (value) => {
|
|
5826
|
-
if (value === "back") {
|
|
5827
|
-
setEvaluation(void 0);
|
|
5828
|
-
}
|
|
5829
|
-
if (value === "run") {
|
|
5830
|
-
run(evaluation);
|
|
5831
|
-
}
|
|
5832
|
-
} });
|
|
5833
|
-
};
|
|
5834
|
-
var eval_actions_default = EvalActions;
|
|
5835
|
-
|
|
5836
|
-
// src/cli/index.tsx
|
|
5837
|
-
import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
5838
|
-
var Main = ({ exulu }) => {
|
|
5839
|
-
patchConsole((stream, data) => {
|
|
5840
|
-
setLogs([...logs, data]);
|
|
5841
|
-
});
|
|
5842
|
-
const [logs, setLogs] = useState2([]);
|
|
5843
|
-
const [view, setView] = useState2();
|
|
5844
|
-
const [agent, setAgent] = useState2();
|
|
5845
|
-
const [evaluations, setEvaluations] = useState2([]);
|
|
5846
|
-
const [evaluation, setEvaluation] = useState2();
|
|
5847
|
-
return /* @__PURE__ */ jsxs3(Box2, { borderStyle: "round", borderColor: "cyan", padding: 1, flexDirection: "column", width: "70%", children: [
|
|
5848
|
-
/* @__PURE__ */ jsx5(Text4, { children: "Logs:" }),
|
|
5849
|
-
/* @__PURE__ */ jsx5(UnorderedList3, { children: logs.map((log, index) => /* @__PURE__ */ jsx5(UnorderedList3.Item, { children: /* @__PURE__ */ jsx5(Text4, { children: log }) })) }),
|
|
5850
|
-
!view && /* @__PURE__ */ jsx5(nav_default, { setView }),
|
|
5851
|
-
view === "agents" && !agent && /* @__PURE__ */ jsx5(agent_selector_default, { exulu, setAgent, setEvaluations }),
|
|
5852
|
-
view === "agents" && agent && !evaluation && /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
5853
|
-
/* @__PURE__ */ jsxs3(Text4, { children: [
|
|
5854
|
-
'Selected agent "',
|
|
5855
|
-
agent.name,
|
|
5856
|
-
'". Please select an evaluation:'
|
|
5857
|
-
] }),
|
|
5858
|
-
/* @__PURE__ */ jsx5(eval_selector_default, { evaluations, setEvaluation })
|
|
5859
|
-
] }),
|
|
5860
|
-
view === "agents" && agent && evaluation && /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
5861
|
-
/* @__PURE__ */ jsxs3(Text4, { children: [
|
|
5862
|
-
"Selected evaluation: ",
|
|
5863
|
-
evaluation.runner.name
|
|
5864
|
-
] }),
|
|
5865
|
-
/* @__PURE__ */ jsx5(eval_actions_default, { agent, evaluation, setEvaluation })
|
|
5866
|
-
] })
|
|
5867
|
-
] });
|
|
5868
|
-
};
|
|
5869
|
-
var cli_default = {
|
|
5870
|
-
run: (exulu) => {
|
|
5871
|
-
render2(/* @__PURE__ */ jsx5(Main, { exulu }));
|
|
5872
|
-
}
|
|
5873
|
-
};
|
|
5874
|
-
|
|
5875
6217
|
// src/index.ts
|
|
5876
6218
|
var ExuluJobs = {
|
|
5877
6219
|
redis: redisClient,
|
|
@@ -5901,7 +6243,6 @@ export {
|
|
|
5901
6243
|
ExuluApp,
|
|
5902
6244
|
authentication as ExuluAuthentication,
|
|
5903
6245
|
ExuluChunkers,
|
|
5904
|
-
cli_default as ExuluCli,
|
|
5905
6246
|
ExuluContext,
|
|
5906
6247
|
ExuluDatabase,
|
|
5907
6248
|
ExuluEmbedder,
|