@superatomai/sdk-node 0.0.24 → 0.0.25

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.js CHANGED
@@ -164,6 +164,7 @@ var init_utils = __esm({
164
164
  // src/index.ts
165
165
  var index_exports = {};
166
166
  __export(index_exports, {
167
+ BM25L: () => BM25L,
167
168
  CONTEXT_CONFIG: () => CONTEXT_CONFIG,
168
169
  CleanupService: () => CleanupService,
169
170
  LLM: () => LLM,
@@ -175,7 +176,10 @@ __export(index_exports, {
175
176
  UIBlock: () => UIBlock,
176
177
  UILogCollector: () => UILogCollector,
177
178
  UserManager: () => UserManager,
178
- logger: () => logger
179
+ hybridRerank: () => hybridRerank,
180
+ logger: () => logger,
181
+ rerankChromaResults: () => rerankChromaResults,
182
+ rerankConversationResults: () => rerankConversationResults
179
183
  });
180
184
  module.exports = __toCommonJS(index_exports);
181
185
 
@@ -3819,6 +3823,166 @@ var KB = {
3819
3823
  };
3820
3824
  var knowledge_base_default = KB;
3821
3825
 
3826
+ // src/utils/bm25l-reranker.ts
3827
+ var BM25L = class {
3828
+ /**
3829
+ * @param documents - Array of raw documents (strings)
3830
+ * @param opts - Optional BM25L parameters
3831
+ */
3832
+ constructor(documents = [], opts = {}) {
3833
+ if (!Array.isArray(documents)) {
3834
+ throw new Error("BM25L: documents must be an array of strings.");
3835
+ }
3836
+ this.k1 = typeof opts.k1 === "number" ? opts.k1 : 1.5;
3837
+ this.b = typeof opts.b === "number" ? opts.b : 0.75;
3838
+ this.delta = typeof opts.delta === "number" ? opts.delta : 0.5;
3839
+ this.documents = documents.map((d) => typeof d === "string" ? this.tokenize(d) : []);
3840
+ this.docLengths = this.documents.map((doc) => doc.length);
3841
+ this.avgDocLength = this.docLengths.reduce((a, b) => a + b, 0) / (this.docLengths.length || 1);
3842
+ this.termDocFreq = {};
3843
+ this.documents.forEach((doc) => {
3844
+ const seen = /* @__PURE__ */ new Set();
3845
+ doc.forEach((term) => {
3846
+ if (!seen.has(term)) {
3847
+ seen.add(term);
3848
+ this.termDocFreq[term] = (this.termDocFreq[term] || 0) + 1;
3849
+ }
3850
+ });
3851
+ });
3852
+ }
3853
+ /**
3854
+ * Tokenize text into lowercase alphanumeric tokens
3855
+ */
3856
+ tokenize(text) {
3857
+ if (typeof text !== "string") return [];
3858
+ return text.toLowerCase().replace(/[^a-z0-9\s]/g, " ").split(/\s+/).filter(Boolean);
3859
+ }
3860
+ /**
3861
+ * Compute IDF (Inverse Document Frequency) with smoothing
3862
+ */
3863
+ idf(term) {
3864
+ const df = this.termDocFreq[term] || 0;
3865
+ const N = this.documents.length || 1;
3866
+ return Math.log(1 + (N - df + 0.5) / (df + 0.5));
3867
+ }
3868
+ /**
3869
+ * Compute BM25L score for a single document
3870
+ */
3871
+ score(query, docIndex) {
3872
+ if (typeof query !== "string") return 0;
3873
+ if (docIndex < 0 || docIndex >= this.documents.length) return 0;
3874
+ const tokens = this.tokenize(query);
3875
+ if (tokens.length === 0) return 0;
3876
+ const doc = this.documents[docIndex];
3877
+ const docLength = this.docLengths[docIndex] || 1;
3878
+ const freq = {};
3879
+ for (const t of doc) {
3880
+ freq[t] = (freq[t] || 0) + 1;
3881
+ }
3882
+ let sum = 0;
3883
+ for (const term of tokens) {
3884
+ const tf = freq[term] || 0;
3885
+ if (tf === 0) continue;
3886
+ const idfVal = this.idf(term);
3887
+ let tfL = tf - this.b * (docLength / this.avgDocLength) + this.delta;
3888
+ if (tfL < 0) tfL = 0;
3889
+ sum += idfVal * (tfL / (this.k1 + tfL));
3890
+ }
3891
+ return sum;
3892
+ }
3893
+ /**
3894
+ * Search and rank all documents
3895
+ */
3896
+ search(query) {
3897
+ return this.documents.map((_, i) => ({
3898
+ index: i,
3899
+ score: this.score(query, i)
3900
+ })).sort((a, b) => b.score - a.score);
3901
+ }
3902
+ };
3903
+ function normalizeScores(scores) {
3904
+ if (scores.length === 0) return [];
3905
+ const min = Math.min(...scores);
3906
+ const max = Math.max(...scores);
3907
+ if (max === min) {
3908
+ return scores.map(() => max === 0 ? 0 : 1);
3909
+ }
3910
+ return scores.map((score) => (score - min) / (max - min));
3911
+ }
3912
+ function hybridRerank(query, items, getDocument, getSemanticScore, options = {}) {
3913
+ const {
3914
+ semanticWeight = 0.7,
3915
+ bm25Weight = 0.3,
3916
+ minScore = 0,
3917
+ k1 = 1.5,
3918
+ b = 0.75,
3919
+ delta = 0.5
3920
+ } = options;
3921
+ if (items.length === 0) return [];
3922
+ const documents = items.map(getDocument);
3923
+ const semanticScores = items.map(getSemanticScore);
3924
+ const bm25 = new BM25L(documents, { k1, b, delta });
3925
+ const bm25Scores = items.map((_, i) => bm25.score(query, i));
3926
+ const normalizedSemantic = normalizeScores(semanticScores);
3927
+ const normalizedBM25 = normalizeScores(bm25Scores);
3928
+ const results = items.map((item, i) => {
3929
+ const hybridScore = semanticWeight * normalizedSemantic[i] + bm25Weight * normalizedBM25[i];
3930
+ return {
3931
+ item,
3932
+ originalIndex: i,
3933
+ semanticScore: semanticScores[i],
3934
+ bm25Score: bm25Scores[i],
3935
+ hybridScore
3936
+ };
3937
+ });
3938
+ return results.filter((r) => r.hybridScore >= minScore).sort((a, b2) => b2.hybridScore - a.hybridScore);
3939
+ }
3940
+ function rerankChromaResults(query, chromaResults, options = {}) {
3941
+ const ids = chromaResults.ids[0] || [];
3942
+ const documents = chromaResults.documents[0] || [];
3943
+ const metadatas = chromaResults.metadatas[0] || [];
3944
+ const distances = chromaResults.distances[0] || [];
3945
+ if (ids.length === 0) return [];
3946
+ const items = ids.map((id, i) => ({
3947
+ id,
3948
+ document: documents[i],
3949
+ metadata: metadatas[i],
3950
+ distance: distances[i]
3951
+ }));
3952
+ const reranked = hybridRerank(
3953
+ query,
3954
+ items,
3955
+ (item) => item.document || "",
3956
+ // Convert L2 distance to similarity score
3957
+ (item) => 1 / (1 + item.distance),
3958
+ options
3959
+ );
3960
+ return reranked.map((r) => ({
3961
+ id: r.item.id,
3962
+ document: r.item.document,
3963
+ metadata: r.item.metadata,
3964
+ distance: r.item.distance,
3965
+ semanticScore: r.semanticScore,
3966
+ bm25Score: r.bm25Score,
3967
+ hybridScore: r.hybridScore
3968
+ }));
3969
+ }
3970
+ function rerankConversationResults(query, results, options = {}) {
3971
+ if (results.length === 0) return [];
3972
+ const reranked = hybridRerank(
3973
+ query,
3974
+ results,
3975
+ (item) => item.userPrompt || "",
3976
+ (item) => item.similarity || 0,
3977
+ options
3978
+ );
3979
+ return reranked.map((r) => ({
3980
+ ...r.item,
3981
+ hybridScore: r.hybridScore,
3982
+ bm25Score: r.bm25Score
3983
+ }));
3984
+ }
3985
+
3822
3986
  // src/userResponse/conversation-search.ts
3823
3987
  var searchConversations = async ({
3824
3988
  userPrompt,
@@ -3865,8 +4029,93 @@ var searchConversations = async ({
3865
4029
  return null;
3866
4030
  }
3867
4031
  };
4032
+ var searchConversationsWithReranking = async (options) => {
4033
+ const {
4034
+ userPrompt,
4035
+ collections,
4036
+ userId,
4037
+ similarityThreshold = 0.6,
4038
+ rerankCandidates = 50,
4039
+ // Fetch more candidates for better reranking
4040
+ hybridOptions = {
4041
+ semanticWeight: 0.7,
4042
+ bm25Weight: 0.3
4043
+ }
4044
+ } = options;
4045
+ try {
4046
+ if (!collections || !collections["conversation-history"]) {
4047
+ logger.info("[ConversationSearch] conversation-history collection not registered, skipping");
4048
+ return null;
4049
+ }
4050
+ if (!collections["conversation-history"]["searchMultiple"]) {
4051
+ logger.info("[ConversationSearch] searchMultiple not available, falling back to standard search");
4052
+ return searchConversations({
4053
+ userPrompt,
4054
+ collections,
4055
+ userId,
4056
+ similarityThreshold
4057
+ });
4058
+ }
4059
+ logger.info(`[ConversationSearch] Hybrid search for: "${userPrompt.substring(0, 50)}..."`);
4060
+ logger.info(`[ConversationSearch] Fetching ${rerankCandidates} candidates for reranking`);
4061
+ logger.info(`[ConversationSearch] Weights - Semantic: ${hybridOptions.semanticWeight}, BM25: ${hybridOptions.bm25Weight}`);
4062
+ const results = await collections["conversation-history"]["searchMultiple"]({
4063
+ userPrompt,
4064
+ userId,
4065
+ limit: rerankCandidates,
4066
+ threshold: 0
4067
+ // No threshold - get all candidates for reranking
4068
+ });
4069
+ if (!results || results.length === 0) {
4070
+ logger.info("[ConversationSearch] No conversations found in database");
4071
+ return null;
4072
+ }
4073
+ logger.info(`[ConversationSearch] Retrieved ${results.length} candidates for reranking`);
4074
+ const candidatesForReranking = results.map((r) => ({
4075
+ ...r,
4076
+ userPrompt: r.metadata?.userPrompt || ""
4077
+ }));
4078
+ const reranked = rerankConversationResults(userPrompt, candidatesForReranking, hybridOptions);
4079
+ if (reranked.length === 0) {
4080
+ logger.info("[ConversationSearch] No results after reranking");
4081
+ return null;
4082
+ }
4083
+ const best = reranked[0];
4084
+ const hybridScore = best.hybridScore;
4085
+ const semanticScore = best.similarity || 0;
4086
+ const matchedUserPrompt = best.userPrompt || best.metadata?.userPrompt || "";
4087
+ logger.info(`[ConversationSearch] Best match after reranking:`);
4088
+ logger.info(` - Hybrid score: ${(hybridScore * 100).toFixed(2)}%`);
4089
+ logger.info(` - Semantic score: ${(semanticScore * 100).toFixed(2)}%`);
4090
+ logger.info(` - BM25L score: ${best.bm25Score.toFixed(4)}`);
4091
+ logger.info(` - Matched prompt: "${matchedUserPrompt}"`);
4092
+ logger.info(` - Query prompt: "${userPrompt}"`);
4093
+ if (semanticScore < similarityThreshold) {
4094
+ logger.info(
4095
+ `[ConversationSearch] Semantic score ${(semanticScore * 100).toFixed(2)}% below threshold ${(similarityThreshold * 100).toFixed(2)}% - rejecting match`
4096
+ );
4097
+ return null;
4098
+ }
4099
+ logger.info(
4100
+ `[ConversationSearch] \u2713 Found match with semantic score ${(semanticScore * 100).toFixed(2)}%`
4101
+ );
4102
+ logger.info(` - Returning cached result for: "${matchedUserPrompt}"`);
4103
+ return {
4104
+ uiBlock: best.uiBlock,
4105
+ similarity: semanticScore,
4106
+ hybridScore,
4107
+ bm25Score: best.bm25Score,
4108
+ metadata: best.metadata
4109
+ };
4110
+ } catch (error) {
4111
+ const errorMsg = error instanceof Error ? error.message : String(error);
4112
+ logger.warn(`[ConversationSearch] Error in hybrid search: ${errorMsg}`);
4113
+ return null;
4114
+ }
4115
+ };
3868
4116
  var ConversationSearch = {
3869
- searchConversations
4117
+ searchConversations,
4118
+ searchConversationsWithReranking
3870
4119
  };
3871
4120
  var conversation_search_default = ConversationSearch;
3872
4121
 
@@ -3915,7 +4164,7 @@ var BaseLLM = class {
3915
4164
  * @param componentStreamCallback - Optional callback to stream primary KPI component as soon as it's identified
3916
4165
  * @returns Object containing matched components, layout title/description, and follow-up actions
3917
4166
  */
3918
- async matchComponentsFromAnalysis(analysisContent, components, apiKey, logCollector, componentStreamCallback) {
4167
+ async matchComponentsFromAnalysis(analysisContent, components, apiKey, logCollector, componentStreamCallback, deferredTools, executedTools) {
3919
4168
  try {
3920
4169
  logger.debug(`[${this.getProviderName()}] Starting component matching from text response`);
3921
4170
  let availableComponentsText = "No components available";
@@ -3931,12 +4180,44 @@ var BaseLLM = class {
3931
4180
  Props Structure: ${propsPreview}`;
3932
4181
  }).join("\n\n");
3933
4182
  }
4183
+ let deferredToolsText = "No deferred external tools for this request.";
4184
+ if (deferredTools && deferredTools.length > 0) {
4185
+ logger.info(`[${this.getProviderName()}] Passing ${deferredTools.length} deferred tools to component matching`);
4186
+ deferredToolsText = "The following external tools need user input via a Form component.\n**IMPORTANT: Use these EXACT values when generating Form externalTool prop.**\n\n" + deferredTools.map((tool, idx) => {
4187
+ return `${idx + 1}. **${tool.name}**
4188
+ toolId: "${tool.id}" (USE THIS EXACT VALUE - do not modify!)
4189
+ toolName: "${tool.name}"
4190
+ parameters: ${JSON.stringify(tool.params || {})}
4191
+ requiredFields:
4192
+ ${JSON.stringify(tool.requiredFields || [], null, 2)}`;
4193
+ }).join("\n\n");
4194
+ }
4195
+ let executedToolsText = "No external tools were executed for data fetching.";
4196
+ if (executedTools && executedTools.length > 0) {
4197
+ logger.info(`[${this.getProviderName()}] Passing ${executedTools.length} executed tools to component matching`);
4198
+ executedToolsText = "The following external tools were executed to fetch data.\n**IMPORTANT: For components displaying this data, use externalTool prop instead of query.**\n**IMPORTANT: Use the result data to populate deferred tool parameters when applicable.**\n\n" + executedTools.map((tool, idx) => {
4199
+ let resultPreview = "No result data";
4200
+ if (tool.result) {
4201
+ const resultStr = typeof tool.result === "string" ? tool.result : JSON.stringify(tool.result, null, 2);
4202
+ resultPreview = resultStr.length > 2e3 ? resultStr.substring(0, 2e3) + "\n... (truncated)" : resultStr;
4203
+ }
4204
+ return `${idx + 1}. **${tool.name}**
4205
+ toolId: "${tool.id}" (USE THIS EXACT VALUE for externalTool.toolId)
4206
+ toolName: "${tool.name}" (USE THIS EXACT VALUE for externalTool.toolName)
4207
+ parameters: ${JSON.stringify(tool.params || {})} (USE THESE for externalTool.parameters)
4208
+ result: ${resultPreview}`;
4209
+ }).join("\n\n");
4210
+ }
3934
4211
  const schemaDoc = schema.generateSchemaDocumentation();
3935
4212
  logger.file("\n=============================\nText analysis response:", analysisContent);
4213
+ logger.file("\n=============================\nDeferred tools:", deferredToolsText);
4214
+ logger.file("\n=============================\nExecuted tools:", executedToolsText);
3936
4215
  const prompts = await promptLoader.loadPrompts("match-text-components", {
3937
4216
  ANALYSIS_CONTENT: analysisContent,
3938
4217
  AVAILABLE_COMPONENTS: availableComponentsText,
3939
- SCHEMA_DOC: schemaDoc
4218
+ SCHEMA_DOC: schemaDoc,
4219
+ DEFERRED_TOOLS: deferredToolsText,
4220
+ EXECUTED_TOOLS: executedToolsText
3940
4221
  });
3941
4222
  logger.debug(`[${this.getProviderName()}] Loaded match-text-components prompts`);
3942
4223
  logger.file("\n=============================\nmatch text components system prompt:", prompts.system);
@@ -4056,9 +4337,6 @@ var BaseLLM = class {
4056
4337
  matchedComponents.forEach((comp, idx) => {
4057
4338
  logger.info(`[${this.getProviderName()}] ${idx + 1}. ${comp.componentType} (${comp.componentName}) - ${comp.originalSuggestion || "N/A"}`);
4058
4339
  });
4059
- if (suggestedComponents.length !== matchedComponents.length) {
4060
- logger.warn(`[${this.getProviderName()}] \u26A0\uFE0F MISMATCH: Text suggested ${suggestedComponents.length} components, but LLM matched ${matchedComponents.length}`);
4061
- }
4062
4340
  logger.file("\n=============================\nFull LLM response:", JSON.stringify(result, null, 2));
4063
4341
  const rawActions = result.actions || [];
4064
4342
  const actions = convertQuestionsToActions(rawActions);
@@ -4280,21 +4558,44 @@ var BaseLLM = class {
4280
4558
  let availableToolsDoc = "No external tools are available for this request.";
4281
4559
  if (externalTools && externalTools.length > 0) {
4282
4560
  logger.info(`[${this.getProviderName()}] External tools available: ${externalTools.map((t) => t.name).join(", ")}`);
4283
- availableToolsDoc = "\u26A0\uFE0F **EXECUTE THESE TOOLS IMMEDIATELY** \u26A0\uFE0F\n\nThe following external tools have been identified as necessary for this request. You MUST call them:\n\n" + externalTools.map((tool, idx) => {
4284
- const paramsText = Object.entries(tool.params || {}).map(([key, value]) => {
4285
- const valueType = typeof value;
4286
- if (valueType === "string" && ["string", "number", "integer", "boolean", "array", "object"].includes(String(value).toLowerCase())) {
4287
- return `- ${key}: ${value}`;
4288
- } else {
4289
- return `- ${key}: ${JSON.stringify(value)} (default value - use this)`;
4561
+ const immediateTools = externalTools.filter((t) => t.executionType === "immediate" || t.executionType === "deferred" && t.userProvidedData);
4562
+ const deferredTools = externalTools.filter((t) => t.executionType === "deferred" && !t.userProvidedData);
4563
+ let toolsDocParts = [];
4564
+ if (immediateTools.length > 0) {
4565
+ const immediateDoc = "## IMMEDIATE EXECUTION TOOLS\nExecute these tools right away:\n\n" + immediateTools.map((tool, idx) => {
4566
+ const paramsText = Object.entries(tool.params || {}).map(([key, value]) => {
4567
+ const valueType = typeof value;
4568
+ if (valueType === "string" && ["string", "number", "integer", "boolean", "array", "object"].includes(String(value).toLowerCase())) {
4569
+ return `- ${key}: ${value}`;
4570
+ } else {
4571
+ return `- ${key}: ${JSON.stringify(value)} (default value - use this)`;
4572
+ }
4573
+ }).join("\n ");
4574
+ let userDataText = "";
4575
+ if (tool.userProvidedData) {
4576
+ userDataText = "\n **User Provided Data** (use these values):\n " + Object.entries(tool.userProvidedData).map(([key, value]) => `- ${key}: ${JSON.stringify(value)}`).join("\n ");
4290
4577
  }
4291
- }).join("\n ");
4292
- return `${idx + 1}. **${tool.name}** (ID: ${tool.id})
4578
+ return `${idx + 1}. **${tool.name}** (ID: ${tool.id})
4579
+ Execution Type: IMMEDIATE
4293
4580
  Description: ${tool.description}
4294
- **ACTION REQUIRED**: Call this tool with the parameters below
4295
4581
  Parameters:
4296
- ${paramsText}`;
4297
- }).join("\n\n");
4582
+ ${paramsText}${userDataText}`;
4583
+ }).join("\n\n");
4584
+ toolsDocParts.push(immediateDoc);
4585
+ }
4586
+ if (deferredTools.length > 0) {
4587
+ const deferredDoc = "## DEFERRED TOOLS (DO NOT EXECUTE)\nThese tools need user input. A Form component will be generated to collect the data.\n**DO NOT call these tools.** Instead, acknowledge the request and inform user that a form will be shown.\n\n" + deferredTools.map((tool, idx) => {
4588
+ const requiredFieldsText = (tool.requiredFields || []).map((f) => `- ${f.label || f.name} (${f.type})${f.required ? " *required*" : ""}`).join("\n ");
4589
+ return `${idx + 1}. **${tool.name}** (ID: ${tool.id})
4590
+ Execution Type: DEFERRED (needs form input)
4591
+ Description: ${tool.description}
4592
+ Reason: ${tool.executionReason || "Write operation requires user confirmation"}
4593
+ Required Fields:
4594
+ ${requiredFieldsText || "(fields will be determined by form)"}`;
4595
+ }).join("\n\n");
4596
+ toolsDocParts.push(deferredDoc);
4597
+ }
4598
+ availableToolsDoc = toolsDocParts.join("\n\n---\n\n");
4298
4599
  }
4299
4600
  const schemaDoc = schema.generateSchemaDocumentation();
4300
4601
  const knowledgeBaseContext = await knowledge_base_default.getKnowledgeBase({
@@ -4335,8 +4636,12 @@ var BaseLLM = class {
4335
4636
  }
4336
4637
  }];
4337
4638
  if (externalTools && externalTools.length > 0) {
4338
- externalTools.forEach((tool) => {
4339
- logger.info(`[${this.getProviderName()}] Processing external tool:`, JSON.stringify(tool, null, 2));
4639
+ const executableTools = externalTools.filter(
4640
+ (t) => t.executionType === "immediate" || t.executionType === "deferred" && t.userProvidedData
4641
+ );
4642
+ logger.info(`[${this.getProviderName()}] Executable tools: ${executableTools.length} of ${externalTools.length} total`);
4643
+ executableTools.forEach((tool) => {
4644
+ logger.info(`[${this.getProviderName()}] Processing executable tool:`, JSON.stringify(tool, null, 2));
4340
4645
  const properties = {};
4341
4646
  const required = [];
4342
4647
  Object.entries(tool.params || {}).forEach(([key, typeOrValue]) => {
@@ -4405,13 +4710,14 @@ var BaseLLM = class {
4405
4710
  input_schema: inputSchema
4406
4711
  });
4407
4712
  });
4408
- logger.info(`[${this.getProviderName()}] Added ${externalTools.length} external tools to tool calling capability`);
4713
+ logger.info(`[${this.getProviderName()}] Added ${executableTools.length} executable tools to tool calling capability (${externalTools.length - executableTools.length} deferred tools await form input)`);
4409
4714
  logger.info(`[${this.getProviderName()}] Complete tools array:`, JSON.stringify(tools, null, 2));
4410
4715
  }
4411
4716
  const queryAttempts = /* @__PURE__ */ new Map();
4412
4717
  const MAX_QUERY_ATTEMPTS = 6;
4413
4718
  const toolAttempts = /* @__PURE__ */ new Map();
4414
4719
  const MAX_TOOL_ATTEMPTS = 3;
4720
+ const executedToolsList = [];
4415
4721
  let maxAttemptsReached = false;
4416
4722
  let fullStreamedText = "";
4417
4723
  const wrappedStreamCallback = streamCallback ? (chunk) => {
@@ -4594,6 +4900,17 @@ Please try rephrasing your request or contact support.
4594
4900
  const result2 = await externalTool.fn(toolInput);
4595
4901
  logger.info(`[${this.getProviderName()}] External tool ${externalTool.name} executed successfully`);
4596
4902
  logCollector?.info(`\u2713 ${externalTool.name} executed successfully`);
4903
+ if (!executedToolsList.find((t) => t.id === externalTool.id)) {
4904
+ executedToolsList.push({
4905
+ id: externalTool.id,
4906
+ name: externalTool.name,
4907
+ params: toolInput,
4908
+ // The actual parameters used in this execution
4909
+ result: result2
4910
+ // Store the actual result data for populating deferred tool params
4911
+ });
4912
+ logger.info(`[${this.getProviderName()}] Tracked executed tool: ${externalTool.name} with params: ${JSON.stringify(toolInput)}`);
4913
+ }
4597
4914
  if (wrappedStreamCallback) {
4598
4915
  wrappedStreamCallback(`\u2705 **${externalTool.name} completed successfully**
4599
4916
 
@@ -4683,12 +5000,31 @@ ${errorMsg}
4683
5000
  wrappedStreamCallback(answerMarker);
4684
5001
  logger.info(`[${this.getProviderName()}] Streamed answer component to frontend: ${component.name} (${component.type})`);
4685
5002
  } : void 0;
5003
+ const deferredTools = externalTools?.filter((t) => {
5004
+ if (t.executionType === "deferred" && !t.userProvidedData) return true;
5005
+ if (category === "data_modification" && !t.userProvidedData) {
5006
+ const name = (t.name || t.id || "").toLowerCase();
5007
+ const isWriteOperation = /create|add|insert|new|update|edit|modify|delete|remove|send/.test(name);
5008
+ if (isWriteOperation) {
5009
+ logger.info(`[${this.getProviderName()}] Inferred deferred execution for tool: ${t.name}`);
5010
+ return true;
5011
+ }
5012
+ }
5013
+ return false;
5014
+ }) || [];
5015
+ if (deferredTools.length > 0) {
5016
+ logger.info(`[${this.getProviderName()}] Passing ${deferredTools.length} deferred tools for Form generation`);
5017
+ }
5018
+ logger.info(`[${this.getProviderName()}] passing deferred tools to the matching function: ${JSON.stringify(deferredTools, null, 2)}`);
5019
+ logger.info(`[${this.getProviderName()}] passing executed tools to the matching function: ${JSON.stringify(executedToolsList, null, 2)}`);
4686
5020
  const matchResult = await this.matchComponentsFromAnalysis(
4687
5021
  textResponse,
4688
5022
  components,
4689
5023
  apiKey,
4690
5024
  logCollector,
4691
- componentStreamCallback
5025
+ componentStreamCallback,
5026
+ deferredTools,
5027
+ executedToolsList
4692
5028
  );
4693
5029
  matchedComponents = matchResult.components;
4694
5030
  layoutTitle = matchResult.layoutTitle;
@@ -4757,23 +5093,20 @@ ${errorMsg}
4757
5093
  */
4758
5094
  async handleUserRequest(userPrompt, components, apiKey, logCollector, conversationHistory, responseMode = "text", streamCallback, collections, externalTools, userId) {
4759
5095
  const startTime = Date.now();
4760
- logger.info(`[${this.getProviderName()}] handleUserRequest called with responseMode: ${responseMode}`);
5096
+ logger.info(`[${this.getProviderName()}] handleUserRequest called for user prompt: ${userPrompt}`);
4761
5097
  logCollector?.info(`Starting request processing with mode: ${responseMode}`);
4762
5098
  try {
4763
5099
  logger.info(`[${this.getProviderName()}] Step 1: Searching previous conversations...`);
4764
5100
  logCollector?.info("Step 1: Searching for similar previous conversations...");
4765
- const conversationMatch = await conversation_search_default.searchConversations({
5101
+ const conversationMatch = await conversation_search_default.searchConversationsWithReranking({
4766
5102
  userPrompt,
4767
5103
  collections,
4768
5104
  userId,
4769
5105
  similarityThreshold: 0.6
4770
5106
  // 60% threshold
4771
5107
  });
4772
- logger.info("conversationMatch:", conversationMatch);
4773
5108
  if (conversationMatch) {
4774
- logger.info(
4775
- `[${this.getProviderName()}] \u2713 Found matching conversation with ${(conversationMatch.similarity * 100).toFixed(2)}% similarity`
4776
- );
5109
+ logger.info(`[${this.getProviderName()}] \u2713 Found matching conversation with ${(conversationMatch.similarity * 100).toFixed(2)}% similarity`);
4777
5110
  logCollector?.info(
4778
5111
  `\u2713 Found similar conversation (${(conversationMatch.similarity * 100).toFixed(2)}% match)`
4779
5112
  );
@@ -4781,7 +5114,6 @@ ${errorMsg}
4781
5114
  const isValidComponent = rawComponent && typeof rawComponent === "object" && Object.keys(rawComponent).length > 0;
4782
5115
  const component = isValidComponent ? rawComponent : null;
4783
5116
  const cachedTextResponse = conversationMatch.uiBlock?.analysis || conversationMatch.uiBlock?.textResponse || conversationMatch.uiBlock?.text || "";
4784
- logger.debug(`[${this.getProviderName()}] Cached component: ${component ? "present" : "null"}, cachedTextResponse: ${cachedTextResponse ? cachedTextResponse.substring(0, 50) + "..." : "empty"}`);
4785
5117
  if (this.containsFormComponent(component)) {
4786
5118
  logger.info(`[${this.getProviderName()}] Skipping cached result - Form components contain stale defaultValues, fetching fresh data`);
4787
5119
  logCollector?.info("Skipping cache for form - fetching current values from database...");
@@ -4879,22 +5211,31 @@ ${errorMsg}
4879
5211
  if (categoryClassification.externalTools && categoryClassification.externalTools.length > 0) {
4880
5212
  logger.info(`[${this.getProviderName()}] Identified ${categoryClassification.externalTools.length} external tools needed`);
4881
5213
  logCollector?.info(`Identified external tools: ${categoryClassification.externalTools.map((t) => t.name || t.type).join(", ")}`);
4882
- toolsToUse = categoryClassification.externalTools?.map((t) => ({
4883
- id: t.type,
4884
- name: t.name,
4885
- description: t.description,
4886
- params: t.parameters || {},
4887
- fn: (() => {
4888
- const realTool = externalTools?.find((tool) => tool.id === t.type);
4889
- if (realTool) {
4890
- logger.info(`[${this.getProviderName()}] Using real tool implementation for ${t.type}`);
4891
- return realTool.fn;
4892
- } else {
4893
- logger.warn(`[${this.getProviderName()}] Tool ${t.type} not found in registered tools`);
4894
- return async () => ({ success: false, message: `Tool ${t.name || t.type} not registered` });
4895
- }
4896
- })()
4897
- })) || [];
5214
+ logger.info(`[${this.getProviderName()}] Raw external tools from classification: ${JSON.stringify(categoryClassification.externalTools, null, 2)}`);
5215
+ toolsToUse = categoryClassification.externalTools?.map((t) => {
5216
+ const realTool = externalTools?.find((tool) => tool.id === t.type);
5217
+ logger.info(`[${this.getProviderName()}] Tool ${t.name}: executionType=${t.executionType}, userProvidedData=${t.userProvidedData ? "present" : "null"}`);
5218
+ return {
5219
+ id: t.type,
5220
+ name: t.name,
5221
+ description: t.description,
5222
+ params: t.parameters || {},
5223
+ // NEW: Include execution type info from category classification
5224
+ executionType: t.executionType || "immediate",
5225
+ executionReason: t.executionReason || "",
5226
+ requiredFields: t.requiredFields || [],
5227
+ userProvidedData: t.userProvidedData || null,
5228
+ fn: (() => {
5229
+ if (realTool) {
5230
+ logger.info(`[${this.getProviderName()}] Using real tool implementation for ${t.type}`);
5231
+ return realTool.fn;
5232
+ } else {
5233
+ logger.warn(`[${this.getProviderName()}] Tool ${t.type} not found in registered tools`);
5234
+ return async () => ({ success: false, message: `Tool ${t.name || t.type} not registered` });
5235
+ }
5236
+ })()
5237
+ };
5238
+ }) || [];
4898
5239
  }
4899
5240
  if (categoryClassification.category === "general") {
4900
5241
  logger.info(`[${this.getProviderName()}] Routing to general conversation (no database operations)`);
@@ -5585,7 +5926,6 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
5585
5926
  }
5586
5927
  logCollector.info(`Starting user prompt request with ${components.length} components`);
5587
5928
  const conversationHistory = thread.getConversationContext(CONTEXT_CONFIG.MAX_CONVERSATION_CONTEXT_BLOCKS, existingUiBlockId);
5588
- logger.info("conversationHistory", conversationHistory);
5589
5929
  const responseMode = payload.responseMode || "component";
5590
5930
  logger.info("responseMode", responseMode);
5591
5931
  let streamCallback;
@@ -9523,6 +9863,7 @@ var SuperatomSDK = class {
9523
9863
  };
9524
9864
  // Annotate the CommonJS export names for ESM import in node:
9525
9865
  0 && (module.exports = {
9866
+ BM25L,
9526
9867
  CONTEXT_CONFIG,
9527
9868
  CleanupService,
9528
9869
  LLM,
@@ -9534,6 +9875,9 @@ var SuperatomSDK = class {
9534
9875
  UIBlock,
9535
9876
  UILogCollector,
9536
9877
  UserManager,
9537
- logger
9878
+ hybridRerank,
9879
+ logger,
9880
+ rerankChromaResults,
9881
+ rerankConversationResults
9538
9882
  });
9539
9883
  //# sourceMappingURL=index.js.map