@superatomai/sdk-node 0.0.52 → 0.0.53

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
@@ -31,13 +31,14 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
31
31
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
32
 
33
33
  // src/utils/logger.ts
34
- var import_fs, PREFIX, LOGSTREAM, LOG_LEVEL_PRIORITY, MESSAGE_LEVEL_PRIORITY, Logger, logger;
34
+ var import_fs, PREFIX, LOG_FILE_PATH, LOGSTREAM, LOG_LEVEL_PRIORITY, MESSAGE_LEVEL_PRIORITY, Logger, logger;
35
35
  var init_logger = __esm({
36
36
  "src/utils/logger.ts"() {
37
37
  "use strict";
38
38
  import_fs = __toESM(require("fs"));
39
39
  PREFIX = "[SuperatomSDK]";
40
- LOGSTREAM = import_fs.default.createWriteStream("superatom-sdk.log", { flags: "a" });
40
+ LOG_FILE_PATH = "superatom-sdk.log";
41
+ LOGSTREAM = import_fs.default.createWriteStream(LOG_FILE_PATH, { flags: "a" });
41
42
  LOG_LEVEL_PRIORITY = {
42
43
  errors: 0,
43
44
  warnings: 1,
@@ -121,9 +122,43 @@ var init_logger = __esm({
121
122
  console.log(PREFIX, "[DEBUG]", ...args);
122
123
  }
123
124
  }
125
+ /**
126
+ * Write to log file
127
+ */
124
128
  file(...args) {
125
129
  LOGSTREAM.write(args.join(" ") + "\n");
126
130
  }
131
+ /**
132
+ * Clear the log file (call at start of new user request)
133
+ */
134
+ clearFile() {
135
+ LOGSTREAM.end();
136
+ LOGSTREAM = import_fs.default.createWriteStream(LOG_FILE_PATH, { flags: "w" });
137
+ LOGSTREAM.write(`
138
+ ${"=".repeat(80)}
139
+ `);
140
+ LOGSTREAM.write(`NEW REQUEST - ${(/* @__PURE__ */ new Date()).toISOString()}
141
+ `);
142
+ LOGSTREAM.write(`${"=".repeat(80)}
143
+
144
+ `);
145
+ }
146
+ /**
147
+ * Log LLM method prompts with clear labeling
148
+ */
149
+ logLLMPrompt(methodName, promptType, content) {
150
+ const header = `
151
+ ${"#".repeat(80)}
152
+ [LLM METHOD: ${methodName}] - ${promptType.toUpperCase()} PROMPT
153
+ ${"#".repeat(80)}
154
+ `;
155
+ LOGSTREAM.write(header);
156
+ LOGSTREAM.write(content);
157
+ LOGSTREAM.write(`
158
+ ${"#".repeat(80)}
159
+
160
+ `);
161
+ }
127
162
  };
128
163
  logger = new Logger();
129
164
  }
@@ -1041,8 +1076,20 @@ If adaptation is not possible or would fundamentally change the component:
1041
1076
 
1042
1077
  Analyze the user's request and:
1043
1078
  1. **Select the most appropriate component** from the available components list
1044
- 2. **Determine the data source**: Database query OR External tool (ERP)
1045
- 3. **Generate complete props** for the selected component including the data retrieval/modification method
1079
+ 2. **Determine the data source**: Database query OR External tool
1080
+ 3. **Generate complete props** for the selected component
1081
+
1082
+ ## Available External Tools
1083
+
1084
+ The following external tools are available:
1085
+
1086
+ {{AVAILABLE_TOOLS}}
1087
+
1088
+ When a tool is needed to complete the user's request:
1089
+ 1. **Analyze the request** to determine which tool is needed
1090
+ 2. **Extract parameters** from the user's question
1091
+ 3. **Execute the tool** by calling it with the extracted parameters
1092
+ 4. **Use the results** to configure the component (field names for axes, columns, etc.)
1046
1093
 
1047
1094
  ## Component Selection Rules
1048
1095
 
@@ -1198,7 +1245,20 @@ You MUST respond with ONLY a valid JSON object (no markdown, no code blocks):
1198
1245
 
1199
1246
  1. **Create a filter component** based on the user's request
1200
1247
  2. **Update existing components** with dynamic title/description interpolation if needed
1201
- 3. **Preserve all existing props** - the filter system handles param merging at runtime
1248
+ 3. **Set default values in component externalTool.params** that match the filter's default option
1249
+ 4. **Preserve all existing props** - the filter system handles param merging at runtime
1250
+
1251
+ ## Available External Tools
1252
+
1253
+ The following external tools are available:
1254
+
1255
+ {{AVAILABLE_TOOLS}}
1256
+
1257
+ When a tool is needed to complete the user's request:
1258
+ 1. **Analyze the request** to determine which tool is needed
1259
+ 2. **Extract parameters** from the user's question
1260
+ 3. **Execute the tool** by calling it with the extracted parameters
1261
+ 4. **Use the results** to configure the filter and components properly
1202
1262
 
1203
1263
  ## How The Filter System Works
1204
1264
 
@@ -1228,16 +1288,20 @@ Use \`{%filterKeyLabel%}\` syntax for dynamic text:
1228
1288
  ## Updating Existing Components
1229
1289
 
1230
1290
  **IMPORTANT - Parameter Handling:**
1231
- - Do NOT change existing parameter values to placeholders
1232
- - Keep all existing/default parameter values as they are
1233
1291
  - The filter system MERGES params at runtime: filter params override component defaults
1292
+ - Component's \`externalTool.params\` should have DEFAULT values matching the filter's default option
1293
+ - This ensures the component shows correct data on initial load before user changes the filter
1234
1294
 
1235
1295
  **What to modify in existing components:**
1236
1296
  - Modify \`title\` for \`{%filterKeyLabel%}\` interpolation (only if title exists)
1237
1297
  - Modify \`description\` for interpolation (only if description exists)
1238
- - Modify other props only if specifically needed for the filter to function
1298
+ - Set \`externalTool.params\` default values to match filter's default option params
1239
1299
  - Copy ALL other props exactly as provided
1240
1300
 
1301
+ **Example - Setting default params:**
1302
+ If filter has default option with params: \`{ startDate: "2025-01-01", endDate: "2025-01-31" }\`
1303
+ Then component's externalTool should have: \`{ params: { startDate: "2025-01-01", endDate: "2025-01-31" } }\`
1304
+
1241
1305
  ### Database Query Rules
1242
1306
  {{DATABASE_RULES}}
1243
1307
 
@@ -1285,10 +1349,11 @@ You MUST respond with ONLY a valid JSON object (no markdown, no code blocks):
1285
1349
  **CRITICAL RULES:**
1286
1350
  1. Return ONLY valid JSON (no markdown code blocks)
1287
1351
  2. **COPY ALL existing props** - modify title/description for interpolation only if they exist
1288
- 3. **DO NOT change parameter values** - keep existing defaults, filter merges at runtime
1352
+ 3. **Set externalTool.params defaults** to match the filter's default option params
1289
1353
  4. Use \`{%filterKeyLabel%}\` for dynamic text (NOT \`{{...}}\` or \`$...\`)
1290
1354
  5. Filter \`params\` must have ACTUAL values (real dates, strings), not placeholders
1291
1355
  6. Each filter option needs: label, value, params (and optionally isDefault)
1356
+ 7. Component default params MUST match filter default - this ensures correct initial data load
1292
1357
 
1293
1358
  ## Database Schema
1294
1359
  {{SCHEMA_DOC}}
@@ -2359,6 +2424,57 @@ var KbNodesRequestMessageSchema = import_zod3.z.object({
2359
2424
  type: import_zod3.z.literal("KB_NODES"),
2360
2425
  payload: KbNodesRequestPayloadSchema
2361
2426
  });
2427
+ var MenusQueryFiltersSchema = import_zod3.z.object({
2428
+ parentId: import_zod3.z.number().nullable().optional(),
2429
+ isActive: import_zod3.z.boolean().optional()
2430
+ });
2431
+ var MenusRequestPayloadSchema = import_zod3.z.object({
2432
+ operation: import_zod3.z.enum([
2433
+ "create",
2434
+ "update",
2435
+ "delete",
2436
+ "getAll",
2437
+ "getOne",
2438
+ "getRootMenus",
2439
+ "getChildMenus",
2440
+ "getHierarchy",
2441
+ "query",
2442
+ "reorder"
2443
+ ]),
2444
+ data: import_zod3.z.object({
2445
+ id: import_zod3.z.number().optional(),
2446
+ name: import_zod3.z.string().optional(),
2447
+ componentName: import_zod3.z.string().optional(),
2448
+ icon: import_zod3.z.string().optional(),
2449
+ userMessage: import_zod3.z.string().optional(),
2450
+ parentId: import_zod3.z.number().nullable().optional(),
2451
+ sortOrder: import_zod3.z.number().optional(),
2452
+ props: import_zod3.z.record(import_zod3.z.unknown()).optional(),
2453
+ isActive: import_zod3.z.boolean().optional(),
2454
+ // Query operation fields
2455
+ filters: MenusQueryFiltersSchema.optional(),
2456
+ limit: import_zod3.z.number().optional(),
2457
+ sort: import_zod3.z.enum(["ASC", "DESC"]).optional(),
2458
+ // Reorder operation fields
2459
+ items: import_zod3.z.array(import_zod3.z.object({
2460
+ id: import_zod3.z.number(),
2461
+ sortOrder: import_zod3.z.number()
2462
+ })).optional()
2463
+ }).optional()
2464
+ });
2465
+ var MenusRequestMessageSchema = import_zod3.z.object({
2466
+ id: import_zod3.z.string(),
2467
+ from: MessageParticipantSchema,
2468
+ type: import_zod3.z.literal("MENUS"),
2469
+ payload: MenusRequestPayloadSchema
2470
+ });
2471
+ var MenusResponsePayloadSchema = import_zod3.z.object({
2472
+ success: import_zod3.z.boolean(),
2473
+ error: import_zod3.z.string().optional(),
2474
+ data: import_zod3.z.any().optional(),
2475
+ count: import_zod3.z.number().optional(),
2476
+ message: import_zod3.z.string().optional()
2477
+ });
2362
2478
  var DashCompRequestPayloadSchema = import_zod3.z.object({
2363
2479
  prompt: import_zod3.z.string(),
2364
2480
  userId: import_zod3.z.string().optional(),
@@ -5719,35 +5835,35 @@ ${JSON.stringify(tool.requiredFields || [], null, 2)}`;
5719
5835
  let executedToolsText = "No external tools were executed for data fetching.";
5720
5836
  if (executedTools && executedTools.length > 0) {
5721
5837
  logger.info(`[${this.getProviderName()}] Passing ${executedTools.length} executed tools to component matching`);
5722
- 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) => {
5838
+ 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) => {
5723
5839
  let outputSchemaText = "Not available";
5840
+ let fieldNamesList = "";
5724
5841
  let recordCount = "unknown";
5725
5842
  if (tool.outputSchema) {
5726
5843
  const fields = tool.outputSchema.fields || [];
5727
5844
  recordCount = tool.result?._recordCount || (Array.isArray(tool.result) ? tool.result.length : "unknown");
5845
+ const numericFields = fields.filter((f) => f.type === "number").map((f) => f.name);
5846
+ const stringFields = fields.filter((f) => f.type === "string").map((f) => f.name);
5847
+ fieldNamesList = `
5848
+ \u{1F4CA} NUMERIC FIELDS (use for yAxisKey, valueKey, aggregationField): ${numericFields.join(", ") || "none"}
5849
+ \u{1F4DD} STRING FIELDS (use for xAxisKey, groupBy, nameKey): ${stringFields.join(", ") || "none"}`;
5728
5850
  const fieldsText = fields.map(
5729
- (f) => ` - name: "${f.name}" \u2190 USE THIS EXACT VALUE in config
5730
- type: ${f.type}
5731
- description: ${f.description}`
5851
+ (f) => ` "${f.name}" (${f.type}): ${f.description}`
5732
5852
  ).join("\n");
5733
5853
  outputSchemaText = `${tool.outputSchema.description}
5734
- Fields (use the "name" value in your config):
5854
+ Fields:
5735
5855
  ${fieldsText}`;
5736
5856
  }
5737
5857
  return `${idx + 1}. **${tool.name}**
5738
- toolId: "${tool.id}" (USE THIS EXACT VALUE for externalTool.toolId)
5739
- toolName: "${tool.name}" (USE THIS EXACT VALUE for externalTool.toolName)
5740
- parameters: ${JSON.stringify(tool.params || {})} (USE THESE for externalTool.parameters)
5858
+ toolId: "${tool.id}"
5859
+ toolName: "${tool.name}"
5860
+ parameters: ${JSON.stringify(tool.params || {})}
5741
5861
  recordCount: ${recordCount} rows returned
5742
- outputSchema: ${outputSchemaText}
5743
- \u26A0\uFE0F DO NOT embed this tool's data in SQL - use externalTool prop OR query database tables`;
5862
+ outputSchema: ${outputSchemaText}${fieldNamesList}`;
5744
5863
  }).join("\n\n");
5745
5864
  }
5746
5865
  const schemaDoc = schema.generateSchemaDocumentation();
5747
5866
  const databaseRules = await promptLoader.loadDatabaseRules();
5748
- logger.file("\n=============================\nText analysis response:", analysisContent);
5749
- logger.file("\n=============================\nDeferred tools:", deferredToolsText);
5750
- logger.file("\n=============================\nExecuted tools:", executedToolsText);
5751
5867
  const prompts = await promptLoader.loadPrompts("match-text-components", {
5752
5868
  ANALYSIS_CONTENT: analysisContent,
5753
5869
  AVAILABLE_COMPONENTS: availableComponentsText,
@@ -5757,7 +5873,13 @@ ${fieldsText}`;
5757
5873
  EXECUTED_TOOLS: executedToolsText
5758
5874
  });
5759
5875
  logger.debug(`[${this.getProviderName()}] Loaded match-text-components prompts`);
5760
- logger.file("\n=============================\nmatch text components system prompt:", prompts.system);
5876
+ const systemPromptStr = Array.isArray(prompts.system) ? prompts.system.join("\n") : prompts.system;
5877
+ logger.logLLMPrompt("matchComponentsFromAnalysis", "system", systemPromptStr);
5878
+ logger.logLLMPrompt("matchComponentsFromAnalysis", "user", `Text Analysis:
5879
+ ${analysisContent}
5880
+
5881
+ Executed Tools:
5882
+ ${executedToolsText}`);
5761
5883
  logCollector?.info("Matching components from text response...");
5762
5884
  let fullResponseText = "";
5763
5885
  let answerComponentExtracted = false;
@@ -5894,40 +6016,41 @@ ${fieldsText}`;
5894
6016
  if (executedTool?.outputSchema?.fields && cleanedProps.config) {
5895
6017
  const validFieldNames = executedTool.outputSchema.fields.map((f) => f.name);
5896
6018
  const validFieldNamesLower = validFieldNames.map((n) => n.toLowerCase());
5897
- const findMatchingField = (fieldName) => {
6019
+ const findMatchingField = (fieldName, configKey) => {
5898
6020
  if (!fieldName) return null;
5899
6021
  const lowerField = fieldName.toLowerCase();
5900
6022
  const exactIdx = validFieldNamesLower.indexOf(lowerField);
5901
6023
  if (exactIdx !== -1) return validFieldNames[exactIdx];
5902
- const partialMatches = [];
5903
- for (let i = 0; i < validFieldNames.length; i++) {
5904
- const validName = validFieldNames[i];
5905
- const validLower = validFieldNamesLower[i];
5906
- if (validLower.endsWith(lowerField)) {
5907
- const score = lowerField.length / validLower.length;
5908
- partialMatches.push({ name: validName, score });
5909
- continue;
5910
- }
5911
- if (lowerField.endsWith(validLower)) {
5912
- const score = validLower.length / lowerField.length;
5913
- partialMatches.push({ name: validName, score });
5914
- continue;
5915
- }
5916
- if (validLower.endsWith(lowerField) || lowerField.length <= 5 && validLower.includes(lowerField)) {
5917
- const score = lowerField.length <= 5 ? 0.3 : 0.5;
5918
- partialMatches.push({ name: validName, score });
6024
+ const containsMatches = validFieldNames.filter(
6025
+ (_, i) => validFieldNamesLower[i].includes(lowerField) || lowerField.includes(validFieldNamesLower[i])
6026
+ );
6027
+ if (containsMatches.length === 1) return containsMatches[0];
6028
+ const fieldTypes = executedTool.outputSchema.fields.reduce((acc, f) => {
6029
+ acc[f.name] = f.type;
6030
+ return acc;
6031
+ }, {});
6032
+ const numericConfigKeys = ["yAxisKey", "valueKey", "aggregationField", "sizeKey"];
6033
+ const stringConfigKeys = ["xAxisKey", "nameKey", "labelKey", "groupBy"];
6034
+ if (numericConfigKeys.includes(configKey)) {
6035
+ const numericFields = validFieldNames.filter((f) => fieldTypes[f] === "number");
6036
+ const match = numericFields.find((f) => f.toLowerCase().includes(lowerField) || lowerField.includes(f.toLowerCase()));
6037
+ if (match) return match;
6038
+ if (numericFields.length > 0) {
6039
+ logger.warn(`[${this.getProviderName()}] No match for "${fieldName}", using first numeric field: ${numericFields[0]}`);
6040
+ return numericFields[0];
5919
6041
  }
5920
6042
  }
5921
- if (partialMatches.length > 0) {
5922
- partialMatches.sort((a, b) => b.score - a.score);
5923
- if (partialMatches.length === 1 || partialMatches[0].score > partialMatches[1].score + 0.1) {
5924
- return partialMatches[0].name;
5925
- } else {
5926
- logger.warn(`[${this.getProviderName()}] Ambiguous field match for "${fieldName}": ${partialMatches.map((m) => m.name).join(", ")}`);
5927
- return null;
6043
+ if (stringConfigKeys.includes(configKey)) {
6044
+ const stringFields = validFieldNames.filter((f) => fieldTypes[f] === "string");
6045
+ const match = stringFields.find((f) => f.toLowerCase().includes(lowerField) || lowerField.includes(f.toLowerCase()));
6046
+ if (match) return match;
6047
+ if (stringFields.length > 0) {
6048
+ logger.warn(`[${this.getProviderName()}] No match for "${fieldName}", using first string field: ${stringFields[0]}`);
6049
+ return stringFields[0];
5928
6050
  }
5929
6051
  }
5930
- return null;
6052
+ logger.warn(`[${this.getProviderName()}] No match for "${fieldName}", using first field: ${validFieldNames[0]}`);
6053
+ return validFieldNames[0];
5931
6054
  };
5932
6055
  const configFieldsToValidate = [
5933
6056
  "xAxisKey",
@@ -5946,12 +6069,10 @@ ${fieldsText}`;
5946
6069
  const fieldValue = cleanedProps.config[configKey];
5947
6070
  if (fieldValue && typeof fieldValue === "string") {
5948
6071
  if (!validFieldNames.includes(fieldValue)) {
5949
- const correctedField = findMatchingField(fieldValue);
6072
+ const correctedField = findMatchingField(fieldValue, configKey);
5950
6073
  if (correctedField) {
5951
6074
  logger.warn(`[${this.getProviderName()}] Correcting config.${configKey}: "${fieldValue}" \u2192 "${correctedField}"`);
5952
6075
  cleanedProps.config[configKey] = correctedField;
5953
- } else {
5954
- logger.warn(`[${this.getProviderName()}] config.${configKey}="${fieldValue}" not found in outputSchema [${validFieldNames.join(", ")}]`);
5955
6076
  }
5956
6077
  }
5957
6078
  }
@@ -5959,7 +6080,7 @@ ${fieldsText}`;
5959
6080
  if (Array.isArray(cleanedProps.config.series)) {
5960
6081
  cleanedProps.config.series = cleanedProps.config.series.map((s) => {
5961
6082
  if (s.dataKey && typeof s.dataKey === "string" && !validFieldNames.includes(s.dataKey)) {
5962
- const correctedField = findMatchingField(s.dataKey);
6083
+ const correctedField = findMatchingField(s.dataKey, "yAxisKey");
5963
6084
  if (correctedField) {
5964
6085
  logger.warn(`[${this.getProviderName()}] Correcting series.dataKey: "${s.dataKey}" \u2192 "${correctedField}"`);
5965
6086
  return { ...s, dataKey: correctedField };
@@ -6026,6 +6147,10 @@ ${fieldsText}`;
6026
6147
  AVAILABLE_TOOLS: availableToolsDoc,
6027
6148
  SCHEMA_DOC: schemaDoc || "No database schema available"
6028
6149
  });
