@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.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,58 @@ 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
+ let contentStr;
157
+ if (typeof content === "string") {
158
+ contentStr = content;
159
+ } else if (Array.isArray(content)) {
160
+ contentStr = content.map((item) => {
161
+ if (typeof item === "string") return item;
162
+ if (item.text) return item.text;
163
+ if (item.content) return typeof item.content === "string" ? item.content : JSON.stringify(item.content, null, 2);
164
+ return JSON.stringify(item, null, 2);
165
+ }).join("\n\n");
166
+ } else if (typeof content === "object") {
167
+ contentStr = JSON.stringify(content, null, 2);
168
+ } else {
169
+ contentStr = String(content);
170
+ }
171
+ LOGSTREAM.write(contentStr);
172
+ LOGSTREAM.write(`
173
+ ${"#".repeat(80)}
174
+
175
+ `);
176
+ }
127
177
  };
128
178
  logger = new Logger();
129
179
  }
@@ -1041,8 +1091,20 @@ If adaptation is not possible or would fundamentally change the component:
1041
1091
 
1042
1092
  Analyze the user's request and:
1043
1093
  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
1094
+ 2. **Determine the data source**: Database query OR External tool
1095
+ 3. **Generate complete props** for the selected component
1096
+
1097
+ ## Available External Tools
1098
+
1099
+ The following external tools are available:
1100
+
1101
+ {{AVAILABLE_TOOLS}}
1102
+
1103
+ When a tool is needed to complete the user's request:
1104
+ 1. **Analyze the request** to determine which tool is needed
1105
+ 2. **Extract parameters** from the user's question
1106
+ 3. **Execute the tool** by calling it with the extracted parameters
1107
+ 4. **Use the results** to configure the component (field names for axes, columns, etc.)
1046
1108
 
1047
1109
  ## Component Selection Rules
1048
1110
 
@@ -1198,7 +1260,20 @@ You MUST respond with ONLY a valid JSON object (no markdown, no code blocks):
1198
1260
 
1199
1261
  1. **Create a filter component** based on the user's request
1200
1262
  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
1263
+ 3. **Set default values in component externalTool.params** that match the filter's default option
1264
+ 4. **Preserve all existing props** - the filter system handles param merging at runtime
1265
+
1266
+ ## Available External Tools
1267
+
1268
+ The following external tools are available:
1269
+
1270
+ {{AVAILABLE_TOOLS}}
1271
+
1272
+ When a tool is needed to complete the user's request:
1273
+ 1. **Analyze the request** to determine which tool is needed
1274
+ 2. **Extract parameters** from the user's question
1275
+ 3. **Execute the tool** by calling it with the extracted parameters
1276
+ 4. **Use the results** to configure the filter and components properly
1202
1277
 
1203
1278
  ## How The Filter System Works
1204
1279
 
@@ -1228,16 +1303,20 @@ Use \`{%filterKeyLabel%}\` syntax for dynamic text:
1228
1303
  ## Updating Existing Components
1229
1304
 
1230
1305
  **IMPORTANT - Parameter Handling:**
1231
- - Do NOT change existing parameter values to placeholders
1232
- - Keep all existing/default parameter values as they are
1233
1306
  - The filter system MERGES params at runtime: filter params override component defaults
1307
+ - Component's \`externalTool.params\` should have DEFAULT values matching the filter's default option
1308
+ - This ensures the component shows correct data on initial load before user changes the filter
1234
1309
 
1235
1310
  **What to modify in existing components:**
1236
1311
  - Modify \`title\` for \`{%filterKeyLabel%}\` interpolation (only if title exists)
1237
1312
  - Modify \`description\` for interpolation (only if description exists)
1238
- - Modify other props only if specifically needed for the filter to function
1313
+ - Set \`externalTool.params\` default values to match filter's default option params
1239
1314
  - Copy ALL other props exactly as provided
1240
1315
 
1316
+ **Example - Setting default params:**
1317
+ If filter has default option with params: \`{ startDate: "2025-01-01", endDate: "2025-01-31" }\`
1318
+ Then component's externalTool should have: \`{ params: { startDate: "2025-01-01", endDate: "2025-01-31" } }\`
1319
+
1241
1320
  ### Database Query Rules
1242
1321
  {{DATABASE_RULES}}
1243
1322
 
@@ -1285,10 +1364,11 @@ You MUST respond with ONLY a valid JSON object (no markdown, no code blocks):
1285
1364
  **CRITICAL RULES:**
1286
1365
  1. Return ONLY valid JSON (no markdown code blocks)
1287
1366
  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
1367
+ 3. **Set externalTool.params defaults** to match the filter's default option params
1289
1368
  4. Use \`{%filterKeyLabel%}\` for dynamic text (NOT \`{{...}}\` or \`$...\`)
1290
1369
  5. Filter \`params\` must have ACTUAL values (real dates, strings), not placeholders
1291
1370
  6. Each filter option needs: label, value, params (and optionally isDefault)
1371
+ 7. Component default params MUST match filter default - this ensures correct initial data load
1292
1372
 
1293
1373
  ## Database Schema
1294
1374
  {{SCHEMA_DOC}}
@@ -2359,6 +2439,57 @@ var KbNodesRequestMessageSchema = import_zod3.z.object({
2359
2439
  type: import_zod3.z.literal("KB_NODES"),
2360
2440
  payload: KbNodesRequestPayloadSchema
2361
2441
  });
2442
+ var MenusQueryFiltersSchema = import_zod3.z.object({
2443
+ parentId: import_zod3.z.number().nullable().optional(),
2444
+ isActive: import_zod3.z.boolean().optional()
2445
+ });
2446
+ var MenusRequestPayloadSchema = import_zod3.z.object({
2447
+ operation: import_zod3.z.enum([
2448
+ "create",
2449
+ "update",
2450
+ "delete",
2451
+ "getAll",
2452
+ "getOne",
2453
+ "getRootMenus",
2454
+ "getChildMenus",
2455
+ "getHierarchy",
2456
+ "query",
2457
+ "reorder"
2458
+ ]),
2459
+ data: import_zod3.z.object({
2460
+ id: import_zod3.z.number().optional(),
2461
+ name: import_zod3.z.string().optional(),
2462
+ componentName: import_zod3.z.string().optional(),
2463
+ icon: import_zod3.z.string().optional(),
2464
+ userMessage: import_zod3.z.string().optional(),
2465
+ parentId: import_zod3.z.number().nullable().optional(),
2466
+ sortOrder: import_zod3.z.number().optional(),
2467
+ props: import_zod3.z.record(import_zod3.z.unknown()).optional(),
2468
+ isActive: import_zod3.z.boolean().optional(),
2469
+ // Query operation fields
2470
+ filters: MenusQueryFiltersSchema.optional(),
2471
+ limit: import_zod3.z.number().optional(),
2472
+ sort: import_zod3.z.enum(["ASC", "DESC"]).optional(),
2473
+ // Reorder operation fields
2474
+ items: import_zod3.z.array(import_zod3.z.object({
2475
+ id: import_zod3.z.number(),
2476
+ sortOrder: import_zod3.z.number()
2477
+ })).optional()
2478
+ }).optional()
2479
+ });
2480
+ var MenusRequestMessageSchema = import_zod3.z.object({
2481
+ id: import_zod3.z.string(),
2482
+ from: MessageParticipantSchema,
2483
+ type: import_zod3.z.literal("MENUS"),
2484
+ payload: MenusRequestPayloadSchema
2485
+ });
2486
+ var MenusResponsePayloadSchema = import_zod3.z.object({
2487
+ success: import_zod3.z.boolean(),
2488
+ error: import_zod3.z.string().optional(),
2489
+ data: import_zod3.z.any().optional(),
2490
+ count: import_zod3.z.number().optional(),
2491
+ message: import_zod3.z.string().optional()
2492
+ });
2362
2493
  var DashCompRequestPayloadSchema = import_zod3.z.object({
2363
2494
  prompt: import_zod3.z.string(),
2364
2495
  userId: import_zod3.z.string().optional(),
@@ -5719,35 +5850,35 @@ ${JSON.stringify(tool.requiredFields || [], null, 2)}`;
5719
5850
  let executedToolsText = "No external tools were executed for data fetching.";
