@exulu/backend 1.24.0 → 1.25.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +3 -4
- package/dist/index.cjs +131 -197
- package/dist/index.js +131 -197
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
## [1.25.1](https://github.com/Qventu/exulu-backend/compare/v1.25.0...v1.25.1) (2025-10-07)
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
###
|
|
4
|
+
### Bug Fixes
|
|
5
5
|
|
|
6
|
-
*
|
|
7
|
-
* project files ([24d34cc](https://github.com/Qventu/exulu-backend/commit/24d34ccb61777356653de2b5319064098cc5d877))
|
|
6
|
+
* log headers in graphql ([8d3094b](https://github.com/Qventu/exulu-backend/commit/8d3094b1a9b836c06352e9ed6057c4654fe457d4))
|
package/dist/index.cjs
CHANGED
|
@@ -168,6 +168,9 @@ async function ensureDatabaseExists() {
|
|
|
168
168
|
} else {
|
|
169
169
|
console.log(`[EXULU] Database '${dbName}' already exists.`);
|
|
170
170
|
}
|
|
171
|
+
} catch (error) {
|
|
172
|
+
console.error("[EXULU] Error while checking to ensure the database exists, this could be if the user running the server does not have database admin rights, it is fine to ignore this if you are sure the database exists.", error);
|
|
173
|
+
return;
|
|
171
174
|
} finally {
|
|
172
175
|
await defaultKnex.destroy();
|
|
173
176
|
}
|
|
@@ -198,7 +201,11 @@ async function postgresClient() {
|
|
|
198
201
|
ssl: process.env.POSTGRES_DB_SSL === "true" ? { rejectUnauthorized: false } : false
|
|
199
202
|
}
|
|
200
203
|
});
|
|
201
|
-
|
|
204
|
+
try {
|
|
205
|
+
await knex.schema.createExtensionIfNotExists("vector");
|
|
206
|
+
} catch (error) {
|
|
207
|
+
console.error("[EXULU] Error creating vector extension, this might be fine if you already activated the extension and the 'user' running this script does not have higher level database permissions.", error);
|
|
208
|
+
}
|
|
202
209
|
db["exulu"] = knex;
|
|
203
210
|
} catch (error) {
|
|
204
211
|
console.error("[EXULU] Error initializing exulu database.", error);
|
|
@@ -461,7 +468,7 @@ var import_jose = require("jose");
|
|
|
461
468
|
var getToken = async (authHeader) => {
|
|
462
469
|
const token = authHeader.split(" ")[1];
|
|
463
470
|
if (!token) {
|
|
464
|
-
throw new Error("No token provided");
|
|
471
|
+
throw new Error("No token provided for user authentication in headers.");
|
|
465
472
|
}
|
|
466
473
|
if (!process.env.NEXTAUTH_SECRET) {
|
|
467
474
|
throw new Error("No NEXTAUTH_SECRET provided");
|
|
@@ -2391,7 +2398,11 @@ var postprocessUpdate = async ({
|
|
|
2391
2398
|
const { db: db3 } = await postgresClient();
|
|
2392
2399
|
console.log("[EXULU] Deleting chunks for item", result.id);
|
|
2393
2400
|
await db3.from(getChunksTableName(context.id)).where({ source: result.id }).delete();
|
|
2401
|
+
console.log("[EXULU] Deleted chunks for item", result.id);
|
|
2402
|
+
console.log("[EXULU] Embedder", context.embedder);
|
|
2403
|
+
console.log("[EXULU] Configuration", context.configuration);
|
|
2394
2404
|
if (context.embedder && (context.configuration.calculateVectors === "onUpdate" || context.configuration.calculateVectors === "always")) {
|
|
2405
|
+
console.log("[EXULU] Generating embeddings for item", result.id);
|
|
2395
2406
|
const { job } = await context.embeddings.generate.one({
|
|
2396
2407
|
item: result,
|
|
2397
2408
|
user,
|
|
@@ -3922,6 +3933,11 @@ var ExuluAgent2 = class {
|
|
|
3922
3933
|
// prepareStep could be used here to set the model for the first step or change other params
|
|
3923
3934
|
system,
|
|
3924
3935
|
maxRetries: 2,
|
|
3936
|
+
providerOptions: {
|
|
3937
|
+
openai: {
|
|
3938
|
+
reasoningSummary: "auto"
|
|
3939
|
+
}
|
|
3940
|
+
},
|
|
3925
3941
|
tools: convertToolsArrayToObject(
|
|
3926
3942
|
currentTools,
|
|
3927
3943
|
allExuluTools,
|
|
@@ -4521,8 +4537,8 @@ var ExuluContext = class {
|
|
|
4521
4537
|
CREATE INDEX IF NOT EXISTS ${tableName}_embedding_hnsw_cosine
|
|
4522
4538
|
ON ${tableName}
|
|
4523
4539
|
USING hnsw (embedding vector_cosine_ops)
|
|
4524
|
-
WHERE embedding IS NOT NULL
|
|
4525
4540
|
WITH (m = 16, ef_construction = 64)
|
|
4541
|
+
WHERE embedding IS NOT NULL
|
|
4526
4542
|
`);
|
|
4527
4543
|
return;
|
|
4528
4544
|
};
|
|
@@ -5073,6 +5089,13 @@ var createUppyRoutes = async (app, config) => {
|
|
|
5073
5089
|
var import_utils4 = require("@apollo/utils.keyvaluecache");
|
|
5074
5090
|
var import_body_parser = __toESM(require("body-parser"), 1);
|
|
5075
5091
|
var import_crypto_js3 = __toESM(require("crypto-js"), 1);
|
|
5092
|
+
var import_openai = __toESM(require("openai"), 1);
|
|
5093
|
+
var import_fs = __toESM(require("fs"), 1);
|
|
5094
|
+
var import_node_crypto3 = require("crypto");
|
|
5095
|
+
var import_api = require("@opentelemetry/api");
|
|
5096
|
+
var import_ai2 = require("ai");
|
|
5097
|
+
var import_express_http_proxy = __toESM(require("express-http-proxy"), 1);
|
|
5098
|
+
var import_sdk = __toESM(require("@anthropic-ai/sdk"), 1);
|
|
5076
5099
|
|
|
5077
5100
|
// src/registry/utils/claude-messages.ts
|
|
5078
5101
|
var CLAUDE_MESSAGES = {
|
|
@@ -5097,13 +5120,6 @@ var CLAUDE_MESSAGES = {
|
|
|
5097
5120
|
};
|
|
5098
5121
|
|
|
5099
5122
|
// src/registry/routes.ts
|
|
5100
|
-
var import_openai = __toESM(require("openai"), 1);
|
|
5101
|
-
var import_fs = __toESM(require("fs"), 1);
|
|
5102
|
-
var import_node_crypto3 = require("crypto");
|
|
5103
|
-
var import_api = require("@opentelemetry/api");
|
|
5104
|
-
var import_ai2 = require("ai");
|
|
5105
|
-
var import_zod2 = require("zod");
|
|
5106
|
-
var import_client_s33 = require("@aws-sdk/client-s3");
|
|
5107
5123
|
var REQUEST_SIZE_LIMIT = "50mb";
|
|
5108
5124
|
var global_queues = {
|
|
5109
5125
|
logs_cleaner: "logs-cleaner"
|
|
@@ -5213,7 +5229,8 @@ var createExpressRoutes = async (app, logger, agents, tools, contexts, config, t
|
|
|
5213
5229
|
path: req.path,
|
|
5214
5230
|
requestId: "req-" + Date.now(),
|
|
5215
5231
|
ipAddress: req.ip,
|
|
5216
|
-
userAgent: req.get("User-Agent")
|
|
5232
|
+
userAgent: req.get("User-Agent"),
|
|
5233
|
+
headers: req.headers
|
|
5217
5234
|
});
|
|
5218
5235
|
logger.info("================");
|
|
5219
5236
|
const authenticationResult = await requestValidators.authenticate(req);
|
|
@@ -5461,11 +5478,92 @@ Mood: friendly and intelligent.
|
|
|
5461
5478
|
} else {
|
|
5462
5479
|
console.log("[EXULU] skipping uppy file upload routes, because no S3 compatible region, key or secret is set in ExuluApp instance.");
|
|
5463
5480
|
}
|
|
5464
|
-
|
|
5481
|
+
app.use("/xxx/anthropic/:id", import_express4.default.raw({ type: "*/*", limit: REQUEST_SIZE_LIMIT }), (0, import_express_http_proxy.default)(
|
|
5482
|
+
(req, res, next) => {
|
|
5483
|
+
return "https://api.anthropic.com";
|
|
5484
|
+
},
|
|
5485
|
+
{
|
|
5486
|
+
limit: "50mb",
|
|
5487
|
+
memoizeHost: false,
|
|
5488
|
+
preserveHostHdr: true,
|
|
5489
|
+
secure: false,
|
|
5490
|
+
reqAsBuffer: true,
|
|
5491
|
+
proxyReqBodyDecorator: function(bodyContent, srcReq) {
|
|
5492
|
+
return bodyContent;
|
|
5493
|
+
},
|
|
5494
|
+
userResDecorator: function(proxyRes, proxyResData, userReq, userRes) {
|
|
5495
|
+
console.log("[EXULU] Proxy response!", proxyResData);
|
|
5496
|
+
proxyResData = proxyResData.toString();
|
|
5497
|
+
console.log("[EXULU] Proxy response string!", proxyResData);
|
|
5498
|
+
return proxyResData;
|
|
5499
|
+
},
|
|
5500
|
+
proxyReqPathResolver: (req) => {
|
|
5501
|
+
const prefix = `/gateway/anthropic/${req.params.id}`;
|
|
5502
|
+
let path2 = req.url.startsWith(prefix) ? req.url.slice(prefix.length) : req.url;
|
|
5503
|
+
if (!path2.startsWith("/")) path2 = "/" + path2;
|
|
5504
|
+
console.log("[EXULU] Provider path:", path2);
|
|
5505
|
+
return path2;
|
|
5506
|
+
},
|
|
5507
|
+
proxyReqOptDecorator: function(proxyReqOpts, srcReq) {
|
|
5508
|
+
return new Promise(async (resolve, reject) => {
|
|
5509
|
+
try {
|
|
5510
|
+
const authenticationResult = await requestValidators.authenticate(srcReq);
|
|
5511
|
+
if (!authenticationResult.user?.id) {
|
|
5512
|
+
console.log("[EXULU] failed authentication result", authenticationResult);
|
|
5513
|
+
reject(authenticationResult.message);
|
|
5514
|
+
return;
|
|
5515
|
+
}
|
|
5516
|
+
console.log("[EXULU] Authenticated call", authenticationResult.user?.email);
|
|
5517
|
+
const { db: db3 } = await postgresClient();
|
|
5518
|
+
let query = db3("agents");
|
|
5519
|
+
query.select("*");
|
|
5520
|
+
query = applyAccessControl(agentsSchema2(), authenticationResult.user, query);
|
|
5521
|
+
query.where({ id: srcReq.params.id });
|
|
5522
|
+
const agent = await query.first();
|
|
5523
|
+
if (!agent) {
|
|
5524
|
+
reject(new Error("Agent with id " + srcReq.params.id + " not found."));
|
|
5525
|
+
return;
|
|
5526
|
+
}
|
|
5527
|
+
console.log("[EXULU] Agent loaded", agent.name);
|
|
5528
|
+
const backend = agents.find((x) => x.id === agent.backend);
|
|
5529
|
+
if (!process.env.NEXTAUTH_SECRET) {
|
|
5530
|
+
reject(new Error("Missing NEXTAUTH_SECRET"));
|
|
5531
|
+
return;
|
|
5532
|
+
}
|
|
5533
|
+
if (!agent.providerapikey) {
|
|
5534
|
+
reject(new Error("API Key not set for agent"));
|
|
5535
|
+
return;
|
|
5536
|
+
}
|
|
5537
|
+
const variableName = agent.providerapikey;
|
|
5538
|
+
const variable = await db3.from("variables").where({ name: variableName }).first();
|
|
5539
|
+
console.log("[EXULU] Variable loaded", variable);
|
|
5540
|
+
let providerapikey = variable.value;
|
|
5541
|
+
if (!variable.encrypted) {
|
|
5542
|
+
reject(new Error("API Key not encrypted for agent"));
|
|
5543
|
+
return;
|
|
5544
|
+
}
|
|
5545
|
+
if (variable.encrypted) {
|
|
5546
|
+
const bytes = import_crypto_js3.default.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
|
|
5547
|
+
providerapikey = bytes.toString(import_crypto_js3.default.enc.Utf8);
|
|
5548
|
+
}
|
|
5549
|
+
console.log("[EXULU] Provider API key", providerapikey);
|
|
5550
|
+
proxyReqOpts.headers["x-api-key"] = providerapikey;
|
|
5551
|
+
proxyReqOpts.rejectUnauthorized = false;
|
|
5552
|
+
delete proxyReqOpts.headers["provider"];
|
|
5553
|
+
const url = new URL("https://api.anthropic.com");
|
|
5554
|
+
proxyReqOpts.headers["host"] = url.host;
|
|
5555
|
+
proxyReqOpts.headers["anthropic-version"] = "2023-06-01";
|
|
5556
|
+
console.log("[EXULU] Proxy request headers", proxyReqOpts.headers);
|
|
5557
|
+
resolve(proxyReqOpts);
|
|
5558
|
+
} catch (error) {
|
|
5559
|
+
console.error("[EXULU] Proxy error", error);
|
|
5560
|
+
reject(error);
|
|
5561
|
+
}
|
|
5562
|
+
});
|
|
5563
|
+
}
|
|
5564
|
+
}
|
|
5565
|
+
));
|
|
5465
5566
|
app.use("/gateway/anthropic/:id", import_express4.default.raw({ type: "*/*", limit: REQUEST_SIZE_LIMIT }), async (req, res) => {
|
|
5466
|
-
console.log("[EXULU] Coding request!!!");
|
|
5467
|
-
const path2 = req.url;
|
|
5468
|
-
const url = `${TARGET_API}${path2}`;
|
|
5469
5567
|
try {
|
|
5470
5568
|
if (!req.body.tools) {
|
|
5471
5569
|
req.body.tools = [];
|
|
@@ -5476,7 +5574,6 @@ Mood: friendly and intelligent.
|
|
|
5476
5574
|
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
5477
5575
|
return;
|
|
5478
5576
|
}
|
|
5479
|
-
console.log("[EXULU] Authenticated call", authenticationResult.user?.email);
|
|
5480
5577
|
const { db: db3 } = await postgresClient();
|
|
5481
5578
|
let query = db3("agents");
|
|
5482
5579
|
query.select("*");
|
|
@@ -5491,17 +5588,7 @@ Mood: friendly and intelligent.
|
|
|
5491
5588
|
res.end(Buffer.from(arrayBuffer));
|
|
5492
5589
|
return;
|
|
5493
5590
|
}
|
|
5494
|
-
console.log("[EXULU]
|
|
5495
|
-
const backend = agents.find((x) => x.id === agent.backend);
|
|
5496
|
-
if (!backend) {
|
|
5497
|
-
const arrayBuffer = createCustomAnthropicStreamingMessage(`
|
|
5498
|
-
\x1B[41m -- Agent ${agent.name} does not have a exulu backend setup, or the exulu backend that was assigned no longer exists. --
|
|
5499
|
-
\x1B[0m`);
|
|
5500
|
-
res.setHeader("Content-Type", "application/json");
|
|
5501
|
-
res.end(Buffer.from(arrayBuffer));
|
|
5502
|
-
return;
|
|
5503
|
-
}
|
|
5504
|
-
console.log("[EXULU] Backend loaded", backend.id);
|
|
5591
|
+
console.log("[EXULU] anthropic proxy called for agent:", agent?.name);
|
|
5505
5592
|
if (!process.env.NEXTAUTH_SECRET) {
|
|
5506
5593
|
const arrayBuffer = createCustomAnthropicStreamingMessage(CLAUDE_MESSAGES.missing_nextauth_secret);
|
|
5507
5594
|
res.setHeader("Content-Type", "application/json");
|
|
@@ -5522,7 +5609,7 @@ Mood: friendly and intelligent.
|
|
|
5522
5609
|
res.end(Buffer.from(arrayBuffer));
|
|
5523
5610
|
return;
|
|
5524
5611
|
}
|
|
5525
|
-
let
|
|
5612
|
+
let anthropicApiKey = variable.value;
|
|
5526
5613
|
if (!variable.encrypted) {
|
|
5527
5614
|
const arrayBuffer = createCustomAnthropicStreamingMessage(CLAUDE_MESSAGES.anthropic_token_variable_not_encrypted);
|
|
5528
5615
|
res.setHeader("Content-Type", "application/json");
|
|
@@ -5531,90 +5618,30 @@ Mood: friendly and intelligent.
|
|
|
5531
5618
|
}
|
|
5532
5619
|
if (variable.encrypted) {
|
|
5533
5620
|
const bytes = import_crypto_js3.default.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
|
|
5534
|
-
|
|
5621
|
+
anthropicApiKey = bytes.toString(import_crypto_js3.default.enc.Utf8);
|
|
5535
5622
|
}
|
|
5536
5623
|
const headers = {
|
|
5537
|
-
"x-api-key":
|
|
5624
|
+
"x-api-key": anthropicApiKey,
|
|
5538
5625
|
"anthropic-version": "2023-06-01",
|
|
5539
5626
|
"content-type": req.headers["content-type"] || "application/json"
|
|
5540
5627
|
};
|
|
5541
5628
|
if (req.headers["accept"]) headers["accept"] = req.headers["accept"];
|
|
5542
5629
|
if (req.headers["user-agent"]) headers["user-agent"] = req.headers["user-agent"];
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
apiKey: providerapikey
|
|
5546
|
-
});
|
|
5547
|
-
if (!model) {
|
|
5548
|
-
const arrayBuffer = createCustomAnthropicStreamingMessage(`
|
|
5549
|
-
\x1B[41m -- Could not create language model instance fro agent ${agent.name}. --
|
|
5550
|
-
\x1B[0m`);
|
|
5551
|
-
res.setHeader("Content-Type", "application/json");
|
|
5552
|
-
res.end(Buffer.from(arrayBuffer));
|
|
5553
|
-
return;
|
|
5554
|
-
}
|
|
5555
|
-
const systemMessagesConcatenated = req.body.system.map((x) => x.text).join("\n\n\n");
|
|
5556
|
-
let messages = convertClaudeCodeMessagesToVercelAISdkMessages(
|
|
5557
|
-
req.body.messages
|
|
5558
|
-
);
|
|
5559
|
-
const tools2 = convertClaudeCodeToolsToVercelAISdkTools(
|
|
5560
|
-
req.body.tools
|
|
5561
|
-
);
|
|
5562
|
-
console.log("STREAMING TEXT");
|
|
5563
|
-
const result = (0, import_ai2.streamText)({
|
|
5564
|
-
model,
|
|
5565
|
-
// Should be a LanguageModelV1
|
|
5566
|
-
// TIP FOR DEBUGGING IF YOU RUN INTO ISSUES / ERRORS REGARDING THE MESSAGES FORMAT. STORE THE 'raw' VARIABLE TO A FILE (fs.write)
|
|
5567
|
-
// AND COPY THE CONTENT INTO THE messages: BELOW, TYPESCRIPT WILL TELL YOU WHAT IS WRONG WHICH IS USUALLY EASIER TO READ THAN THE
|
|
5568
|
-
// ERROR OUTPUT IN THE LOGS.
|
|
5569
|
-
messages,
|
|
5570
|
-
system: systemMessagesConcatenated || "",
|
|
5571
|
-
// prepareStep could be used here to set the model for the first step or change other params
|
|
5572
|
-
maxOutputTokens: req.body.max_tokens,
|
|
5573
|
-
temperature: req.body.temperature,
|
|
5574
|
-
maxRetries: 2,
|
|
5575
|
-
providerOptions: {
|
|
5576
|
-
metadata: {
|
|
5577
|
-
user_id: req.body.metadata?.user_id
|
|
5578
|
-
}
|
|
5579
|
-
},
|
|
5580
|
-
tools: tools2,
|
|
5581
|
-
onFinish: (data) => console.log("[EXULU] Finished stream"),
|
|
5582
|
-
onError: (error) => console.error("[EXULU] chat stream error.", error)
|
|
5583
|
-
// stopWhen: [stepCountIs(1)],
|
|
5630
|
+
const client2 = new import_sdk.default({
|
|
5631
|
+
apiKey: anthropicApiKey
|
|
5584
5632
|
});
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
try {
|
|
5589
|
-
for await (const uiMessage of (0, import_ai2.readUIMessageStream)({
|
|
5590
|
-
stream: result.toUIMessageStream()
|
|
5591
|
-
})) {
|
|
5592
|
-
console.log("Streaming chunk:", uiMessage);
|
|
5593
|
-
const message = {
|
|
5594
|
-
type: "message",
|
|
5595
|
-
role: uiMessage.role,
|
|
5596
|
-
content: uiMessage.parts.map((part) => {
|
|
5597
|
-
if (part.type.includes("tool-")) {
|
|
5598
|
-
const type = part.type;
|
|
5599
|
-
part.type = "tool_use";
|
|
5600
|
-
part.name = type.replace("tool-", "");
|
|
5601
|
-
part.id = part.toolCallId;
|
|
5602
|
-
}
|
|
5603
|
-
return part;
|
|
5604
|
-
})
|
|
5605
|
-
};
|
|
5606
|
-
responses.push(message);
|
|
5607
|
-
console.log("Wrote message to response", message);
|
|
5633
|
+
for await (const event of client2.messages.stream(req.body)) {
|
|
5634
|
+
console.log("[EXULU] Event", event);
|
|
5635
|
+
if (event.message?.usage) {
|
|
5608
5636
|
}
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
res.setHeader("Content-Type", "application/json");
|
|
5615
|
-
res.end(Buffer.from(arrayBuffer));
|
|
5616
|
-
return;
|
|
5637
|
+
const msg = `event: ${event.type}
|
|
5638
|
+
data: ${JSON.stringify(event)}
|
|
5639
|
+
|
|
5640
|
+
`;
|
|
5641
|
+
res.write(msg);
|
|
5617
5642
|
}
|
|
5643
|
+
res.write("event: done\ndata: [DONE]\n\n");
|
|
5644
|
+
res.end();
|
|
5618
5645
|
} catch (error) {
|
|
5619
5646
|
console.error("[PROXY] Manual proxy error:", error);
|
|
5620
5647
|
if (!res.headersSent) {
|
|
@@ -5629,97 +5656,6 @@ Mood: friendly and intelligent.
|
|
|
5629
5656
|
app.use(import_express4.default.static("public"));
|
|
5630
5657
|
return app;
|
|
5631
5658
|
};
|
|
5632
|
-
var convertClaudeCodeToolsToVercelAISdkTools = (tools) => {
|
|
5633
|
-
const result = {};
|
|
5634
|
-
for (const tool2 of tools) {
|
|
5635
|
-
const mySchema = (0, import_ai2.jsonSchema)(tool2.input_schema);
|
|
5636
|
-
tools[tool2.name] = {
|
|
5637
|
-
id: tool2.name,
|
|
5638
|
-
name: tool2.name,
|
|
5639
|
-
description: tool2.description,
|
|
5640
|
-
inputSchema: mySchema
|
|
5641
|
-
};
|
|
5642
|
-
}
|
|
5643
|
-
return result;
|
|
5644
|
-
};
|
|
5645
|
-
var convertClaudeCodeMessagesToVercelAISdkMessages = (messages) => {
|
|
5646
|
-
let raw = messages.map((msg) => {
|
|
5647
|
-
if (!msg.role) {
|
|
5648
|
-
msg.role = "assistant";
|
|
5649
|
-
}
|
|
5650
|
-
delete msg.id;
|
|
5651
|
-
if (!Array.isArray(msg.content)) {
|
|
5652
|
-
return {
|
|
5653
|
-
role: msg.role,
|
|
5654
|
-
content: msg.content
|
|
5655
|
-
};
|
|
5656
|
-
}
|
|
5657
|
-
if (msg.content.some((part) => part.type === "tool_result")) {
|
|
5658
|
-
msg.role = "tool";
|
|
5659
|
-
}
|
|
5660
|
-
let parts = msg.content.map((part) => {
|
|
5661
|
-
if (part.type === "step-start") {
|
|
5662
|
-
return void 0;
|
|
5663
|
-
}
|
|
5664
|
-
if (part.type === "reasoning") {
|
|
5665
|
-
const content = part.text?.length > 1 ? part.text : part.content;
|
|
5666
|
-
return {
|
|
5667
|
-
type: "reasoning",
|
|
5668
|
-
text: content || "No reasoning content provided"
|
|
5669
|
-
};
|
|
5670
|
-
}
|
|
5671
|
-
if (part.type === "tool_use") {
|
|
5672
|
-
part.type = "tool-call";
|
|
5673
|
-
}
|
|
5674
|
-
if (part.type === "tool_result") {
|
|
5675
|
-
part.type = "tool-result";
|
|
5676
|
-
part.output = {
|
|
5677
|
-
type: "text",
|
|
5678
|
-
value: part.text || part.content
|
|
5679
|
-
};
|
|
5680
|
-
part.text = null;
|
|
5681
|
-
part.content = null;
|
|
5682
|
-
if (!part.name && part.tool_use_id) {
|
|
5683
|
-
const allParts = raw.map((x) => x.content).flat();
|
|
5684
|
-
const result = allParts.find((x) => {
|
|
5685
|
-
return x.toolCallId === part.tool_use_id && x.name;
|
|
5686
|
-
});
|
|
5687
|
-
console.log("FIND RESULT!!!!", result);
|
|
5688
|
-
if (result) {
|
|
5689
|
-
part.name = result.name;
|
|
5690
|
-
} else {
|
|
5691
|
-
part.name = "...";
|
|
5692
|
-
}
|
|
5693
|
-
}
|
|
5694
|
-
}
|
|
5695
|
-
if (part.tool_use_id) {
|
|
5696
|
-
part.toolCallId = part.tool_use_id;
|
|
5697
|
-
delete part.tool_use_id;
|
|
5698
|
-
}
|
|
5699
|
-
return {
|
|
5700
|
-
type: part.type,
|
|
5701
|
-
...part.text || part.content ? { text: part.text || part.content } : {},
|
|
5702
|
-
...part.toolCallId ? { toolCallId: part.toolCallId } : {},
|
|
5703
|
-
...part.name ? { toolName: part.name } : {},
|
|
5704
|
-
...part.input ? { input: part.input } : {},
|
|
5705
|
-
...part.output ? { output: part.output } : {},
|
|
5706
|
-
...part.cache_control?.type ? {
|
|
5707
|
-
providerOptions: {
|
|
5708
|
-
anthropic: { cacheControl: { type: part.cache_control?.type } }
|
|
5709
|
-
}
|
|
5710
|
-
} : {}
|
|
5711
|
-
};
|
|
5712
|
-
});
|
|
5713
|
-
parts = parts.filter((part) => part !== void 0);
|
|
5714
|
-
return {
|
|
5715
|
-
role: msg.role,
|
|
5716
|
-
id: msg.id,
|
|
5717
|
-
content: parts
|
|
5718
|
-
};
|
|
5719
|
-
});
|
|
5720
|
-
raw = raw.filter((msg) => msg !== void 0);
|
|
5721
|
-
return raw;
|
|
5722
|
-
};
|
|
5723
5659
|
var createCustomAnthropicStreamingMessage = (message) => {
|
|
5724
5660
|
const responseData = {
|
|
5725
5661
|
type: "message",
|
|
@@ -5782,8 +5718,6 @@ var createWorkers = async (queues2, logger, contexts, _logsDir, tracer) => {
|
|
|
5782
5718
|
}, data.role, bullmqJob.id);
|
|
5783
5719
|
return result;
|
|
5784
5720
|
}
|
|
5785
|
-
if (bullmqJob.data.type === "workflow") {
|
|
5786
|
-
}
|
|
5787
5721
|
} catch (error) {
|
|
5788
5722
|
await db3.from("jobs").where({ redis: bullmqJob.id }).update({
|
|
5789
5723
|
status: "failed",
|
|
@@ -5850,7 +5784,7 @@ var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
|
5850
5784
|
var import_node_crypto4 = require("crypto");
|
|
5851
5785
|
var import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
|
|
5852
5786
|
var import_types = require("@modelcontextprotocol/sdk/types.js");
|
|
5853
|
-
var
|
|
5787
|
+
var import_zod2 = require("zod");
|
|
5854
5788
|
var import_express6 = require("express");
|
|
5855
5789
|
var import_api3 = require("@opentelemetry/api");
|
|
5856
5790
|
var SESSION_ID_HEADER = "mcp-session-id";
|
|
@@ -5907,7 +5841,7 @@ var ExuluMCP = class {
|
|
|
5907
5841
|
{
|
|
5908
5842
|
title: "Code Review",
|
|
5909
5843
|
description: "Review code for best practices and potential issues",
|
|
5910
|
-
argsSchema: { code:
|
|
5844
|
+
argsSchema: { code: import_zod2.z.string() }
|
|
5911
5845
|
},
|
|
5912
5846
|
({ code }) => ({
|
|
5913
5847
|
messages: [{
|
package/dist/index.js
CHANGED
|
@@ -117,6 +117,9 @@ async function ensureDatabaseExists() {
|
|
|
117
117
|
} else {
|
|
118
118
|
console.log(`[EXULU] Database '${dbName}' already exists.`);
|
|
119
119
|
}
|
|
120
|
+
} catch (error) {
|
|
121
|
+
console.error("[EXULU] Error while checking to ensure the database exists, this could be if the user running the server does not have database admin rights, it is fine to ignore this if you are sure the database exists.", error);
|
|
122
|
+
return;
|
|
120
123
|
} finally {
|
|
121
124
|
await defaultKnex.destroy();
|
|
122
125
|
}
|
|
@@ -147,7 +150,11 @@ async function postgresClient() {
|
|
|
147
150
|
ssl: process.env.POSTGRES_DB_SSL === "true" ? { rejectUnauthorized: false } : false
|
|
148
151
|
}
|
|
149
152
|
});
|
|
150
|
-
|
|
153
|
+
try {
|
|
154
|
+
await knex.schema.createExtensionIfNotExists("vector");
|
|
155
|
+
} catch (error) {
|
|
156
|
+
console.error("[EXULU] Error creating vector extension, this might be fine if you already activated the extension and the 'user' running this script does not have higher level database permissions.", error);
|
|
157
|
+
}
|
|
151
158
|
db["exulu"] = knex;
|
|
152
159
|
} catch (error) {
|
|
153
160
|
console.error("[EXULU] Error initializing exulu database.", error);
|
|
@@ -410,7 +417,7 @@ import { jwtVerify, importJWK } from "jose";
|
|
|
410
417
|
var getToken = async (authHeader) => {
|
|
411
418
|
const token = authHeader.split(" ")[1];
|
|
412
419
|
if (!token) {
|
|
413
|
-
throw new Error("No token provided");
|
|
420
|
+
throw new Error("No token provided for user authentication in headers.");
|
|
414
421
|
}
|
|
415
422
|
if (!process.env.NEXTAUTH_SECRET) {
|
|
416
423
|
throw new Error("No NEXTAUTH_SECRET provided");
|
|
@@ -2340,7 +2347,11 @@ var postprocessUpdate = async ({
|
|
|
2340
2347
|
const { db: db3 } = await postgresClient();
|
|
2341
2348
|
console.log("[EXULU] Deleting chunks for item", result.id);
|
|
2342
2349
|
await db3.from(getChunksTableName(context.id)).where({ source: result.id }).delete();
|
|
2350
|
+
console.log("[EXULU] Deleted chunks for item", result.id);
|
|
2351
|
+
console.log("[EXULU] Embedder", context.embedder);
|
|
2352
|
+
console.log("[EXULU] Configuration", context.configuration);
|
|
2343
2353
|
if (context.embedder && (context.configuration.calculateVectors === "onUpdate" || context.configuration.calculateVectors === "always")) {
|
|
2354
|
+
console.log("[EXULU] Generating embeddings for item", result.id);
|
|
2344
2355
|
const { job } = await context.embeddings.generate.one({
|
|
2345
2356
|
item: result,
|
|
2346
2357
|
user,
|
|
@@ -3875,6 +3886,11 @@ var ExuluAgent2 = class {
|
|
|
3875
3886
|
// prepareStep could be used here to set the model for the first step or change other params
|
|
3876
3887
|
system,
|
|
3877
3888
|
maxRetries: 2,
|
|
3889
|
+
providerOptions: {
|
|
3890
|
+
openai: {
|
|
3891
|
+
reasoningSummary: "auto"
|
|
3892
|
+
}
|
|
3893
|
+
},
|
|
3878
3894
|
tools: convertToolsArrayToObject(
|
|
3879
3895
|
currentTools,
|
|
3880
3896
|
allExuluTools,
|
|
@@ -4474,8 +4490,8 @@ var ExuluContext = class {
|
|
|
4474
4490
|
CREATE INDEX IF NOT EXISTS ${tableName}_embedding_hnsw_cosine
|
|
4475
4491
|
ON ${tableName}
|
|
4476
4492
|
USING hnsw (embedding vector_cosine_ops)
|
|
4477
|
-
WHERE embedding IS NOT NULL
|
|
4478
4493
|
WITH (m = 16, ef_construction = 64)
|
|
4494
|
+
WHERE embedding IS NOT NULL
|
|
4479
4495
|
`);
|
|
4480
4496
|
return;
|
|
4481
4497
|
};
|
|
@@ -5039,6 +5055,13 @@ var createUppyRoutes = async (app, config) => {
|
|
|
5039
5055
|
import { InMemoryLRUCache } from "@apollo/utils.keyvaluecache";
|
|
5040
5056
|
import bodyParser from "body-parser";
|
|
5041
5057
|
import CryptoJS3 from "crypto-js";
|
|
5058
|
+
import OpenAI from "openai";
|
|
5059
|
+
import fs from "fs";
|
|
5060
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
5061
|
+
import "@opentelemetry/api";
|
|
5062
|
+
import { jsonSchema } from "ai";
|
|
5063
|
+
import proxy from "express-http-proxy";
|
|
5064
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
5042
5065
|
|
|
5043
5066
|
// src/registry/utils/claude-messages.ts
|
|
5044
5067
|
var CLAUDE_MESSAGES = {
|
|
@@ -5063,13 +5086,6 @@ var CLAUDE_MESSAGES = {
|
|
|
5063
5086
|
};
|
|
5064
5087
|
|
|
5065
5088
|
// src/registry/routes.ts
|
|
5066
|
-
import OpenAI from "openai";
|
|
5067
|
-
import fs from "fs";
|
|
5068
|
-
import { randomUUID as randomUUID3 } from "crypto";
|
|
5069
|
-
import "@opentelemetry/api";
|
|
5070
|
-
import { jsonSchema, readUIMessageStream, streamText as streamText2 } from "ai";
|
|
5071
|
-
import "zod";
|
|
5072
|
-
import "@aws-sdk/client-s3";
|
|
5073
5089
|
var REQUEST_SIZE_LIMIT = "50mb";
|
|
5074
5090
|
var global_queues = {
|
|
5075
5091
|
logs_cleaner: "logs-cleaner"
|
|
@@ -5179,7 +5195,8 @@ var createExpressRoutes = async (app, logger, agents, tools, contexts, config, t
|
|
|
5179
5195
|
path: req.path,
|
|
5180
5196
|
requestId: "req-" + Date.now(),
|
|
5181
5197
|
ipAddress: req.ip,
|
|
5182
|
-
userAgent: req.get("User-Agent")
|
|
5198
|
+
userAgent: req.get("User-Agent"),
|
|
5199
|
+
headers: req.headers
|
|
5183
5200
|
});
|
|
5184
5201
|
logger.info("================");
|
|
5185
5202
|
const authenticationResult = await requestValidators.authenticate(req);
|
|
@@ -5427,11 +5444,92 @@ Mood: friendly and intelligent.
|
|
|
5427
5444
|
} else {
|
|
5428
5445
|
console.log("[EXULU] skipping uppy file upload routes, because no S3 compatible region, key or secret is set in ExuluApp instance.");
|
|
5429
5446
|
}
|
|
5430
|
-
|
|
5447
|
+
app.use("/xxx/anthropic/:id", express.raw({ type: "*/*", limit: REQUEST_SIZE_LIMIT }), proxy(
|
|
5448
|
+
(req, res, next) => {
|
|
5449
|
+
return "https://api.anthropic.com";
|
|
5450
|
+
},
|
|
5451
|
+
{
|
|
5452
|
+
limit: "50mb",
|
|
5453
|
+
memoizeHost: false,
|
|
5454
|
+
preserveHostHdr: true,
|
|
5455
|
+
secure: false,
|
|
5456
|
+
reqAsBuffer: true,
|
|
5457
|
+
proxyReqBodyDecorator: function(bodyContent, srcReq) {
|
|
5458
|
+
return bodyContent;
|
|
5459
|
+
},
|
|
5460
|
+
userResDecorator: function(proxyRes, proxyResData, userReq, userRes) {
|
|
5461
|
+
console.log("[EXULU] Proxy response!", proxyResData);
|
|
5462
|
+
proxyResData = proxyResData.toString();
|
|
5463
|
+
console.log("[EXULU] Proxy response string!", proxyResData);
|
|
5464
|
+
return proxyResData;
|
|
5465
|
+
},
|
|
5466
|
+
proxyReqPathResolver: (req) => {
|
|
5467
|
+
const prefix = `/gateway/anthropic/${req.params.id}`;
|
|
5468
|
+
let path2 = req.url.startsWith(prefix) ? req.url.slice(prefix.length) : req.url;
|
|
5469
|
+
if (!path2.startsWith("/")) path2 = "/" + path2;
|
|
5470
|
+
console.log("[EXULU] Provider path:", path2);
|
|
5471
|
+
return path2;
|
|
5472
|
+
},
|
|
5473
|
+
proxyReqOptDecorator: function(proxyReqOpts, srcReq) {
|
|
5474
|
+
return new Promise(async (resolve, reject) => {
|
|
5475
|
+
try {
|
|
5476
|
+
const authenticationResult = await requestValidators.authenticate(srcReq);
|
|
5477
|
+
if (!authenticationResult.user?.id) {
|
|
5478
|
+
console.log("[EXULU] failed authentication result", authenticationResult);
|
|
5479
|
+
reject(authenticationResult.message);
|
|
5480
|
+
return;
|
|
5481
|
+
}
|
|
5482
|
+
console.log("[EXULU] Authenticated call", authenticationResult.user?.email);
|
|
5483
|
+
const { db: db3 } = await postgresClient();
|
|
5484
|
+
let query = db3("agents");
|
|
5485
|
+
query.select("*");
|
|
5486
|
+
query = applyAccessControl(agentsSchema2(), authenticationResult.user, query);
|
|
5487
|
+
query.where({ id: srcReq.params.id });
|
|
5488
|
+
const agent = await query.first();
|
|
5489
|
+
if (!agent) {
|
|
5490
|
+
reject(new Error("Agent with id " + srcReq.params.id + " not found."));
|
|
5491
|
+
return;
|
|
5492
|
+
}
|
|
5493
|
+
console.log("[EXULU] Agent loaded", agent.name);
|
|
5494
|
+
const backend = agents.find((x) => x.id === agent.backend);
|
|
5495
|
+
if (!process.env.NEXTAUTH_SECRET) {
|
|
5496
|
+
reject(new Error("Missing NEXTAUTH_SECRET"));
|
|
5497
|
+
return;
|
|
5498
|
+
}
|
|
5499
|
+
if (!agent.providerapikey) {
|
|
5500
|
+
reject(new Error("API Key not set for agent"));
|
|
5501
|
+
return;
|
|
5502
|
+
}
|
|
5503
|
+
const variableName = agent.providerapikey;
|
|
5504
|
+
const variable = await db3.from("variables").where({ name: variableName }).first();
|
|
5505
|
+
console.log("[EXULU] Variable loaded", variable);
|
|
5506
|
+
let providerapikey = variable.value;
|
|
5507
|
+
if (!variable.encrypted) {
|
|
5508
|
+
reject(new Error("API Key not encrypted for agent"));
|
|
5509
|
+
return;
|
|
5510
|
+
}
|
|
5511
|
+
if (variable.encrypted) {
|
|
5512
|
+
const bytes = CryptoJS3.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
|
|
5513
|
+
providerapikey = bytes.toString(CryptoJS3.enc.Utf8);
|
|
5514
|
+
}
|
|
5515
|
+
console.log("[EXULU] Provider API key", providerapikey);
|
|
5516
|
+
proxyReqOpts.headers["x-api-key"] = providerapikey;
|
|
5517
|
+
proxyReqOpts.rejectUnauthorized = false;
|
|
5518
|
+
delete proxyReqOpts.headers["provider"];
|
|
5519
|
+
const url = new URL("https://api.anthropic.com");
|
|
5520
|
+
proxyReqOpts.headers["host"] = url.host;
|
|
5521
|
+
proxyReqOpts.headers["anthropic-version"] = "2023-06-01";
|
|
5522
|
+
console.log("[EXULU] Proxy request headers", proxyReqOpts.headers);
|
|
5523
|
+
resolve(proxyReqOpts);
|
|
5524
|
+
} catch (error) {
|
|
5525
|
+
console.error("[EXULU] Proxy error", error);
|
|
5526
|
+
reject(error);
|
|
5527
|
+
}
|
|
5528
|
+
});
|
|
5529
|
+
}
|
|
5530
|
+
}
|
|
5531
|
+
));
|
|
5431
5532
|
app.use("/gateway/anthropic/:id", express.raw({ type: "*/*", limit: REQUEST_SIZE_LIMIT }), async (req, res) => {
|
|
5432
|
-
console.log("[EXULU] Coding request!!!");
|
|
5433
|
-
const path2 = req.url;
|
|
5434
|
-
const url = `${TARGET_API}${path2}`;
|
|
5435
5533
|
try {
|
|
5436
5534
|
if (!req.body.tools) {
|
|
5437
5535
|
req.body.tools = [];
|
|
@@ -5442,7 +5540,6 @@ Mood: friendly and intelligent.
|
|
|
5442
5540
|
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
5443
5541
|
return;
|
|
5444
5542
|
}
|
|
5445
|
-
console.log("[EXULU] Authenticated call", authenticationResult.user?.email);
|
|
5446
5543
|
const { db: db3 } = await postgresClient();
|
|
5447
5544
|
let query = db3("agents");
|
|
5448
5545
|
query.select("*");
|
|
@@ -5457,17 +5554,7 @@ Mood: friendly and intelligent.
|
|
|
5457
5554
|
res.end(Buffer.from(arrayBuffer));
|
|
5458
5555
|
return;
|
|
5459
5556
|
}
|
|
5460
|
-
console.log("[EXULU]
|
|
5461
|
-
const backend = agents.find((x) => x.id === agent.backend);
|
|
5462
|
-
if (!backend) {
|
|
5463
|
-
const arrayBuffer = createCustomAnthropicStreamingMessage(`
|
|
5464
|
-
\x1B[41m -- Agent ${agent.name} does not have a exulu backend setup, or the exulu backend that was assigned no longer exists. --
|
|
5465
|
-
\x1B[0m`);
|
|
5466
|
-
res.setHeader("Content-Type", "application/json");
|
|
5467
|
-
res.end(Buffer.from(arrayBuffer));
|
|
5468
|
-
return;
|
|
5469
|
-
}
|
|
5470
|
-
console.log("[EXULU] Backend loaded", backend.id);
|
|
5557
|
+
console.log("[EXULU] anthropic proxy called for agent:", agent?.name);
|
|
5471
5558
|
if (!process.env.NEXTAUTH_SECRET) {
|
|
5472
5559
|
const arrayBuffer = createCustomAnthropicStreamingMessage(CLAUDE_MESSAGES.missing_nextauth_secret);
|
|
5473
5560
|
res.setHeader("Content-Type", "application/json");
|
|
@@ -5488,7 +5575,7 @@ Mood: friendly and intelligent.
|
|
|
5488
5575
|
res.end(Buffer.from(arrayBuffer));
|
|
5489
5576
|
return;
|
|
5490
5577
|
}
|
|
5491
|
-
let
|
|
5578
|
+
let anthropicApiKey = variable.value;
|
|
5492
5579
|
if (!variable.encrypted) {
|
|
5493
5580
|
const arrayBuffer = createCustomAnthropicStreamingMessage(CLAUDE_MESSAGES.anthropic_token_variable_not_encrypted);
|
|
5494
5581
|
res.setHeader("Content-Type", "application/json");
|
|
@@ -5497,90 +5584,30 @@ Mood: friendly and intelligent.
|
|
|
5497
5584
|
}
|
|
5498
5585
|
if (variable.encrypted) {
|
|
5499
5586
|
const bytes = CryptoJS3.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
|
|
5500
|
-
|
|
5587
|
+
anthropicApiKey = bytes.toString(CryptoJS3.enc.Utf8);
|
|
5501
5588
|
}
|
|
5502
5589
|
const headers = {
|
|
5503
|
-
"x-api-key":
|
|
5590
|
+
"x-api-key": anthropicApiKey,
|
|
5504
5591
|
"anthropic-version": "2023-06-01",
|
|
5505
5592
|
"content-type": req.headers["content-type"] || "application/json"
|
|
5506
5593
|
};
|
|
5507
5594
|
if (req.headers["accept"]) headers["accept"] = req.headers["accept"];
|
|
5508
5595
|
if (req.headers["user-agent"]) headers["user-agent"] = req.headers["user-agent"];
|
|
5509
|
-
|
|
5510
|
-
|
|
5511
|
-
apiKey: providerapikey
|
|
5512
|
-
});
|
|
5513
|
-
if (!model) {
|
|
5514
|
-
const arrayBuffer = createCustomAnthropicStreamingMessage(`
|
|
5515
|
-
\x1B[41m -- Could not create language model instance fro agent ${agent.name}. --
|
|
5516
|
-
\x1B[0m`);
|
|
5517
|
-
res.setHeader("Content-Type", "application/json");
|
|
5518
|
-
res.end(Buffer.from(arrayBuffer));
|
|
5519
|
-
return;
|
|
5520
|
-
}
|
|
5521
|
-
const systemMessagesConcatenated = req.body.system.map((x) => x.text).join("\n\n\n");
|
|
5522
|
-
let messages = convertClaudeCodeMessagesToVercelAISdkMessages(
|
|
5523
|
-
req.body.messages
|
|
5524
|
-
);
|
|
5525
|
-
const tools2 = convertClaudeCodeToolsToVercelAISdkTools(
|
|
5526
|
-
req.body.tools
|
|
5527
|
-
);
|
|
5528
|
-
console.log("STREAMING TEXT");
|
|
5529
|
-
const result = streamText2({
|
|
5530
|
-
model,
|
|
5531
|
-
// Should be a LanguageModelV1
|
|
5532
|
-
// TIP FOR DEBUGGING IF YOU RUN INTO ISSUES / ERRORS REGARDING THE MESSAGES FORMAT. STORE THE 'raw' VARIABLE TO A FILE (fs.write)
|
|
5533
|
-
// AND COPY THE CONTENT INTO THE messages: BELOW, TYPESCRIPT WILL TELL YOU WHAT IS WRONG WHICH IS USUALLY EASIER TO READ THAN THE
|
|
5534
|
-
// ERROR OUTPUT IN THE LOGS.
|
|
5535
|
-
messages,
|
|
5536
|
-
system: systemMessagesConcatenated || "",
|
|
5537
|
-
// prepareStep could be used here to set the model for the first step or change other params
|
|
5538
|
-
maxOutputTokens: req.body.max_tokens,
|
|
5539
|
-
temperature: req.body.temperature,
|
|
5540
|
-
maxRetries: 2,
|
|
5541
|
-
providerOptions: {
|
|
5542
|
-
metadata: {
|
|
5543
|
-
user_id: req.body.metadata?.user_id
|
|
5544
|
-
}
|
|
5545
|
-
},
|
|
5546
|
-
tools: tools2,
|
|
5547
|
-
onFinish: (data) => console.log("[EXULU] Finished stream"),
|
|
5548
|
-
onError: (error) => console.error("[EXULU] chat stream error.", error)
|
|
5549
|
-
// stopWhen: [stepCountIs(1)],
|
|
5596
|
+
const client2 = new Anthropic({
|
|
5597
|
+
apiKey: anthropicApiKey
|
|
5550
5598
|
});
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
try {
|
|
5555
|
-
for await (const uiMessage of readUIMessageStream({
|
|
5556
|
-
stream: result.toUIMessageStream()
|
|
5557
|
-
})) {
|
|
5558
|
-
console.log("Streaming chunk:", uiMessage);
|
|
5559
|
-
const message = {
|
|
5560
|
-
type: "message",
|
|
5561
|
-
role: uiMessage.role,
|
|
5562
|
-
content: uiMessage.parts.map((part) => {
|
|
5563
|
-
if (part.type.includes("tool-")) {
|
|
5564
|
-
const type = part.type;
|
|
5565
|
-
part.type = "tool_use";
|
|
5566
|
-
part.name = type.replace("tool-", "");
|
|
5567
|
-
part.id = part.toolCallId;
|
|
5568
|
-
}
|
|
5569
|
-
return part;
|
|
5570
|
-
})
|
|
5571
|
-
};
|
|
5572
|
-
responses.push(message);
|
|
5573
|
-
console.log("Wrote message to response", message);
|
|
5599
|
+
for await (const event of client2.messages.stream(req.body)) {
|
|
5600
|
+
console.log("[EXULU] Event", event);
|
|
5601
|
+
if (event.message?.usage) {
|
|
5574
5602
|
}
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
res.setHeader("Content-Type", "application/json");
|
|
5581
|
-
res.end(Buffer.from(arrayBuffer));
|
|
5582
|
-
return;
|
|
5603
|
+
const msg = `event: ${event.type}
|
|
5604
|
+
data: ${JSON.stringify(event)}
|
|
5605
|
+
|
|
5606
|
+
`;
|
|
5607
|
+
res.write(msg);
|
|
5583
5608
|
}
|
|
5609
|
+
res.write("event: done\ndata: [DONE]\n\n");
|
|
5610
|
+
res.end();
|
|
5584
5611
|
} catch (error) {
|
|
5585
5612
|
console.error("[PROXY] Manual proxy error:", error);
|
|
5586
5613
|
if (!res.headersSent) {
|
|
@@ -5595,97 +5622,6 @@ Mood: friendly and intelligent.
|
|
|
5595
5622
|
app.use(express.static("public"));
|
|
5596
5623
|
return app;
|
|
5597
5624
|
};
|
|
5598
|
-
var convertClaudeCodeToolsToVercelAISdkTools = (tools) => {
|
|
5599
|
-
const result = {};
|
|
5600
|
-
for (const tool2 of tools) {
|
|
5601
|
-
const mySchema = jsonSchema(tool2.input_schema);
|
|
5602
|
-
tools[tool2.name] = {
|
|
5603
|
-
id: tool2.name,
|
|
5604
|
-
name: tool2.name,
|
|
5605
|
-
description: tool2.description,
|
|
5606
|
-
inputSchema: mySchema
|
|
5607
|
-
};
|
|
5608
|
-
}
|
|
5609
|
-
return result;
|
|
5610
|
-
};
|
|
5611
|
-
var convertClaudeCodeMessagesToVercelAISdkMessages = (messages) => {
|
|
5612
|
-
let raw = messages.map((msg) => {
|
|
5613
|
-
if (!msg.role) {
|
|
5614
|
-
msg.role = "assistant";
|
|
5615
|
-
}
|
|
5616
|
-
delete msg.id;
|
|
5617
|
-
if (!Array.isArray(msg.content)) {
|
|
5618
|
-
return {
|
|
5619
|
-
role: msg.role,
|
|
5620
|
-
content: msg.content
|
|
5621
|
-
};
|
|
5622
|
-
}
|
|
5623
|
-
if (msg.content.some((part) => part.type === "tool_result")) {
|
|
5624
|
-
msg.role = "tool";
|
|
5625
|
-
}
|
|
5626
|
-
let parts = msg.content.map((part) => {
|
|
5627
|
-
if (part.type === "step-start") {
|
|
5628
|
-
return void 0;
|
|
5629
|
-
}
|
|
5630
|
-
if (part.type === "reasoning") {
|
|
5631
|
-
const content = part.text?.length > 1 ? part.text : part.content;
|
|
5632
|
-
return {
|
|
5633
|
-
type: "reasoning",
|
|
5634
|
-
text: content || "No reasoning content provided"
|
|
5635
|
-
};
|
|
5636
|
-
}
|
|
5637
|
-
if (part.type === "tool_use") {
|
|
5638
|
-
part.type = "tool-call";
|
|
5639
|
-
}
|
|
5640
|
-
if (part.type === "tool_result") {
|
|
5641
|
-
part.type = "tool-result";
|
|
5642
|
-
part.output = {
|
|
5643
|
-
type: "text",
|
|
5644
|
-
value: part.text || part.content
|
|
5645
|
-
};
|
|
5646
|
-
part.text = null;
|
|
5647
|
-
part.content = null;
|
|
5648
|
-
if (!part.name && part.tool_use_id) {
|
|
5649
|
-
const allParts = raw.map((x) => x.content).flat();
|
|
5650
|
-
const result = allParts.find((x) => {
|
|
5651
|
-
return x.toolCallId === part.tool_use_id && x.name;
|
|
5652
|
-
});
|
|
5653
|
-
console.log("FIND RESULT!!!!", result);
|
|
5654
|
-
if (result) {
|
|
5655
|
-
part.name = result.name;
|
|
5656
|
-
} else {
|
|
5657
|
-
part.name = "...";
|
|
5658
|
-
}
|
|
5659
|
-
}
|
|
5660
|
-
}
|
|
5661
|
-
if (part.tool_use_id) {
|
|
5662
|
-
part.toolCallId = part.tool_use_id;
|
|
5663
|
-
delete part.tool_use_id;
|
|
5664
|
-
}
|
|
5665
|
-
return {
|
|
5666
|
-
type: part.type,
|
|
5667
|
-
...part.text || part.content ? { text: part.text || part.content } : {},
|
|
5668
|
-
...part.toolCallId ? { toolCallId: part.toolCallId } : {},
|
|
5669
|
-
...part.name ? { toolName: part.name } : {},
|
|
5670
|
-
...part.input ? { input: part.input } : {},
|
|
5671
|
-
...part.output ? { output: part.output } : {},
|
|
5672
|
-
...part.cache_control?.type ? {
|
|
5673
|
-
providerOptions: {
|
|
5674
|
-
anthropic: { cacheControl: { type: part.cache_control?.type } }
|
|
5675
|
-
}
|
|
5676
|
-
} : {}
|
|
5677
|
-
};
|
|
5678
|
-
});
|
|
5679
|
-
parts = parts.filter((part) => part !== void 0);
|
|
5680
|
-
return {
|
|
5681
|
-
role: msg.role,
|
|
5682
|
-
id: msg.id,
|
|
5683
|
-
content: parts
|
|
5684
|
-
};
|
|
5685
|
-
});
|
|
5686
|
-
raw = raw.filter((msg) => msg !== void 0);
|
|
5687
|
-
return raw;
|
|
5688
|
-
};
|
|
5689
5625
|
var createCustomAnthropicStreamingMessage = (message) => {
|
|
5690
5626
|
const responseData = {
|
|
5691
5627
|
type: "message",
|
|
@@ -5748,8 +5684,6 @@ var createWorkers = async (queues2, logger, contexts, _logsDir, tracer) => {
|
|
|
5748
5684
|
}, data.role, bullmqJob.id);
|
|
5749
5685
|
return result;
|
|
5750
5686
|
}
|
|
5751
|
-
if (bullmqJob.data.type === "workflow") {
|
|
5752
|
-
}
|
|
5753
5687
|
} catch (error) {
|
|
5754
5688
|
await db3.from("jobs").where({ redis: bullmqJob.id }).update({
|
|
5755
5689
|
status: "failed",
|
|
@@ -5816,7 +5750,7 @@ import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mc
|
|
|
5816
5750
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
5817
5751
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
5818
5752
|
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
5819
|
-
import { z as
|
|
5753
|
+
import { z as z2 } from "zod";
|
|
5820
5754
|
import "express";
|
|
5821
5755
|
import "@opentelemetry/api";
|
|
5822
5756
|
var SESSION_ID_HEADER = "mcp-session-id";
|
|
@@ -5873,7 +5807,7 @@ var ExuluMCP = class {
|
|
|
5873
5807
|
{
|
|
5874
5808
|
title: "Code Review",
|
|
5875
5809
|
description: "Review code for best practices and potential issues",
|
|
5876
|
-
argsSchema: { code:
|
|
5810
|
+
argsSchema: { code: z2.string() }
|
|
5877
5811
|
},
|
|
5878
5812
|
({ code }) => ({
|
|
5879
5813
|
messages: [{
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exulu/backend",
|
|
3
3
|
"author": "Qventu Bv.",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.25.1",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"private": false,
|
|
7
7
|
"publishConfig": {
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"@opentelemetry/sdk-node": "^0.203.0",
|
|
67
67
|
"@opentelemetry/semantic-conventions": "^1.36.0",
|
|
68
68
|
"@opentelemetry/winston-transport": "^0.14.1",
|
|
69
|
-
"ai": "^5.0.
|
|
69
|
+
"ai": "^5.0.56",
|
|
70
70
|
"apollo-server": "^3.13.0",
|
|
71
71
|
"bcryptjs": "^3.0.2",
|
|
72
72
|
"body-parser": "^2.2.0",
|
|
@@ -78,6 +78,7 @@
|
|
|
78
78
|
"csv-parse": "^5.6.0",
|
|
79
79
|
"dotenv": "^16.5.0",
|
|
80
80
|
"express": "^5.1.0",
|
|
81
|
+
"express-http-proxy": "^2.1.2",
|
|
81
82
|
"graphql": "^16.11.0",
|
|
82
83
|
"graphql-tools": "^9.0.18",
|
|
83
84
|
"graphql-type-json": "^0.3.2",
|