6150
+ const systemPrompt = Array.isArray(prompts.system) ? prompts.system.join("\n") : prompts.system;
6151
+ const userPromptText = Array.isArray(prompts.user) ? prompts.user.join("\n") : prompts.user;
6152
+ logger.logLLMPrompt("classifyQuestionCategory", "system", systemPrompt);
6153
+ logger.logLLMPrompt("classifyQuestionCategory", "user", userPromptText);
6029
6154
  const result = await LLM.stream(
6030
6155
  {
6031
6156
  sys: prompts.system,
@@ -6213,7 +6338,6 @@ ${fieldsText}`;
6213
6338
  collections,
6214
6339
  topK: 1
6215
6340
  });
6216
- logger.file("\n=============================\nknowledge base context:", knowledgeBaseContext);
6217
6341
  const prompts = await promptLoader.loadPrompts("text-response", {
6218
6342
  USER_PROMPT: userPrompt,
6219
6343
  CONVERSATION_HISTORY: conversationHistory || "No previous conversation",
@@ -6222,8 +6346,10 @@ ${fieldsText}`;
6222
6346
  KNOWLEDGE_BASE_CONTEXT: knowledgeBaseContext || "No additional knowledge base context available.",
6223
6347
  AVAILABLE_EXTERNAL_TOOLS: availableToolsDoc
6224
6348
  });