5720
5851
  if (executedTools && executedTools.length > 0) {
5721
5852
  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) => {
5853
+ 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
5854
  let outputSchemaText = "Not available";
5855
+ let fieldNamesList = "";
5724
5856
  let recordCount = "unknown";
5725
5857
  if (tool.outputSchema) {
5726
5858
  const fields = tool.outputSchema.fields || [];
5727
5859
  recordCount = tool.result?._recordCount || (Array.isArray(tool.result) ? tool.result.length : "unknown");
5860
+ const numericFields = fields.filter((f) => f.type === "number").map((f) => f.name);
5861
+ const stringFields = fields.filter((f) => f.type === "string").map((f) => f.name);
5862
+ fieldNamesList = `
5863
+ \u{1F4CA} NUMERIC FIELDS (use for yAxisKey, valueKey, aggregationField): ${numericFields.join(", ") || "none"}
5864
+ \u{1F4DD} STRING FIELDS (use for xAxisKey, groupBy, nameKey): ${stringFields.join(", ") || "none"}`;
5728
5865
  const fieldsText = fields.map(
5729
- (f) => ` - name: "${f.name}" \u2190 USE THIS EXACT VALUE in config
5730
- type: ${f.type}
5731
- description: ${f.description}`
5866
+ (f) => ` "${f.name}" (${f.type}): ${f.description}`
5732
5867
  ).join("\n");
5733
5868
  outputSchemaText = `${tool.outputSchema.description}
5734
- Fields (use the "name" value in your config):
5869
+ Fields:
5735
5870
  ${fieldsText}`;
5736
5871
  }
5737
5872
  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)
5873
+ toolId: "${tool.id}"
5874
+ toolName: "${tool.name}"
5875
+ parameters: ${JSON.stringify(tool.params || {})}
5741
5876
  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`;
5877
+ outputSchema: ${outputSchemaText}${fieldNamesList}`;
5744
5878
  }).join("\n\n");
5745
5879
  }
5746
5880
  const schemaDoc = schema.generateSchemaDocumentation();
5747
5881
  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
