@exulu/backend 1.24.0 → 1.25.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 +3 -3
- package/dist/index.cjs +129 -196
- package/dist/index.js +129 -196
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
# [1.
|
|
1
|
+
# [1.25.0](https://github.com/Qventu/exulu-backend/compare/v1.24.0...v1.25.0) (2025-09-30)
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
### Features
|
|
5
5
|
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* switch to anthropic sdk for claude code endpoint ([4dcfcfd](https://github.com/Qventu/exulu-backend/commit/4dcfcfde031e5515e7329a217498dce4aeafd364))
|
|
7
|
+
* trying out different proxy methods for opencode and claude code ([7630bec](https://github.com/Qventu/exulu-backend/commit/7630bec940a55da58b3dfb210b3a56849cd31a54))
|
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"
|
|
@@ -5461,11 +5477,92 @@ Mood: friendly and intelligent.
|
|
|
5461
5477
|
} else {
|
|
5462
5478
|
console.log("[EXULU] skipping uppy file upload routes, because no S3 compatible region, key or secret is set in ExuluApp instance.");
|
|
5463
5479
|
}
|
|
5464
|
-
|
|
5480
|
+
app.use("/xxx/anthropic/:id", import_express4.default.raw({ type: "*/*", limit: REQUEST_SIZE_LIMIT }), (0, import_express_http_proxy.default)(
|
|
5481
|
+
(req, res, next) => {
|
|
5482
|
+
return "https://api.anthropic.com";
|
|
5483
|
+
},
|
|
5484
|
+
{
|
|
5485
|
+
limit: "50mb",
|
|
5486
|
+
memoizeHost: false,
|
|
5487
|
+
preserveHostHdr: true,
|
|
5488
|
+
secure: false,
|
|
5489
|
+
reqAsBuffer: true,
|
|
5490
|
+
proxyReqBodyDecorator: function(bodyContent, srcReq) {
|
|
5491
|
+
return bodyContent;
|
|
5492
|
+
},
|
|
5493
|
+
userResDecorator: function(proxyRes, proxyResData, userReq, userRes) {
|
|
5494
|
+
console.log("[EXULU] Proxy response!", proxyResData);
|
|
5495
|
+
proxyResData = proxyResData.toString();
|
|
5496
|
+
console.log("[EXULU] Proxy response string!", proxyResData);
|
|
5497
|
+
return proxyResData;
|
|
5498
|
+
},
|
|
5499
|
+
proxyReqPathResolver: (req) => {
|
|
5500
|
+
const prefix = `/gateway/anthropic/${req.params.id}`;
|
|
5501
|
+
let path2 = req.url.startsWith(prefix) ? req.url.slice(prefix.length) : req.url;
|
|
5502
|
+
if (!path2.startsWith("/")) path2 = "/" + path2;
|
|
5503
|
+
console.log("[EXULU] Provider path:", path2);
|
|
5504
|
+
return path2;
|
|
5505
|
+
},
|
|
5506
|
+
proxyReqOptDecorator: function(proxyReqOpts, srcReq) {
|
|
5507
|
+
return new Promise(async (resolve, reject) => {
|
|
5508
|
+
try {
|
|
5509
|
+
const authenticationResult = await requestValidators.authenticate(srcReq);
|
|
5510
|
+
if (!authenticationResult.user?.id) {
|
|
5511
|
+
console.log("[EXULU] failed authentication result", authenticationResult);
|
|
5512
|
+
reject(authenticationResult.message);
|
|
5513
|
+
return;
|
|
5514
|
+
}
|
|
5515
|
+
console.log("[EXULU] Authenticated call", authenticationResult.user?.email);
|
|
5516
|
+
const { db: db3 } = await postgresClient();
|
|
5517
|
+
let query = db3("agents");
|
|
5518
|
+
query.select("*");
|
|
5519
|
+
query = applyAccessControl(agentsSchema2(), authenticationResult.user, query);
|
|
5520
|
+
query.where({ id: srcReq.params.id });
|
|
5521
|
+
const agent = await query.first();
|
|
5522
|
+
if (!agent) {
|
|
5523
|
+
reject(new Error("Agent with id " + srcReq.params.id + " not found."));
|
|
5524
|
+
return;
|
|
5525
|
+
}
|
|
5526
|
+
console.log("[EXULU] Agent loaded", agent.name);
|
|
5527
|
+
const backend = agents.find((x) => x.id === agent.backend);
|
|
5528
|
+
if (!process.env.NEXTAUTH_SECRET) {
|
|
5529
|
+
reject(new Error("Missing NEXTAUTH_SECRET"));
|
|
5530
|
+
return;
|
|
5531
|
+
}
|
|
5532
|
+
if (!agent.providerapikey) {
|
|
5533
|
+
reject(new Error("API Key not set for agent"));
|
|
5534
|
+
return;
|
|
5535
|
+
}
|
|
5536
|
+
const variableName = agent.providerapikey;
|
|
5537
|
+
const variable = await db3.from("variables").where({ name: variableName }).first();
|
|
5538
|
+
console.log("[EXULU] Variable loaded", variable);
|
|
5539
|
+
let providerapikey = variable.value;
|
|
5540
|
+
if (!variable.encrypted) {
|
|
5541
|
+
reject(new Error("API Key not encrypted for agent"));
|
|
5542
|
+
return;
|
|
5543
|
+
}
|
|
5544
|
+
if (variable.encrypted) {
|
|
5545
|
+
const bytes = import_crypto_js3.default.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
|
|
5546
|
+
providerapikey = bytes.toString(import_crypto_js3.default.enc.Utf8);
|
|
5547
|
+
}
|
|
5548
|
+
console.log("[EXULU] Provider API key", providerapikey);
|
|
5549
|
+
proxyReqOpts.headers["x-api-key"] = providerapikey;
|
|
5550
|
+
proxyReqOpts.rejectUnauthorized = false;
|
|
5551
|
+
delete proxyReqOpts.headers["provider"];
|
|
5552
|
+
const url = new URL("https://api.anthropic.com");
|
|
5553
|
+
proxyReqOpts.headers["host"] = url.host;
|
|
5554
|
+
proxyReqOpts.headers["anthropic-version"] = "2023-06-01";
|
|
5555
|
+
console.log("[EXULU] Proxy request headers", proxyReqOpts.headers);
|
|
5556
|
+
resolve(proxyReqOpts);
|
|
5557
|
+
} catch (error) {
|
|
5558
|
+
console.error("[EXULU] Proxy error", error);
|
|
5559
|
+
reject(error);
|
|
5560
|
+
}
|
|
5561
|
+
});
|
|
5562
|
+
}
|
|
5563
|
+
}
|
|
5564
|
+
));
|
|
5465
5565
|
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
5566
|
try {
|
|
5470
5567
|
if (!req.body.tools) {
|
|
5471
5568
|
req.body.tools = [];
|
|
@@ -5476,7 +5573,6 @@ Mood: friendly and intelligent.
|
|
|
5476
5573
|
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
5477
5574
|
return;
|
|
5478
5575
|
}
|
|
5479
|
-
console.log("[EXULU] Authenticated call", authenticationResult.user?.email);
|
|
5480
5576
|
const { db: db3 } = await postgresClient();
|
|
5481
5577
|
let query = db3("agents");
|
|
5482
5578
|
query.select("*");
|
|
@@ -5491,17 +5587,7 @@ Mood: friendly and intelligent.
|
|
|
5491
5587
|
res.end(Buffer.from(arrayBuffer));
|
|
5492
5588
|
return;
|
|
5493
5589
|
}
|
|
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);
|
|
5590
|
+
console.log("[EXULU] anthropic proxy called for agent:", agent?.name);
|
|
5505
5591
|
if (!process.env.NEXTAUTH_SECRET) {
|
|
5506
5592
|
const arrayBuffer = createCustomAnthropicStreamingMessage(CLAUDE_MESSAGES.missing_nextauth_secret);
|
|
5507
5593
|
res.setHeader("Content-Type", "application/json");
|
|
@@ -5522,7 +5608,7 @@ Mood: friendly and intelligent.
|
|
|
5522
5608
|
res.end(Buffer.from(arrayBuffer));
|
|
5523
5609
|
return;
|
|
5524
5610
|
}
|
|
5525
|
-
let
|
|
5611
|
+
let anthropicApiKey = variable.value;
|
|
5526
5612
|
if (!variable.encrypted) {
|
|
5527
5613
|
const arrayBuffer = createCustomAnthropicStreamingMessage(CLAUDE_MESSAGES.anthropic_token_variable_not_encrypted);
|
|
5528
5614
|
res.setHeader("Content-Type", "application/json");
|
|
@@ -5531,90 +5617,30 @@ Mood: friendly and intelligent.
|
|
|
5531
5617
|
}
|
|
5532
5618
|
if (variable.encrypted) {
|
|
5533
5619
|
const bytes = import_crypto_js3.default.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
|
|
5534
|
-
|
|
5620
|
+
anthropicApiKey = bytes.toString(import_crypto_js3.default.enc.Utf8);
|
|
5535
5621
|
}
|
|
5536
5622
|
const headers = {
|
|
5537
|
-
"x-api-key":
|
|
5623
|
+
"x-api-key": anthropicApiKey,
|
|
5538
5624
|
"anthropic-version": "2023-06-01",
|
|
5539
5625
|
"content-type": req.headers["content-type"] || "application/json"
|
|
5540
5626
|
};
|
|
5541
5627
|
if (req.headers["accept"]) headers["accept"] = req.headers["accept"];
|
|
5542
5628
|
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)],
|
|
5629
|
+
const client2 = new import_sdk.default({
|
|
5630
|
+
apiKey: anthropicApiKey
|
|
5584
5631
|
});
|
|
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);
|
|
5632
|
+
for await (const event of client2.messages.stream(req.body)) {
|
|
5633
|
+
console.log("[EXULU] Event", event);
|
|
5634
|
+
if (event.message?.usage) {
|
|
5608
5635
|
}
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
res.setHeader("Content-Type", "application/json");
|
|
5615
|
-
res.end(Buffer.from(arrayBuffer));
|
|
5616
|
-
return;
|
|
5636
|
+
const msg = `event: ${event.type}
|
|
5637
|
+
data: ${JSON.stringify(event)}
|
|
5638
|
+
|
|
5639
|
+
`;
|
|
5640
|
+
res.write(msg);
|
|
5617
5641
|
}
|
|
5642
|
+
res.write("event: done\ndata: [DONE]\n\n");
|
|
5643
|
+
res.end();
|
|
5618
5644
|
} catch (error) {
|
|
5619
5645
|
console.error("[PROXY] Manual proxy error:", error);
|
|
5620
5646
|
if (!res.headersSent) {
|
|
@@ -5629,97 +5655,6 @@ Mood: friendly and intelligent.
|
|
|
5629
5655
|
app.use(import_express4.default.static("public"));
|
|
5630
5656
|
return app;
|
|
5631
5657
|
};
|
|
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
5658
|
var createCustomAnthropicStreamingMessage = (message) => {
|
|
5724
5659
|
const responseData = {
|
|
5725
5660
|
type: "message",
|
|
@@ -5782,8 +5717,6 @@ var createWorkers = async (queues2, logger, contexts, _logsDir, tracer) => {
|
|
|
5782
5717
|
}, data.role, bullmqJob.id);
|
|
5783
5718
|
return result;
|
|
5784
5719
|
}
|
|
5785
|
-
if (bullmqJob.data.type === "workflow") {
|
|
5786
|
-
}
|
|
5787
5720
|
} catch (error) {
|
|
5788
5721
|
await db3.from("jobs").where({ redis: bullmqJob.id }).update({
|
|
5789
5722
|
status: "failed",
|
|
@@ -5850,7 +5783,7 @@ var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
|
5850
5783
|
var import_node_crypto4 = require("crypto");
|
|
5851
5784
|
var import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
|
|
5852
5785
|
var import_types = require("@modelcontextprotocol/sdk/types.js");
|
|
5853
|
-
var
|
|
5786
|
+
var import_zod2 = require("zod");
|
|
5854
5787
|
var import_express6 = require("express");
|
|
5855
5788
|
var import_api3 = require("@opentelemetry/api");
|
|
5856
5789
|
var SESSION_ID_HEADER = "mcp-session-id";
|
|
@@ -5907,7 +5840,7 @@ var ExuluMCP = class {
|
|
|
5907
5840
|
{
|
|
5908
5841
|
title: "Code Review",
|
|
5909
5842
|
description: "Review code for best practices and potential issues",
|
|
5910
|
-
argsSchema: { code:
|
|
5843
|
+
argsSchema: { code: import_zod2.z.string() }
|
|
5911
5844
|
},
|
|
5912
5845
|
({ code }) => ({
|
|
5913
5846
|
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"
|
|
@@ -5427,11 +5443,92 @@ Mood: friendly and intelligent.
|
|
|
5427
5443
|
} else {
|
|
5428
5444
|
console.log("[EXULU] skipping uppy file upload routes, because no S3 compatible region, key or secret is set in ExuluApp instance.");
|
|
5429
5445
|
}
|
|
5430
|
-
|
|
5446
|
+
app.use("/xxx/anthropic/:id", express.raw({ type: "*/*", limit: REQUEST_SIZE_LIMIT }), proxy(
|
|
5447
|
+
(req, res, next) => {
|
|
5448
|
+
return "https://api.anthropic.com";
|
|
5449
|
+
},
|
|
5450
|
+
{
|
|
5451
|
+
limit: "50mb",
|
|
5452
|
+
memoizeHost: false,
|
|
5453
|
+
preserveHostHdr: true,
|
|
5454
|
+
secure: false,
|
|
5455
|
+
reqAsBuffer: true,
|
|
5456
|
+
proxyReqBodyDecorator: function(bodyContent, srcReq) {
|
|
5457
|
+
return bodyContent;
|
|
5458
|
+
},
|
|
5459
|
+
userResDecorator: function(proxyRes, proxyResData, userReq, userRes) {
|
|
5460
|
+
console.log("[EXULU] Proxy response!", proxyResData);
|
|
5461
|
+
proxyResData = proxyResData.toString();
|
|
5462
|
+
console.log("[EXULU] Proxy response string!", proxyResData);
|
|
5463
|
+
return proxyResData;
|
|
5464
|
+
},
|
|
5465
|
+
proxyReqPathResolver: (req) => {
|
|
5466
|
+
const prefix = `/gateway/anthropic/${req.params.id}`;
|
|
5467
|
+
let path2 = req.url.startsWith(prefix) ? req.url.slice(prefix.length) : req.url;
|
|
5468
|
+
if (!path2.startsWith("/")) path2 = "/" + path2;
|
|
5469
|
+
console.log("[EXULU] Provider path:", path2);
|
|
5470
|
+
return path2;
|
|
5471
|
+
},
|
|
5472
|
+
proxyReqOptDecorator: function(proxyReqOpts, srcReq) {
|
|
5473
|
+
return new Promise(async (resolve, reject) => {
|
|
5474
|
+
try {
|
|
5475
|
+
const authenticationResult = await requestValidators.authenticate(srcReq);
|
|
5476
|
+
if (!authenticationResult.user?.id) {
|
|
5477
|
+
console.log("[EXULU] failed authentication result", authenticationResult);
|
|
5478
|
+
reject(authenticationResult.message);
|
|
5479
|
+
return;
|
|
5480
|
+
}
|
|
5481
|
+
console.log("[EXULU] Authenticated call", authenticationResult.user?.email);
|
|
5482
|
+
const { db: db3 } = await postgresClient();
|
|
5483
|
+
let query = db3("agents");
|
|
5484
|
+
query.select("*");
|
|
5485
|
+
query = applyAccessControl(agentsSchema2(), authenticationResult.user, query);
|
|
5486
|
+
query.where({ id: srcReq.params.id });
|
|
5487
|
+
const agent = await query.first();
|
|
5488
|
+
if (!agent) {
|
|
5489
|
+
reject(new Error("Agent with id " + srcReq.params.id + " not found."));
|
|
5490
|
+
return;
|
|
5491
|
+
}
|
|
5492
|
+
console.log("[EXULU] Agent loaded", agent.name);
|
|
5493
|
+
const backend = agents.find((x) => x.id === agent.backend);
|
|
5494
|
+
if (!process.env.NEXTAUTH_SECRET) {
|
|
5495
|
+
reject(new Error("Missing NEXTAUTH_SECRET"));
|
|
5496
|
+
return;
|
|
5497
|
+
}
|
|
5498
|
+
if (!agent.providerapikey) {
|
|
5499
|
+
reject(new Error("API Key not set for agent"));
|
|
5500
|
+
return;
|
|
5501
|
+
}
|
|
5502
|
+
const variableName = agent.providerapikey;
|
|
5503
|
+
const variable = await db3.from("variables").where({ name: variableName }).first();
|
|
5504
|
+
console.log("[EXULU] Variable loaded", variable);
|
|
5505
|
+
let providerapikey = variable.value;
|
|
5506
|
+
if (!variable.encrypted) {
|
|
5507
|
+
reject(new Error("API Key not encrypted for agent"));
|
|
5508
|
+
return;
|
|
5509
|
+
}
|
|
5510
|
+
if (variable.encrypted) {
|
|
5511
|
+
const bytes = CryptoJS3.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
|
|
5512
|
+
providerapikey = bytes.toString(CryptoJS3.enc.Utf8);
|
|
5513
|
+
}
|
|
5514
|
+
console.log("[EXULU] Provider API key", providerapikey);
|
|
5515
|
+
proxyReqOpts.headers["x-api-key"] = providerapikey;
|
|
5516
|
+
proxyReqOpts.rejectUnauthorized = false;
|
|
5517
|
+
delete proxyReqOpts.headers["provider"];
|
|
5518
|
+
const url = new URL("https://api.anthropic.com");
|
|
5519
|
+
proxyReqOpts.headers["host"] = url.host;
|
|
5520
|
+
proxyReqOpts.headers["anthropic-version"] = "2023-06-01";
|
|
5521
|
+
console.log("[EXULU] Proxy request headers", proxyReqOpts.headers);
|
|
5522
|
+
resolve(proxyReqOpts);
|
|
5523
|
+
} catch (error) {
|
|
5524
|
+
console.error("[EXULU] Proxy error", error);
|
|
5525
|
+
reject(error);
|
|
5526
|
+
}
|
|
5527
|
+
});
|
|
5528
|
+
}
|
|
5529
|
+
}
|
|
5530
|
+
));
|
|
5431
5531
|
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
5532
|
try {
|
|
5436
5533
|
if (!req.body.tools) {
|
|
5437
5534
|
req.body.tools = [];
|
|
@@ -5442,7 +5539,6 @@ Mood: friendly and intelligent.
|
|
|
5442
5539
|
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
5443
5540
|
return;
|
|
5444
5541
|
}
|
|
5445
|
-
console.log("[EXULU] Authenticated call", authenticationResult.user?.email);
|
|
5446
5542
|
const { db: db3 } = await postgresClient();
|
|
5447
5543
|
let query = db3("agents");
|
|
5448
5544
|
query.select("*");
|
|
@@ -5457,17 +5553,7 @@ Mood: friendly and intelligent.
|
|
|
5457
5553
|
res.end(Buffer.from(arrayBuffer));
|
|
5458
5554
|
return;
|
|
5459
5555
|
}
|
|
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);
|
|
5556
|
+
console.log("[EXULU] anthropic proxy called for agent:", agent?.name);
|
|
5471
5557
|
if (!process.env.NEXTAUTH_SECRET) {
|
|
5472
5558
|
const arrayBuffer = createCustomAnthropicStreamingMessage(CLAUDE_MESSAGES.missing_nextauth_secret);
|
|
5473
5559
|
res.setHeader("Content-Type", "application/json");
|
|
@@ -5488,7 +5574,7 @@ Mood: friendly and intelligent.
|
|
|
5488
5574
|
res.end(Buffer.from(arrayBuffer));
|
|
5489
5575
|
return;
|
|
5490
5576
|
}
|
|
5491
|
-
let
|
|
5577
|
+
let anthropicApiKey = variable.value;
|
|
5492
5578
|
if (!variable.encrypted) {
|
|
5493
5579
|
const arrayBuffer = createCustomAnthropicStreamingMessage(CLAUDE_MESSAGES.anthropic_token_variable_not_encrypted);
|
|
5494
5580
|
res.setHeader("Content-Type", "application/json");
|
|
@@ -5497,90 +5583,30 @@ Mood: friendly and intelligent.
|
|
|
5497
5583
|
}
|
|
5498
5584
|
if (variable.encrypted) {
|
|
5499
5585
|
const bytes = CryptoJS3.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
|
|
5500
|
-
|
|
5586
|
+
anthropicApiKey = bytes.toString(CryptoJS3.enc.Utf8);
|
|
5501
5587
|
}
|
|
5502
5588
|
const headers = {
|
|
5503
|
-
"x-api-key":
|
|
5589
|
+
"x-api-key": anthropicApiKey,
|
|
5504
5590
|
"anthropic-version": "2023-06-01",
|
|
5505
5591
|
"content-type": req.headers["content-type"] || "application/json"
|
|
5506
5592
|
};
|
|
5507
5593
|
if (req.headers["accept"]) headers["accept"] = req.headers["accept"];
|
|
5508
5594
|
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)],
|
|
5595
|
+
const client2 = new Anthropic({
|
|
5596
|
+
apiKey: anthropicApiKey
|
|
5550
5597
|
});
|
|
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);
|
|
5598
|
+
for await (const event of client2.messages.stream(req.body)) {
|
|
5599
|
+
console.log("[EXULU] Event", event);
|
|
5600
|
+
if (event.message?.usage) {
|
|
5574
5601
|
}
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
res.setHeader("Content-Type", "application/json");
|
|
5581
|
-
res.end(Buffer.from(arrayBuffer));
|
|
5582
|
-
return;
|
|
5602
|
+
const msg = `event: ${event.type}
|
|
5603
|
+
data: ${JSON.stringify(event)}
|
|
5604
|
+
|
|
5605
|
+
`;
|
|
5606
|
+
res.write(msg);
|
|
5583
5607
|
}
|
|
5608
|
+
res.write("event: done\ndata: [DONE]\n\n");
|
|
5609
|
+
res.end();
|
|
5584
5610
|
} catch (error) {
|
|
5585
5611
|
console.error("[PROXY] Manual proxy error:", error);
|
|
5586
5612
|
if (!res.headersSent) {
|
|
@@ -5595,97 +5621,6 @@ Mood: friendly and intelligent.
|
|
|
5595
5621
|
app.use(express.static("public"));
|
|
5596
5622
|
return app;
|
|
5597
5623
|
};
|
|
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
5624
|
var createCustomAnthropicStreamingMessage = (message) => {
|
|
5690
5625
|
const responseData = {
|
|
5691
5626
|
type: "message",
|
|
@@ -5748,8 +5683,6 @@ var createWorkers = async (queues2, logger, contexts, _logsDir, tracer) => {
|
|
|
5748
5683
|
}, data.role, bullmqJob.id);
|
|
5749
5684
|
return result;
|
|
5750
5685
|
}
|
|
5751
|
-
if (bullmqJob.data.type === "workflow") {
|
|
5752
|
-
}
|
|
5753
5686
|
} catch (error) {
|
|
5754
5687
|
await db3.from("jobs").where({ redis: bullmqJob.id }).update({
|
|
5755
5688
|
status: "failed",
|
|
@@ -5816,7 +5749,7 @@ import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mc
|
|
|
5816
5749
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
5817
5750
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
5818
5751
|
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
5819
|
-
import { z as
|
|
5752
|
+
import { z as z2 } from "zod";
|
|
5820
5753
|
import "express";
|
|
5821
5754
|
import "@opentelemetry/api";
|
|
5822
5755
|
var SESSION_ID_HEADER = "mcp-session-id";
|
|
@@ -5873,7 +5806,7 @@ var ExuluMCP = class {
|
|
|
5873
5806
|
{
|
|
5874
5807
|
title: "Code Review",
|
|
5875
5808
|
description: "Review code for best practices and potential issues",
|
|
5876
|
-
argsSchema: { code:
|
|
5809
|
+
argsSchema: { code: z2.string() }
|
|
5877
5810
|
},
|
|
5878
5811
|
({ code }) => ({
|
|
5879
5812
|
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.0",
|
|
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",
|