6225
- logger.file("\n=============================\nsystem prompt:", prompts.system);
6226
- logger.file("\n=============================\nuser prompt:", prompts.user);
6349
+ const sysPrompt = Array.isArray(prompts.system) ? prompts.system.join("\n") : prompts.system;
6350
+ const usrPrompt = Array.isArray(prompts.user) ? prompts.user.join("\n") : prompts.user;
6351
+ logger.logLLMPrompt("generateTextResponse", "system", sysPrompt);
6352
+ logger.logLLMPrompt("generateTextResponse", "user", usrPrompt);
6227
6353
  logger.debug(`[${this.getProviderName()}] Loaded text-response prompts with schema`);
6228
6354
  logger.debug(`[${this.getProviderName()}] System prompt length: ${prompts.system.length}, User prompt length: ${prompts.user.length}`);
6229
6355
  logCollector?.info("Generating text response with query execution capability...");
@@ -6609,7 +6735,6 @@ ${errorMsg}
6609
6735
  data: {
6610
6736
  text: textResponse,
6611
6737
  // Include the streamed text showing all attempts
6612
- matchedComponents: [],
6613
6738
  actions: [],
6614
6739
  method: `${this.getProviderName()}-text-response-max-attempts`
6615
6740
  }
@@ -6707,7 +6832,6 @@ ${errorMsg}
6707
6832
  success: true,