5882
  const prompts = await promptLoader.loadPrompts("match-text-components", {
5752
5883
  ANALYSIS_CONTENT: analysisContent,
5753
5884
  AVAILABLE_COMPONENTS: availableComponentsText,
@@ -5757,7 +5888,13 @@ ${fieldsText}`;
5757
5888
  EXECUTED_TOOLS: executedToolsText
5758
5889
  });
5759
5890
  logger.debug(`[${this.getProviderName()}] Loaded match-text-components prompts`);
5760
- logger.file("\n=============================\nmatch text components system prompt:", prompts.system);
5891
+ const systemPromptStr = Array.isArray(prompts.system) ? prompts.system.join("\n") : prompts.system;
5892
+ logger.logLLMPrompt("matchComponentsFromAnalysis", "system", systemPromptStr);
5893
+ logger.logLLMPrompt("matchComponentsFromAnalysis", "user", `Text Analysis:
5894
+ ${analysisContent}
5895
+
5896
+ Executed Tools:
5897
+ ${executedToolsText}`);
5761
5898
  logCollector?.info("Matching components from text response...");
5762
5899
  let fullResponseText = "";
5763
5900
  let answerComponentExtracted = false;
@@ -5894,40 +6031,41 @@ ${fieldsText}`;
5894
6031
  if (executedTool?.outputSchema?.fields && cleanedProps.config) {
5895
6032
  const validFieldNames = executedTool.outputSchema.fields.map((f) => f.name);
5896
6033
  const validFieldNamesLower = validFieldNames.map((n) => n.toLowerCase());
5897
- const findMatchingField = (fieldName) => {
6034
+ const findMatchingField = (fieldName, configKey) => {
5898
6035
  if (!fieldName) return null;
5899
6036
  const lowerField = fieldName.toLowerCase();
5900
6037
  const exactIdx = validFieldNamesLower.indexOf(lowerField);
5901
6038
  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 });
6039
+ const containsMatches = validFieldNames.filter(
6040
+ (_, i) => validFieldNamesLower[i].includes(lowerField) || lowerField.includes(validFieldNamesLower[i])
6041
+ );
6042
+ if (containsMatches.length === 1) return containsMatches[0];
6043
+ const fieldTypes = executedTool.outputSchema.fields.reduce((acc, f) => {
6044
+ acc[f.name] = f.type;
6045
+ return acc;
6046
+ }, {});
6047
+ const numericConfigKeys = ["yAxisKey", "valueKey", "aggregationField", "sizeKey"];
6048
+ const stringConfigKeys = ["xAxisKey", "nameKey", "labelKey", "groupBy"];
6049
+ if (numericConfigKeys.includes(configKey)) {
6050
+ const numericFields = validFieldNames.filter((f) => fieldTypes[f] === "number");
6051
+ const match = numericFields.find((f) => f.toLowerCase().includes(lowerField) || lowerField.includes(f.toLowerCase()));
6052
+ if (match) return match;
6053
+ if (numericFields.length > 0) {
6054
+ logger.warn(`[${this.getProviderName()}] No match for "${fieldName}", using first numeric field: ${numericFields[0]}`);
6055
+ return numericFields[0];
5919
6056
  }
5920
6057
  }
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;
6058
+ if (stringConfigKeys.includes(configKey)) {
6059
+ const stringFields = validFieldNames.filter((f) => fieldTypes[f] === "string");
6060
+ const match = stringFields.find((f) => f.toLowerCase().includes(lowerField) || lowerField.includes(f.toLowerCase()));
6061
+ if (match) return match;
6062
+ if (stringFields.length > 0) {
6063
+ logger.warn(`[${this.getProviderName()}] No match for "${fieldName}", using first string field: ${stringFields[0]}`);
6064
+ return stringFields[0];
5928
6065
  }
5929
6066
  }
5930
- return null;
6067
+ logger.warn(`[${this.getProviderName()}] No match for "${fieldName}", using first field: ${validFieldNames[0]}`);
6068
+ return validFieldNames[0];
5931
6069
  };
5932
6070
  const configFieldsToValidate = [
5933
6071
  "xAxisKey",
@@ -5946,12 +6084,10 @@ ${fieldsText}`;
5946
6084
  const fieldValue = cleanedProps.config[configKey];
5947
6085
  if (fieldValue && typeof fieldValue === "string") {
5948
6086
  if (!validFieldNames.includes(fieldValue)) {
5949
- const correctedField = findMatchingField(fieldValue);
6087
+ const correctedField = findMatchingField(fieldValue, configKey);
5950
6088
  if (correctedField) {
5951
6089
  logger.warn(`[${this.getProviderName()}] Correcting config.${configKey}: "${fieldValue}" \u2192 "${correctedField}"`);
5952
6090
  cleanedProps.config[configKey] = correctedField;
5953
- } else {
5954
- logger.warn(`[${this.getProviderName()}] config.${configKey}="${fieldValue}" not found in outputSchema [${validFieldNames.join(", ")}]`);
5955
6091
  }
5956
6092
  }
5957
6093
  }
@@ -5959,7 +6095,7 @@ ${fieldsText}`;
5959
6095
  if (Array.isArray(cleanedProps.config.series)) {
5960
6096
  cleanedProps.config.series = cleanedProps.config.series.map((s) => {
5961
6097
  if (s.dataKey && typeof s.dataKey === "string" && !validFieldNames.includes(s.dataKey)) {
5962
- const correctedField = findMatchingField(s.dataKey);
6098
+ const correctedField = findMatchingField(s.dataKey, "yAxisKey");
5963
6099
  if (correctedField) {
5964
6100
  logger.warn(`[${this.getProviderName()}] Correcting series.dataKey: "${s.dataKey}" \u2192 "${correctedField}"`);
5965
6101
  return { ...s, dataKey: correctedField };
@@ -6026,6 +6162,10 @@ ${fieldsText}`;
6026
6162
  AVAILABLE_TOOLS: availableToolsDoc,
6027
6163
  SCHEMA_DOC: schemaDoc || "No database schema available"
6028
6164
  });
6165
+ const systemPrompt = Array.isArray(prompts.system) ? prompts.system.join("\n") : prompts.system;
6166
+ const userPromptText = Array.isArray(prompts.user) ? prompts.user.join("\n") : prompts.user;
6167
+ logger.logLLMPrompt("classifyQuestionCategory", "system", systemPrompt);
6168
+ logger.logLLMPrompt("classifyQuestionCategory", "user", userPromptText);
6029
6169
  const result = await LLM.stream(
6030
6170
  {
6031
6171
  sys: prompts.system,
@@ -6213,7 +6353,6 @@ ${fieldsText}`;
6213
6353
  collections,
6214
6354
  topK: 1
6215
6355
  });
6216
- logger.file("\n=============================\nknowledge base context:", knowledgeBaseContext);
6217
6356
  const prompts = await promptLoader.loadPrompts("text-response", {
6218
6357
  USER_PROMPT: userPrompt,
6219
6358
  CONVERSATION_HISTORY: conversationHistory || "No previous conversation",
@@ -6222,8 +6361,10 @@ ${fieldsText}`;
6222
6361
  KNOWLEDGE_BASE_CONTEXT: knowledgeBaseContext || "No additional knowledge base context available.",
6223
6362
  AVAILABLE_EXTERNAL_TOOLS: availableToolsDoc
6224
6363
  });
6225
- logger.file("\n=============================\nsystem prompt:", prompts.system);
6226
- logger.file("\n=============================\nuser prompt:", prompts.user);
6364
+ const sysPrompt = Array.isArray(prompts.system) ? prompts.system.join("\n") : prompts.system;
6365
+ const usrPrompt = Array.isArray(prompts.user) ? prompts.user.join("\n") : prompts.user;
6366
+ logger.logLLMPrompt("generateTextResponse", "system", sysPrompt);
6367
+ logger.logLLMPrompt("generateTextResponse", "user", usrPrompt);
6227
6368
  logger.debug(`[${this.getProviderName()}] Loaded text-response prompts with schema`);
6228
6369
  logger.debug(`[${this.getProviderName()}] System prompt length: ${prompts.system.length}, User prompt length: ${prompts.user.length}`);
6229
6370
  logCollector?.info("Generating text response with query execution capability...");
@@ -6609,7 +6750,6 @@ ${errorMsg}
6609
6750
  data: {
6610
6751
  text: textResponse,
6611
6752
  // Include the streamed text showing all attempts
6612
- matchedComponents: [],
6613
6753
  actions: [],
6614
6754
  method: `${this.getProviderName()}-text-response-max-attempts`
6615
6755
  }
@@ -6707,7 +6847,6 @@ ${errorMsg}
6707
6847
  success: true,
6708
6848
  data: {
6709
6849
  text: textResponse,
6710
- matchedComponents,
6711
6850
  component: container_componet,
6712
6851
  actions,
6713
6852
  method: `${this.getProviderName()}-text-response`
@@ -6731,7 +6870,6 @@ ${errorMsg}
6731
6870
  errors,
6732
6871
  data: {
6733
6872
  text: "I apologize, but I encountered an error while processing your question. Please try rephrasing or ask something else.",
6734
- matchedComponents: [],
6735
6873
  actions: [],
6736
6874
  method: `${this.getProviderName()}-text-response-error`
6737
6875
  }
@@ -6755,6 +6893,8 @@ ${errorMsg}
6755
6893
  const startTime = Date.now();
6756
6894
  logger.info(`[${this.getProviderName()}] handleUserRequest called for user prompt: ${userPrompt}`);
6757
6895
  logCollector?.info(`Starting request processing with mode: ${responseMode}`);
6896
+ logger.clearFile();
6897
+ logger.logLLMPrompt("handleUserRequest", "user", `User Prompt: ${userPrompt}`);
6758
6898
  try {
6759
6899
  logger.info(`[${this.getProviderName()}] Step 1: Searching previous conversations...`);
6760
6900
  logCollector?.info("Step 1: Searching for similar previous conversations...");
@@ -6762,8 +6902,8 @@ ${errorMsg}
6762
6902
  userPrompt,
6763
6903
  collections,
6764
6904
  userId,
6765
- similarityThreshold: 0.75
6766
- // 75% threshold
6905
+ similarityThreshold: 0.8
6906
+ // 80% threshold
6767
6907
  });
6768
6908
  if (conversationMatch) {
6769
6909
  logger.info(`[${this.getProviderName()}] \u2713 Found matching conversation with ${(conversationMatch.similarity * 100).toFixed(2)}% similarity`);
@@ -6788,7 +6928,6 @@ ${errorMsg}
6788
6928
  data: {
6789
6929
  text: cachedTextResponse,
6790
6930
  component: null,
6791
- matchedComponents: [],
6792
6931
  actions: conversationMatch.uiBlock?.actions || [],
6793
6932
  reasoning: `Exact match from previous general conversation`,
6794
6933
  method: `${this.getProviderName()}-semantic-match-general`,
@@ -6816,7 +6955,6 @@ ${errorMsg}
6816
6955
  data: {
6817
6956
  text: cachedTextResponse,
6818
6957
  component,
6819
- matchedComponents: component?.props?.config?.components || [],
6820
6958
  actions: cachedActions,
6821
6959
  reasoning: `Exact match from previous conversation (${(conversationMatch.similarity * 100).toFixed(2)}% similarity)`,
6822
6960
  method: `${this.getProviderName()}-semantic-match-exact`,
@@ -6852,7 +6990,6 @@ ${errorMsg}
6852
6990
  data: {
6853
6991
  text: textResponseToUse,
6854
6992
  component: adaptResult.adaptedComponent,
6855
- matchedComponents: adaptResult.adaptedComponent?.props?.config?.components || [],
6856
6993
  actions: cachedActions,
6857
6994
  reasoning: `Adapted from previous conversation: ${originalPrompt}`,
6858
6995
  method: `${this.getProviderName()}-semantic-match`,
@@ -6903,6 +7040,8 @@ ${errorMsg}
6903
7040
  executionReason: t.executionReason || "",
6904
7041
  requiredFields: t.requiredFields || [],
6905
7042
  userProvidedData: t.userProvidedData || null,
7043
+ // CRITICAL: Include outputSchema from real tool for component config generation
7044
+ outputSchema: realTool?.outputSchema,
6906
7045
  fn: (() => {
6907
7046
  if (realTool) {
6908
7047
  logger.info(`[${this.getProviderName()}] Using real tool implementation for ${t.type}`);
@@ -10459,6 +10598,331 @@ function sendResponse8(id, res, sendMessage, clientId) {
10459
10598
  sendMessage(response);
10460
10599
  }
10461
10600
 
10601
+ // src/handlers/menus.ts
10602
+ init_logger();
10603
+ async function handleMenusRequest(data, collections, sendMessage) {
10604
+ const executeCollection = async (collection, op, params) => {
10605
+ const handler = collections[collection]?.[op];
10606
+ if (!handler) {
10607
+ throw new Error(`Collection operation ${collection}.${op} not found`);
10608
+ }
10609
+ return await handler(params);
10610
+ };
10611
+ try {
10612
+ const request = MenusRequestMessageSchema.parse(data);
10613
+ const { id, payload, from } = request;
10614
+ const { operation, data: requestData } = payload;
10615
+ const menuId = requestData?.id;
10616
+ const name = requestData?.name;
10617
+ const componentName = requestData?.componentName;
10618
+ const icon = requestData?.icon;
10619
+ const userMessage = requestData?.userMessage;
10620
+ const parentId = requestData?.parentId;
10621
+ const sortOrder = requestData?.sortOrder;
10622
+ const props = requestData?.props;
10623
+ const isActive = requestData?.isActive;
10624
+ const filters = requestData?.filters;
10625
+ const limit = requestData?.limit;
10626
+ const sort = requestData?.sort;
10627
+ const items = requestData?.items;
10628
+ switch (operation) {
10629
+ case "create":
10630
+ await handleCreate7(id, name, componentName, icon, userMessage, parentId, sortOrder, props, isActive, executeCollection, sendMessage, from.id);
10631
+ break;
10632
+ case "update":
10633
+ await handleUpdate7(id, menuId, name, componentName, icon, userMessage, parentId, sortOrder, props, isActive, executeCollection, sendMessage, from.id);
10634
+ break;
10635
+ case "delete":
10636
+ await handleDelete7(id, menuId, executeCollection, sendMessage, from.id);
10637
+ break;
10638
+ case "getAll":
10639
+ await handleGetAll7(id, executeCollection, sendMessage, from.id);
10640
+ break;
10641
+ case "getOne":
10642
+ await handleGetOne7(id, menuId, executeCollection, sendMessage, from.id);
10643
+ break;
10644
+ case "getRootMenus":
10645
+ await handleGetRootMenus(id, executeCollection, sendMessage, from.id);
10646
+ break;
10647
+ case "getChildMenus":
10648
+ await handleGetChildMenus(id, parentId, executeCollection, sendMessage, from.id);
10649
+ break;
10650
+ case "getHierarchy":
10651
+ await handleGetHierarchy(id, executeCollection, sendMessage, from.id);
10652
+ break;
10653
+ case "query":
10654
+ await handleQuery6(id, filters, limit, sort, executeCollection, sendMessage, from.id);
10655
+ break;
10656
+ case "reorder":
10657
+ await handleReorder(id, items, executeCollection, sendMessage, from.id);
10658
+ break;
10659
+ default:
10660
+ sendResponse9(id, {
10661
+ success: false,
10662
+ error: `Unknown operation: ${operation}`
10663
+ }, sendMessage, from.id);
10664
+ }
10665
+ } catch (error) {
10666
+ logger.error("Failed to handle menus request:", error);
10667
+ sendResponse9(null, {
10668
+ success: false,
10669
+ error: error instanceof Error ? error.message : "Unknown error occurred"
10670
+ }, sendMessage);
10671
+ }
10672
+ }
10673
+ async function handleCreate7(id, name, componentName, icon, userMessage, parentId, sortOrder, props, isActive, executeCollection, sendMessage, clientId) {
10674
+ if (!name) {
10675
+ sendResponse9(id, {
10676
+ success: false,
10677
+ error: "name is required"
10678
+ }, sendMessage, clientId);
10679
+ return;
10680
+ }
10681
+ if (!componentName) {
10682
+ sendResponse9(id, {
10683
+ success: false,
10684
+ error: "componentName is required"
10685
+ }, sendMessage, clientId);
10686
+ return;
10687
+ }
10688
+ try {
10689
+ const result = await executeCollection("menus", "create", {
10690
+ name,
10691
+ componentName,
10692
+ icon,
10693
+ userMessage,
10694
+ parentId,
10695
+ sortOrder,
10696
+ props,
10697
+ isActive
10698
+ });
10699
+ sendResponse9(id, {
10700
+ success: true,
10701
+ data: result.data,
10702
+ message: "Menu created successfully"
10703
+ }, sendMessage, clientId);
10704
+ logger.info(`Menu created: ID ${result.data?.id}`);
10705
+ } catch (error) {
10706
+ sendResponse9(id, {
10707
+ success: false,
10708
+ error: error instanceof Error ? error.message : "Failed to create menu"
10709
+ }, sendMessage, clientId);
10710
+ }
10711
+ }
10712
+ async function handleUpdate7(id, menuId, name, componentName, icon, userMessage, parentId, sortOrder, props, isActive, executeCollection, sendMessage, clientId) {
10713
+ if (!menuId) {
10714
+ sendResponse9(id, {
10715
+ success: false,
10716
+ error: "Menu ID is required"
10717
+ }, sendMessage, clientId);
10718
+ return;
10719
+ }
10720
+ try {
10721
+ const result = await executeCollection("menus", "update", {
10722
+ id: menuId,
10723
+ name,
10724
+ componentName,
10725
+ icon,
10726
+ userMessage,
10727
+ parentId,
10728
+ sortOrder,
10729
+ props,
10730
+ isActive
10731
+ });
10732
+ sendResponse9(id, {
10733
+ success: true,
10734
+ data: result.data,
10735
+ message: "Menu updated successfully"
10736
+ }, sendMessage, clientId);
10737
+ logger.info(`Menu updated: ID ${menuId}`);
10738
+ } catch (error) {
10739
+ sendResponse9(id, {
10740
+ success: false,
10741
+ error: error instanceof Error ? error.message : "Failed to update menu"
10742
+ }, sendMessage, clientId);
10743
+ }
10744
+ }
10745
+ async function handleDelete7(id, menuId, executeCollection, sendMessage, clientId) {
10746
+ if (!menuId) {
10747
+ sendResponse9(id, {
10748
+ success: false,
10749
+ error: "Menu ID is required"
10750
+ }, sendMessage, clientId);
10751
+ return;
10752
+ }
10753
+ try {
10754
+ const result = await executeCollection("menus", "delete", { id: menuId });
10755
+ sendResponse9(id, {
10756
+ success: true,
10757
+ data: result.data,
10758
+ message: "Menu deleted successfully"
10759
+ }, sendMessage, clientId);
10760
+ logger.info(`Menu deleted: ID ${menuId}`);
10761
+ } catch (error) {
10762
+ sendResponse9(id, {
10763
+ success: false,
10764
+ error: error instanceof Error ? error.message : "Failed to delete menu"
10765
+ }, sendMessage, clientId);
10766
+ }
10767
+ }
10768
+ async function handleGetAll7(id, executeCollection, sendMessage, clientId) {
10769
+ try {
10770
+ const result = await executeCollection("menus", "getAll", {});
10771
+ sendResponse9(id, {
10772
+ success: true,
10773
+ data: result.data,
10774
+ count: result.count,
10775
+ message: `Retrieved ${result.count} menus`
10776
+ }, sendMessage, clientId);
10777
+ logger.info(`Retrieved all menus (count: ${result.count})`);
10778
+ } catch (error) {
10779
+ sendResponse9(id, {
10780
+ success: false,
10781
+ error: error instanceof Error ? error.message : "Failed to get menus"
10782
+ }, sendMessage, clientId);
10783
+ }
10784
+ }
10785
+ async function handleGetOne7(id, menuId, executeCollection, sendMessage, clientId) {
10786
+ if (!menuId) {
10787
+ sendResponse9(id, {
10788
+ success: false,
10789
+ error: "Menu ID is required"
10790
+ }, sendMessage, clientId);
10791
+ return;
10792
+ }
10793
+ try {
10794
+ const result = await executeCollection("menus", "getOne", { id: menuId });
10795
+ sendResponse9(id, {
10796
+ success: true,
10797
+ data: result.data,
10798
+ message: `Retrieved menu ID ${menuId}`
10799
+ }, sendMessage, clientId);
10800
+ logger.info(`Retrieved menu: ID ${menuId}`);
10801
+ } catch (error) {
10802
+ sendResponse9(id, {
10803
+ success: false,
10804
+ error: error instanceof Error ? error.message : "Failed to get menu"
10805
+ }, sendMessage, clientId);
10806
+ }
10807
+ }
10808
+ async function handleGetRootMenus(id, executeCollection, sendMessage, clientId) {
10809
+ try {
10810
+ const result = await executeCollection("menus", "getRootMenus", {});
10811
+ sendResponse9(id, {
10812
+ success: true,
10813
+ data: result.data,
10814
+ count: result.count,
10815
+ message: `Retrieved ${result.count} root menus`
10816
+ }, sendMessage, clientId);
10817
+ logger.info(`Retrieved root menus (count: ${result.count})`);
10818
+ } catch (error) {
10819
+ sendResponse9(id, {
10820
+ success: false,
10821
+ error: error instanceof Error ? error.message : "Failed to get root menus"
10822
+ }, sendMessage, clientId);
10823
+ }
10824
+ }
10825
+ async function handleGetChildMenus(id, parentId, executeCollection, sendMessage, clientId) {
10826
+ if (parentId === void 0 || parentId === null) {
10827
+ sendResponse9(id, {
10828
+ success: false,
10829
+ error: "parentId is required"
10830
+ }, sendMessage, clientId);
10831
+ return;
10832
+ }
10833
+ try {
10834
+ const result = await executeCollection("menus", "getChildMenus", { parentId });
10835
+ sendResponse9(id, {
10836
+ success: true,
10837
+ data: result.data,
10838
+ count: result.count,
10839
+ message: `Retrieved ${result.count} child menus for parent ${parentId}`
10840
+ }, sendMessage, clientId);
10841
+ logger.info(`Retrieved child menus for parent ${parentId} (count: ${result.count})`);
10842
+ } catch (error) {
10843
+ sendResponse9(id, {
10844
+ success: false,
10845
+ error: error instanceof Error ? error.message : "Failed to get child menus"
10846
+ }, sendMessage, clientId);
10847
+ }
10848
+ }
10849
+ async function handleGetHierarchy(id, executeCollection, sendMessage, clientId) {
10850
+ try {
10851
+ const result = await executeCollection("menus", "getHierarchy", {});
10852
+ sendResponse9(id, {
10853
+ success: true,
10854
+ data: result.data,
10855
+ count: result.count,
10856
+ message: `Retrieved menus hierarchy with ${result.count} root items`
10857
+ }, sendMessage, clientId);
10858
+ logger.info(`Retrieved menus hierarchy (root count: ${result.count})`);
10859
+ } catch (error) {
10860
+ sendResponse9(id, {
10861
+ success: false,
10862
+ error: error instanceof Error ? error.message : "Failed to get menus hierarchy"
10863
+ }, sendMessage, clientId);
10864
+ }
10865
+ }
10866
+ async function handleQuery6(id, filters, limit, sort, executeCollection, sendMessage, clientId) {
10867
+ try {
10868
+ const result = await executeCollection("menus", "query", {
10869
+ filters: filters || {},
10870
+ limit,
10871
+ sort
10872
+ });
10873
+ sendResponse9(id, {
10874
+ success: true,
10875
+ data: result.data,
10876
+ count: result.count,
10877
+ message: `Query returned ${result.count} menus`
10878
+ }, sendMessage, clientId);
10879
+ logger.info(`Query returned ${result.count} menus`);
10880
+ } catch (error) {
10881
+ sendResponse9(id, {
10882
+ success: false,
10883
+ error: error instanceof Error ? error.message : "Failed to query menus"
10884
+ }, sendMessage, clientId);
10885
+ }
10886
+ }
10887
+ async function handleReorder(id, items, executeCollection, sendMessage, clientId) {
10888
+ if (!items || !Array.isArray(items) || items.length === 0) {
10889
+ sendResponse9(id, {
10890
+ success: false,
10891
+ error: "items array is required"
10892
+ }, sendMessage, clientId);
10893
+ return;
10894
+ }
10895
+ try {
10896
+ const result = await executeCollection("menus", "reorder", { items });
10897
+ sendResponse9(id, {
10898
+ success: true,
10899
+ data: result.data,
10900
+ message: `Reordered ${items.length} menus successfully`
10901
+ }, sendMessage, clientId);
10902
+ logger.info(`Reordered ${items.length} menus`);
10903
+ } catch (error) {
10904
+ sendResponse9(id, {
10905
+ success: false,
10906
+ error: error instanceof Error ? error.message : "Failed to reorder menus"
10907
+ }, sendMessage, clientId);
10908
+ }
10909
+ }
10910
+ function sendResponse9(id, res, sendMessage, clientId) {
10911
+ const response = {
10912
+ id: id || "unknown",
10913
+ type: "MENUS_RES",
10914
+ from: { type: "data-agent" },
10915
+ to: {
10916
+ type: "admin",
10917
+ id: clientId
10918
+ },
10919
+ payload: {
10920
+ ...res
10921
+ }
10922
+ };
10923
+ sendMessage(response);
10924
+ }
10925
+
10462
10926
  // src/dashComp/index.ts
10463
10927
  init_logger();
10464
10928
 
@@ -10585,37 +11049,74 @@ async function pickComponentWithLLM(prompt, components, anthropicApiKey, groqApi
10585
11049
  return { success: false, errors };
10586
11050
  }
10587
11051
  logger.info(`[DASH_COMP_REQ] Using model: ${model}`);
10588
- const result = await LLM.stream(
11052
+ const executedTools = [];
11053
+ const llmTools = (tools || []).map((tool) => ({
11054
+ name: tool.id,
11055
+ description: tool.description,
11056
+ input_schema: {
11057
+ type: "object",
11058
+ properties: Object.fromEntries(
11059
+ 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` }])
11060
+ ),
11061
+ additionalProperties: false
11062
+ }
11063
+ }));
11064
+ const toolHandler = async (toolName, toolInput) => {
11065
+ const tool = tools?.find((t) => t.id === toolName);
11066
+ if (!tool) {
11067
+ throw new Error(`Unknown tool: ${toolName}`);
11068
+ }
11069
+ logger.info(`[DASH_COMP_REQ] Executing external tool: ${tool.name} (${tool.id})`);
11070
+ const result2 = await tool.fn(toolInput);
11071
+ executedTools.push({
11072
+ id: tool.id,
11073
+ name: tool.name,
11074
+ params: toolInput,
11075
+ result: result2,
11076
+ outputSchema: tool.outputSchema
11077
+ });
11078
+ logger.info(`[DASH_COMP_REQ] Tool ${tool.name} executed successfully`);
11079
+ return JSON.stringify(result2, null, 2);
11080
+ };
11081
+ const result = await LLM.streamWithTools(
10589
11082
  {
10590
11083
  sys: prompts.system,
10591
11084
  user: prompts.user
10592
11085
  },
11086
+ llmTools,
11087
+ toolHandler,
10593
11088
  {
10594
11089
  model,
10595
11090
  maxTokens: 8192,
10596
11091
  temperature: 0.2,
10597
11092
  apiKey
10598
11093
  },
10599
- true
10600
- // Parse as JSON
11094
+ 5
11095
+ // max iterations
10601
11096
  );
