@superatomai/sdk-node 0.0.52 → 0.0.54

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.mjs CHANGED
@@ -10,12 +10,13 @@ var __export = (target, all) => {
10
10
 
11
11
  // src/utils/logger.ts
12
12
  import fs from "fs";
13
- var PREFIX, LOGSTREAM, LOG_LEVEL_PRIORITY, MESSAGE_LEVEL_PRIORITY, Logger, logger;
13
+ var PREFIX, LOG_FILE_PATH, LOGSTREAM, LOG_LEVEL_PRIORITY, MESSAGE_LEVEL_PRIORITY, Logger, logger;
14
14
  var init_logger = __esm({
15
15
  "src/utils/logger.ts"() {
16
16
  "use strict";
17
17
  PREFIX = "[SuperatomSDK]";
18
- LOGSTREAM = fs.createWriteStream("superatom-sdk.log", { flags: "a" });
18
+ LOG_FILE_PATH = "superatom-sdk.log";
19
+ LOGSTREAM = fs.createWriteStream(LOG_FILE_PATH, { flags: "a" });
19
20
  LOG_LEVEL_PRIORITY = {
20
21
  errors: 0,
21
22
  warnings: 1,
@@ -99,9 +100,58 @@ var init_logger = __esm({
99
100
  console.log(PREFIX, "[DEBUG]", ...args);
100
101
  }
101
102
  }
103
+ /**
104
+ * Write to log file
105
+ */
102
106
  file(...args) {
103
107
  LOGSTREAM.write(args.join(" ") + "\n");
104
108
  }
109
+ /**
110
+ * Clear the log file (call at start of new user request)
111
+ */
112
+ clearFile() {
113
+ LOGSTREAM.end();
114
+ LOGSTREAM = fs.createWriteStream(LOG_FILE_PATH, { flags: "w" });
115
+ LOGSTREAM.write(`
116
+ ${"=".repeat(80)}
117
+ `);
118
+ LOGSTREAM.write(`NEW REQUEST - ${(/* @__PURE__ */ new Date()).toISOString()}
119
+ `);
120
+ LOGSTREAM.write(`${"=".repeat(80)}
121
+
122
+ `);
123
+ }
124
+ /**
125
+ * Log LLM method prompts with clear labeling
126
+ */
127
+ logLLMPrompt(methodName, promptType, content) {
128
+ const header = `
129
+ ${"#".repeat(80)}
130
+ [LLM METHOD: ${methodName}] - ${promptType.toUpperCase()} PROMPT
131
+ ${"#".repeat(80)}
132
+ `;
133
+ LOGSTREAM.write(header);
134
+ let contentStr;
135
+ if (typeof content === "string") {
136
+ contentStr = content;
137
+ } else if (Array.isArray(content)) {
138
+ contentStr = content.map((item) => {
139
+ if (typeof item === "string") return item;
140
+ if (item.text) return item.text;
141
+ if (item.content) return typeof item.content === "string" ? item.content : JSON.stringify(item.content, null, 2);
142
+ return JSON.stringify(item, null, 2);
143
+ }).join("\n\n");
144
+ } else if (typeof content === "object") {
145
+ contentStr = JSON.stringify(content, null, 2);
146
+ } else {
147
+ contentStr = String(content);
148
+ }
149
+ LOGSTREAM.write(contentStr);
150
+ LOGSTREAM.write(`
151
+ ${"#".repeat(80)}
152
+
153
+ `);
154
+ }
105
155
  };
106
156
  logger = new Logger();
107
157
  }
@@ -1019,8 +1069,20 @@ If adaptation is not possible or would fundamentally change the component:
1019
1069
 
1020
1070
  Analyze the user's request and:
1021
1071
  1. **Select the most appropriate component** from the available components list
1022
- 2. **Determine the data source**: Database query OR External tool (ERP)
1023
- 3. **Generate complete props** for the selected component including the data retrieval/modification method
1072
+ 2. **Determine the data source**: Database query OR External tool
1073
+ 3. **Generate complete props** for the selected component
1074
+
1075
+ ## Available External Tools
1076
+
1077
+ The following external tools are available:
1078
+
1079
+ {{AVAILABLE_TOOLS}}
1080
+
1081
+ When a tool is needed to complete the user's request:
1082
+ 1. **Analyze the request** to determine which tool is needed
1083
+ 2. **Extract parameters** from the user's question
1084
+ 3. **Execute the tool** by calling it with the extracted parameters
1085
+ 4. **Use the results** to configure the component (field names for axes, columns, etc.)
1024
1086
 
1025
1087
  ## Component Selection Rules
1026
1088
 
@@ -1176,7 +1238,20 @@ You MUST respond with ONLY a valid JSON object (no markdown, no code blocks):
1176
1238
 
1177
1239
  1. **Create a filter component** based on the user's request
1178
1240
  2. **Update existing components** with dynamic title/description interpolation if needed
1179
- 3. **Preserve all existing props** - the filter system handles param merging at runtime
1241
+ 3. **Set default values in component externalTool.params** that match the filter's default option
1242
+ 4. **Preserve all existing props** - the filter system handles param merging at runtime
1243
+
1244
+ ## Available External Tools
1245
+
1246
+ The following external tools are available:
1247
+
1248
+ {{AVAILABLE_TOOLS}}
1249
+
1250
+ When a tool is needed to complete the user's request:
1251
+ 1. **Analyze the request** to determine which tool is needed
1252
+ 2. **Extract parameters** from the user's question
1253
+ 3. **Execute the tool** by calling it with the extracted parameters
1254
+ 4. **Use the results** to configure the filter and components properly
1180
1255
 
1181
1256
  ## How The Filter System Works
1182
1257
 
@@ -1206,16 +1281,20 @@ Use \`{%filterKeyLabel%}\` syntax for dynamic text:
1206
1281
  ## Updating Existing Components
1207
1282
 
1208
1283
  **IMPORTANT - Parameter Handling:**
1209
- - Do NOT change existing parameter values to placeholders
1210
- - Keep all existing/default parameter values as they are
1211
1284
  - The filter system MERGES params at runtime: filter params override component defaults
1285
+ - Component's \`externalTool.params\` should have DEFAULT values matching the filter's default option
1286
+ - This ensures the component shows correct data on initial load before user changes the filter
1212
1287
 
1213
1288
  **What to modify in existing components:**
1214
1289
  - Modify \`title\` for \`{%filterKeyLabel%}\` interpolation (only if title exists)
1215
1290
  - Modify \`description\` for interpolation (only if description exists)
1216
- - Modify other props only if specifically needed for the filter to function
1291
+ - Set \`externalTool.params\` default values to match filter's default option params
1217
1292
  - Copy ALL other props exactly as provided
1218
1293
 
1294
+ **Example - Setting default params:**
1295
+ If filter has default option with params: \`{ startDate: "2025-01-01", endDate: "2025-01-31" }\`
1296
+ Then component's externalTool should have: \`{ params: { startDate: "2025-01-01", endDate: "2025-01-31" } }\`
1297
+
1219
1298
  ### Database Query Rules
1220
1299
  {{DATABASE_RULES}}
1221
1300
 
@@ -1263,10 +1342,11 @@ You MUST respond with ONLY a valid JSON object (no markdown, no code blocks):
1263
1342
  **CRITICAL RULES:**
1264
1343
  1. Return ONLY valid JSON (no markdown code blocks)
1265
1344
  2. **COPY ALL existing props** - modify title/description for interpolation only if they exist
1266
- 3. **DO NOT change parameter values** - keep existing defaults, filter merges at runtime
1345
+ 3. **Set externalTool.params defaults** to match the filter's default option params
1267
1346
  4. Use \`{%filterKeyLabel%}\` for dynamic text (NOT \`{{...}}\` or \`$...\`)
1268
1347
  5. Filter \`params\` must have ACTUAL values (real dates, strings), not placeholders
1269
1348
  6. Each filter option needs: label, value, params (and optionally isDefault)
1349
+ 7. Component default params MUST match filter default - this ensures correct initial data load
1270
1350
 
1271
1351
  ## Database Schema
1272
1352
  {{SCHEMA_DOC}}
@@ -2309,6 +2389,57 @@ var KbNodesRequestMessageSchema = z3.object({
2309
2389
  type: z3.literal("KB_NODES"),
2310
2390
  payload: KbNodesRequestPayloadSchema
2311
2391
  });
2392
+ var MenusQueryFiltersSchema = z3.object({
2393
+ parentId: z3.number().nullable().optional(),
2394
+ isActive: z3.boolean().optional()
2395
+ });
2396
+ var MenusRequestPayloadSchema = z3.object({
2397
+ operation: z3.enum([
2398
+ "create",
2399
+ "update",
2400
+ "delete",
2401
+ "getAll",
2402
+ "getOne",
2403
+ "getRootMenus",
2404
+ "getChildMenus",
2405
+ "getHierarchy",
2406
+ "query",
2407
+ "reorder"
2408
+ ]),
2409
+ data: z3.object({
2410
+ id: z3.number().optional(),
2411
+ name: z3.string().optional(),
2412
+ componentName: z3.string().optional(),
2413
+ icon: z3.string().optional(),
2414
+ userMessage: z3.string().optional(),
2415
+ parentId: z3.number().nullable().optional(),
2416
+ sortOrder: z3.number().optional(),
2417
+ props: z3.record(z3.unknown()).optional(),
2418
+ isActive: z3.boolean().optional(),
2419
+ // Query operation fields
2420
+ filters: MenusQueryFiltersSchema.optional(),
2421
+ limit: z3.number().optional(),
2422
+ sort: z3.enum(["ASC", "DESC"]).optional(),
2423
+ // Reorder operation fields
2424
+ items: z3.array(z3.object({
2425
+ id: z3.number(),
2426
+ sortOrder: z3.number()
2427
+ })).optional()
2428
+ }).optional()
2429
+ });
2430
+ var MenusRequestMessageSchema = z3.object({
2431
+ id: z3.string(),
2432
+ from: MessageParticipantSchema,
2433
+ type: z3.literal("MENUS"),
2434
+ payload: MenusRequestPayloadSchema
2435
+ });
2436
+ var MenusResponsePayloadSchema = z3.object({
2437
+ success: z3.boolean(),
2438
+ error: z3.string().optional(),
2439
+ data: z3.any().optional(),
2440
+ count: z3.number().optional(),
2441
+ message: z3.string().optional()
2442
+ });
2312
2443
  var DashCompRequestPayloadSchema = z3.object({
2313
2444
  prompt: z3.string(),
2314
2445
  userId: z3.string().optional(),
@@ -5669,35 +5800,35 @@ ${JSON.stringify(tool.requiredFields || [], null, 2)}`;
5669
5800
  let executedToolsText = "No external tools were executed for data fetching.";
5670
5801
  if (executedTools && executedTools.length > 0) {
5671
5802
  logger.info(`[${this.getProviderName()}] Passing ${executedTools.length} executed tools to component matching`);
5672
- 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 outputSchema fields to set correct config keys (xAxisKey, yAxisKey, valueKey, nameKey, etc.)**\n**IMPORTANT: Use the result data to populate deferred tool parameters when applicable.**\n\n" + executedTools.map((tool, idx) => {
5803
+ 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 ONLY the field names listed in outputSchema for config keys.**\n\n" + executedTools.map((tool, idx) => {
5673
5804
  let outputSchemaText = "Not available";
5805
+ let fieldNamesList = "";
5674
5806
  let recordCount = "unknown";
5675
5807
  if (tool.outputSchema) {
5676
5808
  const fields = tool.outputSchema.fields || [];
5677
5809
  recordCount = tool.result?._recordCount || (Array.isArray(tool.result) ? tool.result.length : "unknown");
5810
+ const numericFields = fields.filter((f) => f.type === "number").map((f) => f.name);
5811
+ const stringFields = fields.filter((f) => f.type === "string").map((f) => f.name);
5812
+ fieldNamesList = `
5813
+ \u{1F4CA} NUMERIC FIELDS (use for yAxisKey, valueKey, aggregationField): ${numericFields.join(", ") || "none"}
5814
+ \u{1F4DD} STRING FIELDS (use for xAxisKey, groupBy, nameKey): ${stringFields.join(", ") || "none"}`;
5678
5815
  const fieldsText = fields.map(
5679
- (f) => ` - name: "${f.name}" \u2190 USE THIS EXACT VALUE in config
5680
- type: ${f.type}
5681
- description: ${f.description}`
5816
+ (f) => ` "${f.name}" (${f.type}): ${f.description}`
5682
5817
  ).join("\n");
5683
5818
  outputSchemaText = `${tool.outputSchema.description}
5684
- Fields (use the "name" value in your config):
5819
+ Fields:
5685
5820
  ${fieldsText}`;
5686
5821
  }
5687
5822
  return `${idx + 1}. **${tool.name}**
5688
- toolId: "${tool.id}" (USE THIS EXACT VALUE for externalTool.toolId)
5689
- toolName: "${tool.name}" (USE THIS EXACT VALUE for externalTool.toolName)
5690
- parameters: ${JSON.stringify(tool.params || {})} (USE THESE for externalTool.parameters)
5823
+ toolId: "${tool.id}"
5824
+ toolName: "${tool.name}"
5825
+ parameters: ${JSON.stringify(tool.params || {})}
5691
5826
  recordCount: ${recordCount} rows returned
5692
- outputSchema: ${outputSchemaText}
5693
- \u26A0\uFE0F DO NOT embed this tool's data in SQL - use externalTool prop OR query database tables`;
5827
+ outputSchema: ${outputSchemaText}${fieldNamesList}`;
5694
5828
  }).join("\n\n");
5695
5829
  }
5696
5830
  const schemaDoc = schema.generateSchemaDocumentation();
5697
5831
  const databaseRules = await promptLoader.loadDatabaseRules();
5698
- logger.file("\n=============================\nText analysis response:", analysisContent);
5699
- logger.file("\n=============================\nDeferred tools:", deferredToolsText);
5700
- logger.file("\n=============================\nExecuted tools:", executedToolsText);
5701
5832
  const prompts = await promptLoader.loadPrompts("match-text-components", {
5702
5833
  ANALYSIS_CONTENT: analysisContent,
5703
5834
  AVAILABLE_COMPONENTS: availableComponentsText,
@@ -5707,7 +5838,13 @@ ${fieldsText}`;
5707
5838
  EXECUTED_TOOLS: executedToolsText
5708
5839
  });
5709
5840
  logger.debug(`[${this.getProviderName()}] Loaded match-text-components prompts`);
5710
- logger.file("\n=============================\nmatch text components system prompt:", prompts.system);
5841
+ const systemPromptStr = Array.isArray(prompts.system) ? prompts.system.join("\n") : prompts.system;
5842
+ logger.logLLMPrompt("matchComponentsFromAnalysis", "system", systemPromptStr);
5843
+ logger.logLLMPrompt("matchComponentsFromAnalysis", "user", `Text Analysis:
5844
+ ${analysisContent}
5845
+
5846
+ Executed Tools:
5847
+ ${executedToolsText}`);
5711
5848
  logCollector?.info("Matching components from text response...");
5712
5849
  let fullResponseText = "";
5713
5850
  let answerComponentExtracted = false;
@@ -5844,40 +5981,41 @@ ${fieldsText}`;
5844
5981
  if (executedTool?.outputSchema?.fields && cleanedProps.config) {
5845
5982
  const validFieldNames = executedTool.outputSchema.fields.map((f) => f.name);
5846
5983
  const validFieldNamesLower = validFieldNames.map((n) => n.toLowerCase());
5847
- const findMatchingField = (fieldName) => {
5984
+ const findMatchingField = (fieldName, configKey) => {
5848
5985
  if (!fieldName) return null;
5849
5986
  const lowerField = fieldName.toLowerCase();
5850
5987
  const exactIdx = validFieldNamesLower.indexOf(lowerField);
5851
5988
  if (exactIdx !== -1) return validFieldNames[exactIdx];
5852
- const partialMatches = [];
5853
- for (let i = 0; i < validFieldNames.length; i++) {
5854
- const validName = validFieldNames[i];
5855
- const validLower = validFieldNamesLower[i];
5856
- if (validLower.endsWith(lowerField)) {
5857
- const score = lowerField.length / validLower.length;
5858
- partialMatches.push({ name: validName, score });
5859
- continue;
5860
- }
5861
- if (lowerField.endsWith(validLower)) {
5862
- const score = validLower.length / lowerField.length;
5863
- partialMatches.push({ name: validName, score });
5864
- continue;
5865
- }
5866
- if (validLower.endsWith(lowerField) || lowerField.length <= 5 && validLower.includes(lowerField)) {
5867
- const score = lowerField.length <= 5 ? 0.3 : 0.5;
5868
- partialMatches.push({ name: validName, score });
5989
+ const containsMatches = validFieldNames.filter(
5990
+ (_, i) => validFieldNamesLower[i].includes(lowerField) || lowerField.includes(validFieldNamesLower[i])
5991
+ );
5992
+ if (containsMatches.length === 1) return containsMatches[0];
5993
+ const fieldTypes = executedTool.outputSchema.fields.reduce((acc, f) => {
5994
+ acc[f.name] = f.type;
5995
+ return acc;
5996
+ }, {});
5997
+ const numericConfigKeys = ["yAxisKey", "valueKey", "aggregationField", "sizeKey"];
5998
+ const stringConfigKeys = ["xAxisKey", "nameKey", "labelKey", "groupBy"];
5999
+ if (numericConfigKeys.includes(configKey)) {
6000
+ const numericFields = validFieldNames.filter((f) => fieldTypes[f] === "number");
6001
+ const match = numericFields.find((f) => f.toLowerCase().includes(lowerField) || lowerField.includes(f.toLowerCase()));
6002
+ if (match) return match;
6003
+ if (numericFields.length > 0) {
6004
+ logger.warn(`[${this.getProviderName()}] No match for "${fieldName}", using first numeric field: ${numericFields[0]}`);
6005
+ return numericFields[0];
5869
6006
  }
5870
6007
  }
5871
- if (partialMatches.length > 0) {
5872
- partialMatches.sort((a, b) => b.score - a.score);
5873
- if (partialMatches.length === 1 || partialMatches[0].score > partialMatches[1].score + 0.1) {
5874
- return partialMatches[0].name;
5875
- } else {
5876
- logger.warn(`[${this.getProviderName()}] Ambiguous field match for "${fieldName}": ${partialMatches.map((m) => m.name).join(", ")}`);
5877
- return null;
6008
+ if (stringConfigKeys.includes(configKey)) {
6009
+ const stringFields = validFieldNames.filter((f) => fieldTypes[f] === "string");
6010
+ const match = stringFields.find((f) => f.toLowerCase().includes(lowerField) || lowerField.includes(f.toLowerCase()));
6011
+ if (match) return match;
6012
+ if (stringFields.length > 0) {
6013
+ logger.warn(`[${this.getProviderName()}] No match for "${fieldName}", using first string field: ${stringFields[0]}`);
6014
+ return stringFields[0];
5878
6015
  }
5879
6016
  }
5880
- return null;
6017
+ logger.warn(`[${this.getProviderName()}] No match for "${fieldName}", using first field: ${validFieldNames[0]}`);
6018
+ return validFieldNames[0];
5881
6019
  };
5882
6020
  const configFieldsToValidate = [
5883
6021
  "xAxisKey",
@@ -5896,12 +6034,10 @@ ${fieldsText}`;
5896
6034
  const fieldValue = cleanedProps.config[configKey];
5897
6035
  if (fieldValue && typeof fieldValue === "string") {
5898
6036
  if (!validFieldNames.includes(fieldValue)) {
5899
- const correctedField = findMatchingField(fieldValue);
6037
+ const correctedField = findMatchingField(fieldValue, configKey);
5900
6038
  if (correctedField) {
5901
6039
  logger.warn(`[${this.getProviderName()}] Correcting config.${configKey}: "${fieldValue}" \u2192 "${correctedField}"`);
5902
6040
  cleanedProps.config[configKey] = correctedField;
5903
- } else {
5904
- logger.warn(`[${this.getProviderName()}] config.${configKey}="${fieldValue}" not found in outputSchema [${validFieldNames.join(", ")}]`);
5905
6041
  }
5906
6042
  }
5907
6043
  }
@@ -5909,7 +6045,7 @@ ${fieldsText}`;
5909
6045
  if (Array.isArray(cleanedProps.config.series)) {
5910
6046
  cleanedProps.config.series = cleanedProps.config.series.map((s) => {
5911
6047
  if (s.dataKey && typeof s.dataKey === "string" && !validFieldNames.includes(s.dataKey)) {
5912
- const correctedField = findMatchingField(s.dataKey);
6048
+ const correctedField = findMatchingField(s.dataKey, "yAxisKey");
5913
6049
  if (correctedField) {
5914
6050
  logger.warn(`[${this.getProviderName()}] Correcting series.dataKey: "${s.dataKey}" \u2192 "${correctedField}"`);
5915
6051
  return { ...s, dataKey: correctedField };
@@ -5976,6 +6112,10 @@ ${fieldsText}`;
5976
6112
  AVAILABLE_TOOLS: availableToolsDoc,
5977
6113
  SCHEMA_DOC: schemaDoc || "No database schema available"
5978
6114
  });