6708
6833
  data: {
6709
6834
  text: textResponse,
6710
- matchedComponents,
6711
6835
  component: container_componet,
6712
6836
  actions,
6713
6837
  method: `${this.getProviderName()}-text-response`
@@ -6731,7 +6855,6 @@ ${errorMsg}
6731
6855
  errors,
6732
6856
  data: {
6733
6857
  text: "I apologize, but I encountered an error while processing your question. Please try rephrasing or ask something else.",
6734
- matchedComponents: [],
6735
6858
  actions: [],
6736
6859
  method: `${this.getProviderName()}-text-response-error`
6737
6860
  }
@@ -6755,6 +6878,8 @@ ${errorMsg}
6755
6878
  const startTime = Date.now();
6756
6879
  logger.info(`[${this.getProviderName()}] handleUserRequest called for user prompt: ${userPrompt}`);
6757
6880
  logCollector?.info(`Starting request processing with mode: ${responseMode}`);
6881
+ logger.clearFile();
6882
+ logger.logLLMPrompt("handleUserRequest", "user", `User Prompt: ${userPrompt}`);
6758
6883
  try {
6759
6884
  logger.info(`[${this.getProviderName()}] Step 1: Searching previous conversations...`);
6760
6885
  logCollector?.info("Step 1: Searching for similar previous conversations...");
@@ -6762,8 +6887,8 @@ ${errorMsg}
6762
6887
  userPrompt,
6763
6888
  collections,
6764
6889
  userId,
6765
- similarityThreshold: 0.75
6766
- // 75% threshold
6890
+ similarityThreshold: 0.8
6891
+ // 80% threshold
6767
6892
  });
