@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.js
CHANGED
|
@@ -747,7 +747,7 @@ var JOB_STATUS_ENUM = {
|
|
|
747
747
|
};
|
|
748
748
|
|
|
749
749
|
// ee/agentic-retrieval/index.ts
|
|
750
|
-
import { z as
|
|
750
|
+
import { z as z6 } from "zod";
|
|
751
751
|
import {
|
|
752
752
|
stepCountIs,
|
|
753
753
|
tool as tool2,
|
|
@@ -3188,7 +3188,7 @@ var mapType = (t, type, name, defaultValue, unique) => {
|
|
|
3188
3188
|
|
|
3189
3189
|
// src/exulu/tool.ts
|
|
3190
3190
|
import { tool } from "ai";
|
|
3191
|
-
import { z as
|
|
3191
|
+
import { z as z4 } from "zod";
|
|
3192
3192
|
import CryptoJS2 from "crypto-js";
|
|
3193
3193
|
|
|
3194
3194
|
// src/templates/tools/convert-exulu-tools-to-ai-sdk-tools.ts
|
|
@@ -3404,6 +3404,102 @@ function sanitizeToolName(name) {
|
|
|
3404
3404
|
|
|
3405
3405
|
// src/templates/tools/convert-exulu-tools-to-ai-sdk-tools.ts
|
|
3406
3406
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
3407
|
+
|
|
3408
|
+
// src/templates/tools/memory-tool.ts
|
|
3409
|
+
import { z as z3 } from "zod";
|
|
3410
|
+
import fs from "fs";
|
|
3411
|
+
var createNewMemoryItemTool = (agent, context) => {
|
|
3412
|
+
const fields = {
|
|
3413
|
+
name: z3.string().describe("The name of the item to create"),
|
|
3414
|
+
description: z3.string().describe("The description of the item to create")
|
|
3415
|
+
};
|
|
3416
|
+
for (const field of context.fields) {
|
|
3417
|
+
switch (field.type) {
|
|
3418
|
+
case "text":
|
|
3419
|
+
case "longText":
|
|
3420
|
+
case "shortText":
|
|
3421
|
+
case "code":
|
|
3422
|
+
case "enum":
|
|
3423
|
+
fields[field.name] = z3.string().describe("The " + field.name + " of the item to create");
|
|
3424
|
+
break;
|
|
3425
|
+
case "json":
|
|
3426
|
+
fields[field.name] = z3.string({}).describe(
|
|
3427
|
+
"The " + field.name + " of the item to create, it should be a valid JSON string."
|
|
3428
|
+
);
|
|
3429
|
+
break;
|
|
3430
|
+
case "markdown":
|
|
3431
|
+
fields[field.name] = z3.string().describe(
|
|
3432
|
+
"The " + field.name + " of the item to create, it should be a valid Markdown string."
|
|
3433
|
+
);
|
|
3434
|
+
break;
|
|
3435
|
+
case "number":
|
|
3436
|
+
fields[field.name] = z3.number().describe("The " + field.name + " of the item to create");
|
|
3437
|
+
break;
|
|
3438
|
+
case "boolean":
|
|
3439
|
+
fields[field.name] = z3.boolean().describe("The " + field.name + " of the item to create");
|
|
3440
|
+
break;
|
|
3441
|
+
case "file":
|
|
3442
|
+
case "uuid":
|
|
3443
|
+
case "date":
|
|
3444
|
+
break;
|
|
3445
|
+
default:
|
|
3446
|
+
fields[field.name] = z3.string().describe("The " + field.name + " of the item to create");
|
|
3447
|
+
break;
|
|
3448
|
+
}
|
|
3449
|
+
}
|
|
3450
|
+
const toolName = "create_" + sanitizeName(context.name) + "_memory_item";
|
|
3451
|
+
return new ExuluTool({
|
|
3452
|
+
id: toolName,
|
|
3453
|
+
name: "Create " + context.name + " Memory Item",
|
|
3454
|
+
category: agent.name + "_memory",
|
|
3455
|
+
description: "Create a new memory item in the " + agent.name + " memory context",
|
|
3456
|
+
type: "function",
|
|
3457
|
+
inputSchema: z3.object(fields),
|
|
3458
|
+
config: [],
|
|
3459
|
+
execute: async ({ name, description, mode, information, exuluConfig, user }) => {
|
|
3460
|
+
let result = { result: "" };
|
|
3461
|
+
fs.writeFileSync("memory-tool.json", JSON.stringify({ name, description, information }, null, 2));
|
|
3462
|
+
try {
|
|
3463
|
+
const newItem = {
|
|
3464
|
+
name,
|
|
3465
|
+
description,
|
|
3466
|
+
information,
|
|
3467
|
+
rights_mode: "public"
|
|
3468
|
+
};
|
|
3469
|
+
const { item: createdItem, job: createdJob } = await context.createItem(
|
|
3470
|
+
newItem,
|
|
3471
|
+
exuluConfig,
|
|
3472
|
+
user?.id,
|
|
3473
|
+
user?.role?.id,
|
|
3474
|
+
false
|
|
3475
|
+
);
|
|
3476
|
+
fs.writeFileSync("memory-tool-created.json", JSON.stringify({ createdItem, createdJob }, null, 2));
|
|
3477
|
+
if (createdJob) {
|
|
3478
|
+
result = {
|
|
3479
|
+
result: `Created a Job to create the memory item with the following ID: ${createdJob}`
|
|
3480
|
+
};
|
|
3481
|
+
} else if (createdItem) {
|
|
3482
|
+
result = {
|
|
3483
|
+
result: `Created memory item with the following ID: ${createdItem.id}`
|
|
3484
|
+
};
|
|
3485
|
+
} else {
|
|
3486
|
+
result = {
|
|
3487
|
+
result: `Failed to create memory item`
|
|
3488
|
+
};
|
|
3489
|
+
}
|
|
3490
|
+
} catch (error) {
|
|
3491
|
+
fs.writeFileSync("memory-tool-error.json", JSON.stringify({ name, description, information, error }, null, 2));
|
|
3492
|
+
console.error("[EXULU] Error creating memory item", error);
|
|
3493
|
+
result = {
|
|
3494
|
+
result: `Failed to create memory item: ${error instanceof Error ? error.message : String(error)}`
|
|
3495
|
+
};
|
|
3496
|
+
}
|
|
3497
|
+
return result;
|
|
3498
|
+
}
|
|
3499
|
+
});
|
|
3500
|
+
};
|
|
3501
|
+
|
|
3502
|
+
// src/templates/tools/convert-exulu-tools-to-ai-sdk-tools.ts
|
|
3407
3503
|
var generateS3Key = (filename) => `${randomUUID2()}-${filename}`;
|
|
3408
3504
|
var s3Client2;
|
|
3409
3505
|
var getMimeType = (type) => {
|
|
@@ -3507,6 +3603,21 @@ var convertExuluToolsToAiSdkTools = async (currentTools, approvedTools, allExulu
|
|
|
3507
3603
|
currentTools.push(projectRetrievalTool);
|
|
3508
3604
|
}
|
|
3509
3605
|
}
|
|
3606
|
+
if (agent?.memory && contexts?.length) {
|
|
3607
|
+
const context = contexts.find((context2) => context2.id === agent?.memory);
|
|
3608
|
+
if (!context) {
|
|
3609
|
+
throw new Error(
|
|
3610
|
+
"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."
|
|
3611
|
+
);
|
|
3612
|
+
}
|
|
3613
|
+
const createNewMemoryTool = createNewMemoryItemTool(agent, context);
|
|
3614
|
+
if (createNewMemoryTool) {
|
|
3615
|
+
if (!currentTools) {
|
|
3616
|
+
currentTools = [];
|
|
3617
|
+
}
|
|
3618
|
+
currentTools.push(createNewMemoryTool);
|
|
3619
|
+
}
|
|
3620
|
+
}
|
|
3510
3621
|
console.log("[EXULU] Convert tools array to object, session items", items);
|
|
3511
3622
|
if (items) {
|
|
3512
3623
|
const sessionItemsRetrievalTool = await createSessionItemsRetrievalTool({
|
|
@@ -3752,7 +3863,7 @@ var ExuluTool = class {
|
|
|
3752
3863
|
this.type = type;
|
|
3753
3864
|
this.tool = tool({
|
|
3754
3865
|
description,
|
|
3755
|
-
inputSchema: inputSchema ||
|
|
3866
|
+
inputSchema: inputSchema || z4.object({}),
|
|
3756
3867
|
execute: execute2
|
|
3757
3868
|
});
|
|
3758
3869
|
}
|
|
@@ -3845,7 +3956,7 @@ var ExuluTool = class {
|
|
|
3845
3956
|
};
|
|
3846
3957
|
|
|
3847
3958
|
// src/exulu/context.ts
|
|
3848
|
-
import { z as
|
|
3959
|
+
import { z as z5 } from "zod";
|
|
3849
3960
|
|
|
3850
3961
|
// ee/queues/decorator.ts
|
|
3851
3962
|
import "bullmq";
|
|
@@ -4003,6 +4114,7 @@ var ExuluContext2 = class {
|
|
|
4003
4114
|
exuluConfig
|
|
4004
4115
|
});
|
|
4005
4116
|
if (!result) {
|
|
4117
|
+
console.log("[EXULU] Item filtered out by processor, skipping processing execution...");
|
|
4006
4118
|
return {
|
|
4007
4119
|
result: void 0,
|
|
4008
4120
|
job: void 0
|
|
@@ -4203,10 +4315,12 @@ var ExuluContext2 = class {
|
|
|
4203
4315
|
}
|
|
4204
4316
|
}
|
|
4205
4317
|
});
|
|
4318
|
+
console.log("[EXULU] Creating item", item);
|
|
4206
4319
|
const mutation = db2.from(getTableName(this.id)).insert({
|
|
4207
4320
|
...item,
|
|
4208
4321
|
tags: item.tags ? Array.isArray(item.tags) ? item.tags.join(",") : item.tags : void 0
|
|
4209
4322
|
}).returning("id");
|
|
4323
|
+
console.log("[EXULU] Upsert", upsert);
|
|
4210
4324
|
if (upsert) {
|
|
4211
4325
|
if (item.external_id) {
|
|
4212
4326
|
mutation.onConflict("external_id").merge();
|
|
@@ -4225,7 +4339,6 @@ var ExuluContext2 = class {
|
|
|
4225
4339
|
let shouldGenerateEmbeddings = this.embedder && generateEmbeddingsOverwrite !== false && (generateEmbeddingsOverwrite || this.configuration.calculateVectors === "onInsert" || this.configuration.calculateVectors === "always");
|
|
4226
4340
|
if (this.processor) {
|
|
4227
4341
|
const processor = this.processor;
|
|
4228
|
-
console.log("[EXULU] Processor found", processor);
|
|
4229
4342
|
if (processor && (processor?.config?.trigger === "onInsert" || processor?.config?.trigger === "onUpdate" || processor?.config?.trigger === "always")) {
|
|
4230
4343
|
const { job: processorJob, result: processorResult } = await this.processField(
|
|
4231
4344
|
"api",
|
|
@@ -4240,7 +4353,7 @@ var ExuluContext2 = class {
|
|
|
4240
4353
|
if (processorJob) {
|
|
4241
4354
|
jobs.push(processorJob);
|
|
4242
4355
|
}
|
|
4243
|
-
if (!processorJob) {
|
|
4356
|
+
if (!processorJob && processorResult) {
|
|
4244
4357
|
await db2.from(getTableName(this.id)).where({ id: results[0].id }).update({
|
|
4245
4358
|
...processorResult
|
|
4246
4359
|
});
|
|
@@ -4311,7 +4424,7 @@ var ExuluContext2 = class {
|
|
|
4311
4424
|
if (processorJob) {
|
|
4312
4425
|
jobs.push(processorJob);
|
|
4313
4426
|
}
|
|
4314
|
-
if (!processorJob) {
|
|
4427
|
+
if (!processorJob && processorResult) {
|
|
4315
4428
|
await db2.from(getTableName(this.id)).where({ id: record.id }).update({
|
|
4316
4429
|
...processorResult
|
|
4317
4430
|
});
|
|
@@ -4565,12 +4678,12 @@ var ExuluContext2 = class {
|
|
|
4565
4678
|
category: "contexts",
|
|
4566
4679
|
needsApproval: true,
|
|
4567
4680
|
// todo make configurable
|
|
4568
|
-
inputSchema:
|
|
4569
|
-
query:
|
|
4570
|
-
keywords:
|
|
4681
|
+
inputSchema: z5.object({
|
|
4682
|
+
query: z5.string().describe("The original question that the user asked"),
|
|
4683
|
+
keywords: z5.array(z5.string()).describe(
|
|
4571
4684
|
"The keywords that are relevant to the user's question, for example names of specific products, systems or parts, IDs, etc."
|
|
4572
4685
|
),
|
|
4573
|
-
method:
|
|
4686
|
+
method: z5.enum(["keyword", "semantic", "hybrid"]).default("hybrid").describe(
|
|
4574
4687
|
"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)"
|
|
4575
4688
|
)
|
|
4576
4689
|
}),
|
|
@@ -4781,11 +4894,11 @@ function createCustomAgenticRetrievalToolLoopAgent({
|
|
|
4781
4894
|
return await generateText({
|
|
4782
4895
|
model,
|
|
4783
4896
|
output: Output.object({
|
|
4784
|
-
schema:
|
|
4785
|
-
reasoning:
|
|
4897
|
+
schema: z6.object({
|
|
4898
|
+
reasoning: z6.string().describe(
|
|
4786
4899
|
"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."
|
|
4787
4900
|
),
|
|
4788
|
-
finished:
|
|
4901
|
+
finished: z6.boolean().describe(
|
|
4789
4902
|
"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."
|
|
4790
4903
|
)
|
|
4791
4904
|
})
|
|
@@ -4915,9 +5028,9 @@ function createCustomAgenticRetrievalToolLoopAgent({
|
|
|
4915
5028
|
if (chunksCount > 1) {
|
|
4916
5029
|
dynamicTools[getMoreToolName] = tool2({
|
|
4917
5030
|
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.`,
|
|
4918
|
-
inputSchema:
|
|
4919
|
-
from_index:
|
|
4920
|
-
to_index:
|
|
5031
|
+
inputSchema: z6.object({
|
|
5032
|
+
from_index: z6.number().default(1).describe("The index of the chunk to start from."),
|
|
5033
|
+
to_index: z6.number().max(chunksCount).describe("The index of the chunk to end at, max is " + chunksCount)
|
|
4921
5034
|
}),
|
|
4922
5035
|
execute: async ({ from_index, to_index }) => {
|
|
4923
5036
|
const chunks2 = await db2(chunksTable).select("*").where("source", chunk.item_id).whereBetween("chunk_index", [from_index, to_index]).orderBy("chunk_index", "asc");
|
|
@@ -4945,8 +5058,8 @@ function createCustomAgenticRetrievalToolLoopAgent({
|
|
|
4945
5058
|
);
|
|
4946
5059
|
dynamicTools[getContentToolName] = tool2({
|
|
4947
5060
|
description: `Get the content of the page ${chunk.chunk_index} for the item ${chunk.item_name}`,
|
|
4948
|
-
inputSchema:
|
|
4949
|
-
reasoning:
|
|
5061
|
+
inputSchema: z6.object({
|
|
5062
|
+
reasoning: z6.string().describe("The reasoning for why you need to get the content of the page.")
|
|
4950
5063
|
}),
|
|
4951
5064
|
execute: async ({ reasoning }) => {
|
|
4952
5065
|
const { db: db3 } = await postgresClient();
|
|
@@ -5014,8 +5127,8 @@ var createAgenticRetrievalAgent = ({
|
|
|
5014
5127
|
search_items_by_name: tool2({
|
|
5015
5128
|
description: `
|
|
5016
5129
|
Search for relevant items by name across the available knowledge bases.`,
|
|
5017
|
-
inputSchema:
|
|
5018
|
-
knowledge_base_ids:
|
|
5130
|
+
inputSchema: z6.object({
|
|
5131
|
+
knowledge_base_ids: z6.array(z6.enum(contexts.map((ctx) => ctx.id))).describe(`
|
|
5019
5132
|
The available knowledge bases are:
|
|
5020
5133
|
${contexts.map(
|
|
5021
5134
|
(ctx) => `
|
|
@@ -5027,8 +5140,8 @@ var createAgenticRetrievalAgent = ({
|
|
|
5027
5140
|
`
|
|
5028
5141
|
).join("\n")}
|
|
5029
5142
|
`),
|
|
5030
|
-
item_name:
|
|
5031
|
-
limit:
|
|
5143
|
+
item_name: z6.string().describe("The name of the item to search for."),
|
|
5144
|
+
limit: z6.number().default(100).describe(
|
|
5032
5145
|
"Maximum number of items to return (max 400), if searching through multiple knowledge bases, the limit is applied for each knowledge base individually."
|
|
5033
5146
|
)
|
|
5034
5147
|
}),
|
|
@@ -5134,11 +5247,11 @@ var createAgenticRetrievalAgent = ({
|
|
|
5134
5247
|
- You can always fetch content later if needed
|
|
5135
5248
|
|
|
5136
5249
|
`,
|
|
5137
|
-
inputSchema:
|
|
5138
|
-
query:
|
|
5250
|
+
inputSchema: z6.object({
|
|
5251
|
+
query: z6.string().describe(
|
|
5139
5252
|
"The search query to find relevant chunks, this must always be related to the content you are looking for, not something like 'Page 2'."
|
|
5140
5253
|
),
|
|
5141
|
-
knowledge_base_ids:
|
|
5254
|
+
knowledge_base_ids: z6.array(z6.enum(contexts.map((ctx) => ctx.id))).describe(`
|
|
5142
5255
|
The available knowledge bases are:
|
|
5143
5256
|
${contexts.map(
|
|
5144
5257
|
(ctx) => `
|
|
@@ -5150,25 +5263,25 @@ var createAgenticRetrievalAgent = ({
|
|
|
5150
5263
|
`
|
|
5151
5264
|
).join("\n")}
|
|
5152
5265
|
`),
|
|
5153
|
-
keywords:
|
|
5266
|
+
keywords: z6.array(z6.string()).optional().describe(
|
|
5154
5267
|
"Keywords to search for. Usually extracted from the query, allowing for more precise search results."
|
|
5155
5268
|
),
|
|
5156
|
-
searchMethod:
|
|
5269
|
+
searchMethod: z6.enum(["keyword", "semantic", "hybrid"]).default("hybrid").describe(
|
|
5157
5270
|
"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)"
|
|
5158
5271
|
),
|
|
5159
|
-
includeContent:
|
|
5272
|
+
includeContent: z6.boolean().default(true).describe(
|
|
5160
5273
|
"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."
|
|
5161
5274
|
),
|
|
5162
|
-
item_ids:
|
|
5275
|
+
item_ids: z6.array(z6.string()).optional().describe(
|
|
5163
5276
|
"Use if you wish to retrieve content from specific items (documents) based on the item ID."
|
|
5164
5277
|
),
|
|
5165
|
-
item_names:
|
|
5278
|
+
item_names: z6.array(z6.string()).optional().describe(
|
|
5166
5279
|
"Use if you wish to retrieve content from specific items (documents) based on the item name. Can be a partial match."
|
|
5167
5280
|
),
|
|
5168
|
-
item_external_ids:
|
|
5281
|
+
item_external_ids: z6.array(z6.string()).optional().describe(
|
|
5169
5282
|
"Use if you wish to retrieve content from specific items (documents) based on the item external ID. Can be a partial match."
|
|
5170
5283
|
),
|
|
5171
|
-
limit:
|
|
5284
|
+
limit: z6.number().default(10).describe("Maximum number of chunks to return (max 10)")
|
|
5172
5285
|
}),
|
|
5173
5286
|
execute: async ({
|
|
5174
5287
|
query,
|
|
@@ -5359,9 +5472,9 @@ var createAgenticRetrievalTool = ({
|
|
|
5359
5472
|
default: true
|
|
5360
5473
|
}))
|
|
5361
5474
|
],
|
|
5362
|
-
inputSchema:
|
|
5363
|
-
query:
|
|
5364
|
-
userInstructions:
|
|
5475
|
+
inputSchema: z6.object({
|
|
5476
|
+
query: z6.string().describe("The question or query to answer using the knowledge bases"),
|
|
5477
|
+
userInstructions: z6.string().optional().describe("Instructions provided by the user to customize the retrieval process.")
|
|
5365
5478
|
}),
|
|
5366
5479
|
execute: async function* ({
|
|
5367
5480
|
query,
|
|
@@ -7379,7 +7492,32 @@ var createWorkers = async (providers, queues2, config, contexts, rerankers, eval
|
|
|
7379
7492
|
);
|
|
7380
7493
|
}
|
|
7381
7494
|
const exuluStorage = new ExuluStorage({ config });
|
|
7382
|
-
|
|
7495
|
+
if (context.processor.filter) {
|
|
7496
|
+
const result2 = await context.processor.filter({
|
|
7497
|
+
item: data.inputs,
|
|
7498
|
+
user: data.user,
|
|
7499
|
+
role: data.role,
|
|
7500
|
+
utils: {
|
|
7501
|
+
storage: exuluStorage
|
|
7502
|
+
},
|
|
7503
|
+
exuluConfig: config
|
|
7504
|
+
});
|
|
7505
|
+
if (!result2) {
|
|
7506
|
+
console.log("[EXULU] Item filtered out by processor, skipping processing execution...");
|
|
7507
|
+
return {
|
|
7508
|
+
result: "Item filtered out by processor, skipping processing execution...",
|
|
7509
|
+
// last message
|
|
7510
|
+
metadata: {
|
|
7511
|
+
item: {
|
|
7512
|
+
name: data.inputs?.name,
|
|
7513
|
+
id: data.inputs?.id,
|
|
7514
|
+
external_id: data.inputs?.external_id
|
|
7515
|
+
}
|
|
7516
|
+
}
|
|
7517
|
+
};
|
|
7518
|
+
}
|
|
7519
|
+
}
|
|
7520
|
+
console.log("[EXULU] POS 2 -- EXULU CONTEXT PROCESS FIELD", data.inputs);
|
|
7383
7521
|
let processorResult = await context.processor.execute({
|
|
7384
7522
|
item: data.inputs,
|
|
7385
7523
|
user: data.user,
|
|
@@ -9829,7 +9967,7 @@ import { InMemoryLRUCache } from "@apollo/utils.keyvaluecache";
|
|
|
9829
9967
|
import bodyParser from "body-parser";
|
|
9830
9968
|
import CryptoJS6 from "crypto-js";
|
|
9831
9969
|
import OpenAI from "openai";
|
|
9832
|
-
import
|
|
9970
|
+
import fs2 from "fs";
|
|
9833
9971
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
9834
9972
|
import "@opentelemetry/api";
|
|
9835
9973
|
import Anthropic from "@anthropic-ai/sdk";
|
|
@@ -9885,98 +10023,6 @@ function generateSlug(name) {
|
|
|
9885
10023
|
// src/exulu/provider.ts
|
|
9886
10024
|
import CryptoJS5 from "crypto-js";
|
|
9887
10025
|
import { parseOfficeAsync } from "officeparser";
|
|
9888
|
-
|
|
9889
|
-
// src/templates/tools/memory-tool.ts
|
|
9890
|
-
import { z as z6 } from "zod";
|
|
9891
|
-
var createNewMemoryItemTool = (agent, context) => {
|
|
9892
|
-
const fields = {
|
|
9893
|
-
name: z6.string().describe("The name of the item to create"),
|
|
9894
|
-
description: z6.string().describe("The description of the item to create")
|
|
9895
|
-
};
|
|
9896
|
-
for (const field of context.fields) {
|
|
9897
|
-
switch (field.type) {
|
|
9898
|
-
case "text":
|
|
9899
|
-
case "longText":
|
|
9900
|
-
case "shortText":
|
|
9901
|
-
case "code":
|
|
9902
|
-
case "enum":
|
|
9903
|
-
fields[field.name] = z6.string().describe("The " + field.name + " of the item to create");
|
|
9904
|
-
break;
|
|
9905
|
-
case "json":
|
|
9906
|
-
fields[field.name] = z6.string({}).describe(
|
|
9907
|
-
"The " + field.name + " of the item to create, it should be a valid JSON string."
|
|
9908
|
-
);
|
|
9909
|
-
break;
|
|
9910
|
-
case "markdown":
|
|
9911
|
-
fields[field.name] = z6.string().describe(
|
|
9912
|
-
"The " + field.name + " of the item to create, it should be a valid Markdown string."
|
|
9913
|
-
);
|
|
9914
|
-
break;
|
|
9915
|
-
case "number":
|
|
9916
|
-
fields[field.name] = z6.number().describe("The " + field.name + " of the item to create");
|
|
9917
|
-
break;
|
|
9918
|
-
case "boolean":
|
|
9919
|
-
fields[field.name] = z6.boolean().describe("The " + field.name + " of the item to create");
|
|
9920
|
-
break;
|
|
9921
|
-
case "file":
|
|
9922
|
-
case "uuid":
|
|
9923
|
-
case "date":
|
|
9924
|
-
break;
|
|
9925
|
-
default:
|
|
9926
|
-
fields[field.name] = z6.string().describe("The " + field.name + " of the item to create");
|
|
9927
|
-
break;
|
|
9928
|
-
}
|
|
9929
|
-
}
|
|
9930
|
-
return new ExuluTool({
|
|
9931
|
-
id: "create_" + agent.name + "_memory_item",
|
|
9932
|
-
name: "Create " + agent.name + " Memory Item",
|
|
9933
|
-
category: agent.name + "_memory",
|
|
9934
|
-
description: "Create a new memory item in the " + agent.name + " memory context",
|
|
9935
|
-
type: "function",
|
|
9936
|
-
inputSchema: z6.object(fields),
|
|
9937
|
-
config: [],
|
|
9938
|
-
execute: async ({ name, description, mode, information, exuluConfig, user }) => {
|
|
9939
|
-
let result = { result: "" };
|
|
9940
|
-
switch (mode) {
|
|
9941
|
-
case "learnings":
|
|
9942
|
-
break;
|
|
9943
|
-
case "knowledge":
|
|
9944
|
-
const newItem = {
|
|
9945
|
-
name,
|
|
9946
|
-
description,
|
|
9947
|
-
information,
|
|
9948
|
-
rights_mode: "public"
|
|
9949
|
-
};
|
|
9950
|
-
const { item: createdItem, job: createdJob } = await context.createItem(
|
|
9951
|
-
newItem,
|
|
9952
|
-
exuluConfig,
|
|
9953
|
-
user?.id,
|
|
9954
|
-
user?.role?.id,
|
|
9955
|
-
false
|
|
9956
|
-
);
|
|
9957
|
-
if (createdJob) {
|
|
9958
|
-
result = {
|
|
9959
|
-
result: `Created a Job to create the memory item with the following ID: ${createdJob}`
|
|
9960
|
-
};
|
|
9961
|
-
} else if (createdItem) {
|
|
9962
|
-
result = {
|
|
9963
|
-
result: `Created memory item with the following ID: ${createdItem.id}`
|
|
9964
|
-
};
|
|
9965
|
-
} else {
|
|
9966
|
-
result = {
|
|
9967
|
-
result: `Failed to create memory item`
|
|
9968
|
-
};
|
|
9969
|
-
}
|
|
9970
|
-
break;
|
|
9971
|
-
default:
|
|
9972
|
-
throw new Error(`Invalid mode: ${mode}`);
|
|
9973
|
-
}
|
|
9974
|
-
return result;
|
|
9975
|
-
}
|
|
9976
|
-
});
|
|
9977
|
-
};
|
|
9978
|
-
|
|
9979
|
-
// src/exulu/provider.ts
|
|
9980
10026
|
var ExuluProvider = class {
|
|
9981
10027
|
// Must begin with a letter (a-z) or underscore (_). Subsequent characters in a name can be letters, digits (0-9), or
|
|
9982
10028
|
// underscores and be a max length of 80 characters and at least 5 characters long.
|
|
@@ -10236,13 +10282,6 @@ var ExuluProvider = class {
|
|
|
10236
10282
|
|
|
10237
10283
|
${result.chunks.map((chunk) => chunk.chunk_content).join("\n\n")}`;
|
|
10238
10284
|
}
|
|
10239
|
-
const createNewMemoryTool = createNewMemoryItemTool(agent, context);
|
|
10240
|
-
if (createNewMemoryTool) {
|
|
10241
|
-
if (!currentTools) {
|
|
10242
|
-
currentTools = [];
|
|
10243
|
-
}
|
|
10244
|
-
currentTools.push(createNewMemoryTool);
|
|
10245
|
-
}
|
|
10246
10285
|
}
|
|
10247
10286
|
const personalizationInformation = exuluConfig?.privacy?.systemPromptPersonalization !== false ? `
|
|
10248
10287
|
${user?.firstname ? `The users first name is "${user.firstname}"` : ""}
|
|
@@ -10306,6 +10345,10 @@ var ExuluProvider = class {
|
|
|
10306
10345
|
Example: {url: https://www.google.com, title: Google, snippet: The result of the web search.}
|
|
10307
10346
|
`;
|
|
10308
10347
|
}
|
|
10348
|
+
system += `
|
|
10349
|
+
|
|
10350
|
+
When a tool execution is not approved by the user, do not retry it unless explicitly asked by the user. ' +
|
|
10351
|
+
'Inform the user that the action was not performed.`;
|
|
10309
10352
|
if (prompt) {
|
|
10310
10353
|
let result = { object: null, text: "" };
|
|
10311
10354
|
let inputTokens = 0;
|
|
@@ -10620,13 +10663,6 @@ ${extractedText}
|
|
|
10620
10663
|
|
|
10621
10664
|
${result2.chunks.map((chunk) => chunk.chunk_content).join("\n\n")}`;
|
|
10622
10665
|
}
|
|
10623
|
-
const createNewMemoryTool = createNewMemoryItemTool(agent, context);
|
|
10624
|
-
if (createNewMemoryTool) {
|
|
10625
|
-
if (!currentTools) {
|
|
10626
|
-
currentTools = [];
|
|
10627
|
-
}
|
|
10628
|
-
currentTools.push(createNewMemoryTool);
|
|
10629
|
-
}
|
|
10630
10666
|
}
|
|
10631
10667
|
messages = messages.filter(
|
|
10632
10668
|
(message2, index, self) => index === self.findLastIndex((t) => t.id === message2.id)
|
|
@@ -10685,6 +10721,10 @@ ${extractedText}
|
|
|
10685
10721
|
Example: {url: https://www.google.com, title: Google, snippet: The result of the web search.}
|
|
10686
10722
|
`;
|
|
10687
10723
|
}
|
|
10724
|
+
system += `
|
|
10725
|
+
|
|
10726
|
+
When a tool execution is not approved by the user, do not retry it unless explicitly asked by the user. ' +
|
|
10727
|
+
'Inform the user that the action was not performed.`;
|
|
10688
10728
|
const result = streamText({
|
|
10689
10729
|
model,
|
|
10690
10730
|
// Should be a LanguageModelV1
|
|
@@ -10829,12 +10869,11 @@ var providerRateLimiter = async (key, windowSeconds, limit, points) => {
|
|
|
10829
10869
|
|
|
10830
10870
|
// src/exulu/routes.ts
|
|
10831
10871
|
import { convertJsonSchemaToZod } from "zod-from-json-schema";
|
|
10832
|
-
import "zod";
|
|
10833
10872
|
var REQUEST_SIZE_LIMIT = "50mb";
|
|
10834
10873
|
var getExuluVersionNumber = async () => {
|
|
10835
10874
|
try {
|
|
10836
10875
|
const path2 = process.cwd();
|
|
10837
|
-
const packageJson =
|
|
10876
|
+
const packageJson = fs2.readFileSync(path2 + "/package.json", "utf8");
|
|
10838
10877
|
const packageData = JSON.parse(packageJson);
|
|
10839
10878
|
const exuluVersion = packageData.dependencies["@exulu/backend"];
|
|
10840
10879
|
console.log(`[EXULU] Installed exulu-backend version: ${exuluVersion}`);
|
|
@@ -11744,7 +11783,7 @@ import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
|
11744
11783
|
import "express";
|
|
11745
11784
|
import "@opentelemetry/api";
|
|
11746
11785
|
import CryptoJS7 from "crypto-js";
|
|
11747
|
-
import { z as
|
|
11786
|
+
import { z as z8 } from "zod";
|
|
11748
11787
|
var SESSION_ID_HEADER = "mcp-session-id";
|
|
11749
11788
|
var ExuluMCP = class {
|
|
11750
11789
|
server = {};
|
|
@@ -11827,7 +11866,7 @@ var ExuluMCP = class {
|
|
|
11827
11866
|
title: tool3.name + " agent",
|
|
11828
11867
|
description: tool3.description,
|
|
11829
11868
|
inputSchema: {
|
|
11830
|
-
inputs: tool3.inputSchema ||
|
|
11869
|
+
inputs: tool3.inputSchema || z8.object({})
|
|
11831
11870
|
}
|
|
11832
11871
|
},
|
|
11833
11872
|
async ({ inputs }, args) => {
|
|
@@ -11879,7 +11918,7 @@ var ExuluMCP = class {
|
|
|
11879
11918
|
title: "Get List of Prompt Templates",
|
|
11880
11919
|
description: "Retrieves a list of prompt templates available for this agent. Returns the name, description, and ID of each template.",
|
|
11881
11920
|
inputSchema: {
|
|
11882
|
-
inputs:
|
|
11921
|
+
inputs: z8.object({})
|
|
11883
11922
|
}
|
|
11884
11923
|
},
|
|
11885
11924
|
async ({ inputs }, args) => {
|
|
@@ -11925,8 +11964,8 @@ var ExuluMCP = class {
|
|
|
11925
11964
|
title: "Get Prompt Template Details",
|
|
11926
11965
|
description: "Retrieves the full details of a specific prompt template by ID, including the actual template content with variables.",
|
|
11927
11966
|
inputSchema: {
|
|
11928
|
-
inputs:
|
|
11929
|
-
id:
|
|
11967
|
+
inputs: z8.object({
|
|
11968
|
+
id: z8.string().describe("The ID of the prompt template to retrieve")
|
|
11930
11969
|
})
|
|
11931
11970
|
}
|
|
11932
11971
|
},
|
|
@@ -12834,7 +12873,7 @@ var ExuluEval = class {
|
|
|
12834
12873
|
};
|
|
12835
12874
|
|
|
12836
12875
|
// src/templates/evals/index.ts
|
|
12837
|
-
import { z as
|
|
12876
|
+
import { z as z9 } from "zod";
|
|
12838
12877
|
var llmAsJudgeEval = () => {
|
|
12839
12878
|
if (process.env.REDIS_HOST?.length && process.env.REDIS_PORT?.length) {
|
|
12840
12879
|
return new ExuluEval({
|
|
@@ -12879,8 +12918,8 @@ var llmAsJudgeEval = () => {
|
|
|
12879
12918
|
contexts: [],
|
|
12880
12919
|
rerankers: [],
|
|
12881
12920
|
prompt,
|
|
12882
|
-
outputSchema:
|
|
12883
|
-
score:
|
|
12921
|
+
outputSchema: z9.object({
|
|
12922
|
+
score: z9.number().min(0).max(100).describe("The score between 0 and 100.")
|
|
12884
12923
|
}),
|
|
12885
12924
|
providerapikey
|
|
12886
12925
|
});
|
|
@@ -13108,12 +13147,12 @@ Usage:
|
|
|
13108
13147
|
- If no todos exist yet, an empty list will be returned`;
|
|
13109
13148
|
|
|
13110
13149
|
// src/templates/tools/todo/todo.ts
|
|
13111
|
-
import
|
|
13112
|
-
var TodoSchema =
|
|
13113
|
-
content:
|
|
13114
|
-
status:
|
|
13115
|
-
priority:
|
|
13116
|
-
id:
|
|
13150
|
+
import z10 from "zod";
|
|
13151
|
+
var TodoSchema = z10.object({
|
|
13152
|
+
content: z10.string().describe("Brief description of the task"),
|
|
13153
|
+
status: z10.string().describe("Current status of the task: pending, in_progress, completed, cancelled"),
|
|
13154
|
+
priority: z10.string().describe("Priority level of the task: high, medium, low"),
|
|
13155
|
+
id: z10.string().describe("Unique identifier for the todo item")
|
|
13117
13156
|
});
|
|
13118
13157
|
var TodoWriteTool = new ExuluTool({
|
|
13119
13158
|
id: "todo_write",
|
|
@@ -13129,8 +13168,8 @@ var TodoWriteTool = new ExuluTool({
|
|
|
13129
13168
|
default: todowrite_default
|
|
13130
13169
|
}
|
|
13131
13170
|
],
|
|
13132
|
-
inputSchema:
|
|
13133
|
-
todos:
|
|
13171
|
+
inputSchema: z10.object({
|
|
13172
|
+
todos: z10.array(TodoSchema).describe("The updated todo list")
|
|
13134
13173
|
}),
|
|
13135
13174
|
execute: async (inputs) => {
|
|
13136
13175
|
const { sessionID, todos, user } = inputs;
|
|
@@ -13165,7 +13204,7 @@ var TodoReadTool = new ExuluTool({
|
|
|
13165
13204
|
id: "todo_read",
|
|
13166
13205
|
name: "Todo Read",
|
|
13167
13206
|
description: "Use this tool to read your todo list",
|
|
13168
|
-
inputSchema:
|
|
13207
|
+
inputSchema: z10.object({}),
|
|
13169
13208
|
type: "function",
|
|
13170
13209
|
category: "todo",
|
|
13171
13210
|
config: [
|
|
@@ -13203,6 +13242,222 @@ async function getTodos(sessionID) {
|
|
|
13203
13242
|
}
|
|
13204
13243
|
var todoTools = [TodoWriteTool, TodoReadTool];
|
|
13205
13244
|
|
|
13245
|
+
// src/templates/tools/question/questionask.txt
|
|
13246
|
+
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.
|
|
13247
|
+
|
|
13248
|
+
## When to Use This Tool
|
|
13249
|
+
|
|
13250
|
+
Use this tool when you need to:
|
|
13251
|
+
|
|
13252
|
+
1. Get user input on implementation choices - When there are multiple valid approaches and you need the user to decide
|
|
13253
|
+
2. Clarify ambiguous requirements - When the user's request could be interpreted in different ways
|
|
13254
|
+
3. Gather preferences - When you need to know the user's preference for styling, naming, architecture, etc.
|
|
13255
|
+
4. Validate assumptions - When you want to confirm your understanding before proceeding
|
|
13256
|
+
5. Offer options - When presenting multiple solutions and letting the user choose
|
|
13257
|
+
|
|
13258
|
+
## When NOT to Use This Tool
|
|
13259
|
+
|
|
13260
|
+
Skip using this tool when:
|
|
13261
|
+
1. The answer is clear from context or previous messages
|
|
13262
|
+
2. You're asking a yes/no question (use a simpler approach)
|
|
13263
|
+
3. The question requires a free-form text answer (this tool is for multiple choice only)
|
|
13264
|
+
4. You're asking for information that should be obvious from the codebase
|
|
13265
|
+
|
|
13266
|
+
## How It Works
|
|
13267
|
+
|
|
13268
|
+
- Provide a question and an array of answer option strings
|
|
13269
|
+
- The tool automatically adds "None of the above..." as a final option
|
|
13270
|
+
- Each answer option is assigned a unique ID
|
|
13271
|
+
- The question is stored with a "pending" status until the user answers
|
|
13272
|
+
- Use the Question Read tool to check if the question has been answered
|
|
13273
|
+
|
|
13274
|
+
## Examples
|
|
13275
|
+
|
|
13276
|
+
<example>
|
|
13277
|
+
User: Add authentication to my app
|
|
13278
|
+
Assistant: I need to ask the user which authentication method they prefer.
|
|
13279
|
+
*Uses Question Ask tool with:*
|
|
13280
|
+
question: "Which authentication method would you like to implement?"
|
|
13281
|
+
answerOptions: ["JWT tokens", "OAuth 2.0", "Session-based auth", "Firebase Auth"]
|
|
13282
|
+
|
|
13283
|
+
<reasoning>
|
|
13284
|
+
The assistant used the question tool because there are multiple valid authentication approaches and the user's preference will significantly impact the implementation.
|
|
13285
|
+
</reasoning>
|
|
13286
|
+
</example>
|
|
13287
|
+
|
|
13288
|
+
<example>
|
|
13289
|
+
User: Style this button
|
|
13290
|
+
Assistant: I need to know the user's styling preference.
|
|
13291
|
+
*Uses Question Ask tool with:*
|
|
13292
|
+
question: "What styling approach should I use for the button?"
|
|
13293
|
+
answerOptions: ["Tailwind CSS", "CSS Modules", "Styled Components", "Plain CSS"]
|
|
13294
|
+
|
|
13295
|
+
<reasoning>
|
|
13296
|
+
The assistant used the question tool because the codebase could support multiple styling approaches and the user's preference matters for consistency.
|
|
13297
|
+
</reasoning>
|
|
13298
|
+
</example>
|
|
13299
|
+
|
|
13300
|
+
<example>
|
|
13301
|
+
User: Add error handling
|
|
13302
|
+
Assistant: I'll implement comprehensive error handling with try-catch blocks and proper error messages.
|
|
13303
|
+
*Does not use the question tool*
|
|
13304
|
+
|
|
13305
|
+
<reasoning>
|
|
13306
|
+
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.
|
|
13307
|
+
</reasoning>
|
|
13308
|
+
</example>
|
|
13309
|
+
|
|
13310
|
+
## Answer Format
|
|
13311
|
+
|
|
13312
|
+
When you call this tool, answer options are automatically converted to this format:
|
|
13313
|
+
\`\`\`json
|
|
13314
|
+
[
|
|
13315
|
+
{ "id": "randomId1", "text": "First option" },
|
|
13316
|
+
{ "id": "randomId2", "text": "Second option" },
|
|
13317
|
+
{ "id": "randomId3", "text": "Third option" },
|
|
13318
|
+
{ "id": "randomId4", "text": "None of the above..." }
|
|
13319
|
+
]
|
|
13320
|
+
\`\`\`
|
|
13321
|
+
|
|
13322
|
+
The "None of the above..." option is always added automatically, so you don't need to include it in your answerOptions array.
|
|
13323
|
+
|
|
13324
|
+
## Reading Answers
|
|
13325
|
+
|
|
13326
|
+
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.
|
|
13327
|
+
`;
|
|
13328
|
+
|
|
13329
|
+
// src/templates/tools/question/questionread.txt
|
|
13330
|
+
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';
|
|
13331
|
+
|
|
13332
|
+
// src/templates/tools/question/question.ts
|
|
13333
|
+
import z11 from "zod";
|
|
13334
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
13335
|
+
var AnswerOptionSchema = z11.object({
|
|
13336
|
+
id: z11.string().describe("Unique identifier for the answer option"),
|
|
13337
|
+
text: z11.string().describe("The text of the answer option")
|
|
13338
|
+
});
|
|
13339
|
+
var _QuestionSchema = z11.object({
|
|
13340
|
+
id: z11.string().describe("Unique identifier for the question"),
|
|
13341
|
+
question: z11.string().describe("The question to ask the user"),
|
|
13342
|
+
answerOptions: z11.array(AnswerOptionSchema).describe("Array of possible answer options"),
|
|
13343
|
+
selectedAnswerId: z11.string().optional().describe("The ID of the answer option selected by the user"),
|
|
13344
|
+
status: z11.enum(["pending", "answered"]).describe("Status of the question: pending or answered")
|
|
13345
|
+
});
|
|
13346
|
+
var QuestionAskTool = new ExuluTool({
|
|
13347
|
+
id: "question_ask",
|
|
13348
|
+
name: "Question Ask",
|
|
13349
|
+
description: "Use this tool to ask a question to the user with multiple choice answers",
|
|
13350
|
+
type: "function",
|
|
13351
|
+
category: "question",
|
|
13352
|
+
config: [
|
|
13353
|
+
{
|
|
13354
|
+
name: "description",
|
|
13355
|
+
description: "The description of the question tool, if set overwrites the default description.",
|
|
13356
|
+
type: "string",
|
|
13357
|
+
default: questionask_default
|
|
13358
|
+
}
|
|
13359
|
+
],
|
|
13360
|
+
inputSchema: z11.object({
|
|
13361
|
+
question: z11.string().describe("The question to ask the user"),
|
|
13362
|
+
answerOptions: z11.array(z11.string()).describe("Array of possible answer options (strings)")
|
|
13363
|
+
}),
|
|
13364
|
+
execute: async (inputs) => {
|
|
13365
|
+
const { sessionID, question, answerOptions, user } = inputs;
|
|
13366
|
+
if (!user) {
|
|
13367
|
+
throw new Error(
|
|
13368
|
+
"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."
|
|
13369
|
+
);
|
|
13370
|
+
}
|
|
13371
|
+
if (!sessionID) {
|
|
13372
|
+
throw new Error(
|
|
13373
|
+
"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."
|
|
13374
|
+
);
|
|
13375
|
+
}
|
|
13376
|
+
const session = await getSession({ sessionID });
|
|
13377
|
+
if (!session?.id) {
|
|
13378
|
+
throw new Error(
|
|
13379
|
+
"Session with ID " + sessionID + " not found in the question ask tool."
|
|
13380
|
+
);
|
|
13381
|
+
}
|
|
13382
|
+
const hasAccessToSession = await checkRecordAccess(session, "read", user);
|
|
13383
|
+
if (!hasAccessToSession) {
|
|
13384
|
+
throw new Error("You don't have access to this session " + session.id + ".");
|
|
13385
|
+
}
|
|
13386
|
+
const answerOptionsWithIds = answerOptions.map((text) => ({
|
|
13387
|
+
id: randomUUID6(),
|
|
13388
|
+
text
|
|
13389
|
+
}));
|
|
13390
|
+
answerOptionsWithIds.push({
|
|
13391
|
+
id: randomUUID6(),
|
|
13392
|
+
text: "None of the above..."
|
|
13393
|
+
});
|
|
13394
|
+
const newQuestion = {
|
|
13395
|
+
id: randomUUID6(),
|
|
13396
|
+
question,
|
|
13397
|
+
answerOptions: answerOptionsWithIds,
|
|
13398
|
+
status: "pending"
|
|
13399
|
+
};
|
|
13400
|
+
await addQuestion({
|
|
13401
|
+
session,
|
|
13402
|
+
question: newQuestion
|
|
13403
|
+
});
|
|
13404
|
+
return {
|
|
13405
|
+
result: JSON.stringify(
|
|
13406
|
+
{
|
|
13407
|
+
questionId: newQuestion.id,
|
|
13408
|
+
question: newQuestion.question,
|
|
13409
|
+
answerOptions: newQuestion.answerOptions,
|
|
13410
|
+
status: newQuestion.status
|
|
13411
|
+
},
|
|
13412
|
+
null,
|
|
13413
|
+
2
|
|
13414
|
+
)
|
|
13415
|
+
};
|
|
13416
|
+
}
|
|
13417
|
+
});
|
|
13418
|
+
var QuestionReadTool = new ExuluTool({
|
|
13419
|
+
id: "question_read",
|
|
13420
|
+
name: "Question Read",
|
|
13421
|
+
description: "Use this tool to read questions and their answers",
|
|
13422
|
+
inputSchema: z11.object({}),
|
|
13423
|
+
type: "function",
|
|
13424
|
+
category: "question",
|
|
13425
|
+
config: [
|
|
13426
|
+
{
|
|
13427
|
+
name: "description",
|
|
13428
|
+
description: "The description of the question read tool, if set overwrites the default description.",
|
|
13429
|
+
type: "string",
|
|
13430
|
+
default: questionread_default
|
|
13431
|
+
}
|
|
13432
|
+
],
|
|
13433
|
+
execute: async (inputs) => {
|
|
13434
|
+
const { sessionID } = inputs;
|
|
13435
|
+
const questions = await getQuestions(sessionID);
|
|
13436
|
+
return {
|
|
13437
|
+
result: JSON.stringify(questions, null, 2)
|
|
13438
|
+
};
|
|
13439
|
+
}
|
|
13440
|
+
});
|
|
13441
|
+
async function addQuestion(input) {
|
|
13442
|
+
const metadata = input.session.metadata ?? {};
|
|
13443
|
+
metadata["questions"] ??= [];
|
|
13444
|
+
metadata["questions"].push(input.question);
|
|
13445
|
+
const { db: db2 } = await postgresClient();
|
|
13446
|
+
await db2.from("agent_sessions").where({ id: input.session.id }).update({
|
|
13447
|
+
metadata
|
|
13448
|
+
});
|
|
13449
|
+
return input.session;
|
|
13450
|
+
}
|
|
13451
|
+
async function getQuestions(sessionID) {
|
|
13452
|
+
const { db: db2 } = await postgresClient();
|
|
13453
|
+
const session = await db2.from("agent_sessions").where({ id: sessionID }).first();
|
|
13454
|
+
if (!session) {
|
|
13455
|
+
throw new Error("Session not found for session ID: " + sessionID);
|
|
13456
|
+
}
|
|
13457
|
+
return session.metadata?.questions ?? [];
|
|
13458
|
+
}
|
|
13459
|
+
var questionTools = [QuestionAskTool, QuestionReadTool];
|
|
13460
|
+
|
|
13206
13461
|
// src/templates/tools/perplexity.ts
|
|
13207
13462
|
import z12 from "zod";
|
|
13208
13463
|
import Perplexity from "@perplexity-ai/perplexity_ai";
|
|
@@ -13313,14 +13568,29 @@ var isValidPostgresName = (id) => {
|
|
|
13313
13568
|
|
|
13314
13569
|
// src/exulu/app/index.ts
|
|
13315
13570
|
var isDev = process.env.NODE_ENV !== "production";
|
|
13571
|
+
var lineLimitFormat = winston2.format((info) => {
|
|
13572
|
+
if (typeof info.message === "string") {
|
|
13573
|
+
const lines = info.message.split("\n");
|
|
13574
|
+
if (lines.length > 50) {
|
|
13575
|
+
const truncatedLines = lines.slice(0, 50);
|
|
13576
|
+
truncatedLines.push(`... (${lines.length - 50} more lines omitted)`);
|
|
13577
|
+
info.message = truncatedLines.join("\n");
|
|
13578
|
+
}
|
|
13579
|
+
}
|
|
13580
|
+
return info;
|
|
13581
|
+
});
|
|
13316
13582
|
var consoleTransport = new winston2.transports.Console({
|
|
13317
13583
|
format: isDev ? winston2.format.combine(
|
|
13584
|
+
lineLimitFormat(),
|
|
13318
13585
|
winston2.format.colorize(),
|
|
13319
13586
|
winston2.format.timestamp({ format: "HH:mm:ss" }),
|
|
13320
13587
|
winston2.format.printf(({ timestamp, level, message }) => {
|
|
13321
13588
|
return `${timestamp} [${level}] ${message}`;
|
|
13322
13589
|
})
|
|
13323
|
-
) : winston2.format.
|
|
13590
|
+
) : winston2.format.combine(
|
|
13591
|
+
lineLimitFormat(),
|
|
13592
|
+
winston2.format.json()
|
|
13593
|
+
)
|
|
13324
13594
|
});
|
|
13325
13595
|
var formatArg = (arg) => typeof arg === "object" ? util.inspect(arg, { depth: null, colors: isDev }) : String(arg);
|
|
13326
13596
|
var createLogMethod = (logger, logLevel) => {
|
|
@@ -13390,6 +13660,7 @@ var ExuluApp2 = class {
|
|
|
13390
13660
|
this._tools = [
|
|
13391
13661
|
...tools ?? [],
|
|
13392
13662
|
...todoTools,
|
|
13663
|
+
...questionTools,
|
|
13393
13664
|
...perplexityTools,
|
|
13394
13665
|
// Add contexts as tools
|
|
13395
13666
|
...Object.values(contexts || {}).map((context) => context.tool()).filter(Boolean)
|
|
@@ -16169,12 +16440,12 @@ Or manually run the setup script:
|
|
|
16169
16440
|
}
|
|
16170
16441
|
|
|
16171
16442
|
// ee/python/documents/processing/doc_processor.ts
|
|
16172
|
-
import * as
|
|
16443
|
+
import * as fs3 from "fs";
|
|
16173
16444
|
import * as path from "path";
|
|
16174
16445
|
import { generateText as generateText3, Output as Output3 } from "ai";
|
|
16175
16446
|
import { z as z13 } from "zod";
|
|
16176
16447
|
import pLimit from "p-limit";
|
|
16177
|
-
import { randomUUID as
|
|
16448
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
16178
16449
|
import * as mammoth from "mammoth";
|
|
16179
16450
|
import TurndownService from "turndown";
|
|
16180
16451
|
import WordExtractor from "word-extractor";
|
|
@@ -16394,7 +16665,7 @@ function reconstructHeadings(correctedText, headingsHierarchy) {
|
|
|
16394
16665
|
return result;
|
|
16395
16666
|
}
|
|
16396
16667
|
async function validatePageWithVLM(page, imagePath, model) {
|
|
16397
|
-
const imageBuffer = await
|
|
16668
|
+
const imageBuffer = await fs3.promises.readFile(imagePath);
|
|
16398
16669
|
const imageBase64 = imageBuffer.toString("base64");
|
|
16399
16670
|
const mimeType = "image/png";
|
|
16400
16671
|
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.
|
|
@@ -16726,7 +16997,7 @@ ${setupResult.output || ""}`);
|
|
|
16726
16997
|
if (!result.success) {
|
|
16727
16998
|
throw new Error(`Document processing failed: ${result.stderr}`);
|
|
16728
16999
|
}
|
|
16729
|
-
const jsonContent = await
|
|
17000
|
+
const jsonContent = await fs3.promises.readFile(paths.json, "utf-8");
|
|
16730
17001
|
json = JSON.parse(jsonContent);
|
|
16731
17002
|
} else if (config?.processor.name === "officeparser") {
|
|
16732
17003
|
const text = await parseOfficeAsync2(buffer, {
|
|
@@ -16759,9 +17030,9 @@ ${setupResult.output || ""}`);
|
|
|
16759
17030
|
}, 10);
|
|
16760
17031
|
const parser = new LiteParse();
|
|
16761
17032
|
const screenshots = await parser.screenshot(paths.source, void 0);
|
|
16762
|
-
await
|
|
17033
|
+
await fs3.promises.mkdir(paths.images, { recursive: true });
|
|
16763
17034
|
for (const screenshot of screenshots) {
|
|
16764
|
-
await
|
|
17035
|
+
await fs3.promises.writeFile(
|
|
16765
17036
|
path.join(
|
|
16766
17037
|
paths.images,
|
|
16767
17038
|
`${screenshot.pageNum}.png`
|
|
@@ -16776,15 +17047,15 @@ ${setupResult.output || ""}`);
|
|
|
16776
17047
|
image: screenshots.find((s) => s.pageNum === page.index + 1)?.imagePath,
|
|
16777
17048
|
headings: []
|
|
16778
17049
|
}));
|
|
16779
|
-
|
|
17050
|
+
fs3.writeFileSync(paths.json, JSON.stringify(json, null, 2));
|
|
16780
17051
|
} else if (config?.processor.name === "liteparse") {
|
|
16781
17052
|
const parser = new LiteParse();
|
|
16782
17053
|
const result = await parser.parse(paths.source);
|
|
16783
17054
|
const screenshots = await parser.screenshot(paths.source, void 0);
|
|
16784
17055
|
console.log(`[EXULU] Liteparse screenshots: ${JSON.stringify(screenshots)}`);
|
|
16785
|
-
await
|
|
17056
|
+
await fs3.promises.mkdir(paths.images, { recursive: true });
|
|
16786
17057
|
for (const screenshot of screenshots) {
|
|
16787
|
-
await
|
|
17058
|
+
await fs3.promises.writeFile(path.join(paths.images, `${screenshot.pageNum}.png`), screenshot.imageBuffer);
|
|
16788
17059
|
screenshot.imagePath = path.join(paths.images, `${screenshot.pageNum}.png`);
|
|
16789
17060
|
}
|
|
16790
17061
|
json = result.pages.map((page) => ({
|
|
@@ -16792,7 +17063,7 @@ ${setupResult.output || ""}`);
|
|
|
16792
17063
|
content: page.text,
|
|
16793
17064
|
image: screenshots.find((s) => s.pageNum === page.pageNum)?.imagePath
|
|
16794
17065
|
}));
|
|
16795
|
-
|
|
17066
|
+
fs3.writeFileSync(paths.json, JSON.stringify(json, null, 2));
|
|
16796
17067
|
}
|
|
16797
17068
|
console.log(`[EXULU]
|
|
16798
17069
|
\u2713 Document processing completed successfully`);
|
|
@@ -16823,13 +17094,13 @@ ${setupResult.output || ""}`);
|
|
|
16823
17094
|
console.log(`[EXULU] Corrected: ${page.vlm_corrected_text.substring(0, 150)}...`);
|
|
16824
17095
|
});
|
|
16825
17096
|
}
|
|
16826
|
-
await
|
|
17097
|
+
await fs3.promises.writeFile(
|
|
16827
17098
|
paths.json,
|
|
16828
17099
|
JSON.stringify(json, null, 2),
|
|
16829
17100
|
"utf-8"
|
|
16830
17101
|
);
|
|
16831
17102
|
}
|
|
16832
|
-
const markdownStream =
|
|
17103
|
+
const markdownStream = fs3.createWriteStream(paths.markdown, { encoding: "utf-8" });
|
|
16833
17104
|
for (let i = 0; i < json.length; i++) {
|
|
16834
17105
|
const p = json[i];
|
|
16835
17106
|
if (!p) continue;
|
|
@@ -16845,7 +17116,7 @@ ${setupResult.output || ""}`);
|
|
|
16845
17116
|
});
|
|
16846
17117
|
console.log(`[EXULU] Validated output saved to: ${paths.json}`);
|
|
16847
17118
|
console.log(`[EXULU] Validated markdown saved to: ${paths.markdown}`);
|
|
16848
|
-
const markdown = await
|
|
17119
|
+
const markdown = await fs3.promises.readFile(paths.markdown, "utf-8");
|
|
16849
17120
|
const processedJson = json.map((e) => {
|
|
16850
17121
|
const finalContent = e.vlm_corrected_text ?? e.content;
|
|
16851
17122
|
return {
|
|
@@ -16872,11 +17143,11 @@ var loadFile = async (file, name, tempDir) => {
|
|
|
16872
17143
|
if (!fileType) {
|
|
16873
17144
|
throw new Error("[EXULU] File name does not include extension, extension is required for document processing.");
|
|
16874
17145
|
}
|
|
16875
|
-
const UUID =
|
|
17146
|
+
const UUID = randomUUID7();
|
|
16876
17147
|
let buffer;
|
|
16877
17148
|
if (Buffer.isBuffer(file)) {
|
|
16878
17149
|
filePath = path.join(tempDir, `${UUID}.${fileType}`);
|
|
16879
|
-
await
|
|
17150
|
+
await fs3.promises.writeFile(filePath, file);
|
|
16880
17151
|
buffer = file;
|
|
16881
17152
|
} else {
|
|
16882
17153
|
filePath = filePath.trim();
|
|
@@ -16884,11 +17155,11 @@ var loadFile = async (file, name, tempDir) => {
|
|
|
16884
17155
|
const response = await fetch(filePath);
|
|
16885
17156
|
const array = await response.arrayBuffer();
|
|
16886
17157
|
const tempFilePath = path.join(tempDir, `${UUID}.${fileType}`);
|
|
16887
|
-
await
|
|
17158
|
+
await fs3.promises.writeFile(tempFilePath, Buffer.from(array));
|
|
16888
17159
|
buffer = Buffer.from(array);
|
|
16889
17160
|
filePath = tempFilePath;
|
|
16890
17161
|
} else {
|
|
16891
|
-
buffer = await
|
|
17162
|
+
buffer = await fs3.promises.readFile(file);
|
|
16892
17163
|
}
|
|
16893
17164
|
}
|
|
16894
17165
|
return { filePath, fileType, buffer };
|
|
@@ -16902,13 +17173,13 @@ async function documentProcessor({
|
|
|
16902
17173
|
if (!license["advanced-document-processing"]) {
|
|
16903
17174
|
throw new Error("Advanced document processing is an enterprise feature, please add a valid Exulu enterprise license key to use it.");
|
|
16904
17175
|
}
|
|
16905
|
-
const uuid =
|
|
17176
|
+
const uuid = randomUUID7();
|
|
16906
17177
|
const tempDir = path.join(process.cwd(), "temp", uuid);
|
|
16907
17178
|
const localFilesAndFoldersToDelete = [tempDir];
|
|
16908
17179
|
console.log(`[EXULU] Temporary directory for processing document ${name}: ${tempDir}`);
|
|
16909
|
-
await
|
|
17180
|
+
await fs3.promises.mkdir(tempDir, { recursive: true });
|
|
16910
17181
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
16911
|
-
await
|
|
17182
|
+
await fs3.promises.writeFile(path.join(tempDir, "created_at.txt"), timestamp);
|
|
16912
17183
|
try {
|
|
16913
17184
|
const {
|
|
16914
17185
|
filePath,
|
|
@@ -16949,7 +17220,7 @@ async function documentProcessor({
|
|
|
16949
17220
|
if (config?.debugging?.deleteTempFiles !== false) {
|
|
16950
17221
|
for (const file2 of localFilesAndFoldersToDelete) {
|
|
16951
17222
|
try {
|
|
16952
|
-
await
|
|
17223
|
+
await fs3.promises.rm(file2, { recursive: true });
|
|
16953
17224
|
console.log(`[EXULU] Deleted file or folder: ${file2}`);
|
|
16954
17225
|
} catch (error) {
|
|
16955
17226
|
console.error(`[EXULU] Error deleting file or folder: ${file2}`, error);
|