6115
+ const systemPrompt = Array.isArray(prompts.system) ? prompts.system.join("\n") : prompts.system;
6116
+ const userPromptText = Array.isArray(prompts.user) ? prompts.user.join("\n") : prompts.user;
6117
+ logger.logLLMPrompt("classifyQuestionCategory", "system", systemPrompt);
6118
+ logger.logLLMPrompt("classifyQuestionCategory", "user", userPromptText);
5979
6119
  const result = await LLM.stream(
5980
6120
  {
5981
6121
  sys: prompts.system,
@@ -6163,7 +6303,6 @@ ${fieldsText}`;
6163
6303
  collections,
6164
6304
  topK: 1
6165
6305
  });
6166
- logger.file("\n=============================\nknowledge base context:", knowledgeBaseContext);
6167
6306
  const prompts = await promptLoader.loadPrompts("text-response", {
6168
6307
  USER_PROMPT: userPrompt,
6169
6308
  CONVERSATION_HISTORY: conversationHistory || "No previous conversation",
@@ -6172,8 +6311,10 @@ ${fieldsText}`;
6172
6311
  KNOWLEDGE_BASE_CONTEXT: knowledgeBaseContext || "No additional knowledge base context available.",
6173
6312
  AVAILABLE_EXTERNAL_TOOLS: availableToolsDoc
6174
6313
  });