6768
6893
  if (conversationMatch) {
6769
6894
  logger.info(`[${this.getProviderName()}] \u2713 Found matching conversation with ${(conversationMatch.similarity * 100).toFixed(2)}% similarity`);
@@ -6788,7 +6913,6 @@ ${errorMsg}
6788
6913
  data: {
6789
6914
  text: cachedTextResponse,
6790
6915
  component: null,
6791
- matchedComponents: [],
6792
6916
  actions: conversationMatch.uiBlock?.actions || [],
6793
6917
  reasoning: `Exact match from previous general conversation`,
6794
6918
  method: `${this.getProviderName()}-semantic-match-general`,
@@ -6816,7 +6940,6 @@ ${errorMsg}
6816
6940
  data: {
6817
6941
  text: cachedTextResponse,
6818
6942
  component,
6819
- matchedComponents: component?.props?.config?.components || [],
6820
6943
  actions: cachedActions,
6821
6944
  reasoning: `Exact match from previous conversation (${(conversationMatch.similarity * 100).toFixed(2)}% similarity)`,
6822
6945
  method: `${this.getProviderName()}-semantic-match-exact`,
@@ -6852,7 +6975,6 @@ ${errorMsg}
6852
6975
  data: {
6853
6976
  text: textResponseToUse,
6854
6977
  component: adaptResult.adaptedComponent,
6855
- matchedComponents: adaptResult.adaptedComponent?.props?.config?.components || [],
6856
6978
  actions: cachedActions,
6857
6979
  reasoning: `Adapted from previous conversation: ${originalPrompt}`,
6858
6980
  method: `${this.getProviderName()}-semantic-match`,
@@ -10459,6 +10581,331 @@ function sendResponse8(id, res, sendMessage, clientId) {
10459
10581
  sendMessage(response);
10460
10582
  }
10461
10583
 
10584
+ // src/handlers/menus.ts
10585
+ init_logger();
10586
+ async function handleMenusRequest(data, collections, sendMessage) {
10587
+ const executeCollection = async (collection, op, params) => {
10588
+ const handler = collections[collection]?.[op];
10589
+ if (!handler) {
10590
+ throw new Error(`Collection operation ${collection}.${op} not found`);
10591
+ }
10592
+ return await handler(params);
10593
+ };
10594
+ try {
10595
+ const request = MenusRequestMessageSchema.parse(data);
10596
+ const { id, payload, from } = request;
10597
+ const { operation, data: requestData } = payload;
10598
+ const menuId = requestData?.id;
10599
+ const name = requestData?.name;
10600
+ const componentName = requestData?.componentName;
10601
+ const icon = requestData?.icon;
10602
+ const userMessage = requestData?.userMessage;
10603
+ const parentId = requestData?.parentId;
10604
+ const sortOrder = requestData?.sortOrder;
10605
+ const props = requestData?.props;
10606
+ const isActive = requestData?.isActive;
10607
+ const filters = requestData?.filters;
10608
+ const limit = requestData?.limit;
10609
+ const sort = requestData?.sort;
10610
+ const items = requestData?.items;
10611
+ switch (operation) {
10612
+ case "create":
10613
+ await handleCreate7(id, name, componentName, icon, userMessage, parentId, sortOrder, props, isActive, executeCollection, sendMessage, from.id);
10614
+ break;
10615
+ case "update":
10616
+ await handleUpdate7(id, menuId, name, componentName, icon, userMessage, parentId, sortOrder, props, isActive, executeCollection, sendMessage, from.id);
10617
+ break;
10618
+ case "delete":
10619
+ await handleDelete7(id, menuId, executeCollection, sendMessage, from.id);
10620
+ break;
10621
+ case "getAll":
10622
+ await handleGetAll7(id, executeCollection, sendMessage, from.id);
10623
+ break;
10624
+ case "getOne":
10625
+ await handleGetOne7(id, menuId, executeCollection, sendMessage, from.id);
10626
+ break;
10627
+ case "getRootMenus":
10628
+ await handleGetRootMenus(id, executeCollection, sendMessage, from.id);
10629
+ break;
10630
+ case "getChildMenus":
10631
+ await handleGetChildMenus(id, parentId, executeCollection, sendMessage, from.id);
10632
+ break;
10633
+ case "getHierarchy":
10634
+ await handleGetHierarchy(id, executeCollection, sendMessage, from.id);
10635
+ break;
10636
+ case "query":
10637
+ await handleQuery6(id, filters, limit, sort, executeCollection, sendMessage, from.id);
10638
+ break;
10639
+ case "reorder":
10640
+ await handleReorder(id, items, executeCollection, sendMessage, from.id);
10641
+ break;
10642
+ default:
10643
+ sendResponse9(id, {
10644
+ success: false,
10645
+ error: `Unknown operation: ${operation}`
10646
+ }, sendMessage, from.id);
10647
+ }
10648
+ } catch (error) {
10649
+ logger.error("Failed to handle menus request:", error);
10650
+ sendResponse9(null, {
10651
+ success: false,
10652
+ error: error instanceof Error ? error.message : "Unknown error occurred"
10653
+ }, sendMessage);
10654
+ }
10655
+ }
10656
+ async function handleCreate7(id, name, componentName, icon, userMessage, parentId, sortOrder, props, isActive, executeCollection, sendMessage, clientId) {
10657
+ if (!name) {
10658
+ sendResponse9(id, {
10659
+ success: false,
10660
+ error: "name is required"
10661
+ }, sendMessage, clientId);
10662
+ return;
10663
+ }
10664
+ if (!componentName) {
10665
+ sendResponse9(id, {
10666
+ success: false,
10667
+ error: "componentName is required"
10668
+ }, sendMessage, clientId);
10669
+ return;
10670
+ }
10671
+ try {
10672
+ const result = await executeCollection("menus", "create", {
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 created successfully"
10686
+ }, sendMessage, clientId);
10687
+ logger.info(`Menu created: ID ${result.data?.id}`);
10688
+ } catch (error) {
10689
+ sendResponse9(id, {
10690
+ success: false,
10691
+ error: error instanceof Error ? error.message : "Failed to create menu"
10692
+ }, sendMessage, clientId);
10693
+ }
10694
+ }
10695
+ async function handleUpdate7(id, menuId, name, componentName, icon, userMessage, parentId, sortOrder, props, isActive, 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", "update", {
10705
+ id: menuId,
10706
+ name,
10707
+ componentName,
10708
+ icon,
10709
+ userMessage,
10710
+ parentId,
10711
+ sortOrder,
10712
+ props,
10713
+ isActive
10714
+ });
10715
+ sendResponse9(id, {
10716
+ success: true,
10717
+ data: result.data,
10718
+ message: "Menu updated successfully"
10719
+ }, sendMessage, clientId);
10720
+ logger.info(`Menu updated: ID ${menuId}`);
10721
+ } catch (error) {
10722
+ sendResponse9(id, {
10723
+ success: false,
10724
+ error: error instanceof Error ? error.message : "Failed to update menu"
10725
+ }, sendMessage, clientId);
10726
+ }
10727
+ }
10728
+ async function handleDelete7(id, menuId, executeCollection, sendMessage, clientId) {
10729
+ if (!menuId) {
10730
+ sendResponse9(id, {
10731
+ success: false,
10732
+ error: "Menu ID is required"
10733
+ }, sendMessage, clientId);
10734
+ return;
10735
+ }
10736
+ try {
10737
+ const result = await executeCollection("menus", "delete", { id: menuId });
10738
+ sendResponse9(id, {
10739
+ success: true,
10740
+ data: result.data,
10741
+ message: "Menu deleted successfully"
10742
+ }, sendMessage, clientId);
10743
+ logger.info(`Menu deleted: ID ${menuId}`);
10744
+ } catch (error) {
10745
+ sendResponse9(id, {
10746
+ success: false,
10747
+ error: error instanceof Error ? error.message : "Failed to delete menu"
10748
+ }, sendMessage, clientId);
10749
+ }
10750
+ }
10751
+ async function handleGetAll7(id, executeCollection, sendMessage, clientId) {
10752
+ try {
10753
+ const result = await executeCollection("menus", "getAll", {});
10754
+ sendResponse9(id, {
10755
+ success: true,
10756
+ data: result.data,
10757
+ count: result.count,
10758
+ message: `Retrieved ${result.count} menus`
10759
+ }, sendMessage, clientId);
10760
+ logger.info(`Retrieved all menus (count: ${result.count})`);
10761
+ } catch (error) {
10762
+ sendResponse9(id, {
10763
+ success: false,
10764
+ error: error instanceof Error ? error.message : "Failed to get menus"
10765
+ }, sendMessage, clientId);
10766
+ }
10767
+ }
10768
+ async function handleGetOne7(id, menuId, executeCollection, sendMessage, clientId) {
10769
+ if (!menuId) {
10770
+ sendResponse9(id, {
10771
+ success: false,
10772
+ error: "Menu ID is required"
10773
+ }, sendMessage, clientId);
10774
+ return;
10775
+ }
10776
+ try {
10777
+ const result = await executeCollection("menus", "getOne", { id: menuId });
10778
+ sendResponse9(id, {
10779
+ success: true,
10780
+ data: result.data,
10781
+ message: `Retrieved menu ID ${menuId}`
10782
+ }, sendMessage, clientId);
10783
+ logger.info(`Retrieved menu: ID ${menuId}`);
10784
+ } catch (error) {
10785
+ sendResponse9(id, {
10786
+ success: false,
10787
+ error: error instanceof Error ? error.message : "Failed to get menu"
10788
+ }, sendMessage, clientId);
10789
+ }
10790
+ }
10791
+ async function handleGetRootMenus(id, executeCollection, sendMessage, clientId) {
10792
+ try {
10793
+ const result = await executeCollection("menus", "getRootMenus", {});
10794
+ sendResponse9(id, {
10795
+ success: true,
10796
+ data: result.data,
10797
+ count: result.count,
10798
+ message: `Retrieved ${result.count} root menus`
10799
+ }, sendMessage, clientId);
10800
+ logger.info(`Retrieved root menus (count: ${result.count})`);
10801
+ } catch (error) {
10802
+ sendResponse9(id, {
10803
+ success: false,
10804
+ error: error instanceof Error ? error.message : "Failed to get root menus"
10805
+ }, sendMessage, clientId);
10806
+ }
10807
+ }
10808
+ async function handleGetChildMenus(id, parentId, executeCollection, sendMessage, clientId) {
10809
+ if (parentId === void 0 || parentId === null) {
10810
+ sendResponse9(id, {
10811
+ success: false,
10812
+ error: "parentId is required"
10813
+ }, sendMessage, clientId);
10814
+ return;
10815
+ }
10816
+ try {
10817
+ const result = await executeCollection("menus", "getChildMenus", { parentId });
10818
+ sendResponse9(id, {
10819
+ success: true,
10820
+ data: result.data,
10821
+ count: result.count,
10822
+ message: `Retrieved ${result.count} child menus for parent ${parentId}`
10823
+ }, sendMessage, clientId);
10824
+ logger.info(`Retrieved child menus for parent ${parentId} (count: ${result.count})`);
10825
+ } catch (error) {
10826
+ sendResponse9(id, {
10827
+ success: false,
10828
+ error: error instanceof Error ? error.message : "Failed to get child menus"
10829
+ }, sendMessage, clientId);
10830
+ }
10831
+ }
10832
+ async function handleGetHierarchy(id, executeCollection, sendMessage, clientId) {
10833
+ try {
10834
+ const result = await executeCollection("menus", "getHierarchy", {});
10835
+ sendResponse9(id, {
10836
+ success: true,
10837
+ data: result.data,
10838
+ count: result.count,
10839
+ message: `Retrieved menus hierarchy with ${result.count} root items`
10840
+ }, sendMessage, clientId);
10841
+ logger.info(`Retrieved menus hierarchy (root count: ${result.count})`);
10842
+ } catch (error) {
10843
+ sendResponse9(id, {
10844
+ success: false,
10845
+ error: error instanceof Error ? error.message : "Failed to get menus hierarchy"
10846
+ }, sendMessage, clientId);
10847
+ }
10848
+ }
10849
+ async function handleQuery6(id, filters, limit, sort, executeCollection, sendMessage, clientId) {
10850
+ try {
10851
+ const result = await executeCollection("menus", "query", {
10852
+ filters: filters || {},
10853
+ limit,
10854
+ sort
10855
+ });
10856
+ sendResponse9(id, {
10857
+ success: true,
10858
+ data: result.data,
10859
+ count: result.count,
10860
+ message: `Query returned ${result.count} menus`
10861
+ }, sendMessage, clientId);
10862
+ logger.info(`Query returned ${result.count} menus`);
10863
+ } catch (error) {
10864
+ sendResponse9(id, {
10865
+ success: false,
10866
+ error: error instanceof Error ? error.message : "Failed to query menus"
10867
+ }, sendMessage, clientId);
10868
+ }
10869
+ }
10870
+ async function handleReorder(id, items, executeCollection, sendMessage, clientId) {
10871
+ if (!items || !Array.isArray(items) || items.length === 0) {
10872
+ sendResponse9(id, {
10873
+ success: false,
10874
+ error: "items array is required"
10875
+ }, sendMessage, clientId);
10876
+ return;
10877
+ }
10878
+ try {
10879
+ const result = await executeCollection("menus", "reorder", { items });
10880
+ sendResponse9(id, {
10881
+ success: true,
10882
+ data: result.data,
10883
+ message: `Reordered ${items.length} menus successfully`
10884
+ }, sendMessage, clientId);
10885
+ logger.info(`Reordered ${items.length} menus`);
10886
+ } catch (error) {
10887
+ sendResponse9(id, {
10888
+ success: false,
10889
+ error: error instanceof Error ? error.message : "Failed to reorder menus"
10890
+ }, sendMessage, clientId);
10891
+ }
10892
+ }
10893
+ function sendResponse9(id, res, sendMessage, clientId) {
10894
+ const response = {
10895
+ id: id || "unknown",
10896
+ type: "MENUS_RES",
10897
+ from: { type: "data-agent" },
10898
+ to: {
10899
+ type: "admin",
10900
+ id: clientId
10901
+ },
10902
+ payload: {
10903
+ ...res
10904
+ }
10905
+ };
10906
+ sendMessage(response);
10907
+ }
10908
+
10462
10909
  // src/dashComp/index.ts
10463
10910
  init_logger();
10464
10911
 
@@ -10585,37 +11032,74 @@ async function pickComponentWithLLM(prompt, components, anthropicApiKey, groqApi
10585
11032
  return { success: false, errors };
10586
11033
  }
10587
11034
  logger.info(`[DASH_COMP_REQ] Using model: ${model}`);
10588
- const result = await LLM.stream(
11035
+ const executedTools = [];
11036
+ const llmTools = (tools || []).map((tool) => ({
11037
+ name: tool.id,
11038
+ description: tool.description,
11039
+ input_schema: {
11040
+ type: "object",
11041
+ properties: Object.fromEntries(
11042
+ 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` }])
11043
+ ),
11044
+ additionalProperties: false
11045
+ }
11046
+ }));
11047
+ const toolHandler = async (toolName, toolInput) => {
11048
+ const tool = tools?.find((t) => t.id === toolName);
11049
+ if (!tool) {
11050
+ throw new Error(`Unknown tool: ${toolName}`);
11051
+ }
11052
+ logger.info(`[DASH_COMP_REQ] Executing external tool: ${tool.name} (${tool.id})`);
11053
+ const result2 = await tool.fn(toolInput);
11054
+ executedTools.push({
11055
+ id: tool.id,
11056
+ name: tool.name,
11057
+ params: toolInput,
11058
+ result: result2,
11059
+ outputSchema: tool.outputSchema
11060
+ });
11061
+ logger.info(`[DASH_COMP_REQ] Tool ${tool.name} executed successfully`);
11062
+ return JSON.stringify(result2, null, 2);
11063
+ };
11064
+ const result = await LLM.streamWithTools(
10589
11065
  {
10590
11066
  sys: prompts.system,
10591
11067
  user: prompts.user
10592
11068
  },
11069
+ llmTools,
11070
+ toolHandler,
10593
11071
  {
10594
11072
  model,
10595
11073
  maxTokens: 8192,
10596
11074
  temperature: 0.2,
10597
11075
  apiKey
10598
11076
  },
10599
- true
10600
- // Parse as JSON
11077
+ 5
11078
+ // max iterations
10601
11079
  );
11080
+ const jsonMatch = result.match(/\{[\s\S]*\}/);
11081
+ const parsedResult = jsonMatch ? JSON.parse(jsonMatch[0]) : null;
11082
+ if (!parsedResult) {
11083
+ errors.push("Failed to parse LLM response as JSON");
11084
+ return { success: false, errors };
11085
+ }
10602
11086
  logger.debug("[DASH_COMP_REQ] LLM response received");
10603
- logger.file("[DASH_COMP_REQ] LLM response:", JSON.stringify(result, null, 2));
10604
- if (!result.componentId || !result.props) {
11087
+ logger.file("[DASH_COMP_REQ] LLM response:", JSON.stringify(parsedResult, null, 2));
11088
+ if (!parsedResult.componentId || !parsedResult.props) {
10605
11089
  errors.push("Invalid LLM response: missing componentId or props");
10606
11090
  userPromptErrorLogger.logError("DASH_COMP_REQ", "Invalid LLM response structure", {
10607
11091
  prompt,
10608
- result,
10609
- missingFields: { componentId: !result.componentId, props: !result.props }
11092
+ result: parsedResult,
11093
+ missingFields: { componentId: !parsedResult.componentId, props: !parsedResult.props }
10610
11094
  });
10611
11095
  return { success: false, errors };
10612
11096
  }
10613
- const originalComponent = components.find((c) => c.name === result.componentName);
11097
+ const originalComponent = components.find((c) => c.name === parsedResult.componentName);
10614
11098
  if (!originalComponent) {
10615
- errors.push(`Component ${result.componentName} not found in available components`);
11099
+ errors.push(`Component ${parsedResult.componentName} not found in available components`);
10616
11100
  userPromptErrorLogger.logError("DASH_COMP_REQ", "Component not found", {
10617
11101
  prompt,
10618
- componentName: result.componentName,
11102
+ componentName: parsedResult.componentName,
10619
11103
  availableComponentNames: components.map((c) => c.name)
10620
11104
  });
10621
11105
  return { success: false, errors };
@@ -10624,23 +11108,27 @@ async function pickComponentWithLLM(prompt, components, anthropicApiKey, groqApi
10624
11108
  ...originalComponent,
10625
11109
  props: {
10626
11110
  ...originalComponent.props,
10627
- ...result.props
11111
+ ...parsedResult.props
10628
11112
  }
10629
11113
  };
10630
11114
  logger.info(`[DASH_COMP_REQ] Successfully picked component: ${finalComponent.name} (${finalComponent.type})`);
10631
- if (result.props.query) {
11115
+ if (parsedResult.props.query) {
10632
11116
  logger.info(`[DASH_COMP_REQ] Data source: Database query`);
10633
11117
  }
10634
- if (result.props.externalTool) {
10635
- logger.info(`[DASH_COMP_REQ] Data source: External tool - ${result.props.externalTool.toolName}`);
11118
+ if (parsedResult.props.externalTool) {
11119
+ logger.info(`[DASH_COMP_REQ] Data source: External tool - ${parsedResult.props.externalTool.toolName}`);
11120
+ }
11121
+ if (executedTools.length > 0) {
11122
+ logger.info(`[DASH_COMP_REQ] Executed ${executedTools.length} external tool(s): ${executedTools.map((t) => t.name).join(", ")}`);
10636
11123
  }
10637
11124
  return {
10638
11125
  success: true,
10639
11126
  data: {
10640
11127
  component: finalComponent,
10641
- reasoning: result.reasoning || "Component selected based on user prompt",
10642
- dataSource: result.props.query ? "database" : result.props.externalTool ? "external_tool" : "none",
10643
- isUpdate: result.isUpdate || false
11128
+ reasoning: parsedResult.reasoning || "Component selected based on user prompt",
11129
+ dataSource: parsedResult.props.query ? "database" : parsedResult.props.externalTool ? "external_tool" : "none",
11130
+ isUpdate: parsedResult.isUpdate || false,
11131
+ executedTools: executedTools.length > 0 ? executedTools : void 0
10644
11132
  },
10645
11133
  errors: []
10646
11134
  };
@@ -10693,11 +11181,49 @@ async function createFilterWithLLM(prompt, components, existingComponents, anthr
10693
11181
  return { success: false, errors };
10694
11182
  }
10695
11183
  logger.info(`[DASH_COMP_REQ:FILTER] Using model: ${model}`);
10696
- const result = await LLM.stream(
11184
+ const executedTools = [];
11185
+ const llmTools = (tools || []).map((tool) => ({
11186
+ name: tool.id,
11187
+ description: tool.description,
11188
+ input_schema: {
11189
+ type: "object",
11190
+ properties: Object.fromEntries(
11191
+ 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` }])
11192
+ ),
11193
+ additionalProperties: false
11194
+ }
11195
+ }));
11196
+ const toolHandler = async (toolName, toolInput) => {
11197
+ const tool = tools?.find((t) => t.id === toolName);
11198
+ if (!tool) {
11199
+ throw new Error(`Unknown tool: ${toolName}`);
11200
+ }
11201
+ logger.info(`[DASH_COMP_REQ:FILTER] Executing external tool: ${tool.name} (${tool.id})`);
11202
+ const result2 = await tool.fn(toolInput);
11203
+ executedTools.push({
11204
+ id: tool.id,
11205
+ name: tool.name,
11206
+ params: toolInput,
11207
+ result: result2,
11208
+ outputSchema: tool.outputSchema
11209
+ });
11210
+ logger.info(`[DASH_COMP_REQ:FILTER] Tool ${tool.name} executed successfully`);
11211
+ return JSON.stringify(result2, null, 2);
11212
+ };
11213
+ const rawResult = await LLM.streamWithTools(
10697
11214
  { sys: prompts.system, user: prompts.user },
11215
+ llmTools,
11216
+ toolHandler,
10698
11217
  { model, maxTokens: 16384, temperature: 0.2, apiKey },
10699
- true
11218
+ 5
11219
+ // max iterations
10700
11220
  );