11097
+ const jsonMatch = result.match(/\{[\s\S]*\}/);
11098
+ const parsedResult = jsonMatch ? JSON.parse(jsonMatch[0]) : null;
11099
+ if (!parsedResult) {
11100
+ errors.push("Failed to parse LLM response as JSON");
11101
+ return { success: false, errors };
11102
+ }
10602
11103
  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) {
11104
+ logger.file("[DASH_COMP_REQ] LLM response:", JSON.stringify(parsedResult, null, 2));
11105
+ if (!parsedResult.componentId || !parsedResult.props) {
10605
11106
  errors.push("Invalid LLM response: missing componentId or props");
10606
11107
  userPromptErrorLogger.logError("DASH_COMP_REQ", "Invalid LLM response structure", {
10607
11108
  prompt,
10608
- result,
10609
- missingFields: { componentId: !result.componentId, props: !result.props }
11109
+ result: parsedResult,
11110
+ missingFields: { componentId: !parsedResult.componentId, props: !parsedResult.props }
10610
11111
  });
10611
11112
  return { success: false, errors };
10612
11113
  }
10613
- const originalComponent = components.find((c) => c.name === result.componentName);
11114
+ const originalComponent = components.find((c) => c.name === parsedResult.componentName);
10614
11115
  if (!originalComponent) {
10615
- errors.push(`Component ${result.componentName} not found in available components`);
11116
+ errors.push(`Component ${parsedResult.componentName} not found in available components`);
10616
11117
  userPromptErrorLogger.logError("DASH_COMP_REQ", "Component not found", {
10617
11118
  prompt,
10618
- componentName: result.componentName,
11119
+ componentName: parsedResult.componentName,
10619
11120
  availableComponentNames: components.map((c) => c.name)
10620
11121
  });
10621
11122
  return { success: false, errors };
@@ -10624,23 +11125,27 @@ async function pickComponentWithLLM(prompt, components, anthropicApiKey, groqApi
10624
11125
  ...originalComponent,
10625
11126
  props: {
10626
11127
  ...originalComponent.props,
10627
- ...result.props
11128
+ ...parsedResult.props
10628
11129
  }
10629
11130
  };
10630
11131
  logger.info(`[DASH_COMP_REQ] Successfully picked component: ${finalComponent.name} (${finalComponent.type})`);
10631
- if (result.props.query) {
11132
+ if (parsedResult.props.query) {
10632
11133
  logger.info(`[DASH_COMP_REQ] Data source: Database query`);
10633
11134
  }
10634
- if (result.props.externalTool) {
10635
- logger.info(`[DASH_COMP_REQ] Data source: External tool - ${result.props.externalTool.toolName}`);
11135
+ if (parsedResult.props.externalTool) {
11136
+ logger.info(`[DASH_COMP_REQ] Data source: External tool - ${parsedResult.props.externalTool.toolName}`);
11137
+ }
11138
+ if (executedTools.length > 0) {
11139
+ logger.info(`[DASH_COMP_REQ] Executed ${executedTools.length} external tool(s): ${executedTools.map((t) => t.name).join(", ")}`);
10636
11140
  }
10637
11141
  return {
10638
11142
  success: true,
10639
11143
  data: {
10640
11144
  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
11145
+ reasoning: parsedResult.reasoning || "Component selected based on user prompt",
11146
+ dataSource: parsedResult.props.query ? "database" : parsedResult.props.externalTool ? "external_tool" : "none",
11147
+ isUpdate: parsedResult.isUpdate || false,
11148
+ executedTools: executedTools.length > 0 ? executedTools : void 0
10644
11149
  },
10645
11150
  errors: []
10646
11151
  };
@@ -10693,11 +11198,49 @@ async function createFilterWithLLM(prompt, components, existingComponents, anthr
10693
11198
  return { success: false, errors };
10694
11199
  }
10695
11200
  logger.info(`[DASH_COMP_REQ:FILTER] Using model: ${model}`);
10696
- const result = await LLM.stream(
11201
+ const executedTools = [];
11202
+ const llmTools = (tools || []).map((tool) => ({
11203
+ name: tool.id,
11204
+ description: tool.description,
11205
+ input_schema: {
11206
+ type: "object",
11207
+ properties: Object.fromEntries(
11208
+ 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` }])
11209
+ ),
11210
+ additionalProperties: false
11211
+ }
11212
+ }));
11213
+ const toolHandler = async (toolName, toolInput) => {
11214
+ const tool = tools?.find((t) => t.id === toolName);
11215
+ if (!tool) {
11216
+ throw new Error(`Unknown tool: ${toolName}`);
11217
+ }
11218
+ logger.info(`[DASH_COMP_REQ:FILTER] Executing external tool: ${tool.name} (${tool.id})`);
11219
+ const result2 = await tool.fn(toolInput);
11220
+ executedTools.push({
11221
+ id: tool.id,
11222
+ name: tool.name,
11223
+ params: toolInput,
11224
+ result: result2,
11225
+ outputSchema: tool.outputSchema
11226
+ });
11227
+ logger.info(`[DASH_COMP_REQ:FILTER] Tool ${tool.name} executed successfully`);
11228
+ return JSON.stringify(result2, null, 2);
11229
+ };
11230
+ const rawResult = await LLM.streamWithTools(
10697
11231
  { sys: prompts.system, user: prompts.user },
11232
+ llmTools,
11233
+ toolHandler,
10698
11234
  { model, maxTokens: 16384, temperature: 0.2, apiKey },
10699
- true
11235
+ 5
11236
+ // max iterations
10700
11237
  );
11238
+ const jsonMatch = rawResult.match(/\{[\s\S]*\}/);
11239
+ const result = jsonMatch ? JSON.parse(jsonMatch[0]) : null;
11240
+ if (!result) {
11241
+ errors.push("Failed to parse LLM response as JSON");
11242
+ return { success: false, errors };
11243
+ }
10701
11244
  logger.debug("[DASH_COMP_REQ:FILTER] LLM response received");
10702
11245
  logger.file("[DASH_COMP_REQ:FILTER] LLM response:", JSON.stringify(result, null, 2));
10703
11246
  if (!result.filterComponent) {
@@ -10711,13 +11254,17 @@ async function createFilterWithLLM(prompt, components, existingComponents, anthr
10711
11254
  }
10712
11255
  logger.info(`[DASH_COMP_REQ:FILTER] Successfully created filter: ${result.filterComponent.componentType}`);
10713
11256
  logger.info(`[DASH_COMP_REQ:FILTER] Updated ${result.updatedComponents?.length || 0} components`);
11257
+ if (executedTools.length > 0) {
11258
+ logger.info(`[DASH_COMP_REQ:FILTER] Executed ${executedTools.length} external tool(s): ${executedTools.map((t) => t.name).join(", ")}`);
11259
+ }
10714
11260
  return {
10715
11261
  success: true,
10716
11262
  data: {
10717
11263
  filterComponent: result.filterComponent,
10718
11264
  updatedComponents: result.updatedComponents || [],
10719
11265
  filterBindings: result.filterBindings || {},
10720
- reasoning: result.reasoning || "Filter created based on user prompt"
11266
+ reasoning: result.reasoning || "Filter created based on user prompt",
11267
+ executedTools: executedTools.length > 0 ? executedTools : void 0
10721
11268
  },
10722
11269
  errors: []
10723
11270
  };
@@ -11895,6 +12442,11 @@ var SuperatomSDK = class {
11895
12442
  logger.error("Failed to handle KB nodes request:", error);
11896
12443
  });
11897
12444
  break;
12445
+ case "MENUS":
12446
+ handleMenusRequest(parsed, this.collections, (msg) => this.send(msg)).catch((error) => {
12447
+ logger.error("Failed to handle menus request:", error);
12448
+ });
12449
+ break;
11898
12450
  case "DASH_COMP_REQ":
11899
12451
  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
12452
  logger.error("Failed to handle dash comp request:", error);