@exulu/backend 0.1.9 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +116 -119
- package/dist/index.js +116 -119
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -59,18 +59,19 @@ var redisServer = {
|
|
|
59
59
|
var client = {};
|
|
60
60
|
async function redisClient() {
|
|
61
61
|
if (!redisServer.host || !redisServer.port) {
|
|
62
|
-
return {
|
|
63
|
-
client: null
|
|
64
|
-
};
|
|
62
|
+
return { client: null };
|
|
65
63
|
}
|
|
66
64
|
if (!client["exulu"]) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
65
|
+
try {
|
|
66
|
+
const url = `redis://${redisServer.host}:${redisServer.port}`;
|
|
67
|
+
client["exulu"] = (0, import_redis.createClient)({
|
|
68
|
+
url
|
|
69
|
+
});
|
|
70
|
+
await client["exulu"].connect();
|
|
71
|
+
} catch (error) {
|
|
72
|
+
console.error(`[EXULU] error connecting to redis:`, error);
|
|
73
|
+
return { client: null };
|
|
74
|
+
}
|
|
74
75
|
}
|
|
75
76
|
return {
|
|
76
77
|
client: client["exulu"]
|
|
@@ -113,25 +114,30 @@ var import_knex3 = require("pgvector/knex");
|
|
|
113
114
|
var db = {};
|
|
114
115
|
async function postgresClient() {
|
|
115
116
|
if (!db["exulu"]) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
117
|
+
try {
|
|
118
|
+
console.log("[EXULU] Initializing exulu database.");
|
|
119
|
+
console.log(process.env.POSTGRES_DB_HOST);
|
|
120
|
+
console.log(process.env.POSTGRES_DB_PORT);
|
|
121
|
+
console.log(process.env.POSTGRES_DB_USER);
|
|
122
|
+
console.log(process.env.POSTGRES_DB_PASSWORD);
|
|
123
|
+
console.log(process.env.POSTGRES_DB_SSL);
|
|
124
|
+
const knex = (0, import_knex.default)({
|
|
125
|
+
client: "pg",
|
|
126
|
+
connection: {
|
|
127
|
+
host: process.env.POSTGRES_DB_HOST,
|
|
128
|
+
port: parseInt(process.env.POSTGRES_DB_PORT || "5432"),
|
|
129
|
+
user: process.env.POSTGRES_DB_USER,
|
|
130
|
+
database: "exulu",
|
|
131
|
+
password: process.env.POSTGRES_DB_PASSWORD,
|
|
132
|
+
ssl: process.env.POSTGRES_DB_SSL === "true" ? { rejectUnauthorized: false } : false
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
await knex.schema.createExtensionIfNotExists("vector");
|
|
136
|
+
db["exulu"] = knex;
|
|
137
|
+
} catch (error) {
|
|
138
|
+
console.error("[EXULU] Error initializing exulu database.", error);
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
135
141
|
}
|
|
136
142
|
return {
|
|
137
143
|
db: db["exulu"]
|
|
@@ -547,8 +553,8 @@ var up = async function(knex) {
|
|
|
547
553
|
}
|
|
548
554
|
};
|
|
549
555
|
var SALT_ROUNDS = 12;
|
|
550
|
-
async function encryptApiKey(
|
|
551
|
-
const hash = await import_bcryptjs.default.hash(
|
|
556
|
+
async function encryptApiKey(apikey) {
|
|
557
|
+
const hash = await import_bcryptjs.default.hash(apikey, SALT_ROUNDS);
|
|
552
558
|
return hash;
|
|
553
559
|
}
|
|
554
560
|
var execute = async () => {
|
|
@@ -596,7 +602,7 @@ var execute = async () => {
|
|
|
596
602
|
super_admin: true,
|
|
597
603
|
createdAt: /* @__PURE__ */ new Date(),
|
|
598
604
|
updatedAt: /* @__PURE__ */ new Date(),
|
|
599
|
-
type: "
|
|
605
|
+
type: "api",
|
|
600
606
|
apikey: `${encryptedKey}${postFix}`,
|
|
601
607
|
// password: "admin", todo add this again when we implement password auth / encryption as alternative to OTP
|
|
602
608
|
role: roleId
|
|
@@ -1424,30 +1430,38 @@ var import_express3 = require("express");
|
|
|
1424
1430
|
|
|
1425
1431
|
// src/registry/rate-limiter.ts
|
|
1426
1432
|
var rateLimiter = async (key, windowSeconds, limit, points) => {
|
|
1427
|
-
|
|
1428
|
-
|
|
1433
|
+
try {
|
|
1434
|
+
const { client: client2 } = await redisClient();
|
|
1435
|
+
if (!client2) {
|
|
1436
|
+
console.warn("[EXULU] Rate limiting disabled - Redis not available");
|
|
1437
|
+
return {
|
|
1438
|
+
status: true,
|
|
1439
|
+
retryAfter: null
|
|
1440
|
+
};
|
|
1441
|
+
}
|
|
1442
|
+
const redisKey = `exulu/${key}`;
|
|
1443
|
+
const current = await client2.incrBy(redisKey, points);
|
|
1444
|
+
if (current === points) {
|
|
1445
|
+
await client2.expire(redisKey, windowSeconds);
|
|
1446
|
+
}
|
|
1447
|
+
if (current > limit) {
|
|
1448
|
+
const ttl = await client2.ttl(redisKey);
|
|
1449
|
+
return {
|
|
1450
|
+
status: false,
|
|
1451
|
+
retryAfter: ttl
|
|
1452
|
+
};
|
|
1453
|
+
}
|
|
1429
1454
|
return {
|
|
1430
|
-
status:
|
|
1431
|
-
retryAfter:
|
|
1432
|
-
// 10 seconds
|
|
1455
|
+
status: true,
|
|
1456
|
+
retryAfter: null
|
|
1433
1457
|
};
|
|
1434
|
-
}
|
|
1435
|
-
|
|
1436
|
-
const current = await client2.incrBy(redisKey, points);
|
|
1437
|
-
if (current === points) {
|
|
1438
|
-
await client2.expire(redisKey, windowSeconds);
|
|
1439
|
-
}
|
|
1440
|
-
if (current > limit) {
|
|
1441
|
-
const ttl = await client2.ttl(redisKey);
|
|
1458
|
+
} catch (error) {
|
|
1459
|
+
console.error("[EXULU] Rate limiting error:", error);
|
|
1442
1460
|
return {
|
|
1443
|
-
status:
|
|
1444
|
-
retryAfter:
|
|
1461
|
+
status: true,
|
|
1462
|
+
retryAfter: null
|
|
1445
1463
|
};
|
|
1446
1464
|
}
|
|
1447
|
-
return {
|
|
1448
|
-
status: true,
|
|
1449
|
-
retryAfter: null
|
|
1450
|
-
};
|
|
1451
1465
|
};
|
|
1452
1466
|
|
|
1453
1467
|
// src/registry/route-validators/index.ts
|
|
@@ -1546,10 +1560,10 @@ var authentication = async ({
|
|
|
1546
1560
|
code: 401
|
|
1547
1561
|
};
|
|
1548
1562
|
}
|
|
1549
|
-
const filtered = users.filter(({
|
|
1563
|
+
const filtered = users.filter(({ apikey: apikey2, id }) => apikey2.includes(keyName));
|
|
1550
1564
|
for (const user of filtered) {
|
|
1551
|
-
const lastSlashIndex = user.
|
|
1552
|
-
const compareValue = lastSlashIndex !== -1 ? user.
|
|
1565
|
+
const lastSlashIndex = user.apikey.lastIndexOf("/");
|
|
1566
|
+
const compareValue = lastSlashIndex !== -1 ? user.apikey.substring(0, lastSlashIndex) : user.apikey;
|
|
1553
1567
|
const isMatch = await import_bcryptjs2.default.compare(keyValue, compareValue);
|
|
1554
1568
|
if (isMatch) {
|
|
1555
1569
|
await db2.from("users").where({ id: user.id }).update({
|
|
@@ -1741,6 +1755,10 @@ var ExuluQueues = class {
|
|
|
1741
1755
|
if (existing) {
|
|
1742
1756
|
return existing;
|
|
1743
1757
|
}
|
|
1758
|
+
if (!redisServer.host?.length || !redisServer.port?.length) {
|
|
1759
|
+
console.error(`[EXULU] no redis server configured, but you are trying to use a queue ( ${name}), likely in an agent or workflow (look for ExuluQueues.use() ).`);
|
|
1760
|
+
throw new Error(`[EXULU] no redis server configured.`);
|
|
1761
|
+
}
|
|
1744
1762
|
const newQueue = new import_bullmq5.Queue(`${name}`, { connection: redisServer });
|
|
1745
1763
|
this.queues.push(newQueue);
|
|
1746
1764
|
return newQueue;
|
|
@@ -2160,9 +2178,6 @@ var createUppyRoutes = async (app) => {
|
|
|
2160
2178
|
return stsClient;
|
|
2161
2179
|
}
|
|
2162
2180
|
app.use(bodyParser.urlencoded({ extended: true }), bodyParser.json());
|
|
2163
|
-
app.get("/", (req, res) => {
|
|
2164
|
-
res.json("Exulu upload server.");
|
|
2165
|
-
});
|
|
2166
2181
|
app.get("/s3/list", async (req, res, next) => {
|
|
2167
2182
|
const apikey = req.headers["exulu-api-key"] || null;
|
|
2168
2183
|
let authtoken = null;
|
|
@@ -2496,6 +2511,7 @@ var createUppyRoutes = async (app) => {
|
|
|
2496
2511
|
};
|
|
2497
2512
|
|
|
2498
2513
|
// src/registry/routes.ts
|
|
2514
|
+
var import_utils = require("@apollo/utils.keyvaluecache");
|
|
2499
2515
|
var Papa = require("papaparse");
|
|
2500
2516
|
var global_queues = {
|
|
2501
2517
|
logs_cleaner: "logs-cleaner"
|
|
@@ -2589,10 +2605,21 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
2589
2605
|
{ route: "/items/export/:context", method: "GET", note: "Export items from context" },
|
|
2590
2606
|
{ route: "/graphql", method: "POST", note: "GraphQL endpoint" }
|
|
2591
2607
|
);
|
|
2592
|
-
|
|
2608
|
+
if (redisServer.host?.length && redisServer.port?.length) {
|
|
2609
|
+
await createRecurringJobs();
|
|
2610
|
+
} else {
|
|
2611
|
+
console.log("===========================", "[EXULU] no redis server configured, not setting up recurring jobs.", "===========================");
|
|
2612
|
+
}
|
|
2593
2613
|
const schema = createSDL([usersSchema, rolesSchema, agentsSchema, jobsSchema]);
|
|
2594
|
-
|
|
2614
|
+
console.log("[EXULU] graphql server");
|
|
2615
|
+
const server = new import_server3.ApolloServer({
|
|
2616
|
+
cache: new import_utils.InMemoryLRUCache(),
|
|
2617
|
+
schema,
|
|
2618
|
+
introspection: true
|
|
2619
|
+
});
|
|
2620
|
+
console.log("[EXULU] starting graphql server");
|
|
2595
2621
|
await server.start();
|
|
2622
|
+
console.log("[EXULU] graphql server started");
|
|
2596
2623
|
app.use(
|
|
2597
2624
|
"/graphql",
|
|
2598
2625
|
(0, import_cors.default)(),
|
|
@@ -2649,7 +2676,6 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
2649
2676
|
}
|
|
2650
2677
|
});
|
|
2651
2678
|
});
|
|
2652
|
-
console.log("tools", tools);
|
|
2653
2679
|
app.get("/tools", async (req, res) => {
|
|
2654
2680
|
res.status(200).json(tools.map((tool) => ({
|
|
2655
2681
|
id: tool.id,
|
|
@@ -2926,6 +2952,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
2926
2952
|
method: "DELETE",
|
|
2927
2953
|
note: `Delete specific embedding for a context.`
|
|
2928
2954
|
});
|
|
2955
|
+
console.log("[EXULU] delete embedding by id");
|
|
2929
2956
|
app.delete(`items/:context/:id`, async (req, res) => {
|
|
2930
2957
|
const id = req.params.id;
|
|
2931
2958
|
if (!req.params.context) {
|
|
@@ -2957,6 +2984,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
2957
2984
|
message: "Embedding deleted."
|
|
2958
2985
|
});
|
|
2959
2986
|
});
|
|
2987
|
+
console.log("[EXULU] statistics timeseries");
|
|
2960
2988
|
app.post("/statistics/timeseries", async (req, res) => {
|
|
2961
2989
|
const authenticationResult = await requestValidators.authenticate(req);
|
|
2962
2990
|
if (!authenticationResult.user?.id) {
|
|
@@ -3005,6 +3033,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3005
3033
|
}
|
|
3006
3034
|
});
|
|
3007
3035
|
});
|
|
3036
|
+
console.log("[EXULU] statistics totals");
|
|
3008
3037
|
app.post("/statistics/totals", async (req, res) => {
|
|
3009
3038
|
const authenticationResult = await requestValidators.authenticate(req);
|
|
3010
3039
|
if (!authenticationResult.user?.id) {
|
|
@@ -3033,6 +3062,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3033
3062
|
}
|
|
3034
3063
|
});
|
|
3035
3064
|
});
|
|
3065
|
+
console.log("[EXULU] contexts statistics");
|
|
3036
3066
|
app.get("/contexts/statistics", async (req, res) => {
|
|
3037
3067
|
const authenticationResult = await requestValidators.authenticate(req);
|
|
3038
3068
|
if (!authenticationResult.user?.id) {
|
|
@@ -3064,6 +3094,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3064
3094
|
}
|
|
3065
3095
|
});
|
|
3066
3096
|
});
|
|
3097
|
+
console.log("[EXULU] context by id");
|
|
3067
3098
|
app.get(`/contexts/:id`, async (req, res) => {
|
|
3068
3099
|
const authenticationResult = await requestValidators.authenticate(req);
|
|
3069
3100
|
if (!authenticationResult.user?.id) {
|
|
@@ -3109,6 +3140,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3109
3140
|
// todo
|
|
3110
3141
|
});
|
|
3111
3142
|
});
|
|
3143
|
+
console.log("[EXULU] items export by context");
|
|
3112
3144
|
app.get(`/items/export/:context`, async (req, res) => {
|
|
3113
3145
|
if (!req.params.context) {
|
|
3114
3146
|
res.status(400).json({
|
|
@@ -3137,6 +3169,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3137
3169
|
const ISOTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
3138
3170
|
res.status(200).attachment(`${context.name}-items-export-${ISOTime}.csv`).send(csv);
|
|
3139
3171
|
});
|
|
3172
|
+
console.log("[EXULU] contexts get list");
|
|
3140
3173
|
app.get(`/contexts`, async (req, res) => {
|
|
3141
3174
|
console.log("contexts!!");
|
|
3142
3175
|
const authenticationResult = await requestValidators.authenticate(req);
|
|
@@ -3166,6 +3199,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3166
3199
|
}))
|
|
3167
3200
|
})));
|
|
3168
3201
|
});
|
|
3202
|
+
console.log("[EXULU] workflows get list");
|
|
3169
3203
|
app.get(`/workflows`, async (req, res) => {
|
|
3170
3204
|
const authenticationResult = await requestValidators.authenticate(req);
|
|
3171
3205
|
if (!authenticationResult.user?.id) {
|
|
@@ -3181,6 +3215,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3181
3215
|
inputSchema: workflow.inputSchema ? (0, import_zodex.zerialize)(workflow.inputSchema) : null
|
|
3182
3216
|
})));
|
|
3183
3217
|
});
|
|
3218
|
+
console.log("[EXULU] workflow by id");
|
|
3184
3219
|
app.get(`/workflows/:id`, async (req, res) => {
|
|
3185
3220
|
const authenticationResult = await requestValidators.authenticate(req);
|
|
3186
3221
|
if (!authenticationResult.user?.id) {
|
|
@@ -3208,6 +3243,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3208
3243
|
workflow: void 0
|
|
3209
3244
|
});
|
|
3210
3245
|
});
|
|
3246
|
+
console.log("[EXULU] contexts");
|
|
3211
3247
|
contexts.forEach((context) => {
|
|
3212
3248
|
const sources = context.sources.get();
|
|
3213
3249
|
if (!Array.isArray(sources)) {
|
|
@@ -3223,66 +3259,14 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3223
3259
|
note: `Webhook updater for ${context.name}`
|
|
3224
3260
|
});
|
|
3225
3261
|
app.post(`${updater.slug}/${updater.type}/:context`, async (req, res) => {
|
|
3226
|
-
|
|
3227
|
-
if (!id) {
|
|
3228
|
-
res.status(400).json({
|
|
3229
|
-
message: "Missing context id in request."
|
|
3230
|
-
});
|
|
3231
|
-
return;
|
|
3232
|
-
}
|
|
3233
|
-
const context2 = contexts.find((context3) => context3.id === id);
|
|
3234
|
-
if (!context2) {
|
|
3235
|
-
res.status(400).json({
|
|
3236
|
-
message: `Context for provided id: ${id} not found.`
|
|
3237
|
-
});
|
|
3238
|
-
return;
|
|
3239
|
-
}
|
|
3240
|
-
if (!context2.embedder.queue) {
|
|
3241
|
-
res.status(500).json({ detail: "No queue set for embedder." });
|
|
3242
|
-
return;
|
|
3243
|
-
}
|
|
3244
|
-
const authenticationResult = await requestValidators.authenticate(req);
|
|
3245
|
-
if (!authenticationResult.user?.id) {
|
|
3246
|
-
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
3247
|
-
return;
|
|
3248
|
-
}
|
|
3249
|
-
const requestValidationResult = requestValidators.embedders(req, updater.configuration);
|
|
3250
|
-
if (requestValidationResult.error) {
|
|
3251
|
-
res.status(requestValidationResult.code || 500).json({ detail: `${requestValidationResult.message}` });
|
|
3252
|
-
return;
|
|
3253
|
-
}
|
|
3254
|
-
const documents = await updater.fn(req.body.configuration);
|
|
3255
|
-
const batches = [];
|
|
3256
|
-
for (let i = 0; i < documents.length; i += context2.embedder.batchSize) {
|
|
3257
|
-
batches.push(documents.slice(i, i + context2.embedder.batchSize));
|
|
3258
|
-
}
|
|
3259
|
-
let promises2 = [];
|
|
3260
|
-
if (batches.length > 0) {
|
|
3261
|
-
promises2 = batches.map((documents2) => {
|
|
3262
|
-
return bullmqDecorator({
|
|
3263
|
-
label: `Job running context '${context2.name}' with embedder '${context2.embedder.name}' for '${req.body.label}'`,
|
|
3264
|
-
type: "embedder",
|
|
3265
|
-
embedder: context2.embedder.id,
|
|
3266
|
-
updater: updater.id,
|
|
3267
|
-
context: context2.id,
|
|
3268
|
-
trigger: updater.type,
|
|
3269
|
-
source: source.id,
|
|
3270
|
-
inputs: req.body.inputs,
|
|
3271
|
-
...updater.configuration && { configuration: req.body.configuration },
|
|
3272
|
-
documents: documents2,
|
|
3273
|
-
queue: context2.embedder.queue,
|
|
3274
|
-
user: authenticationResult.user.id
|
|
3275
|
-
});
|
|
3276
|
-
});
|
|
3277
|
-
}
|
|
3278
|
-
const jobs = await Promise.all(promises2);
|
|
3279
|
-
res.status(200).json(jobs);
|
|
3262
|
+
res.status(200).json([]);
|
|
3280
3263
|
return;
|
|
3281
3264
|
});
|
|
3282
3265
|
}
|
|
3283
3266
|
});
|
|
3284
3267
|
});
|
|
3285
3268
|
});
|
|
3269
|
+
console.log("[EXULU] agents");
|
|
3286
3270
|
agents.forEach((agent) => {
|
|
3287
3271
|
const slug = agent.slug;
|
|
3288
3272
|
if (!slug) return;
|
|
@@ -3310,6 +3294,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3310
3294
|
return;
|
|
3311
3295
|
}
|
|
3312
3296
|
if (agent.rateLimit) {
|
|
3297
|
+
console.log("[EXULU] rate limiting agent.", agent.rateLimit);
|
|
3313
3298
|
const limit = await rateLimiter(
|
|
3314
3299
|
agent.rateLimit.name || agent.id,
|
|
3315
3300
|
agent.rateLimit.rate_limit.time,
|
|
@@ -3382,6 +3367,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3382
3367
|
}
|
|
3383
3368
|
});
|
|
3384
3369
|
});
|
|
3370
|
+
console.log("[EXULU] workflows");
|
|
3385
3371
|
workflows.forEach((workflow) => {
|
|
3386
3372
|
routeLogs.push({
|
|
3387
3373
|
route: workflow.slug,
|
|
@@ -3447,7 +3433,11 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3447
3433
|
return;
|
|
3448
3434
|
});
|
|
3449
3435
|
});
|
|
3450
|
-
|
|
3436
|
+
if (process.env.COMPANION_S3_REGION && process.env.COMPANION_S3_KEY && process.env.COMPANION_S3_SECRET) {
|
|
3437
|
+
await createUppyRoutes(app);
|
|
3438
|
+
} else {
|
|
3439
|
+
console.log("[EXULU] skipping uppy file upload routes, because no S3 compatible region, key or secret is set in the environment.");
|
|
3440
|
+
}
|
|
3451
3441
|
console.log("Routes:");
|
|
3452
3442
|
console.table(routeLogs);
|
|
3453
3443
|
};
|
|
@@ -3547,11 +3537,18 @@ var bullmq = {
|
|
|
3547
3537
|
var fs2 = __toESM(require("fs"), 1);
|
|
3548
3538
|
var import_path = __toESM(require("path"), 1);
|
|
3549
3539
|
var defaultLogsDir = import_path.default.join(process.cwd(), "logs");
|
|
3550
|
-
var redisConnection
|
|
3551
|
-
...redisServer,
|
|
3552
|
-
maxRetriesPerRequest: null
|
|
3553
|
-
});
|
|
3540
|
+
var redisConnection;
|
|
3554
3541
|
var createWorkers = async (queues2, contexts, embedders, workflows, _logsDir) => {
|
|
3542
|
+
if (!redisServer.host || !redisServer.port) {
|
|
3543
|
+
console.error("[EXULU] you are trying to start workers, but no redis server is configured in the environment.");
|
|
3544
|
+
throw new Error("No redis server configured in the environment, so cannot start workers.");
|
|
3545
|
+
}
|
|
3546
|
+
if (!redisConnection) {
|
|
3547
|
+
redisConnection = new import_ioredis.default({
|
|
3548
|
+
...redisServer,
|
|
3549
|
+
maxRetriesPerRequest: null
|
|
3550
|
+
});
|
|
3551
|
+
}
|
|
3555
3552
|
const logsDir = _logsDir || defaultLogsDir;
|
|
3556
3553
|
const workers = queues2.map((queue) => {
|
|
3557
3554
|
console.log(`[EXULU] creating worker for queue ${queue}.`);
|
package/dist/index.js
CHANGED
|
@@ -18,18 +18,19 @@ var redisServer = {
|
|
|
18
18
|
var client = {};
|
|
19
19
|
async function redisClient() {
|
|
20
20
|
if (!redisServer.host || !redisServer.port) {
|
|
21
|
-
return {
|
|
22
|
-
client: null
|
|
23
|
-
};
|
|
21
|
+
return { client: null };
|
|
24
22
|
}
|
|
25
23
|
if (!client["exulu"]) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
24
|
+
try {
|
|
25
|
+
const url = `redis://${redisServer.host}:${redisServer.port}`;
|
|
26
|
+
client["exulu"] = createClient({
|
|
27
|
+
url
|
|
28
|
+
});
|
|
29
|
+
await client["exulu"].connect();
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error(`[EXULU] error connecting to redis:`, error);
|
|
32
|
+
return { client: null };
|
|
33
|
+
}
|
|
33
34
|
}
|
|
34
35
|
return {
|
|
35
36
|
client: client["exulu"]
|
|
@@ -72,25 +73,30 @@ import "pgvector/knex";
|
|
|
72
73
|
var db = {};
|
|
73
74
|
async function postgresClient() {
|
|
74
75
|
if (!db["exulu"]) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
76
|
+
try {
|
|
77
|
+
console.log("[EXULU] Initializing exulu database.");
|
|
78
|
+
console.log(process.env.POSTGRES_DB_HOST);
|
|
79
|
+
console.log(process.env.POSTGRES_DB_PORT);
|
|
80
|
+
console.log(process.env.POSTGRES_DB_USER);
|
|
81
|
+
console.log(process.env.POSTGRES_DB_PASSWORD);
|
|
82
|
+
console.log(process.env.POSTGRES_DB_SSL);
|
|
83
|
+
const knex = Knex({
|
|
84
|
+
client: "pg",
|
|
85
|
+
connection: {
|
|
86
|
+
host: process.env.POSTGRES_DB_HOST,
|
|
87
|
+
port: parseInt(process.env.POSTGRES_DB_PORT || "5432"),
|
|
88
|
+
user: process.env.POSTGRES_DB_USER,
|
|
89
|
+
database: "exulu",
|
|
90
|
+
password: process.env.POSTGRES_DB_PASSWORD,
|
|
91
|
+
ssl: process.env.POSTGRES_DB_SSL === "true" ? { rejectUnauthorized: false } : false
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
await knex.schema.createExtensionIfNotExists("vector");
|
|
95
|
+
db["exulu"] = knex;
|
|
96
|
+
} catch (error) {
|
|
97
|
+
console.error("[EXULU] Error initializing exulu database.", error);
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
94
100
|
}
|
|
95
101
|
return {
|
|
96
102
|
db: db["exulu"]
|
|
@@ -506,8 +512,8 @@ var up = async function(knex) {
|
|
|
506
512
|
}
|
|
507
513
|
};
|
|
508
514
|
var SALT_ROUNDS = 12;
|
|
509
|
-
async function encryptApiKey(
|
|
510
|
-
const hash = await bcrypt.hash(
|
|
515
|
+
async function encryptApiKey(apikey) {
|
|
516
|
+
const hash = await bcrypt.hash(apikey, SALT_ROUNDS);
|
|
511
517
|
return hash;
|
|
512
518
|
}
|
|
513
519
|
var execute = async () => {
|
|
@@ -555,7 +561,7 @@ var execute = async () => {
|
|
|
555
561
|
super_admin: true,
|
|
556
562
|
createdAt: /* @__PURE__ */ new Date(),
|
|
557
563
|
updatedAt: /* @__PURE__ */ new Date(),
|
|
558
|
-
type: "
|
|
564
|
+
type: "api",
|
|
559
565
|
apikey: `${encryptedKey}${postFix}`,
|
|
560
566
|
// password: "admin", todo add this again when we implement password auth / encryption as alternative to OTP
|
|
561
567
|
role: roleId
|
|
@@ -1383,30 +1389,38 @@ import "express";
|
|
|
1383
1389
|
|
|
1384
1390
|
// src/registry/rate-limiter.ts
|
|
1385
1391
|
var rateLimiter = async (key, windowSeconds, limit, points) => {
|
|
1386
|
-
|
|
1387
|
-
|
|
1392
|
+
try {
|
|
1393
|
+
const { client: client2 } = await redisClient();
|
|
1394
|
+
if (!client2) {
|
|
1395
|
+
console.warn("[EXULU] Rate limiting disabled - Redis not available");
|
|
1396
|
+
return {
|
|
1397
|
+
status: true,
|
|
1398
|
+
retryAfter: null
|
|
1399
|
+
};
|
|
1400
|
+
}
|
|
1401
|
+
const redisKey = `exulu/${key}`;
|
|
1402
|
+
const current = await client2.incrBy(redisKey, points);
|
|
1403
|
+
if (current === points) {
|
|
1404
|
+
await client2.expire(redisKey, windowSeconds);
|
|
1405
|
+
}
|
|
1406
|
+
if (current > limit) {
|
|
1407
|
+
const ttl = await client2.ttl(redisKey);
|
|
1408
|
+
return {
|
|
1409
|
+
status: false,
|
|
1410
|
+
retryAfter: ttl
|
|
1411
|
+
};
|
|
1412
|
+
}
|
|
1388
1413
|
return {
|
|
1389
|
-
status:
|
|
1390
|
-
retryAfter:
|
|
1391
|
-
// 10 seconds
|
|
1414
|
+
status: true,
|
|
1415
|
+
retryAfter: null
|
|
1392
1416
|
};
|
|
1393
|
-
}
|
|
1394
|
-
|
|
1395
|
-
const current = await client2.incrBy(redisKey, points);
|
|
1396
|
-
if (current === points) {
|
|
1397
|
-
await client2.expire(redisKey, windowSeconds);
|
|
1398
|
-
}
|
|
1399
|
-
if (current > limit) {
|
|
1400
|
-
const ttl = await client2.ttl(redisKey);
|
|
1417
|
+
} catch (error) {
|
|
1418
|
+
console.error("[EXULU] Rate limiting error:", error);
|
|
1401
1419
|
return {
|
|
1402
|
-
status:
|
|
1403
|
-
retryAfter:
|
|
1420
|
+
status: true,
|
|
1421
|
+
retryAfter: null
|
|
1404
1422
|
};
|
|
1405
1423
|
}
|
|
1406
|
-
return {
|
|
1407
|
-
status: true,
|
|
1408
|
-
retryAfter: null
|
|
1409
|
-
};
|
|
1410
1424
|
};
|
|
1411
1425
|
|
|
1412
1426
|
// src/registry/route-validators/index.ts
|
|
@@ -1505,10 +1519,10 @@ var authentication = async ({
|
|
|
1505
1519
|
code: 401
|
|
1506
1520
|
};
|
|
1507
1521
|
}
|
|
1508
|
-
const filtered = users.filter(({
|
|
1522
|
+
const filtered = users.filter(({ apikey: apikey2, id }) => apikey2.includes(keyName));
|
|
1509
1523
|
for (const user of filtered) {
|
|
1510
|
-
const lastSlashIndex = user.
|
|
1511
|
-
const compareValue = lastSlashIndex !== -1 ? user.
|
|
1524
|
+
const lastSlashIndex = user.apikey.lastIndexOf("/");
|
|
1525
|
+
const compareValue = lastSlashIndex !== -1 ? user.apikey.substring(0, lastSlashIndex) : user.apikey;
|
|
1512
1526
|
const isMatch = await bcrypt2.compare(keyValue, compareValue);
|
|
1513
1527
|
if (isMatch) {
|
|
1514
1528
|
await db2.from("users").where({ id: user.id }).update({
|
|
@@ -1700,6 +1714,10 @@ var ExuluQueues = class {
|
|
|
1700
1714
|
if (existing) {
|
|
1701
1715
|
return existing;
|
|
1702
1716
|
}
|
|
1717
|
+
if (!redisServer.host?.length || !redisServer.port?.length) {
|
|
1718
|
+
console.error(`[EXULU] no redis server configured, but you are trying to use a queue ( ${name}), likely in an agent or workflow (look for ExuluQueues.use() ).`);
|
|
1719
|
+
throw new Error(`[EXULU] no redis server configured.`);
|
|
1720
|
+
}
|
|
1703
1721
|
const newQueue = new Queue3(`${name}`, { connection: redisServer });
|
|
1704
1722
|
this.queues.push(newQueue);
|
|
1705
1723
|
return newQueue;
|
|
@@ -2119,9 +2137,6 @@ var createUppyRoutes = async (app) => {
|
|
|
2119
2137
|
return stsClient;
|
|
2120
2138
|
}
|
|
2121
2139
|
app.use(bodyParser.urlencoded({ extended: true }), bodyParser.json());
|
|
2122
|
-
app.get("/", (req, res) => {
|
|
2123
|
-
res.json("Exulu upload server.");
|
|
2124
|
-
});
|
|
2125
2140
|
app.get("/s3/list", async (req, res, next) => {
|
|
2126
2141
|
const apikey = req.headers["exulu-api-key"] || null;
|
|
2127
2142
|
let authtoken = null;
|
|
@@ -2455,6 +2470,7 @@ var createUppyRoutes = async (app) => {
|
|
|
2455
2470
|
};
|
|
2456
2471
|
|
|
2457
2472
|
// src/registry/routes.ts
|
|
2473
|
+
import { InMemoryLRUCache } from "@apollo/utils.keyvaluecache";
|
|
2458
2474
|
var Papa = __require("papaparse");
|
|
2459
2475
|
var global_queues = {
|
|
2460
2476
|
logs_cleaner: "logs-cleaner"
|
|
@@ -2548,10 +2564,21 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
2548
2564
|
{ route: "/items/export/:context", method: "GET", note: "Export items from context" },
|
|
2549
2565
|
{ route: "/graphql", method: "POST", note: "GraphQL endpoint" }
|
|
2550
2566
|
);
|
|
2551
|
-
|
|
2567
|
+
if (redisServer.host?.length && redisServer.port?.length) {
|
|
2568
|
+
await createRecurringJobs();
|
|
2569
|
+
} else {
|
|
2570
|
+
console.log("===========================", "[EXULU] no redis server configured, not setting up recurring jobs.", "===========================");
|
|
2571
|
+
}
|
|
2552
2572
|
const schema = createSDL([usersSchema, rolesSchema, agentsSchema, jobsSchema]);
|
|
2553
|
-
|
|
2573
|
+
console.log("[EXULU] graphql server");
|
|
2574
|
+
const server = new ApolloServer({
|
|
2575
|
+
cache: new InMemoryLRUCache(),
|
|
2576
|
+
schema,
|
|
2577
|
+
introspection: true
|
|
2578
|
+
});
|
|
2579
|
+
console.log("[EXULU] starting graphql server");
|
|
2554
2580
|
await server.start();
|
|
2581
|
+
console.log("[EXULU] graphql server started");
|
|
2555
2582
|
app.use(
|
|
2556
2583
|
"/graphql",
|
|
2557
2584
|
cors(),
|
|
@@ -2608,7 +2635,6 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
2608
2635
|
}
|
|
2609
2636
|
});
|
|
2610
2637
|
});
|
|
2611
|
-
console.log("tools", tools);
|
|
2612
2638
|
app.get("/tools", async (req, res) => {
|
|
2613
2639
|
res.status(200).json(tools.map((tool) => ({
|
|
2614
2640
|
id: tool.id,
|
|
@@ -2885,6 +2911,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
2885
2911
|
method: "DELETE",
|
|
2886
2912
|
note: `Delete specific embedding for a context.`
|
|
2887
2913
|
});
|
|
2914
|
+
console.log("[EXULU] delete embedding by id");
|
|
2888
2915
|
app.delete(`items/:context/:id`, async (req, res) => {
|
|
2889
2916
|
const id = req.params.id;
|
|
2890
2917
|
if (!req.params.context) {
|
|
@@ -2916,6 +2943,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
2916
2943
|
message: "Embedding deleted."
|
|
2917
2944
|
});
|
|
2918
2945
|
});
|
|
2946
|
+
console.log("[EXULU] statistics timeseries");
|
|
2919
2947
|
app.post("/statistics/timeseries", async (req, res) => {
|
|
2920
2948
|
const authenticationResult = await requestValidators.authenticate(req);
|
|
2921
2949
|
if (!authenticationResult.user?.id) {
|
|
@@ -2964,6 +2992,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
2964
2992
|
}
|
|
2965
2993
|
});
|
|
2966
2994
|
});
|
|
2995
|
+
console.log("[EXULU] statistics totals");
|
|
2967
2996
|
app.post("/statistics/totals", async (req, res) => {
|
|
2968
2997
|
const authenticationResult = await requestValidators.authenticate(req);
|
|
2969
2998
|
if (!authenticationResult.user?.id) {
|
|
@@ -2992,6 +3021,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
2992
3021
|
}
|
|
2993
3022
|
});
|
|
2994
3023
|
});
|
|
3024
|
+
console.log("[EXULU] contexts statistics");
|
|
2995
3025
|
app.get("/contexts/statistics", async (req, res) => {
|
|
2996
3026
|
const authenticationResult = await requestValidators.authenticate(req);
|
|
2997
3027
|
if (!authenticationResult.user?.id) {
|
|
@@ -3023,6 +3053,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3023
3053
|
}
|
|
3024
3054
|
});
|
|
3025
3055
|
});
|
|
3056
|
+
console.log("[EXULU] context by id");
|
|
3026
3057
|
app.get(`/contexts/:id`, async (req, res) => {
|
|
3027
3058
|
const authenticationResult = await requestValidators.authenticate(req);
|
|
3028
3059
|
if (!authenticationResult.user?.id) {
|
|
@@ -3068,6 +3099,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3068
3099
|
// todo
|
|
3069
3100
|
});
|
|
3070
3101
|
});
|
|
3102
|
+
console.log("[EXULU] items export by context");
|
|
3071
3103
|
app.get(`/items/export/:context`, async (req, res) => {
|
|
3072
3104
|
if (!req.params.context) {
|
|
3073
3105
|
res.status(400).json({
|
|
@@ -3096,6 +3128,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3096
3128
|
const ISOTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
3097
3129
|
res.status(200).attachment(`${context.name}-items-export-${ISOTime}.csv`).send(csv);
|
|
3098
3130
|
});
|
|
3131
|
+
console.log("[EXULU] contexts get list");
|
|
3099
3132
|
app.get(`/contexts`, async (req, res) => {
|
|
3100
3133
|
console.log("contexts!!");
|
|
3101
3134
|
const authenticationResult = await requestValidators.authenticate(req);
|
|
@@ -3125,6 +3158,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3125
3158
|
}))
|
|
3126
3159
|
})));
|
|
3127
3160
|
});
|
|
3161
|
+
console.log("[EXULU] workflows get list");
|
|
3128
3162
|
app.get(`/workflows`, async (req, res) => {
|
|
3129
3163
|
const authenticationResult = await requestValidators.authenticate(req);
|
|
3130
3164
|
if (!authenticationResult.user?.id) {
|
|
@@ -3140,6 +3174,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3140
3174
|
inputSchema: workflow.inputSchema ? zerialize(workflow.inputSchema) : null
|
|
3141
3175
|
})));
|
|
3142
3176
|
});
|
|
3177
|
+
console.log("[EXULU] workflow by id");
|
|
3143
3178
|
app.get(`/workflows/:id`, async (req, res) => {
|
|
3144
3179
|
const authenticationResult = await requestValidators.authenticate(req);
|
|
3145
3180
|
if (!authenticationResult.user?.id) {
|
|
@@ -3167,6 +3202,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3167
3202
|
workflow: void 0
|
|
3168
3203
|
});
|
|
3169
3204
|
});
|
|
3205
|
+
console.log("[EXULU] contexts");
|
|
3170
3206
|
contexts.forEach((context) => {
|
|
3171
3207
|
const sources = context.sources.get();
|
|
3172
3208
|
if (!Array.isArray(sources)) {
|
|
@@ -3182,66 +3218,14 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3182
3218
|
note: `Webhook updater for ${context.name}`
|
|
3183
3219
|
});
|
|
3184
3220
|
app.post(`${updater.slug}/${updater.type}/:context`, async (req, res) => {
|
|
3185
|
-
|
|
3186
|
-
if (!id) {
|
|
3187
|
-
res.status(400).json({
|
|
3188
|
-
message: "Missing context id in request."
|
|
3189
|
-
});
|
|
3190
|
-
return;
|
|
3191
|
-
}
|
|
3192
|
-
const context2 = contexts.find((context3) => context3.id === id);
|
|
3193
|
-
if (!context2) {
|
|
3194
|
-
res.status(400).json({
|
|
3195
|
-
message: `Context for provided id: ${id} not found.`
|
|
3196
|
-
});
|
|
3197
|
-
return;
|
|
3198
|
-
}
|
|
3199
|
-
if (!context2.embedder.queue) {
|
|
3200
|
-
res.status(500).json({ detail: "No queue set for embedder." });
|
|
3201
|
-
return;
|
|
3202
|
-
}
|
|
3203
|
-
const authenticationResult = await requestValidators.authenticate(req);
|
|
3204
|
-
if (!authenticationResult.user?.id) {
|
|
3205
|
-
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
3206
|
-
return;
|
|
3207
|
-
}
|
|
3208
|
-
const requestValidationResult = requestValidators.embedders(req, updater.configuration);
|
|
3209
|
-
if (requestValidationResult.error) {
|
|
3210
|
-
res.status(requestValidationResult.code || 500).json({ detail: `${requestValidationResult.message}` });
|
|
3211
|
-
return;
|
|
3212
|
-
}
|
|
3213
|
-
const documents = await updater.fn(req.body.configuration);
|
|
3214
|
-
const batches = [];
|
|
3215
|
-
for (let i = 0; i < documents.length; i += context2.embedder.batchSize) {
|
|
3216
|
-
batches.push(documents.slice(i, i + context2.embedder.batchSize));
|
|
3217
|
-
}
|
|
3218
|
-
let promises2 = [];
|
|
3219
|
-
if (batches.length > 0) {
|
|
3220
|
-
promises2 = batches.map((documents2) => {
|
|
3221
|
-
return bullmqDecorator({
|
|
3222
|
-
label: `Job running context '${context2.name}' with embedder '${context2.embedder.name}' for '${req.body.label}'`,
|
|
3223
|
-
type: "embedder",
|
|
3224
|
-
embedder: context2.embedder.id,
|
|
3225
|
-
updater: updater.id,
|
|
3226
|
-
context: context2.id,
|
|
3227
|
-
trigger: updater.type,
|
|
3228
|
-
source: source.id,
|
|
3229
|
-
inputs: req.body.inputs,
|
|
3230
|
-
...updater.configuration && { configuration: req.body.configuration },
|
|
3231
|
-
documents: documents2,
|
|
3232
|
-
queue: context2.embedder.queue,
|
|
3233
|
-
user: authenticationResult.user.id
|
|
3234
|
-
});
|
|
3235
|
-
});
|
|
3236
|
-
}
|
|
3237
|
-
const jobs = await Promise.all(promises2);
|
|
3238
|
-
res.status(200).json(jobs);
|
|
3221
|
+
res.status(200).json([]);
|
|
3239
3222
|
return;
|
|
3240
3223
|
});
|
|
3241
3224
|
}
|
|
3242
3225
|
});
|
|
3243
3226
|
});
|
|
3244
3227
|
});
|
|
3228
|
+
console.log("[EXULU] agents");
|
|
3245
3229
|
agents.forEach((agent) => {
|
|
3246
3230
|
const slug = agent.slug;
|
|
3247
3231
|
if (!slug) return;
|
|
@@ -3269,6 +3253,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3269
3253
|
return;
|
|
3270
3254
|
}
|
|
3271
3255
|
if (agent.rateLimit) {
|
|
3256
|
+
console.log("[EXULU] rate limiting agent.", agent.rateLimit);
|
|
3272
3257
|
const limit = await rateLimiter(
|
|
3273
3258
|
agent.rateLimit.name || agent.id,
|
|
3274
3259
|
agent.rateLimit.rate_limit.time,
|
|
@@ -3341,6 +3326,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3341
3326
|
}
|
|
3342
3327
|
});
|
|
3343
3328
|
});
|
|
3329
|
+
console.log("[EXULU] workflows");
|
|
3344
3330
|
workflows.forEach((workflow) => {
|
|
3345
3331
|
routeLogs.push({
|
|
3346
3332
|
route: workflow.slug,
|
|
@@ -3406,7 +3392,11 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
|
|
|
3406
3392
|
return;
|
|
3407
3393
|
});
|
|
3408
3394
|
});
|
|
3409
|
-
|
|
3395
|
+
if (process.env.COMPANION_S3_REGION && process.env.COMPANION_S3_KEY && process.env.COMPANION_S3_SECRET) {
|
|
3396
|
+
await createUppyRoutes(app);
|
|
3397
|
+
} else {
|
|
3398
|
+
console.log("[EXULU] skipping uppy file upload routes, because no S3 compatible region, key or secret is set in the environment.");
|
|
3399
|
+
}
|
|
3410
3400
|
console.log("Routes:");
|
|
3411
3401
|
console.table(routeLogs);
|
|
3412
3402
|
};
|
|
@@ -3506,11 +3496,18 @@ var bullmq = {
|
|
|
3506
3496
|
import * as fs2 from "fs";
|
|
3507
3497
|
import path2 from "path";
|
|
3508
3498
|
var defaultLogsDir = path2.join(process.cwd(), "logs");
|
|
3509
|
-
var redisConnection
|
|
3510
|
-
...redisServer,
|
|
3511
|
-
maxRetriesPerRequest: null
|
|
3512
|
-
});
|
|
3499
|
+
var redisConnection;
|
|
3513
3500
|
var createWorkers = async (queues2, contexts, embedders, workflows, _logsDir) => {
|
|
3501
|
+
if (!redisServer.host || !redisServer.port) {
|
|
3502
|
+
console.error("[EXULU] you are trying to start workers, but no redis server is configured in the environment.");
|
|
3503
|
+
throw new Error("No redis server configured in the environment, so cannot start workers.");
|
|
3504
|
+
}
|
|
3505
|
+
if (!redisConnection) {
|
|
3506
|
+
redisConnection = new IORedis({
|
|
3507
|
+
...redisServer,
|
|
3508
|
+
maxRetriesPerRequest: null
|
|
3509
|
+
});
|
|
3510
|
+
}
|
|
3514
3511
|
const logsDir = _logsDir || defaultLogsDir;
|
|
3515
3512
|
const workers = queues2.map((queue) => {
|
|
3516
3513
|
console.log(`[EXULU] creating worker for queue ${queue}.`);
|