11221
+ const jsonMatch = rawResult.match(/\{[\s\S]*\}/);
11222
+ const result = jsonMatch ? JSON.parse(jsonMatch[0]) : null;
11223
+ if (!result) {
11224
+ errors.push("Failed to parse LLM response as JSON");
11225
+ return { success: false, errors };
11226
+ }
10701
11227
  logger.debug("[DASH_COMP_REQ:FILTER] LLM response received");
10702
11228
  logger.file("[DASH_COMP_REQ:FILTER] LLM response:", JSON.stringify(result, null, 2));
10703
11229
  if (!result.filterComponent) {
@@ -10711,13 +11237,17 @@ async function createFilterWithLLM(prompt, components, existingComponents, anthr
10711
11237
  }
10712
11238
  logger.info(`[DASH_COMP_REQ:FILTER] Successfully created filter: ${result.filterComponent.componentType}`);
10713
11239
  logger.info(`[DASH_COMP_REQ:FILTER] Updated ${result.updatedComponents?.length || 0} components`);
11240
+ if (executedTools.length > 0) {
11241
+ logger.info(`[DASH_COMP_REQ:FILTER] Executed ${executedTools.length} external tool(s): ${executedTools.map((t) => t.name).join(", ")}`);
11242
+ }
10714
11243
  return {
10715
11244
  success: true,
10716
11245
  data: {
10717
11246
  filterComponent: result.filterComponent,
10718
11247
  updatedComponents: result.updatedComponents || [],
10719
11248
  filterBindings: result.filterBindings || {},
10720
- reasoning: result.reasoning || "Filter created based on user prompt"
11249
+ reasoning: result.reasoning || "Filter created based on user prompt",
11250
+ executedTools: executedTools.length > 0 ? executedTools : void 0
10721
11251
  },
10722
11252
  errors: []
10723
11253
  };
@@ -11895,6 +12425,11 @@ var SuperatomSDK = class {
11895
12425
  logger.error("Failed to handle KB nodes request:", error);
11896
12426
  });
11897
12427
  break;
12428
+ case "MENUS":
12429
+ handleMenusRequest(parsed, this.collections, (msg) => this.send(msg)).catch((error) => {
12430
+ logger.error("Failed to handle menus request:", error);
12431
+ });
12432
+ break;
11898
12433
  case "DASH_COMP_REQ":
11899
12434
  handleDashCompRequest(parsed, this.components, (msg) => this.send(msg), this.anthropicApiKey, this.groqApiKey, this.geminiApiKey, this.openaiApiKey, this.llmProviders, this.collections, this.tools).catch((error) => {
11900
12435
  logger.error("Failed to handle dash comp request:", error);