@exulu/backend 1.15.0 → 1.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -35
- package/dist/index.cjs +359 -259
- package/dist/index.d.cts +34 -20
- package/dist/index.d.ts +34 -20
- package/dist/index.js +355 -256
- package/documentation/logging.md +122 -0
- package/documentation/otel.md +145 -0
- package/package.json +15 -4
- package/lms.md +0 -3
package/dist/index.js
CHANGED
|
@@ -175,87 +175,48 @@ import pgvector2 from "pgvector/knex";
|
|
|
175
175
|
import "bullmq";
|
|
176
176
|
import { v4 as uuidv4 } from "uuid";
|
|
177
177
|
var bullmqDecorator = async ({
|
|
178
|
+
queue,
|
|
178
179
|
label,
|
|
179
|
-
type,
|
|
180
|
-
workflow,
|
|
181
180
|
embedder,
|
|
182
181
|
inputs,
|
|
183
|
-
queue,
|
|
184
182
|
user,
|
|
185
|
-
|
|
186
|
-
session,
|
|
187
|
-
configuration,
|
|
188
|
-
updater,
|
|
189
|
-
context,
|
|
190
|
-
steps,
|
|
191
|
-
source,
|
|
192
|
-
documents,
|
|
183
|
+
role,
|
|
193
184
|
trigger,
|
|
194
|
-
|
|
185
|
+
workflow,
|
|
186
|
+
item,
|
|
187
|
+
context
|
|
195
188
|
}) => {
|
|
189
|
+
if (embedder && workflow) {
|
|
190
|
+
throw new Error("Cannot have both embedder and workflow in the same job.");
|
|
191
|
+
}
|
|
192
|
+
if (workflow && item) {
|
|
193
|
+
throw new Error("Cannot have both workflow and item in the same job.");
|
|
194
|
+
}
|
|
195
|
+
let type = "embedder";
|
|
196
|
+
if (workflow) {
|
|
197
|
+
type = "workflow";
|
|
198
|
+
}
|
|
196
199
|
const redisId = uuidv4();
|
|
197
200
|
const job = await queue.add(
|
|
198
201
|
`${embedder || workflow}`,
|
|
199
202
|
{
|
|
200
|
-
|
|
203
|
+
label,
|
|
201
204
|
...embedder && { embedder },
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
...
|
|
205
|
-
...
|
|
206
|
-
...source && { source },
|
|
207
|
-
...documents && { documents },
|
|
208
|
-
...steps && { steps },
|
|
205
|
+
type: `${type}`,
|
|
206
|
+
inputs,
|
|
207
|
+
...user && { user },
|
|
208
|
+
...role && { role },
|
|
209
209
|
...trigger && { trigger },
|
|
210
|
+
...workflow && { workflow },
|
|
210
211
|
...item && { item },
|
|
211
|
-
|
|
212
|
-
user,
|
|
213
|
-
inputs,
|
|
214
|
-
label,
|
|
215
|
-
session
|
|
212
|
+
...context && { context }
|
|
216
213
|
},
|
|
217
214
|
{
|
|
218
215
|
jobId: redisId
|
|
219
216
|
}
|
|
220
217
|
);
|
|
221
|
-
const { db: db3 } = await postgresClient();
|
|
222
|
-
const now = /* @__PURE__ */ new Date();
|
|
223
|
-
console.log("[EXULU] scheduling new job", inputs);
|
|
224
|
-
const insertData = {
|
|
225
|
-
name: `${label}`,
|
|
226
|
-
redis: job.id,
|
|
227
|
-
status: "waiting",
|
|
228
|
-
type,
|
|
229
|
-
inputs,
|
|
230
|
-
agent,
|
|
231
|
-
item,
|
|
232
|
-
createdAt: now,
|
|
233
|
-
updatedAt: now,
|
|
234
|
-
user,
|
|
235
|
-
session,
|
|
236
|
-
...embedder && { embedder },
|
|
237
|
-
...workflow && { workflow },
|
|
238
|
-
...configuration && { configuration },
|
|
239
|
-
...steps && { steps },
|
|
240
|
-
...updater && { updater },
|
|
241
|
-
...context && { context },
|
|
242
|
-
...source && { source },
|
|
243
|
-
...documents && { documents: documents.map((doc2) => doc2.id) },
|
|
244
|
-
...trigger && { trigger }
|
|
245
|
-
};
|
|
246
|
-
await db3("jobs").insert(insertData).onConflict("redis").merge({
|
|
247
|
-
...insertData,
|
|
248
|
-
updatedAt: now
|
|
249
|
-
// Only updatedAt changes on updates
|
|
250
|
-
});
|
|
251
|
-
const doc = await db3.from("jobs").where({ redis: job.id }).first();
|
|
252
|
-
if (!doc?.id) {
|
|
253
|
-
throw new Error("Failed to get job ID after insert/update");
|
|
254
|
-
}
|
|
255
|
-
console.log("[EXULU] created job", doc?.id);
|
|
256
218
|
return {
|
|
257
219
|
...job,
|
|
258
|
-
id: doc?.id,
|
|
259
220
|
redis: job.id
|
|
260
221
|
};
|
|
261
222
|
};
|
|
@@ -427,6 +388,7 @@ var ExuluEvalUtils = {
|
|
|
427
388
|
// src/registry/classes.ts
|
|
428
389
|
import CryptoJS from "crypto-js";
|
|
429
390
|
import "express";
|
|
391
|
+
import "@opentelemetry/api";
|
|
430
392
|
function sanitizeToolName(name) {
|
|
431
393
|
if (typeof name !== "string") return "";
|
|
432
394
|
let sanitized = name.replace(/[^a-zA-Z0-9_-]+/g, "_");
|
|
@@ -436,7 +398,7 @@ function sanitizeToolName(name) {
|
|
|
436
398
|
}
|
|
437
399
|
return sanitized;
|
|
438
400
|
}
|
|
439
|
-
var convertToolsArrayToObject = (tools, configs, providerApiKey) => {
|
|
401
|
+
var convertToolsArrayToObject = (tools, configs, providerApiKey, user, role) => {
|
|
440
402
|
if (!tools) return {};
|
|
441
403
|
const sanitizedTools = tools ? tools.map((tool2) => ({
|
|
442
404
|
...tool2,
|
|
@@ -471,6 +433,8 @@ var convertToolsArrayToObject = (tools, configs, providerApiKey) => {
|
|
|
471
433
|
// is available, after we added the .value property
|
|
472
434
|
// by hydrating it from the variables table.
|
|
473
435
|
providerApiKey,
|
|
436
|
+
user,
|
|
437
|
+
role,
|
|
474
438
|
config: config ? config.config.reduce((acc, curr) => {
|
|
475
439
|
acc[curr.name] = curr.value;
|
|
476
440
|
return acc;
|
|
@@ -576,10 +540,12 @@ var ExuluAgent = class {
|
|
|
576
540
|
}),
|
|
577
541
|
description: `A function that calls an AI agent named: ${this.name}. The agent does the following: ${this.description}.`,
|
|
578
542
|
config: [],
|
|
579
|
-
execute: async ({ prompt, config, providerApiKey }) => {
|
|
543
|
+
execute: async ({ prompt, config, providerApiKey, user, role }) => {
|
|
580
544
|
return await this.generateSync({
|
|
581
545
|
prompt,
|
|
582
546
|
providerApiKey,
|
|
547
|
+
user,
|
|
548
|
+
role,
|
|
583
549
|
statistics: {
|
|
584
550
|
label: "",
|
|
585
551
|
trigger: "tool"
|
|
@@ -588,7 +554,7 @@ var ExuluAgent = class {
|
|
|
588
554
|
}
|
|
589
555
|
});
|
|
590
556
|
};
|
|
591
|
-
generateSync = async ({ prompt, user, session, message, tools, statistics, toolConfigs, providerApiKey }) => {
|
|
557
|
+
generateSync = async ({ prompt, user, role, session, message, tools, statistics, toolConfigs, providerApiKey }) => {
|
|
592
558
|
if (!this.model) {
|
|
593
559
|
throw new Error("Model is required for streaming.");
|
|
594
560
|
}
|
|
@@ -598,6 +564,9 @@ var ExuluAgent = class {
|
|
|
598
564
|
if (prompt && message) {
|
|
599
565
|
throw new Error("Message and prompt cannot be provided at the same time.");
|
|
600
566
|
}
|
|
567
|
+
if (!prompt && !message) {
|
|
568
|
+
throw new Error("Prompt or message is required for generating.");
|
|
569
|
+
}
|
|
601
570
|
const model = this.model.create({
|
|
602
571
|
apiKey: providerApiKey
|
|
603
572
|
});
|
|
@@ -617,28 +586,54 @@ var ExuluAgent = class {
|
|
|
617
586
|
}
|
|
618
587
|
console.log("[EXULU] Model provider key", providerApiKey);
|
|
619
588
|
console.log("[EXULU] Tool configs", toolConfigs);
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
589
|
+
if (prompt) {
|
|
590
|
+
const { text } = await generateText({
|
|
591
|
+
model,
|
|
592
|
+
// Should be a LanguageModelV1
|
|
593
|
+
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.",
|
|
594
|
+
prompt,
|
|
595
|
+
maxRetries: 2,
|
|
596
|
+
tools: convertToolsArrayToObject(tools, toolConfigs, providerApiKey, user, role),
|
|
597
|
+
stopWhen: [stepCountIs(5)]
|
|
598
|
+
});
|
|
599
|
+
if (statistics) {
|
|
600
|
+
await updateStatistic({
|
|
601
|
+
name: "count",
|
|
602
|
+
label: statistics.label,
|
|
603
|
+
type: STATISTICS_TYPE_ENUM.AGENT_RUN,
|
|
604
|
+
trigger: statistics.trigger,
|
|
605
|
+
count: 1,
|
|
606
|
+
user,
|
|
607
|
+
role
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
return text;
|
|
611
|
+
}
|
|
612
|
+
if (messages) {
|
|
613
|
+
const { text } = await generateText({
|
|
614
|
+
model,
|
|
615
|
+
// Should be a LanguageModelV1
|
|
616
|
+
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.",
|
|
617
|
+
messages: convertToModelMessages(messages),
|
|
618
|
+
maxRetries: 2,
|
|
619
|
+
tools: convertToolsArrayToObject(tools, toolConfigs, providerApiKey, user, role),
|
|
620
|
+
stopWhen: [stepCountIs(5)]
|
|
637
621
|
});
|
|
622
|
+
if (statistics) {
|
|
623
|
+
await updateStatistic({
|
|
624
|
+
name: "count",
|
|
625
|
+
label: statistics.label,
|
|
626
|
+
type: STATISTICS_TYPE_ENUM.AGENT_RUN,
|
|
627
|
+
trigger: statistics.trigger,
|
|
628
|
+
count: 1,
|
|
629
|
+
user,
|
|
630
|
+
role
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
return text;
|
|
638
634
|
}
|
|
639
|
-
return text;
|
|
640
635
|
};
|
|
641
|
-
generateStream = async ({ express: express3, user, session, message, tools, statistics, toolConfigs, providerApiKey }) => {
|
|
636
|
+
generateStream = async ({ express: express3, user, role, session, message, tools, statistics, toolConfigs, providerApiKey }) => {
|
|
642
637
|
if (!this.model) {
|
|
643
638
|
throw new Error("Model is required for streaming.");
|
|
644
639
|
}
|
|
@@ -668,10 +663,10 @@ var ExuluAgent = class {
|
|
|
668
663
|
const result = streamText({
|
|
669
664
|
model,
|
|
670
665
|
// Should be a LanguageModelV1
|
|
671
|
-
messages:
|
|
666
|
+
messages: convertToModelMessages(messages),
|
|
672
667
|
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.",
|
|
673
668
|
maxRetries: 2,
|
|
674
|
-
tools: convertToolsArrayToObject(tools, toolConfigs, providerApiKey),
|
|
669
|
+
tools: convertToolsArrayToObject(tools, toolConfigs, providerApiKey, user, role),
|
|
675
670
|
onError: (error) => console.error("[EXULU] chat stream error.", error),
|
|
676
671
|
stopWhen: [stepCountIs(5)]
|
|
677
672
|
});
|
|
@@ -699,7 +694,9 @@ var ExuluAgent = class {
|
|
|
699
694
|
label: statistics.label,
|
|
700
695
|
type: STATISTICS_TYPE_ENUM.AGENT_RUN,
|
|
701
696
|
trigger: statistics.trigger,
|
|
702
|
-
count: 1
|
|
697
|
+
count: 1,
|
|
698
|
+
user,
|
|
699
|
+
role
|
|
703
700
|
});
|
|
704
701
|
}
|
|
705
702
|
}
|
|
@@ -743,14 +740,16 @@ var ExuluEmbedder = class {
|
|
|
743
740
|
this.queue = queue;
|
|
744
741
|
this.generateEmbeddings = generateEmbeddings;
|
|
745
742
|
}
|
|
746
|
-
async generateFromQuery(query, statistics) {
|
|
743
|
+
async generateFromQuery(query, statistics, user, role) {
|
|
747
744
|
if (statistics) {
|
|
748
745
|
await updateStatistic({
|
|
749
746
|
name: "count",
|
|
750
747
|
label: statistics.label,
|
|
751
748
|
type: STATISTICS_TYPE_ENUM.EMBEDDER_GENERATE,
|
|
752
749
|
trigger: statistics.trigger,
|
|
753
|
-
count: 1
|
|
750
|
+
count: 1,
|
|
751
|
+
user,
|
|
752
|
+
role
|
|
754
753
|
});
|
|
755
754
|
}
|
|
756
755
|
return await this.generateEmbeddings({
|
|
@@ -763,14 +762,16 @@ var ExuluEmbedder = class {
|
|
|
763
762
|
}]
|
|
764
763
|
});
|
|
765
764
|
}
|
|
766
|
-
async generateFromDocument(input, statistics) {
|
|
765
|
+
async generateFromDocument(input, statistics, user, role) {
|
|
767
766
|
if (statistics) {
|
|
768
767
|
await updateStatistic({
|
|
769
768
|
name: "count",
|
|
770
769
|
label: statistics.label,
|
|
771
770
|
type: STATISTICS_TYPE_ENUM.EMBEDDER_GENERATE,
|
|
772
771
|
trigger: statistics.trigger,
|
|
773
|
-
count: 1
|
|
772
|
+
count: 1,
|
|
773
|
+
user,
|
|
774
|
+
role
|
|
774
775
|
});
|
|
775
776
|
}
|
|
776
777
|
if (!this.chunker) {
|
|
@@ -1024,8 +1025,36 @@ var ExuluContext = class {
|
|
|
1024
1025
|
const tableExists = await db3.schema.hasTable(this.getTableName());
|
|
1025
1026
|
return tableExists;
|
|
1026
1027
|
};
|
|
1027
|
-
async
|
|
1028
|
-
|
|
1028
|
+
createAndUpsertEmbeddings = async (item, user, statistics, role) => {
|
|
1029
|
+
const { db: db3 } = await postgresClient();
|
|
1030
|
+
const { id: source, chunks } = await this.embedder.generateFromDocument({
|
|
1031
|
+
...item,
|
|
1032
|
+
id: item.id
|
|
1033
|
+
}, {
|
|
1034
|
+
label: statistics.label || this.name,
|
|
1035
|
+
trigger: statistics.trigger || "agent"
|
|
1036
|
+
}, user, role);
|
|
1037
|
+
const exists = await db3.schema.hasTable(this.getChunksTableName());
|
|
1038
|
+
if (!exists) {
|
|
1039
|
+
await this.createChunksTable();
|
|
1040
|
+
}
|
|
1041
|
+
await db3.from(this.getChunksTableName()).where({ source }).delete();
|
|
1042
|
+
await db3.from(this.getChunksTableName()).insert(chunks.map((chunk) => ({
|
|
1043
|
+
source,
|
|
1044
|
+
content: chunk.content,
|
|
1045
|
+
chunk_index: chunk.index,
|
|
1046
|
+
embedding: pgvector2.toSql(chunk.vector)
|
|
1047
|
+
})));
|
|
1048
|
+
await db3.from(this.getTableName()).where({ id: item.id }).update({
|
|
1049
|
+
embeddings_updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1050
|
+
}).returning("id");
|
|
1051
|
+
return {
|
|
1052
|
+
id: item.id,
|
|
1053
|
+
chunks: chunks?.length || 0
|
|
1054
|
+
};
|
|
1055
|
+
};
|
|
1056
|
+
async updateItem(user, item, role, trigger) {
|
|
1057
|
+
if (!item.id) {
|
|
1029
1058
|
throw new Error("Id is required for updating an item.");
|
|
1030
1059
|
}
|
|
1031
1060
|
const { db: db3 } = await postgresClient();
|
|
@@ -1042,51 +1071,37 @@ var ExuluContext = class {
|
|
|
1042
1071
|
delete item.created_at;
|
|
1043
1072
|
delete item.upsert;
|
|
1044
1073
|
item.updated_at = db3.fn.now();
|
|
1045
|
-
const result = await db3.from(this.getTableName()).where({ id }).update(item).returning("id");
|
|
1074
|
+
const result = await db3.from(this.getTableName()).where({ id: item.id }).update(item).returning("id");
|
|
1046
1075
|
if (this.configuration.calculateVectors === "onUpdate" || this.configuration.calculateVectors === "always") {
|
|
1047
1076
|
if (this.embedder.queue?.name) {
|
|
1048
1077
|
console.log("[EXULU] embedder is in queue mode, scheduling job.");
|
|
1049
1078
|
const job = await bullmqDecorator({
|
|
1050
|
-
label:
|
|
1079
|
+
label: `${this.embedder.name}`,
|
|
1051
1080
|
embedder: this.embedder.id,
|
|
1052
|
-
|
|
1081
|
+
context: this.id,
|
|
1053
1082
|
inputs: item,
|
|
1083
|
+
item: item.id,
|
|
1054
1084
|
queue: this.embedder.queue,
|
|
1055
|
-
user
|
|
1085
|
+
user,
|
|
1086
|
+
role,
|
|
1087
|
+
trigger: trigger || "agent"
|
|
1056
1088
|
});
|
|
1057
1089
|
return {
|
|
1058
1090
|
id: result[0].id,
|
|
1059
1091
|
job: job.id
|
|
1060
1092
|
};
|
|
1061
1093
|
}
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
},
|
|
1066
|
-
label: this.name,
|
|
1067
|
-
trigger: "agent"
|
|
1068
|
-
});
|
|
1069
|
-
const exists = await db3.schema.hasTable(this.getChunksTableName());
|
|
1070
|
-
if (!exists) {
|
|
1071
|
-
await this.createChunksTable();
|
|
1072
|
-
}
|
|
1073
|
-
await db3.from(this.getChunksTableName()).where({ source }).delete();
|
|
1074
|
-
await db3.from(this.getChunksTableName()).insert(chunks.map((chunk) => ({
|
|
1075
|
-
source,
|
|
1076
|
-
content: chunk.content,
|
|
1077
|
-
chunk_index: chunk.index,
|
|
1078
|
-
embedding: pgvector2.toSql(chunk.vector)
|
|
1079
|
-
})));
|
|
1080
|
-
await db3.from(this.getTableName()).where({ id }).update({
|
|
1081
|
-
embeddings_updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1082
|
-
}).returning("id");
|
|
1094
|
+
await this.createAndUpsertEmbeddings(item, user, {
|
|
1095
|
+
label: this.embedder.name,
|
|
1096
|
+
trigger: trigger || "agent"
|
|
1097
|
+
}, role);
|
|
1083
1098
|
}
|
|
1084
1099
|
return {
|
|
1085
1100
|
id: result[0].id,
|
|
1086
1101
|
job: void 0
|
|
1087
1102
|
};
|
|
1088
1103
|
}
|
|
1089
|
-
async insertItem(user, item, upsert = false) {
|
|
1104
|
+
async insertItem(user, item, upsert = false, role, trigger) {
|
|
1090
1105
|
if (!item.name) {
|
|
1091
1106
|
throw new Error("Name field is required.");
|
|
1092
1107
|
}
|
|
@@ -1097,14 +1112,20 @@ var ExuluContext = class {
|
|
|
1097
1112
|
throw new Error("Item with external id " + item.external_id + " already exists.");
|
|
1098
1113
|
}
|
|
1099
1114
|
if (existingItem && upsert) {
|
|
1100
|
-
await this.updateItem(user,
|
|
1115
|
+
await this.updateItem(user, {
|
|
1116
|
+
...item,
|
|
1117
|
+
id: existingItem.id
|
|
1118
|
+
}, role, trigger);
|
|
1101
1119
|
return existingItem.id;
|
|
1102
1120
|
}
|
|
1103
1121
|
}
|
|
1104
1122
|
if (upsert && item.id) {
|
|
1105
1123
|
const existingItem = await db3.from(this.getTableName()).where({ id: item.id }).first();
|
|
1106
1124
|
if (existingItem && upsert) {
|
|
1107
|
-
await this.updateItem(user,
|
|
1125
|
+
await this.updateItem(user, {
|
|
1126
|
+
...item,
|
|
1127
|
+
id: existingItem.id
|
|
1128
|
+
}, role, trigger);
|
|
1108
1129
|
return existingItem.id;
|
|
1109
1130
|
}
|
|
1110
1131
|
}
|
|
@@ -1129,12 +1150,15 @@ var ExuluContext = class {
|
|
|
1129
1150
|
if (this.embedder.queue?.name) {
|
|
1130
1151
|
console.log("[EXULU] embedder is in queue mode, scheduling job.");
|
|
1131
1152
|
const job = await bullmqDecorator({
|
|
1132
|
-
label:
|
|
1153
|
+
label: `${this.embedder.name}`,
|
|
1133
1154
|
embedder: this.embedder.id,
|
|
1134
|
-
|
|
1155
|
+
context: this.id,
|
|
1135
1156
|
inputs: item,
|
|
1157
|
+
item: item.id,
|
|
1136
1158
|
queue: this.embedder.queue,
|
|
1137
|
-
user
|
|
1159
|
+
user,
|
|
1160
|
+
role,
|
|
1161
|
+
trigger: trigger || "agent"
|
|
1138
1162
|
});
|
|
1139
1163
|
return {
|
|
1140
1164
|
id: result[0].id,
|
|
@@ -1142,27 +1166,13 @@ var ExuluContext = class {
|
|
|
1142
1166
|
};
|
|
1143
1167
|
}
|
|
1144
1168
|
console.log("[EXULU] embedder is not in queue mode, calculating vectors directly.");
|
|
1145
|
-
|
|
1169
|
+
await this.createAndUpsertEmbeddings({
|
|
1146
1170
|
...item,
|
|
1147
1171
|
id: result[0].id
|
|
1148
|
-
}, {
|
|
1149
|
-
label: this.name,
|
|
1150
|
-
trigger: "agent"
|
|
1151
|
-
});
|
|
1152
|
-
const exists = await db3.schema.hasTable(this.getChunksTableName());
|
|
1153
|
-
if (!exists) {
|
|
1154
|
-
await this.createChunksTable();
|
|
1155
|
-
}
|
|
1156
|
-
console.log("[EXULU] Inserting chunks.");
|
|
1157
|
-
await db3.from(this.getChunksTableName()).insert(chunks.map((chunk) => ({
|
|
1158
|
-
source,
|
|
1159
|
-
content: chunk.content,
|
|
1160
|
-
chunk_index: chunk.index,
|
|
1161
|
-
embedding: pgvector2.toSql(chunk.vector)
|
|
1162
|
-
})));
|
|
1163
|
-
await db3.from(this.getTableName()).where({ id: result[0].id }).update({
|
|
1164
|
-
embeddings_updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1165
|
-
}).returning("id");
|
|
1172
|
+
}, user, {
|
|
1173
|
+
label: this.embedder.name,
|
|
1174
|
+
trigger: trigger || "agent"
|
|
1175
|
+
}, role);
|
|
1166
1176
|
}
|
|
1167
1177
|
return {
|
|
1168
1178
|
id: result[0].id,
|
|
@@ -1176,6 +1186,8 @@ var ExuluContext = class {
|
|
|
1176
1186
|
order,
|
|
1177
1187
|
page,
|
|
1178
1188
|
name,
|
|
1189
|
+
user,
|
|
1190
|
+
role,
|
|
1179
1191
|
archived,
|
|
1180
1192
|
query,
|
|
1181
1193
|
method
|
|
@@ -1244,7 +1256,9 @@ var ExuluContext = class {
|
|
|
1244
1256
|
name: "count",
|
|
1245
1257
|
label: statistics.label,
|
|
1246
1258
|
type: STATISTICS_TYPE_ENUM.CONTEXT_RETRIEVE,
|
|
1247
|
-
trigger: statistics.trigger
|
|
1259
|
+
trigger: statistics.trigger,
|
|
1260
|
+
user,
|
|
1261
|
+
role
|
|
1248
1262
|
});
|
|
1249
1263
|
}
|
|
1250
1264
|
if (this.queryRewriter) {
|
|
@@ -1260,7 +1274,10 @@ var ExuluContext = class {
|
|
|
1260
1274
|
itemsQuery.select(chunksTable + ".chunk_index");
|
|
1261
1275
|
itemsQuery.select(chunksTable + ".created_at as chunk_created_at");
|
|
1262
1276
|
itemsQuery.select(chunksTable + ".updated_at as chunk_updated_at");
|
|
1263
|
-
const { chunks } = await this.embedder.generateFromQuery(query
|
|
1277
|
+
const { chunks } = await this.embedder.generateFromQuery(query, {
|
|
1278
|
+
label: this.name,
|
|
1279
|
+
trigger: "agent"
|
|
1280
|
+
}, user, role);
|
|
1264
1281
|
if (!chunks?.[0]?.vector) {
|
|
1265
1282
|
throw new Error("No vector generated for query.");
|
|
1266
1283
|
}
|
|
@@ -1408,11 +1425,13 @@ var ExuluContext = class {
|
|
|
1408
1425
|
}),
|
|
1409
1426
|
config: [],
|
|
1410
1427
|
description: `Gets information from the context called: ${this.name}. The context description is: ${this.description}.`,
|
|
1411
|
-
execute: async ({ query }) => {
|
|
1428
|
+
execute: async ({ query, user, role }) => {
|
|
1412
1429
|
return await this.getItems({
|
|
1413
1430
|
page: 1,
|
|
1414
1431
|
limit: 10,
|
|
1415
1432
|
query,
|
|
1433
|
+
user,
|
|
1434
|
+
role,
|
|
1416
1435
|
statistics: {
|
|
1417
1436
|
label: this.name,
|
|
1418
1437
|
trigger: "agent"
|
|
@@ -1854,6 +1873,7 @@ import { zerialize } from "zodex";
|
|
|
1854
1873
|
|
|
1855
1874
|
// src/bullmq/queues.ts
|
|
1856
1875
|
import { Queue as Queue3 } from "bullmq";
|
|
1876
|
+
import { BullMQOtel } from "bullmq-otel";
|
|
1857
1877
|
var ExuluQueues = class {
|
|
1858
1878
|
queues;
|
|
1859
1879
|
constructor() {
|
|
@@ -1871,7 +1891,13 @@ var ExuluQueues = class {
|
|
|
1871
1891
|
console.error(`[EXULU] no redis server configured, but you are trying to use a queue ( ${name}), likely in an agent or embedder (look for ExuluQueues.use() ).`);
|
|
1872
1892
|
throw new Error(`[EXULU] no redis server configured.`);
|
|
1873
1893
|
}
|
|
1874
|
-
const newQueue = new Queue3(
|
|
1894
|
+
const newQueue = new Queue3(
|
|
1895
|
+
`${name}`,
|
|
1896
|
+
{
|
|
1897
|
+
connection: redisServer,
|
|
1898
|
+
telemetry: new BullMQOtel("simple-guide")
|
|
1899
|
+
}
|
|
1900
|
+
);
|
|
1875
1901
|
this.queues.push(newQueue);
|
|
1876
1902
|
return newQueue;
|
|
1877
1903
|
}
|
|
@@ -3658,6 +3684,7 @@ Intelligence Management Platform
|
|
|
3658
3684
|
import OpenAI from "openai";
|
|
3659
3685
|
import fs2 from "fs";
|
|
3660
3686
|
import { randomUUID } from "crypto";
|
|
3687
|
+
import "@opentelemetry/api";
|
|
3661
3688
|
var REQUEST_SIZE_LIMIT = "50mb";
|
|
3662
3689
|
var global_queues = {
|
|
3663
3690
|
logs_cleaner: "logs-cleaner"
|
|
@@ -3696,7 +3723,7 @@ var createRecurringJobs = async () => {
|
|
|
3696
3723
|
console.table(recurringJobSchedulersLogs);
|
|
3697
3724
|
return queue;
|
|
3698
3725
|
};
|
|
3699
|
-
var createExpressRoutes = async (app, agents, tools, contexts) => {
|
|
3726
|
+
var createExpressRoutes = async (app, logger, agents, tools, contexts, config, tracer) => {
|
|
3700
3727
|
const routeLogs = [];
|
|
3701
3728
|
var corsOptions = {
|
|
3702
3729
|
origin: "*",
|
|
@@ -3790,6 +3817,16 @@ var createExpressRoutes = async (app, agents, tools, contexts) => {
|
|
|
3790
3817
|
express.json({ limit: REQUEST_SIZE_LIMIT }),
|
|
3791
3818
|
expressMiddleware(server, {
|
|
3792
3819
|
context: async ({ req }) => {
|
|
3820
|
+
logger.info("================");
|
|
3821
|
+
logger.info({
|
|
3822
|
+
message: "Incoming Request",
|
|
3823
|
+
method: req.method,
|
|
3824
|
+
path: req.path,
|
|
3825
|
+
requestId: "req-" + Date.now(),
|
|
3826
|
+
ipAddress: req.ip,
|
|
3827
|
+
userAgent: req.get("User-Agent")
|
|
3828
|
+
});
|
|
3829
|
+
logger.info("================");
|
|
3793
3830
|
const authenticationResult = await requestValidators.authenticate(req);
|
|
3794
3831
|
if (!authenticationResult.user?.id) {
|
|
3795
3832
|
throw new Error(authenticationResult.message);
|
|
@@ -4176,7 +4213,15 @@ Mood: friendly and intelligent.
|
|
|
4176
4213
|
if (!exists) {
|
|
4177
4214
|
await context.createItemsTable();
|
|
4178
4215
|
}
|
|
4179
|
-
const result = await context.updateItem(
|
|
4216
|
+
const result = await context.updateItem(
|
|
4217
|
+
authenticationResult.user.id,
|
|
4218
|
+
{
|
|
4219
|
+
...req.body,
|
|
4220
|
+
id: req.params.id
|
|
4221
|
+
},
|
|
4222
|
+
authenticationResult.user.role?.id,
|
|
4223
|
+
authenticationResult.user.type === "api" ? "api" : "user"
|
|
4224
|
+
);
|
|
4180
4225
|
res.status(200).json({
|
|
4181
4226
|
message: "Item updated successfully.",
|
|
4182
4227
|
id: result
|
|
@@ -4223,7 +4268,13 @@ Mood: friendly and intelligent.
|
|
|
4223
4268
|
await context.createItemsTable();
|
|
4224
4269
|
}
|
|
4225
4270
|
console.log("[EXULU] inserting item", req.body);
|
|
4226
|
-
const result = await context.insertItem(
|
|
4271
|
+
const result = await context.insertItem(
|
|
4272
|
+
authenticationResult.user.id,
|
|
4273
|
+
req.body,
|
|
4274
|
+
!!req.body.upsert,
|
|
4275
|
+
authenticationResult.user.role?.id,
|
|
4276
|
+
authenticationResult.user.type === "api" ? "api" : "user"
|
|
4277
|
+
);
|
|
4227
4278
|
console.log("[EXULU] result", result);
|
|
4228
4279
|
res.status(200).json({
|
|
4229
4280
|
message: "Item created successfully.",
|
|
@@ -4284,6 +4335,8 @@ Mood: friendly and intelligent.
|
|
|
4284
4335
|
const result = await context.getItems({
|
|
4285
4336
|
sort,
|
|
4286
4337
|
order,
|
|
4338
|
+
user: authenticationResult.user?.id,
|
|
4339
|
+
role: authenticationResult.user?.role?.id,
|
|
4287
4340
|
page,
|
|
4288
4341
|
limit,
|
|
4289
4342
|
archived: req.query.archived === "true",
|
|
@@ -4445,6 +4498,8 @@ Mood: friendly and intelligent.
|
|
|
4445
4498
|
const items = await context.getItems({
|
|
4446
4499
|
page: 1,
|
|
4447
4500
|
// todo add pagination
|
|
4501
|
+
user: authenticationResult.user?.id,
|
|
4502
|
+
role: authenticationResult.user?.role?.id,
|
|
4448
4503
|
limit: 500
|
|
4449
4504
|
});
|
|
4450
4505
|
const csv = Papa.unparse(items);
|
|
@@ -4568,7 +4623,7 @@ Mood: friendly and intelligent.
|
|
|
4568
4623
|
return;
|
|
4569
4624
|
}
|
|
4570
4625
|
console.log("[EXULU] agent tools", agentInstance.tools);
|
|
4571
|
-
const enabledTools = agentInstance.tools.map(({ config, toolId }) => tools.find(({ id }) => id === toolId)).filter(Boolean);
|
|
4626
|
+
const enabledTools = agentInstance.tools.map(({ config: config2, toolId }) => tools.find(({ id }) => id === toolId)).filter(Boolean);
|
|
4572
4627
|
console.log("[EXULU] enabled tools", enabledTools);
|
|
4573
4628
|
const variableName = agentInstance.providerApiKey;
|
|
4574
4629
|
const variable = await db3.from("variables").where({ name: variableName }).first();
|
|
@@ -4595,7 +4650,8 @@ Mood: friendly and intelligent.
|
|
|
4595
4650
|
res,
|
|
4596
4651
|
req
|
|
4597
4652
|
},
|
|
4598
|
-
user:
|
|
4653
|
+
user: user?.id,
|
|
4654
|
+
role: user?.role?.id,
|
|
4599
4655
|
session: headers.session,
|
|
4600
4656
|
message: req.body.message,
|
|
4601
4657
|
tools: enabledTools,
|
|
@@ -4609,8 +4665,9 @@ Mood: friendly and intelligent.
|
|
|
4609
4665
|
return;
|
|
4610
4666
|
} else {
|
|
4611
4667
|
const response = await agent.generateSync({
|
|
4612
|
-
user:
|
|
4668
|
+
user: user?.id,
|
|
4613
4669
|
session: headers.session,
|
|
4670
|
+
role: user?.role?.id,
|
|
4614
4671
|
message: req.body.message,
|
|
4615
4672
|
tools: enabledTools.map((tool2) => tool2.tool),
|
|
4616
4673
|
providerApiKey,
|
|
@@ -4709,7 +4766,9 @@ Mood: friendly and intelligent.
|
|
|
4709
4766
|
label: "Claude Code",
|
|
4710
4767
|
type: STATISTICS_TYPE_ENUM.AGENT_RUN,
|
|
4711
4768
|
trigger: "claude-code",
|
|
4712
|
-
count: 1
|
|
4769
|
+
count: 1,
|
|
4770
|
+
user: authenticationResult.user?.id,
|
|
4771
|
+
role: authenticationResult.user.role?.id
|
|
4713
4772
|
});
|
|
4714
4773
|
response.headers.forEach((value, key) => {
|
|
4715
4774
|
res.setHeader(key, value);
|
|
@@ -4770,43 +4829,22 @@ import IORedis from "ioredis";
|
|
|
4770
4829
|
import { Worker } from "bullmq";
|
|
4771
4830
|
|
|
4772
4831
|
// src/registry/utils.ts
|
|
4773
|
-
import "bullmq";
|
|
4774
4832
|
var bullmq = {
|
|
4775
|
-
validate: (
|
|
4776
|
-
if (!
|
|
4777
|
-
throw new Error(`Missing job data for job ${
|
|
4778
|
-
}
|
|
4779
|
-
if (!bullmqJob.data.type) {
|
|
4780
|
-
throw new Error(`Missing property "type" in data for job ${bullmqJob.id}.`);
|
|
4833
|
+
validate: (id, data) => {
|
|
4834
|
+
if (!data) {
|
|
4835
|
+
throw new Error(`Missing job data for job ${id}.`);
|
|
4781
4836
|
}
|
|
4782
|
-
if (!
|
|
4783
|
-
throw new Error(`Missing property "
|
|
4837
|
+
if (!data.type) {
|
|
4838
|
+
throw new Error(`Missing property "type" in data for job ${id}.`);
|
|
4784
4839
|
}
|
|
4785
|
-
if (
|
|
4786
|
-
throw new Error(`
|
|
4840
|
+
if (!data.inputs) {
|
|
4841
|
+
throw new Error(`Missing property "inputs" in data for job ${id}.`);
|
|
4787
4842
|
}
|
|
4788
|
-
if (
|
|
4789
|
-
throw new Error(`Property "
|
|
4843
|
+
if (data.type !== "embedder" && data.type !== "workflow") {
|
|
4844
|
+
throw new Error(`Property "type" in data for job ${id} must be of value "embedder" or "workflow".`);
|
|
4790
4845
|
}
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
workflow: async (bullmqJob, exuluJob, workflow, logsDir) => {
|
|
4794
|
-
if (!workflow) {
|
|
4795
|
-
throw new Error(`Workflow function with id: ${bullmqJob.data.backend} not found in registry.`);
|
|
4796
|
-
}
|
|
4797
|
-
console.log("[EXULU] starting workflow with job inputs.", bullmqJob.data.inputs);
|
|
4798
|
-
const logger = new ExuluLogger(exuluJob, logsDir);
|
|
4799
|
-
const output = await workflow.start({
|
|
4800
|
-
job: exuluJob,
|
|
4801
|
-
inputs: bullmqJob.data.inputs,
|
|
4802
|
-
user: bullmqJob.data.user,
|
|
4803
|
-
logger,
|
|
4804
|
-
session: bullmqJob.data.session,
|
|
4805
|
-
agent: bullmqJob.data.agent,
|
|
4806
|
-
label: bullmqJob.data.label
|
|
4807
|
-
});
|
|
4808
|
-
await logger.write(`Workflow completed. ${JSON.stringify(output)}`, "INFO");
|
|
4809
|
-
return output;
|
|
4846
|
+
if (!data.workflow && !data.embedder) {
|
|
4847
|
+
throw new Error(`Either a workflow or embedder must be set for job ${id}.`);
|
|
4810
4848
|
}
|
|
4811
4849
|
}
|
|
4812
4850
|
};
|
|
@@ -4814,9 +4852,10 @@ var bullmq = {
|
|
|
4814
4852
|
// src/registry/workers.ts
|
|
4815
4853
|
import * as fs3 from "fs";
|
|
4816
4854
|
import path2 from "path";
|
|
4855
|
+
import "@opentelemetry/api";
|
|
4817
4856
|
var defaultLogsDir = path2.join(process.cwd(), "logs");
|
|
4818
4857
|
var redisConnection;
|
|
4819
|
-
var createWorkers = async (queues2, contexts, _logsDir) => {
|
|
4858
|
+
var createWorkers = async (queues2, logger, contexts, _logsDir, tracer) => {
|
|
4820
4859
|
if (!redisServer.host || !redisServer.port) {
|
|
4821
4860
|
console.error("[EXULU] you are trying to start worker, but no redis server is configured in the environment.");
|
|
4822
4861
|
throw new Error("No redis server configured in the environment, so cannot start worker.");
|
|
@@ -4833,84 +4872,30 @@ var createWorkers = async (queues2, contexts, _logsDir) => {
|
|
|
4833
4872
|
const worker = new Worker(
|
|
4834
4873
|
`${queue}`,
|
|
4835
4874
|
async (bullmqJob) => {
|
|
4875
|
+
const logger2 = new ExuluLogger(bullmqJob, logsDir);
|
|
4836
4876
|
const { db: db3 } = await postgresClient();
|
|
4837
4877
|
try {
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
}
|
|
4843
|
-
const context = contexts.find((context2) => context2.id === bullmqJob.data.context);
|
|
4878
|
+
const data = bullmqJob.data;
|
|
4879
|
+
bullmq.validate(bullmqJob.id, data);
|
|
4880
|
+
if (data.type === "embedder") {
|
|
4881
|
+
const context = contexts.find((context2) => context2.id === data.context);
|
|
4844
4882
|
if (!context) {
|
|
4845
|
-
throw new Error(`Context ${
|
|
4883
|
+
throw new Error(`Context ${data.context} not found in the registry.`);
|
|
4846
4884
|
}
|
|
4847
|
-
if (!
|
|
4885
|
+
if (!data.embedder) {
|
|
4848
4886
|
throw new Error(`No embedder set for embedder job.`);
|
|
4849
4887
|
}
|
|
4850
|
-
const embedder = contexts.find((context2) => context2.embedder?.id ===
|
|
4888
|
+
const embedder = contexts.find((context2) => context2.embedder?.id === data.embedder);
|
|
4851
4889
|
if (!embedder) {
|
|
4852
|
-
throw new Error(`Embedder ${
|
|
4853
|
-
}
|
|
4854
|
-
if (!bullmqJob.data.source) {
|
|
4855
|
-
throw new Error("No source set for embedder job.");
|
|
4856
|
-
}
|
|
4857
|
-
const source = context.sources.get(bullmqJob.data.source);
|
|
4858
|
-
if (!source) {
|
|
4859
|
-
throw new Error(`Source ${bullmqJob.data.source} not found in the registry.`);
|
|
4860
|
-
}
|
|
4861
|
-
if (!bullmqJob.data.updater) {
|
|
4862
|
-
throw new Error("No updater set for embedder job.");
|
|
4890
|
+
throw new Error(`Embedder ${data.embedder} not found in the registry.`);
|
|
4863
4891
|
}
|
|
4864
|
-
const
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
}
|
|
4868
|
-
if (!bullmqJob.data.documents) {
|
|
4869
|
-
throw new Error("No input documents set for embedder job.");
|
|
4870
|
-
}
|
|
4871
|
-
if (!Array.isArray(bullmqJob.data.documents)) {
|
|
4872
|
-
throw new Error("Input documents must be an array.");
|
|
4873
|
-
}
|
|
4874
|
-
const result = await embedder.upsert(bullmqJob.data.context, bullmqJob.data.documents, {
|
|
4875
|
-
label: context.name,
|
|
4876
|
-
trigger: bullmqJob.data.trigger || "unknown"
|
|
4892
|
+
const result = await context.createAndUpsertEmbeddings(data.inputs, data.user, {
|
|
4893
|
+
label: embedder.name,
|
|
4894
|
+
trigger: data.trigger
|
|
4877
4895
|
});
|
|
4878
|
-
const mongoRecord = await db3.from("jobs").where({ redis: bullmqJob.id }).first();
|
|
4879
|
-
if (!mongoRecord) {
|
|
4880
|
-
throw new Error("Job not found in the database.");
|
|
4881
|
-
}
|
|
4882
|
-
const finishedAt = /* @__PURE__ */ new Date();
|
|
4883
|
-
const duration = (finishedAt.getTime() - new Date(mongoRecord.createdAt).getTime()) / 1e3;
|
|
4884
|
-
await db3.from("jobs").where({ redis: bullmqJob.id }).update({
|
|
4885
|
-
status: "completed",
|
|
4886
|
-
finishedAt,
|
|
4887
|
-
duration,
|
|
4888
|
-
result: JSON.stringify(result)
|
|
4889
|
-
});
|
|
4890
|
-
await db3.from((void 0).getTableName()).where({ id: result[0].id }).update({
|
|
4891
|
-
embeddings_updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
4892
|
-
}).returning("id");
|
|
4893
4896
|
return result;
|
|
4894
4897
|
}
|
|
4895
4898
|
if (bullmqJob.data.type === "workflow") {
|
|
4896
|
-
const workflow = workflows.find((workflow2) => workflow2.id === bullmqJob.data.workflow);
|
|
4897
|
-
if (!workflow) {
|
|
4898
|
-
throw new Error(`Workflow ${bullmqJob.data.workflow} not found in the registry.`);
|
|
4899
|
-
}
|
|
4900
|
-
const exuluJob = await db3.from("jobs").where({ redis: bullmqJob.id }).first();
|
|
4901
|
-
if (!exuluJob) {
|
|
4902
|
-
throw new Error("Job not found in the database.");
|
|
4903
|
-
}
|
|
4904
|
-
const result = await bullmq.process.workflow(bullmqJob, exuluJob, workflow, logsDir);
|
|
4905
|
-
const finishedAt = /* @__PURE__ */ new Date();
|
|
4906
|
-
const duration = (finishedAt.getTime() - new Date(exuluJob.createdAt).getTime()) / 1e3;
|
|
4907
|
-
await db3.from("jobs").where({ redis: bullmqJob.id }).update({
|
|
4908
|
-
status: "completed",
|
|
4909
|
-
finishedAt,
|
|
4910
|
-
duration,
|
|
4911
|
-
result: JSON.stringify(result)
|
|
4912
|
-
});
|
|
4913
|
-
return result;
|
|
4914
4899
|
}
|
|
4915
4900
|
} catch (error) {
|
|
4916
4901
|
await db3.from("jobs").where({ redis: bullmqJob.id }).update({
|
|
@@ -4980,6 +4965,7 @@ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/
|
|
|
4980
4965
|
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
4981
4966
|
import { z as z2 } from "zod";
|
|
4982
4967
|
import "express";
|
|
4968
|
+
import "@opentelemetry/api";
|
|
4983
4969
|
var SESSION_ID_HEADER = "mcp-session-id";
|
|
4984
4970
|
var ExuluMCP = class {
|
|
4985
4971
|
server;
|
|
@@ -4987,7 +4973,7 @@ var ExuluMCP = class {
|
|
|
4987
4973
|
express;
|
|
4988
4974
|
constructor() {
|
|
4989
4975
|
}
|
|
4990
|
-
create = async ({ express: express3, contexts, agents, config, tools }) => {
|
|
4976
|
+
create = async ({ express: express3, contexts, agents, config, tools, tracer, logger }) => {
|
|
4991
4977
|
this.express = express3;
|
|
4992
4978
|
if (!this.server) {
|
|
4993
4979
|
console.log("[EXULU] Creating MCP server.");
|
|
@@ -5165,6 +5151,38 @@ var defaultAgent = new ExuluAgent({
|
|
|
5165
5151
|
}
|
|
5166
5152
|
});
|
|
5167
5153
|
|
|
5154
|
+
// src/registry/index.ts
|
|
5155
|
+
import { trace as trace2 } from "@opentelemetry/api";
|
|
5156
|
+
|
|
5157
|
+
// src/registry/logger.ts
|
|
5158
|
+
import { OpenTelemetryTransportV3 } from "@opentelemetry/winston-transport";
|
|
5159
|
+
import winston from "winston";
|
|
5160
|
+
var createLogger = ({
|
|
5161
|
+
enableOtel
|
|
5162
|
+
}) => {
|
|
5163
|
+
const logger = winston.createLogger({
|
|
5164
|
+
level: "debug",
|
|
5165
|
+
format: winston.format.combine(
|
|
5166
|
+
winston.format.timestamp(),
|
|
5167
|
+
winston.format.errors({
|
|
5168
|
+
stack: true
|
|
5169
|
+
}),
|
|
5170
|
+
winston.format.metadata(),
|
|
5171
|
+
winston.format.json()
|
|
5172
|
+
),
|
|
5173
|
+
defaultMeta: {
|
|
5174
|
+
service: "Test-Exulu",
|
|
5175
|
+
environment: process.env.NODE_ENV || "development"
|
|
5176
|
+
},
|
|
5177
|
+
transports: [
|
|
5178
|
+
new winston.transports.Console(),
|
|
5179
|
+
...enableOtel ? [new OpenTelemetryTransportV3()] : []
|
|
5180
|
+
]
|
|
5181
|
+
});
|
|
5182
|
+
return logger;
|
|
5183
|
+
};
|
|
5184
|
+
var logger_default = createLogger;
|
|
5185
|
+
|
|
5168
5186
|
// src/registry/index.ts
|
|
5169
5187
|
var ExuluApp = class {
|
|
5170
5188
|
_agents = [];
|
|
@@ -5231,10 +5249,20 @@ var ExuluApp = class {
|
|
|
5231
5249
|
bullmq = {
|
|
5232
5250
|
workers: {
|
|
5233
5251
|
create: async () => {
|
|
5252
|
+
let tracer;
|
|
5253
|
+
if (this._config?.telemetry?.enabled) {
|
|
5254
|
+
console.log("[EXULU] telemetry enabled");
|
|
5255
|
+
tracer = trace2.getTracer("exulu", "1.0.0");
|
|
5256
|
+
}
|
|
5257
|
+
const logger = logger_default({
|
|
5258
|
+
enableOtel: this._config?.workers?.telemetry?.enabled ?? false
|
|
5259
|
+
});
|
|
5234
5260
|
return await createWorkers(
|
|
5235
5261
|
this._queues,
|
|
5262
|
+
logger,
|
|
5236
5263
|
Object.values(this._contexts ?? {}),
|
|
5237
|
-
this._config?.workers?.logsDir
|
|
5264
|
+
this._config?.workers?.logsDir,
|
|
5265
|
+
tracer
|
|
5238
5266
|
);
|
|
5239
5267
|
}
|
|
5240
5268
|
}
|
|
@@ -5246,11 +5274,22 @@ var ExuluApp = class {
|
|
|
5246
5274
|
throw new Error("Express app not initialized");
|
|
5247
5275
|
}
|
|
5248
5276
|
const app = this._expressApp;
|
|
5277
|
+
let tracer;
|
|
5278
|
+
if (this._config?.telemetry?.enabled) {
|
|
5279
|
+
console.log("[EXULU] telemetry enabled");
|
|
5280
|
+
tracer = trace2.getTracer("exulu", "1.0.0");
|
|
5281
|
+
}
|
|
5282
|
+
const logger = logger_default({
|
|
5283
|
+
enableOtel: this._config?.telemetry?.enabled ?? false
|
|
5284
|
+
});
|
|
5249
5285
|
await createExpressRoutes(
|
|
5250
5286
|
app,
|
|
5287
|
+
logger,
|
|
5251
5288
|
this._agents,
|
|
5252
5289
|
this._tools,
|
|
5253
|
-
Object.values(this._contexts ?? {})
|
|
5290
|
+
Object.values(this._contexts ?? {}),
|
|
5291
|
+
this._config,
|
|
5292
|
+
tracer
|
|
5254
5293
|
);
|
|
5255
5294
|
if (this._config?.MCP.enabled) {
|
|
5256
5295
|
const mcp = new ExuluMCP();
|
|
@@ -5259,7 +5298,9 @@ var ExuluApp = class {
|
|
|
5259
5298
|
contexts: this._contexts,
|
|
5260
5299
|
agents: this._agents,
|
|
5261
5300
|
config: this._config,
|
|
5262
|
-
tools: this._tools
|
|
5301
|
+
tools: this._tools,
|
|
5302
|
+
tracer,
|
|
5303
|
+
logger
|
|
5263
5304
|
});
|
|
5264
5305
|
await mcp.connect();
|
|
5265
5306
|
}
|
|
@@ -6672,6 +6713,50 @@ var execute = async () => {
|
|
|
6672
6713
|
return;
|
|
6673
6714
|
};
|
|
6674
6715
|
|
|
6716
|
+
// src/registry/otel.ts
|
|
6717
|
+
import process2 from "process";
|
|
6718
|
+
import { NodeSDK } from "@opentelemetry/sdk-node";
|
|
6719
|
+
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
|
|
6720
|
+
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
|
|
6721
|
+
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
|
|
6722
|
+
import { resourceFromAttributes } from "@opentelemetry/resources";
|
|
6723
|
+
import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
|
|
6724
|
+
import { BatchLogRecordProcessor } from "@opentelemetry/sdk-logs";
|
|
6725
|
+
var create = ({
|
|
6726
|
+
SIGNOZ_ACCESS_TOKEN,
|
|
6727
|
+
SIGNOZ_TRACES_URL,
|
|
6728
|
+
SIGNOZ_LOGS_URL
|
|
6729
|
+
}) => {
|
|
6730
|
+
console.log("SIGNOZ_ACCESS_TOKEN", SIGNOZ_ACCESS_TOKEN);
|
|
6731
|
+
console.log("SIGNOZ_TRACES_URL", SIGNOZ_TRACES_URL);
|
|
6732
|
+
console.log("SIGNOZ_LOGS_URL", SIGNOZ_LOGS_URL);
|
|
6733
|
+
const exporterOptions = {
|
|
6734
|
+
url: SIGNOZ_TRACES_URL,
|
|
6735
|
+
headers: {
|
|
6736
|
+
"signoz-access-token": SIGNOZ_ACCESS_TOKEN
|
|
6737
|
+
}
|
|
6738
|
+
};
|
|
6739
|
+
const traceExporter = new OTLPTraceExporter(exporterOptions);
|
|
6740
|
+
const logExporter = new OTLPLogExporter({
|
|
6741
|
+
url: SIGNOZ_LOGS_URL,
|
|
6742
|
+
headers: {
|
|
6743
|
+
"signoz-access-token": SIGNOZ_ACCESS_TOKEN
|
|
6744
|
+
}
|
|
6745
|
+
});
|
|
6746
|
+
const sdk = new NodeSDK({
|
|
6747
|
+
traceExporter,
|
|
6748
|
+
logRecordProcessors: [new BatchLogRecordProcessor(logExporter)],
|
|
6749
|
+
instrumentations: [getNodeAutoInstrumentations()],
|
|
6750
|
+
resource: resourceFromAttributes({
|
|
6751
|
+
[ATTR_SERVICE_NAME]: "Test-Exulu"
|
|
6752
|
+
})
|
|
6753
|
+
});
|
|
6754
|
+
process2.on("SIGTERM", () => {
|
|
6755
|
+
sdk.shutdown().then(() => console.log("Tracing terminated")).catch((error) => console.log("Error terminating tracing", error)).finally(() => process2.exit(0));
|
|
6756
|
+
});
|
|
6757
|
+
return sdk;
|
|
6758
|
+
};
|
|
6759
|
+
|
|
6675
6760
|
// types/enums/jobs.ts
|
|
6676
6761
|
var JOB_STATUS_ENUM = {
|
|
6677
6762
|
completed: "completed",
|
|
@@ -6690,6 +6775,19 @@ var ExuluJobs = {
|
|
|
6690
6775
|
validate: validateJob
|
|
6691
6776
|
}
|
|
6692
6777
|
};
|
|
6778
|
+
var ExuluOtel = {
|
|
6779
|
+
create: ({
|
|
6780
|
+
SIGNOZ_ACCESS_TOKEN,
|
|
6781
|
+
SIGNOZ_TRACES_URL,
|
|
6782
|
+
SIGNOZ_LOGS_URL
|
|
6783
|
+
}) => {
|
|
6784
|
+
return create({
|
|
6785
|
+
SIGNOZ_ACCESS_TOKEN,
|
|
6786
|
+
SIGNOZ_TRACES_URL,
|
|
6787
|
+
SIGNOZ_LOGS_URL
|
|
6788
|
+
});
|
|
6789
|
+
}
|
|
6790
|
+
};
|
|
6693
6791
|
var db2 = {
|
|
6694
6792
|
init: async () => {
|
|
6695
6793
|
await execute();
|
|
@@ -6721,6 +6819,7 @@ export {
|
|
|
6721
6819
|
ExuluEval,
|
|
6722
6820
|
ExuluJobs,
|
|
6723
6821
|
ExuluLogger,
|
|
6822
|
+
ExuluOtel,
|
|
6724
6823
|
queues as ExuluQueues,
|
|
6725
6824
|
ExuluSource,
|
|
6726
6825
|
ExuluTool,
|