@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.cjs
CHANGED
|
@@ -1253,7 +1253,7 @@ var hydrateVariables = async (tool5) => {
|
|
|
1253
1253
|
await Promise.all(promises2);
|
|
1254
1254
|
return tool5;
|
|
1255
1255
|
};
|
|
1256
|
-
var convertExuluToolsToAiSdkTools = async (currentTools, approvedTools, allExuluTools, configs, providerapikey, contexts, rerankers, user, exuluConfig, sessionID, req, project, sessionItems, model, agent) => {
|
|
1256
|
+
var convertExuluToolsToAiSdkTools = async (currentTools, approvedTools, allExuluTools, configs, providerapikey, contexts, rerankers, user, exuluConfig, sessionID, req, project, sessionItems, model, agent, memoryItems) => {
|
|
1257
1257
|
if (!currentTools) return {};
|
|
1258
1258
|
if (!allExuluTools) {
|
|
1259
1259
|
allExuluTools = [];
|
|
@@ -1309,7 +1309,8 @@ var convertExuluToolsToAiSdkTools = async (currentTools, approvedTools, allExulu
|
|
|
1309
1309
|
user,
|
|
1310
1310
|
role: user?.role?.id,
|
|
1311
1311
|
model,
|
|
1312
|
-
|
|
1312
|
+
preselected: sessionItems,
|
|
1313
|
+
memoryItems
|
|
1313
1314
|
});
|
|
1314
1315
|
if (agenticSearchTool) {
|
|
1315
1316
|
const index = currentTools.findIndex((tool5) => tool5.id === "agentic_context_search");
|
|
@@ -1443,6 +1444,8 @@ var convertExuluToolsToAiSdkTools = async (currentTools, approvedTools, allExulu
|
|
|
1443
1444
|
...inputs,
|
|
1444
1445
|
model,
|
|
1445
1446
|
sessionID,
|
|
1447
|
+
sessionItems,
|
|
1448
|
+
memory: memoryItems,
|
|
1446
1449
|
req,
|
|
1447
1450
|
// Convert config to object format if a config object
|
|
1448
1451
|
// is available, after we added the .value property
|
|
@@ -1630,6 +1633,95 @@ var ExuluTool = class {
|
|
|
1630
1633
|
};
|
|
1631
1634
|
};
|
|
1632
1635
|
|
|
1636
|
+
// ee/agentic-retrieval/v3/classifier.ts
|
|
1637
|
+
var import_ai2 = require("ai");
|
|
1638
|
+
var import_zod5 = require("zod");
|
|
1639
|
+
|
|
1640
|
+
// src/utils/with-retry.ts
|
|
1641
|
+
async function withRetry(generateFn, maxRetries = 3) {
|
|
1642
|
+
let lastError;
|
|
1643
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
1644
|
+
try {
|
|
1645
|
+
return await generateFn();
|
|
1646
|
+
} catch (error) {
|
|
1647
|
+
lastError = error;
|
|
1648
|
+
console.error(`[EXULU] generateText attempt ${attempt} failed:`, error);
|
|
1649
|
+
if (attempt === maxRetries) {
|
|
1650
|
+
throw error;
|
|
1651
|
+
}
|
|
1652
|
+
await new Promise((resolve3) => setTimeout(resolve3, Math.pow(2, attempt) * 1e3));
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
throw lastError;
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
// ee/agentic-retrieval/v3/classifier.ts
|
|
1659
|
+
async function classifyQuery(query, contexts, samples, model) {
|
|
1660
|
+
const contextDescriptions = contexts.map((ctx) => {
|
|
1661
|
+
const sample = samples.find((s) => s.contextId === ctx.id);
|
|
1662
|
+
const fieldList = sample?.fields.join(", ") ?? "name, external_id";
|
|
1663
|
+
return `
|
|
1664
|
+
<context>
|
|
1665
|
+
<id>
|
|
1666
|
+
${ctx.id}
|
|
1667
|
+
</id>
|
|
1668
|
+
<name>
|
|
1669
|
+
${ctx.name}
|
|
1670
|
+
</name>
|
|
1671
|
+
<description>
|
|
1672
|
+
${ctx.description}
|
|
1673
|
+
</description>
|
|
1674
|
+
<fields>
|
|
1675
|
+
${fieldList}
|
|
1676
|
+
</fields>
|
|
1677
|
+
<example_items>
|
|
1678
|
+
${sample?.exampleItems.map((item) => JSON.stringify(item)).join("\n")}
|
|
1679
|
+
</example_items>
|
|
1680
|
+
</context>
|
|
1681
|
+
`;
|
|
1682
|
+
}).join("\n\n");
|
|
1683
|
+
const result = await withRetry(async () => {
|
|
1684
|
+
const result2 = await (0, import_ai2.generateText)({
|
|
1685
|
+
model,
|
|
1686
|
+
temperature: 0,
|
|
1687
|
+
output: import_ai2.Output.object({
|
|
1688
|
+
schema: import_zod5.z.object({
|
|
1689
|
+
queryType: import_zod5.z.enum(["aggregate", "list", "targeted", "exploratory"]).describe(
|
|
1690
|
+
"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)."
|
|
1691
|
+
),
|
|
1692
|
+
language: import_zod5.z.string().describe("ISO 639-3 language code of the query (e.g. eng, deu, fra)"),
|
|
1693
|
+
suggestedContextIds: import_zod5.z.array(import_zod5.z.enum(contexts.map((c) => c.id))).describe(
|
|
1694
|
+
"IDs of knowledge bases most likely to contain the answer. Return empty array to search all contexts."
|
|
1695
|
+
)
|
|
1696
|
+
})
|
|
1697
|
+
}),
|
|
1698
|
+
toolChoice: "none",
|
|
1699
|
+
system: `You are a query classifier for a multi-knowledge-base retrieval system.
|
|
1700
|
+
Classify the query and identify which knowledge bases are most relevant.
|
|
1701
|
+
|
|
1702
|
+
Available knowledge bases:
|
|
1703
|
+
${contextDescriptions}
|
|
1704
|
+
|
|
1705
|
+
Guidelines for queryType:
|
|
1706
|
+
- 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.
|
|
1707
|
+
- When in doubt between aggregate and targeted: always choose targeted.
|
|
1708
|
+
|
|
1709
|
+
Guidelines for suggestedContextIds:
|
|
1710
|
+
- Be conservative: only suggest contexts that are genuinely likely to contain the answer.
|
|
1711
|
+
Aim for 2\u20133 focused suggestions rather than listing everything.
|
|
1712
|
+
- Use each knowledge base's name and description (shown above) to judge relevance.
|
|
1713
|
+
- Return an empty array only if you truly cannot determine which contexts are relevant.`,
|
|
1714
|
+
prompt: `Query: ${query}`
|
|
1715
|
+
});
|
|
1716
|
+
return result2.output;
|
|
1717
|
+
}, 3);
|
|
1718
|
+
return result;
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1721
|
+
// ee/agentic-retrieval/v3/tools.ts
|
|
1722
|
+
var import_zod6 = require("zod");
|
|
1723
|
+
var import_ai3 = require("ai");
|
|
1724
|
+
|
|
1633
1725
|
// src/uppy/index.ts
|
|
1634
1726
|
var import_express = require("express");
|
|
1635
1727
|
var import_client_s32 = require("@aws-sdk/client-s3");
|
|
@@ -3481,6 +3573,45 @@ var promptFavoritesSchema = {
|
|
|
3481
3573
|
}
|
|
3482
3574
|
]
|
|
3483
3575
|
};
|
|
3576
|
+
var contextPresetsSchema = {
|
|
3577
|
+
type: "context_presets",
|
|
3578
|
+
name: {
|
|
3579
|
+
plural: "context_presets",
|
|
3580
|
+
singular: "context_preset"
|
|
3581
|
+
},
|
|
3582
|
+
RBAC: true,
|
|
3583
|
+
fields: [
|
|
3584
|
+
{
|
|
3585
|
+
name: "name",
|
|
3586
|
+
type: "text",
|
|
3587
|
+
required: true,
|
|
3588
|
+
index: true
|
|
3589
|
+
},
|
|
3590
|
+
{
|
|
3591
|
+
name: "description",
|
|
3592
|
+
type: "text"
|
|
3593
|
+
},
|
|
3594
|
+
{
|
|
3595
|
+
name: "preset_items",
|
|
3596
|
+
type: "json",
|
|
3597
|
+
required: true
|
|
3598
|
+
},
|
|
3599
|
+
{
|
|
3600
|
+
name: "tags",
|
|
3601
|
+
type: "json"
|
|
3602
|
+
},
|
|
3603
|
+
{
|
|
3604
|
+
name: "usage_count",
|
|
3605
|
+
type: "number",
|
|
3606
|
+
default: 0
|
|
3607
|
+
},
|
|
3608
|
+
{
|
|
3609
|
+
name: "favorite_count",
|
|
3610
|
+
type: "number",
|
|
3611
|
+
default: 0
|
|
3612
|
+
}
|
|
3613
|
+
]
|
|
3614
|
+
};
|
|
3484
3615
|
var addCoreFields = (schema) => {
|
|
3485
3616
|
schema.fields.forEach((field) => {
|
|
3486
3617
|
if (field.type === "file") {
|
|
@@ -3530,7 +3661,8 @@ var coreSchemas = {
|
|
|
3530
3661
|
platformConfigurationsSchema: () => addCoreFields(platformConfigurationsSchema),
|
|
3531
3662
|
promptLibrarySchema: () => addCoreFields(promptLibrarySchema),
|
|
3532
3663
|
embedderSettingsSchema: () => addCoreFields(embedderSettingsSchema),
|
|
3533
|
-
promptFavoritesSchema: () => addCoreFields(promptFavoritesSchema)
|
|
3664
|
+
promptFavoritesSchema: () => addCoreFields(promptFavoritesSchema),
|
|
3665
|
+
contextPresetsSchema: () => addCoreFields(contextPresetsSchema)
|
|
3534
3666
|
};
|
|
3535
3667
|
if (license["agent-feedback"]) {
|
|
3536
3668
|
schemas.feedbackSchema = () => addCoreFields(feedbackSchema);
|
|
@@ -4572,7 +4704,7 @@ var ExuluContext2 = class {
|
|
|
4572
4704
|
job: jobs.length > 0 ? jobs.join(",") : void 0
|
|
4573
4705
|
};
|
|
4574
4706
|
};
|
|
4575
|
-
updateItem = async (item, config, user, role, generateEmbeddingsOverwrite) => {
|
|
4707
|
+
updateItem = async (item, config, user, role, generateEmbeddingsOverwrite, runProcessorOverwrite) => {
|
|
4576
4708
|
console.log("[EXULU] updating item", item);
|
|
4577
4709
|
const { db: db2 } = await postgresClient();
|
|
4578
4710
|
if (item.field) {
|
|
@@ -4598,7 +4730,7 @@ var ExuluContext2 = class {
|
|
|
4598
4730
|
let shouldGenerateEmbeddings = this.embedder && generateEmbeddingsOverwrite !== false && (generateEmbeddingsOverwrite || this.configuration.calculateVectors === "onUpdate" || this.configuration.calculateVectors === "always");
|
|
4599
4731
|
if (this.processor) {
|
|
4600
4732
|
const processor = this.processor;
|
|
4601
|
-
if (processor && (processor?.config?.trigger === "onInsert" || processor?.config?.trigger === "onUpdate" || processor?.config?.trigger === "always")) {
|
|
4733
|
+
if (processor && runProcessorOverwrite !== false && (runProcessorOverwrite || processor?.config?.trigger === "onInsert" || processor?.config?.trigger === "onUpdate" || processor?.config?.trigger === "always")) {
|
|
4602
4734
|
const { job: processorJob, result: processorResult } = await this.processField(
|
|
4603
4735
|
"api",
|
|
4604
4736
|
{
|
|
@@ -4864,120 +4996,7 @@ var ExuluContext2 = class {
|
|
|
4864
4996
|
};
|
|
4865
4997
|
};
|
|
4866
4998
|
|
|
4867
|
-
// ee/agentic-retrieval/v3/context-sampler.ts
|
|
4868
|
-
var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
4869
|
-
var ContextSampler = class {
|
|
4870
|
-
cache = /* @__PURE__ */ new Map();
|
|
4871
|
-
async getSamples(contexts, user, role) {
|
|
4872
|
-
return Promise.all(contexts.map((ctx) => this.getSample(ctx, user, role)));
|
|
4873
|
-
}
|
|
4874
|
-
async getSample(ctx, user, role) {
|
|
4875
|
-
const cached = this.cache.get(ctx.id);
|
|
4876
|
-
if (cached && Date.now() - cached.sampledAt < CACHE_TTL_MS) {
|
|
4877
|
-
return cached;
|
|
4878
|
-
}
|
|
4879
|
-
const { db: db2 } = await postgresClient();
|
|
4880
|
-
const tableName = getTableName(ctx.id);
|
|
4881
|
-
const tableDefinition = convertContextToTableDefinition(ctx);
|
|
4882
|
-
const customFieldNames = ctx.fields.map((f) => f.name);
|
|
4883
|
-
const selectFields = ["id", "name", "external_id", ...customFieldNames];
|
|
4884
|
-
let exampleItems = [];
|
|
4885
|
-
try {
|
|
4886
|
-
let query = db2(tableName).select(selectFields).whereNull("archived").limit(2);
|
|
4887
|
-
query = applyAccessControl(tableDefinition, query, user, tableName);
|
|
4888
|
-
exampleItems = await query;
|
|
4889
|
-
} catch {
|
|
4890
|
-
}
|
|
4891
|
-
const sample = {
|
|
4892
|
-
contextId: ctx.id,
|
|
4893
|
-
contextName: ctx.name,
|
|
4894
|
-
fields: ["name", "external_id", ...customFieldNames],
|
|
4895
|
-
exampleItems,
|
|
4896
|
-
sampledAt: Date.now()
|
|
4897
|
-
};
|
|
4898
|
-
this.cache.set(ctx.id, sample);
|
|
4899
|
-
return sample;
|
|
4900
|
-
}
|
|
4901
|
-
/** Evict a context from cache so it's re-sampled on next use */
|
|
4902
|
-
invalidate(contextId) {
|
|
4903
|
-
this.cache.delete(contextId);
|
|
4904
|
-
}
|
|
4905
|
-
};
|
|
4906
|
-
|
|
4907
|
-
// ee/agentic-retrieval/v3/classifier.ts
|
|
4908
|
-
var import_ai2 = require("ai");
|
|
4909
|
-
var import_zod5 = require("zod");
|
|
4910
|
-
|
|
4911
|
-
// src/utils/with-retry.ts
|
|
4912
|
-
async function withRetry(generateFn, maxRetries = 3) {
|
|
4913
|
-
let lastError;
|
|
4914
|
-
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
4915
|
-
try {
|
|
4916
|
-
return await generateFn();
|
|
4917
|
-
} catch (error) {
|
|
4918
|
-
lastError = error;
|
|
4919
|
-
console.error(`[EXULU] generateText attempt ${attempt} failed:`, error);
|
|
4920
|
-
if (attempt === maxRetries) {
|
|
4921
|
-
throw error;
|
|
4922
|
-
}
|
|
4923
|
-
await new Promise((resolve3) => setTimeout(resolve3, Math.pow(2, attempt) * 1e3));
|
|
4924
|
-
}
|
|
4925
|
-
}
|
|
4926
|
-
throw lastError;
|
|
4927
|
-
}
|
|
4928
|
-
|
|
4929
|
-
// ee/agentic-retrieval/v3/classifier.ts
|
|
4930
|
-
async function classifyQuery(query, contexts, samples, model) {
|
|
4931
|
-
const contextDescriptions = contexts.map((ctx) => {
|
|
4932
|
-
const sample = samples.find((s) => s.contextId === ctx.id);
|
|
4933
|
-
const fieldList = sample?.fields.join(", ") ?? "name, external_id";
|
|
4934
|
-
const exampleStr = sample?.exampleItems.length ? `
|
|
4935
|
-
Example records: ${JSON.stringify(sample.exampleItems.slice(0, 2))}` : "";
|
|
4936
|
-
return ` - ${ctx.id}: ${ctx.name}
|
|
4937
|
-
Description: ${ctx.description}
|
|
4938
|
-
Fields: ${fieldList}${exampleStr}`;
|
|
4939
|
-
}).join("\n\n");
|
|
4940
|
-
const result = await withRetry(async () => {
|
|
4941
|
-
const result2 = await (0, import_ai2.generateText)({
|
|
4942
|
-
model,
|
|
4943
|
-
temperature: 0,
|
|
4944
|
-
output: import_ai2.Output.object({
|
|
4945
|
-
schema: import_zod5.z.object({
|
|
4946
|
-
queryType: import_zod5.z.enum(["aggregate", "list", "targeted", "exploratory"]).describe(
|
|
4947
|
-
"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)."
|
|
4948
|
-
),
|
|
4949
|
-
language: import_zod5.z.string().describe("ISO 639-3 language code of the query (e.g. eng, deu, fra)"),
|
|
4950
|
-
suggestedContextIds: import_zod5.z.array(import_zod5.z.enum(contexts.map((c) => c.id))).describe(
|
|
4951
|
-
"IDs of knowledge bases most likely to contain the answer. Return empty array to search all contexts."
|
|
4952
|
-
)
|
|
4953
|
-
})
|
|
4954
|
-
}),
|
|
4955
|
-
toolChoice: "none",
|
|
4956
|
-
system: `You are a query classifier for a multi-knowledge-base retrieval system.
|
|
4957
|
-
Classify the query and identify which knowledge bases are most relevant.
|
|
4958
|
-
|
|
4959
|
-
Available knowledge bases:
|
|
4960
|
-
${contextDescriptions}
|
|
4961
|
-
|
|
4962
|
-
Guidelines for queryType:
|
|
4963
|
-
- 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.
|
|
4964
|
-
- When in doubt between aggregate and targeted: always choose targeted.
|
|
4965
|
-
|
|
4966
|
-
Guidelines for suggestedContextIds:
|
|
4967
|
-
- Be conservative: only suggest contexts that are genuinely likely to contain the answer.
|
|
4968
|
-
Aim for 2\u20133 focused suggestions rather than listing everything.
|
|
4969
|
-
- Use each knowledge base's name and description (shown above) to judge relevance.
|
|
4970
|
-
- Return an empty array only if you truly cannot determine which contexts are relevant.`,
|
|
4971
|
-
prompt: `Query: ${query}`
|
|
4972
|
-
});
|
|
4973
|
-
return result2.output;
|
|
4974
|
-
}, 3);
|
|
4975
|
-
return result;
|
|
4976
|
-
}
|
|
4977
|
-
|
|
4978
4999
|
// ee/agentic-retrieval/v3/tools.ts
|
|
4979
|
-
var import_zod6 = require("zod");
|
|
4980
|
-
var import_ai3 = require("ai");
|
|
4981
5000
|
function buildContextEnum(contexts) {
|
|
4982
5001
|
return import_zod6.z.array(import_zod6.z.enum(contexts.map((c) => c.id))).describe(
|
|
4983
5002
|
contexts.map(
|
|
@@ -5019,7 +5038,7 @@ function parseGlobalItemIds(globalIds) {
|
|
|
5019
5038
|
return map;
|
|
5020
5039
|
}
|
|
5021
5040
|
function createRetrievalTools(params) {
|
|
5022
|
-
const { contexts, user, role, updateVirtualFiles, preselectedItemsByContext } = params;
|
|
5041
|
+
const { contexts, toolVariablesConfig, user, role, updateVirtualFiles, preselectedItemsByContext } = params;
|
|
5023
5042
|
const ctxEnum = buildContextEnum(contexts);
|
|
5024
5043
|
const count_items_or_chunks = (0, import_ai3.tool)({
|
|
5025
5044
|
description: "Count items or chunks WITHOUT loading them into context. Use for 'how many', 'count', or 'total number of' queries.",
|
|
@@ -5152,7 +5171,7 @@ Use includeContent: true when you need the ACTUAL text to answer a question.
|
|
|
5152
5171
|
|
|
5153
5172
|
For listing queries: always start with includeContent: false, then use dynamic tools to fetch specific pages.`,
|
|
5154
5173
|
inputSchema: import_zod6.z.object({
|
|
5155
|
-
|
|
5174
|
+
userQuery: import_zod6.z.string().describe("The original unaltered question from the user"),
|
|
5156
5175
|
knowledge_base_id: import_zod6.z.enum(contexts.map((c) => c.id)).describe(
|
|
5157
5176
|
contexts.map(
|
|
5158
5177
|
(c) => `<knowledge_base id="${c.id}" name="${c.name}">${c.description}</knowledge_base>`
|
|
@@ -5171,7 +5190,7 @@ For listing queries: always start with includeContent: false, then use dynamic t
|
|
|
5171
5190
|
limit: import_zod6.z.number().default(20).describe("Max chunks with content (max 20). Without content, up to 200 are returned.")
|
|
5172
5191
|
}),
|
|
5173
5192
|
execute: async ({
|
|
5174
|
-
|
|
5193
|
+
userQuery,
|
|
5175
5194
|
knowledge_base_id,
|
|
5176
5195
|
keywords,
|
|
5177
5196
|
searchMethod,
|
|
@@ -5182,7 +5201,8 @@ For listing queries: always start with includeContent: false, then use dynamic t
|
|
|
5182
5201
|
limit
|
|
5183
5202
|
}) => {
|
|
5184
5203
|
const [ctx] = resolveContexts([knowledge_base_id], contexts);
|
|
5185
|
-
const
|
|
5204
|
+
const maxResults = toolVariablesConfig?.[`${ctx.id}_|_max_results`] || 20;
|
|
5205
|
+
const effectiveLimit = includeContent ? Math.min(limit ?? maxResults, maxResults) : Math.min((limit ?? maxResults) * maxResults, 400);
|
|
5186
5206
|
const itemFilters = [];
|
|
5187
5207
|
if (preselectedItemsByContext) {
|
|
5188
5208
|
const contextItemIds = preselectedItemsByContext.get(knowledge_base_id);
|
|
@@ -5204,7 +5224,7 @@ For listing queries: always start with includeContent: false, then use dynamic t
|
|
|
5204
5224
|
if (item_names)
|
|
5205
5225
|
itemFilters.push({ name: { or: item_names.map((n) => ({ contains: n })) } });
|
|
5206
5226
|
if (item_external_ids) itemFilters.push({ external_id: { in: item_external_ids } });
|
|
5207
|
-
const effectiveQuery =
|
|
5227
|
+
const effectiveQuery = userQuery || keywords?.join(" ") || "";
|
|
5208
5228
|
let method = mapSearchMethod(searchMethod ?? "hybrid");
|
|
5209
5229
|
if (method === "hybridSearch" || method === "cosineDistance") {
|
|
5210
5230
|
if (!ctx.embedder) {
|
|
@@ -5212,6 +5232,7 @@ For listing queries: always start with includeContent: false, then use dynamic t
|
|
|
5212
5232
|
method = "tsvector";
|
|
5213
5233
|
}
|
|
5214
5234
|
}
|
|
5235
|
+
const expandChunks = toolVariablesConfig?.[`${ctx.id}_|_expand_chunks`] || 0;
|
|
5215
5236
|
try {
|
|
5216
5237
|
const { chunks } = await ctx.search({
|
|
5217
5238
|
query: effectiveQuery,
|
|
@@ -5224,7 +5245,11 @@ For listing queries: always start with includeContent: false, then use dynamic t
|
|
|
5224
5245
|
sort: { field: "updatedAt", direction: "desc" },
|
|
5225
5246
|
user,
|
|
5226
5247
|
role,
|
|
5227
|
-
trigger: "tool"
|
|
5248
|
+
trigger: "tool",
|
|
5249
|
+
expand: expandChunks > 0 ? {
|
|
5250
|
+
before: expandChunks,
|
|
5251
|
+
after: expandChunks
|
|
5252
|
+
} : void 0
|
|
5228
5253
|
});
|
|
5229
5254
|
return JSON.stringify(
|
|
5230
5255
|
chunks.map(
|
|
@@ -5996,6 +6021,46 @@ var TrajectoryLogger = class {
|
|
|
5996
6021
|
}
|
|
5997
6022
|
};
|
|
5998
6023
|
|
|
6024
|
+
// ee/agentic-retrieval/v3/context-sampler.ts
|
|
6025
|
+
var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
6026
|
+
var ContextSampler = class {
|
|
6027
|
+
cache = /* @__PURE__ */ new Map();
|
|
6028
|
+
async getSamples(contexts, user, role) {
|
|
6029
|
+
return Promise.all(contexts.map((ctx) => this.getSample(ctx, user, role)));
|
|
6030
|
+
}
|
|
6031
|
+
async getSample(ctx, user, role) {
|
|
6032
|
+
const cached = this.cache.get(ctx.id);
|
|
6033
|
+
if (cached && Date.now() - cached.sampledAt < CACHE_TTL_MS) {
|
|
6034
|
+
return cached;
|
|
6035
|
+
}
|
|
6036
|
+
const { db: db2 } = await postgresClient();
|
|
6037
|
+
const tableName = getTableName(ctx.id);
|
|
6038
|
+
const tableDefinition = convertContextToTableDefinition(ctx);
|
|
6039
|
+
const customFieldNames = ctx.fields.map((f) => f.name);
|
|
6040
|
+
const selectFields = ["id", "name", "external_id", ...customFieldNames];
|
|
6041
|
+
let exampleItems = [];
|
|
6042
|
+
try {
|
|
6043
|
+
let query = db2(tableName).select(selectFields).whereNull("archived").limit(2);
|
|
6044
|
+
query = applyAccessControl(tableDefinition, query, user, tableName);
|
|
6045
|
+
exampleItems = await query;
|
|
6046
|
+
} catch {
|
|
6047
|
+
}
|
|
6048
|
+
const sample = {
|
|
6049
|
+
contextId: ctx.id,
|
|
6050
|
+
contextName: ctx.name,
|
|
6051
|
+
fields: ["name", "external_id", ...customFieldNames],
|
|
6052
|
+
exampleItems,
|
|
6053
|
+
sampledAt: Date.now()
|
|
6054
|
+
};
|
|
6055
|
+
this.cache.set(ctx.id, sample);
|
|
6056
|
+
return sample;
|
|
6057
|
+
}
|
|
6058
|
+
/** Evict a context from cache so it's re-sampled on next use */
|
|
6059
|
+
invalidate(contextId) {
|
|
6060
|
+
this.cache.delete(contextId);
|
|
6061
|
+
}
|
|
6062
|
+
};
|
|
6063
|
+
|
|
5999
6064
|
// ee/agentic-retrieval/v3/index.ts
|
|
6000
6065
|
var sampler = new ContextSampler();
|
|
6001
6066
|
async function* executeV3({
|
|
@@ -6003,6 +6068,7 @@ async function* executeV3({
|
|
|
6003
6068
|
contexts,
|
|
6004
6069
|
reranker,
|
|
6005
6070
|
model,
|
|
6071
|
+
toolVariablesConfig,
|
|
6006
6072
|
user,
|
|
6007
6073
|
role,
|
|
6008
6074
|
customInstructions,
|
|
@@ -6028,15 +6094,39 @@ async function* executeV3({
|
|
|
6028
6094
|
}
|
|
6029
6095
|
console.log("[EXULU] v3 \u2014 classified as:", classification);
|
|
6030
6096
|
const strategy = STRATEGIES[classification.queryType];
|
|
6097
|
+
const contextSpecificInstructions = activeContexts.map((ctx) => {
|
|
6098
|
+
const instructions = toolVariablesConfig?.[`${ctx.id}_|_instructions`] ?? "";
|
|
6099
|
+
if (instructions) {
|
|
6100
|
+
return `
|
|
6101
|
+
<${ctx.id}>
|
|
6102
|
+
${instructions}
|
|
6103
|
+
</${ctx.id}>
|
|
6104
|
+
`;
|
|
6105
|
+
} else {
|
|
6106
|
+
return null;
|
|
6107
|
+
}
|
|
6108
|
+
}).filter(Boolean).join("\n");
|
|
6031
6109
|
const suggestedIds = classification.suggestedContextIds;
|
|
6032
6110
|
const fallbackIds = activeContexts.filter((c) => !suggestedIds.includes(c.id)).map((c) => c.id);
|
|
6033
|
-
|
|
6111
|
+
let contextBase = suggestedIds.length > 0 ? `
|
|
6112
|
+
Suggested priority contexts: [${suggestedIds.join(", ")}].
|
|
6113
|
+
|
|
6114
|
+
Also available: [${fallbackIds.join(", ")}].
|
|
6115
|
+
|
|
6116
|
+
Custom instructions may require searching additional or all contexts \u2014 follow them.` : `All contexts available: [${activeContexts.map((c) => c.id).join(", ")}].`;
|
|
6034
6117
|
const preselectedNote = preselectedByContext?.size ? `
|
|
6035
6118
|
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.` : "";
|
|
6119
|
+
if (contextSpecificInstructions?.length) {
|
|
6120
|
+
contextBase += `
|
|
6121
|
+
Context specific instructions:
|
|
6122
|
+
${contextSpecificInstructions}
|
|
6123
|
+
`;
|
|
6124
|
+
}
|
|
6036
6125
|
const contextGuidance = contextBase + preselectedNote;
|
|
6037
6126
|
const bashToolkit = await (0, import_bash_tool.createBashTool)({ files: {} });
|
|
6038
6127
|
const retrievalTools = createRetrievalTools({
|
|
6039
6128
|
contexts: activeContexts,
|
|
6129
|
+
toolVariablesConfig,
|
|
6040
6130
|
user,
|
|
6041
6131
|
role,
|
|
6042
6132
|
updateVirtualFiles: (files) => bashToolkit.sandbox.writeFiles(files),
|
|
@@ -6091,7 +6181,8 @@ function createAgenticRetrievalToolV3({
|
|
|
6091
6181
|
user,
|
|
6092
6182
|
role,
|
|
6093
6183
|
model,
|
|
6094
|
-
|
|
6184
|
+
preselected,
|
|
6185
|
+
memoryItems
|
|
6095
6186
|
}) {
|
|
6096
6187
|
const license = checkLicense();
|
|
6097
6188
|
if (!license["agentic-retrieval"]) {
|
|
@@ -6144,27 +6235,57 @@ function createAgenticRetrievalToolV3({
|
|
|
6144
6235
|
default: false
|
|
6145
6236
|
},
|
|
6146
6237
|
{
|
|
6147
|
-
name: "
|
|
6238
|
+
name: "logging",
|
|
6148
6239
|
description: "Save a detailed markdown + JSON log of every retrieval execution to disk. Useful for debugging and evaluation.",
|
|
6149
6240
|
type: "boolean",
|
|
6150
6241
|
default: false
|
|
6151
6242
|
},
|
|
6152
6243
|
...contexts.map((ctx) => ({
|
|
6153
|
-
name: ctx.id,
|
|
6244
|
+
name: ctx.id + "_|_enabled",
|
|
6154
6245
|
description: `Enable search in "${ctx.name}". ${ctx.description}`,
|
|
6155
6246
|
type: "boolean",
|
|
6156
6247
|
default: true
|
|
6248
|
+
})),
|
|
6249
|
+
...contexts.map((ctx) => ({
|
|
6250
|
+
name: `${ctx.id}_|_instructions`,
|
|
6251
|
+
description: `Instructions for the retrieval agent about how to search in the ${ctx.name} context`,
|
|
6252
|
+
type: "string",
|
|
6253
|
+
default: ""
|
|
6254
|
+
})),
|
|
6255
|
+
...contexts.map((ctx) => ({
|
|
6256
|
+
name: `${ctx.id}_|_priority`,
|
|
6257
|
+
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`,
|
|
6258
|
+
type: "number",
|
|
6259
|
+
default: 0
|
|
6260
|
+
})),
|
|
6261
|
+
...contexts.map((ctx) => ({
|
|
6262
|
+
name: `${ctx.id}_|_max_results`,
|
|
6263
|
+
description: `Defines the maximum number of results to return for the ${ctx.name} context`,
|
|
6264
|
+
type: "number",
|
|
6265
|
+
default: 0
|
|
6266
|
+
})),
|
|
6267
|
+
...contexts.map((ctx) => ({
|
|
6268
|
+
name: `${ctx.id}_|_max_steps`,
|
|
6269
|
+
description: `Defines the maximum number of steps the agent is allowed to take when searching the ${ctx.name} context`,
|
|
6270
|
+
type: "number",
|
|
6271
|
+
default: 0
|
|
6272
|
+
})),
|
|
6273
|
+
...contexts.map((ctx) => ({
|
|
6274
|
+
name: `${ctx.id}_|_expand_chunks`,
|
|
6275
|
+
description: `Defines if the agent automatically retrieves nearby chunks around the matched chunks, usefull if relevant content might be split up`,
|
|
6276
|
+
type: "number",
|
|
6277
|
+
default: 0
|
|
6157
6278
|
}))
|
|
6158
6279
|
],
|
|
6159
6280
|
inputSchema: import_zod9.z.object({
|
|
6160
|
-
|
|
6281
|
+
userQuery: import_zod9.z.string().describe("The original unaltered question from the user"),
|
|
6161
6282
|
userInstructions: import_zod9.z.string().optional().describe("Additional instructions from the user to guide retrieval"),
|
|
6162
6283
|
confirmedContextIds: import_zod9.z.array(import_zod9.z.string()).optional().describe(
|
|
6163
6284
|
"Knowledge base IDs explicitly confirmed by the user to be used in the retrieval. When presen only searches these contexts. "
|
|
6164
6285
|
)
|
|
6165
6286
|
}),
|
|
6166
6287
|
execute: async function* ({
|
|
6167
|
-
|
|
6288
|
+
userQuery,
|
|
6168
6289
|
userInstructions,
|
|
6169
6290
|
confirmedContextIds,
|
|
6170
6291
|
toolVariablesConfig,
|
|
@@ -6182,10 +6303,10 @@ function createAgenticRetrievalToolV3({
|
|
|
6182
6303
|
let managedContextEnabled = false;
|
|
6183
6304
|
if (toolVariablesConfig) {
|
|
6184
6305
|
configInstructions = toolVariablesConfig["instructions"] ?? "";
|
|
6185
|
-
logTrajectory = toolVariablesConfig["
|
|
6306
|
+
logTrajectory = toolVariablesConfig["logging"] === true || toolVariablesConfig["logging"] === "true";
|
|
6186
6307
|
managedContextEnabled = toolVariablesConfig["managed_context"] === true || toolVariablesConfig["managed_context"] === "true";
|
|
6187
6308
|
activeContexts = contexts.filter(
|
|
6188
|
-
(ctx) => toolVariablesConfig[ctx.id] === true || toolVariablesConfig[ctx.id] === "true" || toolVariablesConfig[ctx.id] === 1
|
|
6309
|
+
(ctx) => toolVariablesConfig[ctx.id + "_|_enabled"] === true || toolVariablesConfig[ctx.id + "_|_enabled"] === "true" || toolVariablesConfig[ctx.id + "_|_enabled"] === 1
|
|
6189
6310
|
);
|
|
6190
6311
|
if (activeContexts.length === 0) activeContexts = contexts;
|
|
6191
6312
|
requiresPreselectedContexts = toolVariablesConfig["require_preselected_contexts"] === true || toolVariablesConfig["require_preselected_contexts"] === "true";
|
|
@@ -6195,13 +6316,13 @@ function createAgenticRetrievalToolV3({
|
|
|
6195
6316
|
}
|
|
6196
6317
|
}
|
|
6197
6318
|
console.log("[EXULU] Managed context enabled:", managedContextEnabled);
|
|
6198
|
-
console.log("[EXULU] Preselected item IDs:",
|
|
6199
|
-
if (managedContextEnabled && !
|
|
6319
|
+
console.log("[EXULU] Preselected item IDs:", preselected);
|
|
6320
|
+
if (managedContextEnabled && !preselected?.length) {
|
|
6200
6321
|
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.");
|
|
6201
6322
|
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." };
|
|
6202
6323
|
return;
|
|
6203
6324
|
}
|
|
6204
|
-
if (requiresPreselectedContexts && !confirmedContextIds?.length && !
|
|
6325
|
+
if (requiresPreselectedContexts && !confirmedContextIds?.length && !preselected?.length) {
|
|
6205
6326
|
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.");
|
|
6206
6327
|
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." };
|
|
6207
6328
|
return;
|
|
@@ -6212,21 +6333,43 @@ function createAgenticRetrievalToolV3({
|
|
|
6212
6333
|
if (filtered.length > 0) activeContexts = filtered;
|
|
6213
6334
|
}
|
|
6214
6335
|
const combinedInstructions = [
|
|
6215
|
-
configInstructions ? `
|
|
6216
|
-
|
|
6217
|
-
|
|
6336
|
+
configInstructions ? `
|
|
6337
|
+
Configuration instructions:
|
|
6338
|
+
<configuration_instructions>
|
|
6339
|
+
${configInstructions}
|
|
6340
|
+
</configuration_instructions>
|
|
6341
|
+
` : "",
|
|
6342
|
+
adminInstructions ? `
|
|
6343
|
+
Admin instructions:
|
|
6344
|
+
<admin_instructions>
|
|
6345
|
+
${adminInstructions}
|
|
6346
|
+
</admin_instructions>
|
|
6347
|
+
` : "",
|
|
6348
|
+
userInstructions ? `
|
|
6349
|
+
User instructions:
|
|
6350
|
+
<user_instructions>
|
|
6351
|
+
${userInstructions}
|
|
6352
|
+
</user_instructions>
|
|
6353
|
+
` : "",
|
|
6354
|
+
memoryItems ? `
|
|
6355
|
+
Relevant memories (these are items that the agent has retrieved from the memory context and are relevant to the query):
|
|
6356
|
+
<relevant_memories>
|
|
6357
|
+
${memoryItems?.map((item) => JSON.stringify(item)).join("\n")}
|
|
6358
|
+
</relevant_memories>
|
|
6359
|
+
` : ""
|
|
6218
6360
|
].filter(Boolean).join("\n");
|
|
6219
6361
|
for await (const output of executeV3({
|
|
6220
|
-
query,
|
|
6362
|
+
query: userQuery,
|
|
6221
6363
|
contexts: activeContexts,
|
|
6222
6364
|
reranker: configuredReranker,
|
|
6365
|
+
toolVariablesConfig,
|
|
6223
6366
|
model,
|
|
6224
6367
|
user,
|
|
6225
6368
|
role,
|
|
6226
6369
|
customInstructions: combinedInstructions || void 0,
|
|
6227
6370
|
logTrajectory,
|
|
6228
6371
|
sessionId: sessionID,
|
|
6229
|
-
preselectedItemIds
|
|
6372
|
+
preselectedItemIds: preselected
|
|
6230
6373
|
})) {
|
|
6231
6374
|
yield { result: JSON.stringify(output) };
|
|
6232
6375
|
}
|
|
@@ -11046,6 +11189,7 @@ var ExuluProvider = class {
|
|
|
11046
11189
|
});
|
|
11047
11190
|
}
|
|
11048
11191
|
let memoryContext = "";
|
|
11192
|
+
let memoryItems;
|
|
11049
11193
|
if (agent?.memory && contexts?.length && query) {
|
|
11050
11194
|
const context = contexts.find((context2) => context2.id === agent?.memory);
|
|
11051
11195
|
if (!context) {
|
|
@@ -11068,6 +11212,7 @@ var ExuluProvider = class {
|
|
|
11068
11212
|
page: 1
|
|
11069
11213
|
});
|
|
11070
11214
|
if (result?.chunks?.length) {
|
|
11215
|
+
memoryItems = result.chunks;
|
|
11071
11216
|
memoryContext = `
|
|
11072
11217
|
Pre-fetched relevant information for this query:
|
|
11073
11218
|
|
|
@@ -11188,7 +11333,8 @@ When a tool execution is not approved by the user, do not retry it unless explic
|
|
|
11188
11333
|
project,
|
|
11189
11334
|
sessionItems,
|
|
11190
11335
|
model,
|
|
11191
|
-
agent
|
|
11336
|
+
agent,
|
|
11337
|
+
memoryItems
|
|
11192
11338
|
),
|
|
11193
11339
|
stopWhen: [(0, import_ai8.stepCountIs)(maxStepCount || 5)]
|
|
11194
11340
|
// make configurable
|
|
@@ -11265,7 +11411,8 @@ When a tool execution is not approved by the user, do not retry it unless explic
|
|
|
11265
11411
|
project,
|
|
11266
11412
|
sessionItems,
|
|
11267
11413
|
model,
|
|
11268
|
-
agent
|
|
11414
|
+
agent,
|
|
11415
|
+
memoryItems
|
|
11269
11416
|
),
|
|
11270
11417
|
stopWhen: [(0, import_ai8.stepCountIs)(maxStepCount || 5)]
|
|
11271
11418
|
});
|
|
@@ -11447,6 +11594,7 @@ ${extractedText}
|
|
|
11447
11594
|
});
|
|
11448
11595
|
}
|
|
11449
11596
|
let memoryContext = "";
|
|
11597
|
+
let memoryItems;
|
|
11450
11598
|
if (agent?.memory && contexts?.length && query) {
|
|
11451
11599
|
const context = contexts.find((context2) => context2.id === agent?.memory);
|
|
11452
11600
|
if (!context) {
|
|
@@ -11470,6 +11618,7 @@ ${extractedText}
|
|
|
11470
11618
|
});
|
|
11471
11619
|
import_fs.default.writeFileSync("pre-fetched-relevant-information.json", JSON.stringify(result2, null, 2));
|
|
11472
11620
|
if (result2?.chunks?.length) {
|
|
11621
|
+
memoryItems = result2.chunks;
|
|
11473
11622
|
memoryContext = `
|
|
11474
11623
|
<pre-fetched relevant information for this query>:
|
|
11475
11624
|
|
|
@@ -11489,9 +11638,6 @@ ${extractedText}
|
|
|
11489
11638
|
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.";
|
|
11490
11639
|
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.";
|
|
11491
11640
|
system += "\n\n" + genericContext;
|
|
11492
|
-
if (memoryContext) {
|
|
11493
|
-
system += "\n\n" + memoryContext;
|
|
11494
|
-
}
|
|
11495
11641
|
const includesContextSearchTool = currentTools?.some(
|
|
11496
11642
|
(tool5) => tool5.name.toLowerCase().includes("context_search") || tool5.id.includes("context_search") || tool5.type === "context"
|
|
11497
11643
|
);
|
|
@@ -11576,7 +11722,8 @@ When a tool execution is not approved by the user, do not retry it unless explic
|
|
|
11576
11722
|
project,
|
|
11577
11723
|
sessionItems,
|
|
11578
11724
|
model,
|
|
11579
|
-
agent
|
|
11725
|
+
agent,
|
|
11726
|
+
memoryItems
|
|
11580
11727
|
),
|
|
11581
11728
|
onError: (error) => {
|
|
11582
11729
|
console.error("[EXULU] chat stream error.", error);
|
|
@@ -12169,6 +12316,7 @@ var {
|
|
|
12169
12316
|
workflowTemplatesSchema: workflowTemplatesSchema2,
|
|
12170
12317
|
rbacSchema: rbacSchema2,
|
|
12171
12318
|
promptLibrarySchema: promptLibrarySchema2,
|
|
12319
|
+
contextPresetsSchema: contextPresetsSchema2,
|
|
12172
12320
|
embedderSettingsSchema: embedderSettingsSchema2,
|
|
12173
12321
|
promptFavoritesSchema: promptFavoritesSchema2,
|
|
12174
12322
|
statisticsSchema: statisticsSchema2
|
|
@@ -12216,6 +12364,7 @@ var createExpressRoutes = async (app, providers, tools, contexts, config, evals,
|
|
|
12216
12364
|
projectsSchema2(),
|
|
12217
12365
|
jobResultsSchema2(),
|
|
12218
12366
|
promptLibrarySchema2(),
|
|
12367
|
+
contextPresetsSchema2(),
|
|
12219
12368
|
embedderSettingsSchema2(),
|
|
12220
12369
|
promptFavoritesSchema2(),
|
|
12221
12370
|
evalRunsSchema2(),
|
|
@@ -17282,6 +17431,7 @@ var {
|
|
|
17282
17431
|
projectsSchema: projectsSchema3,
|
|
17283
17432
|
jobResultsSchema: jobResultsSchema3,
|
|
17284
17433
|
promptLibrarySchema: promptLibrarySchema3,
|
|
17434
|
+
contextPresetsSchema: contextPresetsSchema3,
|
|
17285
17435
|
embedderSettingsSchema: embedderSettingsSchema3,
|
|
17286
17436
|
promptFavoritesSchema: promptFavoritesSchema3
|
|
17287
17437
|
} = coreSchemas.get();
|
|
@@ -17319,6 +17469,7 @@ var up = async function(knex) {
|
|
|
17319
17469
|
projectsSchema3(),
|
|
17320
17470
|
jobResultsSchema3(),
|
|
17321
17471
|
promptLibrarySchema3(),
|
|
17472
|
+
contextPresetsSchema3(),
|
|
17322
17473
|
embedderSettingsSchema3(),
|
|
17323
17474
|
promptFavoritesSchema3(),
|
|
17324
17475
|
rbacSchema3(),
|