@exulu/backend 1.55.0 → 1.56.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 +297 -146
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +297 -146
- package/ee/agentic-retrieval/v3/classifier.ts +19 -5
- package/ee/agentic-retrieval/v3/context-sampler.ts +10 -1
- package/ee/agentic-retrieval/v3/index.ts +110 -28
- package/ee/agentic-retrieval/v3/tools.ts +13 -5
- package/ee/agentic-retrieval/v4/agent-loop.ts +208 -0
- package/ee/agentic-retrieval/v4/context-sampler.ts +79 -0
- package/ee/agentic-retrieval/v4/index.ts +690 -0
- package/ee/agentic-retrieval/v4/types.ts +58 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1193,7 +1193,7 @@ var hydrateVariables = async (tool5) => {
|
|
|
1193
1193
|
await Promise.all(promises2);
|
|
1194
1194
|
return tool5;
|
|
1195
1195
|
};
|
|
1196
|
-
var convertExuluToolsToAiSdkTools = async (currentTools, approvedTools, allExuluTools, configs, providerapikey, contexts, rerankers, user, exuluConfig, sessionID, req, project, sessionItems, model, agent) => {
|
|
1196
|
+
var convertExuluToolsToAiSdkTools = async (currentTools, approvedTools, allExuluTools, configs, providerapikey, contexts, rerankers, user, exuluConfig, sessionID, req, project, sessionItems, model, agent, memoryItems) => {
|
|
1197
1197
|
if (!currentTools) return {};
|
|
1198
1198
|
if (!allExuluTools) {
|
|
1199
1199
|
allExuluTools = [];
|
|
@@ -1249,7 +1249,8 @@ var convertExuluToolsToAiSdkTools = async (currentTools, approvedTools, allExulu
|
|
|
1249
1249
|
user,
|
|
1250
1250
|
role: user?.role?.id,
|
|
1251
1251
|
model,
|
|
1252
|
-
|
|
1252
|
+
preselected: sessionItems,
|
|
1253
|
+
memoryItems
|
|
1253
1254
|
});
|
|
1254
1255
|
if (agenticSearchTool) {
|
|
1255
1256
|
const index = currentTools.findIndex((tool5) => tool5.id === "agentic_context_search");
|
|
@@ -1383,6 +1384,8 @@ var convertExuluToolsToAiSdkTools = async (currentTools, approvedTools, allExulu
|
|
|
1383
1384
|
...inputs,
|
|
1384
1385
|
model,
|
|
1385
1386
|
sessionID,
|
|
1387
|
+
sessionItems,
|
|
1388
|
+
memory: memoryItems,
|
|
1386
1389
|
req,
|
|
1387
1390
|
// Convert config to object format if a config object
|
|
1388
1391
|
// is available, after we added the .value property
|
|
@@ -1570,6 +1573,95 @@ var ExuluTool = class {
|
|
|
1570
1573
|
};
|
|
1571
1574
|
};
|
|
1572
1575
|
|
|
1576
|
+
// ee/agentic-retrieval/v3/classifier.ts
|
|
1577
|
+
import { generateText, Output } from "ai";
|
|
1578
|
+
import { z as z5 } from "zod";
|
|
1579
|
+
|
|
1580
|
+
// src/utils/with-retry.ts
|
|
1581
|
+
async function withRetry(generateFn, maxRetries = 3) {
|
|
1582
|
+
let lastError;
|
|
1583
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
1584
|
+
try {
|
|
1585
|
+
return await generateFn();
|
|
1586
|
+
} catch (error) {
|
|
1587
|
+
lastError = error;
|
|
1588
|
+
console.error(`[EXULU] generateText attempt ${attempt} failed:`, error);
|
|
1589
|
+
if (attempt === maxRetries) {
|
|
1590
|
+
throw error;
|
|
1591
|
+
}
|
|
1592
|
+
await new Promise((resolve3) => setTimeout(resolve3, Math.pow(2, attempt) * 1e3));
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
throw lastError;
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
// ee/agentic-retrieval/v3/classifier.ts
|
|
1599
|
+
async function classifyQuery(query, contexts, samples, model) {
|
|
1600
|
+
const contextDescriptions = contexts.map((ctx) => {
|
|
1601
|
+
const sample = samples.find((s) => s.contextId === ctx.id);
|
|
1602
|
+
const fieldList = sample?.fields.join(", ") ?? "name, external_id";
|
|
1603
|
+
return `
|
|
1604
|
+
<context>
|
|
1605
|
+
<id>
|
|
1606
|
+
${ctx.id}
|
|
1607
|
+
</id>
|
|
1608
|
+
<name>
|
|
1609
|
+
${ctx.name}
|
|
1610
|
+
</name>
|
|
1611
|
+
<description>
|
|
1612
|
+
${ctx.description}
|
|
1613
|
+
</description>
|
|
1614
|
+
<fields>
|
|
1615
|
+
${fieldList}
|
|
1616
|
+
</fields>
|
|
1617
|
+
<example_items>
|
|
1618
|
+
${sample?.exampleItems.map((item) => JSON.stringify(item)).join("\n")}
|
|
1619
|
+
</example_items>
|
|
1620
|
+
</context>
|
|
1621
|
+
`;
|
|
1622
|
+
}).join("\n\n");
|
|
1623
|
+
const result = await withRetry(async () => {
|
|
1624
|
+
const result2 = await generateText({
|
|
1625
|
+
model,
|
|
1626
|
+
temperature: 0,
|
|
1627
|
+
output: Output.object({
|
|
1628
|
+
schema: z5.object({
|
|
1629
|
+
queryType: z5.enum(["aggregate", "list", "targeted", "exploratory"]).describe(
|
|
1630
|
+
"aggregate: ONLY use when the user explicitly asks to COUNT how many documents/items/tickets exist in the knowledge base (e.g. 'how many documents about X?', 'total number of tickets'). NEVER use for: real-world statistics stored in a document, intent statements, how-to questions, error/fault descriptions, configuration questions, or any query that does not explicitly ask for a count of knowledge base entries. When in doubt, choose targeted. list: user wants to enumerate matching items/documents (show me all, list documents about). targeted: use for almost everything \u2014 specific fact, answer, configuration, how-to, error/fault, feature/behavior question. Also use for intent statements and short commands describing a desired state (phrases that state what the user wants to do or achieve, even without an explicit question word). Real-world statistics stored in documents also go here. When in doubt, choose targeted over aggregate or exploratory. exploratory: only for broad conceptual questions needing multi-source synthesis (what is the process for Z, explain how X works, general overview of topic Y)."
|
|
1631
|
+
),
|
|
1632
|
+
language: z5.string().describe("ISO 639-3 language code of the query (e.g. eng, deu, fra)"),
|
|
1633
|
+
suggestedContextIds: z5.array(z5.enum(contexts.map((c) => c.id))).describe(
|
|
1634
|
+
"IDs of knowledge bases most likely to contain the answer. Return empty array to search all contexts."
|
|
1635
|
+
)
|
|
1636
|
+
})
|
|
1637
|
+
}),
|
|
1638
|
+
toolChoice: "none",
|
|
1639
|
+
system: `You are a query classifier for a multi-knowledge-base retrieval system.
|
|
1640
|
+
Classify the query and identify which knowledge bases are most relevant.
|
|
1641
|
+
|
|
1642
|
+
Available knowledge bases:
|
|
1643
|
+
${contextDescriptions}
|
|
1644
|
+
|
|
1645
|
+
Guidelines for queryType:
|
|
1646
|
+
- Use "aggregate" ONLY when the query contains explicit counting language (e.g., "how many", "count", "total number", "wie viele"). Short statements, commands, or phrases without a question word are NEVER aggregate \u2014 classify them as targeted.
|
|
1647
|
+
- When in doubt between aggregate and targeted: always choose targeted.
|
|
1648
|
+
|
|
1649
|
+
Guidelines for suggestedContextIds:
|
|
1650
|
+
- Be conservative: only suggest contexts that are genuinely likely to contain the answer.
|
|
1651
|
+
Aim for 2\u20133 focused suggestions rather than listing everything.
|
|
1652
|
+
- Use each knowledge base's name and description (shown above) to judge relevance.
|
|
1653
|
+
- Return an empty array only if you truly cannot determine which contexts are relevant.`,
|
|
1654
|
+
prompt: `Query: ${query}`
|
|
1655
|
+
});
|
|
1656
|
+
return result2.output;
|
|
1657
|
+
}, 3);
|
|
1658
|
+
return result;
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
// ee/agentic-retrieval/v3/tools.ts
|
|
1662
|
+
import { z as z6 } from "zod";
|
|
1663
|
+
import { tool as tool2 } from "ai";
|
|
1664
|
+
|
|
1573
1665
|
// src/uppy/index.ts
|
|
1574
1666
|
import "express";
|
|
1575
1667
|
import {
|
|
@@ -3434,6 +3526,45 @@ var promptFavoritesSchema = {
|
|
|
3434
3526
|
}
|
|
3435
3527
|
]
|
|
3436
3528
|
};
|
|
3529
|
+
var contextPresetsSchema = {
|
|
3530
|
+
type: "context_presets",
|
|
3531
|
+
name: {
|
|
3532
|
+
plural: "context_presets",
|
|
3533
|
+
singular: "context_preset"
|
|
3534
|
+
},
|
|
3535
|
+
RBAC: true,
|
|
3536
|
+
fields: [
|
|
3537
|
+
{
|
|
3538
|
+
name: "name",
|
|
3539
|
+
type: "text",
|
|
3540
|
+
required: true,
|
|
3541
|
+
index: true
|
|
3542
|
+
},
|
|
3543
|
+
{
|
|
3544
|
+
name: "description",
|
|
3545
|
+
type: "text"
|
|
3546
|
+
},
|
|
3547
|
+
{
|
|
3548
|
+
name: "preset_items",
|
|
3549
|
+
type: "json",
|
|
3550
|
+
required: true
|
|
3551
|
+
},
|
|
3552
|
+
{
|
|
3553
|
+
name: "tags",
|
|
3554
|
+
type: "json"
|
|
3555
|
+
},
|
|
3556
|
+
{
|
|
3557
|
+
name: "usage_count",
|
|
3558
|
+
type: "number",
|
|
3559
|
+
default: 0
|
|
3560
|
+
},
|
|
3561
|
+
{
|
|
3562
|
+
name: "favorite_count",
|
|
3563
|
+
type: "number",
|
|
3564
|
+
default: 0
|
|
3565
|
+
}
|
|
3566
|
+
]
|
|
3567
|
+
};
|
|
3437
3568
|
var addCoreFields = (schema) => {
|
|
3438
3569
|
schema.fields.forEach((field) => {
|
|
3439
3570
|
if (field.type === "file") {
|
|
@@ -3483,7 +3614,8 @@ var coreSchemas = {
|
|
|
3483
3614
|
platformConfigurationsSchema: () => addCoreFields(platformConfigurationsSchema),
|
|
3484
3615
|
promptLibrarySchema: () => addCoreFields(promptLibrarySchema),
|
|
3485
3616
|
embedderSettingsSchema: () => addCoreFields(embedderSettingsSchema),
|
|
3486
|
-
promptFavoritesSchema: () => addCoreFields(promptFavoritesSchema)
|
|
3617
|
+
promptFavoritesSchema: () => addCoreFields(promptFavoritesSchema),
|
|
3618
|
+
contextPresetsSchema: () => addCoreFields(contextPresetsSchema)
|
|
3487
3619
|
};
|
|
3488
3620
|
if (license["agent-feedback"]) {
|
|
3489
3621
|
schemas.feedbackSchema = () => addCoreFields(feedbackSchema);
|
|
@@ -4525,7 +4657,7 @@ var ExuluContext2 = class {
|
|
|
4525
4657
|
job: jobs.length > 0 ? jobs.join(",") : void 0
|
|
4526
4658
|
};
|
|
4527
4659
|
};
|
|
4528
|
-
updateItem = async (item, config, user, role, generateEmbeddingsOverwrite) => {
|
|
4660
|
+
updateItem = async (item, config, user, role, generateEmbeddingsOverwrite, runProcessorOverwrite) => {
|
|
4529
4661
|
console.log("[EXULU] updating item", item);
|
|
4530
4662
|
const { db: db2 } = await postgresClient();
|
|
4531
4663
|
if (item.field) {
|
|
@@ -4551,7 +4683,7 @@ var ExuluContext2 = class {
|
|
|
4551
4683
|
let shouldGenerateEmbeddings = this.embedder && generateEmbeddingsOverwrite !== false && (generateEmbeddingsOverwrite || this.configuration.calculateVectors === "onUpdate" || this.configuration.calculateVectors === "always");
|
|
4552
4684
|
if (this.processor) {
|
|
4553
4685
|
const processor = this.processor;
|
|
4554
|
-
if (processor && (processor?.config?.trigger === "onInsert" || processor?.config?.trigger === "onUpdate" || processor?.config?.trigger === "always")) {
|
|
4686
|
+
if (processor && runProcessorOverwrite !== false && (runProcessorOverwrite || processor?.config?.trigger === "onInsert" || processor?.config?.trigger === "onUpdate" || processor?.config?.trigger === "always")) {
|
|
4555
4687
|
const { job: processorJob, result: processorResult } = await this.processField(
|
|
4556
4688
|
"api",
|
|
4557
4689
|
{
|
|
@@ -4817,120 +4949,7 @@ var ExuluContext2 = class {
|
|
|
4817
4949
|
};
|
|
4818
4950
|
};
|
|
4819
4951
|
|
|
4820
|
-
// ee/agentic-retrieval/v3/context-sampler.ts
|
|
4821
|
-
var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
4822
|
-
var ContextSampler = class {
|
|
4823
|
-
cache = /* @__PURE__ */ new Map();
|
|
4824
|
-
async getSamples(contexts, user, role) {
|
|
4825
|
-
return Promise.all(contexts.map((ctx) => this.getSample(ctx, user, role)));
|
|
4826
|
-
}
|
|
4827
|
-
async getSample(ctx, user, role) {
|
|
4828
|
-
const cached = this.cache.get(ctx.id);
|
|
4829
|
-
if (cached && Date.now() - cached.sampledAt < CACHE_TTL_MS) {
|
|
4830
|
-
return cached;
|
|
4831
|
-
}
|
|
4832
|
-
const { db: db2 } = await postgresClient();
|
|
4833
|
-
const tableName = getTableName(ctx.id);
|
|
4834
|
-
const tableDefinition = convertContextToTableDefinition(ctx);
|
|
4835
|
-
const customFieldNames = ctx.fields.map((f) => f.name);
|
|
4836
|
-
const selectFields = ["id", "name", "external_id", ...customFieldNames];
|
|
4837
|
-
let exampleItems = [];
|
|
4838
|
-
try {
|
|
4839
|
-
let query = db2(tableName).select(selectFields).whereNull("archived").limit(2);
|
|
4840
|
-
query = applyAccessControl(tableDefinition, query, user, tableName);
|
|
4841
|
-
exampleItems = await query;
|
|
4842
|
-
} catch {
|
|
4843
|
-
}
|
|
4844
|
-
const sample = {
|
|
4845
|
-
contextId: ctx.id,
|
|
4846
|
-
contextName: ctx.name,
|
|
4847
|
-
fields: ["name", "external_id", ...customFieldNames],
|
|
4848
|
-
exampleItems,
|
|
4849
|
-
sampledAt: Date.now()
|
|
4850
|
-
};
|
|
4851
|
-
this.cache.set(ctx.id, sample);
|
|
4852
|
-
return sample;
|
|
4853
|
-
}
|
|
4854
|
-
/** Evict a context from cache so it's re-sampled on next use */
|
|
4855
|
-
invalidate(contextId) {
|
|
4856
|
-
this.cache.delete(contextId);
|
|
4857
|
-
}
|
|
4858
|
-
};
|
|
4859
|
-
|
|
4860
|
-
// ee/agentic-retrieval/v3/classifier.ts
|
|
4861
|
-
import { generateText, Output } from "ai";
|
|
4862
|
-
import { z as z5 } from "zod";
|
|
4863
|
-
|
|
4864
|
-
// src/utils/with-retry.ts
|
|
4865
|
-
async function withRetry(generateFn, maxRetries = 3) {
|
|
4866
|
-
let lastError;
|
|
4867
|
-
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
4868
|
-
try {
|
|
4869
|
-
return await generateFn();
|
|
4870
|
-
} catch (error) {
|
|
4871
|
-
lastError = error;
|
|
4872
|
-
console.error(`[EXULU] generateText attempt ${attempt} failed:`, error);
|
|
4873
|
-
if (attempt === maxRetries) {
|
|
4874
|
-
throw error;
|
|
4875
|
-
}
|
|
4876
|
-
await new Promise((resolve3) => setTimeout(resolve3, Math.pow(2, attempt) * 1e3));
|
|
4877
|
-
}
|
|
4878
|
-
}
|
|
4879
|
-
throw lastError;
|
|
4880
|
-
}
|
|
4881
|
-
|
|
4882
|
-
// ee/agentic-retrieval/v3/classifier.ts
|
|
4883
|
-
async function classifyQuery(query, contexts, samples, model) {
|
|
4884
|
-
const contextDescriptions = contexts.map((ctx) => {
|
|
4885
|
-
const sample = samples.find((s) => s.contextId === ctx.id);
|
|
4886
|
-
const fieldList = sample?.fields.join(", ") ?? "name, external_id";
|
|
4887
|
-
const exampleStr = sample?.exampleItems.length ? `
|
|
4888
|
-
Example records: ${JSON.stringify(sample.exampleItems.slice(0, 2))}` : "";
|
|
4889
|
-
return ` - ${ctx.id}: ${ctx.name}
|
|
4890
|
-
Description: ${ctx.description}
|
|
4891
|
-
Fields: ${fieldList}${exampleStr}`;
|
|
4892
|
-
}).join("\n\n");
|
|
4893
|
-
const result = await withRetry(async () => {
|
|
4894
|
-
const result2 = await generateText({
|
|
4895
|
-
model,
|
|
4896
|
-
temperature: 0,
|
|
4897
|
-
output: Output.object({
|
|
4898
|
-
schema: z5.object({
|
|
4899
|
-
queryType: z5.enum(["aggregate", "list", "targeted", "exploratory"]).describe(
|
|
4900
|
-
"aggregate: ONLY use when the user explicitly asks to COUNT how many documents/items/tickets exist in the knowledge base (e.g. 'how many documents about X?', 'total number of tickets'). NEVER use for: real-world statistics stored in a document, intent statements, how-to questions, error/fault descriptions, configuration questions, or any query that does not explicitly ask for a count of knowledge base entries. When in doubt, choose targeted. list: user wants to enumerate matching items/documents (show me all, list documents about). targeted: use for almost everything \u2014 specific fact, answer, configuration, how-to, error/fault, feature/behavior question. Also use for intent statements and short commands describing a desired state (phrases that state what the user wants to do or achieve, even without an explicit question word). Real-world statistics stored in documents also go here. When in doubt, choose targeted over aggregate or exploratory. exploratory: only for broad conceptual questions needing multi-source synthesis (what is the process for Z, explain how X works, general overview of topic Y)."
|
|
4901
|
-
),
|
|
4902
|
-
language: z5.string().describe("ISO 639-3 language code of the query (e.g. eng, deu, fra)"),
|
|
4903
|
-
suggestedContextIds: z5.array(z5.enum(contexts.map((c) => c.id))).describe(
|
|
4904
|
-
"IDs of knowledge bases most likely to contain the answer. Return empty array to search all contexts."
|
|
4905
|
-
)
|
|
4906
|
-
})
|
|
4907
|
-
}),
|
|
4908
|
-
toolChoice: "none",
|
|
4909
|
-
system: `You are a query classifier for a multi-knowledge-base retrieval system.
|
|
4910
|
-
Classify the query and identify which knowledge bases are most relevant.
|
|
4911
|
-
|
|
4912
|
-
Available knowledge bases:
|
|
4913
|
-
${contextDescriptions}
|
|
4914
|
-
|
|
4915
|
-
Guidelines for queryType:
|
|
4916
|
-
- Use "aggregate" ONLY when the query contains explicit counting language (e.g., "how many", "count", "total number", "wie viele"). Short statements, commands, or phrases without a question word are NEVER aggregate \u2014 classify them as targeted.
|
|
4917
|
-
- When in doubt between aggregate and targeted: always choose targeted.
|
|
4918
|
-
|
|
4919
|
-
Guidelines for suggestedContextIds:
|
|
4920
|
-
- Be conservative: only suggest contexts that are genuinely likely to contain the answer.
|
|
4921
|
-
Aim for 2\u20133 focused suggestions rather than listing everything.
|
|
4922
|
-
- Use each knowledge base's name and description (shown above) to judge relevance.
|
|
4923
|
-
- Return an empty array only if you truly cannot determine which contexts are relevant.`,
|
|
4924
|
-
prompt: `Query: ${query}`
|
|
4925
|
-
});
|
|
4926
|
-
return result2.output;
|
|
4927
|
-
}, 3);
|
|
4928
|
-
return result;
|
|
4929
|
-
}
|
|
4930
|
-
|
|
4931
4952
|
// ee/agentic-retrieval/v3/tools.ts
|
|
4932
|
-
import { z as z6 } from "zod";
|
|
4933
|
-
import { tool as tool2 } from "ai";
|
|
4934
4953
|
function buildContextEnum(contexts) {
|
|
4935
4954
|
return z6.array(z6.enum(contexts.map((c) => c.id))).describe(
|
|
4936
4955
|
contexts.map(
|
|
@@ -4972,7 +4991,7 @@ function parseGlobalItemIds(globalIds) {
|
|
|
4972
4991
|
return map;
|
|
4973
4992
|
}
|
|
4974
4993
|
function createRetrievalTools(params) {
|
|
4975
|
-
const { contexts, user, role, updateVirtualFiles, preselectedItemsByContext } = params;
|
|
4994
|
+
const { contexts, toolVariablesConfig, user, role, updateVirtualFiles, preselectedItemsByContext } = params;
|
|
4976
4995
|
const ctxEnum = buildContextEnum(contexts);
|
|
4977
4996
|
const count_items_or_chunks = tool2({
|
|
4978
4997
|
description: "Count items or chunks WITHOUT loading them into context. Use for 'how many', 'count', or 'total number of' queries.",
|
|
@@ -5105,7 +5124,7 @@ Use includeContent: true when you need the ACTUAL text to answer a question.
|
|
|
5105
5124
|
|
|
5106
5125
|
For listing queries: always start with includeContent: false, then use dynamic tools to fetch specific pages.`,
|
|
5107
5126
|
inputSchema: z6.object({
|
|
5108
|
-
|
|
5127
|
+
userQuery: z6.string().describe("The original unaltered question from the user"),
|
|
5109
5128
|
knowledge_base_id: z6.enum(contexts.map((c) => c.id)).describe(
|
|
5110
5129
|
contexts.map(
|
|
5111
5130
|
(c) => `<knowledge_base id="${c.id}" name="${c.name}">${c.description}</knowledge_base>`
|
|
@@ -5124,7 +5143,7 @@ For listing queries: always start with includeContent: false, then use dynamic t
|
|
|
5124
5143
|
limit: z6.number().default(20).describe("Max chunks with content (max 20). Without content, up to 200 are returned.")
|
|
5125
5144
|
}),
|
|
5126
5145
|
execute: async ({
|
|
5127
|
-
|
|
5146
|
+
userQuery,
|
|
5128
5147
|
knowledge_base_id,
|
|
5129
5148
|
keywords,
|
|
5130
5149
|
searchMethod,
|
|
@@ -5135,7 +5154,8 @@ For listing queries: always start with includeContent: false, then use dynamic t
|
|
|
5135
5154
|
limit
|
|
5136
5155
|
}) => {
|
|
5137
5156
|
const [ctx] = resolveContexts([knowledge_base_id], contexts);
|
|
5138
|
-
const
|
|
5157
|
+
const maxResults = toolVariablesConfig?.[`${ctx.id}_|_max_results`] || 20;
|
|
5158
|
+
const effectiveLimit = includeContent ? Math.min(limit ?? maxResults, maxResults) : Math.min((limit ?? maxResults) * maxResults, 400);
|
|
5139
5159
|
const itemFilters = [];
|
|
5140
5160
|
if (preselectedItemsByContext) {
|
|
5141
5161
|
const contextItemIds = preselectedItemsByContext.get(knowledge_base_id);
|
|
@@ -5157,7 +5177,7 @@ For listing queries: always start with includeContent: false, then use dynamic t
|
|
|
5157
5177
|
if (item_names)
|
|
5158
5178
|
itemFilters.push({ name: { or: item_names.map((n) => ({ contains: n })) } });
|
|
5159
5179
|
if (item_external_ids) itemFilters.push({ external_id: { in: item_external_ids } });
|
|
5160
|
-
const effectiveQuery =
|
|
5180
|
+
const effectiveQuery = userQuery || keywords?.join(" ") || "";
|
|
5161
5181
|
let method = mapSearchMethod(searchMethod ?? "hybrid");
|
|
5162
5182
|
if (method === "hybridSearch" || method === "cosineDistance") {
|
|
5163
5183
|
if (!ctx.embedder) {
|
|
@@ -5165,6 +5185,7 @@ For listing queries: always start with includeContent: false, then use dynamic t
|
|
|
5165
5185
|
method = "tsvector";
|
|
5166
5186
|
}
|
|
5167
5187
|
}
|
|
5188
|
+
const expandChunks = toolVariablesConfig?.[`${ctx.id}_|_expand_chunks`] || 0;
|
|
5168
5189
|
try {
|
|
5169
5190
|
const { chunks } = await ctx.search({
|
|
5170
5191
|
query: effectiveQuery,
|
|
@@ -5177,7 +5198,11 @@ For listing queries: always start with includeContent: false, then use dynamic t
|
|
|
5177
5198
|
sort: { field: "updatedAt", direction: "desc" },
|
|
5178
5199
|
user,
|
|
5179
5200
|
role,
|
|
5180
|
-
trigger: "tool"
|
|
5201
|
+
trigger: "tool",
|
|
5202
|
+
expand: expandChunks > 0 ? {
|
|
5203
|
+
before: expandChunks,
|
|
5204
|
+
after: expandChunks
|
|
5205
|
+
} : void 0
|
|
5181
5206
|
});
|
|
5182
5207
|
return JSON.stringify(
|
|
5183
5208
|
chunks.map(
|
|
@@ -5949,6 +5974,46 @@ var TrajectoryLogger = class {
|
|
|
5949
5974
|
}
|
|
5950
5975
|
};
|
|
5951
5976
|
|
|
5977
|
+
// ee/agentic-retrieval/v3/context-sampler.ts
|
|
5978
|
+
var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
5979
|
+
var ContextSampler = class {
|
|
5980
|
+
cache = /* @__PURE__ */ new Map();
|
|
5981
|
+
async getSamples(contexts, user, role) {
|
|
5982
|
+
return Promise.all(contexts.map((ctx) => this.getSample(ctx, user, role)));
|
|
5983
|
+
}
|
|
5984
|
+
async getSample(ctx, user, role) {
|
|
5985
|
+
const cached = this.cache.get(ctx.id);
|
|
5986
|
+
if (cached && Date.now() - cached.sampledAt < CACHE_TTL_MS) {
|
|
5987
|
+
return cached;
|
|
5988
|
+
}
|
|
5989
|
+
const { db: db2 } = await postgresClient();
|
|
5990
|
+
const tableName = getTableName(ctx.id);
|
|
5991
|
+
const tableDefinition = convertContextToTableDefinition(ctx);
|
|
5992
|
+
const customFieldNames = ctx.fields.map((f) => f.name);
|
|
5993
|
+
const selectFields = ["id", "name", "external_id", ...customFieldNames];
|
|
5994
|
+
let exampleItems = [];
|
|
5995
|
+
try {
|
|
5996
|
+
let query = db2(tableName).select(selectFields).whereNull("archived").limit(2);
|
|
5997
|
+
query = applyAccessControl(tableDefinition, query, user, tableName);
|
|
5998
|
+
exampleItems = await query;
|
|
5999
|
+
} catch {
|
|
6000
|
+
}
|
|
6001
|
+
const sample = {
|
|
6002
|
+
contextId: ctx.id,
|
|
6003
|
+
contextName: ctx.name,
|
|
6004
|
+
fields: ["name", "external_id", ...customFieldNames],
|
|
6005
|
+
exampleItems,
|
|
6006
|
+
sampledAt: Date.now()
|
|
6007
|
+
};
|
|
6008
|
+
this.cache.set(ctx.id, sample);
|
|
6009
|
+
return sample;
|
|
6010
|
+
}
|
|
6011
|
+
/** Evict a context from cache so it's re-sampled on next use */
|
|
6012
|
+
invalidate(contextId) {
|
|
6013
|
+
this.cache.delete(contextId);
|
|
6014
|
+
}
|
|
6015
|
+
};
|
|
6016
|
+
|
|
5952
6017
|
// ee/agentic-retrieval/v3/index.ts
|
|
5953
6018
|
var sampler = new ContextSampler();
|
|
5954
6019
|
async function* executeV3({
|
|
@@ -5956,6 +6021,7 @@ async function* executeV3({
|
|
|
5956
6021
|
contexts,
|
|
5957
6022
|
reranker,
|
|
5958
6023
|
model,
|
|
6024
|
+
toolVariablesConfig,
|
|
5959
6025
|
user,
|
|
5960
6026
|
role,
|
|
5961
6027
|
customInstructions,
|
|
@@ -5981,15 +6047,39 @@ async function* executeV3({
|
|
|
5981
6047
|
}
|
|
5982
6048
|
console.log("[EXULU] v3 \u2014 classified as:", classification);
|
|
5983
6049
|
const strategy = STRATEGIES[classification.queryType];
|
|
6050
|
+
const contextSpecificInstructions = activeContexts.map((ctx) => {
|
|
6051
|
+
const instructions = toolVariablesConfig?.[`${ctx.id}_|_instructions`] ?? "";
|
|
6052
|
+
if (instructions) {
|
|
6053
|
+
return `
|
|
6054
|
+
<${ctx.id}>
|
|
6055
|
+
${instructions}
|
|
6056
|
+
</${ctx.id}>
|
|
6057
|
+
`;
|
|
6058
|
+
} else {
|
|
6059
|
+
return null;
|
|
6060
|
+
}
|
|
6061
|
+
}).filter(Boolean).join("\n");
|
|
5984
6062
|
const suggestedIds = classification.suggestedContextIds;
|
|
5985
6063
|
const fallbackIds = activeContexts.filter((c) => !suggestedIds.includes(c.id)).map((c) => c.id);
|
|
5986
|
-
|
|
6064
|
+
let contextBase = suggestedIds.length > 0 ? `
|
|
6065
|
+
Suggested priority contexts: [${suggestedIds.join(", ")}].
|
|
6066
|
+
|
|
6067
|
+
Also available: [${fallbackIds.join(", ")}].
|
|
6068
|
+
|
|
6069
|
+
Custom instructions may require searching additional or all contexts \u2014 follow them.` : `All contexts available: [${activeContexts.map((c) => c.id).join(", ")}].`;
|
|
5987
6070
|
const preselectedNote = preselectedByContext?.size ? `
|
|
5988
6071
|
SCOPE CONSTRAINT: Retrieval is scoped to preselected items/contexts. Per context: ${[...preselectedByContext.entries()].map(([ctx, ids]) => ids === null ? `${ctx} (full context)` : `${ctx} (${ids.length} item${ids.length === 1 ? "" : "s"})`).join(", ")}. All tools enforce this scope automatically. For full-context entries you may search freely; for item-restricted entries do NOT use search_items_by_name for discovery \u2014 go directly to search_content or save_search_results.` : "";
|
|
6072
|
+
if (contextSpecificInstructions?.length) {
|
|
6073
|
+
contextBase += `
|
|
6074
|
+
Context specific instructions:
|
|
6075
|
+
${contextSpecificInstructions}
|
|
6076
|
+
`;
|
|
6077
|
+
}
|
|
5989
6078
|
const contextGuidance = contextBase + preselectedNote;
|
|
5990
6079
|
const bashToolkit = await createBashTool({ files: {} });
|
|
5991
6080
|
const retrievalTools = createRetrievalTools({
|
|
5992
6081
|
contexts: activeContexts,
|
|
6082
|
+
toolVariablesConfig,
|
|
5993
6083
|
user,
|
|
5994
6084
|
role,
|
|
5995
6085
|
updateVirtualFiles: (files) => bashToolkit.sandbox.writeFiles(files),
|
|
@@ -6044,7 +6134,8 @@ function createAgenticRetrievalToolV3({
|
|
|
6044
6134
|
user,
|
|
6045
6135
|
role,
|
|
6046
6136
|
model,
|
|
6047
|
-
|
|
6137
|
+
preselected,
|
|
6138
|
+
memoryItems
|
|
6048
6139
|
}) {
|
|
6049
6140
|
const license = checkLicense();
|
|
6050
6141
|
if (!license["agentic-retrieval"]) {
|
|
@@ -6097,27 +6188,57 @@ function createAgenticRetrievalToolV3({
|
|
|
6097
6188
|
default: false
|
|
6098
6189
|
},
|
|
6099
6190
|
{
|
|
6100
|
-
name: "
|
|
6191
|
+
name: "logging",
|
|
6101
6192
|
description: "Save a detailed markdown + JSON log of every retrieval execution to disk. Useful for debugging and evaluation.",
|
|
6102
6193
|
type: "boolean",
|
|
6103
6194
|
default: false
|
|
6104
6195
|
},
|
|
6105
6196
|
...contexts.map((ctx) => ({
|
|
6106
|
-
name: ctx.id,
|
|
6197
|
+
name: ctx.id + "_|_enabled",
|
|
6107
6198
|
description: `Enable search in "${ctx.name}". ${ctx.description}`,
|
|
6108
6199
|
type: "boolean",
|
|
6109
6200
|
default: true
|
|
6201
|
+
})),
|
|
6202
|
+
...contexts.map((ctx) => ({
|
|
6203
|
+
name: `${ctx.id}_|_instructions`,
|
|
6204
|
+
description: `Instructions for the retrieval agent about how to search in the ${ctx.name} context`,
|
|
6205
|
+
type: "string",
|
|
6206
|
+
default: ""
|
|
6207
|
+
})),
|
|
6208
|
+
...contexts.map((ctx) => ({
|
|
6209
|
+
name: `${ctx.id}_|_priority`,
|
|
6210
|
+
description: `Defines in which order the context should be searched in, the higher the number the higher the priority, if contexts have the same priority they are searched in parallel`,
|
|
6211
|
+
type: "number",
|
|
6212
|
+
default: 0
|
|
6213
|
+
})),
|
|
6214
|
+
...contexts.map((ctx) => ({
|
|
6215
|
+
name: `${ctx.id}_|_max_results`,
|
|
6216
|
+
description: `Defines the maximum number of results to return for the ${ctx.name} context`,
|
|
6217
|
+
type: "number",
|
|
6218
|
+
default: 0
|
|
6219
|
+
})),
|
|
6220
|
+
...contexts.map((ctx) => ({
|
|
6221
|
+
name: `${ctx.id}_|_max_steps`,
|
|
6222
|
+
description: `Defines the maximum number of steps the agent is allowed to take when searching the ${ctx.name} context`,
|
|
6223
|
+
type: "number",
|
|
6224
|
+
default: 0
|
|
6225
|
+
})),
|
|
6226
|
+
...contexts.map((ctx) => ({
|
|
6227
|
+
name: `${ctx.id}_|_expand_chunks`,
|
|
6228
|
+
description: `Defines if the agent automatically retrieves nearby chunks around the matched chunks, usefull if relevant content might be split up`,
|
|
6229
|
+
type: "number",
|
|
6230
|
+
default: 0
|
|
6110
6231
|
}))
|
|
6111
6232
|
],
|
|
6112
6233
|
inputSchema: z9.object({
|
|
6113
|
-
|
|
6234
|
+
userQuery: z9.string().describe("The original unaltered question from the user"),
|
|
6114
6235
|
userInstructions: z9.string().optional().describe("Additional instructions from the user to guide retrieval"),
|
|
6115
6236
|
confirmedContextIds: z9.array(z9.string()).optional().describe(
|
|
6116
6237
|
"Knowledge base IDs explicitly confirmed by the user to be used in the retrieval. When presen only searches these contexts. "
|
|
6117
6238
|
)
|
|
6118
6239
|
}),
|
|
6119
6240
|
execute: async function* ({
|
|
6120
|
-
|
|
6241
|
+
userQuery,
|
|
6121
6242
|
userInstructions,
|
|
6122
6243
|
confirmedContextIds,
|
|
6123
6244
|
toolVariablesConfig,
|
|
@@ -6135,10 +6256,10 @@ function createAgenticRetrievalToolV3({
|
|
|
6135
6256
|
let managedContextEnabled = false;
|
|
6136
6257
|
if (toolVariablesConfig) {
|
|
6137
6258
|
configInstructions = toolVariablesConfig["instructions"] ?? "";
|
|
6138
|
-
logTrajectory = toolVariablesConfig["
|
|
6259
|
+
logTrajectory = toolVariablesConfig["logging"] === true || toolVariablesConfig["logging"] === "true";
|
|
6139
6260
|
managedContextEnabled = toolVariablesConfig["managed_context"] === true || toolVariablesConfig["managed_context"] === "true";
|
|
6140
6261
|
activeContexts = contexts.filter(
|
|
6141
|
-
(ctx) => toolVariablesConfig[ctx.id] === true || toolVariablesConfig[ctx.id] === "true" || toolVariablesConfig[ctx.id] === 1
|
|
6262
|
+
(ctx) => toolVariablesConfig[ctx.id + "_|_enabled"] === true || toolVariablesConfig[ctx.id + "_|_enabled"] === "true" || toolVariablesConfig[ctx.id + "_|_enabled"] === 1
|
|
6142
6263
|
);
|
|
6143
6264
|
if (activeContexts.length === 0) activeContexts = contexts;
|
|
6144
6265
|
requiresPreselectedContexts = toolVariablesConfig["require_preselected_contexts"] === true || toolVariablesConfig["require_preselected_contexts"] === "true";
|
|
@@ -6148,13 +6269,13 @@ function createAgenticRetrievalToolV3({
|
|
|
6148
6269
|
}
|
|
6149
6270
|
}
|
|
6150
6271
|
console.log("[EXULU] Managed context enabled:", managedContextEnabled);
|
|
6151
|
-
console.log("[EXULU] Preselected item IDs:",
|
|
6152
|
-
if (managedContextEnabled && !
|
|
6272
|
+
console.log("[EXULU] Preselected item IDs:", preselected);
|
|
6273
|
+
if (managedContextEnabled && !preselected?.length) {
|
|
6153
6274
|
console.log("[EXULU] Managed context was enabled for the agentic retrieval tool. This means that the user must preselect items that the agentic retrieval tool will search in, please notify the user to preselect items before executing the tool.");
|
|
6154
6275
|
yield { result: "Managed context was enabled for the agentic retrieval tool. This means that the user must preselect items that the agentic retrieval tool will search in, please notify the user to preselect items before executing the tool." };
|
|
6155
6276
|
return;
|
|
6156
6277
|
}
|
|
6157
|
-
if (requiresPreselectedContexts && !confirmedContextIds?.length && !
|
|
6278
|
+
if (requiresPreselectedContexts && !confirmedContextIds?.length && !preselected?.length) {
|
|
6158
6279
|
console.log("[EXULU] The user must choose between the available contexts before executing the tool. The available contexts are: " + activeContexts.map((c) => c.id).join(", ") + ". If the question_ask tool is available use that to ask the user which contexts they want to search in, otherwise just ask them in plain text.");
|
|
6159
6280
|
yield { result: "The user must choose between the available contexts before executing the tool, the available contexts are: " + activeContexts.map((c) => c.id).join(", ") + ". If the question_ask tool is available use that to ask the user which contexts they want to search in, otherwise just ask them in plain text." };
|
|
6160
6281
|
return;
|
|
@@ -6165,21 +6286,43 @@ function createAgenticRetrievalToolV3({
|
|
|
6165
6286
|
if (filtered.length > 0) activeContexts = filtered;
|
|
6166
6287
|
}
|
|
6167
6288
|
const combinedInstructions = [
|
|
6168
|
-
configInstructions ? `
|
|
6169
|
-
|
|
6170
|
-
|
|
6289
|
+
configInstructions ? `
|
|
6290
|
+
Configuration instructions:
|
|
6291
|
+
<configuration_instructions>
|
|
6292
|
+
${configInstructions}
|
|
6293
|
+
</configuration_instructions>
|
|
6294
|
+
` : "",
|
|
6295
|
+
adminInstructions ? `
|
|
6296
|
+
Admin instructions:
|
|
6297
|
+
<admin_instructions>
|
|
6298
|
+
${adminInstructions}
|
|
6299
|
+
</admin_instructions>
|
|
6300
|
+
` : "",
|
|
6301
|
+
userInstructions ? `
|
|
6302
|
+
User instructions:
|
|
6303
|
+
<user_instructions>
|
|
6304
|
+
${userInstructions}
|
|
6305
|
+
</user_instructions>
|
|
6306
|
+
` : "",
|
|
6307
|
+
memoryItems ? `
|
|
6308
|
+
Relevant memories (these are items that the agent has retrieved from the memory context and are relevant to the query):
|
|
6309
|
+
<relevant_memories>
|
|
6310
|
+
${memoryItems?.map((item) => JSON.stringify(item)).join("\n")}
|
|
6311
|
+
</relevant_memories>
|
|
6312
|
+
` : ""
|
|
6171
6313
|
].filter(Boolean).join("\n");
|
|
6172
6314
|
for await (const output of executeV3({
|
|
6173
|
-
query,
|
|
6315
|
+
query: userQuery,
|
|
6174
6316
|
contexts: activeContexts,
|
|
6175
6317
|
reranker: configuredReranker,
|
|
6318
|
+
toolVariablesConfig,
|
|
6176
6319
|
model,
|
|
6177
6320
|
user,
|
|
6178
6321
|
role,
|
|
6179
6322
|
customInstructions: combinedInstructions || void 0,
|
|
6180
6323
|
logTrajectory,
|
|
6181
6324
|
sessionId: sessionID,
|
|
6182
|
-
preselectedItemIds
|
|
6325
|
+
preselectedItemIds: preselected
|
|
6183
6326
|
})) {
|
|
6184
6327
|
yield { result: JSON.stringify(output) };
|
|
6185
6328
|
}
|
|
@@ -11006,6 +11149,7 @@ var ExuluProvider = class {
|
|
|
11006
11149
|
});
|
|
11007
11150
|
}
|
|
11008
11151
|
let memoryContext = "";
|
|
11152
|
+
let memoryItems;
|
|
11009
11153
|
if (agent?.memory && contexts?.length && query) {
|
|
11010
11154
|
const context = contexts.find((context2) => context2.id === agent?.memory);
|
|
11011
11155
|
if (!context) {
|
|
@@ -11028,6 +11172,7 @@ var ExuluProvider = class {
|
|
|
11028
11172
|
page: 1
|
|
11029
11173
|
});
|
|
11030
11174
|
if (result?.chunks?.length) {
|
|
11175
|
+
memoryItems = result.chunks;
|
|
11031
11176
|
memoryContext = `
|
|
11032
11177
|
Pre-fetched relevant information for this query:
|
|
11033
11178
|
|
|
@@ -11148,7 +11293,8 @@ When a tool execution is not approved by the user, do not retry it unless explic
|
|
|
11148
11293
|
project,
|
|
11149
11294
|
sessionItems,
|
|
11150
11295
|
model,
|
|
11151
|
-
agent
|
|
11296
|
+
agent,
|
|
11297
|
+
memoryItems
|
|
11152
11298
|
),
|
|
11153
11299
|
stopWhen: [stepCountIs2(maxStepCount || 5)]
|
|
11154
11300
|
// make configurable
|
|
@@ -11225,7 +11371,8 @@ When a tool execution is not approved by the user, do not retry it unless explic
|
|
|
11225
11371
|
project,
|
|
11226
11372
|
sessionItems,
|
|
11227
11373
|
model,
|
|
11228
|
-
agent
|
|
11374
|
+
agent,
|
|
11375
|
+
memoryItems
|
|
11229
11376
|
),
|
|
11230
11377
|
stopWhen: [stepCountIs2(maxStepCount || 5)]
|
|
11231
11378
|
});
|
|
@@ -11407,6 +11554,7 @@ ${extractedText}
|
|
|
11407
11554
|
});
|
|
11408
11555
|
}
|
|
11409
11556
|
let memoryContext = "";
|
|
11557
|
+
let memoryItems;
|
|
11410
11558
|
if (agent?.memory && contexts?.length && query) {
|
|
11411
11559
|
const context = contexts.find((context2) => context2.id === agent?.memory);
|
|
11412
11560
|
if (!context) {
|
|
@@ -11430,6 +11578,7 @@ ${extractedText}
|
|
|
11430
11578
|
});
|
|
11431
11579
|
fs2.writeFileSync("pre-fetched-relevant-information.json", JSON.stringify(result2, null, 2));
|
|
11432
11580
|
if (result2?.chunks?.length) {
|
|
11581
|
+
memoryItems = result2.chunks;
|
|
11433
11582
|
memoryContext = `
|
|
11434
11583
|
<pre-fetched relevant information for this query>:
|
|
11435
11584
|
|
|
@@ -11449,9 +11598,6 @@ ${extractedText}
|
|
|
11449
11598
|
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.";
|
|
11450
11599
|
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.";
|
|
11451
11600
|
system += "\n\n" + genericContext;
|
|
11452
|
-
if (memoryContext) {
|
|
11453
|
-
system += "\n\n" + memoryContext;
|
|
11454
|
-
}
|
|
11455
11601
|
const includesContextSearchTool = currentTools?.some(
|
|
11456
11602
|
(tool5) => tool5.name.toLowerCase().includes("context_search") || tool5.id.includes("context_search") || tool5.type === "context"
|
|
11457
11603
|
);
|
|
@@ -11536,7 +11682,8 @@ When a tool execution is not approved by the user, do not retry it unless explic
|
|
|
11536
11682
|
project,
|
|
11537
11683
|
sessionItems,
|
|
11538
11684
|
model,
|
|
11539
|
-
agent
|
|
11685
|
+
agent,
|
|
11686
|
+
memoryItems
|
|
11540
11687
|
),
|
|
11541
11688
|
onError: (error) => {
|
|
11542
11689
|
console.error("[EXULU] chat stream error.", error);
|
|
@@ -12133,6 +12280,7 @@ var {
|
|
|
12133
12280
|
workflowTemplatesSchema: workflowTemplatesSchema2,
|
|
12134
12281
|
rbacSchema: rbacSchema2,
|
|
12135
12282
|
promptLibrarySchema: promptLibrarySchema2,
|
|
12283
|
+
contextPresetsSchema: contextPresetsSchema2,
|
|
12136
12284
|
embedderSettingsSchema: embedderSettingsSchema2,
|
|
12137
12285
|
promptFavoritesSchema: promptFavoritesSchema2,
|
|
12138
12286
|
statisticsSchema: statisticsSchema2
|
|
@@ -12180,6 +12328,7 @@ var createExpressRoutes = async (app, providers, tools, contexts, config, evals,
|
|
|
12180
12328
|
projectsSchema2(),
|
|
12181
12329
|
jobResultsSchema2(),
|
|
12182
12330
|
promptLibrarySchema2(),
|
|
12331
|
+
contextPresetsSchema2(),
|
|
12183
12332
|
embedderSettingsSchema2(),
|
|
12184
12333
|
promptFavoritesSchema2(),
|
|
12185
12334
|
evalRunsSchema2(),
|
|
@@ -17246,6 +17395,7 @@ var {
|
|
|
17246
17395
|
projectsSchema: projectsSchema3,
|
|
17247
17396
|
jobResultsSchema: jobResultsSchema3,
|
|
17248
17397
|
promptLibrarySchema: promptLibrarySchema3,
|
|
17398
|
+
contextPresetsSchema: contextPresetsSchema3,
|
|
17249
17399
|
embedderSettingsSchema: embedderSettingsSchema3,
|
|
17250
17400
|
promptFavoritesSchema: promptFavoritesSchema3
|
|
17251
17401
|
} = coreSchemas.get();
|
|
@@ -17283,6 +17433,7 @@ var up = async function(knex) {
|
|
|
17283
17433
|
projectsSchema3(),
|
|
17284
17434
|
jobResultsSchema3(),
|
|
17285
17435
|
promptLibrarySchema3(),
|
|
17436
|
+
contextPresetsSchema3(),
|
|
17286
17437
|
embedderSettingsSchema3(),
|
|
17287
17438
|
promptFavoritesSchema3(),
|
|
17288
17439
|
rbacSchema3(),
|