6175
- logger.file("\n=============================\nsystem prompt:", prompts.system);
6176
- logger.file("\n=============================\nuser prompt:", prompts.user);
6314
+ const sysPrompt = Array.isArray(prompts.system) ? prompts.system.join("\n") : prompts.system;
6315
+ const usrPrompt = Array.isArray(prompts.user) ? prompts.user.join("\n") : prompts.user;
6316
+ logger.logLLMPrompt("generateTextResponse", "system", sysPrompt);
6317
+ logger.logLLMPrompt("generateTextResponse", "user", usrPrompt);
6177
6318
  logger.debug(`[${this.getProviderName()}] Loaded text-response prompts with schema`);
6178
6319
  logger.debug(`[${this.getProviderName()}] System prompt length: ${prompts.system.length}, User prompt length: ${prompts.user.length}`);
6179
6320
  logCollector?.info("Generating text response with query execution capability...");
@@ -6559,7 +6700,6 @@ ${errorMsg}
6559
6700
  data: {
6560
6701
  text: textResponse,
6561
6702
  // Include the streamed text showing all attempts
6562
- matchedComponents: [],
6563
6703
  actions: [],
6564
6704
  method: `${this.getProviderName()}-text-response-max-attempts`
6565
6705
  }
@@ -6657,7 +6797,6 @@ ${errorMsg}
6657
6797
  success: true,
