@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/dist/index.cjs CHANGED
@@ -43,7 +43,8 @@ __export(index_exports, {
43
43
  ExuluJobs: () => ExuluJobs,
44
44
  ExuluOtel: () => ExuluOtel,
45
45
  ExuluQueues: () => queues,
46
- ExuluTool: () => ExuluTool3,
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 import_zod = require("zod");
126
- var import_ai2 = require("ai");
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 import_graphql2 = require("graphql");
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((tool2) => !disabledTools.includes(tool2.id));
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 import_ai = require("ai");
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 import_graphql2.GraphQLScalarType({
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 === import_graphql2.Kind.STRING) {
3562
+ if (ast.kind === import_graphql3.Kind.STRING) {
2751
3563
  return new Date(ast.value);
2752
3564
  }
2753
- if (ast.kind === import_graphql2.Kind.INT) {
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 (tool2) => {
4736
+ async (tool3) => {
3917
4737
  let hydrated;
3918
- if (tool2.type === "agent") {
3919
- if (tool2.id === result.id) {
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(tool2.id);
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 " + tool2.id + " was not found in the database."
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 " + tool2.id + " does not have a backend set for it."
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 === tool2.id);
4780
+ hydrated = tools.find((t) => t.id === tool3.id);
3945
4781
  }
3946
4782
  const hydratedTool = {
3947
- ...tool2,
4783
+ ...tool3,
3948
4784
  name: hydrated?.name || "",
3949
4785
  description: hydrated?.description || "",
3950
- category: tool2?.category || "default"
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 createProjectRetrievalTool({
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((tool2) => tool2 !== null);
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
- operators.or.forEach((operator) => {
4283
- query = converOperatorToQuery(
4284
- query,
4285
- fieldName,
4286
- operator,
4287
- table,
4288
- field_prefix
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 applySorting = (query, sort, field_prefix) => {
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 = applySorting(dataQuery, sort);
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 = applySorting(query, sort);
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, filters = [], sort } = args;
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
- filters,
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
- filters,
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
- filters,
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, filters, table, "items");
5497
+ chunksQuery = applyFilters(chunksQuery, itemFilters, table, "items");
5498
+ chunksQuery = applyFilters(chunksQuery, chunkFilters, table, "chunks");
4628
5499
  chunksQuery = applyAccessControl(table, chunksQuery, user, "items");
4629
- chunksQuery = applySorting(chunksQuery, sort, "items");
4630
- if (queryRewriter) {
5500
+ chunksQuery = applySorting2(chunksQuery, sort, "items");
5501
+ if (queryRewriter && query) {
4631
5502
  query = await queryRewriter(query);
4632
5503
  }
4633
- const { chunks: queryChunks } = await embedder.generateFromQuery(
4634
- context.id,
4635
- query,
4636
- {
4637
- label: table.name.singular,
4638
- trigger
4639
- },
4640
- user?.id,
4641
- role
4642
- );
4643
- if (!queryChunks?.[0]?.vector) {
4644
- throw new Error("No vector generated for query.");
4645
- }
4646
- const vector = queryChunks[0].vector;
4647
- const vectorStr = `ARRAY[${vector.join(",")}]`;
4648
- const vectorExpr = `${vectorStr}::vector`;
4649
- const language = configuration.language || "english";
4650
- console.log("[EXULU] Vector search params:", { method, query, cutoffs });
4651
- let resultChunks = [];
4652
- switch (method) {
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 tokens = query.trim().split(/\s+/).filter((t) => t.length > 0);
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
- tokens,
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, filters, table, "items");
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, filters, table, "items");
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
- filters,
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!, filters: [Filter${tableNameSingularUpperCaseFirst}], cutoffs: SearchCutoffs, expand: SearchExpand): ${tableNameSingular}VectorSearchResult
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
- filters: JSON!
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!, cases: [ID!]): RunEvalReturnPayload
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: context2.fields.map((field) => {
5945
- return {
5946
- ...field,
5947
- name: sanitizeName(field.name),
5948
- label: field.name?.replace("_s3key", "")
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
- (tool2) => tool2 !== null
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
- (tool2) => tool2.name?.toLowerCase().includes(searchTerm) || tool2.description?.toLowerCase().includes(searchTerm)
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((tool2) => tool2.category === category);
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((tool2) => {
7025
+ items: paginatedTools.map((tool3) => {
6085
7026
  const object = {};
6086
7027
  requestedFields.forEach((field) => {
6087
- object[field] = tool2[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((tool2) => tool2.category).filter((category) => category && typeof category === "string");
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_-]+/g, "_");
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.length > 128) {
7097
- sanitized = sanitized.substring(0, 128);
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 projectsCache = /* @__PURE__ */ new Map();
7102
- var createProjectRetrievalTool = async ({
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
- if (cachedProject && cachedProject.age > OneMinuteAgo) {
7112
- project = cachedProject.project;
7113
- } else {
7114
- const { db: db3 } = await postgresClient();
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 ExuluTool3({
7130
- id: "project_information_retrieval_tool_" + projectId,
7131
- name: "Project information retrieval tool for project " + project.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: import_zod.z.object({
7134
- query: import_zod.z.string().describe("The query to retrieve information about the project " + project.name + "."),
7135
- keywords: import_zod.z.array(import_zod.z.string()).describe("The most relevant keywords in the query, such as names of people, companies, products, etc. in the project " + project.name + ".")
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: "function",
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
- return await context.search({
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
- filters: [{
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 convertToolsArrayToObject = async (currentTools, allExuluTools, configs, providerapikey, contexts, user, exuluConfig, sessionID, req, project) => {
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
- const projectRetrievalTool = await createProjectRetrievalTool({
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
- const sanitizedTools = currentTools ? currentTools.map((tool2) => ({
7216
- ...tool2,
7217
- name: sanitizeToolName(tool2.name)
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: toolVariableConfig ? toolVariableConfig.config.reduce((acc, curr) => {
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
- yield response;
7322
- return response;
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 (tool2) => {
8510
+ var hydrateVariables = async (tool3) => {
7333
8511
  const { db: db3 } = await postgresClient();
7334
- const promises = tool2.config.map(async (toolConfig) => {
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 tool2;
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 ExuluTool3({
8679
+ return new ExuluTool({
7476
8680
  id: agentInstance.id,
7477
8681
  name: `${agentInstance.name}`,
7478
8682
  type: "agent",
7479
8683
  category: "agents",
7480
- inputSchema: import_zod.z.object({
7481
- prompt: import_zod.z.string().describe("The prompt (usually a question for the agent) to send to the agent."),
7482
- information: import_zod.z.string().describe("A summary of relevant context / information from the current session")
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, import_ai2.validateUIMessages)({
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
- const includesContextSearchTool = currentTools?.some((tool2) => tool2.name.toLowerCase().includes("context_search") || tool2.id.includes("context_search"));
7608
- console.log("[EXULU] Current tools: " + currentTools?.map((tool2) => tool2.name));
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 context search tool inline in the response using this exact JSON format
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 (e.g., "dx-newlift-newton-knowledge-g3y6r1") from the tool result
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, import_ai2.generateObject)({
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, import_ai2.generateText)({
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, import_ai2.stepCountIs)(5)]
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, import_ai2.generateText)({
8982
+ const { text, totalUsage } = await (0, import_ai3.generateText)({
7708
8983
  model,
7709
8984
  // Should be a LanguageModelV1
7710
8985
  system,
7711
- messages: (0, import_ai2.convertToModelMessages)(messages, {
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, import_ai2.stepCountIs)(5)]
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, import_ai2.validateUIMessages)({
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.findIndex((t) => t.id === message2.id)
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
- const includesContextSearchTool = currentTools?.some((tool2) => tool2.name.toLowerCase().includes("context_search") || tool2.id.includes("context_search"));
7893
- console.log("[EXULU] Current tools: " + currentTools?.map((tool2) => tool2.name));
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 context search tool inline in the response using this exact JSON format
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 (e.g., "dx-newlift-newton-knowledge-g3y6r1") from the tool result
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
- const result = (0, import_ai2.streamText)({
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, import_ai2.convertToModelMessages)(messages, {
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, import_ai2.stepCountIs)(5)]
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 ExuluTool3 = class {
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, import_ai2.tool)({
9495
+ this.tool = (0, import_ai3.tool)({
8146
9496
  description,
8147
- inputSchema: inputSchema || import_zod.z.object({}),
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 tool2 = tools[sanitizeName(this.name)] || tools[this.name] || tools[this.id];
8193
- if (!tool2?.execute) {
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 generator = tool2.execute(inputs, {
8198
- toolCallId: this.id + "_" + (0, import_node_crypto2.randomUUID)(),
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 refreshPostgresClient();
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 ExuluTool3({
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: import_zod.z.object({
8855
- originalQuestion: import_zod.z.string().describe("The original question that the user asked"),
8856
- relevantKeywords: import_zod.z.array(import_zod.z.string()).describe("The keywords that are relevant to the user's question, for example names of specific products, systems or parts, IDs, etc.")
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 ({ originalQuestion, relevantKeywords, user, role }) => {
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: originalQuestion,
8866
- filters: [],
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 import_fs = __toESM(require("fs"), 1);
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 import_ai3 = require("ai");
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 = import_fs.default.readFileSync(path + "/package.json", "utf8");
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("/generate/agent/image", async (req, res) => {
9137
- console.log("[EXULU] generate/agent/image", req.body);
9138
- const authenticationResult = await requestValidators.authenticate(req);
9139
- if (!authenticationResult.user?.id) {
9140
- res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
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 (!import_fs.default.existsSync("public")) {
9219
- import_fs.default.mkdirSync("public");
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, import_ai3.createIdGenerator)({
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 tool2 = enabledTools.find((tool3) => tool3.id === toolName.replace("exulu_", ""));
9632
- if (!tool2 || !tool2.tool.execute) {
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 tool2.tool.execute(inputs, {
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 import_zod2 = require("zod");
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 tool2 of enabledTools || []) {
9803
- if (server.tools[tool2.id]) {
11273
+ for (const tool3 of enabledTools || []) {
11274
+ if (server.tools[tool3.id]) {
9804
11275
  continue;
9805
11276
  }
9806
- server.mcp.registerTool(sanitizeToolName(tool2.name + "_agent_" + tool2.id), {
9807
- title: tool2.name + " agent",
9808
- description: tool2.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: tool2.inputSchema || import_zod2.z.object({})
11281
+ inputs: tool3.inputSchema || import_zod3.z.object({})
9811
11282
  }
9812
11283
  }, async ({ inputs }, args) => {
9813
- console.log("[EXULU] MCP tool name", tool2.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
- [tool2],
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(tool2.name)];
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: tool2.id + "_" + (0, import_node_crypto4.randomUUID)(),
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[tool2.id] = tool2.name;
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: import_zod2.z.object({})
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: import_zod2.z.object({
9890
- id: import_zod2.z.string().describe("The ID of the prompt template to retrieve")
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 import_zod3 = require("zod");
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: import_zod3.z.object({
10710
- score: import_zod3.z.number().min(0).max(100).describe("The score between 0 and 100.")
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 import_zod6 = __toESM(require("zod"), 1);
11572
- var TodoSchema = import_zod6.default.object({
11573
- content: import_zod6.default.string().describe("Brief description of the task"),
11574
- status: import_zod6.default.string().describe("Current status of the task: pending, in_progress, completed, cancelled"),
11575
- priority: import_zod6.default.string().describe("Priority level of the task: high, medium, low"),
11576
- id: import_zod6.default.string().describe("Unique identifier for the todo item")
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 ExuluTool3({
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: import_zod6.default.object({
11590
- todos: import_zod6.default.array(TodoSchema).describe("The updated todo list")
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 ExuluTool3({
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: import_zod6.default.object({}),
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((tool2) => ({
11755
- name: tool2.name ?? "",
11756
- id: tool2.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,