@exulu/backend 1.45.0 → 1.46.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mcp.json +7 -0
- package/CHANGELOG.md +8 -2
- package/dist/index.cjs +2035 -932
- package/dist/index.d.cts +138 -22
- package/dist/index.d.ts +138 -22
- package/dist/index.js +2014 -912
- package/package.json +11 -5
- package/types/models/agent-session.ts +1 -0
- package/types/models/agent.ts +3 -0
package/dist/index.cjs
CHANGED
|
@@ -43,7 +43,8 @@ __export(index_exports, {
|
|
|
43
43
|
ExuluJobs: () => ExuluJobs,
|
|
44
44
|
ExuluOtel: () => ExuluOtel,
|
|
45
45
|
ExuluQueues: () => queues,
|
|
46
|
-
|
|
46
|
+
ExuluReranker: () => ExuluReranker4,
|
|
47
|
+
ExuluTool: () => ExuluTool,
|
|
47
48
|
ExuluUtils: () => ExuluUtils,
|
|
48
49
|
ExuluVariables: () => ExuluVariables,
|
|
49
50
|
db: () => db2,
|
|
@@ -122,8 +123,8 @@ var validateJob = (job) => {
|
|
|
122
123
|
|
|
123
124
|
// src/registry/classes.ts
|
|
124
125
|
var import_bullmq3 = require("bullmq");
|
|
125
|
-
var
|
|
126
|
-
var
|
|
126
|
+
var import_zod2 = require("zod");
|
|
127
|
+
var import_ai3 = require("ai");
|
|
127
128
|
|
|
128
129
|
// types/enums/statistics.ts
|
|
129
130
|
var STATISTICS_TYPE_ENUM = {
|
|
@@ -251,16 +252,6 @@ async function postgresClient() {
|
|
|
251
252
|
db: db["exulu"]
|
|
252
253
|
};
|
|
253
254
|
}
|
|
254
|
-
var refreshPostgresClient = async () => {
|
|
255
|
-
if (db["exulu"]) {
|
|
256
|
-
await db["exulu"].destroy();
|
|
257
|
-
db["exulu"] = void 0;
|
|
258
|
-
}
|
|
259
|
-
const { db: refreshed } = await postgresClient();
|
|
260
|
-
return {
|
|
261
|
-
db: refreshed
|
|
262
|
-
};
|
|
263
|
-
};
|
|
264
255
|
|
|
265
256
|
// src/registry/classes.ts
|
|
266
257
|
var import_knex5 = __toESM(require("pgvector/knex"), 1);
|
|
@@ -458,7 +449,7 @@ var import_crypto_js3 = __toESM(require("crypto-js"), 1);
|
|
|
458
449
|
// src/registry/utils/graphql.ts
|
|
459
450
|
var import_schema = require("@graphql-tools/schema");
|
|
460
451
|
var import_graphql_type_json = __toESM(require("graphql-type-json"), 1);
|
|
461
|
-
var
|
|
452
|
+
var import_graphql3 = require("graphql");
|
|
462
453
|
var import_crypto_js2 = __toESM(require("crypto-js"), 1);
|
|
463
454
|
|
|
464
455
|
// src/auth/get-token.ts
|
|
@@ -822,6 +813,11 @@ var agentSessionsSchema = {
|
|
|
822
813
|
name: "role",
|
|
823
814
|
type: "uuid"
|
|
824
815
|
},
|
|
816
|
+
{
|
|
817
|
+
name: "session_items",
|
|
818
|
+
// array of items as global ids ('<context_id>/<item_id>')
|
|
819
|
+
type: "json"
|
|
820
|
+
},
|
|
825
821
|
{
|
|
826
822
|
name: "title",
|
|
827
823
|
type: "text"
|
|
@@ -945,10 +941,19 @@ var agentsSchema = {
|
|
|
945
941
|
name: "description",
|
|
946
942
|
type: "text"
|
|
947
943
|
},
|
|
944
|
+
{
|
|
945
|
+
name: "welcomemessage",
|
|
946
|
+
type: "text"
|
|
947
|
+
},
|
|
948
948
|
{
|
|
949
949
|
name: "instructions",
|
|
950
950
|
type: "text"
|
|
951
951
|
},
|
|
952
|
+
{
|
|
953
|
+
name: "memory",
|
|
954
|
+
type: "text"
|
|
955
|
+
// allows selecting a exulu context as native memory for the agent
|
|
956
|
+
},
|
|
952
957
|
{
|
|
953
958
|
name: "providerapikey",
|
|
954
959
|
type: "text"
|
|
@@ -1560,6 +1565,795 @@ var rateLimiter = async (key, windowSeconds, limit, points) => {
|
|
|
1560
1565
|
}
|
|
1561
1566
|
};
|
|
1562
1567
|
|
|
1568
|
+
// src/registry/agentic-retrieval.ts
|
|
1569
|
+
var import_zod = require("zod");
|
|
1570
|
+
var import_ai = require("ai");
|
|
1571
|
+
var import_fs = __toESM(require("fs"), 1);
|
|
1572
|
+
|
|
1573
|
+
// src/registry/query-preprocessing.ts
|
|
1574
|
+
var import_franc = require("franc");
|
|
1575
|
+
var import_natural = __toESM(require("natural"), 1);
|
|
1576
|
+
var STEMMER_MAP = {
|
|
1577
|
+
"eng": import_natural.default.PorterStemmer,
|
|
1578
|
+
// English
|
|
1579
|
+
"deu": import_natural.default.PorterStemmerDe,
|
|
1580
|
+
// German
|
|
1581
|
+
"fra": import_natural.default.PorterStemmerFr,
|
|
1582
|
+
// French
|
|
1583
|
+
"rus": import_natural.default.PorterStemmerRu,
|
|
1584
|
+
// Russian
|
|
1585
|
+
"ita": import_natural.default.PorterStemmerIt,
|
|
1586
|
+
// Italian
|
|
1587
|
+
"nld": import_natural.default.PorterStemmerNl,
|
|
1588
|
+
// Dutch
|
|
1589
|
+
"por": import_natural.default.PorterStemmerPt,
|
|
1590
|
+
// Portuguese
|
|
1591
|
+
"spa": import_natural.default.PorterStemmerEs,
|
|
1592
|
+
// Spanish
|
|
1593
|
+
"swe": import_natural.default.PorterStemmerSv,
|
|
1594
|
+
// Swedish
|
|
1595
|
+
"nor": import_natural.default.PorterStemmerNo,
|
|
1596
|
+
// Norwegian
|
|
1597
|
+
"dan": import_natural.default.PorterStemmer
|
|
1598
|
+
// Danish (fallback to English)
|
|
1599
|
+
};
|
|
1600
|
+
var COMMON_LANGUAGES = ["eng", "deu", "fra", "spa", "ita", "por", "rus", "nld"];
|
|
1601
|
+
function detectQueryLanguage(query, minLength = 10) {
|
|
1602
|
+
const cleaned = query.trim();
|
|
1603
|
+
if (cleaned.length < minLength) {
|
|
1604
|
+
if (/[äöüßÄÖÜ]/.test(cleaned)) {
|
|
1605
|
+
return "deu";
|
|
1606
|
+
}
|
|
1607
|
+
if (/[àâæçéèêëîïôùûüÿœÀÂÆÇÉÈÊËÎÏÔÙÛÜŸŒ]/.test(cleaned)) {
|
|
1608
|
+
return "fra";
|
|
1609
|
+
}
|
|
1610
|
+
if (/[áéíóúñüÁÉÍÓÚÑÜ¿¡]/.test(cleaned)) {
|
|
1611
|
+
return "spa";
|
|
1612
|
+
}
|
|
1613
|
+
return "eng";
|
|
1614
|
+
}
|
|
1615
|
+
const detected = (0, import_franc.franc)(cleaned, { only: COMMON_LANGUAGES, minLength: 3 });
|
|
1616
|
+
if (detected === "und") {
|
|
1617
|
+
return "eng";
|
|
1618
|
+
}
|
|
1619
|
+
return detected;
|
|
1620
|
+
}
|
|
1621
|
+
function stemWord(word, languageCode) {
|
|
1622
|
+
const stemmer = STEMMER_MAP[languageCode] || import_natural.default.PorterStemmer;
|
|
1623
|
+
const cleaned = word.replace(/[^\p{L}\p{N}]/gu, "").toLowerCase();
|
|
1624
|
+
if (!cleaned) {
|
|
1625
|
+
return word;
|
|
1626
|
+
}
|
|
1627
|
+
try {
|
|
1628
|
+
return stemmer.stem(cleaned);
|
|
1629
|
+
} catch (error) {
|
|
1630
|
+
console.warn(`[EXULU] Error stemming word "${word}":`, error);
|
|
1631
|
+
return cleaned;
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
function preprocessQuery(query, options = {}) {
|
|
1635
|
+
const {
|
|
1636
|
+
enableStemming = true,
|
|
1637
|
+
detectLanguage = true,
|
|
1638
|
+
preserveCase = false,
|
|
1639
|
+
minDetectionLength = 10
|
|
1640
|
+
} = options;
|
|
1641
|
+
const language = detectLanguage ? detectQueryLanguage(query, minDetectionLength) : "eng";
|
|
1642
|
+
console.log(`[EXULU] Query preprocessing - Detected language: ${language} for query: "${query}"`);
|
|
1643
|
+
if (!enableStemming) {
|
|
1644
|
+
return {
|
|
1645
|
+
original: query,
|
|
1646
|
+
processed: query,
|
|
1647
|
+
language,
|
|
1648
|
+
stemmed: false
|
|
1649
|
+
};
|
|
1650
|
+
}
|
|
1651
|
+
const words = query.split(/\s+/);
|
|
1652
|
+
const stemmedWords = words.map((word) => {
|
|
1653
|
+
const stemmed = stemWord(word, language);
|
|
1654
|
+
if (preserveCase && word[0] === word[0].toUpperCase()) {
|
|
1655
|
+
return stemmed.charAt(0).toUpperCase() + stemmed.slice(1);
|
|
1656
|
+
}
|
|
1657
|
+
return stemmed;
|
|
1658
|
+
});
|
|
1659
|
+
const processed = stemmedWords.join(" ");
|
|
1660
|
+
console.log(`[EXULU] Query preprocessing - Original: "${query}" \u2192 Stemmed: "${processed}"`);
|
|
1661
|
+
return {
|
|
1662
|
+
original: query,
|
|
1663
|
+
processed,
|
|
1664
|
+
language,
|
|
1665
|
+
stemmed: true
|
|
1666
|
+
};
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
// src/registry/agentic-retrieval.ts
|
|
1670
|
+
var import_zod_to_json_schema = require("zod-to-json-schema");
|
|
1671
|
+
async function withRetry(generateFn, maxRetries = 3) {
|
|
1672
|
+
let lastError;
|
|
1673
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
1674
|
+
try {
|
|
1675
|
+
return await generateFn();
|
|
1676
|
+
} catch (error) {
|
|
1677
|
+
lastError = error;
|
|
1678
|
+
console.error(`[EXULU] generateText attempt ${attempt} failed:`, error);
|
|
1679
|
+
if (attempt === maxRetries) {
|
|
1680
|
+
throw error;
|
|
1681
|
+
}
|
|
1682
|
+
await new Promise((resolve) => setTimeout(resolve, Math.pow(2, attempt) * 1e3));
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
throw lastError;
|
|
1686
|
+
}
|
|
1687
|
+
var baseInstructions = `
|
|
1688
|
+
You are an intelligent information retrieval assistant with access to multiple knowledge bases. You MUST do all your reasoning and
|
|
1689
|
+
outputs in the same language as the user query.
|
|
1690
|
+
|
|
1691
|
+
Your goal is to efficiently retrieve the most relevant information to answer user queries. You don't answer the question yourself, you only
|
|
1692
|
+
retrieve the information and return it, another tool will answer the question based on the information you retrieve.
|
|
1693
|
+
|
|
1694
|
+
CRITICAL: STRUCTURED REASONING PROCESS
|
|
1695
|
+
You MUST follow this structured thinking process for EVERY step. Keep outputs VERY SHORT and in the same language as the user query - ideally one line each:
|
|
1696
|
+
|
|
1697
|
+
1. BEFORE EACH TOOL CALL - Output your search strategy in ONE CONCISE LINE:
|
|
1698
|
+
Format: \u{1F50D} [What you'll search]: [Tool(s)] [Method/Filters] \u2192 [Expected outcome]
|
|
1699
|
+
|
|
1700
|
+
Examples:
|
|
1701
|
+
- "\u{1F50D} Searching for WPA-2 config in DPO-1 docs: search_items_products (name:DPO-1) \u2192 search_chunks_products (hybrid) \u2192 Expecting setup instructions"
|
|
1702
|
+
- "\u{1F50D} Listing tourism items: search_items_hamburg (name contains 'tourism') \u2192 Document list"
|
|
1703
|
+
- "\u{1F50D} Broad search for elevator data: search_chunks_* (hybrid, all contexts) \u2192 Statistical data"
|
|
1704
|
+
|
|
1705
|
+
2. AFTER RECEIVING RESULTS - Reflect in ONE CONCISE LINE:
|
|
1706
|
+
Format: \u{1F4AD} [Count] results | [Relevance] | [Next action]
|
|
1707
|
+
|
|
1708
|
+
Examples:
|
|
1709
|
+
- "\u{1F4AD} 5 chunks found | Highly relevant WPA-2 instructions | Sufficient - returning results"
|
|
1710
|
+
- "\u{1F4AD} 12 items with 'tourism' | Complete list | Done"
|
|
1711
|
+
- "\u{1F4AD} 0 results in context_1 | Not found | Trying remaining contexts"
|
|
1712
|
+
|
|
1713
|
+
IMPORTANT:
|
|
1714
|
+
- ONE LINE per reasoning block - be extremely concise
|
|
1715
|
+
- Use the same language as the user query
|
|
1716
|
+
- Focus only on: what, tool/method, outcome
|
|
1717
|
+
|
|
1718
|
+
Choose your strategy based on the query type:
|
|
1719
|
+
|
|
1720
|
+
FOR LISTING QUERIES (e.g., "list all documents about X" or "what items mention Y"):
|
|
1721
|
+
|
|
1722
|
+
You have two options, you can use the search_items_by_name_[context_id] tool to find the items by name, if it is likely that
|
|
1723
|
+
the name includes a specific keyword, for example when looking for documentation regarding a specific product. Or you can use
|
|
1724
|
+
the search_[context_id] tool with the parameter "includeContent: false" to search inside the actual content of all or specific
|
|
1725
|
+
items, if you are looking for any items that might be relevant or contain information relevant to the query, but do not need
|
|
1726
|
+
the actual content in your response.
|
|
1727
|
+
|
|
1728
|
+
IMPORTANT: For listing queries, NEVER set includeContent: true unless you need the actual text content to answer the question
|
|
1729
|
+
|
|
1730
|
+
FOR TARGETED QUERIES ABOUT SPECIFIC ITEMS (e.g., "how do I configure WPA-2 on my DPO-1? router" or "search in Document ABC"):
|
|
1731
|
+
TWO-STEP PATTERN:
|
|
1732
|
+
1. STEP 1 - Find the items:
|
|
1733
|
+
As described above, you have two options, you can use the search_items_by_name_[context_id] tool to find the items by name, if it is likely that
|
|
1734
|
+
the name includes a specific keyword, for example when looking for documentation regarding a specific product. Or you can use
|
|
1735
|
+
the search_[context_id] tool with the parameter "includeContent: false" to search inside the actual content of all or specific
|
|
1736
|
+
items, if you are looking for any items that might be relevant or contain information relevant to the query, but do not need
|
|
1737
|
+
the actual content in your response.
|
|
1738
|
+
2. STEP 2 - If step 1 returned any items, search for relevant information within those items: Use search_[context_id] with
|
|
1739
|
+
hybrid search method. Example: search_[context_id] with hybrid search method and parameters item_name: 'DPO-1'
|
|
1740
|
+
This searches only within the specific items you found. If no items were found in step 1 you should still
|
|
1741
|
+
do the search_[context_id] but without pre-filtering them.
|
|
1742
|
+
|
|
1743
|
+
Note that the query input for the search_[context_id] tools should not be used to search for things like "Page 2",
|
|
1744
|
+
"Section 3", "Chapter 4", etc. but rather be used to search for specific information or answers within the
|
|
1745
|
+
content of the items.
|
|
1746
|
+
|
|
1747
|
+
ONLY say "no information found" if you have:
|
|
1748
|
+
\u2713 Searched ALL available contexts (not just likely ones)
|
|
1749
|
+
\u2713 Tried hybrid, keyword, AND semantic search in each context
|
|
1750
|
+
\u2713 Tried variations of the search terms
|
|
1751
|
+
\u2713 Confirmed zero results across all attempts
|
|
1752
|
+
|
|
1753
|
+
IMPORTANT OPTIMIZATION RULES:
|
|
1754
|
+
|
|
1755
|
+
\u26A0\uFE0F CRITICAL: Always set includeContent: false when:
|
|
1756
|
+
- User asks for a list, overview, or count of documents/items
|
|
1757
|
+
- User wants to know "which documents" or "what items" without needing their content
|
|
1758
|
+
- You only need item names/metadata to answer the query
|
|
1759
|
+
- You can ALWAYS fetch the actual content later if needed with includeContent: true
|
|
1760
|
+
|
|
1761
|
+
\u2713 Only set includeContent: true (or use default) when:
|
|
1762
|
+
- User needs specific information, details, or answers from the content
|
|
1763
|
+
- User asks "how to", "what does it say about", "explain", etc.
|
|
1764
|
+
- You need the actual text to answer the question
|
|
1765
|
+
|
|
1766
|
+
Search Method Selection (for search_* tools):
|
|
1767
|
+
- Use 'hybrid' method by default for best relevance (combines semantic understanding + keyword matching)
|
|
1768
|
+
- Use 'keyword' method for exact term matching (technical terms, product names, IDs, specific phrases)
|
|
1769
|
+
- Use 'semantic' method for conceptual queries where synonyms and paraphrasing matter most
|
|
1770
|
+
|
|
1771
|
+
Filtering and Limits:
|
|
1772
|
+
- Limit results appropriately (don't retrieve more than needed)
|
|
1773
|
+
`;
|
|
1774
|
+
function createCustomAgenticRetrievalToolLoopAgent({
|
|
1775
|
+
language,
|
|
1776
|
+
tools,
|
|
1777
|
+
model,
|
|
1778
|
+
customInstructions
|
|
1779
|
+
}) {
|
|
1780
|
+
return {
|
|
1781
|
+
generate: async function* ({
|
|
1782
|
+
reranker,
|
|
1783
|
+
query,
|
|
1784
|
+
onFinish
|
|
1785
|
+
}) {
|
|
1786
|
+
import_fs.default.writeFileSync("reranker.json", JSON.stringify(reranker, null, 2));
|
|
1787
|
+
let finished = false;
|
|
1788
|
+
let maxSteps = 2;
|
|
1789
|
+
let currentStep = 0;
|
|
1790
|
+
const output = {
|
|
1791
|
+
reasoning: [],
|
|
1792
|
+
chunks: [],
|
|
1793
|
+
usage: [],
|
|
1794
|
+
totalTokens: 0
|
|
1795
|
+
};
|
|
1796
|
+
let dynamicTools = {};
|
|
1797
|
+
while (!finished && currentStep < maxSteps) {
|
|
1798
|
+
currentStep++;
|
|
1799
|
+
console.log("[EXULU] Agentic retrieval step", currentStep);
|
|
1800
|
+
import_fs.default.writeFileSync(currentStep - 1 + "_step_output.json", JSON.stringify(output, null, 2));
|
|
1801
|
+
const systemPrompt = `
|
|
1802
|
+
${baseInstructions}
|
|
1803
|
+
|
|
1804
|
+
AVAILABLE TOOLS:
|
|
1805
|
+
${Object.keys({ ...tools, ...dynamicTools }).map((tool3) => `
|
|
1806
|
+
<tool_${tool3}>
|
|
1807
|
+
<name>${tool3}</name>
|
|
1808
|
+
<description>${tools[tool3]?.description}</description>
|
|
1809
|
+
${tools[tool3]?.inputSchema ? `
|
|
1810
|
+
<inputSchema>${(0, import_zod_to_json_schema.zodToJsonSchema)(tools[tool3]?.inputSchema)}</inputSchema>
|
|
1811
|
+
` : ""}
|
|
1812
|
+
</tool_${tool3}>
|
|
1813
|
+
`).join("\n\n")}
|
|
1814
|
+
|
|
1815
|
+
${customInstructions ? `
|
|
1816
|
+
CUSTOM INSTRUCTIONS: ${customInstructions}` : ""}
|
|
1817
|
+
`;
|
|
1818
|
+
import_fs.default.writeFileSync("system_prompt_" + currentStep + ".txt", systemPrompt);
|
|
1819
|
+
let reasoningOutput;
|
|
1820
|
+
try {
|
|
1821
|
+
reasoningOutput = await withRetry(async () => {
|
|
1822
|
+
console.log("[EXULU] Generating reasoning for step", currentStep);
|
|
1823
|
+
return await (0, import_ai.generateText)({
|
|
1824
|
+
model,
|
|
1825
|
+
output: import_ai.Output.object({
|
|
1826
|
+
schema: import_zod.z.object({
|
|
1827
|
+
reasoning: import_zod.z.string().describe("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."),
|
|
1828
|
+
finished: import_zod.z.boolean().describe("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.")
|
|
1829
|
+
})
|
|
1830
|
+
}),
|
|
1831
|
+
toolChoice: "none",
|
|
1832
|
+
system: systemPrompt,
|
|
1833
|
+
prompt: `
|
|
1834
|
+
Original query: ${query}
|
|
1835
|
+
|
|
1836
|
+
Previous step reasoning and output:
|
|
1837
|
+
|
|
1838
|
+
${output.reasoning.map((reasoning, index) => `
|
|
1839
|
+
<step_${index + 1}>
|
|
1840
|
+
<reasoning>
|
|
1841
|
+
${reasoning.text}
|
|
1842
|
+
</reasoning>
|
|
1843
|
+
|
|
1844
|
+
${reasoning.chunks ? `<retrieved_chunks>
|
|
1845
|
+
${reasoning.chunks.map((chunk) => `
|
|
1846
|
+
${chunk.item_name} - ${chunk.item_id} - ${chunk.context} - ${chunk.chunk_id} - ${chunk.chunk_index}
|
|
1847
|
+
`).join("\n")}
|
|
1848
|
+
</retrieved_chunks>` : ""}
|
|
1849
|
+
|
|
1850
|
+
${reasoning.tools ? `
|
|
1851
|
+
<used_tools>
|
|
1852
|
+
${reasoning.tools.map((tool3) => `
|
|
1853
|
+
${tool3.name} - ${tool3.id} - ${tool3.input}
|
|
1854
|
+
`).join("\n")}
|
|
1855
|
+
</used_tools>
|
|
1856
|
+
` : ""}
|
|
1857
|
+
|
|
1858
|
+
</step_${index + 1}>
|
|
1859
|
+
`).join("\n")}
|
|
1860
|
+
`,
|
|
1861
|
+
stopWhen: [(0, import_ai.stepCountIs)(1)]
|
|
1862
|
+
});
|
|
1863
|
+
});
|
|
1864
|
+
console.log("[EXULU] Reasoning generated for step", currentStep);
|
|
1865
|
+
} catch (error) {
|
|
1866
|
+
console.error("[EXULU] Failed to generate reasoning after 3 retries:", error);
|
|
1867
|
+
throw error;
|
|
1868
|
+
}
|
|
1869
|
+
const {
|
|
1870
|
+
reasoning: briefing,
|
|
1871
|
+
finished: finished2
|
|
1872
|
+
} = reasoningOutput?.output || {};
|
|
1873
|
+
const { usage: reasoningUsage } = reasoningOutput || {};
|
|
1874
|
+
output.usage.push(reasoningUsage);
|
|
1875
|
+
if (finished2) {
|
|
1876
|
+
console.log("[EXULU] Agentic retrieval finished for step", currentStep);
|
|
1877
|
+
break;
|
|
1878
|
+
}
|
|
1879
|
+
let toolOutput;
|
|
1880
|
+
try {
|
|
1881
|
+
toolOutput = await withRetry(async () => {
|
|
1882
|
+
console.log("[EXULU] Generating tool output for step", currentStep);
|
|
1883
|
+
return await (0, import_ai.generateText)({
|
|
1884
|
+
model,
|
|
1885
|
+
tools: { ...tools, ...dynamicTools },
|
|
1886
|
+
toolChoice: "required",
|
|
1887
|
+
prompt: `${briefing}`,
|
|
1888
|
+
stopWhen: [(0, import_ai.stepCountIs)(1)]
|
|
1889
|
+
});
|
|
1890
|
+
});
|
|
1891
|
+
console.log("[EXULU] Tool output generated for step", currentStep);
|
|
1892
|
+
} catch (error) {
|
|
1893
|
+
console.error("[EXULU] Failed to generate tool output after 3 retries:", error);
|
|
1894
|
+
throw error;
|
|
1895
|
+
}
|
|
1896
|
+
const toolResults = toolOutput.toolResults;
|
|
1897
|
+
const toolCalls = toolOutput.toolCalls;
|
|
1898
|
+
let chunks = [];
|
|
1899
|
+
console.log("[EXULU] Processing tool results for step", currentStep);
|
|
1900
|
+
if (Array.isArray(toolResults)) {
|
|
1901
|
+
chunks = toolResults.map((result) => {
|
|
1902
|
+
import_fs.default.writeFileSync(result.toolCallId + "_tool_call_result.json", JSON.stringify(result, null, 2));
|
|
1903
|
+
let chunks2 = [];
|
|
1904
|
+
if (typeof result.output === "string") {
|
|
1905
|
+
chunks2 = JSON.parse(result.output);
|
|
1906
|
+
} else {
|
|
1907
|
+
chunks2 = result.output;
|
|
1908
|
+
}
|
|
1909
|
+
console.log("[EXULU] Chunks", chunks2);
|
|
1910
|
+
return chunks2.map((chunk) => ({
|
|
1911
|
+
...chunk,
|
|
1912
|
+
context: {
|
|
1913
|
+
name: chunk.context ? chunk.context.replaceAll("_", " ") : "",
|
|
1914
|
+
id: chunk.context
|
|
1915
|
+
}
|
|
1916
|
+
}));
|
|
1917
|
+
}).flat();
|
|
1918
|
+
}
|
|
1919
|
+
if (chunks) {
|
|
1920
|
+
if (reranker) {
|
|
1921
|
+
console.log("[EXULU] Reranking chunks for step, using reranker", reranker.name + "(" + reranker.id + ")", "for step", currentStep, " for " + chunks?.length + " chunks");
|
|
1922
|
+
import_fs.default.writeFileSync("chunks-before-reranking.json", JSON.stringify(chunks, null, 2));
|
|
1923
|
+
chunks = await reranker.run(query, chunks);
|
|
1924
|
+
import_fs.default.writeFileSync("chunks-after-reranking.json", JSON.stringify(chunks, null, 2));
|
|
1925
|
+
console.log("[EXULU] Reranked chunks for step", currentStep, "using reranker", reranker.name + "(" + reranker.id + ")", " resulting in ", chunks?.length + " chunks");
|
|
1926
|
+
}
|
|
1927
|
+
output.chunks.push(...chunks);
|
|
1928
|
+
}
|
|
1929
|
+
console.log("[EXULU] Pushing reasoning for step", currentStep);
|
|
1930
|
+
const exludedContent = toolCalls?.some(
|
|
1931
|
+
(toolCall) => toolCall.input?.includeContent === false || toolCall.toolName.startsWith("search_items_by_name")
|
|
1932
|
+
);
|
|
1933
|
+
for (const chunk of chunks) {
|
|
1934
|
+
const getMoreToolName = sanitizeToolName("get_more_content_from_" + chunk.item_name);
|
|
1935
|
+
const { db: db3 } = await postgresClient();
|
|
1936
|
+
if (!dynamicTools[getMoreToolName]) {
|
|
1937
|
+
const chunksTable = getChunksTableName(chunk.context.id);
|
|
1938
|
+
const countChunksQuery = await db3.from(chunksTable).where(
|
|
1939
|
+
{ source: chunk.item_id }
|
|
1940
|
+
).count("id");
|
|
1941
|
+
const chunksCount = Number(countChunksQuery[0].count) || 0;
|
|
1942
|
+
if (chunksCount > 1) {
|
|
1943
|
+
dynamicTools[getMoreToolName] = (0, import_ai.tool)({
|
|
1944
|
+
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.`,
|
|
1945
|
+
inputSchema: import_zod.z.object({
|
|
1946
|
+
from_index: import_zod.z.number().default(1).describe("The index of the chunk to start from."),
|
|
1947
|
+
to_index: import_zod.z.number().max(chunksCount).describe("The index of the chunk to end at, max is " + chunksCount)
|
|
1948
|
+
}),
|
|
1949
|
+
execute: async ({ from_index, to_index }) => {
|
|
1950
|
+
const chunks2 = await db3(chunksTable).select("*").where("source", chunk.item_id).whereBetween("chunk_index", [from_index, to_index]).orderBy("chunk_index", "asc");
|
|
1951
|
+
return JSON.stringify(chunks2.map((resultChunk) => ({
|
|
1952
|
+
chunk_content: resultChunk.content,
|
|
1953
|
+
chunk_index: resultChunk.chunk_index,
|
|
1954
|
+
chunk_id: resultChunk.id,
|
|
1955
|
+
chunk_source: resultChunk.source,
|
|
1956
|
+
chunk_metadata: resultChunk.metadata,
|
|
1957
|
+
item_id: chunk.item_id,
|
|
1958
|
+
item_name: chunk.item_name,
|
|
1959
|
+
context: chunk.context?.id
|
|
1960
|
+
})), null, 2);
|
|
1961
|
+
}
|
|
1962
|
+
});
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
if (exludedContent) {
|
|
1966
|
+
const getContentToolName = sanitizeToolName("get_" + chunk.item_name + "_page_" + chunk.chunk_index + "_content");
|
|
1967
|
+
dynamicTools[getContentToolName] = (0, import_ai.tool)({
|
|
1968
|
+
description: `Get the content of the page ${chunk.chunk_index} for the item ${chunk.item_name}`,
|
|
1969
|
+
inputSchema: import_zod.z.object({
|
|
1970
|
+
reasoning: import_zod.z.string().describe("The reasoning for why you need to get the content of the page.")
|
|
1971
|
+
}),
|
|
1972
|
+
execute: async ({ reasoning }) => {
|
|
1973
|
+
const { db: db4 } = await postgresClient();
|
|
1974
|
+
const chunksTable = getChunksTableName(chunk.context.id);
|
|
1975
|
+
const resultChunks = await db4(chunksTable).select("*").where("id", chunk.chunk_id).limit(1);
|
|
1976
|
+
if (!resultChunks || !resultChunks[0]) {
|
|
1977
|
+
return null;
|
|
1978
|
+
}
|
|
1979
|
+
return JSON.stringify([{
|
|
1980
|
+
reasoning,
|
|
1981
|
+
chunk_content: resultChunks[0].content,
|
|
1982
|
+
chunk_index: resultChunks[0].chunk_index,
|
|
1983
|
+
chunk_id: resultChunks[0].id,
|
|
1984
|
+
chunk_source: resultChunks[0].source,
|
|
1985
|
+
chunk_metadata: resultChunks[0].metadata,
|
|
1986
|
+
chunk_created_at: resultChunks[0].chunk_created_at,
|
|
1987
|
+
item_id: chunk.item_id,
|
|
1988
|
+
item_name: chunk.item_name,
|
|
1989
|
+
context: chunk.context?.id
|
|
1990
|
+
}], null, 2);
|
|
1991
|
+
}
|
|
1992
|
+
});
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
output.reasoning.push({
|
|
1996
|
+
text: briefing,
|
|
1997
|
+
chunks: chunks || [],
|
|
1998
|
+
tools: toolCalls?.length > 0 ? toolCalls.map((toolCall) => ({
|
|
1999
|
+
name: toolCall.toolName,
|
|
2000
|
+
id: toolCall.toolCallId,
|
|
2001
|
+
input: toolCall.input,
|
|
2002
|
+
output: chunks
|
|
2003
|
+
})) : []
|
|
2004
|
+
});
|
|
2005
|
+
const { usage: toolUsage } = toolOutput || {};
|
|
2006
|
+
console.log("[EXULU] Pushing tool usage for step", currentStep);
|
|
2007
|
+
output.usage.push(toolUsage);
|
|
2008
|
+
console.log(`[EXULU] Agentic retrieval step ${currentStep} completed`);
|
|
2009
|
+
console.log("[EXULU] Agentic retrieval step output", output);
|
|
2010
|
+
yield output;
|
|
2011
|
+
}
|
|
2012
|
+
const totalTokens = output.usage.reduce((acc, usage) => acc + (usage.totalTokens || 0), 0);
|
|
2013
|
+
output.totalTokens = totalTokens;
|
|
2014
|
+
console.log("[EXULU] Agentic retrieval finished", output);
|
|
2015
|
+
onFinish(output);
|
|
2016
|
+
}
|
|
2017
|
+
};
|
|
2018
|
+
}
|
|
2019
|
+
var createAgenticRetrievalAgent = ({
|
|
2020
|
+
contexts,
|
|
2021
|
+
user,
|
|
2022
|
+
role,
|
|
2023
|
+
model,
|
|
2024
|
+
instructions: custom,
|
|
2025
|
+
projectRetrievalTool,
|
|
2026
|
+
language = "eng"
|
|
2027
|
+
}) => {
|
|
2028
|
+
const searchItemsByNameTool = {
|
|
2029
|
+
"search_items_by_name": (0, import_ai.tool)({
|
|
2030
|
+
description: `
|
|
2031
|
+
Search for relevant items by name across the available knowledge bases.`,
|
|
2032
|
+
inputSchema: import_zod.z.object({
|
|
2033
|
+
knowledge_base_ids: import_zod.z.array(import_zod.z.enum(contexts.map((ctx) => ctx.id))).describe(`
|
|
2034
|
+
The available knowledge bases are:
|
|
2035
|
+
${contexts.map((ctx) => `
|
|
2036
|
+
<knowledge_base>
|
|
2037
|
+
<id>${ctx.id}</id>
|
|
2038
|
+
<name>${ctx.name}</name>
|
|
2039
|
+
<description>${ctx.description}</description>
|
|
2040
|
+
</knowledge_base>
|
|
2041
|
+
`).join("\n")}
|
|
2042
|
+
`),
|
|
2043
|
+
item_name: import_zod.z.string().describe("The name of the item to search for."),
|
|
2044
|
+
limit: import_zod.z.number().default(100).describe("Maximum number of items to return (max 400), if searching through multiple knowledge bases, the limit is applied for each knowledge base individually.")
|
|
2045
|
+
}),
|
|
2046
|
+
execute: async ({ item_name, limit, knowledge_base_ids }) => {
|
|
2047
|
+
if (!knowledge_base_ids?.length) {
|
|
2048
|
+
knowledge_base_ids = contexts.map((ctx) => ctx.id);
|
|
2049
|
+
}
|
|
2050
|
+
let itemFilters = [];
|
|
2051
|
+
if (item_name) {
|
|
2052
|
+
itemFilters.push({ name: { contains: item_name } });
|
|
2053
|
+
}
|
|
2054
|
+
const { db: db3 } = await postgresClient();
|
|
2055
|
+
const results = await Promise.all(knowledge_base_ids.map(async (knowledge_base_id) => {
|
|
2056
|
+
const ctx = contexts.find((ctx2) => ctx2.id === knowledge_base_id || ctx2.id.toLowerCase().includes(knowledge_base_id.toLowerCase()));
|
|
2057
|
+
if (!ctx) {
|
|
2058
|
+
console.error("[EXULU] Knowledge base ID that was provided to search items by name not found.", knowledge_base_id);
|
|
2059
|
+
throw new Error("Knowledge base ID that was provided to search items by name not found.");
|
|
2060
|
+
}
|
|
2061
|
+
let itemsQuery = db3(getTableName(ctx.id) + " as items").select([
|
|
2062
|
+
"items.id as item_id",
|
|
2063
|
+
"items.name as item_name",
|
|
2064
|
+
"items.external_id as item_external_id",
|
|
2065
|
+
db3.raw('items."updatedAt" as item_updated_at'),
|
|
2066
|
+
db3.raw('items."createdAt" as item_created_at'),
|
|
2067
|
+
...ctx.fields.map((field) => `items.${field.name} as ${field.name}`)
|
|
2068
|
+
]);
|
|
2069
|
+
if (!limit) {
|
|
2070
|
+
limit = 100;
|
|
2071
|
+
}
|
|
2072
|
+
limit = Math.min(limit, 400);
|
|
2073
|
+
itemsQuery = itemsQuery.limit(limit);
|
|
2074
|
+
const tableDefinition = contextToTableDefinition(ctx);
|
|
2075
|
+
itemsQuery = applyFilters(itemsQuery, itemFilters || [], tableDefinition, "items");
|
|
2076
|
+
itemsQuery = applyAccessControl(tableDefinition, itemsQuery, user, "items");
|
|
2077
|
+
const items2 = await itemsQuery;
|
|
2078
|
+
return items2?.map((item) => ({
|
|
2079
|
+
...item,
|
|
2080
|
+
context: ctx.id
|
|
2081
|
+
}));
|
|
2082
|
+
}));
|
|
2083
|
+
const items = results.flat();
|
|
2084
|
+
const formattedResults = await Promise.all(items.map(async (item, index) => {
|
|
2085
|
+
if (!item.item_id || !item.context) {
|
|
2086
|
+
console.error("[EXULU] Item id and context are required to get chunks.", item);
|
|
2087
|
+
throw new Error("Item id is required to get chunks.");
|
|
2088
|
+
}
|
|
2089
|
+
const chunksTable = getChunksTableName(item.context);
|
|
2090
|
+
const chunks = await db3.from(chunksTable).select(["id", "source", "metadata"]).where("source", item.item_id).limit(1);
|
|
2091
|
+
if (!chunks || !chunks[0]) {
|
|
2092
|
+
return null;
|
|
2093
|
+
}
|
|
2094
|
+
return {
|
|
2095
|
+
item_name: item.item_name,
|
|
2096
|
+
item_id: item.item_id,
|
|
2097
|
+
context: item.context || "",
|
|
2098
|
+
chunk_id: chunks[0].id,
|
|
2099
|
+
chunk_index: 1,
|
|
2100
|
+
chunk_content: void 0,
|
|
2101
|
+
metadata: chunks[0].metadata
|
|
2102
|
+
};
|
|
2103
|
+
}));
|
|
2104
|
+
return JSON.stringify(formattedResults.filter((result) => result !== null), null, 2);
|
|
2105
|
+
}
|
|
2106
|
+
})
|
|
2107
|
+
};
|
|
2108
|
+
const searchTools = {
|
|
2109
|
+
"search_content": (0, import_ai.tool)({
|
|
2110
|
+
description: `
|
|
2111
|
+
Search for relevant information within the actual content of the items across available knowledge bases.
|
|
2112
|
+
|
|
2113
|
+
This tool provides a number of strategies:
|
|
2114
|
+
- Keyword search: search for exact terms, technical names, IDs, or specific phrases
|
|
2115
|
+
- Semantic search: search for conceptual queries where synonyms and paraphrasing matter
|
|
2116
|
+
- Hybrid search: best for most queries - combines semantic understanding with exact term matching
|
|
2117
|
+
|
|
2118
|
+
You can use the includeContent parameter to control whether to return
|
|
2119
|
+
the full chunk content or just metadata.
|
|
2120
|
+
|
|
2121
|
+
Use with includeContent: true (default) when you need to:
|
|
2122
|
+
- Find specific information or answers within documents
|
|
2123
|
+
- Get actual text content that answers a query
|
|
2124
|
+
- Extract details, explanations, or instructions from content
|
|
2125
|
+
|
|
2126
|
+
Use with includeContent: false when you need to:
|
|
2127
|
+
- List which documents/items contain certain topics
|
|
2128
|
+
- Count or overview items that match a content query
|
|
2129
|
+
- Find item names/metadata without loading full content
|
|
2130
|
+
- You can always fetch content later if needed
|
|
2131
|
+
|
|
2132
|
+
`,
|
|
2133
|
+
inputSchema: import_zod.z.object({
|
|
2134
|
+
query: import_zod.z.string().describe("The search query to find relevant chunks, this must always be related to the content you are looking for, not something like 'Page 2'."),
|
|
2135
|
+
knowledge_base_ids: import_zod.z.array(import_zod.z.enum(contexts.map((ctx) => ctx.id))).describe(`
|
|
2136
|
+
The available knowledge bases are:
|
|
2137
|
+
${contexts.map((ctx) => `
|
|
2138
|
+
<knowledge_base>
|
|
2139
|
+
<id>${ctx.id}</id>
|
|
2140
|
+
<name>${ctx.name}</name>
|
|
2141
|
+
<description>${ctx.description}</description>
|
|
2142
|
+
</knowledge_base>
|
|
2143
|
+
`).join("\n")}
|
|
2144
|
+
`),
|
|
2145
|
+
keywords: import_zod.z.array(import_zod.z.string()).optional().describe("Keywords to search for. Usually extracted from the query, allowing for more precise search results."),
|
|
2146
|
+
searchMethod: import_zod.z.enum(["keyword", "semantic", "hybrid"]).default("hybrid").describe(
|
|
2147
|
+
"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)"
|
|
2148
|
+
),
|
|
2149
|
+
includeContent: import_zod.z.boolean().default(true).describe(
|
|
2150
|
+
"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."
|
|
2151
|
+
),
|
|
2152
|
+
item_ids: import_zod.z.array(import_zod.z.string()).optional().describe("Use if you wish to retrieve content from specific items (documents) based on the item ID."),
|
|
2153
|
+
item_names: import_zod.z.array(import_zod.z.string()).optional().describe("Use if you wish to retrieve content from specific items (documents) based on the item name. Can be a partial match."),
|
|
2154
|
+
item_external_ids: import_zod.z.array(import_zod.z.string()).optional().describe("Use if you wish to retrieve content from specific items (documents) based on the item external ID. Can be a partial match."),
|
|
2155
|
+
limit: import_zod.z.number().default(10).describe("Maximum number of chunks to return (max 10)")
|
|
2156
|
+
}),
|
|
2157
|
+
execute: async ({ query, searchMethod, limit, includeContent, item_ids, item_names, item_external_ids, keywords, knowledge_base_ids }) => {
|
|
2158
|
+
if (!knowledge_base_ids?.length) {
|
|
2159
|
+
knowledge_base_ids = contexts.map((ctx) => ctx.id);
|
|
2160
|
+
}
|
|
2161
|
+
const results = await Promise.all(knowledge_base_ids.map(async (knowledge_base_id) => {
|
|
2162
|
+
const ctx = contexts.find((ctx2) => ctx2.id === knowledge_base_id || ctx2.id.toLowerCase().includes(knowledge_base_id.toLowerCase()));
|
|
2163
|
+
if (!ctx) {
|
|
2164
|
+
console.error("[EXULU] Knowledge base ID that was provided to search content not found.", knowledge_base_id);
|
|
2165
|
+
throw new Error("Knowledge base ID that was provided to search content not found.");
|
|
2166
|
+
}
|
|
2167
|
+
let itemFilters = [];
|
|
2168
|
+
if (item_ids) {
|
|
2169
|
+
itemFilters.push({ id: { in: item_ids } });
|
|
2170
|
+
}
|
|
2171
|
+
if (item_names) {
|
|
2172
|
+
itemFilters.push({ name: { or: item_names.map((name) => ({ contains: name })) } });
|
|
2173
|
+
}
|
|
2174
|
+
if (item_external_ids) {
|
|
2175
|
+
itemFilters.push({ external_id: { in: item_external_ids } });
|
|
2176
|
+
}
|
|
2177
|
+
if (!query && keywords) {
|
|
2178
|
+
query = keywords.join(" ");
|
|
2179
|
+
}
|
|
2180
|
+
const results2 = await ctx.search({
|
|
2181
|
+
query,
|
|
2182
|
+
keywords,
|
|
2183
|
+
method: searchMethod === "hybrid" ? "hybridSearch" : searchMethod === "keyword" ? "tsvector" : "cosineDistance",
|
|
2184
|
+
limit: includeContent ? Math.min(limit, 10) : Math.min(limit * 20, 400),
|
|
2185
|
+
page: 1,
|
|
2186
|
+
itemFilters: itemFilters || [],
|
|
2187
|
+
chunkFilters: [],
|
|
2188
|
+
sort: { field: "updatedAt", direction: "desc" },
|
|
2189
|
+
user,
|
|
2190
|
+
role,
|
|
2191
|
+
trigger: "tool"
|
|
2192
|
+
});
|
|
2193
|
+
return results2.chunks.map((chunk) => chunk);
|
|
2194
|
+
}));
|
|
2195
|
+
const resultsFlat = results.flat();
|
|
2196
|
+
const formattedResults = resultsFlat.map((chunk, index) => ({
|
|
2197
|
+
item_name: chunk.item_name,
|
|
2198
|
+
item_id: chunk.item_id,
|
|
2199
|
+
context: chunk.context?.id || "",
|
|
2200
|
+
chunk_id: chunk.chunk_id,
|
|
2201
|
+
chunk_index: chunk.chunk_index,
|
|
2202
|
+
chunk_content: includeContent ? chunk.chunk_content : void 0,
|
|
2203
|
+
metadata: {
|
|
2204
|
+
...chunk.chunk_metadata,
|
|
2205
|
+
cosine_distance: chunk.chunk_cosine_distance,
|
|
2206
|
+
fts_rank: chunk.chunk_fts_rank,
|
|
2207
|
+
hybrid_score: chunk.chunk_hybrid_score
|
|
2208
|
+
}
|
|
2209
|
+
}));
|
|
2210
|
+
return JSON.stringify(formattedResults, null, 2);
|
|
2211
|
+
}
|
|
2212
|
+
})
|
|
2213
|
+
};
|
|
2214
|
+
console.log("[EXULU] Search tools:", Object.keys(searchTools));
|
|
2215
|
+
const agent = createCustomAgenticRetrievalToolLoopAgent({
|
|
2216
|
+
language,
|
|
2217
|
+
model,
|
|
2218
|
+
customInstructions: custom,
|
|
2219
|
+
tools: {
|
|
2220
|
+
...searchTools,
|
|
2221
|
+
...searchItemsByNameTool,
|
|
2222
|
+
...projectRetrievalTool ? { [projectRetrievalTool.id]: projectRetrievalTool.tool } : {}
|
|
2223
|
+
}
|
|
2224
|
+
});
|
|
2225
|
+
return agent;
|
|
2226
|
+
};
|
|
2227
|
+
async function* executeAgenticRetrieval({
|
|
2228
|
+
contexts,
|
|
2229
|
+
reranker,
|
|
2230
|
+
query,
|
|
2231
|
+
user,
|
|
2232
|
+
role,
|
|
2233
|
+
model,
|
|
2234
|
+
instructions,
|
|
2235
|
+
projectRetrievalTool
|
|
2236
|
+
}) {
|
|
2237
|
+
const { language } = preprocessQuery(query, {
|
|
2238
|
+
detectLanguage: true
|
|
2239
|
+
});
|
|
2240
|
+
console.log("[EXULU] Language detected:", language);
|
|
2241
|
+
console.log("[EXULU] Creating agentic retrieval agent", "available contexts:", contexts.map((ctx) => ctx.id));
|
|
2242
|
+
const agent = createAgenticRetrievalAgent({ contexts, user, role, model, instructions, projectRetrievalTool, language });
|
|
2243
|
+
console.log("[EXULU] Starting agentic retrieval");
|
|
2244
|
+
try {
|
|
2245
|
+
let finishResolver;
|
|
2246
|
+
let finishRejector;
|
|
2247
|
+
const finishPromise = new Promise((resolve, reject) => {
|
|
2248
|
+
finishResolver = resolve;
|
|
2249
|
+
finishRejector = reject;
|
|
2250
|
+
});
|
|
2251
|
+
const timeoutId = setTimeout(() => {
|
|
2252
|
+
finishRejector(new Error("Agentic retrieval timed out after 240 seconds"));
|
|
2253
|
+
}, 24e4);
|
|
2254
|
+
const result = agent.generate({
|
|
2255
|
+
reranker,
|
|
2256
|
+
query,
|
|
2257
|
+
onFinish: (output) => {
|
|
2258
|
+
clearTimeout(timeoutId);
|
|
2259
|
+
finishResolver(output);
|
|
2260
|
+
}
|
|
2261
|
+
});
|
|
2262
|
+
for await (const output of result) {
|
|
2263
|
+
yield output;
|
|
2264
|
+
}
|
|
2265
|
+
const finalOutput = await finishPromise;
|
|
2266
|
+
console.log("[EXULU] Agentic retrieval output", finalOutput);
|
|
2267
|
+
return finalOutput;
|
|
2268
|
+
} catch (error) {
|
|
2269
|
+
console.error("[EXULU] Agentic retrieval error:", error);
|
|
2270
|
+
yield JSON.stringify({
|
|
2271
|
+
status: "error",
|
|
2272
|
+
message: error instanceof Error ? error.message : "Unknown error occurred"
|
|
2273
|
+
});
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
2276
|
+
var createAgenticRetrievalTool = ({
|
|
2277
|
+
contexts,
|
|
2278
|
+
rerankers,
|
|
2279
|
+
user,
|
|
2280
|
+
role,
|
|
2281
|
+
model,
|
|
2282
|
+
projectRetrievalTool
|
|
2283
|
+
}) => {
|
|
2284
|
+
const contextNames = contexts.map((ctx) => ctx.id).join(", ");
|
|
2285
|
+
return new ExuluTool({
|
|
2286
|
+
id: "agentic_context_search",
|
|
2287
|
+
name: "Agentic Context Search",
|
|
2288
|
+
description: `Intelligent context search tool that uses AI to reason through and retrieve relevant information from available knowledge bases (${contextNames}). This tool can understand complex queries, search across multiple contexts, filter items by name, id, external id, and expand context as needed.`,
|
|
2289
|
+
category: "contexts",
|
|
2290
|
+
type: "context",
|
|
2291
|
+
// Config to enable / disable individual contexts
|
|
2292
|
+
config: [
|
|
2293
|
+
{
|
|
2294
|
+
name: "instructions",
|
|
2295
|
+
description: `Custom instructions to use when searching the knowledge bases. This is appended to the default system instructions.`,
|
|
2296
|
+
type: "string",
|
|
2297
|
+
default: ""
|
|
2298
|
+
},
|
|
2299
|
+
{
|
|
2300
|
+
name: "reranker",
|
|
2301
|
+
description: "The reranker to use for the retrieval process.",
|
|
2302
|
+
type: "string",
|
|
2303
|
+
default: "none"
|
|
2304
|
+
},
|
|
2305
|
+
...contexts.map((ctx) => ({
|
|
2306
|
+
name: ctx.id,
|
|
2307
|
+
description: `Enable search in the ${ctx.name} context. ${ctx.description}`,
|
|
2308
|
+
type: "boolean",
|
|
2309
|
+
default: true
|
|
2310
|
+
}))
|
|
2311
|
+
],
|
|
2312
|
+
inputSchema: import_zod.z.object({
|
|
2313
|
+
query: import_zod.z.string().describe("The question or query to answer using the knowledge bases"),
|
|
2314
|
+
userInstructions: import_zod.z.string().optional().describe("Instructions provided by the user to customize the retrieval process.")
|
|
2315
|
+
}),
|
|
2316
|
+
execute: async function* ({ query, userInstructions, toolVariablesConfig }) {
|
|
2317
|
+
let configInstructions = "";
|
|
2318
|
+
let configuredReranker;
|
|
2319
|
+
if (toolVariablesConfig) {
|
|
2320
|
+
configInstructions = toolVariablesConfig.instructions;
|
|
2321
|
+
contexts = contexts.filter(
|
|
2322
|
+
(ctx) => toolVariablesConfig[ctx.id] === true || toolVariablesConfig[ctx.id] === "true" || toolVariablesConfig[ctx.id] === 1
|
|
2323
|
+
);
|
|
2324
|
+
if (toolVariablesConfig.reranker) {
|
|
2325
|
+
configuredReranker = rerankers.find((reranker) => reranker.id === toolVariablesConfig.reranker);
|
|
2326
|
+
if (!configuredReranker) {
|
|
2327
|
+
throw new Error("Reranker not found: " + toolVariablesConfig.reranker + ", check with a developer if the reranker was removed from the system.");
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
console.log("[EXULU] Executing agentic retrieval tool with data", {
|
|
2332
|
+
// Log only first level properties
|
|
2333
|
+
// Keys of all toolVariablesConfig vars
|
|
2334
|
+
configs: Object.keys(toolVariablesConfig),
|
|
2335
|
+
query,
|
|
2336
|
+
instructions: configInstructions,
|
|
2337
|
+
reranker: configuredReranker?.id || void 0,
|
|
2338
|
+
contexts: contexts.map((ctx) => ctx.id)
|
|
2339
|
+
});
|
|
2340
|
+
console.log("[EXULU] Executing agentic retrieval tool");
|
|
2341
|
+
for await (const chunk of executeAgenticRetrieval({
|
|
2342
|
+
contexts,
|
|
2343
|
+
reranker: configuredReranker,
|
|
2344
|
+
query,
|
|
2345
|
+
user,
|
|
2346
|
+
role,
|
|
2347
|
+
model,
|
|
2348
|
+
instructions: `${configInstructions ? `CUSTOM INSTRUCTIONS PROVIDED BY THE ADMIN: ${configInstructions}` : ""} ${userInstructions ? `INSTRUCTIONS PROVIDED BY THE USER: ${userInstructions}` : ""}`,
|
|
2349
|
+
projectRetrievalTool
|
|
2350
|
+
})) {
|
|
2351
|
+
yield { result: JSON.stringify(chunk, null, 2) };
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
});
|
|
2355
|
+
};
|
|
2356
|
+
|
|
1563
2357
|
// src/registry/utils.ts
|
|
1564
2358
|
var bullmq = {
|
|
1565
2359
|
validate: (id, data) => {
|
|
@@ -1580,12 +2374,24 @@ var bullmq = {
|
|
|
1580
2374
|
}
|
|
1581
2375
|
}
|
|
1582
2376
|
};
|
|
1583
|
-
var getEnabledTools = async (agentInstance, allExuluTools, disabledTools = [], agents, user) => {
|
|
2377
|
+
var getEnabledTools = async (agentInstance, allExuluTools, allContexts, allRerankers, disabledTools = [], agents, user) => {
|
|
1584
2378
|
let enabledTools = [];
|
|
1585
2379
|
if (agentInstance.tools) {
|
|
1586
2380
|
const results = await Promise.all(agentInstance.tools.map(
|
|
1587
2381
|
async ({ config, id, type }) => {
|
|
1588
2382
|
let hydrated;
|
|
2383
|
+
if (id === "agentic_context_search") {
|
|
2384
|
+
return createAgenticRetrievalTool({
|
|
2385
|
+
// This tool is reinstantiated in the convertToolsArrayToObject function, where
|
|
2386
|
+
// we can access the activated contexts and model that is calling it but we also
|
|
2387
|
+
// return it here so we know it was generally enabled as a tool.
|
|
2388
|
+
contexts: allContexts,
|
|
2389
|
+
rerankers: allRerankers || [],
|
|
2390
|
+
user,
|
|
2391
|
+
role: user?.role?.id,
|
|
2392
|
+
model: void 0
|
|
2393
|
+
});
|
|
2394
|
+
}
|
|
1589
2395
|
if (type === "agent") {
|
|
1590
2396
|
if (id === agentInstance.id) {
|
|
1591
2397
|
return null;
|
|
@@ -1602,7 +2408,7 @@ var getEnabledTools = async (agentInstance, allExuluTools, disabledTools = [], a
|
|
|
1602
2408
|
if (!hasAccessToAgent) {
|
|
1603
2409
|
return null;
|
|
1604
2410
|
}
|
|
1605
|
-
hydrated = await backend.tool(instance.id, agents);
|
|
2411
|
+
hydrated = await backend.tool(instance.id, agents, allContexts, allRerankers || []);
|
|
1606
2412
|
} else {
|
|
1607
2413
|
hydrated = allExuluTools.find((t) => t.id === id);
|
|
1608
2414
|
}
|
|
@@ -1613,7 +2419,7 @@ var getEnabledTools = async (agentInstance, allExuluTools, disabledTools = [], a
|
|
|
1613
2419
|
}
|
|
1614
2420
|
console.log("[EXULU] available tools", enabledTools?.length);
|
|
1615
2421
|
console.log("[EXULU] disabled tools", disabledTools?.length);
|
|
1616
|
-
enabledTools = enabledTools.filter((
|
|
2422
|
+
enabledTools = enabledTools.filter((tool3) => !disabledTools.includes(tool3.id));
|
|
1617
2423
|
return enabledTools;
|
|
1618
2424
|
};
|
|
1619
2425
|
var loadAgentCache = /* @__PURE__ */ new Map();
|
|
@@ -1799,7 +2605,7 @@ var import_ioredis = __toESM(require("ioredis"), 1);
|
|
|
1799
2605
|
var import_bullmq2 = require("bullmq");
|
|
1800
2606
|
var import_api = require("@opentelemetry/api");
|
|
1801
2607
|
var import_uuid2 = require("uuid");
|
|
1802
|
-
var
|
|
2608
|
+
var import_ai2 = require("ai");
|
|
1803
2609
|
var import_crypto_js = __toESM(require("crypto-js"), 1);
|
|
1804
2610
|
|
|
1805
2611
|
// src/registry/log-metadata.ts
|
|
@@ -1835,7 +2641,7 @@ var installGlobalErrorHandlers = () => {
|
|
|
1835
2641
|
unhandledRejectionHandlerInstalled = true;
|
|
1836
2642
|
console.log("[EXULU] Global error handlers installed to prevent worker crashes");
|
|
1837
2643
|
};
|
|
1838
|
-
var createWorkers = async (agents, queues2, config, contexts, evals, tools, tracer) => {
|
|
2644
|
+
var createWorkers = async (agents, queues2, config, contexts, rerankers, evals, tools, tracer) => {
|
|
1839
2645
|
console.log("[EXULU] creating workers for " + queues2?.length + " queues.");
|
|
1840
2646
|
console.log("[EXULU] queues", queues2.map((q) => q.queue.name));
|
|
1841
2647
|
installGlobalErrorHandlers();
|
|
@@ -2011,6 +2817,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
2011
2817
|
agentBackend,
|
|
2012
2818
|
inputMessages,
|
|
2013
2819
|
contexts,
|
|
2820
|
+
rerankers,
|
|
2014
2821
|
user,
|
|
2015
2822
|
tools,
|
|
2016
2823
|
config,
|
|
@@ -2085,6 +2892,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
|
|
|
2085
2892
|
agentBackend,
|
|
2086
2893
|
inputMessages,
|
|
2087
2894
|
contexts,
|
|
2895
|
+
rerankers,
|
|
2088
2896
|
user,
|
|
2089
2897
|
tools,
|
|
2090
2898
|
config
|
|
@@ -2524,6 +3332,7 @@ var processUiMessagesFlow = async ({
|
|
|
2524
3332
|
agentBackend,
|
|
2525
3333
|
inputMessages,
|
|
2526
3334
|
contexts,
|
|
3335
|
+
rerankers,
|
|
2527
3336
|
user,
|
|
2528
3337
|
tools,
|
|
2529
3338
|
config,
|
|
@@ -2533,7 +3342,7 @@ var processUiMessagesFlow = async ({
|
|
|
2533
3342
|
console.log("[EXULU] input messages", inputMessages);
|
|
2534
3343
|
console.log("[EXULU] agent tools", agentInstance.tools?.map((x) => x.name + " (" + x.id + ")"));
|
|
2535
3344
|
const disabledTools = [];
|
|
2536
|
-
let enabledTools = await getEnabledTools(agentInstance, tools, disabledTools, agents, user);
|
|
3345
|
+
let enabledTools = await getEnabledTools(agentInstance, tools, contexts, rerankers, disabledTools, agents, user);
|
|
2537
3346
|
console.log("[EXULU] enabled tools", enabledTools?.map((x) => x.name + " (" + x.id + ")"));
|
|
2538
3347
|
const variableName = agentInstance.providerapikey;
|
|
2539
3348
|
const { db: db3 } = await postgresClient();
|
|
@@ -2606,7 +3415,10 @@ var processUiMessagesFlow = async ({
|
|
|
2606
3415
|
try {
|
|
2607
3416
|
const result = await agentBackend.generateStream({
|
|
2608
3417
|
contexts,
|
|
3418
|
+
rerankers,
|
|
3419
|
+
agentInstance,
|
|
2609
3420
|
user,
|
|
3421
|
+
approvedTools: tools.map((tool3) => "tool-" + sanitizeToolName(tool3.name)),
|
|
2610
3422
|
instructions: agentInstance.instructions,
|
|
2611
3423
|
session: void 0,
|
|
2612
3424
|
previousMessages: messageHistory.messages,
|
|
@@ -2722,7 +3534,7 @@ function getAverage(arr) {
|
|
|
2722
3534
|
}
|
|
2723
3535
|
|
|
2724
3536
|
// src/registry/utils/graphql.ts
|
|
2725
|
-
var GraphQLDate = new
|
|
3537
|
+
var GraphQLDate = new import_graphql3.GraphQLScalarType({
|
|
2726
3538
|
name: "Date",
|
|
2727
3539
|
description: "Date custom scalar type",
|
|
2728
3540
|
serialize(value) {
|
|
@@ -2747,10 +3559,10 @@ var GraphQLDate = new import_graphql2.GraphQLScalarType({
|
|
|
2747
3559
|
return value;
|
|
2748
3560
|
},
|
|
2749
3561
|
parseLiteral(ast) {
|
|
2750
|
-
if (ast.kind ===
|
|
3562
|
+
if (ast.kind === import_graphql3.Kind.STRING) {
|
|
2751
3563
|
return new Date(ast.value);
|
|
2752
3564
|
}
|
|
2753
|
-
if (ast.kind ===
|
|
3565
|
+
if (ast.kind === import_graphql3.Kind.INT) {
|
|
2754
3566
|
return new Date(parseInt(ast.value, 10));
|
|
2755
3567
|
}
|
|
2756
3568
|
return null;
|
|
@@ -2962,6 +3774,32 @@ var getRequestedFields = (info) => {
|
|
|
2962
3774
|
(field) => field !== "pageInfo" && field !== "items" && field !== "RBAC"
|
|
2963
3775
|
);
|
|
2964
3776
|
};
|
|
3777
|
+
var contextItemProcessorMutation = async (context, config, items, user, role) => {
|
|
3778
|
+
let jobs = [];
|
|
3779
|
+
let results = [];
|
|
3780
|
+
await Promise.all(
|
|
3781
|
+
items.map(async (item) => {
|
|
3782
|
+
const result = await context.processField(
|
|
3783
|
+
"api",
|
|
3784
|
+
item,
|
|
3785
|
+
config,
|
|
3786
|
+
user,
|
|
3787
|
+
role
|
|
3788
|
+
);
|
|
3789
|
+
if (result.job) {
|
|
3790
|
+
jobs.push(result.job);
|
|
3791
|
+
}
|
|
3792
|
+
if (result.result) {
|
|
3793
|
+
results.push(result.result);
|
|
3794
|
+
}
|
|
3795
|
+
})
|
|
3796
|
+
);
|
|
3797
|
+
return {
|
|
3798
|
+
message: jobs.length > 0 ? "Processing job scheduled." : "Items processed successfully.",
|
|
3799
|
+
results: results.map((result) => JSON.stringify(result)),
|
|
3800
|
+
jobs
|
|
3801
|
+
};
|
|
3802
|
+
};
|
|
2965
3803
|
var handleRBACUpdate = async (db3, entityName, resourceId, rbacData, existingRbacRecords) => {
|
|
2966
3804
|
const {
|
|
2967
3805
|
users = [],
|
|
@@ -3036,7 +3874,7 @@ var handleRBACUpdate = async (db3, entityName, resourceId, rbacData, existingRba
|
|
|
3036
3874
|
await db3.from("rbac").insert(recordsToInsert);
|
|
3037
3875
|
}
|
|
3038
3876
|
};
|
|
3039
|
-
function createMutations(table, agents, contexts, tools, config) {
|
|
3877
|
+
function createMutations(table, agents, contexts, rerankers, tools, config) {
|
|
3040
3878
|
const tableNamePlural = table.name.plural.toLowerCase();
|
|
3041
3879
|
const tableNameSingular = table.name.singular.toLowerCase();
|
|
3042
3880
|
const validateWriteAccess = async (id, context) => {
|
|
@@ -3161,6 +3999,7 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
3161
3999
|
requestedFields,
|
|
3162
4000
|
agents,
|
|
3163
4001
|
contexts,
|
|
4002
|
+
rerankers,
|
|
3164
4003
|
tools,
|
|
3165
4004
|
result: result[0],
|
|
3166
4005
|
user: context.user
|
|
@@ -3236,6 +4075,7 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
3236
4075
|
requestedFields,
|
|
3237
4076
|
agents,
|
|
3238
4077
|
contexts,
|
|
4078
|
+
rerankers,
|
|
3239
4079
|
tools,
|
|
3240
4080
|
result: results[0],
|
|
3241
4081
|
user: context.user
|
|
@@ -3315,6 +4155,7 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
3315
4155
|
requestedFields,
|
|
3316
4156
|
agents,
|
|
3317
4157
|
contexts,
|
|
4158
|
+
rerankers,
|
|
3318
4159
|
tools,
|
|
3319
4160
|
result,
|
|
3320
4161
|
user: context.user.id
|
|
@@ -3385,6 +4226,7 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
3385
4226
|
requestedFields,
|
|
3386
4227
|
agents,
|
|
3387
4228
|
contexts,
|
|
4229
|
+
rerankers,
|
|
3388
4230
|
tools,
|
|
3389
4231
|
result,
|
|
3390
4232
|
user: context.user.id
|
|
@@ -3433,6 +4275,7 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
3433
4275
|
requestedFields,
|
|
3434
4276
|
agents,
|
|
3435
4277
|
contexts,
|
|
4278
|
+
rerankers,
|
|
3436
4279
|
tools,
|
|
3437
4280
|
result,
|
|
3438
4281
|
user: context.user.id
|
|
@@ -3473,6 +4316,7 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
3473
4316
|
requestedFields,
|
|
3474
4317
|
agents,
|
|
3475
4318
|
contexts,
|
|
4319
|
+
rerankers,
|
|
3476
4320
|
tools,
|
|
3477
4321
|
result,
|
|
3478
4322
|
user: context.user.id
|
|
@@ -3481,32 +4325,6 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
3481
4325
|
};
|
|
3482
4326
|
if (table.type === "items") {
|
|
3483
4327
|
if (table.processor) {
|
|
3484
|
-
const contextItemProcessorMutation = async (context, items, user, role) => {
|
|
3485
|
-
let jobs = [];
|
|
3486
|
-
let results = [];
|
|
3487
|
-
await Promise.all(
|
|
3488
|
-
items.map(async (item) => {
|
|
3489
|
-
const result = await context.processField(
|
|
3490
|
-
"api",
|
|
3491
|
-
item,
|
|
3492
|
-
config,
|
|
3493
|
-
user,
|
|
3494
|
-
role
|
|
3495
|
-
);
|
|
3496
|
-
if (result.job) {
|
|
3497
|
-
jobs.push(result.job);
|
|
3498
|
-
}
|
|
3499
|
-
if (result.result) {
|
|
3500
|
-
results.push(result.result);
|
|
3501
|
-
}
|
|
3502
|
-
})
|
|
3503
|
-
);
|
|
3504
|
-
return {
|
|
3505
|
-
message: jobs.length > 0 ? "Processing job scheduled." : "Items processed successfully.",
|
|
3506
|
-
results: results.map((result) => JSON.stringify(result)),
|
|
3507
|
-
jobs
|
|
3508
|
-
};
|
|
3509
|
-
};
|
|
3510
4328
|
mutations[`${tableNameSingular}ProcessItem`] = async (_, args, context, info) => {
|
|
3511
4329
|
if (!context.user?.super_admin) {
|
|
3512
4330
|
throw new Error(
|
|
@@ -3533,6 +4351,7 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
3533
4351
|
}
|
|
3534
4352
|
return contextItemProcessorMutation(
|
|
3535
4353
|
exists,
|
|
4354
|
+
config,
|
|
3536
4355
|
[item],
|
|
3537
4356
|
context.user.id,
|
|
3538
4357
|
context.user.role?.id
|
|
@@ -3562,6 +4381,7 @@ function createMutations(table, agents, contexts, tools, config) {
|
|
|
3562
4381
|
}
|
|
3563
4382
|
return contextItemProcessorMutation(
|
|
3564
4383
|
exists,
|
|
4384
|
+
config,
|
|
3565
4385
|
items,
|
|
3566
4386
|
context.user.id,
|
|
3567
4387
|
context.user.role?.id
|
|
@@ -3792,14 +4612,14 @@ var applyAccessControl = (table, query, user, field_prefix) => {
|
|
|
3792
4612
|
this.orWhere(`${prefix}created_by`, user.id);
|
|
3793
4613
|
this.orWhere(function() {
|
|
3794
4614
|
this.where(`${prefix}rights_mode`, "users").whereExists(function() {
|
|
3795
|
-
this.select("*").from("rbac").whereRaw("rbac.target_resource_id = " + tableNamePlural + ".id").where("rbac.entity", table.name.singular).where("rbac.access_type", "User").where("rbac.user_id", user.id);
|
|
4615
|
+
this.select("*").from("rbac").whereRaw("rbac.target_resource_id = " + (prefix ? prefix.slice(0, -1) : tableNamePlural) + ".id").where("rbac.entity", table.name.singular).where("rbac.access_type", "User").where("rbac.user_id", user.id);
|
|
3796
4616
|
});
|
|
3797
4617
|
});
|
|
3798
4618
|
}
|
|
3799
4619
|
if (user?.role) {
|
|
3800
4620
|
this.orWhere(function() {
|
|
3801
4621
|
this.where(`${prefix}rights_mode`, "roles").whereExists(function() {
|
|
3802
|
-
this.select("*").from("rbac").whereRaw("rbac.target_resource_id = " + tableNamePlural + ".id").where("rbac.entity", table.name.singular).where("rbac.access_type", "Role").where("rbac.role_id", user.role.id);
|
|
4622
|
+
this.select("*").from("rbac").whereRaw("rbac.target_resource_id = " + (prefix ? prefix.slice(0, -1) : tableNamePlural) + ".id").where("rbac.entity", table.name.singular).where("rbac.access_type", "Role").where("rbac.role_id", user.role.id);
|
|
3803
4623
|
});
|
|
3804
4624
|
});
|
|
3805
4625
|
}
|
|
@@ -3895,7 +4715,7 @@ var removeAgentFields = (requestedFields) => {
|
|
|
3895
4715
|
filtered.push("backend");
|
|
3896
4716
|
return filtered;
|
|
3897
4717
|
};
|
|
3898
|
-
var addAgentFields = async (args, requestedFields, agents, result, tools, user, contexts) => {
|
|
4718
|
+
var addAgentFields = async (args, requestedFields, agents, result, tools, user, contexts, rerankers) => {
|
|
3899
4719
|
let backend = agents.find((a) => a.id === result?.backend);
|
|
3900
4720
|
if (requestedFields.includes("providerName")) {
|
|
3901
4721
|
result.providerName = backend?.providerName || "";
|
|
@@ -3913,22 +4733,38 @@ var addAgentFields = async (args, requestedFields, agents, result, tools, user,
|
|
|
3913
4733
|
if (result.tools) {
|
|
3914
4734
|
result.tools = await Promise.all(
|
|
3915
4735
|
result.tools.map(
|
|
3916
|
-
async (
|
|
4736
|
+
async (tool3) => {
|
|
3917
4737
|
let hydrated;
|
|
3918
|
-
if (
|
|
3919
|
-
|
|
4738
|
+
if (tool3.id === "agentic_context_search") {
|
|
4739
|
+
const instance = createAgenticRetrievalTool({
|
|
4740
|
+
contexts: [],
|
|
4741
|
+
rerankers: [],
|
|
4742
|
+
user,
|
|
4743
|
+
role: user.role?.id,
|
|
4744
|
+
model: void 0
|
|
4745
|
+
});
|
|
4746
|
+
return {
|
|
4747
|
+
...instance,
|
|
4748
|
+
name: instance.name,
|
|
4749
|
+
description: instance.description,
|
|
4750
|
+
category: instance.category,
|
|
4751
|
+
config: tool3.config
|
|
4752
|
+
};
|
|
4753
|
+
}
|
|
4754
|
+
if (tool3.type === "agent") {
|
|
4755
|
+
if (tool3.id === result.id) {
|
|
3920
4756
|
return null;
|
|
3921
4757
|
}
|
|
3922
|
-
const instance = await loadAgent(
|
|
4758
|
+
const instance = await loadAgent(tool3.id);
|
|
3923
4759
|
if (!instance) {
|
|
3924
4760
|
throw new Error(
|
|
3925
|
-
"Trying to load a tool of type 'agent', but the associated agent with id " +
|
|
4761
|
+
"Trying to load a tool of type 'agent', but the associated agent with id " + tool3.id + " was not found in the database."
|
|
3926
4762
|
);
|
|
3927
4763
|
}
|
|
3928
4764
|
const backend2 = agents.find((a) => a.id === instance.backend);
|
|
3929
4765
|
if (!backend2) {
|
|
3930
4766
|
throw new Error(
|
|
3931
|
-
"Trying to load a tool of type 'agent', but the associated agent with id " +
|
|
4767
|
+
"Trying to load a tool of type 'agent', but the associated agent with id " + tool3.id + " does not have a backend set for it."
|
|
3932
4768
|
);
|
|
3933
4769
|
}
|
|
3934
4770
|
const hasAccessToAgent = await checkRecordAccess(
|
|
@@ -3939,15 +4775,15 @@ var addAgentFields = async (args, requestedFields, agents, result, tools, user,
|
|
|
3939
4775
|
if (!hasAccessToAgent) {
|
|
3940
4776
|
return null;
|
|
3941
4777
|
}
|
|
3942
|
-
hydrated = await backend2.tool(instance.id, agents);
|
|
4778
|
+
hydrated = await backend2.tool(instance.id, agents, contexts, rerankers);
|
|
3943
4779
|
} else {
|
|
3944
|
-
hydrated = tools.find((t) => t.id ===
|
|
4780
|
+
hydrated = tools.find((t) => t.id === tool3.id);
|
|
3945
4781
|
}
|
|
3946
4782
|
const hydratedTool = {
|
|
3947
|
-
...
|
|
4783
|
+
...tool3,
|
|
3948
4784
|
name: hydrated?.name || "",
|
|
3949
4785
|
description: hydrated?.description || "",
|
|
3950
|
-
category:
|
|
4786
|
+
category: tool3?.category || "default"
|
|
3951
4787
|
};
|
|
3952
4788
|
console.log("[EXULU] hydratedTool", hydratedTool);
|
|
3953
4789
|
return hydratedTool;
|
|
@@ -3955,7 +4791,7 @@ var addAgentFields = async (args, requestedFields, agents, result, tools, user,
|
|
|
3955
4791
|
)
|
|
3956
4792
|
);
|
|
3957
4793
|
if (args.project) {
|
|
3958
|
-
const projectTool = await
|
|
4794
|
+
const projectTool = await createProjectItemsRetrievalTool({
|
|
3959
4795
|
projectId: args.project,
|
|
3960
4796
|
user,
|
|
3961
4797
|
role: user.role?.id,
|
|
@@ -3965,7 +4801,7 @@ var addAgentFields = async (args, requestedFields, agents, result, tools, user,
|
|
|
3965
4801
|
result.tools.unshift(projectTool);
|
|
3966
4802
|
}
|
|
3967
4803
|
}
|
|
3968
|
-
result.tools = result.tools.filter((
|
|
4804
|
+
result.tools = result.tools.filter((tool3) => tool3 !== null);
|
|
3969
4805
|
} else {
|
|
3970
4806
|
result.tools = [];
|
|
3971
4807
|
}
|
|
@@ -4085,6 +4921,21 @@ var postprocessUpdate = async ({
|
|
|
4085
4921
|
job
|
|
4086
4922
|
};
|
|
4087
4923
|
}
|
|
4924
|
+
if (context.processor && (context.processor.config?.trigger === "onUpdate" || context.processor.config?.trigger === "always")) {
|
|
4925
|
+
const {
|
|
4926
|
+
jobs
|
|
4927
|
+
} = await contextItemProcessorMutation(
|
|
4928
|
+
context,
|
|
4929
|
+
config,
|
|
4930
|
+
[result],
|
|
4931
|
+
user,
|
|
4932
|
+
role
|
|
4933
|
+
);
|
|
4934
|
+
return {
|
|
4935
|
+
result,
|
|
4936
|
+
job: jobs[0]
|
|
4937
|
+
};
|
|
4938
|
+
}
|
|
4088
4939
|
return result;
|
|
4089
4940
|
}
|
|
4090
4941
|
}
|
|
@@ -4156,6 +5007,7 @@ var finalizeRequestedFields = async ({
|
|
|
4156
5007
|
requestedFields,
|
|
4157
5008
|
agents,
|
|
4158
5009
|
contexts,
|
|
5010
|
+
rerankers,
|
|
4159
5011
|
tools,
|
|
4160
5012
|
result,
|
|
4161
5013
|
user
|
|
@@ -4174,6 +5026,7 @@ var finalizeRequestedFields = async ({
|
|
|
4174
5026
|
requestedFields,
|
|
4175
5027
|
agents,
|
|
4176
5028
|
contexts,
|
|
5029
|
+
rerankers,
|
|
4177
5030
|
tools,
|
|
4178
5031
|
result: item,
|
|
4179
5032
|
user
|
|
@@ -4215,7 +5068,8 @@ var finalizeRequestedFields = async ({
|
|
|
4215
5068
|
result,
|
|
4216
5069
|
tools,
|
|
4217
5070
|
user,
|
|
4218
|
-
contexts
|
|
5071
|
+
contexts,
|
|
5072
|
+
rerankers
|
|
4219
5073
|
);
|
|
4220
5074
|
if (!requestedFields.includes("backend")) {
|
|
4221
5075
|
delete result.backend;
|
|
@@ -4264,6 +5118,9 @@ var finalizeRequestedFields = async ({
|
|
|
4264
5118
|
return result;
|
|
4265
5119
|
};
|
|
4266
5120
|
var applyFilters = (query, filters, table, field_prefix) => {
|
|
5121
|
+
if (!filters) {
|
|
5122
|
+
return query;
|
|
5123
|
+
}
|
|
4267
5124
|
filters.forEach((filter) => {
|
|
4268
5125
|
Object.entries(filter).forEach(([fieldName, operators]) => {
|
|
4269
5126
|
if (operators) {
|
|
@@ -4279,14 +5136,18 @@ var applyFilters = (query, filters, table, field_prefix) => {
|
|
|
4279
5136
|
});
|
|
4280
5137
|
}
|
|
4281
5138
|
if (operators.or !== void 0) {
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
5139
|
+
query = query.where((builder) => {
|
|
5140
|
+
operators.or.forEach((operator) => {
|
|
5141
|
+
builder.orWhere((subBuilder) => {
|
|
5142
|
+
converOperatorToQuery(
|
|
5143
|
+
subBuilder,
|
|
5144
|
+
fieldName,
|
|
5145
|
+
operator,
|
|
5146
|
+
table,
|
|
5147
|
+
field_prefix
|
|
5148
|
+
);
|
|
5149
|
+
});
|
|
5150
|
+
});
|
|
4290
5151
|
});
|
|
4291
5152
|
}
|
|
4292
5153
|
query = converOperatorToQuery(
|
|
@@ -4301,7 +5162,7 @@ var applyFilters = (query, filters, table, field_prefix) => {
|
|
|
4301
5162
|
});
|
|
4302
5163
|
return query;
|
|
4303
5164
|
};
|
|
4304
|
-
var
|
|
5165
|
+
var applySorting2 = (query, sort, field_prefix) => {
|
|
4305
5166
|
const prefix = field_prefix ? field_prefix + "." : "";
|
|
4306
5167
|
if (sort) {
|
|
4307
5168
|
sort.field = prefix + sort.field;
|
|
@@ -4335,7 +5196,7 @@ var itemsPaginationRequest = async ({
|
|
|
4335
5196
|
let dataQuery = db3(tableName);
|
|
4336
5197
|
dataQuery = applyFilters(dataQuery, filters, table);
|
|
4337
5198
|
dataQuery = applyAccessControl(table, dataQuery, user);
|
|
4338
|
-
dataQuery =
|
|
5199
|
+
dataQuery = applySorting2(dataQuery, sort);
|
|
4339
5200
|
if (page > 1) {
|
|
4340
5201
|
dataQuery = dataQuery.offset((page - 1) * limit);
|
|
4341
5202
|
}
|
|
@@ -4352,7 +5213,7 @@ var itemsPaginationRequest = async ({
|
|
|
4352
5213
|
}
|
|
4353
5214
|
};
|
|
4354
5215
|
};
|
|
4355
|
-
function createQueries(table, agents, tools, contexts) {
|
|
5216
|
+
function createQueries(table, agents, tools, contexts, rerankers) {
|
|
4356
5217
|
const tableNamePlural = table.name.plural.toLowerCase();
|
|
4357
5218
|
const tableNameSingular = table.name.singular.toLowerCase();
|
|
4358
5219
|
const queries = {
|
|
@@ -4369,6 +5230,7 @@ function createQueries(table, agents, tools, contexts) {
|
|
|
4369
5230
|
requestedFields,
|
|
4370
5231
|
agents,
|
|
4371
5232
|
contexts,
|
|
5233
|
+
rerankers,
|
|
4372
5234
|
tools,
|
|
4373
5235
|
result,
|
|
4374
5236
|
user: context.user
|
|
@@ -4387,6 +5249,7 @@ function createQueries(table, agents, tools, contexts) {
|
|
|
4387
5249
|
requestedFields,
|
|
4388
5250
|
agents,
|
|
4389
5251
|
contexts,
|
|
5252
|
+
rerankers,
|
|
4390
5253
|
tools,
|
|
4391
5254
|
result,
|
|
4392
5255
|
user: context.user
|
|
@@ -4400,7 +5263,7 @@ function createQueries(table, agents, tools, contexts) {
|
|
|
4400
5263
|
let query = db3.from(tableNamePlural).select(sanitizedFields);
|
|
4401
5264
|
query = applyFilters(query, filters, table);
|
|
4402
5265
|
query = applyAccessControl(table, query, context.user);
|
|
4403
|
-
query =
|
|
5266
|
+
query = applySorting2(query, sort);
|
|
4404
5267
|
let result = await query.first();
|
|
4405
5268
|
return finalizeRequestedFields({
|
|
4406
5269
|
args,
|
|
@@ -4408,6 +5271,7 @@ function createQueries(table, agents, tools, contexts) {
|
|
|
4408
5271
|
requestedFields,
|
|
4409
5272
|
agents,
|
|
4410
5273
|
contexts,
|
|
5274
|
+
rerankers,
|
|
4411
5275
|
tools,
|
|
4412
5276
|
result,
|
|
4413
5277
|
user: context.user
|
|
@@ -4436,6 +5300,7 @@ function createQueries(table, agents, tools, contexts) {
|
|
|
4436
5300
|
requestedFields,
|
|
4437
5301
|
agents,
|
|
4438
5302
|
contexts,
|
|
5303
|
+
rerankers,
|
|
4439
5304
|
tools,
|
|
4440
5305
|
result: items,
|
|
4441
5306
|
user: context.user
|
|
@@ -4444,11 +5309,12 @@ function createQueries(table, agents, tools, contexts) {
|
|
|
4444
5309
|
},
|
|
4445
5310
|
// Add generic statistics query for all tables
|
|
4446
5311
|
[`${tableNamePlural}Statistics`]: async (_, args, context, info) => {
|
|
4447
|
-
const { filters = [], groupBy } = args;
|
|
5312
|
+
const { filters = [], groupBy, limit = 10 } = args;
|
|
4448
5313
|
const { db: db3 } = context;
|
|
4449
5314
|
let query = db3(tableNamePlural);
|
|
4450
5315
|
query = applyFilters(query, filters, table);
|
|
4451
5316
|
query = applyAccessControl(table, query, context.user);
|
|
5317
|
+
query = query.limit(limit);
|
|
4452
5318
|
if (groupBy) {
|
|
4453
5319
|
query = query.select(groupBy).groupBy(groupBy);
|
|
4454
5320
|
if (tableNamePlural === "tracking") {
|
|
@@ -4489,11 +5355,12 @@ function createQueries(table, agents, tools, contexts) {
|
|
|
4489
5355
|
if (!exists) {
|
|
4490
5356
|
throw new Error("Context " + table.id + " not found in registry.");
|
|
4491
5357
|
}
|
|
4492
|
-
const { limit = 10, page = 0,
|
|
5358
|
+
const { limit = 10, page = 0, itemFilters = [], chunkFilters = [], sort } = args;
|
|
4493
5359
|
return await vectorSearch({
|
|
4494
5360
|
limit: limit || exists.configuration.maxRetrievalResults || 10,
|
|
4495
5361
|
page,
|
|
4496
|
-
|
|
5362
|
+
itemFilters,
|
|
5363
|
+
chunkFilters,
|
|
4497
5364
|
sort,
|
|
4498
5365
|
context: exists,
|
|
4499
5366
|
db: context.db,
|
|
@@ -4552,11 +5419,13 @@ function createQueries(table, agents, tools, contexts) {
|
|
|
4552
5419
|
var vectorSearch = async ({
|
|
4553
5420
|
limit,
|
|
4554
5421
|
page,
|
|
4555
|
-
|
|
5422
|
+
itemFilters,
|
|
5423
|
+
chunkFilters,
|
|
4556
5424
|
sort,
|
|
4557
5425
|
context,
|
|
4558
5426
|
db: db3,
|
|
4559
5427
|
query,
|
|
5428
|
+
keywords,
|
|
4560
5429
|
method,
|
|
4561
5430
|
user,
|
|
4562
5431
|
role,
|
|
@@ -4568,7 +5437,8 @@ var vectorSearch = async ({
|
|
|
4568
5437
|
console.log("[EXULU] Called vector search.", {
|
|
4569
5438
|
limit,
|
|
4570
5439
|
page,
|
|
4571
|
-
|
|
5440
|
+
itemFilters,
|
|
5441
|
+
chunkFilters,
|
|
4572
5442
|
sort,
|
|
4573
5443
|
context: context.id,
|
|
4574
5444
|
query,
|
|
@@ -4581,7 +5451,7 @@ var vectorSearch = async ({
|
|
|
4581
5451
|
if (limit > 250) {
|
|
4582
5452
|
throw new Error("Limit cannot be greater than 1000.");
|
|
4583
5453
|
}
|
|
4584
|
-
if (!query) {
|
|
5454
|
+
if (!query && !keywords) {
|
|
4585
5455
|
throw new Error("Query is required.");
|
|
4586
5456
|
}
|
|
4587
5457
|
if (!method) {
|
|
@@ -4624,43 +5494,68 @@ var vectorSearch = async ({
|
|
|
4624
5494
|
chunksQuery.leftJoin(mainTable + " as items", function() {
|
|
4625
5495
|
this.on("chunks.source", "=", "items.id");
|
|
4626
5496
|
});
|
|
4627
|
-
chunksQuery = applyFilters(chunksQuery,
|
|
5497
|
+
chunksQuery = applyFilters(chunksQuery, itemFilters, table, "items");
|
|
5498
|
+
chunksQuery = applyFilters(chunksQuery, chunkFilters, table, "chunks");
|
|
4628
5499
|
chunksQuery = applyAccessControl(table, chunksQuery, user, "items");
|
|
4629
|
-
chunksQuery =
|
|
4630
|
-
if (queryRewriter) {
|
|
5500
|
+
chunksQuery = applySorting2(chunksQuery, sort, "items");
|
|
5501
|
+
if (queryRewriter && query) {
|
|
4631
5502
|
query = await queryRewriter(query);
|
|
4632
5503
|
}
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
|
|
4643
|
-
|
|
4644
|
-
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
5504
|
+
let vector = [];
|
|
5505
|
+
let vectorStr = "";
|
|
5506
|
+
let vectorExpr = "";
|
|
5507
|
+
if (query) {
|
|
5508
|
+
const { processed: stemmedQuery, language: language2 } = preprocessQuery(query, {
|
|
5509
|
+
enableStemming: true,
|
|
5510
|
+
detectLanguage: true
|
|
5511
|
+
});
|
|
5512
|
+
console.log("[EXULU] Stemmed query:", stemmedQuery);
|
|
5513
|
+
if (stemmedQuery) {
|
|
5514
|
+
query = stemmedQuery;
|
|
5515
|
+
}
|
|
5516
|
+
const result = await embedder.generateFromQuery(
|
|
5517
|
+
context.id,
|
|
5518
|
+
query,
|
|
5519
|
+
{
|
|
5520
|
+
label: table.name.singular,
|
|
5521
|
+
trigger
|
|
5522
|
+
},
|
|
5523
|
+
user?.id,
|
|
5524
|
+
role
|
|
5525
|
+
);
|
|
5526
|
+
if (!result?.chunks?.[0]?.vector) {
|
|
5527
|
+
throw new Error("No vector generated for query.");
|
|
5528
|
+
}
|
|
5529
|
+
vector = result.chunks[0].vector;
|
|
5530
|
+
vectorStr = `ARRAY[${vector.join(",")}]`;
|
|
5531
|
+
vectorExpr = `${vectorStr}::vector`;
|
|
5532
|
+
}
|
|
5533
|
+
let keywordsQuery = [];
|
|
5534
|
+
if (keywords) {
|
|
5535
|
+
console.log("[EXULU] Using keywords:", keywords);
|
|
5536
|
+
let tokens = keywords?.map((keyword) => keyword.trim()).filter((token) => token.length > 0);
|
|
5537
|
+
const sanitized = tokens?.map((token) => {
|
|
5538
|
+
return token.replace(/[^a-zA-Z0-9]/g, "");
|
|
5539
|
+
}).filter((token) => token.length > 0);
|
|
5540
|
+
keywordsQuery = [.../* @__PURE__ */ new Set([...sanitized, ...tokens])];
|
|
5541
|
+
} else if (query) {
|
|
5542
|
+
console.log("[EXULU] Extracting keywords from query:", query);
|
|
5543
|
+
const tokens = query.trim().split(/\s+/).filter((t) => t.length > 0);
|
|
5544
|
+
console.log("[EXULU] Query tokens:", tokens);
|
|
5545
|
+
keywordsQuery = Array.from(new Set(tokens.flatMap((t) => {
|
|
5546
|
+
return t.split(/[^\w]+/).filter((part) => part.length > 0);
|
|
5547
|
+
})));
|
|
5548
|
+
}
|
|
5549
|
+
const language = configuration.language || "english";
|
|
5550
|
+
console.log("[EXULU] Vector search params:", { method, query, cutoffs });
|
|
5551
|
+
let resultChunks = [];
|
|
5552
|
+
switch (method) {
|
|
4653
5553
|
case "tsvector":
|
|
4654
5554
|
chunksQuery.limit(limit * 2);
|
|
4655
|
-
const
|
|
4656
|
-
const sanitizedTokens = tokens.flatMap((t) => {
|
|
4657
|
-
return t.split(/[^\w]+/).filter((part) => part.length > 0);
|
|
4658
|
-
});
|
|
4659
|
-
const orQuery = sanitizedTokens.join(" | ");
|
|
5555
|
+
const orQuery = keywordsQuery.join(" | ");
|
|
4660
5556
|
console.log("[EXULU] FTS query transformation:", {
|
|
4661
5557
|
original: query,
|
|
4662
|
-
|
|
4663
|
-
sanitizedTokens,
|
|
5558
|
+
keywordsQuery,
|
|
4664
5559
|
orQuery,
|
|
4665
5560
|
cutoff: cutoffs?.tsvector
|
|
4666
5561
|
});
|
|
@@ -4708,7 +5603,8 @@ var vectorSearch = async ({
|
|
|
4708
5603
|
query,
|
|
4709
5604
|
cutoffs?.tsvector || 0
|
|
4710
5605
|
]).whereRaw(`(items.archived IS FALSE OR items.archived IS NULL)`).limit(Math.min(matchCount * 2, 500));
|
|
4711
|
-
fullTextQuery = applyFilters(fullTextQuery,
|
|
5606
|
+
fullTextQuery = applyFilters(fullTextQuery, itemFilters, table, "items");
|
|
5607
|
+
fullTextQuery = applyFilters(fullTextQuery, chunkFilters, table, "chunks");
|
|
4712
5608
|
fullTextQuery = applyAccessControl(table, fullTextQuery, user, "items");
|
|
4713
5609
|
let semanticQuery = db3(chunksTable + " as chunks").select([
|
|
4714
5610
|
"chunks.id",
|
|
@@ -4719,7 +5615,8 @@ var vectorSearch = async ({
|
|
|
4719
5615
|
]).leftJoin(mainTable + " as items", "items.id", "chunks.source").whereNotNull("chunks.embedding").whereRaw(`(1 - (chunks.embedding <=> ${vectorExpr})) >= ?`, [
|
|
4720
5616
|
cutoffs?.cosineDistance || 0
|
|
4721
5617
|
]).whereRaw(`(items.archived IS FALSE OR items.archived IS NULL)`).limit(Math.min(matchCount * 2, 500));
|
|
4722
|
-
semanticQuery = applyFilters(semanticQuery,
|
|
5618
|
+
semanticQuery = applyFilters(semanticQuery, itemFilters, table, "items");
|
|
5619
|
+
semanticQuery = applyFilters(semanticQuery, chunkFilters, table, "chunks");
|
|
4723
5620
|
semanticQuery = applyAccessControl(table, semanticQuery, user, "items");
|
|
4724
5621
|
let hybridQuery = db3.with("full_text", fullTextQuery).with("semantic", semanticQuery).select([
|
|
4725
5622
|
"items.id as item_id",
|
|
@@ -4939,8 +5836,10 @@ var vectorSearch = async ({
|
|
|
4939
5836
|
role
|
|
4940
5837
|
});
|
|
4941
5838
|
return {
|
|
4942
|
-
|
|
5839
|
+
itemFilters,
|
|
5840
|
+
chunkFilters,
|
|
4943
5841
|
query,
|
|
5842
|
+
keywords,
|
|
4944
5843
|
method,
|
|
4945
5844
|
context: {
|
|
4946
5845
|
name: table.name.singular,
|
|
@@ -5042,7 +5941,7 @@ var contextToTableDefinition = (context) => {
|
|
|
5042
5941
|
});
|
|
5043
5942
|
return addCoreFields(definition);
|
|
5044
5943
|
};
|
|
5045
|
-
function createSDL(tables, contexts, agents, tools, config, evals, queues2) {
|
|
5944
|
+
function createSDL(tables, contexts, agents, tools, config, evals, queues2, rerankers) {
|
|
5046
5945
|
const contextSchemas = contexts.map(
|
|
5047
5946
|
(context) => contextToTableDefinition(context)
|
|
5048
5947
|
);
|
|
@@ -5123,11 +6022,11 @@ function createSDL(tables, contexts, agents, tools, config, evals, queues2) {
|
|
|
5123
6022
|
${tableNameSingular}ByIds(ids: [ID!]!): [${tableNameSingular}]!
|
|
5124
6023
|
${tableNamePlural}Pagination(limit: Int, page: Int, filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingularUpperCaseFirst}PaginationResult
|
|
5125
6024
|
${tableNameSingular}One(filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingular}
|
|
5126
|
-
${tableNamePlural}Statistics(filters: [Filter${tableNameSingularUpperCaseFirst}], groupBy: String): [StatisticsResult]!
|
|
6025
|
+
${tableNamePlural}Statistics(filters: [Filter${tableNameSingularUpperCaseFirst}], groupBy: String, limit: Int): [StatisticsResult]!
|
|
5127
6026
|
`;
|
|
5128
6027
|
if (table.type === "items") {
|
|
5129
6028
|
typeDefs += `
|
|
5130
|
-
${tableNamePlural}VectorSearch(query: String!, method: VectorMethodEnum!,
|
|
6029
|
+
${tableNamePlural}VectorSearch(query: String!, method: VectorMethodEnum!, itemFilters: [Filter${tableNameSingularUpperCaseFirst}], cutoffs: SearchCutoffs, expand: SearchExpand): ${tableNameSingular}VectorSearchResult
|
|
5131
6030
|
${tableNameSingular}ChunkById(id: ID!): ${tableNameSingular}VectorSearchChunk
|
|
5132
6031
|
`;
|
|
5133
6032
|
}
|
|
@@ -5197,7 +6096,8 @@ function createSDL(tables, contexts, agents, tools, config, evals, queues2) {
|
|
|
5197
6096
|
type ${tableNameSingular}VectorSearchResult {
|
|
5198
6097
|
chunks: [${tableNameSingular}VectorSearchChunk!]!
|
|
5199
6098
|
context: VectoSearchResultContext!
|
|
5200
|
-
|
|
6099
|
+
itemFilters: JSON!
|
|
6100
|
+
chunkFilters: JSON!
|
|
5201
6101
|
query: String!
|
|
5202
6102
|
method: VectorMethodEnum!
|
|
5203
6103
|
}
|
|
@@ -5249,11 +6149,11 @@ type PageInfo {
|
|
|
5249
6149
|
`;
|
|
5250
6150
|
Object.assign(
|
|
5251
6151
|
resolvers.Query,
|
|
5252
|
-
createQueries(table, agents, tools, contexts)
|
|
6152
|
+
createQueries(table, agents, tools, contexts, rerankers)
|
|
5253
6153
|
);
|
|
5254
6154
|
Object.assign(
|
|
5255
6155
|
resolvers.Mutation,
|
|
5256
|
-
createMutations(table, agents, contexts, tools, config)
|
|
6156
|
+
createMutations(table, agents, contexts, rerankers, tools, config)
|
|
5257
6157
|
);
|
|
5258
6158
|
if (table.RBAC) {
|
|
5259
6159
|
const rbacResolverName = table.name.singular;
|
|
@@ -5284,6 +6184,9 @@ type PageInfo {
|
|
|
5284
6184
|
typeDefs += `
|
|
5285
6185
|
contexts: ContextPaginationResult
|
|
5286
6186
|
`;
|
|
6187
|
+
typeDefs += `
|
|
6188
|
+
rerankers: RerankerPaginationResult
|
|
6189
|
+
`;
|
|
5287
6190
|
typeDefs += `
|
|
5288
6191
|
contextById(id: ID!): Context
|
|
5289
6192
|
`;
|
|
@@ -5291,7 +6194,7 @@ type PageInfo {
|
|
|
5291
6194
|
getUniquePromptTags: [String!]!
|
|
5292
6195
|
`;
|
|
5293
6196
|
mutationDefs += `
|
|
5294
|
-
runEval(id: ID!,
|
|
6197
|
+
runEval(id: ID!, test_case_ids: [ID!]): RunEvalReturnPayload
|
|
5295
6198
|
`;
|
|
5296
6199
|
mutationDefs += `
|
|
5297
6200
|
runWorkflow(id: ID!, variables: JSON): RunWorkflowReturnPayload
|
|
@@ -5628,6 +6531,7 @@ type PageInfo {
|
|
|
5628
6531
|
agentBackend: agentBackend2,
|
|
5629
6532
|
inputMessages,
|
|
5630
6533
|
contexts,
|
|
6534
|
+
rerankers,
|
|
5631
6535
|
user: user2,
|
|
5632
6536
|
tools,
|
|
5633
6537
|
config,
|
|
@@ -5889,6 +6793,18 @@ type PageInfo {
|
|
|
5889
6793
|
}
|
|
5890
6794
|
};
|
|
5891
6795
|
};
|
|
6796
|
+
resolvers.Query["rerankers"] = async (_, args, context, info) => {
|
|
6797
|
+
const requestedFields = getRequestedFields(info);
|
|
6798
|
+
return {
|
|
6799
|
+
items: rerankers.map((reranker) => {
|
|
6800
|
+
const object = {};
|
|
6801
|
+
requestedFields.forEach((field) => {
|
|
6802
|
+
object[field] = reranker[field];
|
|
6803
|
+
});
|
|
6804
|
+
return object;
|
|
6805
|
+
})
|
|
6806
|
+
};
|
|
6807
|
+
};
|
|
5892
6808
|
resolvers.Query["contexts"] = async (_, args, context, info) => {
|
|
5893
6809
|
const data = await Promise.all(
|
|
5894
6810
|
contexts.map(async (context2) => {
|
|
@@ -5941,13 +6857,23 @@ type PageInfo {
|
|
|
5941
6857
|
active: context2.active,
|
|
5942
6858
|
sources,
|
|
5943
6859
|
processor,
|
|
5944
|
-
fields:
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
|
|
6860
|
+
fields: await Promise.all(
|
|
6861
|
+
context2.fields.map(async (field) => {
|
|
6862
|
+
const label = field.name?.replace("_s3key", "");
|
|
6863
|
+
if (field.type === "file" && !field.name.endsWith("_s3key")) {
|
|
6864
|
+
field.name = field.name + "_s3key";
|
|
6865
|
+
}
|
|
6866
|
+
return {
|
|
6867
|
+
...field,
|
|
6868
|
+
name: sanitizeName(field.name),
|
|
6869
|
+
editable: field.editable,
|
|
6870
|
+
...field.type === "file" ? {
|
|
6871
|
+
allowedFileTypes: field.allowedFileTypes
|
|
6872
|
+
} : {},
|
|
6873
|
+
label: field.name?.replace("_s3key", "")
|
|
6874
|
+
};
|
|
6875
|
+
})
|
|
6876
|
+
)
|
|
5951
6877
|
};
|
|
5952
6878
|
})
|
|
5953
6879
|
);
|
|
@@ -6032,6 +6958,7 @@ type PageInfo {
|
|
|
6032
6958
|
return {
|
|
6033
6959
|
...field,
|
|
6034
6960
|
name: sanitizeName(field.name),
|
|
6961
|
+
editable: field.editable,
|
|
6035
6962
|
...field.type === "file" ? {
|
|
6036
6963
|
allowedFileTypes: field.allowedFileTypes
|
|
6037
6964
|
} : {},
|
|
@@ -6060,31 +6987,45 @@ type PageInfo {
|
|
|
6060
6987
|
if (!backend) {
|
|
6061
6988
|
return null;
|
|
6062
6989
|
}
|
|
6063
|
-
return await backend.tool(instance.id, agents);
|
|
6990
|
+
return await backend.tool(instance.id, agents, contexts, rerankers);
|
|
6064
6991
|
})
|
|
6065
6992
|
);
|
|
6993
|
+
let agenticRetrievalTool = void 0;
|
|
6066
6994
|
const filtered = agentTools.filter(
|
|
6067
|
-
(
|
|
6995
|
+
(tool3) => tool3 !== null
|
|
6068
6996
|
);
|
|
6069
6997
|
let allTools = [...filtered, ...tools];
|
|
6998
|
+
if (contexts?.length) {
|
|
6999
|
+
agenticRetrievalTool = createAgenticRetrievalTool({
|
|
7000
|
+
contexts,
|
|
7001
|
+
rerankers,
|
|
7002
|
+
user: context.user,
|
|
7003
|
+
role: context.user?.role?.id,
|
|
7004
|
+
model: void 0
|
|
7005
|
+
// irrelevant at this point as we only retrieve the tool information here, not execute it
|
|
7006
|
+
});
|
|
7007
|
+
if (agenticRetrievalTool) {
|
|
7008
|
+
allTools.push(agenticRetrievalTool);
|
|
7009
|
+
}
|
|
7010
|
+
}
|
|
6070
7011
|
if (search && search.trim()) {
|
|
6071
7012
|
const searchTerm = search.toLowerCase().trim();
|
|
6072
7013
|
allTools = allTools.filter(
|
|
6073
|
-
(
|
|
7014
|
+
(tool3) => tool3.name?.toLowerCase().includes(searchTerm) || tool3.description?.toLowerCase().includes(searchTerm)
|
|
6074
7015
|
);
|
|
6075
7016
|
}
|
|
6076
7017
|
if (category && category.trim()) {
|
|
6077
|
-
allTools = allTools.filter((
|
|
7018
|
+
allTools = allTools.filter((tool3) => tool3.category === category);
|
|
6078
7019
|
}
|
|
6079
7020
|
const total = allTools.length;
|
|
6080
7021
|
const start = page * limit;
|
|
6081
7022
|
const end = start + limit;
|
|
6082
7023
|
const paginatedTools = allTools.slice(start, end);
|
|
6083
7024
|
return {
|
|
6084
|
-
items: paginatedTools.map((
|
|
7025
|
+
items: paginatedTools.map((tool3) => {
|
|
6085
7026
|
const object = {};
|
|
6086
7027
|
requestedFields.forEach((field) => {
|
|
6087
|
-
object[field] =
|
|
7028
|
+
object[field] = tool3[field];
|
|
6088
7029
|
});
|
|
6089
7030
|
return object;
|
|
6090
7031
|
}),
|
|
@@ -6094,7 +7035,7 @@ type PageInfo {
|
|
|
6094
7035
|
};
|
|
6095
7036
|
};
|
|
6096
7037
|
resolvers.Query["toolCategories"] = async () => {
|
|
6097
|
-
const array = tools.map((
|
|
7038
|
+
const array = tools.map((tool3) => tool3.category).filter((category) => category && typeof category === "string");
|
|
6098
7039
|
array.push("contexts");
|
|
6099
7040
|
array.push("agents");
|
|
6100
7041
|
return [...new Set(array)].sort();
|
|
@@ -6181,6 +7122,11 @@ type PageInfo {
|
|
|
6181
7122
|
items: [Context]!
|
|
6182
7123
|
}
|
|
6183
7124
|
`;
|
|
7125
|
+
modelDefs += `
|
|
7126
|
+
type RerankerPaginationResult {
|
|
7127
|
+
items: [Reranker]!
|
|
7128
|
+
}
|
|
7129
|
+
`;
|
|
6184
7130
|
modelDefs += `
|
|
6185
7131
|
type ToolPaginationResult {
|
|
6186
7132
|
items: [Tool]!
|
|
@@ -6282,6 +7228,11 @@ type Context {
|
|
|
6282
7228
|
sources: [ContextSource]
|
|
6283
7229
|
processor: ContextProcessor
|
|
6284
7230
|
}
|
|
7231
|
+
type Reranker {
|
|
7232
|
+
id: ID!
|
|
7233
|
+
name: String!
|
|
7234
|
+
description: String
|
|
7235
|
+
}
|
|
6285
7236
|
type Embedder {
|
|
6286
7237
|
name: String!
|
|
6287
7238
|
id: ID!
|
|
@@ -6693,11 +7644,14 @@ var createUppyRoutes = async (app, contexts, config) => {
|
|
|
6693
7644
|
return;
|
|
6694
7645
|
}
|
|
6695
7646
|
let bucket = key.split("/")[0];
|
|
7647
|
+
console.log("[EXULU] bucket", bucket);
|
|
6696
7648
|
if (!bucket || typeof bucket !== "string" || bucket.trim() === "") {
|
|
6697
7649
|
res.status(400).json({ error: "Missing or invalid `bucket` (should be the first part of the key before the first slash)." });
|
|
6698
7650
|
return;
|
|
6699
7651
|
}
|
|
7652
|
+
console.log("[EXULU] key for download before split", key);
|
|
6700
7653
|
key = key.split("/").slice(1).join("/");
|
|
7654
|
+
console.log("[EXULU] key for download after split", key);
|
|
6701
7655
|
if (typeof key !== "string" || key.trim() === "") {
|
|
6702
7656
|
res.status(400).json({ error: "Missing or invalid `key` query parameter." });
|
|
6703
7657
|
return;
|
|
@@ -7088,53 +8042,135 @@ var createUppyRoutes = async (app, contexts, config) => {
|
|
|
7088
8042
|
|
|
7089
8043
|
// src/registry/classes.ts
|
|
7090
8044
|
var import_officeparser = require("officeparser");
|
|
8045
|
+
var import_fs2 = require("fs");
|
|
7091
8046
|
var s3Client2;
|
|
7092
8047
|
function sanitizeToolName(name) {
|
|
7093
8048
|
if (typeof name !== "string") return "";
|
|
7094
|
-
let sanitized = name.replace(/[^a-zA-Z0-9_
|
|
8049
|
+
let sanitized = name.replace(/[^a-zA-Z0-9_.\:-]+/g, "_");
|
|
8050
|
+
if (sanitized.length > 0 && !/^[a-zA-Z_]/.test(sanitized)) {
|
|
8051
|
+
sanitized = "_" + sanitized;
|
|
8052
|
+
}
|
|
7095
8053
|
sanitized = sanitized.replace(/^_+|_+$/g, "");
|
|
7096
|
-
if (sanitized
|
|
7097
|
-
sanitized =
|
|
8054
|
+
if (!sanitized) {
|
|
8055
|
+
sanitized = "tool";
|
|
8056
|
+
}
|
|
8057
|
+
if (sanitized.length > 64) {
|
|
8058
|
+
sanitized = sanitized.substring(0, 64);
|
|
8059
|
+
}
|
|
8060
|
+
if (!/^[a-zA-Z_]/.test(sanitized)) {
|
|
8061
|
+
sanitized = "_" + sanitized.substring(1);
|
|
7098
8062
|
}
|
|
7099
8063
|
return sanitized;
|
|
7100
8064
|
}
|
|
7101
|
-
var
|
|
7102
|
-
|
|
8065
|
+
var createNewMemoryItemTool = (agent, context) => {
|
|
8066
|
+
const fields = {
|
|
8067
|
+
name: import_zod2.z.string().describe("The name of the item to create"),
|
|
8068
|
+
description: import_zod2.z.string().describe("The description of the item to create")
|
|
8069
|
+
};
|
|
8070
|
+
for (const field of context.fields) {
|
|
8071
|
+
switch (field.type) {
|
|
8072
|
+
case "text":
|
|
8073
|
+
case "longText":
|
|
8074
|
+
case "shortText":
|
|
8075
|
+
case "code":
|
|
8076
|
+
case "enum":
|
|
8077
|
+
fields[field.name] = import_zod2.z.string().describe("The " + field.name + " of the item to create");
|
|
8078
|
+
break;
|
|
8079
|
+
case "json":
|
|
8080
|
+
fields[field.name] = import_zod2.z.string({}).describe("The " + field.name + " of the item to create, it should be a valid JSON string.");
|
|
8081
|
+
break;
|
|
8082
|
+
case "markdown":
|
|
8083
|
+
fields[field.name] = import_zod2.z.string().describe("The " + field.name + " of the item to create, it should be a valid Markdown string.");
|
|
8084
|
+
break;
|
|
8085
|
+
case "number":
|
|
8086
|
+
fields[field.name] = import_zod2.z.number().describe("The " + field.name + " of the item to create");
|
|
8087
|
+
break;
|
|
8088
|
+
case "boolean":
|
|
8089
|
+
fields[field.name] = import_zod2.z.boolean().describe("The " + field.name + " of the item to create");
|
|
8090
|
+
break;
|
|
8091
|
+
case "file":
|
|
8092
|
+
case "uuid":
|
|
8093
|
+
case "date":
|
|
8094
|
+
break;
|
|
8095
|
+
default:
|
|
8096
|
+
fields[field.name] = import_zod2.z.string().describe("The " + field.name + " of the item to create");
|
|
8097
|
+
break;
|
|
8098
|
+
}
|
|
8099
|
+
}
|
|
8100
|
+
return new ExuluTool({
|
|
8101
|
+
id: "create_" + agent.name + "_memory_item",
|
|
8102
|
+
name: "Create " + agent.name + " Memory Item",
|
|
8103
|
+
category: agent.name + "_memory",
|
|
8104
|
+
description: "Create a new memory item in the " + agent.name + " memory context",
|
|
8105
|
+
type: "function",
|
|
8106
|
+
inputSchema: import_zod2.z.object(fields),
|
|
8107
|
+
config: [],
|
|
8108
|
+
execute: async ({ name, description, mode, information, exuluConfig, user }) => {
|
|
8109
|
+
let result = { result: "" };
|
|
8110
|
+
switch (mode) {
|
|
8111
|
+
case "learnings":
|
|
8112
|
+
break;
|
|
8113
|
+
case "knowledge":
|
|
8114
|
+
const newItem = {
|
|
8115
|
+
name,
|
|
8116
|
+
description,
|
|
8117
|
+
information,
|
|
8118
|
+
rights_mode: "public"
|
|
8119
|
+
};
|
|
8120
|
+
const { item: createdItem, job: createdJob } = await context.createItem(
|
|
8121
|
+
newItem,
|
|
8122
|
+
exuluConfig,
|
|
8123
|
+
user?.id,
|
|
8124
|
+
user?.role?.id,
|
|
8125
|
+
false
|
|
8126
|
+
);
|
|
8127
|
+
if (createdJob) {
|
|
8128
|
+
result = {
|
|
8129
|
+
result: `Created a Job to create the memory item with the following ID: ${createdJob}`
|
|
8130
|
+
};
|
|
8131
|
+
} else if (createdItem) {
|
|
8132
|
+
result = {
|
|
8133
|
+
result: `Created memory item with the following ID: ${createdItem.id}`
|
|
8134
|
+
};
|
|
8135
|
+
} else {
|
|
8136
|
+
result = {
|
|
8137
|
+
result: `Failed to create memory item`
|
|
8138
|
+
};
|
|
8139
|
+
}
|
|
8140
|
+
break;
|
|
8141
|
+
default:
|
|
8142
|
+
throw new Error(`Invalid mode: ${mode}`);
|
|
8143
|
+
}
|
|
8144
|
+
return result;
|
|
8145
|
+
}
|
|
8146
|
+
});
|
|
8147
|
+
};
|
|
8148
|
+
var createProjectItemsRetrievalTool = async ({
|
|
7103
8149
|
user,
|
|
7104
8150
|
role,
|
|
7105
8151
|
contexts,
|
|
7106
8152
|
projectId
|
|
7107
8153
|
}) => {
|
|
7108
8154
|
let project;
|
|
7109
|
-
const cachedProject = projectsCache.get(projectId);
|
|
7110
8155
|
const OneMinuteAgo = new Date(Date.now() - 1e3 * 60);
|
|
7111
|
-
|
|
7112
|
-
|
|
7113
|
-
|
|
7114
|
-
|
|
7115
|
-
project = await db3.from("projects").where("id", projectId).first();
|
|
7116
|
-
if (project) {
|
|
7117
|
-
projectsCache.set(projectId, {
|
|
7118
|
-
age: /* @__PURE__ */ new Date(),
|
|
7119
|
-
project
|
|
7120
|
-
});
|
|
7121
|
-
} else {
|
|
7122
|
-
return;
|
|
7123
|
-
}
|
|
8156
|
+
const { db: db3 } = await postgresClient();
|
|
8157
|
+
project = await db3.from("projects").where("id", projectId).first();
|
|
8158
|
+
if (!project) {
|
|
8159
|
+
return;
|
|
7124
8160
|
}
|
|
7125
8161
|
console.log("[EXULU] Project search tool created for project", project);
|
|
7126
8162
|
if (!project.project_items?.length) {
|
|
7127
8163
|
return;
|
|
7128
8164
|
}
|
|
7129
|
-
const projectRetrievalTool = new
|
|
7130
|
-
id: "
|
|
7131
|
-
name: "
|
|
8165
|
+
const projectRetrievalTool = new ExuluTool({
|
|
8166
|
+
id: "context_search_in_knowledge_items_added_to_project_" + projectId,
|
|
8167
|
+
name: "context_search in knowledge items added to project " + project.name,
|
|
7132
8168
|
description: "This tool retrieves information about a project from conversations and items that were added to the project " + project.name + ".",
|
|
7133
|
-
inputSchema:
|
|
7134
|
-
query:
|
|
7135
|
-
keywords:
|
|
8169
|
+
inputSchema: import_zod2.z.object({
|
|
8170
|
+
query: import_zod2.z.string().describe("The query to retrieve information about the project " + project.name + "."),
|
|
8171
|
+
keywords: import_zod2.z.array(import_zod2.z.string()).describe("The most relevant keywords in the query, such as names of people, companies, products, etc. in the project " + project.name + ".")
|
|
7136
8172
|
}),
|
|
7137
|
-
type: "
|
|
8173
|
+
type: "context",
|
|
7138
8174
|
category: "project",
|
|
7139
8175
|
config: [],
|
|
7140
8176
|
execute: async ({ query, keywords }) => {
|
|
@@ -7162,16 +8198,17 @@ var createProjectRetrievalTool = async ({
|
|
|
7162
8198
|
}
|
|
7163
8199
|
const itemIds = set[contextName];
|
|
7164
8200
|
console.log("[EXULU] Project search tool searching through items", itemIds);
|
|
7165
|
-
|
|
8201
|
+
const result = await context.search({
|
|
7166
8202
|
// todo check if it is more performant to use a concatenation of
|
|
7167
8203
|
// the query and keywords, or just the keywords, instead of the
|
|
7168
8204
|
// query itself.
|
|
7169
8205
|
query,
|
|
7170
|
-
|
|
8206
|
+
itemFilters: [{
|
|
7171
8207
|
id: {
|
|
7172
8208
|
in: itemIds
|
|
7173
8209
|
}
|
|
7174
8210
|
}],
|
|
8211
|
+
chunkFilters: [],
|
|
7175
8212
|
user,
|
|
7176
8213
|
role,
|
|
7177
8214
|
method: "hybridSearch",
|
|
@@ -7183,6 +8220,15 @@ var createProjectRetrievalTool = async ({
|
|
|
7183
8220
|
limit: 10,
|
|
7184
8221
|
page: 1
|
|
7185
8222
|
});
|
|
8223
|
+
return {
|
|
8224
|
+
result: result.chunks.map((chunk) => ({
|
|
8225
|
+
...chunk,
|
|
8226
|
+
context: {
|
|
8227
|
+
name: context.name,
|
|
8228
|
+
id: context.id
|
|
8229
|
+
}
|
|
8230
|
+
}))
|
|
8231
|
+
};
|
|
7186
8232
|
}));
|
|
7187
8233
|
console.log("[EXULU] Project search tool results", results);
|
|
7188
8234
|
return {
|
|
@@ -7192,7 +8238,79 @@ var createProjectRetrievalTool = async ({
|
|
|
7192
8238
|
});
|
|
7193
8239
|
return projectRetrievalTool;
|
|
7194
8240
|
};
|
|
7195
|
-
var
|
|
8241
|
+
var createSessionItemsRetrievalTool = async ({
|
|
8242
|
+
user,
|
|
8243
|
+
role,
|
|
8244
|
+
contexts,
|
|
8245
|
+
items
|
|
8246
|
+
}) => {
|
|
8247
|
+
console.log("[EXULU] Session search tool created for session", items);
|
|
8248
|
+
const sessionItemsRetrievalTool = new ExuluTool({
|
|
8249
|
+
id: "session_items_information_context_search",
|
|
8250
|
+
name: "context_search in knowledge items added to session.",
|
|
8251
|
+
description: "Context search in knowledge items added to session.",
|
|
8252
|
+
inputSchema: import_zod2.z.object({
|
|
8253
|
+
query: import_zod2.z.string().describe("The query to retrieve information from knowledge items added to the session.")
|
|
8254
|
+
}),
|
|
8255
|
+
type: "context",
|
|
8256
|
+
category: "session",
|
|
8257
|
+
config: [],
|
|
8258
|
+
execute: async ({ query, keywords }) => {
|
|
8259
|
+
console.log("[EXULU] Session search tool searching for session items", items);
|
|
8260
|
+
const set = {};
|
|
8261
|
+
for (const item of items) {
|
|
8262
|
+
const context = item.split("/")[0];
|
|
8263
|
+
if (!context) {
|
|
8264
|
+
throw new Error("The item added to the project does not have a valid gid with the context id as the prefix before the first slash.");
|
|
8265
|
+
}
|
|
8266
|
+
const id = item.split("/").slice(1).join("/");
|
|
8267
|
+
if (set[context]) {
|
|
8268
|
+
set[context].push(id);
|
|
8269
|
+
} else {
|
|
8270
|
+
set[context] = [id];
|
|
8271
|
+
}
|
|
8272
|
+
}
|
|
8273
|
+
console.log("[EXULU] Session search tool searching through contexts", Object.keys(set));
|
|
8274
|
+
const results = await Promise.all(Object.keys(set).map(async (contextName, index) => {
|
|
8275
|
+
const context = contexts.find((context2) => context2.id === contextName);
|
|
8276
|
+
if (!context) {
|
|
8277
|
+
console.error("[EXULU] Context not found for project information retrieval tool.", contextName);
|
|
8278
|
+
return [];
|
|
8279
|
+
}
|
|
8280
|
+
const itemIds = set[contextName];
|
|
8281
|
+
console.log("[EXULU] Session search tool searching through items", itemIds);
|
|
8282
|
+
return await context.search({
|
|
8283
|
+
// todo check if it is more performant to use a concatenation of
|
|
8284
|
+
// the query and keywords, or just the keywords, instead of the
|
|
8285
|
+
// query itself.
|
|
8286
|
+
query,
|
|
8287
|
+
itemFilters: [{
|
|
8288
|
+
id: {
|
|
8289
|
+
in: itemIds
|
|
8290
|
+
}
|
|
8291
|
+
}],
|
|
8292
|
+
chunkFilters: [],
|
|
8293
|
+
user,
|
|
8294
|
+
role,
|
|
8295
|
+
method: "hybridSearch",
|
|
8296
|
+
sort: {
|
|
8297
|
+
field: "updatedAt",
|
|
8298
|
+
direction: "desc"
|
|
8299
|
+
},
|
|
8300
|
+
trigger: "tool",
|
|
8301
|
+
limit: 10,
|
|
8302
|
+
page: 1
|
|
8303
|
+
});
|
|
8304
|
+
}));
|
|
8305
|
+
console.log("[EXULU] Session search tool results", results);
|
|
8306
|
+
return {
|
|
8307
|
+
result: JSON.stringify(results.flat())
|
|
8308
|
+
};
|
|
8309
|
+
}
|
|
8310
|
+
});
|
|
8311
|
+
return sessionItemsRetrievalTool;
|
|
8312
|
+
};
|
|
8313
|
+
var convertToolsArrayToObject = async (currentTools, approvedTools, allExuluTools, configs, providerapikey, contexts, rerankers, user, exuluConfig, sessionID, req, project, items, model, agentInstance) => {
|
|
7196
8314
|
if (!currentTools) return {};
|
|
7197
8315
|
if (!allExuluTools) {
|
|
7198
8316
|
allExuluTools = [];
|
|
@@ -7201,8 +8319,9 @@ var convertToolsArrayToObject = async (currentTools, allExuluTools, configs, pro
|
|
|
7201
8319
|
if (!contexts) {
|
|
7202
8320
|
contexts = [];
|
|
7203
8321
|
}
|
|
8322
|
+
let projectRetrievalTool;
|
|
7204
8323
|
if (project) {
|
|
7205
|
-
|
|
8324
|
+
projectRetrievalTool = await createProjectItemsRetrievalTool({
|
|
7206
8325
|
user,
|
|
7207
8326
|
role: user?.role?.id,
|
|
7208
8327
|
contexts,
|
|
@@ -7212,11 +8331,54 @@ var convertToolsArrayToObject = async (currentTools, allExuluTools, configs, pro
|
|
|
7212
8331
|
currentTools.push(projectRetrievalTool);
|
|
7213
8332
|
}
|
|
7214
8333
|
}
|
|
7215
|
-
|
|
7216
|
-
|
|
7217
|
-
|
|
8334
|
+
console.log("[EXULU] Convert tools array to object, session items", items);
|
|
8335
|
+
if (items) {
|
|
8336
|
+
const sessionItemsRetrievalTool = await createSessionItemsRetrievalTool({
|
|
8337
|
+
user,
|
|
8338
|
+
role: user?.role?.id,
|
|
8339
|
+
contexts,
|
|
8340
|
+
items
|
|
8341
|
+
});
|
|
8342
|
+
if (sessionItemsRetrievalTool) {
|
|
8343
|
+
currentTools.push(sessionItemsRetrievalTool);
|
|
8344
|
+
}
|
|
8345
|
+
}
|
|
8346
|
+
console.log("[EXULU] Creating agentic search tool", contexts?.length, model);
|
|
8347
|
+
if (contexts?.length && model) {
|
|
8348
|
+
const agenticSearchTool = createAgenticRetrievalTool({
|
|
8349
|
+
contexts: contexts.filter((context) => context.id !== agentInstance?.memory),
|
|
8350
|
+
// dont include the agents memory in the agentic search tool!
|
|
8351
|
+
rerankers: rerankers || [],
|
|
8352
|
+
user,
|
|
8353
|
+
role: user?.role?.id,
|
|
8354
|
+
model,
|
|
8355
|
+
projectRetrievalTool
|
|
8356
|
+
});
|
|
8357
|
+
if (!agenticSearchTool) {
|
|
8358
|
+
throw new Error("Agentic search tool could not be instantiated, this is an internal error in the Exulu framework, please contact support.");
|
|
8359
|
+
}
|
|
8360
|
+
if (agenticSearchTool) {
|
|
8361
|
+
const index = currentTools.findIndex((tool3) => tool3.id === "agentic_context_search");
|
|
8362
|
+
if (index !== -1) {
|
|
8363
|
+
currentTools[index] = {
|
|
8364
|
+
...currentTools[index],
|
|
8365
|
+
// important to keep the original tool config
|
|
8366
|
+
...agenticSearchTool
|
|
8367
|
+
};
|
|
8368
|
+
}
|
|
8369
|
+
}
|
|
8370
|
+
} else {
|
|
8371
|
+
const agenticSearchTool = currentTools.find((tool3) => tool3.id === "agentic_context_search");
|
|
8372
|
+
if (agenticSearchTool) {
|
|
8373
|
+
currentTools.splice(currentTools.indexOf(agenticSearchTool), 1);
|
|
8374
|
+
}
|
|
8375
|
+
}
|
|
8376
|
+
const sanitizedTools = currentTools ? currentTools.map((tool3) => ({
|
|
8377
|
+
...tool3,
|
|
8378
|
+
name: sanitizeToolName(tool3.name)
|
|
7218
8379
|
})) : [];
|
|
7219
8380
|
console.log("[EXULU] Sanitized tools", sanitizedTools.map((x) => x.name + " (" + x.id + ")"));
|
|
8381
|
+
console.log("[EXULU] Approved tools", approvedTools);
|
|
7220
8382
|
return {
|
|
7221
8383
|
...sanitizedTools?.reduce(
|
|
7222
8384
|
(prev, cur) => {
|
|
@@ -7225,11 +8387,16 @@ var convertToolsArrayToObject = async (currentTools, allExuluTools, configs, pro
|
|
|
7225
8387
|
const defaultConfigDescription = toolVariableConfig?.config.find((config) => config.name === "description")?.default;
|
|
7226
8388
|
const toolDescription = cur.description;
|
|
7227
8389
|
const description = userDefinedConfigDescription || defaultConfigDescription || toolDescription;
|
|
8390
|
+
console.log("[EXULU] Tool", cur.name, "needs approval", approvedTools?.includes(cur.name) ? false : true);
|
|
7228
8391
|
return {
|
|
7229
8392
|
...prev,
|
|
7230
8393
|
[cur.name]: {
|
|
7231
8394
|
...cur.tool,
|
|
7232
8395
|
description,
|
|
8396
|
+
// The approvedTools array uses the tool.name lookup as the frontend
|
|
8397
|
+
// Vercel AI SDK uses the sanitized tool name as the key, so this matches.
|
|
8398
|
+
needsApproval: approvedTools?.includes("tool-" + cur.name) ? false : true,
|
|
8399
|
+
// todo make configurable
|
|
7233
8400
|
async *execute(inputs, options) {
|
|
7234
8401
|
console.log("[EXULU] Executing tool", cur.name, "with inputs", inputs, "and options", options);
|
|
7235
8402
|
if (!cur.tool?.execute) {
|
|
@@ -7290,11 +8457,16 @@ var convertToolsArrayToObject = async (currentTools, allExuluTools, configs, pro
|
|
|
7290
8457
|
acc[curr.id] = curr;
|
|
7291
8458
|
return acc;
|
|
7292
8459
|
}, {});
|
|
8460
|
+
const toolVariablesConfigData = toolVariableConfig ? toolVariableConfig.config.reduce((acc, curr) => {
|
|
8461
|
+
acc[curr.name] = curr.value;
|
|
8462
|
+
return acc;
|
|
8463
|
+
}, {}) : {};
|
|
7293
8464
|
const response = await cur.tool.execute({
|
|
7294
8465
|
...inputs,
|
|
8466
|
+
model,
|
|
7295
8467
|
sessionID,
|
|
7296
8468
|
req,
|
|
7297
|
-
// Convert config to object format if a config object
|
|
8469
|
+
// Convert config to object format if a config object
|
|
7298
8470
|
// is available, after we added the .value property
|
|
7299
8471
|
// by hydrating it from the variables table.
|
|
7300
8472
|
providerapikey,
|
|
@@ -7304,10 +8476,7 @@ var convertToolsArrayToObject = async (currentTools, allExuluTools, configs, pro
|
|
|
7304
8476
|
contexts: contextsMap,
|
|
7305
8477
|
upload,
|
|
7306
8478
|
exuluConfig,
|
|
7307
|
-
toolVariablesConfig:
|
|
7308
|
-
acc[curr.name] = curr.value;
|
|
7309
|
-
return acc;
|
|
7310
|
-
}, {}) : {}
|
|
8479
|
+
toolVariablesConfig: toolVariablesConfigData
|
|
7311
8480
|
}, options);
|
|
7312
8481
|
await updateStatistic({
|
|
7313
8482
|
name: "count",
|
|
@@ -7318,8 +8487,17 @@ var convertToolsArrayToObject = async (currentTools, allExuluTools, configs, pro
|
|
|
7318
8487
|
user: user?.id,
|
|
7319
8488
|
role: user?.role?.id
|
|
7320
8489
|
});
|
|
7321
|
-
|
|
7322
|
-
|
|
8490
|
+
if (response && typeof response === "object" && Symbol.asyncIterator in response) {
|
|
8491
|
+
let lastValue;
|
|
8492
|
+
for await (const value of response) {
|
|
8493
|
+
yield value;
|
|
8494
|
+
lastValue = value;
|
|
8495
|
+
}
|
|
8496
|
+
return lastValue;
|
|
8497
|
+
} else {
|
|
8498
|
+
yield response;
|
|
8499
|
+
return response;
|
|
8500
|
+
}
|
|
7323
8501
|
}
|
|
7324
8502
|
}
|
|
7325
8503
|
};
|
|
@@ -7329,16 +8507,27 @@ var convertToolsArrayToObject = async (currentTools, allExuluTools, configs, pro
|
|
|
7329
8507
|
// askForConfirmation
|
|
7330
8508
|
};
|
|
7331
8509
|
};
|
|
7332
|
-
var hydrateVariables = async (
|
|
8510
|
+
var hydrateVariables = async (tool3) => {
|
|
7333
8511
|
const { db: db3 } = await postgresClient();
|
|
7334
|
-
const promises =
|
|
8512
|
+
const promises = tool3.config.map(async (toolConfig) => {
|
|
7335
8513
|
if (!toolConfig.variable) {
|
|
7336
8514
|
return toolConfig;
|
|
7337
8515
|
}
|
|
7338
8516
|
const variableName = toolConfig.variable;
|
|
8517
|
+
const type = toolConfig.type;
|
|
8518
|
+
if (type === "boolean") {
|
|
8519
|
+
toolConfig.value = toolConfig.variable === "true" || toolConfig.variable === true || toolConfig.variable === 1;
|
|
8520
|
+
return toolConfig;
|
|
8521
|
+
} else if (type === "number") {
|
|
8522
|
+
toolConfig.value = parseInt(toolConfig.variable.toString());
|
|
8523
|
+
return toolConfig;
|
|
8524
|
+
} else if (type === "string") {
|
|
8525
|
+
toolConfig.value = toolConfig.variable;
|
|
8526
|
+
return toolConfig;
|
|
8527
|
+
}
|
|
7339
8528
|
const variable = await db3.from("variables").where({ name: variableName }).first();
|
|
7340
8529
|
if (!variable) {
|
|
7341
|
-
throw new Error("Variable " + variableName + " not found.");
|
|
8530
|
+
throw new Error("Variable " + variableName + " not found in hydrateVariables method, with type " + type + ".");
|
|
7342
8531
|
}
|
|
7343
8532
|
let value = variable.value;
|
|
7344
8533
|
if (variable.encrypted) {
|
|
@@ -7349,7 +8538,7 @@ var hydrateVariables = async (tool2) => {
|
|
|
7349
8538
|
return toolConfig;
|
|
7350
8539
|
});
|
|
7351
8540
|
await Promise.all(promises);
|
|
7352
|
-
return
|
|
8541
|
+
return tool3;
|
|
7353
8542
|
};
|
|
7354
8543
|
function generateSlug(name) {
|
|
7355
8544
|
const normalized = name.normalize("NFKD").replace(/[\u0300-\u036f]/g, "");
|
|
@@ -7357,6 +8546,21 @@ function generateSlug(name) {
|
|
|
7357
8546
|
const slug = lowercase.replace(/[\W_]+/g, "-").replace(/^-+|-+$/g, "");
|
|
7358
8547
|
return slug;
|
|
7359
8548
|
}
|
|
8549
|
+
var ExuluReranker4 = class {
|
|
8550
|
+
id;
|
|
8551
|
+
name;
|
|
8552
|
+
description;
|
|
8553
|
+
execute;
|
|
8554
|
+
constructor({ id, name, description, execute: execute2 }) {
|
|
8555
|
+
this.id = id;
|
|
8556
|
+
this.name = name;
|
|
8557
|
+
this.description = description;
|
|
8558
|
+
this.execute = execute2;
|
|
8559
|
+
}
|
|
8560
|
+
async run(query, chunks) {
|
|
8561
|
+
return await this.execute({ query, chunks });
|
|
8562
|
+
}
|
|
8563
|
+
};
|
|
7360
8564
|
function errorHandler(error) {
|
|
7361
8565
|
if (error == null) {
|
|
7362
8566
|
return "unknown error";
|
|
@@ -7467,19 +8671,19 @@ var ExuluAgent3 = class {
|
|
|
7467
8671
|
return this.config?.name || "";
|
|
7468
8672
|
}
|
|
7469
8673
|
// Exports the agent as a tool that can be used by another agent
|
|
7470
|
-
tool = async (instance, agents) => {
|
|
8674
|
+
tool = async (instance, agents, contexts, rerankers) => {
|
|
7471
8675
|
const agentInstance = await loadAgent(instance);
|
|
7472
8676
|
if (!agentInstance) {
|
|
7473
8677
|
return null;
|
|
7474
8678
|
}
|
|
7475
|
-
return new
|
|
8679
|
+
return new ExuluTool({
|
|
7476
8680
|
id: agentInstance.id,
|
|
7477
8681
|
name: `${agentInstance.name}`,
|
|
7478
8682
|
type: "agent",
|
|
7479
8683
|
category: "agents",
|
|
7480
|
-
inputSchema:
|
|
7481
|
-
prompt:
|
|
7482
|
-
information:
|
|
8684
|
+
inputSchema: import_zod2.z.object({
|
|
8685
|
+
prompt: import_zod2.z.string().describe("The prompt (usually a question for the agent) to send to the agent."),
|
|
8686
|
+
information: import_zod2.z.string().describe("A summary of relevant context / information from the current session")
|
|
7483
8687
|
}),
|
|
7484
8688
|
description: `This tool calls an AI agent named: ${agentInstance.name}. The agent does the following: ${agentInstance.description}.`,
|
|
7485
8689
|
config: [],
|
|
@@ -7488,7 +8692,7 @@ var ExuluAgent3 = class {
|
|
|
7488
8692
|
if (!hasAccessToAgent) {
|
|
7489
8693
|
throw new Error("You don't have access to this agent.");
|
|
7490
8694
|
}
|
|
7491
|
-
let enabledTools = await getEnabledTools(agentInstance, allExuluTools, [], agents, user);
|
|
8695
|
+
let enabledTools = await getEnabledTools(agentInstance, allExuluTools, contexts, rerankers, [], agents, user);
|
|
7492
8696
|
const variableName = agentInstance.providerapikey;
|
|
7493
8697
|
let providerapikey;
|
|
7494
8698
|
if (variableName) {
|
|
@@ -7510,6 +8714,9 @@ var ExuluAgent3 = class {
|
|
|
7510
8714
|
console.log("[EXULU] Prompt for agent '" + agentInstance.name + "' that is being called as a tool", prompt.slice(0, 100) + "...");
|
|
7511
8715
|
console.log("[EXULU] Instructions for agent '" + agentInstance.name + "' that is being called as a tool", agentInstance.instructions?.slice(0, 100) + "...");
|
|
7512
8716
|
const response = await this.generateSync({
|
|
8717
|
+
agentInstance,
|
|
8718
|
+
contexts,
|
|
8719
|
+
rerankers,
|
|
7513
8720
|
instructions: agentInstance.instructions,
|
|
7514
8721
|
prompt: "The user has asked the following question: " + prompt + " and the following information is available: " + information,
|
|
7515
8722
|
providerapikey,
|
|
@@ -7548,8 +8755,10 @@ var ExuluAgent3 = class {
|
|
|
7548
8755
|
toolConfigs,
|
|
7549
8756
|
providerapikey,
|
|
7550
8757
|
contexts,
|
|
8758
|
+
rerankers,
|
|
7551
8759
|
exuluConfig,
|
|
7552
8760
|
outputSchema,
|
|
8761
|
+
agentInstance,
|
|
7553
8762
|
instructions
|
|
7554
8763
|
}) => {
|
|
7555
8764
|
console.log("[EXULU] Called generate sync for agent: " + this.name, "with prompt: " + prompt?.slice(0, 100) + "...");
|
|
@@ -7581,17 +8790,54 @@ var ExuluAgent3 = class {
|
|
|
7581
8790
|
page: 1
|
|
7582
8791
|
});
|
|
7583
8792
|
const previousMessagesContent = previousMessages.map((message) => JSON.parse(message.content));
|
|
7584
|
-
messages = await (0,
|
|
8793
|
+
messages = await (0, import_ai3.validateUIMessages)({
|
|
7585
8794
|
// append the new message to the previous messages:
|
|
7586
8795
|
messages: [...previousMessagesContent, ...messages]
|
|
7587
8796
|
});
|
|
7588
8797
|
}
|
|
7589
8798
|
console.log("[EXULU] Message count for agent: " + this.name, "loaded for generating sync.", messages.length);
|
|
7590
8799
|
let project;
|
|
8800
|
+
let sessionItems;
|
|
7591
8801
|
if (session) {
|
|
7592
8802
|
const sessionData = await getSession({ sessionID: session });
|
|
8803
|
+
sessionItems = sessionData.session_items;
|
|
7593
8804
|
project = sessionData.project;
|
|
7594
8805
|
}
|
|
8806
|
+
const query = prompt;
|
|
8807
|
+
let memoryContext = "";
|
|
8808
|
+
if (agentInstance?.memory && contexts?.length && query) {
|
|
8809
|
+
const context = contexts.find((context2) => context2.id === agentInstance?.memory);
|
|
8810
|
+
if (!context) {
|
|
8811
|
+
throw new Error("Context was set for agent memory but not found in the contexts: " + agentInstance?.memory + " please double check with a developer to see if the context was removed from code.");
|
|
8812
|
+
}
|
|
8813
|
+
const result = await context?.search({
|
|
8814
|
+
query,
|
|
8815
|
+
itemFilters: [],
|
|
8816
|
+
chunkFilters: [],
|
|
8817
|
+
method: "hybridSearch",
|
|
8818
|
+
sort: {
|
|
8819
|
+
field: "updatedAt",
|
|
8820
|
+
direction: "desc"
|
|
8821
|
+
},
|
|
8822
|
+
trigger: "agent",
|
|
8823
|
+
limit: 10,
|
|
8824
|
+
// todo make this configurable?
|
|
8825
|
+
page: 1
|
|
8826
|
+
});
|
|
8827
|
+
if (result?.chunks?.length) {
|
|
8828
|
+
memoryContext = `
|
|
8829
|
+
Pre-fetched relevant information for this query:
|
|
8830
|
+
|
|
8831
|
+
${result.chunks.map((chunk) => chunk.chunk_content).join("\n\n")}`;
|
|
8832
|
+
}
|
|
8833
|
+
const createNewMemoryTool = createNewMemoryItemTool(agentInstance, context);
|
|
8834
|
+
if (createNewMemoryTool) {
|
|
8835
|
+
if (!currentTools) {
|
|
8836
|
+
currentTools = [];
|
|
8837
|
+
}
|
|
8838
|
+
currentTools.push(createNewMemoryTool);
|
|
8839
|
+
}
|
|
8840
|
+
}
|
|
7595
8841
|
const personalizationInformation = exuluConfig?.privacy?.systemPromptPersonalization !== false ? `
|
|
7596
8842
|
${user?.firstname ? `The users first name is "${user.firstname}"` : ""}
|
|
7597
8843
|
${user?.lastname ? `The users last name is "${user.lastname}"` : ""}
|
|
@@ -7604,8 +8850,16 @@ var ExuluAgent3 = class {
|
|
|
7604
8850
|
they are talking with the current date in mind as a reference.`;
|
|
7605
8851
|
let system = instructions || "You are a helpful assistant. When you use a tool to answer a question do not explicitly comment on the result of the tool call unless the user has explicitly you to do something with the result.";
|
|
7606
8852
|
system += "\n\n" + genericContext;
|
|
7607
|
-
|
|
7608
|
-
|
|
8853
|
+
if (memoryContext) {
|
|
8854
|
+
system += "\n\n" + memoryContext;
|
|
8855
|
+
}
|
|
8856
|
+
const includesContextSearchTool = currentTools?.some(
|
|
8857
|
+
(tool3) => tool3.name.toLowerCase().includes("context_search") || tool3.id.includes("context_search") || tool3.type === "context"
|
|
8858
|
+
);
|
|
8859
|
+
const includesWebSearchTool = currentTools?.some(
|
|
8860
|
+
(tool3) => tool3.name.toLowerCase().includes("web_search") || tool3.id.includes("web_search") || tool3.type === "web_search"
|
|
8861
|
+
);
|
|
8862
|
+
console.log("[EXULU] Current tools: " + currentTools?.map((tool3) => tool3.name));
|
|
7609
8863
|
console.log("[EXULU] Includes context search tool: " + includesContextSearchTool);
|
|
7610
8864
|
if (includesContextSearchTool) {
|
|
7611
8865
|
system += `
|
|
@@ -7613,14 +8867,14 @@ var ExuluAgent3 = class {
|
|
|
7613
8867
|
|
|
7614
8868
|
|
|
7615
8869
|
When you use a context search tool, you will include references to the items
|
|
7616
|
-
retrieved from the
|
|
8870
|
+
retrieved from the tool call result inline in the response using this exact JSON format
|
|
7617
8871
|
(all on one line, no line breaks):
|
|
7618
8872
|
{item_name: <item_name>, item_id: <item_id>, context: <context_id>, chunk_id: <chunk_id>, chunk_index: <chunk_index>}
|
|
7619
8873
|
|
|
7620
8874
|
IMPORTANT formatting rules:
|
|
7621
8875
|
- Use the exact format shown above, all on ONE line
|
|
7622
8876
|
- Do NOT use quotes around field names or values
|
|
7623
|
-
- Use the context ID
|
|
8877
|
+
- Use the context ID from the tool result
|
|
7624
8878
|
- Include the file/item name, not the full path
|
|
7625
8879
|
- Separate multiple citations with spaces
|
|
7626
8880
|
|
|
@@ -7629,12 +8883,28 @@ var ExuluAgent3 = class {
|
|
|
7629
8883
|
The citations will be rendered as interactive badges in the UI.
|
|
7630
8884
|
`;
|
|
7631
8885
|
}
|
|
8886
|
+
if (includesWebSearchTool) {
|
|
8887
|
+
system += `
|
|
8888
|
+
|
|
8889
|
+
|
|
8890
|
+
When you use a web search tool, you will include references to the results of the tool call result inline in the response using this exact JSON format
|
|
8891
|
+
(all on one line, no line breaks):
|
|
8892
|
+
{url: <url>, title: <title>, snippet: <snippet>}
|
|
8893
|
+
|
|
8894
|
+
IMPORTANT formatting rules:
|
|
8895
|
+
- Use the exact format shown above, all on ONE line
|
|
8896
|
+
- Do NOT use quotes around field names or values
|
|
8897
|
+
- Separate multiple results with spaces
|
|
8898
|
+
|
|
8899
|
+
Example: {url: https://www.google.com, title: Google, snippet: The result of the web search.}
|
|
8900
|
+
`;
|
|
8901
|
+
}
|
|
7632
8902
|
if (prompt) {
|
|
7633
8903
|
let result = { object: null, text: "" };
|
|
7634
8904
|
let inputTokens = 0;
|
|
7635
8905
|
let outputTokens = 0;
|
|
7636
8906
|
if (outputSchema) {
|
|
7637
|
-
const { object, usage } = await (0,
|
|
8907
|
+
const { object, usage } = await (0, import_ai3.generateObject)({
|
|
7638
8908
|
model,
|
|
7639
8909
|
system,
|
|
7640
8910
|
prompt,
|
|
@@ -7646,24 +8916,29 @@ var ExuluAgent3 = class {
|
|
|
7646
8916
|
outputTokens = usage.outputTokens || 0;
|
|
7647
8917
|
} else {
|
|
7648
8918
|
console.log("[EXULU] Generating text for agent: " + this.name, "with prompt: " + prompt?.slice(0, 100) + "...");
|
|
7649
|
-
const { text, totalUsage } = await (0,
|
|
8919
|
+
const { text, totalUsage } = await (0, import_ai3.generateText)({
|
|
7650
8920
|
model,
|
|
7651
8921
|
system,
|
|
7652
8922
|
prompt,
|
|
7653
8923
|
maxRetries: 2,
|
|
7654
8924
|
tools: await convertToolsArrayToObject(
|
|
7655
8925
|
currentTools,
|
|
8926
|
+
[],
|
|
7656
8927
|
allExuluTools,
|
|
7657
8928
|
toolConfigs,
|
|
7658
8929
|
providerapikey,
|
|
7659
8930
|
contexts,
|
|
8931
|
+
rerankers,
|
|
7660
8932
|
user,
|
|
7661
8933
|
exuluConfig,
|
|
7662
8934
|
session,
|
|
7663
8935
|
req,
|
|
7664
|
-
project
|
|
8936
|
+
project,
|
|
8937
|
+
sessionItems,
|
|
8938
|
+
model,
|
|
8939
|
+
agentInstance
|
|
7665
8940
|
),
|
|
7666
|
-
stopWhen: [(0,
|
|
8941
|
+
stopWhen: [(0, import_ai3.stepCountIs)(5)]
|
|
7667
8942
|
});
|
|
7668
8943
|
result.text = text;
|
|
7669
8944
|
inputTokens = totalUsage?.inputTokens || 0;
|
|
@@ -7704,27 +8979,32 @@ var ExuluAgent3 = class {
|
|
|
7704
8979
|
}
|
|
7705
8980
|
if (messages) {
|
|
7706
8981
|
console.log("[EXULU] Generating text for agent: " + this.name, "with messages: " + messages.length);
|
|
7707
|
-
const { text, totalUsage } = await (0,
|
|
8982
|
+
const { text, totalUsage } = await (0, import_ai3.generateText)({
|
|
7708
8983
|
model,
|
|
7709
8984
|
// Should be a LanguageModelV1
|
|
7710
8985
|
system,
|
|
7711
|
-
messages: (0,
|
|
8986
|
+
messages: await (0, import_ai3.convertToModelMessages)(messages, {
|
|
7712
8987
|
ignoreIncompleteToolCalls: true
|
|
7713
8988
|
}),
|
|
7714
8989
|
maxRetries: 2,
|
|
7715
8990
|
tools: await convertToolsArrayToObject(
|
|
7716
8991
|
currentTools,
|
|
8992
|
+
[],
|
|
7717
8993
|
allExuluTools,
|
|
7718
8994
|
toolConfigs,
|
|
7719
8995
|
providerapikey,
|
|
7720
8996
|
contexts,
|
|
8997
|
+
rerankers,
|
|
7721
8998
|
user,
|
|
7722
8999
|
exuluConfig,
|
|
7723
9000
|
session,
|
|
7724
9001
|
req,
|
|
7725
|
-
project
|
|
9002
|
+
project,
|
|
9003
|
+
sessionItems,
|
|
9004
|
+
model,
|
|
9005
|
+
agentInstance
|
|
7726
9006
|
),
|
|
7727
|
-
stopWhen: [(0,
|
|
9007
|
+
stopWhen: [(0, import_ai3.stepCountIs)(5)]
|
|
7728
9008
|
});
|
|
7729
9009
|
if (statistics) {
|
|
7730
9010
|
await Promise.all([
|
|
@@ -7835,13 +9115,16 @@ ${extractedText}
|
|
|
7835
9115
|
generateStream = async ({
|
|
7836
9116
|
user,
|
|
7837
9117
|
session,
|
|
9118
|
+
agentInstance,
|
|
7838
9119
|
message,
|
|
7839
9120
|
previousMessages,
|
|
7840
9121
|
currentTools,
|
|
9122
|
+
approvedTools,
|
|
7841
9123
|
allExuluTools,
|
|
7842
9124
|
toolConfigs,
|
|
7843
9125
|
providerapikey,
|
|
7844
9126
|
contexts,
|
|
9127
|
+
rerankers,
|
|
7845
9128
|
exuluConfig,
|
|
7846
9129
|
instructions,
|
|
7847
9130
|
req
|
|
@@ -7864,9 +9147,11 @@ ${extractedText}
|
|
|
7864
9147
|
let messages = [];
|
|
7865
9148
|
let previousMessagesContent = previousMessages || [];
|
|
7866
9149
|
let project;
|
|
9150
|
+
let sessionItems;
|
|
7867
9151
|
if (session) {
|
|
7868
9152
|
const sessionData = await getSession({ sessionID: session });
|
|
7869
9153
|
project = sessionData.project;
|
|
9154
|
+
sessionItems = sessionData.session_items;
|
|
7870
9155
|
console.log("[EXULU] loading previous messages from session: " + session);
|
|
7871
9156
|
const previousMessages2 = await getAgentMessages({
|
|
7872
9157
|
session,
|
|
@@ -7878,34 +9163,78 @@ ${extractedText}
|
|
|
7878
9163
|
(message2) => JSON.parse(message2.content)
|
|
7879
9164
|
);
|
|
7880
9165
|
}
|
|
7881
|
-
messages = await (0,
|
|
9166
|
+
messages = await (0, import_ai3.validateUIMessages)({
|
|
7882
9167
|
// append the new message to the previous messages:
|
|
7883
9168
|
messages: [...previousMessagesContent, message]
|
|
7884
9169
|
});
|
|
9170
|
+
const query = message.parts?.[0]?.type === "text" ? message.parts[0].text : void 0;
|
|
9171
|
+
let memoryContext = "";
|
|
9172
|
+
if (agentInstance?.memory && contexts?.length && query) {
|
|
9173
|
+
const context = contexts.find((context2) => context2.id === agentInstance?.memory);
|
|
9174
|
+
if (!context) {
|
|
9175
|
+
throw new Error("Context was set for agent memory but not found in the contexts: " + agentInstance?.memory + " please double check with a developer to see if the context was removed from code.");
|
|
9176
|
+
}
|
|
9177
|
+
const result2 = await context?.search({
|
|
9178
|
+
query,
|
|
9179
|
+
itemFilters: [],
|
|
9180
|
+
chunkFilters: [],
|
|
9181
|
+
method: "hybridSearch",
|
|
9182
|
+
sort: {
|
|
9183
|
+
field: "updatedAt",
|
|
9184
|
+
direction: "desc"
|
|
9185
|
+
},
|
|
9186
|
+
trigger: "agent",
|
|
9187
|
+
limit: 10,
|
|
9188
|
+
// todo make this configurable?
|
|
9189
|
+
page: 1
|
|
9190
|
+
});
|
|
9191
|
+
if (result2?.chunks?.length) {
|
|
9192
|
+
memoryContext = `
|
|
9193
|
+
Pre-fetched relevant information for this query:
|
|
9194
|
+
|
|
9195
|
+
${result2.chunks.map((chunk) => chunk.chunk_content).join("\n\n")}`;
|
|
9196
|
+
}
|
|
9197
|
+
const createNewMemoryTool = createNewMemoryItemTool(agentInstance, context);
|
|
9198
|
+
if (createNewMemoryTool) {
|
|
9199
|
+
if (!currentTools) {
|
|
9200
|
+
currentTools = [];
|
|
9201
|
+
}
|
|
9202
|
+
currentTools.push(createNewMemoryTool);
|
|
9203
|
+
}
|
|
9204
|
+
}
|
|
7885
9205
|
messages = messages.filter(
|
|
7886
|
-
(message2, index, self) => index === self.
|
|
9206
|
+
(message2, index, self) => index === self.findLastIndex((t) => t.id === message2.id)
|
|
7887
9207
|
);
|
|
7888
9208
|
messages = await this.processFilePartsInMessages(messages);
|
|
7889
9209
|
const genericContext = "IMPORTANT: \n\n The current date is " + (/* @__PURE__ */ new Date()).toLocaleDateString() + " and the current time is " + (/* @__PURE__ */ new Date()).toLocaleTimeString() + ". If the user does not explicitly provide the current date, for examle when saying ' this weekend', you should assume they are talking with the current date in mind as a reference.";
|
|
7890
9210
|
let system = instructions || "You are a helpful assistant. When you use a tool to answer a question do not explicitly comment on the result of the tool call unless the user has explicitly you to do something with the result.";
|
|
7891
9211
|
system += "\n\n" + genericContext;
|
|
7892
|
-
|
|
7893
|
-
|
|
9212
|
+
if (memoryContext) {
|
|
9213
|
+
system += "\n\n" + memoryContext;
|
|
9214
|
+
}
|
|
9215
|
+
const includesContextSearchTool = currentTools?.some(
|
|
9216
|
+
(tool3) => tool3.name.toLowerCase().includes("context_search") || tool3.id.includes("context_search") || tool3.type === "context"
|
|
9217
|
+
);
|
|
9218
|
+
const includesWebSearchTool = currentTools?.some(
|
|
9219
|
+
(tool3) => tool3.name.toLowerCase().includes("web_search") || tool3.id.includes("web_search") || tool3.type === "web_search"
|
|
9220
|
+
);
|
|
9221
|
+
console.log("[EXULU] Current tools: " + currentTools?.map((tool3) => tool3.name));
|
|
7894
9222
|
console.log("[EXULU] Includes context search tool: " + includesContextSearchTool);
|
|
9223
|
+
console.log("[EXULU] Includes web search tool: " + includesWebSearchTool);
|
|
7895
9224
|
if (includesContextSearchTool) {
|
|
7896
9225
|
system += `
|
|
7897
9226
|
|
|
7898
9227
|
|
|
7899
9228
|
|
|
7900
9229
|
When you use a context search tool, you will include references to the items
|
|
7901
|
-
retrieved from the
|
|
9230
|
+
retrieved from the tool call result inline in the response using this exact JSON format
|
|
7902
9231
|
(all on one line, no line breaks):
|
|
7903
9232
|
{item_name: <item_name>, item_id: <item_id>, context: <context_id>, chunk_id: <chunk_id>, chunk_index: <chunk_index>}
|
|
7904
9233
|
|
|
7905
9234
|
IMPORTANT formatting rules:
|
|
7906
9235
|
- Use the exact format shown above, all on ONE line
|
|
7907
9236
|
- Do NOT use quotes around field names or values
|
|
7908
|
-
- Use the context ID
|
|
9237
|
+
- Use the context ID from the tool result
|
|
7909
9238
|
- Include the file/item name, not the full path
|
|
7910
9239
|
- Separate multiple citations with spaces
|
|
7911
9240
|
|
|
@@ -7914,10 +9243,26 @@ ${extractedText}
|
|
|
7914
9243
|
The citations will be rendered as interactive badges in the UI.
|
|
7915
9244
|
`;
|
|
7916
9245
|
}
|
|
7917
|
-
|
|
9246
|
+
if (includesWebSearchTool) {
|
|
9247
|
+
system += `
|
|
9248
|
+
|
|
9249
|
+
|
|
9250
|
+
When you use a web search tool, you will include references to the results of the tool call result inline in the response using this exact JSON format
|
|
9251
|
+
(all on one line, no line breaks):
|
|
9252
|
+
{url: <url>, title: <title>, snippet: <snippet>}
|
|
9253
|
+
|
|
9254
|
+
IMPORTANT formatting rules:
|
|
9255
|
+
- Use the exact format shown above, all on ONE line
|
|
9256
|
+
- Do NOT use quotes around field names or values
|
|
9257
|
+
- Separate multiple results with spaces
|
|
9258
|
+
|
|
9259
|
+
Example: {url: https://www.google.com, title: Google, snippet: The result of the web search.}
|
|
9260
|
+
`;
|
|
9261
|
+
}
|
|
9262
|
+
const result = (0, import_ai3.streamText)({
|
|
7918
9263
|
model,
|
|
7919
9264
|
// Should be a LanguageModelV1
|
|
7920
|
-
messages: (0,
|
|
9265
|
+
messages: await (0, import_ai3.convertToModelMessages)(messages, {
|
|
7921
9266
|
ignoreIncompleteToolCalls: true
|
|
7922
9267
|
}),
|
|
7923
9268
|
// PrepareStep could be used here to set the model
|
|
@@ -7931,21 +9276,26 @@ ${extractedText}
|
|
|
7931
9276
|
},
|
|
7932
9277
|
tools: await convertToolsArrayToObject(
|
|
7933
9278
|
currentTools,
|
|
9279
|
+
approvedTools,
|
|
7934
9280
|
allExuluTools,
|
|
7935
9281
|
toolConfigs,
|
|
7936
9282
|
providerapikey,
|
|
7937
9283
|
contexts,
|
|
9284
|
+
rerankers,
|
|
7938
9285
|
user,
|
|
7939
9286
|
exuluConfig,
|
|
7940
9287
|
session,
|
|
7941
9288
|
req,
|
|
7942
|
-
project
|
|
9289
|
+
project,
|
|
9290
|
+
sessionItems,
|
|
9291
|
+
model,
|
|
9292
|
+
agentInstance
|
|
7943
9293
|
),
|
|
7944
9294
|
onError: (error) => {
|
|
7945
9295
|
console.error("[EXULU] chat stream error.", error);
|
|
7946
9296
|
throw new Error(`Chat stream error: ${error instanceof Error ? error.message : String(error)}`);
|
|
7947
9297
|
},
|
|
7948
|
-
stopWhen: [(0,
|
|
9298
|
+
stopWhen: [(0, import_ai3.stepCountIs)(5)]
|
|
7949
9299
|
});
|
|
7950
9300
|
return {
|
|
7951
9301
|
stream: result,
|
|
@@ -8122,7 +9472,7 @@ var ExuluEmbedder = class {
|
|
|
8122
9472
|
);
|
|
8123
9473
|
}
|
|
8124
9474
|
};
|
|
8125
|
-
var
|
|
9475
|
+
var ExuluTool = class {
|
|
8126
9476
|
// Must begin with a letter (a-z) or underscore (_). Subsequent characters in a name can be letters, digits (0-9), or
|
|
8127
9477
|
// underscores and be a max length of 80 characters and at least 5 characters long.
|
|
8128
9478
|
// The ID is used for storing references to tools so it is important it does not change.
|
|
@@ -8142,9 +9492,9 @@ var ExuluTool3 = class {
|
|
|
8142
9492
|
this.description = description;
|
|
8143
9493
|
this.inputSchema = inputSchema;
|
|
8144
9494
|
this.type = type;
|
|
8145
|
-
this.tool = (0,
|
|
9495
|
+
this.tool = (0, import_ai3.tool)({
|
|
8146
9496
|
description,
|
|
8147
|
-
inputSchema: inputSchema ||
|
|
9497
|
+
inputSchema: inputSchema || import_zod2.z.object({}),
|
|
8148
9498
|
execute: execute2
|
|
8149
9499
|
});
|
|
8150
9500
|
}
|
|
@@ -8153,8 +9503,17 @@ var ExuluTool3 = class {
|
|
|
8153
9503
|
config,
|
|
8154
9504
|
user,
|
|
8155
9505
|
inputs,
|
|
8156
|
-
project
|
|
9506
|
+
project,
|
|
9507
|
+
items
|
|
8157
9508
|
}) => {
|
|
9509
|
+
console.log("[EXULU] Calling tool execute directly", {
|
|
9510
|
+
agent,
|
|
9511
|
+
config,
|
|
9512
|
+
user,
|
|
9513
|
+
inputs,
|
|
9514
|
+
project,
|
|
9515
|
+
items
|
|
9516
|
+
});
|
|
8158
9517
|
const agentInstance = await loadAgent(agent);
|
|
8159
9518
|
if (!agentInstance) {
|
|
8160
9519
|
throw new Error("Agent not found.");
|
|
@@ -8180,22 +9539,33 @@ var ExuluTool3 = class {
|
|
|
8180
9539
|
const tools = await convertToolsArrayToObject(
|
|
8181
9540
|
[this],
|
|
8182
9541
|
[],
|
|
9542
|
+
[],
|
|
8183
9543
|
agentInstance.tools,
|
|
8184
9544
|
providerapikey,
|
|
8185
9545
|
void 0,
|
|
9546
|
+
void 0,
|
|
8186
9547
|
user,
|
|
8187
9548
|
config,
|
|
8188
9549
|
void 0,
|
|
8189
9550
|
void 0,
|
|
8190
|
-
project
|
|
9551
|
+
project,
|
|
9552
|
+
items,
|
|
9553
|
+
void 0,
|
|
9554
|
+
agentInstance
|
|
8191
9555
|
);
|
|
8192
|
-
const
|
|
8193
|
-
if (!
|
|
9556
|
+
const tool3 = tools[sanitizeName(this.name)] || tools[this.name] || tools[this.id];
|
|
9557
|
+
if (!tool3?.execute) {
|
|
8194
9558
|
throw new Error("Tool " + sanitizeName(this.name) + " not found in " + JSON.stringify(tools));
|
|
8195
9559
|
}
|
|
8196
9560
|
console.log("[EXULU] Tool found", this.name);
|
|
8197
|
-
const
|
|
8198
|
-
|
|
9561
|
+
const toolCallId = this.id + "_" + (0, import_node_crypto2.randomUUID)();
|
|
9562
|
+
console.log("[EXULU] Calling tool execute", {
|
|
9563
|
+
inputs,
|
|
9564
|
+
toolCallId,
|
|
9565
|
+
messages: []
|
|
9566
|
+
});
|
|
9567
|
+
const generator = tool3.execute(inputs, {
|
|
9568
|
+
toolCallId,
|
|
8199
9569
|
messages: []
|
|
8200
9570
|
});
|
|
8201
9571
|
let lastValue;
|
|
@@ -8377,6 +9747,27 @@ var ExuluContext2 = class {
|
|
|
8377
9747
|
...processorResult,
|
|
8378
9748
|
last_processed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
8379
9749
|
});
|
|
9750
|
+
if (this.processor?.config?.generateEmbeddings) {
|
|
9751
|
+
const fullItem = await db3.from(getTableName(this.id)).where({
|
|
9752
|
+
id: processorResult.id
|
|
9753
|
+
}).first();
|
|
9754
|
+
if (!fullItem) {
|
|
9755
|
+
throw new Error(`[EXULU] Item ${processorResult.id} not found after processor update in context ${this.id}`);
|
|
9756
|
+
}
|
|
9757
|
+
const { job: embeddingsJob } = await this.embeddings.generate.one({
|
|
9758
|
+
item: fullItem,
|
|
9759
|
+
user,
|
|
9760
|
+
role,
|
|
9761
|
+
trigger: "processor",
|
|
9762
|
+
config: exuluConfig
|
|
9763
|
+
});
|
|
9764
|
+
if (embeddingsJob) {
|
|
9765
|
+
return {
|
|
9766
|
+
result: processorResult,
|
|
9767
|
+
job: embeddingsJob
|
|
9768
|
+
};
|
|
9769
|
+
}
|
|
9770
|
+
}
|
|
8380
9771
|
return {
|
|
8381
9772
|
result: processorResult,
|
|
8382
9773
|
job: void 0
|
|
@@ -8388,6 +9779,8 @@ var ExuluContext2 = class {
|
|
|
8388
9779
|
...options,
|
|
8389
9780
|
user: options.user,
|
|
8390
9781
|
role: options.role,
|
|
9782
|
+
itemFilters: options.itemFilters,
|
|
9783
|
+
chunkFilters: options.chunkFilters,
|
|
8391
9784
|
context: this,
|
|
8392
9785
|
db: db3,
|
|
8393
9786
|
limit: options?.limit || this.configuration.maxRetrievalResults || 10,
|
|
@@ -8810,7 +10203,7 @@ var ExuluContext2 = class {
|
|
|
8810
10203
|
});
|
|
8811
10204
|
};
|
|
8812
10205
|
createChunksTable = async () => {
|
|
8813
|
-
const { db: db3 } = await
|
|
10206
|
+
const { db: db3 } = await postgresClient();
|
|
8814
10207
|
const tableName = getChunksTableName(this.id);
|
|
8815
10208
|
console.log("[EXULU] Creating table: " + tableName);
|
|
8816
10209
|
await db3.schema.createTable(tableName, (table) => {
|
|
@@ -8846,27 +10239,32 @@ var ExuluContext2 = class {
|
|
|
8846
10239
|
if (this.configuration.enableAsTool === false) {
|
|
8847
10240
|
return null;
|
|
8848
10241
|
}
|
|
8849
|
-
return new
|
|
10242
|
+
return new ExuluTool({
|
|
8850
10243
|
id: this.id,
|
|
8851
10244
|
name: `${this.name}_context_search`,
|
|
8852
10245
|
type: "context",
|
|
8853
10246
|
category: "contexts",
|
|
8854
|
-
inputSchema:
|
|
8855
|
-
|
|
8856
|
-
|
|
10247
|
+
inputSchema: import_zod2.z.object({
|
|
10248
|
+
query: import_zod2.z.string().describe("The original question that the user asked"),
|
|
10249
|
+
keywords: import_zod2.z.array(import_zod2.z.string()).describe("The keywords that are relevant to the user's question, for example names of specific products, systems or parts, IDs, etc."),
|
|
10250
|
+
method: import_zod2.z.enum(["keyword", "semantic", "hybrid"]).default("hybrid").describe(
|
|
10251
|
+
"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)"
|
|
10252
|
+
)
|
|
8857
10253
|
}),
|
|
8858
10254
|
config: [],
|
|
8859
10255
|
description: `Gets information from the context called: ${this.name}. The context description is: ${this.description}.`,
|
|
8860
|
-
execute: async ({
|
|
10256
|
+
execute: async ({ query, keywords, user, role, method }) => {
|
|
8861
10257
|
const { db: db3 } = await postgresClient();
|
|
8862
10258
|
const result = await vectorSearch({
|
|
8863
10259
|
page: 1,
|
|
8864
10260
|
limit: this.configuration.maxRetrievalResults ?? 10,
|
|
8865
|
-
query
|
|
8866
|
-
|
|
10261
|
+
query,
|
|
10262
|
+
keywords,
|
|
10263
|
+
itemFilters: [],
|
|
10264
|
+
chunkFilters: [],
|
|
8867
10265
|
user,
|
|
8868
10266
|
role,
|
|
8869
|
-
method: "hybridSearch",
|
|
10267
|
+
method: method === "hybrid" ? "hybridSearch" : method === "keyword" ? "tsvector" : "cosineDistance",
|
|
8870
10268
|
context: this,
|
|
8871
10269
|
db: db3,
|
|
8872
10270
|
sort: void 0,
|
|
@@ -8987,7 +10385,7 @@ var import_utils4 = require("@apollo/utils.keyvaluecache");
|
|
|
8987
10385
|
var import_body_parser = __toESM(require("body-parser"), 1);
|
|
8988
10386
|
var import_crypto_js4 = __toESM(require("crypto-js"), 1);
|
|
8989
10387
|
var import_openai = __toESM(require("openai"), 1);
|
|
8990
|
-
var
|
|
10388
|
+
var import_fs3 = __toESM(require("fs"), 1);
|
|
8991
10389
|
var import_node_crypto3 = require("crypto");
|
|
8992
10390
|
var import_api2 = require("@opentelemetry/api");
|
|
8993
10391
|
var import_sdk = __toESM(require("@anthropic-ai/sdk"), 1);
|
|
@@ -9018,13 +10416,13 @@ var CLAUDE_MESSAGES = {
|
|
|
9018
10416
|
};
|
|
9019
10417
|
|
|
9020
10418
|
// src/registry/routes.ts
|
|
9021
|
-
var
|
|
10419
|
+
var import_ai4 = require("ai");
|
|
9022
10420
|
var import_cookie_parser = __toESM(require("cookie-parser"), 1);
|
|
9023
10421
|
var REQUEST_SIZE_LIMIT = "50mb";
|
|
9024
10422
|
var getExuluVersionNumber = async () => {
|
|
9025
10423
|
try {
|
|
9026
10424
|
const path = process.cwd();
|
|
9027
|
-
const packageJson =
|
|
10425
|
+
const packageJson = import_fs3.default.readFileSync(path + "/package.json", "utf8");
|
|
9028
10426
|
const packageData = JSON.parse(packageJson);
|
|
9029
10427
|
const exuluVersion = packageData.dependencies["@exulu/backend"];
|
|
9030
10428
|
console.log(`[EXULU] Installed exulu-backend version: ${exuluVersion}`);
|
|
@@ -9056,7 +10454,7 @@ var {
|
|
|
9056
10454
|
promptFavoritesSchema: promptFavoritesSchema2,
|
|
9057
10455
|
statisticsSchema: statisticsSchema2
|
|
9058
10456
|
} = coreSchemas.get();
|
|
9059
|
-
var createExpressRoutes = async (app, agents, tools, contexts, config, evals, tracer, queues2) => {
|
|
10457
|
+
var createExpressRoutes = async (app, agents, tools, contexts, config, evals, tracer, queues2, rerankers) => {
|
|
9060
10458
|
var corsOptions = {
|
|
9061
10459
|
origin: "*",
|
|
9062
10460
|
exposedHeaders: "*",
|
|
@@ -9105,7 +10503,7 @@ var createExpressRoutes = async (app, agents, tools, contexts, config, evals, tr
|
|
|
9105
10503
|
workflowTemplatesSchema2(),
|
|
9106
10504
|
statisticsSchema2(),
|
|
9107
10505
|
rbacSchema2()
|
|
9108
|
-
], contexts ?? [], agents, tools, config, evals, queues2 || []);
|
|
10506
|
+
], contexts ?? [], agents, tools, config, evals, queues2 || [], rerankers || []);
|
|
9109
10507
|
const server = new import_server3.ApolloServer({
|
|
9110
10508
|
cache: new import_utils4.InMemoryLRUCache(),
|
|
9111
10509
|
schema,
|
|
@@ -9133,11 +10531,78 @@ var createExpressRoutes = async (app, agents, tools, contexts, config, evals, tr
|
|
|
9133
10531
|
}
|
|
9134
10532
|
})
|
|
9135
10533
|
);
|
|
9136
|
-
app.post("/
|
|
9137
|
-
|
|
9138
|
-
|
|
9139
|
-
if (
|
|
9140
|
-
|
|
10534
|
+
app.post("/test", async (req, res) => {
|
|
10535
|
+
const { item_name, context_id } = req.body;
|
|
10536
|
+
let itemFilters = [];
|
|
10537
|
+
if (item_name) {
|
|
10538
|
+
itemFilters.push({ name: { contains: item_name } });
|
|
10539
|
+
}
|
|
10540
|
+
const { db: db3 } = await postgresClient();
|
|
10541
|
+
let itemsQuery = db3(getTableName(context_id) + " as items").select([
|
|
10542
|
+
"items.id as item_id",
|
|
10543
|
+
"items.name as item_name",
|
|
10544
|
+
"items.external_id as item_external_id",
|
|
10545
|
+
db3.raw('items."updatedAt" as item_updated_at'),
|
|
10546
|
+
db3.raw('items."createdAt" as item_created_at')
|
|
10547
|
+
]);
|
|
10548
|
+
const limit = 10;
|
|
10549
|
+
itemsQuery = itemsQuery.limit(limit);
|
|
10550
|
+
const ctx = contexts?.find((ctx2) => ctx2.id === context_id);
|
|
10551
|
+
if (!ctx) {
|
|
10552
|
+
res.status(400).json({
|
|
10553
|
+
message: "Context not found."
|
|
10554
|
+
});
|
|
10555
|
+
return;
|
|
10556
|
+
}
|
|
10557
|
+
const tableDefinition = contextToTableDefinition(ctx);
|
|
10558
|
+
itemsQuery = applyFilters(itemsQuery, itemFilters || [], tableDefinition, "items");
|
|
10559
|
+
itemsQuery = applyAccessControl(tableDefinition, itemsQuery, {
|
|
10560
|
+
email: "test@test.com",
|
|
10561
|
+
id: 1,
|
|
10562
|
+
role: {
|
|
10563
|
+
id: "test",
|
|
10564
|
+
name: "test",
|
|
10565
|
+
agents: "read",
|
|
10566
|
+
evals: "read",
|
|
10567
|
+
workflows: "read",
|
|
10568
|
+
variables: "read",
|
|
10569
|
+
users: "read"
|
|
10570
|
+
},
|
|
10571
|
+
super_admin: true
|
|
10572
|
+
}, "items");
|
|
10573
|
+
const items = await itemsQuery;
|
|
10574
|
+
const formattedResults = await Promise.all(items.map(async (item, index) => {
|
|
10575
|
+
const chunksTable = getChunksTableName(ctx?.id || "");
|
|
10576
|
+
console.log("[EXULU] chunksTable", chunksTable);
|
|
10577
|
+
if (!item.item_id) {
|
|
10578
|
+
console.error("[EXULU] Item id is required to get chunks.", item);
|
|
10579
|
+
throw new Error("Item id is required to get chunks.");
|
|
10580
|
+
}
|
|
10581
|
+
const chunks = await db3.from(chunksTable).select(["id", "source", "metadata"]).where("source", item.item_id).limit(1);
|
|
10582
|
+
if (!chunks || !chunks[0]) {
|
|
10583
|
+
console.error("[EXULU] No chunks found for item.", item);
|
|
10584
|
+
return null;
|
|
10585
|
+
}
|
|
10586
|
+
console.log("[EXULU] chunks found for item.", chunks);
|
|
10587
|
+
return {
|
|
10588
|
+
item_name: item.name,
|
|
10589
|
+
item_id: item.id,
|
|
10590
|
+
context: ctx?.id || "",
|
|
10591
|
+
chunk_id: chunks[0].id,
|
|
10592
|
+
chunk_index: 1,
|
|
10593
|
+
chunk_content: void 0,
|
|
10594
|
+
metadata: chunks[0].metadata
|
|
10595
|
+
};
|
|
10596
|
+
}));
|
|
10597
|
+
res.json({
|
|
10598
|
+
items: formattedResults.filter((result) => result !== null)
|
|
10599
|
+
});
|
|
10600
|
+
});
|
|
10601
|
+
app.post("/generate/agent/image", async (req, res) => {
|
|
10602
|
+
console.log("[EXULU] generate/agent/image", req.body);
|
|
10603
|
+
const authenticationResult = await requestValidators.authenticate(req);
|
|
10604
|
+
if (!authenticationResult.user?.id) {
|
|
10605
|
+
res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
|
|
9141
10606
|
return;
|
|
9142
10607
|
}
|
|
9143
10608
|
const { name, description, style } = req.body;
|
|
@@ -9215,10 +10680,9 @@ Mood: friendly and intelligent.
|
|
|
9215
10680
|
}
|
|
9216
10681
|
const image_bytes = Buffer.from(image_base64, "base64");
|
|
9217
10682
|
const uuid = (0, import_node_crypto3.randomUUID)();
|
|
9218
|
-
if (!
|
|
9219
|
-
|
|
10683
|
+
if (!import_fs3.default.existsSync("public")) {
|
|
10684
|
+
import_fs3.default.mkdirSync("public");
|
|
9220
10685
|
}
|
|
9221
|
-
import_fs.default.writeFileSync(`public/${uuid}.png`, image_bytes);
|
|
9222
10686
|
res.status(200).json({
|
|
9223
10687
|
message: "Image generated successfully.",
|
|
9224
10688
|
image: `${process.env.BACKEND}/${uuid}.png`
|
|
@@ -9277,6 +10741,7 @@ Mood: friendly and intelligent.
|
|
|
9277
10741
|
const slug = agent.slug;
|
|
9278
10742
|
if (!slug) return;
|
|
9279
10743
|
app.post(slug + "/:instance", async (req, res) => {
|
|
10744
|
+
console.log("[EXULU] POST " + slug + "/:instance", req.body);
|
|
9280
10745
|
const headers = {
|
|
9281
10746
|
stream: req.headers["stream"] === "true" || false,
|
|
9282
10747
|
user: req.headers["user"] || null,
|
|
@@ -9325,7 +10790,7 @@ Mood: friendly and intelligent.
|
|
|
9325
10790
|
}
|
|
9326
10791
|
console.log("[EXULU] agent tools", agentInstance.tools?.map((x) => x.name + " (" + x.id + ")"));
|
|
9327
10792
|
const disabledTools = req.body.disabledTools ? req.body.disabledTools : [];
|
|
9328
|
-
let enabledTools = await getEnabledTools(agentInstance, tools, disabledTools, agents, user);
|
|
10793
|
+
let enabledTools = await getEnabledTools(agentInstance, tools, contexts || [], rerankers || [], disabledTools, agents, user);
|
|
9329
10794
|
let providerapikey;
|
|
9330
10795
|
const variableName = agentInstance.providerapikey;
|
|
9331
10796
|
if (variableName) {
|
|
@@ -9362,14 +10827,18 @@ Mood: friendly and intelligent.
|
|
|
9362
10827
|
} else {
|
|
9363
10828
|
message = req.body.message;
|
|
9364
10829
|
}
|
|
10830
|
+
const approvedTools = req.body.approvedTools ? typeof req.body.approvedTools === "string" ? JSON.parse(req.body.approvedTools) : req.body.approvedTools : [];
|
|
9365
10831
|
const result = await agent.generateStream({
|
|
9366
10832
|
contexts,
|
|
10833
|
+
rerankers: rerankers || [],
|
|
10834
|
+
agentInstance,
|
|
9367
10835
|
user,
|
|
9368
10836
|
instructions: agentInstance.instructions,
|
|
9369
10837
|
session: headers.session,
|
|
9370
10838
|
message,
|
|
9371
10839
|
previousMessages,
|
|
9372
10840
|
currentTools: enabledTools,
|
|
10841
|
+
approvedTools,
|
|
9373
10842
|
allExuluTools: tools,
|
|
9374
10843
|
providerapikey,
|
|
9375
10844
|
toolConfigs: agentInstance.tools,
|
|
@@ -9396,7 +10865,7 @@ Mood: friendly and intelligent.
|
|
|
9396
10865
|
console.error("[EXULU] chat response error.", error);
|
|
9397
10866
|
return errorHandler(error);
|
|
9398
10867
|
},
|
|
9399
|
-
generateMessageId: (0,
|
|
10868
|
+
generateMessageId: (0, import_ai4.createIdGenerator)({
|
|
9400
10869
|
prefix: "msg_",
|
|
9401
10870
|
size: 16
|
|
9402
10871
|
}),
|
|
@@ -9453,12 +10922,14 @@ Mood: friendly and intelligent.
|
|
|
9453
10922
|
return;
|
|
9454
10923
|
} else {
|
|
9455
10924
|
const response = await agent.generateSync({
|
|
10925
|
+
contexts,
|
|
10926
|
+
rerankers: rerankers || [],
|
|
10927
|
+
agentInstance,
|
|
9456
10928
|
user,
|
|
9457
10929
|
req,
|
|
9458
10930
|
instructions: agentInstance.instructions,
|
|
9459
10931
|
session: headers.session,
|
|
9460
10932
|
inputMessages: [req.body.message],
|
|
9461
|
-
contexts,
|
|
9462
10933
|
currentTools: enabledTools,
|
|
9463
10934
|
allExuluTools: tools,
|
|
9464
10935
|
providerapikey,
|
|
@@ -9575,7 +11046,7 @@ Mood: friendly and intelligent.
|
|
|
9575
11046
|
});
|
|
9576
11047
|
const tokens = {};
|
|
9577
11048
|
const disabledTools = req.body.disabledTools ? req.body.disabledTools : [];
|
|
9578
|
-
let enabledTools = await getEnabledTools(agent, tools, disabledTools, agents, user);
|
|
11049
|
+
let enabledTools = await getEnabledTools(agent, tools, contexts || [], rerankers || [], disabledTools, agents, user);
|
|
9579
11050
|
let system = req.body.system;
|
|
9580
11051
|
if (Array.isArray(req.body.system)) {
|
|
9581
11052
|
system = [
|
|
@@ -9628,12 +11099,12 @@ Mood: friendly and intelligent.
|
|
|
9628
11099
|
console.log("[EXULU] Using tool", toolName);
|
|
9629
11100
|
const inputs = event.message?.input;
|
|
9630
11101
|
const id = event.message?.id;
|
|
9631
|
-
const
|
|
9632
|
-
if (!
|
|
11102
|
+
const tool3 = enabledTools.find((tool4) => tool4.id === toolName.replace("exulu_", ""));
|
|
11103
|
+
if (!tool3 || !tool3.tool.execute) {
|
|
9633
11104
|
console.error("[EXULU] Tool not found or not enabled.", toolName);
|
|
9634
11105
|
continue;
|
|
9635
11106
|
}
|
|
9636
|
-
const toolResult = await
|
|
11107
|
+
const toolResult = await tool3.tool.execute(inputs, {
|
|
9637
11108
|
toolCallId: id,
|
|
9638
11109
|
messages: [{
|
|
9639
11110
|
...event.message,
|
|
@@ -9748,14 +11219,14 @@ var import_types = require("@modelcontextprotocol/sdk/types.js");
|
|
|
9748
11219
|
var import_express4 = require("express");
|
|
9749
11220
|
var import_api3 = require("@opentelemetry/api");
|
|
9750
11221
|
var import_crypto_js5 = __toESM(require("crypto-js"), 1);
|
|
9751
|
-
var
|
|
11222
|
+
var import_zod3 = require("zod");
|
|
9752
11223
|
var SESSION_ID_HEADER = "mcp-session-id";
|
|
9753
11224
|
var ExuluMCP = class {
|
|
9754
11225
|
server = {};
|
|
9755
11226
|
transports = {};
|
|
9756
11227
|
constructor() {
|
|
9757
11228
|
}
|
|
9758
|
-
configure = async ({ user, agentInstance, allTools, allAgents, allContexts, config, tracer }) => {
|
|
11229
|
+
configure = async ({ user, agentInstance, allTools, allAgents, allContexts, allRerankers, config, tracer }) => {
|
|
9759
11230
|
let server = this.server[agentInstance.id];
|
|
9760
11231
|
if (!server) {
|
|
9761
11232
|
console.log("[EXULU] Creating MCP server.");
|
|
@@ -9769,12 +11240,12 @@ var ExuluMCP = class {
|
|
|
9769
11240
|
this.server[agentInstance.id] = server;
|
|
9770
11241
|
}
|
|
9771
11242
|
const disabledTools = [];
|
|
9772
|
-
let enabledTools = await getEnabledTools(agentInstance, allTools, disabledTools, allAgents, user);
|
|
11243
|
+
let enabledTools = await getEnabledTools(agentInstance, allTools, allContexts, allRerankers, disabledTools, allAgents, user);
|
|
9773
11244
|
const backend = allAgents.find((a) => a.id === agentInstance.backend);
|
|
9774
11245
|
if (!backend) {
|
|
9775
11246
|
throw new Error("Agent backend not found for agent " + agentInstance.name + " (" + agentInstance.id + ").");
|
|
9776
11247
|
}
|
|
9777
|
-
const agentTool = await backend.tool(agentInstance.id, allAgents);
|
|
11248
|
+
const agentTool = await backend.tool(agentInstance.id, allAgents, allContexts, allRerankers);
|
|
9778
11249
|
if (agentTool) {
|
|
9779
11250
|
enabledTools = [
|
|
9780
11251
|
...enabledTools,
|
|
@@ -9799,37 +11270,42 @@ var ExuluMCP = class {
|
|
|
9799
11270
|
}
|
|
9800
11271
|
}
|
|
9801
11272
|
console.log("[EXULU] Enabled tools", enabledTools?.map((x) => x.name + " (" + x.id + ")"));
|
|
9802
|
-
for (const
|
|
9803
|
-
if (server.tools[
|
|
11273
|
+
for (const tool3 of enabledTools || []) {
|
|
11274
|
+
if (server.tools[tool3.id]) {
|
|
9804
11275
|
continue;
|
|
9805
11276
|
}
|
|
9806
|
-
server.mcp.registerTool(sanitizeToolName(
|
|
9807
|
-
title:
|
|
9808
|
-
description:
|
|
11277
|
+
server.mcp.registerTool(sanitizeToolName(tool3.name + "_agent_" + tool3.id), {
|
|
11278
|
+
title: tool3.name + " agent",
|
|
11279
|
+
description: tool3.description,
|
|
9809
11280
|
inputSchema: {
|
|
9810
|
-
inputs:
|
|
11281
|
+
inputs: tool3.inputSchema || import_zod3.z.object({})
|
|
9811
11282
|
}
|
|
9812
11283
|
}, async ({ inputs }, args) => {
|
|
9813
|
-
console.log("[EXULU] MCP tool name",
|
|
11284
|
+
console.log("[EXULU] MCP tool name", tool3.name);
|
|
9814
11285
|
console.log("[EXULU] MCP tool inputs", inputs);
|
|
9815
11286
|
console.log("[EXULU] MCP tool args", args);
|
|
9816
11287
|
const configValues = agentInstance.tools;
|
|
9817
11288
|
const tools = await convertToolsArrayToObject(
|
|
9818
|
-
[
|
|
11289
|
+
[tool3],
|
|
11290
|
+
[],
|
|
9819
11291
|
allTools,
|
|
9820
11292
|
configValues,
|
|
9821
11293
|
providerapikey,
|
|
9822
11294
|
allContexts,
|
|
11295
|
+
allRerankers,
|
|
9823
11296
|
user,
|
|
9824
|
-
config
|
|
11297
|
+
config,
|
|
11298
|
+
void 0,
|
|
11299
|
+
void 0,
|
|
11300
|
+
void 0
|
|
9825
11301
|
);
|
|
9826
|
-
const convertedTool = tools[sanitizeToolName(
|
|
11302
|
+
const convertedTool = tools[sanitizeToolName(tool3.name)];
|
|
9827
11303
|
if (!convertedTool?.execute) {
|
|
9828
11304
|
console.error("[EXULU] Tool not found in converted tools array.", tools);
|
|
9829
11305
|
throw new Error("Tool not found in converted tools array.");
|
|
9830
11306
|
}
|
|
9831
11307
|
const iterator = await convertedTool.execute(inputs, {
|
|
9832
|
-
toolCallId:
|
|
11308
|
+
toolCallId: tool3.id + "_" + (0, import_node_crypto4.randomUUID)(),
|
|
9833
11309
|
messages: []
|
|
9834
11310
|
});
|
|
9835
11311
|
let result;
|
|
@@ -9842,7 +11318,7 @@ var ExuluMCP = class {
|
|
|
9842
11318
|
structuredContent: result
|
|
9843
11319
|
};
|
|
9844
11320
|
});
|
|
9845
|
-
server.tools[
|
|
11321
|
+
server.tools[tool3.id] = tool3.name;
|
|
9846
11322
|
}
|
|
9847
11323
|
const getListOfPromptTemplatesName = "getListOfPromptTemplates";
|
|
9848
11324
|
if (!server.tools[getListOfPromptTemplatesName]) {
|
|
@@ -9850,7 +11326,7 @@ var ExuluMCP = class {
|
|
|
9850
11326
|
title: "Get List of Prompt Templates",
|
|
9851
11327
|
description: "Retrieves a list of prompt templates available for this agent. Returns the name, description, and ID of each template.",
|
|
9852
11328
|
inputSchema: {
|
|
9853
|
-
inputs:
|
|
11329
|
+
inputs: import_zod3.z.object({})
|
|
9854
11330
|
}
|
|
9855
11331
|
}, async ({ inputs }, args) => {
|
|
9856
11332
|
console.log("[EXULU] Getting list of prompt templates for agent", agentInstance.id);
|
|
@@ -9886,8 +11362,8 @@ var ExuluMCP = class {
|
|
|
9886
11362
|
title: "Get Prompt Template Details",
|
|
9887
11363
|
description: "Retrieves the full details of a specific prompt template by ID, including the actual template content with variables.",
|
|
9888
11364
|
inputSchema: {
|
|
9889
|
-
inputs:
|
|
9890
|
-
id:
|
|
11365
|
+
inputs: import_zod3.z.object({
|
|
11366
|
+
id: import_zod3.z.string().describe("The ID of the prompt template to retrieve")
|
|
9891
11367
|
})
|
|
9892
11368
|
}
|
|
9893
11369
|
}, async ({ inputs }, args) => {
|
|
@@ -9932,7 +11408,7 @@ var ExuluMCP = class {
|
|
|
9932
11408
|
server.tools[getPromptTemplateDetailsName] = getPromptTemplateDetailsName;
|
|
9933
11409
|
return server.mcp;
|
|
9934
11410
|
};
|
|
9935
|
-
create = async ({ express: express3, allTools, allAgents, allContexts, config }) => {
|
|
11411
|
+
create = async ({ express: express3, allTools, allAgents, allContexts, allRerankers, config }) => {
|
|
9936
11412
|
if (!express3) {
|
|
9937
11413
|
throw new Error("Express not initialized.");
|
|
9938
11414
|
}
|
|
@@ -9974,6 +11450,7 @@ var ExuluMCP = class {
|
|
|
9974
11450
|
allTools,
|
|
9975
11451
|
allAgents,
|
|
9976
11452
|
allContexts,
|
|
11453
|
+
allRerankers,
|
|
9977
11454
|
config
|
|
9978
11455
|
});
|
|
9979
11456
|
if (!server) {
|
|
@@ -10120,6 +11597,105 @@ var claudeSonnet45Agent = new ExuluAgent3({
|
|
|
10120
11597
|
}
|
|
10121
11598
|
});
|
|
10122
11599
|
|
|
11600
|
+
// src/templates/agents/cerebras/index.ts
|
|
11601
|
+
var import_cerebras = require("@ai-sdk/cerebras");
|
|
11602
|
+
var gptOss120bAgent = new ExuluAgent3({
|
|
11603
|
+
id: `default_gpt_oss_120b_agent`,
|
|
11604
|
+
name: `GPT-OSS-120B`,
|
|
11605
|
+
provider: "custom",
|
|
11606
|
+
description: `Custom GPT-OSS-120B model. High intelligence and capability. Moderately Fast.`,
|
|
11607
|
+
type: "agent",
|
|
11608
|
+
capabilities: {
|
|
11609
|
+
text: true,
|
|
11610
|
+
images: [],
|
|
11611
|
+
files: [".pdf", ".txt"],
|
|
11612
|
+
audio: [],
|
|
11613
|
+
video: []
|
|
11614
|
+
},
|
|
11615
|
+
authenticationInformation: "",
|
|
11616
|
+
maxContextLength: 128e3,
|
|
11617
|
+
config: {
|
|
11618
|
+
name: `GPT-OSS-120B`,
|
|
11619
|
+
instructions: "",
|
|
11620
|
+
model: {
|
|
11621
|
+
create: ({ apiKey }) => {
|
|
11622
|
+
if (!apiKey) {
|
|
11623
|
+
throw new Error("Auth credentials not found for GPT-OSS-120B agent, make sure you have set the provider api key to a valid custom API key.");
|
|
11624
|
+
}
|
|
11625
|
+
const vertex = (0, import_cerebras.createCerebras)({
|
|
11626
|
+
apiKey
|
|
11627
|
+
});
|
|
11628
|
+
const model = vertex("gpt-oss-120b");
|
|
11629
|
+
return model;
|
|
11630
|
+
}
|
|
11631
|
+
}
|
|
11632
|
+
}
|
|
11633
|
+
});
|
|
11634
|
+
var llama38bAgent = new ExuluAgent3({
|
|
11635
|
+
id: `default_llama_38b_agent`,
|
|
11636
|
+
name: `LLAMA-38B`,
|
|
11637
|
+
provider: "custom",
|
|
11638
|
+
description: `Custom LLAMA-38B model. High intelligence and capability. Moderately Fast.`,
|
|
11639
|
+
type: "agent",
|
|
11640
|
+
capabilities: {
|
|
11641
|
+
text: true,
|
|
11642
|
+
images: [],
|
|
11643
|
+
files: [".pdf", ".txt"],
|
|
11644
|
+
audio: [],
|
|
11645
|
+
video: []
|
|
11646
|
+
},
|
|
11647
|
+
authenticationInformation: "",
|
|
11648
|
+
maxContextLength: 32e3,
|
|
11649
|
+
config: {
|
|
11650
|
+
name: `LLAMA-38B`,
|
|
11651
|
+
instructions: "",
|
|
11652
|
+
model: {
|
|
11653
|
+
create: ({ apiKey }) => {
|
|
11654
|
+
if (!apiKey) {
|
|
11655
|
+
throw new Error("Auth credentials not found for LLAMA-38B agent, make sure you have set the provider api key to a valid custom API key.");
|
|
11656
|
+
}
|
|
11657
|
+
const vertex = (0, import_cerebras.createCerebras)({
|
|
11658
|
+
apiKey
|
|
11659
|
+
});
|
|
11660
|
+
const model = vertex("llama3.1-8b");
|
|
11661
|
+
return model;
|
|
11662
|
+
}
|
|
11663
|
+
}
|
|
11664
|
+
}
|
|
11665
|
+
});
|
|
11666
|
+
var llama3370bAgent = new ExuluAgent3({
|
|
11667
|
+
id: `default_llama_3370b_agent`,
|
|
11668
|
+
name: `LLAMA-3.3-70B`,
|
|
11669
|
+
provider: "custom",
|
|
11670
|
+
description: `Custom LLAMA-3.3-70B model. High intelligence and capability. Moderately Fast.`,
|
|
11671
|
+
type: "agent",
|
|
11672
|
+
capabilities: {
|
|
11673
|
+
text: true,
|
|
11674
|
+
images: [],
|
|
11675
|
+
files: [".pdf", ".txt"],
|
|
11676
|
+
audio: [],
|
|
11677
|
+
video: []
|
|
11678
|
+
},
|
|
11679
|
+
authenticationInformation: "",
|
|
11680
|
+
maxContextLength: 32e3,
|
|
11681
|
+
config: {
|
|
11682
|
+
name: `LLAMA-3.3-70B`,
|
|
11683
|
+
instructions: "",
|
|
11684
|
+
model: {
|
|
11685
|
+
create: ({ apiKey }) => {
|
|
11686
|
+
if (!apiKey) {
|
|
11687
|
+
throw new Error("Auth credentials not found for LLAMA-3.3-70B agent, make sure you have set the provider api key to a valid custom API key.");
|
|
11688
|
+
}
|
|
11689
|
+
const vertex = (0, import_cerebras.createCerebras)({
|
|
11690
|
+
apiKey
|
|
11691
|
+
});
|
|
11692
|
+
const model = vertex("llama-3.3-70b");
|
|
11693
|
+
return model;
|
|
11694
|
+
}
|
|
11695
|
+
}
|
|
11696
|
+
}
|
|
11697
|
+
});
|
|
11698
|
+
|
|
10123
11699
|
// src/templates/agents/google/vertex/index.ts
|
|
10124
11700
|
var import_google_vertex = require("@ai-sdk/google-vertex");
|
|
10125
11701
|
var vertexAuthenticationInformation = `
|
|
@@ -10193,6 +11769,43 @@ var vertexGemini25FlashAgent = new ExuluAgent3({
|
|
|
10193
11769
|
}
|
|
10194
11770
|
}
|
|
10195
11771
|
});
|
|
11772
|
+
var vertexGemini25ProAgent = new ExuluAgent3({
|
|
11773
|
+
id: `default_vertex_gemini_2_5_pro_agent`,
|
|
11774
|
+
name: `GEMINI-2.5-PRO`,
|
|
11775
|
+
provider: "vertex",
|
|
11776
|
+
description: `Google Vertex Gemini 2.5 Pro model. Very high intelligence and capability. Moderately Fast.`,
|
|
11777
|
+
type: "agent",
|
|
11778
|
+
capabilities: {
|
|
11779
|
+
text: true,
|
|
11780
|
+
images: [".png", ".jpg", ".jpeg", ".webp"],
|
|
11781
|
+
files: [".pdf", ".txt"],
|
|
11782
|
+
audio: [".mpeg", ".mp3", ".m4a", ".wav", ".mp4"],
|
|
11783
|
+
video: [".mp4", ".mpeg"]
|
|
11784
|
+
},
|
|
11785
|
+
authenticationInformation: vertexAuthenticationInformation,
|
|
11786
|
+
maxContextLength: 1048576,
|
|
11787
|
+
config: {
|
|
11788
|
+
name: `GEMINI-2.5-PRO`,
|
|
11789
|
+
instructions: "",
|
|
11790
|
+
model: {
|
|
11791
|
+
create: ({ apiKey }) => {
|
|
11792
|
+
if (!apiKey) {
|
|
11793
|
+
throw new Error("Auth credentials not found for Google Vertex agent, make sure you have set the provider api key to a valid google authentication json.");
|
|
11794
|
+
}
|
|
11795
|
+
const googleAuthPayload = JSON.parse(apiKey || "{}");
|
|
11796
|
+
if (!googleAuthPayload) {
|
|
11797
|
+
throw new Error("API key not found for Google Vertex Gemini 2.5 Pro agent.");
|
|
11798
|
+
}
|
|
11799
|
+
if (!googleAuthPayload.location) {
|
|
11800
|
+
throw new Error("Location not set in authentication json for Google Vertex Gemini 2.5 Pro agent, should be for example 'europe-west1'");
|
|
11801
|
+
}
|
|
11802
|
+
const vertex = (0, import_google_vertex.createVertex)(googleAuthPayload);
|
|
11803
|
+
const model = vertex("gemini-2.5-pro");
|
|
11804
|
+
return model;
|
|
11805
|
+
}
|
|
11806
|
+
}
|
|
11807
|
+
}
|
|
11808
|
+
});
|
|
10196
11809
|
var vertexGemini20FlashAgent = new ExuluAgent3({
|
|
10197
11810
|
id: `default_vertex_gemini_2_0_flash_agent`,
|
|
10198
11811
|
name: `GEMINI-2.0-FLASH`,
|
|
@@ -10672,7 +12285,7 @@ var ExuluQueues = class {
|
|
|
10672
12285
|
var queues = new ExuluQueues();
|
|
10673
12286
|
|
|
10674
12287
|
// src/templates/evals/index.ts
|
|
10675
|
-
var
|
|
12288
|
+
var import_zod4 = require("zod");
|
|
10676
12289
|
var llmAsJudgeEval = () => {
|
|
10677
12290
|
if (process.env.REDIS_HOST?.length && process.env.REDIS_PORT?.length) {
|
|
10678
12291
|
return new ExuluEval3({
|
|
@@ -10705,9 +12318,12 @@ var llmAsJudgeEval = () => {
|
|
|
10705
12318
|
const providerapikey = await ExuluVariables.get(agent.providerapikey);
|
|
10706
12319
|
console.log("[EXULU] prompt", prompt);
|
|
10707
12320
|
const response = await backend.generateSync({
|
|
12321
|
+
agentInstance: agent,
|
|
12322
|
+
contexts: [],
|
|
12323
|
+
rerankers: [],
|
|
10708
12324
|
prompt,
|
|
10709
|
-
outputSchema:
|
|
10710
|
-
score:
|
|
12325
|
+
outputSchema: import_zod4.z.object({
|
|
12326
|
+
score: import_zod4.z.number().min(0).max(100).describe("The score between 0 and 100.")
|
|
10711
12327
|
}),
|
|
10712
12328
|
providerapikey
|
|
10713
12329
|
});
|
|
@@ -10741,647 +12357,6 @@ var getDefaultEvals = () => {
|
|
|
10741
12357
|
return [];
|
|
10742
12358
|
};
|
|
10743
12359
|
|
|
10744
|
-
// src/templates/tools/math.ts
|
|
10745
|
-
var import_zod4 = require("zod");
|
|
10746
|
-
|
|
10747
|
-
// src/templates/tools/utils/arithmetic.ts
|
|
10748
|
-
var Arithmetic = class {
|
|
10749
|
-
/**
|
|
10750
|
-
* Add two numbers together
|
|
10751
|
-
* @param firstNumber - The first number
|
|
10752
|
-
* @param secondNumber - The second number
|
|
10753
|
-
* @returns sum
|
|
10754
|
-
*/
|
|
10755
|
-
static add(firstNumber, secondNumber) {
|
|
10756
|
-
const sum = firstNumber + secondNumber;
|
|
10757
|
-
return sum;
|
|
10758
|
-
}
|
|
10759
|
-
/**
|
|
10760
|
-
* Subtract one number from another
|
|
10761
|
-
* @param minuend - The number to subtract from
|
|
10762
|
-
* @param subtrahend - The number to subtract
|
|
10763
|
-
* @returns difference
|
|
10764
|
-
*/
|
|
10765
|
-
static subtract(minuend, subtrahend) {
|
|
10766
|
-
const difference = minuend - subtrahend;
|
|
10767
|
-
return difference;
|
|
10768
|
-
}
|
|
10769
|
-
/**
|
|
10770
|
-
* Multiply two numbers together
|
|
10771
|
-
* @param firstNumber - The first number
|
|
10772
|
-
* @param secondNumber - The second number
|
|
10773
|
-
* @returns product
|
|
10774
|
-
*/
|
|
10775
|
-
static multiply(firstNumber, secondNumber) {
|
|
10776
|
-
const product = firstNumber * secondNumber;
|
|
10777
|
-
return product;
|
|
10778
|
-
}
|
|
10779
|
-
/**
|
|
10780
|
-
* Divide one number by another
|
|
10781
|
-
* @param numerator - The number to be divided
|
|
10782
|
-
* @param denominator - The number to divide by
|
|
10783
|
-
* @returns quotient
|
|
10784
|
-
*/
|
|
10785
|
-
static division(numerator, denominator) {
|
|
10786
|
-
const quotient = numerator / denominator;
|
|
10787
|
-
return quotient;
|
|
10788
|
-
}
|
|
10789
|
-
/**
|
|
10790
|
-
* Calculate the sum of an array of numbers
|
|
10791
|
-
* @param numbers - Array of numbers to sum
|
|
10792
|
-
* @returns sum of all numbers in the array
|
|
10793
|
-
*/
|
|
10794
|
-
static sum(numbers) {
|
|
10795
|
-
const sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
|
|
10796
|
-
return sum;
|
|
10797
|
-
}
|
|
10798
|
-
/**
|
|
10799
|
-
* Calculate the floor of a number
|
|
10800
|
-
* @param number - Number to find the floor of
|
|
10801
|
-
* @returns floor of the number
|
|
10802
|
-
*/
|
|
10803
|
-
static floor(number) {
|
|
10804
|
-
const floor = Math.floor(number);
|
|
10805
|
-
return floor;
|
|
10806
|
-
}
|
|
10807
|
-
/**
|
|
10808
|
-
* Calculate the ceil of a number
|
|
10809
|
-
* @param number - Number to find the ceil of
|
|
10810
|
-
* @returns ceil of the number
|
|
10811
|
-
*/
|
|
10812
|
-
static ceil(number) {
|
|
10813
|
-
const ceil = Math.ceil(number);
|
|
10814
|
-
return ceil;
|
|
10815
|
-
}
|
|
10816
|
-
/**
|
|
10817
|
-
* Calculate the round of a number
|
|
10818
|
-
* @param number - Number to find the round of
|
|
10819
|
-
* @returns round of the number
|
|
10820
|
-
*/
|
|
10821
|
-
static round(number) {
|
|
10822
|
-
const round = Math.round(number);
|
|
10823
|
-
return round;
|
|
10824
|
-
}
|
|
10825
|
-
/**
|
|
10826
|
-
* Get the remainder of a division equation.
|
|
10827
|
-
* Ex: modulo(5,2) = 1
|
|
10828
|
-
* @param numerator - The number to be divided
|
|
10829
|
-
* @param denominator - The number to divide by
|
|
10830
|
-
* @returns remainder of division
|
|
10831
|
-
*/
|
|
10832
|
-
static modulo(numerator, denominator) {
|
|
10833
|
-
const remainder = numerator % denominator;
|
|
10834
|
-
return remainder;
|
|
10835
|
-
}
|
|
10836
|
-
};
|
|
10837
|
-
|
|
10838
|
-
// src/templates/tools/utils/statistics.ts
|
|
10839
|
-
var Statistics = class {
|
|
10840
|
-
/**
|
|
10841
|
-
* Calculate the arithmetic mean (average) of an array of numbers
|
|
10842
|
-
* @param numbers - Array of numbers to calculate the mean of
|
|
10843
|
-
* @returns The arithmetic mean value
|
|
10844
|
-
*/
|
|
10845
|
-
static mean(numbers) {
|
|
10846
|
-
const sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
|
|
10847
|
-
const mean = sum / numbers.length;
|
|
10848
|
-
return mean;
|
|
10849
|
-
}
|
|
10850
|
-
/**
|
|
10851
|
-
* Calculate the median (middle value) of an array of numbers
|
|
10852
|
-
* @param numbers - Array of numbers to calculate the median of
|
|
10853
|
-
* @returns The median value
|
|
10854
|
-
*/
|
|
10855
|
-
static median(numbers) {
|
|
10856
|
-
numbers.sort();
|
|
10857
|
-
const medianIndex = numbers.length / 2;
|
|
10858
|
-
let medianValue;
|
|
10859
|
-
if (numbers.length % 2 !== 0) {
|
|
10860
|
-
medianValue = numbers[Math.floor(medianIndex)];
|
|
10861
|
-
} else {
|
|
10862
|
-
medianValue = (numbers[medianIndex] + numbers[medianIndex - 1]) / 2;
|
|
10863
|
-
}
|
|
10864
|
-
return medianValue;
|
|
10865
|
-
}
|
|
10866
|
-
/**
|
|
10867
|
-
* Calculate the mode (most frequent value(s)) of an array of numbers
|
|
10868
|
-
* @param numbers - Array of numbers to calculate the mode of
|
|
10869
|
-
* @returns Object containing the mode value(s) and their frequency
|
|
10870
|
-
*/
|
|
10871
|
-
static mode(numbers) {
|
|
10872
|
-
const modeMap = /* @__PURE__ */ new Map();
|
|
10873
|
-
numbers.forEach((value) => {
|
|
10874
|
-
if (modeMap.has(value)) {
|
|
10875
|
-
modeMap.set(value, modeMap.get(value) + 1);
|
|
10876
|
-
} else {
|
|
10877
|
-
modeMap.set(value, 1);
|
|
10878
|
-
}
|
|
10879
|
-
});
|
|
10880
|
-
let maxFrequency = 0;
|
|
10881
|
-
for (const numberFrequency of modeMap.values()) {
|
|
10882
|
-
if (numberFrequency > maxFrequency) {
|
|
10883
|
-
maxFrequency = numberFrequency;
|
|
10884
|
-
}
|
|
10885
|
-
}
|
|
10886
|
-
const modeResult = [];
|
|
10887
|
-
for (const [key, value] of modeMap.entries()) {
|
|
10888
|
-
if (value === maxFrequency) {
|
|
10889
|
-
modeResult.push(key);
|
|
10890
|
-
}
|
|
10891
|
-
}
|
|
10892
|
-
return {
|
|
10893
|
-
modeResult,
|
|
10894
|
-
maxFrequency
|
|
10895
|
-
};
|
|
10896
|
-
}
|
|
10897
|
-
/**
|
|
10898
|
-
* Find the minimum value in an array of numbers
|
|
10899
|
-
* @param numbers - Array of numbers to find the minimum of
|
|
10900
|
-
* @returns The minimum value
|
|
10901
|
-
*/
|
|
10902
|
-
static min(numbers) {
|
|
10903
|
-
const minValue = Math.min(...numbers);
|
|
10904
|
-
return minValue;
|
|
10905
|
-
}
|
|
10906
|
-
/**
|
|
10907
|
-
* Find the maximum value in an array of numbers
|
|
10908
|
-
* @param numbers - Array of numbers to find the maximum of
|
|
10909
|
-
* @returns The maximum value
|
|
10910
|
-
*/
|
|
10911
|
-
static max(numbers) {
|
|
10912
|
-
const maxValue = Math.max(...numbers);
|
|
10913
|
-
return maxValue;
|
|
10914
|
-
}
|
|
10915
|
-
};
|
|
10916
|
-
|
|
10917
|
-
// src/templates/tools/utils/trigonometric.ts
|
|
10918
|
-
var Trigonometric = class {
|
|
10919
|
-
/**
|
|
10920
|
-
* Calculate the sin of a number in radians
|
|
10921
|
-
* @param number - The number to find the sin of
|
|
10922
|
-
* @returns The sin of a number in radians
|
|
10923
|
-
*/
|
|
10924
|
-
static sin(number) {
|
|
10925
|
-
const sin = Math.sin(number);
|
|
10926
|
-
return sin;
|
|
10927
|
-
}
|
|
10928
|
-
/**
|
|
10929
|
-
* Calculate the arcsin of a number in radians
|
|
10930
|
-
* @param number - The number to find the arcsin of
|
|
10931
|
-
* @returns The arcsin of a number in radians
|
|
10932
|
-
*/
|
|
10933
|
-
static arcsin(number) {
|
|
10934
|
-
const arcsin = Math.asin(number);
|
|
10935
|
-
return arcsin;
|
|
10936
|
-
}
|
|
10937
|
-
/**
|
|
10938
|
-
* Calculate the cos of a number in radians
|
|
10939
|
-
* @param number - The number to find the cos of
|
|
10940
|
-
* @returns The cos of a number in radians
|
|
10941
|
-
*/
|
|
10942
|
-
static cos(number) {
|
|
10943
|
-
const cos = Math.cos(number);
|
|
10944
|
-
return cos;
|
|
10945
|
-
}
|
|
10946
|
-
/**
|
|
10947
|
-
* Calculate the arccos of a number in radians
|
|
10948
|
-
* @param number - The number to find the arccos of
|
|
10949
|
-
* @returns The arccos of a number in radians
|
|
10950
|
-
*/
|
|
10951
|
-
static arccos(number) {
|
|
10952
|
-
const arccos = Math.acos(number);
|
|
10953
|
-
return arccos;
|
|
10954
|
-
}
|
|
10955
|
-
/**
|
|
10956
|
-
* Calculate the tangent of a number in radians
|
|
10957
|
-
* @param number - The number to find the tangent of
|
|
10958
|
-
* @returns The tangent of a number in radians
|
|
10959
|
-
*/
|
|
10960
|
-
static tan(number) {
|
|
10961
|
-
const tangent = Math.tan(number);
|
|
10962
|
-
return tangent;
|
|
10963
|
-
}
|
|
10964
|
-
/**
|
|
10965
|
-
* Calculate the arc tangent of a number in radians
|
|
10966
|
-
* @param number - The number to find the arc tangent of
|
|
10967
|
-
* @returns The arc tangent of a number in radians
|
|
10968
|
-
*/
|
|
10969
|
-
static arctan(number) {
|
|
10970
|
-
const arctangent = Math.atan(number);
|
|
10971
|
-
return arctangent;
|
|
10972
|
-
}
|
|
10973
|
-
/**
|
|
10974
|
-
* Converts a radian into its equivalent value in degrees
|
|
10975
|
-
* @param number - The number to get the degree of
|
|
10976
|
-
* @returns The degree of the number
|
|
10977
|
-
*/
|
|
10978
|
-
static radiansToDegrees(number) {
|
|
10979
|
-
const degrees = number * (180 / Math.PI);
|
|
10980
|
-
return degrees;
|
|
10981
|
-
}
|
|
10982
|
-
/**
|
|
10983
|
-
* Converts a degree into its equivalent value in radians
|
|
10984
|
-
* @param number - The number to get the radians of
|
|
10985
|
-
* @returns The radians of the number
|
|
10986
|
-
*/
|
|
10987
|
-
static degreesToRadians(number) {
|
|
10988
|
-
const radians = number * (Math.PI / 180);
|
|
10989
|
-
return radians;
|
|
10990
|
-
}
|
|
10991
|
-
};
|
|
10992
|
-
|
|
10993
|
-
// src/templates/tools/math.ts
|
|
10994
|
-
var additionTool = new ExuluTool3({
|
|
10995
|
-
id: "addition",
|
|
10996
|
-
name: "Addition",
|
|
10997
|
-
description: "Adds two numbers together",
|
|
10998
|
-
type: "function",
|
|
10999
|
-
category: "math",
|
|
11000
|
-
config: [],
|
|
11001
|
-
inputSchema: import_zod4.z.object({
|
|
11002
|
-
firstNumber: import_zod4.z.number().describe("The first addend"),
|
|
11003
|
-
secondNumber: import_zod4.z.number().describe("The second addend")
|
|
11004
|
-
}),
|
|
11005
|
-
execute: async ({ firstNumber, secondNumber }) => {
|
|
11006
|
-
const value = Arithmetic.add(firstNumber, secondNumber);
|
|
11007
|
-
return { result: `${value}` };
|
|
11008
|
-
}
|
|
11009
|
-
});
|
|
11010
|
-
var subtractionTool = new ExuluTool3({
|
|
11011
|
-
id: "subtraction",
|
|
11012
|
-
name: "Subtraction",
|
|
11013
|
-
description: "Subtracts the second number from the first number",
|
|
11014
|
-
type: "function",
|
|
11015
|
-
category: "math",
|
|
11016
|
-
config: [],
|
|
11017
|
-
inputSchema: import_zod4.z.object({
|
|
11018
|
-
minuend: import_zod4.z.number().describe("The number to subtract from (minuend)"),
|
|
11019
|
-
subtrahend: import_zod4.z.number().describe("The number being subtracted (subtrahend)")
|
|
11020
|
-
}),
|
|
11021
|
-
execute: async ({ minuend, subtrahend }) => {
|
|
11022
|
-
const value = Arithmetic.subtract(minuend, subtrahend);
|
|
11023
|
-
return { result: `${value}` };
|
|
11024
|
-
}
|
|
11025
|
-
});
|
|
11026
|
-
var multiplicationTool = new ExuluTool3({
|
|
11027
|
-
id: "multiplication",
|
|
11028
|
-
name: "Multiplication",
|
|
11029
|
-
description: "Multiplies two numbers together",
|
|
11030
|
-
type: "function",
|
|
11031
|
-
category: "math",
|
|
11032
|
-
config: [],
|
|
11033
|
-
inputSchema: import_zod4.z.object({
|
|
11034
|
-
firstNumber: import_zod4.z.number().describe("The first number"),
|
|
11035
|
-
SecondNumber: import_zod4.z.number().describe("The second number")
|
|
11036
|
-
}),
|
|
11037
|
-
execute: async ({ firstNumber, SecondNumber }) => {
|
|
11038
|
-
const value = Arithmetic.multiply(firstNumber, SecondNumber);
|
|
11039
|
-
return { result: `${value}` };
|
|
11040
|
-
}
|
|
11041
|
-
});
|
|
11042
|
-
var divisionTool = new ExuluTool3({
|
|
11043
|
-
id: "division",
|
|
11044
|
-
name: "Division",
|
|
11045
|
-
description: "Divides the first number by the second number",
|
|
11046
|
-
type: "function",
|
|
11047
|
-
category: "math",
|
|
11048
|
-
config: [],
|
|
11049
|
-
inputSchema: import_zod4.z.object({
|
|
11050
|
-
numerator: import_zod4.z.number().describe("The number being divided (numerator)"),
|
|
11051
|
-
denominator: import_zod4.z.number().describe("The number to divide by (denominator)")
|
|
11052
|
-
}),
|
|
11053
|
-
execute: async ({ numerator, denominator }) => {
|
|
11054
|
-
const value = Arithmetic.division(numerator, denominator);
|
|
11055
|
-
return { result: `${value}` };
|
|
11056
|
-
}
|
|
11057
|
-
});
|
|
11058
|
-
var sumTool = new ExuluTool3({
|
|
11059
|
-
id: "sum",
|
|
11060
|
-
name: "Sum",
|
|
11061
|
-
description: "Adds any number of numbers together",
|
|
11062
|
-
type: "function",
|
|
11063
|
-
category: "math",
|
|
11064
|
-
config: [],
|
|
11065
|
-
inputSchema: import_zod4.z.object({
|
|
11066
|
-
numbers: import_zod4.z.array(import_zod4.z.number()).min(1).describe("Array of numbers to sum")
|
|
11067
|
-
}),
|
|
11068
|
-
execute: async ({ numbers }) => {
|
|
11069
|
-
const value = Arithmetic.sum(numbers);
|
|
11070
|
-
return { result: `${value}` };
|
|
11071
|
-
}
|
|
11072
|
-
});
|
|
11073
|
-
var moduloTool = new ExuluTool3({
|
|
11074
|
-
id: "modulo",
|
|
11075
|
-
name: "Modulo",
|
|
11076
|
-
description: "Divides two numbers and returns the remainder",
|
|
11077
|
-
type: "function",
|
|
11078
|
-
category: "math",
|
|
11079
|
-
config: [],
|
|
11080
|
-
inputSchema: import_zod4.z.object({
|
|
11081
|
-
numerator: import_zod4.z.number().describe("The number being divided (numerator)"),
|
|
11082
|
-
denominator: import_zod4.z.number().describe("The number to divide by (denominator)")
|
|
11083
|
-
}),
|
|
11084
|
-
execute: async ({ numerator, denominator }) => {
|
|
11085
|
-
const value = Arithmetic.modulo(numerator, denominator);
|
|
11086
|
-
return { result: `${value}` };
|
|
11087
|
-
}
|
|
11088
|
-
});
|
|
11089
|
-
var meanTool = new ExuluTool3({
|
|
11090
|
-
id: "mean",
|
|
11091
|
-
name: "Mean",
|
|
11092
|
-
description: "Calculates the arithmetic mean of a list of numbers",
|
|
11093
|
-
type: "function",
|
|
11094
|
-
category: "math",
|
|
11095
|
-
config: [],
|
|
11096
|
-
inputSchema: import_zod4.z.object({
|
|
11097
|
-
numbers: import_zod4.z.array(import_zod4.z.number()).min(1).describe("Array of numbers to find the mean of")
|
|
11098
|
-
}),
|
|
11099
|
-
execute: async ({ numbers }) => {
|
|
11100
|
-
const value = Statistics.mean(numbers);
|
|
11101
|
-
return { result: `${value}` };
|
|
11102
|
-
}
|
|
11103
|
-
});
|
|
11104
|
-
var medianTool = new ExuluTool3({
|
|
11105
|
-
id: "median",
|
|
11106
|
-
name: "Median",
|
|
11107
|
-
description: "Calculates the median of a list of numbers",
|
|
11108
|
-
type: "function",
|
|
11109
|
-
category: "math",
|
|
11110
|
-
config: [],
|
|
11111
|
-
inputSchema: import_zod4.z.object({
|
|
11112
|
-
numbers: import_zod4.z.array(import_zod4.z.number()).min(1).describe("Array of numbers to find the median of")
|
|
11113
|
-
}),
|
|
11114
|
-
execute: async ({ numbers }) => {
|
|
11115
|
-
const value = Statistics.median(numbers);
|
|
11116
|
-
return { result: `${value}` };
|
|
11117
|
-
}
|
|
11118
|
-
});
|
|
11119
|
-
var modeTool = new ExuluTool3({
|
|
11120
|
-
id: "mode",
|
|
11121
|
-
name: "Mode",
|
|
11122
|
-
description: "Finds the most common number in a list of numbers",
|
|
11123
|
-
type: "function",
|
|
11124
|
-
category: "math",
|
|
11125
|
-
config: [],
|
|
11126
|
-
inputSchema: import_zod4.z.object({
|
|
11127
|
-
numbers: import_zod4.z.array(import_zod4.z.number()).describe("Array of numbers to find the mode of")
|
|
11128
|
-
}),
|
|
11129
|
-
execute: async ({ numbers }) => {
|
|
11130
|
-
const value = Statistics.mode(numbers);
|
|
11131
|
-
return { result: `Entries (${value.modeResult.join(", ")}) appeared ${value.maxFrequency} times` };
|
|
11132
|
-
}
|
|
11133
|
-
});
|
|
11134
|
-
var minTool = new ExuluTool3({
|
|
11135
|
-
id: "min",
|
|
11136
|
-
name: "Minimum",
|
|
11137
|
-
description: "Finds the minimum value from a list of numbers",
|
|
11138
|
-
type: "function",
|
|
11139
|
-
category: "math",
|
|
11140
|
-
config: [],
|
|
11141
|
-
inputSchema: import_zod4.z.object({
|
|
11142
|
-
numbers: import_zod4.z.array(import_zod4.z.number()).describe("Array of numbers to find the minimum of")
|
|
11143
|
-
}),
|
|
11144
|
-
execute: async ({ numbers }) => {
|
|
11145
|
-
const value = Statistics.min(numbers);
|
|
11146
|
-
return { result: `${value}` };
|
|
11147
|
-
}
|
|
11148
|
-
});
|
|
11149
|
-
var maxTool = new ExuluTool3({
|
|
11150
|
-
id: "max",
|
|
11151
|
-
name: "Maximum",
|
|
11152
|
-
description: "Finds the maximum value from a list of numbers",
|
|
11153
|
-
type: "function",
|
|
11154
|
-
category: "math",
|
|
11155
|
-
config: [],
|
|
11156
|
-
inputSchema: import_zod4.z.object({
|
|
11157
|
-
numbers: import_zod4.z.array(import_zod4.z.number()).describe("Array of numbers to find the maximum of")
|
|
11158
|
-
}),
|
|
11159
|
-
execute: async ({ numbers }) => {
|
|
11160
|
-
const value = Statistics.max(numbers);
|
|
11161
|
-
return { result: `${value}` };
|
|
11162
|
-
}
|
|
11163
|
-
});
|
|
11164
|
-
var floorTool = new ExuluTool3({
|
|
11165
|
-
id: "floor",
|
|
11166
|
-
name: "Floor",
|
|
11167
|
-
description: "Rounds a number down to the nearest integer",
|
|
11168
|
-
type: "function",
|
|
11169
|
-
category: "math",
|
|
11170
|
-
config: [],
|
|
11171
|
-
inputSchema: import_zod4.z.object({
|
|
11172
|
-
number: import_zod4.z.number().describe("The number to round down")
|
|
11173
|
-
}),
|
|
11174
|
-
execute: async ({ number }) => {
|
|
11175
|
-
const value = Arithmetic.floor(number);
|
|
11176
|
-
return { result: `${value}` };
|
|
11177
|
-
}
|
|
11178
|
-
});
|
|
11179
|
-
var ceilingTool = new ExuluTool3({
|
|
11180
|
-
id: "ceiling",
|
|
11181
|
-
name: "Ceiling",
|
|
11182
|
-
description: "Rounds a number up to the nearest integer",
|
|
11183
|
-
type: "function",
|
|
11184
|
-
category: "math",
|
|
11185
|
-
config: [],
|
|
11186
|
-
inputSchema: import_zod4.z.object({
|
|
11187
|
-
number: import_zod4.z.number().describe("The number to round up")
|
|
11188
|
-
}),
|
|
11189
|
-
execute: async ({ number }) => {
|
|
11190
|
-
const value = Arithmetic.ceil(number);
|
|
11191
|
-
return { result: `${value}` };
|
|
11192
|
-
}
|
|
11193
|
-
});
|
|
11194
|
-
var roundTool = new ExuluTool3({
|
|
11195
|
-
id: "round",
|
|
11196
|
-
name: "Round",
|
|
11197
|
-
description: "Rounds a number to the nearest integer",
|
|
11198
|
-
type: "function",
|
|
11199
|
-
category: "math",
|
|
11200
|
-
config: [],
|
|
11201
|
-
inputSchema: import_zod4.z.object({
|
|
11202
|
-
number: import_zod4.z.number().describe("The number to round")
|
|
11203
|
-
}),
|
|
11204
|
-
execute: async ({ number }) => {
|
|
11205
|
-
const value = Arithmetic.round(number);
|
|
11206
|
-
return { result: `${value}` };
|
|
11207
|
-
}
|
|
11208
|
-
});
|
|
11209
|
-
var sinTool = new ExuluTool3({
|
|
11210
|
-
id: "sin",
|
|
11211
|
-
name: "Sine",
|
|
11212
|
-
description: "Calculates the sine of a number in radians",
|
|
11213
|
-
type: "function",
|
|
11214
|
-
category: "math",
|
|
11215
|
-
config: [],
|
|
11216
|
-
inputSchema: import_zod4.z.object({
|
|
11217
|
-
number: import_zod4.z.number().describe("The number in radians to find the sine of")
|
|
11218
|
-
}),
|
|
11219
|
-
execute: async ({ number }) => {
|
|
11220
|
-
const value = Trigonometric.sin(number);
|
|
11221
|
-
return { result: `${value}` };
|
|
11222
|
-
}
|
|
11223
|
-
});
|
|
11224
|
-
var arcsinTool = new ExuluTool3({
|
|
11225
|
-
id: "arcsin",
|
|
11226
|
-
name: "Arcsine",
|
|
11227
|
-
description: "Calculates the arcsine of a number in radians",
|
|
11228
|
-
type: "function",
|
|
11229
|
-
category: "math",
|
|
11230
|
-
config: [],
|
|
11231
|
-
inputSchema: import_zod4.z.object({
|
|
11232
|
-
number: import_zod4.z.number().describe("The number to find the arcsine of")
|
|
11233
|
-
}),
|
|
11234
|
-
execute: async ({ number }) => {
|
|
11235
|
-
const value = Trigonometric.arcsin(number);
|
|
11236
|
-
return { result: `${value}` };
|
|
11237
|
-
}
|
|
11238
|
-
});
|
|
11239
|
-
var cosTool = new ExuluTool3({
|
|
11240
|
-
id: "cos",
|
|
11241
|
-
name: "Cosine",
|
|
11242
|
-
description: "Calculates the cosine of a number in radians",
|
|
11243
|
-
type: "function",
|
|
11244
|
-
category: "math",
|
|
11245
|
-
config: [],
|
|
11246
|
-
inputSchema: import_zod4.z.object({
|
|
11247
|
-
number: import_zod4.z.number().describe("The number in radians to find the cosine of")
|
|
11248
|
-
}),
|
|
11249
|
-
execute: async ({ number }) => {
|
|
11250
|
-
const value = Trigonometric.cos(number);
|
|
11251
|
-
return { result: `${value}` };
|
|
11252
|
-
}
|
|
11253
|
-
});
|
|
11254
|
-
var arccosTool = new ExuluTool3({
|
|
11255
|
-
id: "arccos",
|
|
11256
|
-
name: "Arccosine",
|
|
11257
|
-
description: "Calculates the arccosine of a number in radians",
|
|
11258
|
-
type: "function",
|
|
11259
|
-
category: "math",
|
|
11260
|
-
config: [],
|
|
11261
|
-
inputSchema: import_zod4.z.object({
|
|
11262
|
-
number: import_zod4.z.number().describe("The number to find the arccosine of")
|
|
11263
|
-
}),
|
|
11264
|
-
execute: async ({ number }) => {
|
|
11265
|
-
const value = Trigonometric.arccos(number);
|
|
11266
|
-
return { result: `${value}` };
|
|
11267
|
-
}
|
|
11268
|
-
});
|
|
11269
|
-
var tanTool = new ExuluTool3({
|
|
11270
|
-
id: "tan",
|
|
11271
|
-
name: "Tangent",
|
|
11272
|
-
description: "Calculates the tangent of a number in radians",
|
|
11273
|
-
type: "function",
|
|
11274
|
-
category: "math",
|
|
11275
|
-
config: [],
|
|
11276
|
-
inputSchema: import_zod4.z.object({
|
|
11277
|
-
number: import_zod4.z.number().describe("The number in radians to find the tangent of")
|
|
11278
|
-
}),
|
|
11279
|
-
execute: async ({ number }) => {
|
|
11280
|
-
const value = Trigonometric.tan(number);
|
|
11281
|
-
return { result: `${value}` };
|
|
11282
|
-
}
|
|
11283
|
-
});
|
|
11284
|
-
var arctanTool = new ExuluTool3({
|
|
11285
|
-
id: "arctan",
|
|
11286
|
-
name: "Arctangent",
|
|
11287
|
-
description: "Calculates the arctangent of a number in radians",
|
|
11288
|
-
type: "function",
|
|
11289
|
-
category: "math",
|
|
11290
|
-
config: [],
|
|
11291
|
-
inputSchema: import_zod4.z.object({
|
|
11292
|
-
number: import_zod4.z.number().describe("The number to find the arctangent of")
|
|
11293
|
-
}),
|
|
11294
|
-
execute: async ({ number }) => {
|
|
11295
|
-
const value = Trigonometric.arctan(number);
|
|
11296
|
-
return { result: `${value}` };
|
|
11297
|
-
}
|
|
11298
|
-
});
|
|
11299
|
-
var radiansToDegreesTool = new ExuluTool3({
|
|
11300
|
-
id: "radiansToDegrees",
|
|
11301
|
-
name: "Radians to Degrees",
|
|
11302
|
-
description: "Converts a radian value to its equivalent in degrees",
|
|
11303
|
-
type: "function",
|
|
11304
|
-
category: "math",
|
|
11305
|
-
config: [],
|
|
11306
|
-
inputSchema: import_zod4.z.object({
|
|
11307
|
-
number: import_zod4.z.number().describe("The number in radians to convert to degrees")
|
|
11308
|
-
}),
|
|
11309
|
-
execute: async ({ number }) => {
|
|
11310
|
-
const value = Trigonometric.radiansToDegrees(number);
|
|
11311
|
-
return { result: `${value}` };
|
|
11312
|
-
}
|
|
11313
|
-
});
|
|
11314
|
-
var degreesToRadiansTool = new ExuluTool3({
|
|
11315
|
-
id: "degreesToRadians",
|
|
11316
|
-
name: "Degrees to Radians",
|
|
11317
|
-
description: "Converts a degree value to its equivalent in radians",
|
|
11318
|
-
type: "function",
|
|
11319
|
-
category: "math",
|
|
11320
|
-
config: [],
|
|
11321
|
-
inputSchema: import_zod4.z.object({
|
|
11322
|
-
number: import_zod4.z.number().describe("The number in degrees to convert to radians")
|
|
11323
|
-
}),
|
|
11324
|
-
execute: async ({ number }) => {
|
|
11325
|
-
const value = Trigonometric.degreesToRadians(number);
|
|
11326
|
-
return { result: `${value}` };
|
|
11327
|
-
}
|
|
11328
|
-
});
|
|
11329
|
-
var mathTools = [
|
|
11330
|
-
additionTool,
|
|
11331
|
-
subtractionTool,
|
|
11332
|
-
multiplicationTool,
|
|
11333
|
-
divisionTool,
|
|
11334
|
-
sumTool,
|
|
11335
|
-
moduloTool,
|
|
11336
|
-
meanTool,
|
|
11337
|
-
medianTool,
|
|
11338
|
-
modeTool,
|
|
11339
|
-
minTool,
|
|
11340
|
-
maxTool,
|
|
11341
|
-
floorTool,
|
|
11342
|
-
ceilingTool,
|
|
11343
|
-
roundTool,
|
|
11344
|
-
sinTool,
|
|
11345
|
-
arcsinTool,
|
|
11346
|
-
cosTool,
|
|
11347
|
-
arccosTool,
|
|
11348
|
-
tanTool,
|
|
11349
|
-
arctanTool,
|
|
11350
|
-
radiansToDegreesTool,
|
|
11351
|
-
degreesToRadiansTool
|
|
11352
|
-
];
|
|
11353
|
-
|
|
11354
|
-
// src/templates/tools/preview-pdf.ts
|
|
11355
|
-
var import_zod5 = require("zod");
|
|
11356
|
-
var previewPdfTool = new ExuluTool3({
|
|
11357
|
-
id: "preview_pdf",
|
|
11358
|
-
name: "Preview PDF",
|
|
11359
|
-
description: "Used to display a PDF file in an iframe web view",
|
|
11360
|
-
type: "function",
|
|
11361
|
-
config: [],
|
|
11362
|
-
inputSchema: import_zod5.z.object({
|
|
11363
|
-
s3key: import_zod5.z.string().describe("The S3 key of the PDF file to preview."),
|
|
11364
|
-
page: import_zod5.z.number().describe("The page number to preview, defaults to 1.").optional()
|
|
11365
|
-
}),
|
|
11366
|
-
execute: async ({ s3key, page, exuluConfig }) => {
|
|
11367
|
-
const bucket = s3key.split("/")[0];
|
|
11368
|
-
const key = s3key.split("/").slice(1).join("/");
|
|
11369
|
-
if (!bucket || !key) {
|
|
11370
|
-
throw new Error("Invalid S3 key, must be in the format of <bucket>/<key>.");
|
|
11371
|
-
}
|
|
11372
|
-
const url = await getPresignedUrl(bucket, key, exuluConfig);
|
|
11373
|
-
if (!url) {
|
|
11374
|
-
throw new Error("No URL provided for PDF preview");
|
|
11375
|
-
}
|
|
11376
|
-
return {
|
|
11377
|
-
result: JSON.stringify({
|
|
11378
|
-
url,
|
|
11379
|
-
page: page ?? 1
|
|
11380
|
-
})
|
|
11381
|
-
};
|
|
11382
|
-
}
|
|
11383
|
-
});
|
|
11384
|
-
|
|
11385
12360
|
// src/templates/tools/todo/todowrite.txt
|
|
11386
12361
|
var todowrite_default = `Use this tool to create and manage a structured task list for your current coding session. This helps you track progress, organize complex tasks, and demonstrate thoroughness to the user.
|
|
11387
12362
|
It also helps the user understand the progress of the task and overall progress of their requests.
|
|
@@ -11568,14 +12543,14 @@ Usage:
|
|
|
11568
12543
|
- If no todos exist yet, an empty list will be returned`;
|
|
11569
12544
|
|
|
11570
12545
|
// src/templates/tools/todo/todo.ts
|
|
11571
|
-
var
|
|
11572
|
-
var TodoSchema =
|
|
11573
|
-
content:
|
|
11574
|
-
status:
|
|
11575
|
-
priority:
|
|
11576
|
-
id:
|
|
12546
|
+
var import_zod5 = __toESM(require("zod"), 1);
|
|
12547
|
+
var TodoSchema = import_zod5.default.object({
|
|
12548
|
+
content: import_zod5.default.string().describe("Brief description of the task"),
|
|
12549
|
+
status: import_zod5.default.string().describe("Current status of the task: pending, in_progress, completed, cancelled"),
|
|
12550
|
+
priority: import_zod5.default.string().describe("Priority level of the task: high, medium, low"),
|
|
12551
|
+
id: import_zod5.default.string().describe("Unique identifier for the todo item")
|
|
11577
12552
|
});
|
|
11578
|
-
var TodoWriteTool = new
|
|
12553
|
+
var TodoWriteTool = new ExuluTool({
|
|
11579
12554
|
id: "todo_write",
|
|
11580
12555
|
name: "Todo Write",
|
|
11581
12556
|
description: "Use this tool to write your todo list",
|
|
@@ -11584,10 +12559,11 @@ var TodoWriteTool = new ExuluTool3({
|
|
|
11584
12559
|
config: [{
|
|
11585
12560
|
name: "description",
|
|
11586
12561
|
description: "The description of the todo list, if set overwrites the default description.",
|
|
12562
|
+
type: "string",
|
|
11587
12563
|
default: todowrite_default
|
|
11588
12564
|
}],
|
|
11589
|
-
inputSchema:
|
|
11590
|
-
todos:
|
|
12565
|
+
inputSchema: import_zod5.default.object({
|
|
12566
|
+
todos: import_zod5.default.array(TodoSchema).describe("The updated todo list")
|
|
11591
12567
|
}),
|
|
11592
12568
|
execute: async (inputs) => {
|
|
11593
12569
|
const { sessionID, todos, user } = inputs;
|
|
@@ -11614,16 +12590,17 @@ var TodoWriteTool = new ExuluTool3({
|
|
|
11614
12590
|
};
|
|
11615
12591
|
}
|
|
11616
12592
|
});
|
|
11617
|
-
var TodoReadTool = new
|
|
12593
|
+
var TodoReadTool = new ExuluTool({
|
|
11618
12594
|
id: "todo_read",
|
|
11619
12595
|
name: "Todo Read",
|
|
11620
12596
|
description: "Use this tool to read your todo list",
|
|
11621
|
-
inputSchema:
|
|
12597
|
+
inputSchema: import_zod5.default.object({}),
|
|
11622
12598
|
type: "function",
|
|
11623
12599
|
category: "todo",
|
|
11624
12600
|
config: [{
|
|
11625
12601
|
name: "description",
|
|
11626
12602
|
description: "The description of the todo list, if set overwrites the default description.",
|
|
12603
|
+
type: "string",
|
|
11627
12604
|
default: todoread_default
|
|
11628
12605
|
}],
|
|
11629
12606
|
execute: async (inputs) => {
|
|
@@ -11656,6 +12633,109 @@ var todoTools = [
|
|
|
11656
12633
|
TodoReadTool
|
|
11657
12634
|
];
|
|
11658
12635
|
|
|
12636
|
+
// src/templates/tools/perplexity.ts
|
|
12637
|
+
var import_zod6 = __toESM(require("zod"), 1);
|
|
12638
|
+
var import_perplexity_ai = __toESM(require("@perplexity-ai/perplexity_ai"), 1);
|
|
12639
|
+
var internetSearchTool = new ExuluTool({
|
|
12640
|
+
id: "internet_search",
|
|
12641
|
+
name: "Perplexity Live Internet Search",
|
|
12642
|
+
description: "Search the internet for information.",
|
|
12643
|
+
inputSchema: import_zod6.default.object({
|
|
12644
|
+
query: import_zod6.default.string().describe("The query to the tool."),
|
|
12645
|
+
search_recency_filter: import_zod6.default.enum(["day", "week", "month", "year"]).optional().describe("The recency filter for the search, can be day, week, month or year.")
|
|
12646
|
+
}),
|
|
12647
|
+
category: "internet_search",
|
|
12648
|
+
type: "web_search",
|
|
12649
|
+
config: [
|
|
12650
|
+
{
|
|
12651
|
+
name: "perplexity_api_key",
|
|
12652
|
+
description: "The API key for the Perplexity API.",
|
|
12653
|
+
type: "variable",
|
|
12654
|
+
default: ""
|
|
12655
|
+
},
|
|
12656
|
+
{
|
|
12657
|
+
name: "search_domain_filter",
|
|
12658
|
+
description: "Comma seperated domain filter for the search.",
|
|
12659
|
+
type: "string",
|
|
12660
|
+
default: ""
|
|
12661
|
+
}
|
|
12662
|
+
],
|
|
12663
|
+
execute: async ({ query, search_recency_filter, toolVariablesConfig }) => {
|
|
12664
|
+
const {
|
|
12665
|
+
perplexity_api_key: apiKey,
|
|
12666
|
+
search_domain_filter
|
|
12667
|
+
} = toolVariablesConfig;
|
|
12668
|
+
let domainFilterArray = [];
|
|
12669
|
+
if (search_domain_filter) {
|
|
12670
|
+
domainFilterArray = search_domain_filter.split(",").map((domain) => domain.trim());
|
|
12671
|
+
}
|
|
12672
|
+
const maxRetries = 3;
|
|
12673
|
+
if (!apiKey) {
|
|
12674
|
+
throw new Error("Perplexity API key is required.");
|
|
12675
|
+
}
|
|
12676
|
+
const client2 = new import_perplexity_ai.default({
|
|
12677
|
+
apiKey
|
|
12678
|
+
});
|
|
12679
|
+
let recency_filter;
|
|
12680
|
+
if (search_recency_filter && ["day", "week", "month", "year"].includes(search_recency_filter)) {
|
|
12681
|
+
recency_filter = search_recency_filter;
|
|
12682
|
+
}
|
|
12683
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
12684
|
+
try {
|
|
12685
|
+
const result = await client2.chat.completions.create({
|
|
12686
|
+
messages: [{
|
|
12687
|
+
"role": "user",
|
|
12688
|
+
"content": `${query}`
|
|
12689
|
+
}],
|
|
12690
|
+
model: "sonar-pro",
|
|
12691
|
+
web_search_options: {
|
|
12692
|
+
...domainFilterArray.length > 0 ? { "search_domain_filter": domainFilterArray } : {},
|
|
12693
|
+
...recency_filter ? { "search_recency_filter": recency_filter } : {},
|
|
12694
|
+
"user_location": {
|
|
12695
|
+
"country": "CH"
|
|
12696
|
+
}
|
|
12697
|
+
}
|
|
12698
|
+
});
|
|
12699
|
+
if (!result || !result.choices[0]) {
|
|
12700
|
+
throw new Error("No content returned from internet search.");
|
|
12701
|
+
}
|
|
12702
|
+
const content = result.choices[0].message.content;
|
|
12703
|
+
const parsed = {
|
|
12704
|
+
content: content.replace(/(\[([1-9][0-9]*)\])/g, (match, p1, p2) => {
|
|
12705
|
+
const index = parseInt(p2) - 1;
|
|
12706
|
+
if (!result.search_results || !result.search_results[index]) {
|
|
12707
|
+
return match;
|
|
12708
|
+
}
|
|
12709
|
+
return JSON.stringify({
|
|
12710
|
+
url: result.search_results[index].url,
|
|
12711
|
+
title: result.search_results[index].title,
|
|
12712
|
+
snippet: result.search_results[index].snippet
|
|
12713
|
+
});
|
|
12714
|
+
}),
|
|
12715
|
+
citations: result.citations,
|
|
12716
|
+
search_results: result.search_results
|
|
12717
|
+
};
|
|
12718
|
+
return {
|
|
12719
|
+
result: JSON.stringify(parsed)
|
|
12720
|
+
};
|
|
12721
|
+
} catch (error) {
|
|
12722
|
+
if (error instanceof import_perplexity_ai.default.RateLimitError && attempt < maxRetries - 1) {
|
|
12723
|
+
const delay = Math.pow(2, attempt) * 1e3 + Math.random() * 1e3;
|
|
12724
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
12725
|
+
continue;
|
|
12726
|
+
}
|
|
12727
|
+
throw error;
|
|
12728
|
+
}
|
|
12729
|
+
}
|
|
12730
|
+
return {
|
|
12731
|
+
result: "Max retries exceeded for perplexity research for query."
|
|
12732
|
+
};
|
|
12733
|
+
}
|
|
12734
|
+
});
|
|
12735
|
+
var perplexityTools = [
|
|
12736
|
+
internetSearchTool
|
|
12737
|
+
];
|
|
12738
|
+
|
|
11659
12739
|
// src/registry/index.ts
|
|
11660
12740
|
var isDev = process.env.NODE_ENV !== "production";
|
|
11661
12741
|
var consoleTransport = new import_winston2.default.transports.Console({
|
|
@@ -11692,6 +12772,7 @@ var ExuluApp = class {
|
|
|
11692
12772
|
_config;
|
|
11693
12773
|
_evals = [];
|
|
11694
12774
|
_queues = [];
|
|
12775
|
+
_rerankers = [];
|
|
11695
12776
|
_contexts = {};
|
|
11696
12777
|
_tools = [];
|
|
11697
12778
|
_expressApp = null;
|
|
@@ -11704,16 +12785,24 @@ var ExuluApp = class {
|
|
|
11704
12785
|
agents,
|
|
11705
12786
|
config,
|
|
11706
12787
|
tools,
|
|
11707
|
-
evals
|
|
12788
|
+
evals,
|
|
12789
|
+
rerankers
|
|
11708
12790
|
}) => {
|
|
11709
12791
|
this._evals = redisServer.host?.length && redisServer.port?.length ? [...getDefaultEvals(), ...evals ?? []] : [];
|
|
11710
12792
|
this._contexts = {
|
|
11711
12793
|
...contexts
|
|
11712
12794
|
};
|
|
12795
|
+
this._rerankers = [
|
|
12796
|
+
...rerankers ?? []
|
|
12797
|
+
];
|
|
11713
12798
|
this._agents = [
|
|
11714
12799
|
claudeSonnet4Agent,
|
|
11715
12800
|
claudeOpus4Agent,
|
|
12801
|
+
gptOss120bAgent,
|
|
12802
|
+
llama38bAgent,
|
|
12803
|
+
llama3370bAgent,
|
|
11716
12804
|
vertexGemini25FlashAgent,
|
|
12805
|
+
vertexGemini25ProAgent,
|
|
11717
12806
|
vertexGemini3ProAgent,
|
|
11718
12807
|
claudeSonnet45Agent,
|
|
11719
12808
|
gpt5MiniAgent,
|
|
@@ -11730,9 +12819,8 @@ var ExuluApp = class {
|
|
|
11730
12819
|
this._config = config;
|
|
11731
12820
|
this._tools = [
|
|
11732
12821
|
...tools ?? [],
|
|
11733
|
-
...mathTools,
|
|
11734
|
-
...[previewPdfTool],
|
|
11735
12822
|
...todoTools,
|
|
12823
|
+
...perplexityTools,
|
|
11736
12824
|
// Add contexts as tools
|
|
11737
12825
|
...Object.values(contexts || {}).map((context) => context.tool()).filter(Boolean)
|
|
11738
12826
|
// Because agents are stored in the database, we add those as tools
|
|
@@ -11751,16 +12839,21 @@ var ExuluApp = class {
|
|
|
11751
12839
|
id: agent.id ?? "",
|
|
11752
12840
|
type: "agent"
|
|
11753
12841
|
})),
|
|
11754
|
-
...this._tools.map((
|
|
11755
|
-
name:
|
|
11756
|
-
id:
|
|
12842
|
+
...this._tools.map((tool3) => ({
|
|
12843
|
+
name: tool3.name ?? "",
|
|
12844
|
+
id: tool3.id ?? "",
|
|
11757
12845
|
type: "tool"
|
|
12846
|
+
})),
|
|
12847
|
+
...this._rerankers.map((reranker) => ({
|
|
12848
|
+
name: reranker.name ?? "",
|
|
12849
|
+
id: reranker.id ?? "",
|
|
12850
|
+
type: "reranker"
|
|
11758
12851
|
}))
|
|
11759
12852
|
];
|
|
11760
12853
|
const invalid = checks.filter((x) => !isValidPostgresName(x?.id ?? ""));
|
|
11761
12854
|
if (invalid.length > 0) {
|
|
11762
12855
|
console.error(
|
|
11763
|
-
`%c[EXULU] Invalid ID found for a context, tool or agent: ${invalid.map((x) => x.id).join(", ")}. An ID must begin with a letter (a-z) or underscore (_). Subsequent characters in a name can be letters, digits (0-9), or underscores and be a max length of 80 characters and at least 5 characters long.`,
|
|
12856
|
+
`%c[EXULU] Invalid ID found for a context, tool, reranker or agent: ${invalid.map((x) => x.id).join(", ")}. An ID must begin with a letter (a-z) or underscore (_). Subsequent characters in a name can be letters, digits (0-9), or underscores and be a max length of 80 characters and at least 5 characters long.`,
|
|
11764
12857
|
"color: orange; font-weight: bold; \n \n"
|
|
11765
12858
|
);
|
|
11766
12859
|
throw new Error(
|
|
@@ -11955,6 +13048,7 @@ var ExuluApp = class {
|
|
|
11955
13048
|
filteredQueues,
|
|
11956
13049
|
this._config,
|
|
11957
13050
|
Object.values(this._contexts ?? {}),
|
|
13051
|
+
this._rerankers,
|
|
11958
13052
|
this._evals,
|
|
11959
13053
|
this._tools,
|
|
11960
13054
|
tracer
|
|
@@ -11998,7 +13092,8 @@ var ExuluApp = class {
|
|
|
11998
13092
|
this._config,
|
|
11999
13093
|
this._evals,
|
|
12000
13094
|
tracer,
|
|
12001
|
-
this._queues
|
|
13095
|
+
this._queues,
|
|
13096
|
+
this._rerankers
|
|
12002
13097
|
);
|
|
12003
13098
|
if (this._config?.MCP.enabled) {
|
|
12004
13099
|
const mcp = new ExuluMCP();
|
|
@@ -12007,6 +13102,7 @@ var ExuluApp = class {
|
|
|
12007
13102
|
allTools: this._tools,
|
|
12008
13103
|
allAgents: this._agents,
|
|
12009
13104
|
allContexts: Object.values(this._contexts ?? {}),
|
|
13105
|
+
allRerankers: this._rerankers,
|
|
12010
13106
|
config: this._config
|
|
12011
13107
|
});
|
|
12012
13108
|
}
|
|
@@ -13483,8 +14579,14 @@ var ExuluDefaultAgents = {
|
|
|
13483
14579
|
sonnet4: claudeSonnet4Agent,
|
|
13484
14580
|
sonnet45: claudeSonnet45Agent
|
|
13485
14581
|
},
|
|
14582
|
+
cerebras: {
|
|
14583
|
+
gptOss120b: gptOss120bAgent,
|
|
14584
|
+
llama38b: llama38bAgent,
|
|
14585
|
+
llama3370b: llama3370bAgent
|
|
14586
|
+
},
|
|
13486
14587
|
google: {
|
|
13487
14588
|
vertexGemini25Flash: vertexGemini25FlashAgent,
|
|
14589
|
+
vertexGemini25Pro: vertexGemini25ProAgent,
|
|
13488
14590
|
vertexGemini3Pro: vertexGemini3ProAgent
|
|
13489
14591
|
},
|
|
13490
14592
|
openai: {
|
|
@@ -13640,6 +14742,7 @@ var ExuluChunkers = {
|
|
|
13640
14742
|
ExuluJobs,
|
|
13641
14743
|
ExuluOtel,
|
|
13642
14744
|
ExuluQueues,
|
|
14745
|
+
ExuluReranker,
|
|
13643
14746
|
ExuluTool,
|
|
13644
14747
|
ExuluUtils,
|
|
13645
14748
|
ExuluVariables,
|