6658
6798
  data: {
6659
6799
  text: textResponse,
6660
- matchedComponents,
6661
6800
  component: container_componet,
6662
6801
  actions,
6663
6802
  method: `${this.getProviderName()}-text-response`
@@ -6681,7 +6820,6 @@ ${errorMsg}
6681
6820
  errors,
6682
6821
  data: {
6683
6822
  text: "I apologize, but I encountered an error while processing your question. Please try rephrasing or ask something else.",
6684
- matchedComponents: [],
6685
6823
  actions: [],
6686
6824
  method: `${this.getProviderName()}-text-response-error`
6687
6825
  }
@@ -6705,6 +6843,8 @@ ${errorMsg}
6705
6843
  const startTime = Date.now();
6706
6844
  logger.info(`[${this.getProviderName()}] handleUserRequest called for user prompt: ${userPrompt}`);
6707
6845
  logCollector?.info(`Starting request processing with mode: ${responseMode}`);
6846
+ logger.clearFile();
6847
+ logger.logLLMPrompt("handleUserRequest", "user", `User Prompt: ${userPrompt}`);
6708
6848
  try {
6709
6849
  logger.info(`[${this.getProviderName()}] Step 1: Searching previous conversations...`);
6710
6850
  logCollector?.info("Step 1: Searching for similar previous conversations...");
@@ -6712,8 +6852,8 @@ ${errorMsg}
6712
6852
  userPrompt,
6713
6853
  collections,
6714
6854
  userId,
6715
- similarityThreshold: 0.75
6716
- // 75% threshold
6855
+ similarityThreshold: 0.8
6856
+ // 80% threshold
6717
6857
  });
