@exulu/backend 1.52.0 → 1.53.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 +464 -193
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +456 -185
- package/ee/workers.ts +27 -1
- package/package.json +2 -1
package/dist/index.cjs
CHANGED
|
@@ -805,7 +805,7 @@ var JOB_STATUS_ENUM = {
|
|
|
805
805
|
};
|
|
806
806
|
|
|
807
807
|
// ee/agentic-retrieval/index.ts
|
|
808
|
-
var
|
|
808
|
+
var import_zod6 = require("zod");
|
|
809
809
|
var import_ai2 = require("ai");
|
|
810
810
|
|
|
811
811
|
// src/uppy/index.ts
|
|
@@ -3229,7 +3229,7 @@ var mapType = (t, type, name, defaultValue, unique) => {
|
|
|
3229
3229
|
|
|
3230
3230
|
// src/exulu/tool.ts
|
|
3231
3231
|
var import_ai = require("ai");
|
|
3232
|
-
var
|
|
3232
|
+
var import_zod4 = require("zod");
|
|
3233
3233
|
var import_crypto_js2 = __toESM(require("crypto-js"), 1);
|
|
3234
3234
|
|
|
3235
3235
|
// src/templates/tools/convert-exulu-tools-to-ai-sdk-tools.ts
|
|
@@ -3445,6 +3445,102 @@ function sanitizeToolName(name) {
|
|
|
3445
3445
|
|
|
3446
3446
|
// src/templates/tools/convert-exulu-tools-to-ai-sdk-tools.ts
|
|
3447
3447
|
var import_node_crypto2 = require("crypto");
|
|
3448
|
+
|
|
3449
|
+
// src/templates/tools/memory-tool.ts
|
|
3450
|
+
var import_zod3 = require("zod");
|
|
3451
|
+
var import_fs = __toESM(require("fs"), 1);
|
|
3452
|
+
var createNewMemoryItemTool = (agent, context) => {
|
|
3453
|
+
const fields = {
|
|
3454
|
+
name: import_zod3.z.string().describe("The name of the item to create"),
|
|
3455
|
+
description: import_zod3.z.string().describe("The description of the item to create")
|
|
3456
|
+
};
|
|
3457
|
+
for (const field of context.fields) {
|
|
3458
|
+
switch (field.type) {
|
|
3459
|
+
case "text":
|
|
3460
|
+
case "longText":
|
|
3461
|
+
case "shortText":
|
|
3462
|
+
case "code":
|
|
3463
|
+
case "enum":
|
|
3464
|
+
fields[field.name] = import_zod3.z.string().describe("The " + field.name + " of the item to create");
|
|
3465
|
+
break;
|
|
3466
|
+
case "json":
|
|
3467
|
+
fields[field.name] = import_zod3.z.string({}).describe(
|
|
3468
|
+
"The " + field.name + " of the item to create, it should be a valid JSON string."
|
|
3469
|
+
);
|
|
3470
|
+
break;
|
|
3471
|
+
case "markdown":
|
|
3472
|
+
fields[field.name] = import_zod3.z.string().describe(
|
|
3473
|
+
"The " + field.name + " of the item to create, it should be a valid Markdown string."
|
|
3474
|
+
);
|
|
3475
|
+
break;
|
|
3476
|
+
case "number":
|
|
3477
|
+
fields[field.name] = import_zod3.z.number().describe("The " + field.name + " of the item to create");
|
|
3478
|
+
break;
|
|
3479
|
+
case "boolean":
|
|
3480
|
+
fields[field.name] = import_zod3.z.boolean().describe("The " + field.name + " of the item to create");
|
|
3481
|
+
break;
|
|
3482
|
+
case "file":
|
|
3483
|
+
case "uuid":
|
|
3484
|
+
case "date":
|
|
3485
|
+
break;
|
|
3486
|
+
default:
|
|
3487
|
+
fields[field.name] = import_zod3.z.string().describe("The " + field.name + " of the item to create");
|
|
3488
|
+
break;
|
|
3489
|
+
}
|
|
3490
|
+
}
|
|
3491
|
+
const toolName = "create_" + sanitizeName(context.name) + "_memory_item";
|
|
3492
|
+
return new ExuluTool({
|
|
3493
|
+
id: toolName,
|
|
3494
|
+
name: "Create " + context.name + " Memory Item",
|
|
3495
|
+
category: agent.name + "_memory",
|
|
3496
|
+
description: "Create a new memory item in the " + agent.name + " memory context",
|
|
3497
|
+
type: "function",
|
|
3498
|
+
inputSchema: import_zod3.z.object(fields),
|
|
3499
|
+
config: [],
|
|
3500
|
+
execute: async ({ name, description, mode, information, exuluConfig, user }) => {
|
|
3501
|
+
let result = { result: "" };
|
|
3502
|
+
import_fs.default.writeFileSync("memory-tool.json", JSON.stringify({ name, description, information }, null, 2));
|
|
3503
|
+
try {
|
|
3504
|
+
const newItem = {
|
|
3505
|
+
name,
|
|
3506
|
+
description,
|
|
3507
|
+
information,
|
|
3508
|
+
rights_mode: "public"
|
|
3509
|
+
};
|
|
3510
|
+
const { item: createdItem, job: createdJob } = await context.createItem(
|
|
3511
|
+
newItem,
|
|
3512
|
+
exuluConfig,
|
|
3513
|
+
user?.id,
|
|
3514
|
+
user?.role?.id,
|
|
3515
|
+
false
|
|
3516
|
+
);
|
|
3517
|
+
import_fs.default.writeFileSync("memory-tool-created.json", JSON.stringify({ createdItem, createdJob }, null, 2));
|
|
3518
|
+
if (createdJob) {
|
|
3519
|
+
result = {
|
|
3520
|
+
result: `Created a Job to create the memory item with the following ID: ${createdJob}`
|
|
3521
|
+
};
|
|
3522
|
+
} else if (createdItem) {
|
|
3523
|
+
result = {
|
|
3524
|
+
result: `Created memory item with the following ID: ${createdItem.id}`
|
|
3525
|
+
};
|
|
3526
|
+
} else {
|
|
3527
|
+
result = {
|
|
3528
|
+
result: `Failed to create memory item`
|
|
3529
|
+
};
|
|
3530
|
+
}
|
|
3531
|
+
} catch (error) {
|
|
3532
|
+
import_fs.default.writeFileSync("memory-tool-error.json", JSON.stringify({ name, description, information, error }, null, 2));
|
|
3533
|
+
console.error("[EXULU] Error creating memory item", error);
|
|
3534
|
+
result = {
|
|
3535
|
+
result: `Failed to create memory item: ${error instanceof Error ? error.message : String(error)}`
|
|
3536
|
+
};
|
|
3537
|
+
}
|
|
3538
|
+
return result;
|
|
3539
|
+
}
|
|
3540
|
+
});
|
|
3541
|
+
};
|
|
3542
|
+
|
|
3543
|
+
// src/templates/tools/convert-exulu-tools-to-ai-sdk-tools.ts
|
|
3448
3544
|
var generateS3Key = (filename) => `${(0, import_node_crypto2.randomUUID)()}-${filename}`;
|
|
3449
3545
|
var s3Client2;
|
|
3450
3546
|
var getMimeType = (type) => {
|
|
@@ -3548,6 +3644,21 @@ var convertExuluToolsToAiSdkTools = async (currentTools, approvedTools, allExulu
|
|
|
3548
3644
|
currentTools.push(projectRetrievalTool);
|
|
3549
3645
|
}
|
|
3550
3646
|
}
|
|
3647
|
+
if (agent?.memory && contexts?.length) {
|
|
3648
|
+
const context = contexts.find((context2) => context2.id === agent?.memory);
|
|
3649
|
+
if (!context) {
|
|
3650
|
+
throw new Error(
|
|
3651
|
+
"Context was set for agent memory but not found in the contexts: " + agent?.memory + " please double check with a developer to see if the context was removed from code."
|
|
3652
|
+
);
|
|
3653
|
+
}
|
|
3654
|
+
const createNewMemoryTool = createNewMemoryItemTool(agent, context);
|
|
3655
|
+
if (createNewMemoryTool) {
|
|
3656
|
+
if (!currentTools) {
|
|
3657
|
+
currentTools = [];
|
|
3658
|
+
}
|
|
3659
|
+
currentTools.push(createNewMemoryTool);
|
|
3660
|
+
}
|
|
3661
|
+
}
|
|
3551
3662
|
console.log("[EXULU] Convert tools array to object, session items", items);
|
|
3552
3663
|
if (items) {
|
|
3553
3664
|
const sessionItemsRetrievalTool = await createSessionItemsRetrievalTool({
|
|
@@ -3793,7 +3904,7 @@ var ExuluTool = class {
|
|
|
3793
3904
|
this.type = type;
|
|
3794
3905
|
this.tool = (0, import_ai.tool)({
|
|
3795
3906
|
description,
|
|
3796
|
-
inputSchema: inputSchema ||
|
|
3907
|
+
inputSchema: inputSchema || import_zod4.z.object({}),
|
|
3797
3908
|
execute: execute2
|
|
3798
3909
|
});
|
|
3799
3910
|
}
|
|
@@ -3886,7 +3997,7 @@ var ExuluTool = class {
|
|
|
3886
3997
|
};
|
|
3887
3998
|
|
|
3888
3999
|
// src/exulu/context.ts
|
|
3889
|
-
var
|
|
4000
|
+
var import_zod5 = require("zod");
|
|
3890
4001
|
|
|
3891
4002
|
// ee/queues/decorator.ts
|
|
3892
4003
|
var import_bullmq2 = require("bullmq");
|
|
@@ -4044,6 +4155,7 @@ var ExuluContext2 = class {
|
|
|
4044
4155
|
exuluConfig
|
|
4045
4156
|
});
|
|
4046
4157
|
if (!result) {
|
|
4158
|
+
console.log("[EXULU] Item filtered out by processor, skipping processing execution...");
|
|
4047
4159
|
return {
|
|
4048
4160
|
result: void 0,
|
|
4049
4161
|
job: void 0
|
|
@@ -4244,10 +4356,12 @@ var ExuluContext2 = class {
|
|
|
4244
4356
|
}
|
|
4245
4357
|
}
|
|
4246
4358
|
});
|
|
4359
|
+
console.log("[EXULU] Creating item", item);
|
|
4247
4360
|
const mutation = db2.from(getTableName(this.id)).insert({
|
|
4248
4361
|
...item,
|
|
4249
4362
|
tags: item.tags ? Array.isArray(item.tags) ? item.tags.join(",") : item.tags : void 0
|
|
4250
4363
|
}).returning("id");
|
|
4364
|
+
console.log("[EXULU] Upsert", upsert);
|
|
4251
4365
|
if (upsert) {
|
|
4252
4366
|
if (item.external_id) {
|
|
4253
4367
|
mutation.onConflict("external_id").merge();
|
|
@@ -4266,7 +4380,6 @@ var ExuluContext2 = class {
|
|
|
4266
4380
|
let shouldGenerateEmbeddings = this.embedder && generateEmbeddingsOverwrite !== false && (generateEmbeddingsOverwrite || this.configuration.calculateVectors === "onInsert" || this.configuration.calculateVectors === "always");
|
|
4267
4381
|
if (this.processor) {
|
|
4268
4382
|
const processor = this.processor;
|
|
4269
|
-
console.log("[EXULU] Processor found", processor);
|
|
4270
4383
|
if (processor && (processor?.config?.trigger === "onInsert" || processor?.config?.trigger === "onUpdate" || processor?.config?.trigger === "always")) {
|
|
4271
4384
|
const { job: processorJob, result: processorResult } = await this.processField(
|
|
4272
4385
|
"api",
|
|
@@ -4281,7 +4394,7 @@ var ExuluContext2 = class {
|
|
|
4281
4394
|
if (processorJob) {
|
|
4282
4395
|
jobs.push(processorJob);
|
|
4283
4396
|
}
|
|
4284
|
-
if (!processorJob) {
|
|
4397
|
+
if (!processorJob && processorResult) {
|
|
4285
4398
|
await db2.from(getTableName(this.id)).where({ id: results[0].id }).update({
|
|
4286
4399
|
...processorResult
|
|
4287
4400
|
});
|
|
@@ -4352,7 +4465,7 @@ var ExuluContext2 = class {
|
|
|
4352
4465
|
if (processorJob) {
|
|
4353
4466
|
jobs.push(processorJob);
|
|
4354
4467
|
}
|
|
4355
|
-
if (!processorJob) {
|
|
4468
|
+
if (!processorJob && processorResult) {
|
|
4356
4469
|
await db2.from(getTableName(this.id)).where({ id: record.id }).update({
|
|
4357
4470
|
...processorResult
|
|
4358
4471
|
});
|
|
@@ -4606,12 +4719,12 @@ var ExuluContext2 = class {
|
|
|
4606
4719
|
category: "contexts",
|
|
4607
4720
|
needsApproval: true,
|
|
4608
4721
|
// todo make configurable
|
|
4609
|
-
inputSchema:
|
|
4610
|
-
query:
|
|
4611
|
-
keywords:
|
|
4722
|
+
inputSchema: import_zod5.z.object({
|
|
4723
|
+
query: import_zod5.z.string().describe("The original question that the user asked"),
|
|
4724
|
+
keywords: import_zod5.z.array(import_zod5.z.string()).describe(
|
|
4612
4725
|
"The keywords that are relevant to the user's question, for example names of specific products, systems or parts, IDs, etc."
|
|
4613
4726
|
),
|
|
4614
|
-
method:
|
|
4727
|
+
method: import_zod5.z.enum(["keyword", "semantic", "hybrid"]).default("hybrid").describe(
|
|
4615
4728
|
"Search method: 'hybrid' (best for most queries - combines semantic understanding with exact term matching), 'keyword' (best for exact terms, technical names, IDs, or specific phrases), 'semantic' (best for conceptual queries where synonyms and paraphrasing matter)"
|
|
4616
4729
|
)
|
|
4617
4730
|
}),
|
|
@@ -4822,11 +4935,11 @@ function createCustomAgenticRetrievalToolLoopAgent({
|
|
|
4822
4935
|
return await (0, import_ai2.generateText)({
|
|
4823
4936
|
model,
|
|
4824
4937
|
output: import_ai2.Output.object({
|
|
4825
|
-
schema:
|
|
4826
|
-
reasoning:
|
|
4938
|
+
schema: import_zod6.z.object({
|
|
4939
|
+
reasoning: import_zod6.z.string().describe(
|
|
4827
4940
|
"The reasoning for the next step and why the agent needs to take this step. It MUST start with 'I must call tool XYZ', and MUST include the inputs for that tool."
|
|
4828
4941
|
),
|
|
4829
|
-
finished:
|
|
4942
|
+
finished: import_zod6.z.boolean().describe(
|
|
4830
4943
|
"Whether the agent has finished meaning no further steps are needed, this should only be true if the agent believes no further tool calls are needed to get the relevant information for the query."
|
|
4831
4944
|
)
|
|
4832
4945
|
})
|
|
@@ -4956,9 +5069,9 @@ function createCustomAgenticRetrievalToolLoopAgent({
|
|
|
4956
5069
|
if (chunksCount > 1) {
|
|
4957
5070
|
dynamicTools[getMoreToolName] = (0, import_ai2.tool)({
|
|
4958
5071
|
description: `The item ${chunk.item_name} has a total of${chunksCount} chunks, this tool allows you to get more content from this item across all its pages / chunks.`,
|
|
4959
|
-
inputSchema:
|
|
4960
|
-
from_index:
|
|
4961
|
-
to_index:
|
|
5072
|
+
inputSchema: import_zod6.z.object({
|
|
5073
|
+
from_index: import_zod6.z.number().default(1).describe("The index of the chunk to start from."),
|
|
5074
|
+
to_index: import_zod6.z.number().max(chunksCount).describe("The index of the chunk to end at, max is " + chunksCount)
|
|
4962
5075
|
}),
|
|
4963
5076
|
execute: async ({ from_index, to_index }) => {
|
|
4964
5077
|
const chunks2 = await db2(chunksTable).select("*").where("source", chunk.item_id).whereBetween("chunk_index", [from_index, to_index]).orderBy("chunk_index", "asc");
|
|
@@ -4986,8 +5099,8 @@ function createCustomAgenticRetrievalToolLoopAgent({
|
|
|
4986
5099
|
);
|
|
4987
5100
|
dynamicTools[getContentToolName] = (0, import_ai2.tool)({
|
|
4988
5101
|
description: `Get the content of the page ${chunk.chunk_index} for the item ${chunk.item_name}`,
|
|
4989
|
-
inputSchema:
|
|
4990
|
-
reasoning:
|
|
5102
|
+
inputSchema: import_zod6.z.object({
|
|
5103
|
+
reasoning: import_zod6.z.string().describe("The reasoning for why you need to get the content of the page.")
|
|
4991
5104
|
}),
|
|
4992
5105
|
execute: async ({ reasoning }) => {
|
|
4993
5106
|
const { db: db3 } = await postgresClient();
|
|
@@ -5055,8 +5168,8 @@ var createAgenticRetrievalAgent = ({
|
|
|
5055
5168
|
search_items_by_name: (0, import_ai2.tool)({
|
|
5056
5169
|
description: `
|
|
5057
5170
|
Search for relevant items by name across the available knowledge bases.`,
|
|
5058
|
-
inputSchema:
|
|
5059
|
-
knowledge_base_ids:
|
|
5171
|
+
inputSchema: import_zod6.z.object({
|
|
5172
|
+
knowledge_base_ids: import_zod6.z.array(import_zod6.z.enum(contexts.map((ctx) => ctx.id))).describe(`
|
|
5060
5173
|
The available knowledge bases are:
|
|
5061
5174
|
${contexts.map(
|
|
5062
5175
|
(ctx) => `
|
|
@@ -5068,8 +5181,8 @@ var createAgenticRetrievalAgent = ({
|
|
|
5068
5181
|
`
|
|
5069
5182
|
).join("\n")}
|
|
5070
5183
|
`),
|
|
5071
|
-
item_name:
|
|
5072
|
-
limit:
|
|
5184
|
+
item_name: import_zod6.z.string().describe("The name of the item to search for."),
|
|
5185
|
+
limit: import_zod6.z.number().default(100).describe(
|
|
5073
5186
|
"Maximum number of items to return (max 400), if searching through multiple knowledge bases, the limit is applied for each knowledge base individually."
|
|
5074
5187
|
)
|
|
5075
5188
|
}),
|
|
@@ -5175,11 +5288,11 @@ var createAgenticRetrievalAgent = ({
|
|
|
5175
5288
|
- You can always fetch content later if needed
|
|
5176
5289
|
|
|
5177
5290
|
`,
|
|
5178
|
-
inputSchema:
|
|
5179
|
-
query:
|
|
5291
|
+
inputSchema: import_zod6.z.object({
|
|
5292
|
+
query: import_zod6.z.string().describe(
|
|
5180
5293
|
"The search query to find relevant chunks, this must always be related to the content you are looking for, not something like 'Page 2'."
|
|
5181
5294
|
),
|
|
5182
|
-
knowledge_base_ids:
|
|
5295
|
+
knowledge_base_ids: import_zod6.z.array(import_zod6.z.enum(contexts.map((ctx) => ctx.id))).describe(`
|
|
5183
5296
|
The available knowledge bases are:
|
|
5184
5297
|
${contexts.map(
|
|
5185
5298
|
(ctx) => `
|
|
@@ -5191,25 +5304,25 @@ var createAgenticRetrievalAgent = ({
|
|
|
5191
5304
|
`
|
|
5192
5305
|
).join("\n")}
|
|
5193
5306
|
`),
|
|
5194
|
-
keywords:
|
|
5307
|
+
keywords: import_zod6.z.array(import_zod6.z.string()).optional().describe(
|
|
5195
5308
|
"Keywords to search for. Usually extracted from the query, allowing for more precise search results."
|
|
5196
5309
|
),
|
|
5197
|
-
searchMethod:
|
|
5310
|
+
searchMethod: import_zod6.z.enum(["keyword", "semantic", "hybrid"]).default("hybrid").describe(
|
|
5198
5311
|
"Search method: 'hybrid' (best for most queries - combines semantic understanding with exact term matching), 'keyword' (best for exact terms, technical names, IDs, or specific phrases), 'semantic' (best for conceptual queries where synonyms and paraphrasing matter)"
|
|
5199
5312
|
),
|
|
5200
|
-
includeContent:
|
|
5313
|
+
includeContent: import_zod6.z.boolean().default(true).describe(
|
|
5201
5314
|
"Whether to include the full chunk content in results. Set to FALSE when you only need to know WHICH documents/items are relevant (lists, overviews, counts). Set to TRUE when you need the ACTUAL content to answer the question (information, details, explanations). You can always fetch content later, so prefer FALSE for efficiency when listing documents."
|
|
5202
5315
|
),
|
|
5203
|
-
item_ids:
|
|
5316
|
+
item_ids: import_zod6.z.array(import_zod6.z.string()).optional().describe(
|
|
5204
5317
|
"Use if you wish to retrieve content from specific items (documents) based on the item ID."
|
|
5205
5318
|
),
|
|
5206
|
-
item_names:
|
|
5319
|
+
item_names: import_zod6.z.array(import_zod6.z.string()).optional().describe(
|
|
5207
5320
|
"Use if you wish to retrieve content from specific items (documents) based on the item name. Can be a partial match."
|
|
5208
5321
|
),
|
|
5209
|
-
item_external_ids:
|
|
5322
|
+
item_external_ids: import_zod6.z.array(import_zod6.z.string()).optional().describe(
|
|
5210
5323
|
"Use if you wish to retrieve content from specific items (documents) based on the item external ID. Can be a partial match."
|
|
5211
5324
|
),
|
|
5212
|
-
limit:
|
|
5325
|
+
limit: import_zod6.z.number().default(10).describe("Maximum number of chunks to return (max 10)")
|
|
5213
5326
|
}),
|
|
5214
5327
|
execute: async ({
|
|
5215
5328
|
query,
|
|
@@ -5400,9 +5513,9 @@ var createAgenticRetrievalTool = ({
|
|
|
5400
5513
|
default: true
|
|
5401
5514
|
}))
|
|
5402
5515
|
],
|
|
5403
|
-
inputSchema:
|
|
5404
|
-
query:
|
|
5405
|
-
userInstructions:
|
|
5516
|
+
inputSchema: import_zod6.z.object({
|
|
5517
|
+
query: import_zod6.z.string().describe("The question or query to answer using the knowledge bases"),
|
|
5518
|
+
userInstructions: import_zod6.z.string().optional().describe("Instructions provided by the user to customize the retrieval process.")
|
|
5406
5519
|
}),
|
|
5407
5520
|
execute: async function* ({
|
|
5408
5521
|
query,
|
|
@@ -7420,7 +7533,32 @@ var createWorkers = async (providers, queues2, config, contexts, rerankers, eval
|
|
|
7420
7533
|
);
|
|
7421
7534
|
}
|
|
7422
7535
|
const exuluStorage = new ExuluStorage({ config });
|
|
7423
|
-
|
|
7536
|
+
if (context.processor.filter) {
|
|
7537
|
+
const result2 = await context.processor.filter({
|
|
7538
|
+
item: data.inputs,
|
|
7539
|
+
user: data.user,
|
|
7540
|
+
role: data.role,
|
|
7541
|
+
utils: {
|
|
7542
|
+
storage: exuluStorage
|
|
7543
|
+
},
|
|
7544
|
+
exuluConfig: config
|
|
7545
|
+
});
|
|
7546
|
+
if (!result2) {
|
|
7547
|
+
console.log("[EXULU] Item filtered out by processor, skipping processing execution...");
|
|
7548
|
+
return {
|
|
7549
|
+
result: "Item filtered out by processor, skipping processing execution...",
|
|
7550
|
+
// last message
|
|
7551
|
+
metadata: {
|
|
7552
|
+
item: {
|
|
7553
|
+
name: data.inputs?.name,
|
|
7554
|
+
id: data.inputs?.id,
|
|
7555
|
+
external_id: data.inputs?.external_id
|
|
7556
|
+
}
|
|
7557
|
+
}
|
|
7558
|
+
};
|
|
7559
|
+
}
|
|
7560
|
+
}
|
|
7561
|
+
console.log("[EXULU] POS 2 -- EXULU CONTEXT PROCESS FIELD", data.inputs);
|
|
7424
7562
|
let processorResult = await context.processor.execute({
|
|
7425
7563
|
item: data.inputs,
|
|
7426
7564
|
user: data.user,
|
|
@@ -9870,7 +10008,7 @@ var import_utils5 = require("@apollo/utils.keyvaluecache");
|
|
|
9870
10008
|
var import_body_parser = __toESM(require("body-parser"), 1);
|
|
9871
10009
|
var import_crypto_js6 = __toESM(require("crypto-js"), 1);
|
|
9872
10010
|
var import_openai = __toESM(require("openai"), 1);
|
|
9873
|
-
var
|
|
10011
|
+
var import_fs2 = __toESM(require("fs"), 1);
|
|
9874
10012
|
var import_node_crypto4 = require("crypto");
|
|
9875
10013
|
var import_api2 = require("@opentelemetry/api");
|
|
9876
10014
|
var import_sdk = __toESM(require("@anthropic-ai/sdk"), 1);
|
|
@@ -9919,98 +10057,6 @@ function generateSlug(name) {
|
|
|
9919
10057
|
// src/exulu/provider.ts
|
|
9920
10058
|
var import_crypto_js5 = __toESM(require("crypto-js"), 1);
|
|
9921
10059
|
var import_officeparser = require("officeparser");
|
|
9922
|
-
|
|
9923
|
-
// src/templates/tools/memory-tool.ts
|
|
9924
|
-
var import_zod6 = require("zod");
|
|
9925
|
-
var createNewMemoryItemTool = (agent, context) => {
|
|
9926
|
-
const fields = {
|
|
9927
|
-
name: import_zod6.z.string().describe("The name of the item to create"),
|
|
9928
|
-
description: import_zod6.z.string().describe("The description of the item to create")
|
|
9929
|
-
};
|
|
9930
|
-
for (const field of context.fields) {
|
|
9931
|
-
switch (field.type) {
|
|
9932
|
-
case "text":
|
|
9933
|
-
case "longText":
|
|
9934
|
-
case "shortText":
|
|
9935
|
-
case "code":
|
|
9936
|
-
case "enum":
|
|
9937
|
-
fields[field.name] = import_zod6.z.string().describe("The " + field.name + " of the item to create");
|
|
9938
|
-
break;
|
|
9939
|
-
case "json":
|
|
9940
|
-
fields[field.name] = import_zod6.z.string({}).describe(
|
|
9941
|
-
"The " + field.name + " of the item to create, it should be a valid JSON string."
|
|
9942
|
-
);
|
|
9943
|
-
break;
|
|
9944
|
-
case "markdown":
|
|
9945
|
-
fields[field.name] = import_zod6.z.string().describe(
|
|
9946
|
-
"The " + field.name + " of the item to create, it should be a valid Markdown string."
|
|
9947
|
-
);
|
|
9948
|
-
break;
|
|
9949
|
-
case "number":
|
|
9950
|
-
fields[field.name] = import_zod6.z.number().describe("The " + field.name + " of the item to create");
|
|
9951
|
-
break;
|
|
9952
|
-
case "boolean":
|
|
9953
|
-
fields[field.name] = import_zod6.z.boolean().describe("The " + field.name + " of the item to create");
|
|
9954
|
-
break;
|
|
9955
|
-
case "file":
|
|
9956
|
-
case "uuid":
|
|
9957
|
-
case "date":
|
|
9958
|
-
break;
|
|
9959
|
-
default:
|
|
9960
|
-
fields[field.name] = import_zod6.z.string().describe("The " + field.name + " of the item to create");
|
|
9961
|
-
break;
|
|
9962
|
-
}
|
|
9963
|
-
}
|
|
9964
|
-
return new ExuluTool({
|
|
9965
|
-
id: "create_" + agent.name + "_memory_item",
|
|
9966
|
-
name: "Create " + agent.name + " Memory Item",
|
|
9967
|
-
category: agent.name + "_memory",
|
|
9968
|
-
description: "Create a new memory item in the " + agent.name + " memory context",
|
|
9969
|
-
type: "function",
|
|
9970
|
-
inputSchema: import_zod6.z.object(fields),
|
|
9971
|
-
config: [],
|
|
9972
|
-
execute: async ({ name, description, mode, information, exuluConfig, user }) => {
|
|
9973
|
-
let result = { result: "" };
|
|
9974
|
-
switch (mode) {
|
|
9975
|
-
case "learnings":
|
|
9976
|
-
break;
|
|
9977
|
-
case "knowledge":
|
|
9978
|
-
const newItem = {
|
|
9979
|
-
name,
|
|
9980
|
-
description,
|
|
9981
|
-
information,
|
|
9982
|
-
rights_mode: "public"
|
|
9983
|
-
};
|
|
9984
|
-
const { item: createdItem, job: createdJob } = await context.createItem(
|
|
9985
|
-
newItem,
|
|
9986
|
-
exuluConfig,
|
|
9987
|
-
user?.id,
|
|
9988
|
-
user?.role?.id,
|
|
9989
|
-
false
|
|
9990
|
-
);
|
|
9991
|
-
if (createdJob) {
|
|
9992
|
-
result = {
|
|
9993
|
-
result: `Created a Job to create the memory item with the following ID: ${createdJob}`
|
|
9994
|
-
};
|
|
9995
|
-
} else if (createdItem) {
|
|
9996
|
-
result = {
|
|
9997
|
-
result: `Created memory item with the following ID: ${createdItem.id}`
|
|
9998
|
-
};
|
|
9999
|
-
} else {
|
|
10000
|
-
result = {
|
|
10001
|
-
result: `Failed to create memory item`
|
|
10002
|
-
};
|
|
10003
|
-
}
|
|
10004
|
-
break;
|
|
10005
|
-
default:
|
|
10006
|
-
throw new Error(`Invalid mode: ${mode}`);
|
|
10007
|
-
}
|
|
10008
|
-
return result;
|
|
10009
|
-
}
|
|
10010
|
-
});
|
|
10011
|
-
};
|
|
10012
|
-
|
|
10013
|
-
// src/exulu/provider.ts
|
|
10014
10060
|
var ExuluProvider = class {
|
|
10015
10061
|
// Must begin with a letter (a-z) or underscore (_). Subsequent characters in a name can be letters, digits (0-9), or
|
|
10016
10062
|
// underscores and be a max length of 80 characters and at least 5 characters long.
|
|
@@ -10270,13 +10316,6 @@ var ExuluProvider = class {
|
|
|
10270
10316
|
|
|
10271
10317
|
${result.chunks.map((chunk) => chunk.chunk_content).join("\n\n")}`;
|
|
10272
10318
|
}
|
|
10273
|
-
const createNewMemoryTool = createNewMemoryItemTool(agent, context);
|
|
10274
|
-
if (createNewMemoryTool) {
|
|
10275
|
-
if (!currentTools) {
|
|
10276
|
-
currentTools = [];
|
|
10277
|
-
}
|
|
10278
|
-
currentTools.push(createNewMemoryTool);
|
|
10279
|
-
}
|
|
10280
10319
|
}
|
|
10281
10320
|
const personalizationInformation = exuluConfig?.privacy?.systemPromptPersonalization !== false ? `
|
|
10282
10321
|
${user?.firstname ? `The users first name is "${user.firstname}"` : ""}
|
|
@@ -10340,6 +10379,10 @@ var ExuluProvider = class {
|
|
|
10340
10379
|
Example: {url: https://www.google.com, title: Google, snippet: The result of the web search.}
|
|
10341
10380
|
`;
|
|
10342
10381
|
}
|
|
10382
|
+
system += `
|
|
10383
|
+
|
|
10384
|
+
When a tool execution is not approved by the user, do not retry it unless explicitly asked by the user. ' +
|
|
10385
|
+
'Inform the user that the action was not performed.`;
|
|
10343
10386
|
if (prompt) {
|
|
10344
10387
|
let result = { object: null, text: "" };
|
|
10345
10388
|
let inputTokens = 0;
|
|
@@ -10654,13 +10697,6 @@ ${extractedText}
|
|
|
10654
10697
|
|
|
10655
10698
|
${result2.chunks.map((chunk) => chunk.chunk_content).join("\n\n")}`;
|
|
10656
10699
|
}
|
|
10657
|
-
const createNewMemoryTool = createNewMemoryItemTool(agent, context);
|
|
10658
|
-
if (createNewMemoryTool) {
|
|
10659
|
-
if (!currentTools) {
|
|
10660
|
-
currentTools = [];
|
|
10661
|
-
}
|
|
10662
|
-
currentTools.push(createNewMemoryTool);
|
|
10663
|
-
}
|
|
10664
10700
|
}
|
|
10665
10701
|
messages = messages.filter(
|
|
10666
10702
|
(message2, index, self) => index === self.findLastIndex((t) => t.id === message2.id)
|
|
@@ -10719,6 +10755,10 @@ ${extractedText}
|
|
|
10719
10755
|
Example: {url: https://www.google.com, title: Google, snippet: The result of the web search.}
|
|
10720
10756
|
`;
|
|
10721
10757
|
}
|
|
10758
|
+
system += `
|
|
10759
|
+
|
|
10760
|
+
When a tool execution is not approved by the user, do not retry it unless explicitly asked by the user. ' +
|
|
10761
|
+
'Inform the user that the action was not performed.`;
|
|
10722
10762
|
const result = (0, import_ai4.streamText)({
|
|
10723
10763
|
model,
|
|
10724
10764
|
// Should be a LanguageModelV1
|
|
@@ -10863,12 +10903,11 @@ var providerRateLimiter = async (key, windowSeconds, limit, points) => {
|
|
|
10863
10903
|
|
|
10864
10904
|
// src/exulu/routes.ts
|
|
10865
10905
|
var import_zod_from_json_schema = require("zod-from-json-schema");
|
|
10866
|
-
var import_zod8 = require("zod");
|
|
10867
10906
|
var REQUEST_SIZE_LIMIT = "50mb";
|
|
10868
10907
|
var getExuluVersionNumber = async () => {
|
|
10869
10908
|
try {
|
|
10870
10909
|
const path2 = process.cwd();
|
|
10871
|
-
const packageJson =
|
|
10910
|
+
const packageJson = import_fs2.default.readFileSync(path2 + "/package.json", "utf8");
|
|
10872
10911
|
const packageData = JSON.parse(packageJson);
|
|
10873
10912
|
const exuluVersion = packageData.dependencies["@exulu/backend"];
|
|
10874
10913
|
console.log(`[EXULU] Installed exulu-backend version: ${exuluVersion}`);
|
|
@@ -11778,7 +11817,7 @@ var import_types2 = require("@modelcontextprotocol/sdk/types.js");
|
|
|
11778
11817
|
var import_express4 = require("express");
|
|
11779
11818
|
var import_api3 = require("@opentelemetry/api");
|
|
11780
11819
|
var import_crypto_js7 = __toESM(require("crypto-js"), 1);
|
|
11781
|
-
var
|
|
11820
|
+
var import_zod8 = require("zod");
|
|
11782
11821
|
var SESSION_ID_HEADER = "mcp-session-id";
|
|
11783
11822
|
var ExuluMCP = class {
|
|
11784
11823
|
server = {};
|
|
@@ -11861,7 +11900,7 @@ var ExuluMCP = class {
|
|
|
11861
11900
|
title: tool3.name + " agent",
|
|
11862
11901
|
description: tool3.description,
|
|
11863
11902
|
inputSchema: {
|
|
11864
|
-
inputs: tool3.inputSchema ||
|
|
11903
|
+
inputs: tool3.inputSchema || import_zod8.z.object({})
|
|
11865
11904
|
}
|
|
11866
11905
|
},
|
|
11867
11906
|
async ({ inputs }, args) => {
|
|
@@ -11913,7 +11952,7 @@ var ExuluMCP = class {
|
|
|
11913
11952
|
title: "Get List of Prompt Templates",
|
|
11914
11953
|
description: "Retrieves a list of prompt templates available for this agent. Returns the name, description, and ID of each template.",
|
|
11915
11954
|
inputSchema: {
|
|
11916
|
-
inputs:
|
|
11955
|
+
inputs: import_zod8.z.object({})
|
|
11917
11956
|
}
|
|
11918
11957
|
},
|
|
11919
11958
|
async ({ inputs }, args) => {
|
|
@@ -11959,8 +11998,8 @@ var ExuluMCP = class {
|
|
|
11959
11998
|
title: "Get Prompt Template Details",
|
|
11960
11999
|
description: "Retrieves the full details of a specific prompt template by ID, including the actual template content with variables.",
|
|
11961
12000
|
inputSchema: {
|
|
11962
|
-
inputs:
|
|
11963
|
-
id:
|
|
12001
|
+
inputs: import_zod8.z.object({
|
|
12002
|
+
id: import_zod8.z.string().describe("The ID of the prompt template to retrieve")
|
|
11964
12003
|
})
|
|
11965
12004
|
}
|
|
11966
12005
|
},
|
|
@@ -12868,7 +12907,7 @@ var ExuluEval = class {
|
|
|
12868
12907
|
};
|
|
12869
12908
|
|
|
12870
12909
|
// src/templates/evals/index.ts
|
|
12871
|
-
var
|
|
12910
|
+
var import_zod9 = require("zod");
|
|
12872
12911
|
var llmAsJudgeEval = () => {
|
|
12873
12912
|
if (process.env.REDIS_HOST?.length && process.env.REDIS_PORT?.length) {
|
|
12874
12913
|
return new ExuluEval({
|
|
@@ -12913,8 +12952,8 @@ var llmAsJudgeEval = () => {
|
|
|
12913
12952
|
contexts: [],
|
|
12914
12953
|
rerankers: [],
|
|
12915
12954
|
prompt,
|
|
12916
|
-
outputSchema:
|
|
12917
|
-
score:
|
|
12955
|
+
outputSchema: import_zod9.z.object({
|
|
12956
|
+
score: import_zod9.z.number().min(0).max(100).describe("The score between 0 and 100.")
|
|
12918
12957
|
}),
|
|
12919
12958
|
providerapikey
|
|
12920
12959
|
});
|
|
@@ -13142,12 +13181,12 @@ Usage:
|
|
|
13142
13181
|
- If no todos exist yet, an empty list will be returned`;
|
|
13143
13182
|
|
|
13144
13183
|
// src/templates/tools/todo/todo.ts
|
|
13145
|
-
var
|
|
13146
|
-
var TodoSchema =
|
|
13147
|
-
content:
|
|
13148
|
-
status:
|
|
13149
|
-
priority:
|
|
13150
|
-
id:
|
|
13184
|
+
var import_zod10 = __toESM(require("zod"), 1);
|
|
13185
|
+
var TodoSchema = import_zod10.default.object({
|
|
13186
|
+
content: import_zod10.default.string().describe("Brief description of the task"),
|
|
13187
|
+
status: import_zod10.default.string().describe("Current status of the task: pending, in_progress, completed, cancelled"),
|
|
13188
|
+
priority: import_zod10.default.string().describe("Priority level of the task: high, medium, low"),
|
|
13189
|
+
id: import_zod10.default.string().describe("Unique identifier for the todo item")
|
|
13151
13190
|
});
|
|
13152
13191
|
var TodoWriteTool = new ExuluTool({
|
|
13153
13192
|
id: "todo_write",
|
|
@@ -13163,8 +13202,8 @@ var TodoWriteTool = new ExuluTool({
|
|
|
13163
13202
|
default: todowrite_default
|
|
13164
13203
|
}
|
|
13165
13204
|
],
|
|
13166
|
-
inputSchema:
|
|
13167
|
-
todos:
|
|
13205
|
+
inputSchema: import_zod10.default.object({
|
|
13206
|
+
todos: import_zod10.default.array(TodoSchema).describe("The updated todo list")
|
|
13168
13207
|
}),
|
|
13169
13208
|
execute: async (inputs) => {
|
|
13170
13209
|
const { sessionID, todos, user } = inputs;
|
|
@@ -13199,7 +13238,7 @@ var TodoReadTool = new ExuluTool({
|
|
|
13199
13238
|
id: "todo_read",
|
|
13200
13239
|
name: "Todo Read",
|
|
13201
13240
|
description: "Use this tool to read your todo list",
|
|
13202
|
-
inputSchema:
|
|
13241
|
+
inputSchema: import_zod10.default.object({}),
|
|
13203
13242
|
type: "function",
|
|
13204
13243
|
category: "todo",
|
|
13205
13244
|
config: [
|
|
@@ -13237,6 +13276,222 @@ async function getTodos(sessionID) {
|
|
|
13237
13276
|
}
|
|
13238
13277
|
var todoTools = [TodoWriteTool, TodoReadTool];
|
|
13239
13278
|
|
|
13279
|
+
// src/templates/tools/question/questionask.txt
|
|
13280
|
+
var questionask_default = `Use this tool to ask the user a question with multiple choice answer options during your session. This helps you gather user input, clarify requirements, and make informed decisions based on user preferences.
|
|
13281
|
+
|
|
13282
|
+
## When to Use This Tool
|
|
13283
|
+
|
|
13284
|
+
Use this tool when you need to:
|
|
13285
|
+
|
|
13286
|
+
1. Get user input on implementation choices - When there are multiple valid approaches and you need the user to decide
|
|
13287
|
+
2. Clarify ambiguous requirements - When the user's request could be interpreted in different ways
|
|
13288
|
+
3. Gather preferences - When you need to know the user's preference for styling, naming, architecture, etc.
|
|
13289
|
+
4. Validate assumptions - When you want to confirm your understanding before proceeding
|
|
13290
|
+
5. Offer options - When presenting multiple solutions and letting the user choose
|
|
13291
|
+
|
|
13292
|
+
## When NOT to Use This Tool
|
|
13293
|
+
|
|
13294
|
+
Skip using this tool when:
|
|
13295
|
+
1. The answer is clear from context or previous messages
|
|
13296
|
+
2. You're asking a yes/no question (use a simpler approach)
|
|
13297
|
+
3. The question requires a free-form text answer (this tool is for multiple choice only)
|
|
13298
|
+
4. You're asking for information that should be obvious from the codebase
|
|
13299
|
+
|
|
13300
|
+
## How It Works
|
|
13301
|
+
|
|
13302
|
+
- Provide a question and an array of answer option strings
|
|
13303
|
+
- The tool automatically adds "None of the above..." as a final option
|
|
13304
|
+
- Each answer option is assigned a unique ID
|
|
13305
|
+
- The question is stored with a "pending" status until the user answers
|
|
13306
|
+
- Use the Question Read tool to check if the question has been answered
|
|
13307
|
+
|
|
13308
|
+
## Examples
|
|
13309
|
+
|
|
13310
|
+
<example>
|
|
13311
|
+
User: Add authentication to my app
|
|
13312
|
+
Assistant: I need to ask the user which authentication method they prefer.
|
|
13313
|
+
*Uses Question Ask tool with:*
|
|
13314
|
+
question: "Which authentication method would you like to implement?"
|
|
13315
|
+
answerOptions: ["JWT tokens", "OAuth 2.0", "Session-based auth", "Firebase Auth"]
|
|
13316
|
+
|
|
13317
|
+
<reasoning>
|
|
13318
|
+
The assistant used the question tool because there are multiple valid authentication approaches and the user's preference will significantly impact the implementation.
|
|
13319
|
+
</reasoning>
|
|
13320
|
+
</example>
|
|
13321
|
+
|
|
13322
|
+
<example>
|
|
13323
|
+
User: Style this button
|
|
13324
|
+
Assistant: I need to know the user's styling preference.
|
|
13325
|
+
*Uses Question Ask tool with:*
|
|
13326
|
+
question: "What styling approach should I use for the button?"
|
|
13327
|
+
answerOptions: ["Tailwind CSS", "CSS Modules", "Styled Components", "Plain CSS"]
|
|
13328
|
+
|
|
13329
|
+
<reasoning>
|
|
13330
|
+
The assistant used the question tool because the codebase could support multiple styling approaches and the user's preference matters for consistency.
|
|
13331
|
+
</reasoning>
|
|
13332
|
+
</example>
|
|
13333
|
+
|
|
13334
|
+
<example>
|
|
13335
|
+
User: Add error handling
|
|
13336
|
+
Assistant: I'll implement comprehensive error handling with try-catch blocks and proper error messages.
|
|
13337
|
+
*Does not use the question tool*
|
|
13338
|
+
|
|
13339
|
+
<reasoning>
|
|
13340
|
+
The assistant did not use the question tool because error handling is a standard practice and there's a clear best approach that doesn't require user input.
|
|
13341
|
+
</reasoning>
|
|
13342
|
+
</example>
|
|
13343
|
+
|
|
13344
|
+
## Answer Format
|
|
13345
|
+
|
|
13346
|
+
When you call this tool, answer options are automatically converted to this format:
|
|
13347
|
+
\`\`\`json
|
|
13348
|
+
[
|
|
13349
|
+
{ "id": "randomId1", "text": "First option" },
|
|
13350
|
+
{ "id": "randomId2", "text": "Second option" },
|
|
13351
|
+
{ "id": "randomId3", "text": "Third option" },
|
|
13352
|
+
{ "id": "randomId4", "text": "None of the above..." }
|
|
13353
|
+
]
|
|
13354
|
+
\`\`\`
|
|
13355
|
+
|
|
13356
|
+
The "None of the above..." option is always added automatically, so you don't need to include it in your answerOptions array.
|
|
13357
|
+
|
|
13358
|
+
## Reading Answers
|
|
13359
|
+
|
|
13360
|
+
After asking a question, use the Question Read tool to check if the user has answered. The question status will change from "pending" to "answered" and the selectedAnswerId field will contain the ID of the chosen answer.
|
|
13361
|
+
`;
|
|
13362
|
+
|
|
13363
|
+
// src/templates/tools/question/questionread.txt
|
|
13364
|
+
var questionread_default = 'Use this tool to read questions you\'ve asked and check if they\'ve been answered by the user. This tool helps you track the status of questions and retrieve the user\'s selected answers.\n\n## When to Use This Tool\n\nUse this tool proactively in these situations:\n- After asking a question to check if the user has responded\n- To retrieve the user\'s answer before proceeding with implementation\n- To review all questions and answers in the current session\n- When you need to reference a previous answer\n\n## How It Works\n\n- This tool takes no parameters (leave the input blank or empty)\n- Returns an array of all questions in the session\n- Each question includes:\n - `id`: Unique identifier for the question\n - `question`: The question text\n - `answerOptions`: Array of answer options with their IDs and text\n - `status`: Either "pending" (not answered) or "answered"\n - `selectedAnswerId`: The ID of the chosen answer (only present if answered)\n\n## Usage Pattern\n\nTypically you\'ll:\n1. Use Question Ask to pose a question\n2. Wait for the user to respond\n3. Use Question Read to check the answer\n4. Find the selected answer by matching the `selectedAnswerId` with an option in `answerOptions`\n5. Proceed with implementation based on the user\'s choice\n\n## Example Response\n\n```json\n[\n {\n "id": "question123",\n "question": "Which authentication method would you like to implement?",\n "answerOptions": [\n { "id": "ans1", "text": "JWT tokens" },\n { "id": "ans2", "text": "OAuth 2.0" },\n { "id": "ans3", "text": "Session-based auth" },\n { "id": "ans4", "text": "None of the above..." }\n ],\n "status": "answered",\n "selectedAnswerId": "ans1"\n }\n]\n```\n\nIn this example, the user selected "JWT tokens" (id: ans1).\n\n## Important Notes\n\n- If no questions exist in the session, an empty array will be returned\n- Questions remain in the session even after being answered for reference\n- Use the `selectedAnswerId` to find which answer option the user chose by matching it against the `id` field in `answerOptions`\n';
|
|
13365
|
+
|
|
13366
|
+
// src/templates/tools/question/question.ts
|
|
13367
|
+
var import_zod11 = __toESM(require("zod"), 1);
|
|
13368
|
+
var import_node_crypto6 = require("crypto");
|
|
13369
|
+
var AnswerOptionSchema = import_zod11.default.object({
|
|
13370
|
+
id: import_zod11.default.string().describe("Unique identifier for the answer option"),
|
|
13371
|
+
text: import_zod11.default.string().describe("The text of the answer option")
|
|
13372
|
+
});
|
|
13373
|
+
var _QuestionSchema = import_zod11.default.object({
|
|
13374
|
+
id: import_zod11.default.string().describe("Unique identifier for the question"),
|
|
13375
|
+
question: import_zod11.default.string().describe("The question to ask the user"),
|
|
13376
|
+
answerOptions: import_zod11.default.array(AnswerOptionSchema).describe("Array of possible answer options"),
|
|
13377
|
+
selectedAnswerId: import_zod11.default.string().optional().describe("The ID of the answer option selected by the user"),
|
|
13378
|
+
status: import_zod11.default.enum(["pending", "answered"]).describe("Status of the question: pending or answered")
|
|
13379
|
+
});
|
|
13380
|
+
var QuestionAskTool = new ExuluTool({
|
|
13381
|
+
id: "question_ask",
|
|
13382
|
+
name: "Question Ask",
|
|
13383
|
+
description: "Use this tool to ask a question to the user with multiple choice answers",
|
|
13384
|
+
type: "function",
|
|
13385
|
+
category: "question",
|
|
13386
|
+
config: [
|
|
13387
|
+
{
|
|
13388
|
+
name: "description",
|
|
13389
|
+
description: "The description of the question tool, if set overwrites the default description.",
|
|
13390
|
+
type: "string",
|
|
13391
|
+
default: questionask_default
|
|
13392
|
+
}
|
|
13393
|
+
],
|
|
13394
|
+
inputSchema: import_zod11.default.object({
|
|
13395
|
+
question: import_zod11.default.string().describe("The question to ask the user"),
|
|
13396
|
+
answerOptions: import_zod11.default.array(import_zod11.default.string()).describe("Array of possible answer options (strings)")
|
|
13397
|
+
}),
|
|
13398
|
+
execute: async (inputs) => {
|
|
13399
|
+
const { sessionID, question, answerOptions, user } = inputs;
|
|
13400
|
+
if (!user) {
|
|
13401
|
+
throw new Error(
|
|
13402
|
+
"No authenticated user available, a user is required for the question ask tool, this likely means the tool was called outside a session like in an MCP or API call instead of as part of an authenticated session."
|
|
13403
|
+
);
|
|
13404
|
+
}
|
|
13405
|
+
if (!sessionID) {
|
|
13406
|
+
throw new Error(
|
|
13407
|
+
"Session ID is required for the question ask tool, this likely means the tool was called outside a session like in an MCP or API call instead of as part of a conversation."
|
|
13408
|
+
);
|
|
13409
|
+
}
|
|
13410
|
+
const session = await getSession({ sessionID });
|
|
13411
|
+
if (!session?.id) {
|
|
13412
|
+
throw new Error(
|
|
13413
|
+
"Session with ID " + sessionID + " not found in the question ask tool."
|
|
13414
|
+
);
|
|
13415
|
+
}
|
|
13416
|
+
const hasAccessToSession = await checkRecordAccess(session, "read", user);
|
|
13417
|
+
if (!hasAccessToSession) {
|
|
13418
|
+
throw new Error("You don't have access to this session " + session.id + ".");
|
|
13419
|
+
}
|
|
13420
|
+
const answerOptionsWithIds = answerOptions.map((text) => ({
|
|
13421
|
+
id: (0, import_node_crypto6.randomUUID)(),
|
|
13422
|
+
text
|
|
13423
|
+
}));
|
|
13424
|
+
answerOptionsWithIds.push({
|
|
13425
|
+
id: (0, import_node_crypto6.randomUUID)(),
|
|
13426
|
+
text: "None of the above..."
|
|
13427
|
+
});
|
|
13428
|
+
const newQuestion = {
|
|
13429
|
+
id: (0, import_node_crypto6.randomUUID)(),
|
|
13430
|
+
question,
|
|
13431
|
+
answerOptions: answerOptionsWithIds,
|
|
13432
|
+
status: "pending"
|
|
13433
|
+
};
|
|
13434
|
+
await addQuestion({
|
|
13435
|
+
session,
|
|
13436
|
+
question: newQuestion
|
|
13437
|
+
});
|
|
13438
|
+
return {
|
|
13439
|
+
result: JSON.stringify(
|
|
13440
|
+
{
|
|
13441
|
+
questionId: newQuestion.id,
|
|
13442
|
+
question: newQuestion.question,
|
|
13443
|
+
answerOptions: newQuestion.answerOptions,
|
|
13444
|
+
status: newQuestion.status
|
|
13445
|
+
},
|
|
13446
|
+
null,
|
|
13447
|
+
2
|
|
13448
|
+
)
|
|
13449
|
+
};
|
|
13450
|
+
}
|
|
13451
|
+
});
|
|
13452
|
+
var QuestionReadTool = new ExuluTool({
|
|
13453
|
+
id: "question_read",
|
|
13454
|
+
name: "Question Read",
|
|
13455
|
+
description: "Use this tool to read questions and their answers",
|
|
13456
|
+
inputSchema: import_zod11.default.object({}),
|
|
13457
|
+
type: "function",
|
|
13458
|
+
category: "question",
|
|
13459
|
+
config: [
|
|
13460
|
+
{
|
|
13461
|
+
name: "description",
|
|
13462
|
+
description: "The description of the question read tool, if set overwrites the default description.",
|
|
13463
|
+
type: "string",
|
|
13464
|
+
default: questionread_default
|
|
13465
|
+
}
|
|
13466
|
+
],
|
|
13467
|
+
execute: async (inputs) => {
|
|
13468
|
+
const { sessionID } = inputs;
|
|
13469
|
+
const questions = await getQuestions(sessionID);
|
|
13470
|
+
return {
|
|
13471
|
+
result: JSON.stringify(questions, null, 2)
|
|
13472
|
+
};
|
|
13473
|
+
}
|
|
13474
|
+
});
|
|
13475
|
+
async function addQuestion(input) {
|
|
13476
|
+
const metadata = input.session.metadata ?? {};
|
|
13477
|
+
metadata["questions"] ??= [];
|
|
13478
|
+
metadata["questions"].push(input.question);
|
|
13479
|
+
const { db: db2 } = await postgresClient();
|
|
13480
|
+
await db2.from("agent_sessions").where({ id: input.session.id }).update({
|
|
13481
|
+
metadata
|
|
13482
|
+
});
|
|
13483
|
+
return input.session;
|
|
13484
|
+
}
|
|
13485
|
+
async function getQuestions(sessionID) {
|
|
13486
|
+
const { db: db2 } = await postgresClient();
|
|
13487
|
+
const session = await db2.from("agent_sessions").where({ id: sessionID }).first();
|
|
13488
|
+
if (!session) {
|
|
13489
|
+
throw new Error("Session not found for session ID: " + sessionID);
|
|
13490
|
+
}
|
|
13491
|
+
return session.metadata?.questions ?? [];
|
|
13492
|
+
}
|
|
13493
|
+
var questionTools = [QuestionAskTool, QuestionReadTool];
|
|
13494
|
+
|
|
13240
13495
|
// src/templates/tools/perplexity.ts
|
|
13241
13496
|
var import_zod12 = __toESM(require("zod"), 1);
|
|
13242
13497
|
var import_perplexity_ai = __toESM(require("@perplexity-ai/perplexity_ai"), 1);
|
|
@@ -13347,14 +13602,29 @@ var isValidPostgresName = (id) => {
|
|
|
13347
13602
|
|
|
13348
13603
|
// src/exulu/app/index.ts
|
|
13349
13604
|
var isDev = process.env.NODE_ENV !== "production";
|
|
13605
|
+
var lineLimitFormat = import_winston2.default.format((info) => {
|
|
13606
|
+
if (typeof info.message === "string") {
|
|
13607
|
+
const lines = info.message.split("\n");
|
|
13608
|
+
if (lines.length > 50) {
|
|
13609
|
+
const truncatedLines = lines.slice(0, 50);
|
|
13610
|
+
truncatedLines.push(`... (${lines.length - 50} more lines omitted)`);
|
|
13611
|
+
info.message = truncatedLines.join("\n");
|
|
13612
|
+
}
|
|
13613
|
+
}
|
|
13614
|
+
return info;
|
|
13615
|
+
});
|
|
13350
13616
|
var consoleTransport = new import_winston2.default.transports.Console({
|
|
13351
13617
|
format: isDev ? import_winston2.default.format.combine(
|
|
13618
|
+
lineLimitFormat(),
|
|
13352
13619
|
import_winston2.default.format.colorize(),
|
|
13353
13620
|
import_winston2.default.format.timestamp({ format: "HH:mm:ss" }),
|
|
13354
13621
|
import_winston2.default.format.printf(({ timestamp, level, message }) => {
|
|
13355
13622
|
return `${timestamp} [${level}] ${message}`;
|
|
13356
13623
|
})
|
|
13357
|
-
) : import_winston2.default.format.
|
|
13624
|
+
) : import_winston2.default.format.combine(
|
|
13625
|
+
lineLimitFormat(),
|
|
13626
|
+
import_winston2.default.format.json()
|
|
13627
|
+
)
|
|
13358
13628
|
});
|
|
13359
13629
|
var formatArg = (arg) => typeof arg === "object" ? import_util.default.inspect(arg, { depth: null, colors: isDev }) : String(arg);
|
|
13360
13630
|
var createLogMethod = (logger, logLevel) => {
|
|
@@ -13424,6 +13694,7 @@ var ExuluApp2 = class {
|
|
|
13424
13694
|
this._tools = [
|
|
13425
13695
|
...tools ?? [],
|
|
13426
13696
|
...todoTools,
|
|
13697
|
+
...questionTools,
|
|
13427
13698
|
...perplexityTools,
|
|
13428
13699
|
// Add contexts as tools
|
|
13429
13700
|
...Object.values(contexts || {}).map((context) => context.tool()).filter(Boolean)
|
|
@@ -16003,7 +16274,7 @@ var MarkdownChunker = class {
|
|
|
16003
16274
|
var import_child_process = require("child_process");
|
|
16004
16275
|
var import_util2 = require("util");
|
|
16005
16276
|
var import_path = require("path");
|
|
16006
|
-
var
|
|
16277
|
+
var import_fs3 = require("fs");
|
|
16007
16278
|
var import_url = require("url");
|
|
16008
16279
|
var execAsync = (0, import_util2.promisify)(import_child_process.exec);
|
|
16009
16280
|
function getPackageRoot() {
|
|
@@ -16013,9 +16284,9 @@ function getPackageRoot() {
|
|
|
16013
16284
|
const maxAttempts = 10;
|
|
16014
16285
|
while (attempts < maxAttempts) {
|
|
16015
16286
|
const packageJsonPath = (0, import_path.join)(currentDir, "package.json");
|
|
16016
|
-
if ((0,
|
|
16287
|
+
if ((0, import_fs3.existsSync)(packageJsonPath)) {
|
|
16017
16288
|
try {
|
|
16018
|
-
const packageJson = JSON.parse((0,
|
|
16289
|
+
const packageJson = JSON.parse((0, import_fs3.readFileSync)(packageJsonPath, "utf-8"));
|
|
16019
16290
|
if (packageJson.name === "@exulu/backend") {
|
|
16020
16291
|
return currentDir;
|
|
16021
16292
|
}
|
|
@@ -16042,7 +16313,7 @@ function isPythonEnvironmentSetup(packageRoot) {
|
|
|
16042
16313
|
const root = packageRoot ?? getPackageRoot();
|
|
16043
16314
|
const venvPath = getVenvPath(root);
|
|
16044
16315
|
const pythonPath = (0, import_path.join)(venvPath, "bin", "python");
|
|
16045
|
-
return (0,
|
|
16316
|
+
return (0, import_fs3.existsSync)(venvPath) && (0, import_fs3.existsSync)(pythonPath);
|
|
16046
16317
|
}
|
|
16047
16318
|
async function setupPythonEnvironment(options = {}) {
|
|
16048
16319
|
const {
|
|
@@ -16063,7 +16334,7 @@ async function setupPythonEnvironment(options = {}) {
|
|
|
16063
16334
|
};
|
|
16064
16335
|
}
|
|
16065
16336
|
const setupScriptPath = getSetupScriptPath(packageRoot);
|
|
16066
|
-
if (!(0,
|
|
16337
|
+
if (!(0, import_fs3.existsSync)(setupScriptPath)) {
|
|
16067
16338
|
return {
|
|
16068
16339
|
success: false,
|
|
16069
16340
|
message: `Setup script not found at: ${setupScriptPath}`,
|
|
@@ -16145,13 +16416,13 @@ async function validatePythonEnvironment(packageRoot, checkPackages = true) {
|
|
|
16145
16416
|
const root = packageRoot ?? getPackageRoot();
|
|
16146
16417
|
const venvPath = getVenvPath(root);
|
|
16147
16418
|
const pythonPath = (0, import_path.join)(venvPath, "bin", "python");
|
|
16148
|
-
if (!(0,
|
|
16419
|
+
if (!(0, import_fs3.existsSync)(venvPath)) {
|
|
16149
16420
|
return {
|
|
16150
16421
|
valid: false,
|
|
16151
16422
|
message: getPythonSetupInstructions()
|
|
16152
16423
|
};
|
|
16153
16424
|
}
|
|
16154
|
-
if (!(0,
|
|
16425
|
+
if (!(0, import_fs3.existsSync)(pythonPath)) {
|
|
16155
16426
|
return {
|
|
16156
16427
|
valid: false,
|
|
16157
16428
|
message: "Python virtual environment is corrupted. Please run:\n await setupPythonEnvironment({ force: true })"
|
|
@@ -16203,7 +16474,7 @@ Or manually run the setup script:
|
|
|
16203
16474
|
}
|
|
16204
16475
|
|
|
16205
16476
|
// ee/python/documents/processing/doc_processor.ts
|
|
16206
|
-
var
|
|
16477
|
+
var fs3 = __toESM(require("fs"), 1);
|
|
16207
16478
|
var path = __toESM(require("path"), 1);
|
|
16208
16479
|
var import_ai7 = require("ai");
|
|
16209
16480
|
var import_zod13 = require("zod");
|
|
@@ -16218,7 +16489,7 @@ var import_officeparser2 = require("officeparser");
|
|
|
16218
16489
|
var import_child_process2 = require("child_process");
|
|
16219
16490
|
var import_util3 = require("util");
|
|
16220
16491
|
var import_path2 = require("path");
|
|
16221
|
-
var
|
|
16492
|
+
var import_fs4 = require("fs");
|
|
16222
16493
|
var import_url2 = require("url");
|
|
16223
16494
|
var execAsync2 = (0, import_util3.promisify)(import_child_process2.exec);
|
|
16224
16495
|
function getPackageRoot2() {
|
|
@@ -16228,9 +16499,9 @@ function getPackageRoot2() {
|
|
|
16228
16499
|
const maxAttempts = 10;
|
|
16229
16500
|
while (attempts < maxAttempts) {
|
|
16230
16501
|
const packageJsonPath = (0, import_path2.join)(currentDir, "package.json");
|
|
16231
|
-
if ((0,
|
|
16502
|
+
if ((0, import_fs4.existsSync)(packageJsonPath)) {
|
|
16232
16503
|
try {
|
|
16233
|
-
const packageJson = JSON.parse((0,
|
|
16504
|
+
const packageJson = JSON.parse((0, import_fs4.readFileSync)(packageJsonPath, "utf-8"));
|
|
16234
16505
|
if (packageJson.name === "@exulu/backend") {
|
|
16235
16506
|
return currentDir;
|
|
16236
16507
|
}
|
|
@@ -16292,7 +16563,7 @@ async function executePythonScript(config) {
|
|
|
16292
16563
|
await validatePythonEnvironmentForExecution(packageRoot);
|
|
16293
16564
|
}
|
|
16294
16565
|
const resolvedScriptPath = (0, import_path2.resolve)(packageRoot, scriptPath);
|
|
16295
|
-
if (!(0,
|
|
16566
|
+
if (!(0, import_fs4.existsSync)(resolvedScriptPath)) {
|
|
16296
16567
|
throw new PythonExecutionError(
|
|
16297
16568
|
`Python script not found: ${resolvedScriptPath}`,
|
|
16298
16569
|
"",
|
|
@@ -16428,7 +16699,7 @@ function reconstructHeadings(correctedText, headingsHierarchy) {
|
|
|
16428
16699
|
return result;
|
|
16429
16700
|
}
|
|
16430
16701
|
async function validatePageWithVLM(page, imagePath, model) {
|
|
16431
|
-
const imageBuffer = await
|
|
16702
|
+
const imageBuffer = await fs3.promises.readFile(imagePath);
|
|
16432
16703
|
const imageBase64 = imageBuffer.toString("base64");
|
|
16433
16704
|
const mimeType = "image/png";
|
|
16434
16705
|
const prompt = `You are a document validation assistant. Your task is to analyze a page image and correct the output of an OCR/parsing pipeline. The content may include tables, technical diagrams, schematics, and structured text.
|
|
@@ -16760,7 +17031,7 @@ ${setupResult.output || ""}`);
|
|
|
16760
17031
|
if (!result.success) {
|
|
16761
17032
|
throw new Error(`Document processing failed: ${result.stderr}`);
|
|
16762
17033
|
}
|
|
16763
|
-
const jsonContent = await
|
|
17034
|
+
const jsonContent = await fs3.promises.readFile(paths.json, "utf-8");
|
|
16764
17035
|
json = JSON.parse(jsonContent);
|
|
16765
17036
|
} else if (config?.processor.name === "officeparser") {
|
|
16766
17037
|
const text = await (0, import_officeparser2.parseOfficeAsync)(buffer, {
|
|
@@ -16793,9 +17064,9 @@ ${setupResult.output || ""}`);
|
|
|
16793
17064
|
}, 10);
|
|
16794
17065
|
const parser = new import_liteparse.LiteParse();
|
|
16795
17066
|
const screenshots = await parser.screenshot(paths.source, void 0);
|
|
16796
|
-
await
|
|
17067
|
+
await fs3.promises.mkdir(paths.images, { recursive: true });
|
|
16797
17068
|
for (const screenshot of screenshots) {
|
|
16798
|
-
await
|
|
17069
|
+
await fs3.promises.writeFile(
|
|
16799
17070
|
path.join(
|
|
16800
17071
|
paths.images,
|
|
16801
17072
|
`${screenshot.pageNum}.png`
|
|
@@ -16810,15 +17081,15 @@ ${setupResult.output || ""}`);
|
|
|
16810
17081
|
image: screenshots.find((s) => s.pageNum === page.index + 1)?.imagePath,
|
|
16811
17082
|
headings: []
|
|
16812
17083
|
}));
|
|
16813
|
-
|
|
17084
|
+
fs3.writeFileSync(paths.json, JSON.stringify(json, null, 2));
|
|
16814
17085
|
} else if (config?.processor.name === "liteparse") {
|
|
16815
17086
|
const parser = new import_liteparse.LiteParse();
|
|
16816
17087
|
const result = await parser.parse(paths.source);
|
|
16817
17088
|
const screenshots = await parser.screenshot(paths.source, void 0);
|
|
16818
17089
|
console.log(`[EXULU] Liteparse screenshots: ${JSON.stringify(screenshots)}`);
|
|
16819
|
-
await
|
|
17090
|
+
await fs3.promises.mkdir(paths.images, { recursive: true });
|
|
16820
17091
|
for (const screenshot of screenshots) {
|
|
16821
|
-
await
|
|
17092
|
+
await fs3.promises.writeFile(path.join(paths.images, `${screenshot.pageNum}.png`), screenshot.imageBuffer);
|
|
16822
17093
|
screenshot.imagePath = path.join(paths.images, `${screenshot.pageNum}.png`);
|
|
16823
17094
|
}
|
|
16824
17095
|
json = result.pages.map((page) => ({
|
|
@@ -16826,7 +17097,7 @@ ${setupResult.output || ""}`);
|
|
|
16826
17097
|
content: page.text,
|
|
16827
17098
|
image: screenshots.find((s) => s.pageNum === page.pageNum)?.imagePath
|
|
16828
17099
|
}));
|
|
16829
|
-
|
|
17100
|
+
fs3.writeFileSync(paths.json, JSON.stringify(json, null, 2));
|
|
16830
17101
|
}
|
|
16831
17102
|
console.log(`[EXULU]
|
|
16832
17103
|
\u2713 Document processing completed successfully`);
|
|
@@ -16857,13 +17128,13 @@ ${setupResult.output || ""}`);
|
|
|
16857
17128
|
console.log(`[EXULU] Corrected: ${page.vlm_corrected_text.substring(0, 150)}...`);
|
|
16858
17129
|
});
|
|
16859
17130
|
}
|
|
16860
|
-
await
|
|
17131
|
+
await fs3.promises.writeFile(
|
|
16861
17132
|
paths.json,
|
|
16862
17133
|
JSON.stringify(json, null, 2),
|
|
16863
17134
|
"utf-8"
|
|
16864
17135
|
);
|
|
16865
17136
|
}
|
|
16866
|
-
const markdownStream =
|
|
17137
|
+
const markdownStream = fs3.createWriteStream(paths.markdown, { encoding: "utf-8" });
|
|
16867
17138
|
for (let i = 0; i < json.length; i++) {
|
|
16868
17139
|
const p = json[i];
|
|
16869
17140
|
if (!p) continue;
|
|
@@ -16879,7 +17150,7 @@ ${setupResult.output || ""}`);
|
|
|
16879
17150
|
});
|
|
16880
17151
|
console.log(`[EXULU] Validated output saved to: ${paths.json}`);
|
|
16881
17152
|
console.log(`[EXULU] Validated markdown saved to: ${paths.markdown}`);
|
|
16882
|
-
const markdown = await
|
|
17153
|
+
const markdown = await fs3.promises.readFile(paths.markdown, "utf-8");
|
|
16883
17154
|
const processedJson = json.map((e) => {
|
|
16884
17155
|
const finalContent = e.vlm_corrected_text ?? e.content;
|
|
16885
17156
|
return {
|
|
@@ -16910,7 +17181,7 @@ var loadFile = async (file, name, tempDir) => {
|
|
|
16910
17181
|
let buffer;
|
|
16911
17182
|
if (Buffer.isBuffer(file)) {
|
|
16912
17183
|
filePath = path.join(tempDir, `${UUID}.${fileType}`);
|
|
16913
|
-
await
|
|
17184
|
+
await fs3.promises.writeFile(filePath, file);
|
|
16914
17185
|
buffer = file;
|
|
16915
17186
|
} else {
|
|
16916
17187
|
filePath = filePath.trim();
|
|
@@ -16918,11 +17189,11 @@ var loadFile = async (file, name, tempDir) => {
|
|
|
16918
17189
|
const response = await fetch(filePath);
|
|
16919
17190
|
const array = await response.arrayBuffer();
|
|
16920
17191
|
const tempFilePath = path.join(tempDir, `${UUID}.${fileType}`);
|
|
16921
|
-
await
|
|
17192
|
+
await fs3.promises.writeFile(tempFilePath, Buffer.from(array));
|
|
16922
17193
|
buffer = Buffer.from(array);
|
|
16923
17194
|
filePath = tempFilePath;
|
|
16924
17195
|
} else {
|
|
16925
|
-
buffer = await
|
|
17196
|
+
buffer = await fs3.promises.readFile(file);
|
|
16926
17197
|
}
|
|
16927
17198
|
}
|
|
16928
17199
|
return { filePath, fileType, buffer };
|
|
@@ -16940,9 +17211,9 @@ async function documentProcessor({
|
|
|
16940
17211
|
const tempDir = path.join(process.cwd(), "temp", uuid);
|
|
16941
17212
|
const localFilesAndFoldersToDelete = [tempDir];
|
|
16942
17213
|
console.log(`[EXULU] Temporary directory for processing document ${name}: ${tempDir}`);
|
|
16943
|
-
await
|
|
17214
|
+
await fs3.promises.mkdir(tempDir, { recursive: true });
|
|
16944
17215
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
16945
|
-
await
|
|
17216
|
+
await fs3.promises.writeFile(path.join(tempDir, "created_at.txt"), timestamp);
|
|
16946
17217
|
try {
|
|
16947
17218
|
const {
|
|
16948
17219
|
filePath,
|
|
@@ -16983,7 +17254,7 @@ async function documentProcessor({
|
|
|
16983
17254
|
if (config?.debugging?.deleteTempFiles !== false) {
|
|
16984
17255
|
for (const file2 of localFilesAndFoldersToDelete) {
|
|
16985
17256
|
try {
|
|
16986
|
-
await
|
|
17257
|
+
await fs3.promises.rm(file2, { recursive: true });
|
|
16987
17258
|
console.log(`[EXULU] Deleted file or folder: ${file2}`);
|
|
16988
17259
|
} catch (error) {
|
|
16989
17260
|
console.error(`[EXULU] Error deleting file or folder: ${file2}`, error);
|