6718
6858
  if (conversationMatch) {
6719
6859
  logger.info(`[${this.getProviderName()}] \u2713 Found matching conversation with ${(conversationMatch.similarity * 100).toFixed(2)}% similarity`);
@@ -6738,7 +6878,6 @@ ${errorMsg}
6738
6878
  data: {
6739
6879
  text: cachedTextResponse,
6740
6880
  component: null,
6741
- matchedComponents: [],
6742
6881
  actions: conversationMatch.uiBlock?.actions || [],
6743
6882
  reasoning: `Exact match from previous general conversation`,
6744
6883
  method: `${this.getProviderName()}-semantic-match-general`,
@@ -6766,7 +6905,6 @@ ${errorMsg}
6766
6905
  data: {
6767
6906
  text: cachedTextResponse,
6768
6907
  component,
6769
- matchedComponents: component?.props?.config?.components || [],
6770
6908
  actions: cachedActions,
6771
6909
  reasoning: `Exact match from previous conversation (${(conversationMatch.similarity * 100).toFixed(2)}% similarity)`,
6772
6910
  method: `${this.getProviderName()}-semantic-match-exact`,
@@ -6802,7 +6940,6 @@ ${errorMsg}
6802
6940
  data: {
6803
6941
  text: textResponseToUse,
6804
6942
  component: adaptResult.adaptedComponent,
6805
- matchedComponents: adaptResult.adaptedComponent?.props?.config?.components || [],
6806
6943
  actions: cachedActions,
6807
6944
  reasoning: `Adapted from previous conversation: ${originalPrompt}`,
6808
6945
  method: `${this.getProviderName()}-semantic-match`,
@@ -6853,6 +6990,8 @@ ${errorMsg}
6853
6990
  executionReason: t.executionReason || "",
6854
6991
  requiredFields: t.requiredFields || [],
6855
6992
  userProvidedData: t.userProvidedData || null,
6993
+ // CRITICAL: Include outputSchema from real tool for component config generation
6994
+ outputSchema: realTool?.outputSchema,
6856
6995
  fn: (() => {
6857
6996
  if (realTool) {
6858
6997
  logger.info(`[${this.getProviderName()}] Using real tool implementation for ${t.type}`);
@@ -10409,6 +10548,331 @@ function sendResponse8(id, res, sendMessage, clientId) {
10409
10548
  sendMessage(response);
10410
10549
  }
10411
10550
 
10551
+ // src/handlers/menus.ts
10552
+ init_logger();
10553
+ async function handleMenusRequest(data, collections, sendMessage) {
10554
+ const executeCollection = async (collection, op, params) => {
10555
+ const handler = collections[collection]?.[op];
10556
+ if (!handler) {
10557
+ throw new Error(`Collection operation ${collection}.${op} not found`);
10558
+ }
10559
+ return await handler(params);
10560
+ };
10561
+ try {
10562
+ const request = MenusRequestMessageSchema.parse(data);
10563
+ const { id, payload, from } = request;
10564
+ const { operation, data: requestData } = payload;
10565
+ const menuId = requestData?.id;
10566
+ const name = requestData?.name;
10567
+ const componentName = requestData?.componentName;
10568
+ const icon = requestData?.icon;
10569
+ const userMessage = requestData?.userMessage;
10570
+ const parentId = requestData?.parentId;
10571
+ const sortOrder = requestData?.sortOrder;
10572
+ const props = requestData?.props;
10573
+ const isActive = requestData?.isActive;
10574
+ const filters = requestData?.filters;
10575
+ const limit = requestData?.limit;
10576
+ const sort = requestData?.sort;
10577
+ const items = requestData?.items;
10578
+ switch (operation) {
10579
+ case "create":
10580
+ await handleCreate7(id, name, componentName, icon, userMessage, parentId, sortOrder, props, isActive, executeCollection, sendMessage, from.id);
10581
+ break;
10582
+ case "update":
10583
+ await handleUpdate7(id, menuId, name, componentName, icon, userMessage, parentId, sortOrder, props, isActive, executeCollection, sendMessage, from.id);
10584
+ break;
10585
+ case "delete":
10586
+ await handleDelete7(id, menuId, executeCollection, sendMessage, from.id);
10587
+ break;
10588
+ case "getAll":
10589
+ await handleGetAll7(id, executeCollection, sendMessage, from.id);
10590
+ break;
10591
+ case "getOne":
10592
+ await handleGetOne7(id, menuId, executeCollection, sendMessage, from.id);
10593
+ break;
10594
+ case "getRootMenus":
10595
+ await handleGetRootMenus(id, executeCollection, sendMessage, from.id);
10596
+ break;
10597
+ case "getChildMenus":
10598
+ await handleGetChildMenus(id, parentId, executeCollection, sendMessage, from.id);
10599
+ break;
10600
+ case "getHierarchy":
10601
+ await handleGetHierarchy(id, executeCollection, sendMessage, from.id);
10602
+ break;
10603
+ case "query":
10604
+ await handleQuery6(id, filters, limit, sort, executeCollection, sendMessage, from.id);
10605
+ break;
10606
+ case "reorder":
10607
+ await handleReorder(id, items, executeCollection, sendMessage, from.id);
10608
+ break;
10609
+ default:
10610
+ sendResponse9(id, {
10611
+ success: false,
10612
+ error: `Unknown operation: ${operation}`
10613
+ }, sendMessage, from.id);
10614
+ }
10615
+ } catch (error) {
10616
+ logger.error("Failed to handle menus request:", error);
10617
+ sendResponse9(null, {
10618
+ success: false,
10619
+ error: error instanceof Error ? error.message : "Unknown error occurred"
10620
+ }, sendMessage);
10621
+ }
10622
+ }
10623
+ async function handleCreate7(id, name, componentName, icon, userMessage, parentId, sortOrder, props, isActive, executeCollection, sendMessage, clientId) {
10624
+ if (!name) {
10625
+ sendResponse9(id, {
10626
+ success: false,
10627
+ error: "name is required"
10628
+ }, sendMessage, clientId);
10629
+ return;
10630
+ }
10631
+ if (!componentName) {
10632
+ sendResponse9(id, {
10633
+ success: false,
10634
+ error: "componentName is required"
10635
+ }, sendMessage, clientId);
10636
+ return;
10637
+ }
10638
+ try {
10639
+ const result = await executeCollection("menus", "create", {
10640
+ name,
10641
+ componentName,
10642
+ icon,
10643
+ userMessage,
10644
+ parentId,
10645
+ sortOrder,
10646
+ props,
10647
+ isActive
10648
+ });
10649
+ sendResponse9(id, {
10650
+ success: true,
10651
+ data: result.data,
10652
+ message: "Menu created successfully"
10653
+ }, sendMessage, clientId);
10654
+ logger.info(`Menu created: ID ${result.data?.id}`);
10655
+ } catch (error) {
10656
+ sendResponse9(id, {
10657
+ success: false,
10658
+ error: error instanceof Error ? error.message : "Failed to create menu"
10659
+ }, sendMessage, clientId);
10660
+ }
10661
+ }
10662
+ async function handleUpdate7(id, menuId, name, componentName, icon, userMessage, parentId, sortOrder, props, isActive, executeCollection, sendMessage, clientId) {
10663
+ if (!menuId) {
10664
+ sendResponse9(id, {
10665
+ success: false,
10666
+ error: "Menu ID is required"
10667
+ }, sendMessage, clientId);
10668
+ return;
10669
+ }
10670
+ try {
10671
+ const result = await executeCollection("menus", "update", {
10672
+ id: menuId,
10673
+ name,
10674
+ componentName,
10675
+ icon,
10676
+ userMessage,
10677
+ parentId,
10678
+ sortOrder,
10679
+ props,
10680
+ isActive
10681
+ });
10682
+ sendResponse9(id, {
10683
+ success: true,
10684
+ data: result.data,
10685
+ message: "Menu updated successfully"
10686
+ }, sendMessage, clientId);
10687
+ logger.info(`Menu updated: ID ${menuId}`);
10688
+ } catch (error) {
10689
+ sendResponse9(id, {
10690
+ success: false,
10691
+ error: error instanceof Error ? error.message : "Failed to update menu"
10692
+ }, sendMessage, clientId);
10693
+ }
10694
+ }
10695
+ async function handleDelete7(id, menuId, executeCollection, sendMessage, clientId) {
10696
+ if (!menuId) {
10697
+ sendResponse9(id, {
10698
+ success: false,
10699
+ error: "Menu ID is required"
10700
+ }, sendMessage, clientId);
10701
+ return;
10702
+ }
10703
+ try {
10704
+ const result = await executeCollection("menus", "delete", { id: menuId });
10705
+ sendResponse9(id, {
10706
+ success: true,
10707
+ data: result.data,
10708
+ message: "Menu deleted successfully"
10709
+ }, sendMessage, clientId);
10710
+ logger.info(`Menu deleted: ID ${menuId}`);
10711
+ } catch (error) {
10712
+ sendResponse9(id, {
10713
+ success: false,
10714
+ error: error instanceof Error ? error.message : "Failed to delete menu"
10715
+ }, sendMessage, clientId);
10716
+ }
10717
+ }
10718
+ async function handleGetAll7(id, executeCollection, sendMessage, clientId) {
10719
+ try {
10720
+ const result = await executeCollection("menus", "getAll", {});
10721
+ sendResponse9(id, {
10722
+ success: true,
10723
+ data: result.data,
10724
+ count: result.count,
10725
+ message: `Retrieved ${result.count} menus`
10726
+ }, sendMessage, clientId);
10727
+ logger.info(`Retrieved all menus (count: ${result.count})`);
10728
+ } catch (error) {
10729
+ sendResponse9(id, {
10730
+ success: false,
10731
+ error: error instanceof Error ? error.message : "Failed to get menus"
10732
+ }, sendMessage, clientId);
10733
+ }
10734
+ }
10735
+ async function handleGetOne7(id, menuId, executeCollection, sendMessage, clientId) {
10736
+ if (!menuId) {
10737
+ sendResponse9(id, {
10738
+ success: false,
10739
+ error: "Menu ID is required"
10740
+ }, sendMessage, clientId);
10741
+ return;
10742
+ }
10743
+ try {
10744
+ const result = await executeCollection("menus", "getOne", { id: menuId });
10745
+ sendResponse9(id, {
10746
+ success: true,
10747
+ data: result.data,
10748
+ message: `Retrieved menu ID ${menuId}`
10749
+ }, sendMessage, clientId);
10750
+ logger.info(`Retrieved menu: ID ${menuId}`);
10751
+ } catch (error) {
10752
+ sendResponse9(id, {
10753
+ success: false,
10754
+ error: error instanceof Error ? error.message : "Failed to get menu"
10755
+ }, sendMessage, clientId);
10756
+ }
10757
+ }
10758
+ async function handleGetRootMenus(id, executeCollection, sendMessage, clientId) {
10759
+ try {
10760
+ const result = await executeCollection("menus", "getRootMenus", {});
10761
+ sendResponse9(id, {
10762
+ success: true,
10763
+ data: result.data,
10764
+ count: result.count,
10765
+ message: `Retrieved ${result.count} root menus`
10766
+ }, sendMessage, clientId);
10767
+ logger.info(`Retrieved root menus (count: ${result.count})`);
10768
+ } catch (error) {
10769
+ sendResponse9(id, {
10770
+ success: false,
10771
+ error: error instanceof Error ? error.message : "Failed to get root menus"
10772
+ }, sendMessage, clientId);
10773
+ }
10774
+ }
10775
+ async function handleGetChildMenus(id, parentId, executeCollection, sendMessage, clientId) {
10776
+ if (parentId === void 0 || parentId === null) {
10777
+ sendResponse9(id, {
10778
+ success: false,
10779
+ error: "parentId is required"
10780
+ }, sendMessage, clientId);
10781
+ return;
10782
+ }
10783
+ try {
10784
+ const result = await executeCollection("menus", "getChildMenus", { parentId });
10785
+ sendResponse9(id, {
10786
+ success: true,
10787
+ data: result.data,
10788
+ count: result.count,
10789
+ message: `Retrieved ${result.count} child menus for parent ${parentId}`
10790
+ }, sendMessage, clientId);
10791
+ logger.info(`Retrieved child menus for parent ${parentId} (count: ${result.count})`);
10792
+ } catch (error) {
10793
+ sendResponse9(id, {
10794
+ success: false,
10795
+ error: error instanceof Error ? error.message : "Failed to get child menus"
10796
+ }, sendMessage, clientId);
10797
+ }
10798
+ }
10799
+ async function handleGetHierarchy(id, executeCollection, sendMessage, clientId) {
10800
+ try {
10801
+ const result = await executeCollection("menus", "getHierarchy", {});
10802
+ sendResponse9(id, {
10803
+ success: true,
10804
+ data: result.data,
10805
+ count: result.count,
10806
+ message: `Retrieved menus hierarchy with ${result.count} root items`
10807
+ }, sendMessage, clientId);
10808
+ logger.info(`Retrieved menus hierarchy (root count: ${result.count})`);
10809
+ } catch (error) {
10810
+ sendResponse9(id, {
10811
+ success: false,
10812
+ error: error instanceof Error ? error.message : "Failed to get menus hierarchy"
10813
+ }, sendMessage, clientId);
10814
+ }
10815
+ }
10816
+ async function handleQuery6(id, filters, limit, sort, executeCollection, sendMessage, clientId) {
10817
+ try {
10818
+ const result = await executeCollection("menus", "query", {
10819
+ filters: filters || {},
10820
+ limit,
10821
+ sort
10822
+ });
10823
+ sendResponse9(id, {
10824
+ success: true,
10825
+ data: result.data,
10826
+ count: result.count,
10827
+ message: `Query returned ${result.count} menus`
10828
+ }, sendMessage, clientId);
10829
+ logger.info(`Query returned ${result.count} menus`);
10830
+ } catch (error) {
10831
+ sendResponse9(id, {
10832
+ success: false,
10833
+ error: error instanceof Error ? error.message : "Failed to query menus"
10834
+ }, sendMessage, clientId);
10835
+ }
10836
+ }
10837
+ async function handleReorder(id, items, executeCollection, sendMessage, clientId) {
10838
+ if (!items || !Array.isArray(items) || items.length === 0) {
10839
+ sendResponse9(id, {
10840
+ success: false,
10841
+ error: "items array is required"
10842
+ }, sendMessage, clientId);
10843
+ return;
10844
+ }
10845
+ try {
10846
+ const result = await executeCollection("menus", "reorder", { items });
10847
+ sendResponse9(id, {
10848
+ success: true,
10849
+ data: result.data,
10850
+ message: `Reordered ${items.length} menus successfully`
10851
+ }, sendMessage, clientId);
10852
+ logger.info(`Reordered ${items.length} menus`);
10853
+ } catch (error) {
10854
+ sendResponse9(id, {
10855
+ success: false,
10856
+ error: error instanceof Error ? error.message : "Failed to reorder menus"
10857
+ }, sendMessage, clientId);
10858
+ }
10859
+ }
10860
+ function sendResponse9(id, res, sendMessage, clientId) {
10861
+ const response = {
10862
+ id: id || "unknown",
10863
+ type: "MENUS_RES",
10864
+ from: { type: "data-agent" },
10865
+ to: {
10866
+ type: "admin",
10867
+ id: clientId
10868
+ },
10869
+ payload: {
10870
+ ...res
10871
+ }
10872
+ };
10873
+ sendMessage(response);
10874
+ }
10875
+
10412
10876
  // src/dashComp/index.ts
10413
10877
  init_logger();
10414
10878
 
@@ -10535,37 +10999,74 @@ async function pickComponentWithLLM(prompt, components, anthropicApiKey, groqApi
10535
10999
  return { success: false, errors };
10536
11000
  }
10537
11001
  logger.info(`[DASH_COMP_REQ] Using model: ${model}`);
10538
- const result = await LLM.stream(
11002
+ const executedTools = [];
11003
+ const llmTools = (tools || []).map((tool) => ({
11004
+ name: tool.id,
11005
+ description: tool.description,
11006
+ input_schema: {
11007
+ type: "object",
11008
+ properties: Object.fromEntries(
11009
+ Object.entries(tool.params || {}).map(([key, val]) => [key, { type: typeof val === "string" && ["string", "number", "boolean", "integer"].includes(val.toLowerCase()) ? val.toLowerCase() : "string", description: `${key} parameter` }])
11010
+ ),
11011
+ additionalProperties: false
11012
+ }
11013
+ }));
11014
+ const toolHandler = async (toolName, toolInput) => {
11015
+ const tool = tools?.find((t) => t.id === toolName);
11016
+ if (!tool) {
11017
+ throw new Error(`Unknown tool: ${toolName}`);
11018
+ }
11019
+ logger.info(`[DASH_COMP_REQ] Executing external tool: ${tool.name} (${tool.id})`);
11020
+ const result2 = await tool.fn(toolInput);
11021
+ executedTools.push({
11022
+ id: tool.id,
11023
+ name: tool.name,
11024
+ params: toolInput,
11025
+ result: result2,
11026
+ outputSchema: tool.outputSchema
11027
+ });
11028
+ logger.info(`[DASH_COMP_REQ] Tool ${tool.name} executed successfully`);
11029
+ return JSON.stringify(result2, null, 2);
11030
+ };
11031
+ const result = await LLM.streamWithTools(
10539
11032
  {
10540
11033
  sys: prompts.system,
10541
11034
  user: prompts.user
10542
11035
  },
11036
+ llmTools,
11037
+ toolHandler,
10543
11038
  {
10544
11039
  model,
10545
11040
  maxTokens: 8192,
10546
11041
  temperature: 0.2,
10547
11042
  apiKey
10548
11043
  },
10549
- true
10550
- // Parse as JSON
11044
+ 5
11045
+ // max iterations
10551
11046
  );
11047
+ const jsonMatch = result.match(/\{[\s\S]*\}/);
11048
+ const parsedResult = jsonMatch ? JSON.parse(jsonMatch[0]) : null;
11049
+ if (!parsedResult) {
11050
+ errors.push("Failed to parse LLM response as JSON");
11051
+ return { success: false, errors };
11052
+ }
10552
11053
  logger.debug("[DASH_COMP_REQ] LLM response received");
10553
- logger.file("[DASH_COMP_REQ] LLM response:", JSON.stringify(result, null, 2));
10554
- if (!result.componentId || !result.props) {
11054
+ logger.file("[DASH_COMP_REQ] LLM response:", JSON.stringify(parsedResult, null, 2));
11055
+ if (!parsedResult.componentId || !parsedResult.props) {
10555
11056
  errors.push("Invalid LLM response: missing componentId or props");
10556
11057
  userPromptErrorLogger.logError("DASH_COMP_REQ", "Invalid LLM response structure", {
10557
11058
  prompt,
10558
- result,
10559
- missingFields: { componentId: !result.componentId, props: !result.props }
11059
+ result: parsedResult,
11060
+ missingFields: { componentId: !parsedResult.componentId, props: !parsedResult.props }
10560
11061
  });
10561
11062
  return { success: false, errors };
10562
11063
  }
10563
- const originalComponent = components.find((c) => c.name === result.componentName);
11064
+ const originalComponent = components.find((c) => c.name === parsedResult.componentName);
10564
11065
  if (!originalComponent) {
10565
- errors.push(`Component ${result.componentName} not found in available components`);
11066
+ errors.push(`Component ${parsedResult.componentName} not found in available components`);
10566
11067
  userPromptErrorLogger.logError("DASH_COMP_REQ", "Component not found", {
10567
11068
  prompt,
10568
- componentName: result.componentName,
11069
+ componentName: parsedResult.componentName,
10569
11070
  availableComponentNames: components.map((c) => c.name)
10570
11071
  });
10571
11072
  return { success: false, errors };
@@ -10574,23 +11075,27 @@ async function pickComponentWithLLM(prompt, components, anthropicApiKey, groqApi
10574
11075
  ...originalComponent,
10575
11076
  props: {
10576
11077
  ...originalComponent.props,
10577
- ...result.props
11078
+ ...parsedResult.props
10578
11079
  }
10579
11080
  };
10580
11081
  logger.info(`[DASH_COMP_REQ] Successfully picked component: ${finalComponent.name} (${finalComponent.type})`);
10581
- if (result.props.query) {
11082
+ if (parsedResult.props.query) {
10582
11083
  logger.info(`[DASH_COMP_REQ] Data source: Database query`);
10583
11084
  }
10584
- if (result.props.externalTool) {
10585
- logger.info(`[DASH_COMP_REQ] Data source: External tool - ${result.props.externalTool.toolName}`);
11085
+ if (parsedResult.props.externalTool) {
11086
+ logger.info(`[DASH_COMP_REQ] Data source: External tool - ${parsedResult.props.externalTool.toolName}`);
11087
+ }
11088
+ if (executedTools.length > 0) {
11089
+ logger.info(`[DASH_COMP_REQ] Executed ${executedTools.length} external tool(s): ${executedTools.map((t) => t.name).join(", ")}`);
10586
11090
  }
10587
11091
  return {
10588
11092
  success: true,
10589
11093
  data: {
10590
11094
  component: finalComponent,
10591
- reasoning: result.reasoning || "Component selected based on user prompt",
10592
- dataSource: result.props.query ? "database" : result.props.externalTool ? "external_tool" : "none",
10593
- isUpdate: result.isUpdate || false
11095
+ reasoning: parsedResult.reasoning || "Component selected based on user prompt",
11096
+ dataSource: parsedResult.props.query ? "database" : parsedResult.props.externalTool ? "external_tool" : "none",
11097
+ isUpdate: parsedResult.isUpdate || false,
11098
+ executedTools: executedTools.length > 0 ? executedTools : void 0
10594
11099
  },
10595
11100
  errors: []
10596
11101
  };
@@ -10643,11 +11148,49 @@ async function createFilterWithLLM(prompt, components, existingComponents, anthr
10643
11148
  return { success: false, errors };
10644
11149
  }
10645
11150
  logger.info(`[DASH_COMP_REQ:FILTER] Using model: ${model}`);
10646
- const result = await LLM.stream(
11151
+ const executedTools = [];
11152
+ const llmTools = (tools || []).map((tool) => ({
11153
+ name: tool.id,
11154
+ description: tool.description,
11155
+ input_schema: {
11156
+ type: "object",
11157
+ properties: Object.fromEntries(
11158
+ Object.entries(tool.params || {}).map(([key, val]) => [key, { type: typeof val === "string" && ["string", "number", "boolean", "integer"].includes(val.toLowerCase()) ? val.toLowerCase() : "string", description: `${key} parameter` }])
11159
+ ),
11160
+ additionalProperties: false
11161
+ }
11162
+ }));
11163
+ const toolHandler = async (toolName, toolInput) => {
11164
+ const tool = tools?.find((t) => t.id === toolName);
11165
+ if (!tool) {
11166
+ throw new Error(`Unknown tool: ${toolName}`);
11167
+ }
11168
+ logger.info(`[DASH_COMP_REQ:FILTER] Executing external tool: ${tool.name} (${tool.id})`);
11169
+ const result2 = await tool.fn(toolInput);
11170
+ executedTools.push({
11171
+ id: tool.id,
11172
+ name: tool.name,
11173
+ params: toolInput,
11174
+ result: result2,
11175
+ outputSchema: tool.outputSchema
11176
+ });
11177
+ logger.info(`[DASH_COMP_REQ:FILTER] Tool ${tool.name} executed successfully`);
11178
+ return JSON.stringify(result2, null, 2);
11179
+ };
11180
+ const rawResult = await LLM.streamWithTools(
10647
11181
  { sys: prompts.system, user: prompts.user },
11182
+ llmTools,
11183
+ toolHandler,
10648
11184
  { model, maxTokens: 16384, temperature: 0.2, apiKey },
10649
- true
11185
+ 5
11186
+ // max iterations
10650
11187
  );
11188
+ const jsonMatch = rawResult.match(/\{[\s\S]*\}/);
11189
+ const result = jsonMatch ? JSON.parse(jsonMatch[0]) : null;
11190
+ if (!result) {
11191
+ errors.push("Failed to parse LLM response as JSON");
11192
+ return { success: false, errors };
11193
+ }
10651
11194
  logger.debug("[DASH_COMP_REQ:FILTER] LLM response received");
10652
11195
  logger.file("[DASH_COMP_REQ:FILTER] LLM response:", JSON.stringify(result, null, 2));
10653
11196
  if (!result.filterComponent) {
@@ -10661,13 +11204,17 @@ async function createFilterWithLLM(prompt, components, existingComponents, anthr
10661
11204
  }
10662
11205
  logger.info(`[DASH_COMP_REQ:FILTER] Successfully created filter: ${result.filterComponent.componentType}`);
10663
11206
  logger.info(`[DASH_COMP_REQ:FILTER] Updated ${result.updatedComponents?.length || 0} components`);
11207
+ if (executedTools.length > 0) {
11208
+ logger.info(`[DASH_COMP_REQ:FILTER] Executed ${executedTools.length} external tool(s): ${executedTools.map((t) => t.name).join(", ")}`);
11209
+ }
10664
11210
  return {
10665
11211
  success: true,
10666
11212
  data: {
10667
11213
  filterComponent: result.filterComponent,
10668
11214
  updatedComponents: result.updatedComponents || [],
10669
11215
  filterBindings: result.filterBindings || {},
10670
- reasoning: result.reasoning || "Filter created based on user prompt"
11216
+ reasoning: result.reasoning || "Filter created based on user prompt",
11217
+ executedTools: executedTools.length > 0 ? executedTools : void 0
10671
11218
  },
10672
11219
  errors: []
10673
11220
  };
@@ -11845,6 +12392,11 @@ var SuperatomSDK = class {
11845
12392
  logger.error("Failed to handle KB nodes request:", error);
11846
12393
  });
11847
12394
  break;
12395
+ case "MENUS":
12396
+ handleMenusRequest(parsed, this.collections, (msg) => this.send(msg)).catch((error) => {
12397
+ logger.error("Failed to handle menus request:", error);
12398
+ });
12399
+ break;
11848
12400
  case "DASH_COMP_REQ":
11849
12401
  handleDashCompRequest(parsed, this.components, (msg) => this.send(msg), this.anthropicApiKey, this.groqApiKey, this.geminiApiKey, this.openaiApiKey, this.llmProviders, this.collections, this.tools).catch((error) => {
11850
12402
  logger.error("Failed to handle dash comp request:", error);