@superatomai/sdk-node 0.0.2 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1866,55 +1866,126 @@ import fs3 from "fs";
1866
1866
  import path3 from "path";
1867
1867
  var PromptLoader = class {
1868
1868
  constructor(config) {
1869
+ this.promptCache = /* @__PURE__ */ new Map();
1870
+ this.isInitialized = false;
1869
1871
  logger.debug("Initializing PromptLoader...", process.cwd());
1870
1872
  this.promptsDir = config?.promptsDir || path3.join(process.cwd(), ".prompts");
1873
+ this.defaultPromptsDir = path3.join(__dirname, "..", "..", ".prompts");
1871
1874
  }
1872
1875
  /**
1873
- * Load a single prompt file and replace variables using {{VARIABLE_NAME}} pattern
1876
+ * Initialize and cache all prompts into memory
1877
+ * This should be called once at SDK startup
1878
+ */
1879
+ async initialize() {
1880
+ if (this.isInitialized) {
1881
+ logger.debug("PromptLoader already initialized, skipping...");
1882
+ return;
1883
+ }
1884
+ logger.info("Loading prompts into memory...");
1885
+ const promptTypes = [
1886
+ "classify",
1887
+ "match-component",
1888
+ "modify-props",
1889
+ "single-component",
1890
+ "mutli-component",
1891
+ "actions",
1892
+ "container-metadata"
1893
+ ];
1894
+ for (const promptType of promptTypes) {
1895
+ try {
1896
+ const template = await this.loadPromptTemplate(promptType);
1897
+ this.promptCache.set(promptType, template);
1898
+ logger.debug(`Cached prompt: ${promptType}`);
1899
+ } catch (error) {
1900
+ logger.error(`Failed to load prompt '${promptType}':`, error);
1901
+ throw error;
1902
+ }
1903
+ }
1904
+ this.isInitialized = true;
1905
+ logger.info(`Successfully loaded ${this.promptCache.size} prompt templates into memory`);
1906
+ }
1907
+ /**
1908
+ * Load a prompt template from file system (tries custom dir first, then defaults to SDK dir)
1874
1909
  * @param promptName - Name of the prompt folder
1875
- * @param promptType - Type of prompt ('system' or 'user')
1876
- * @param variables - Variables to replace in the template
1877
- * @returns Processed prompt string
1910
+ * @returns Template with system and user prompts
1878
1911
  */
1879
- async loadPrompt(promptName, promptType, variables) {
1880
- try {
1881
- const promptPath = path3.join(
1882
- this.promptsDir,
1883
- promptName,
1884
- `${promptType}.md`
1885
- );
1886
- logger.debug(`Loading prompt '${promptName}/${promptType}.md' from ${promptPath} process path: ${process.cwd()}`);
1887
- let content = fs3.readFileSync(promptPath, "utf-8");
1888
- for (const [key, value] of Object.entries(variables)) {
1889
- const pattern = new RegExp(`{{${key}}}`, "g");
1890
- const replacementValue = typeof value === "string" ? value : JSON.stringify(value);
1891
- content = content.replace(pattern, replacementValue);
1912
+ async loadPromptTemplate(promptName) {
1913
+ const tryLoadFromDir = (dir) => {
1914
+ try {
1915
+ const systemPath = path3.join(dir, promptName, "system.md");
1916
+ const userPath = path3.join(dir, promptName, "user.md");
1917
+ if (fs3.existsSync(systemPath) && fs3.existsSync(userPath)) {
1918
+ const system = fs3.readFileSync(systemPath, "utf-8");
1919
+ const user = fs3.readFileSync(userPath, "utf-8");
1920
+ logger.debug(`Loaded prompt '${promptName}' from ${dir}`);
1921
+ return { system, user };
1922
+ }
1923
+ return null;
1924
+ } catch (error) {
1925
+ return null;
1892
1926
  }
1893
- return content;
1894
- } catch (error) {
1895
- console.error(`Error loading prompt '${promptName}/${promptType}.md':`, error);
1896
- throw error;
1927
+ };
1928
+ let template = tryLoadFromDir(this.promptsDir);
1929
+ if (!template) {
1930
+ logger.warn(`Prompt '${promptName}' not found in ${this.promptsDir}, trying default location...`);
1931
+ template = tryLoadFromDir(this.defaultPromptsDir);
1897
1932
  }
1933
+ if (!template) {
1934
+ throw new Error(`Prompt template '${promptName}' not found in either ${this.promptsDir} or ${this.defaultPromptsDir}`);
1935
+ }
1936
+ return template;
1937
+ }
1938
+ /**
1939
+ * Replace variables in a template string using {{VARIABLE_NAME}} pattern
1940
+ * @param template - Template string with placeholders
1941
+ * @param variables - Variables to replace in the template
1942
+ * @returns Processed string
1943
+ */
1944
+ replaceVariables(template, variables) {
1945
+ let content = template;
1946
+ for (const [key, value] of Object.entries(variables)) {
1947
+ const pattern = new RegExp(`{{${key}}}`, "g");
1948
+ const replacementValue = typeof value === "string" ? value : JSON.stringify(value);
1949
+ content = content.replace(pattern, replacementValue);
1950
+ }
1951
+ return content;
1898
1952
  }
1899
1953
  /**
1900
- * Load both system and user prompts and replace variables
1954
+ * Load both system and user prompts from cache and replace variables
1901
1955
  * @param promptName - Name of the prompt folder
1902
1956
  * @param variables - Variables to replace in the templates
1903
1957
  * @returns Object containing both system and user prompts
1904
1958
  */
1905
1959
  async loadPrompts(promptName, variables) {
1906
- const [system, user] = await Promise.all([
1907
- this.loadPrompt(promptName, "system", variables),
1908
- this.loadPrompt(promptName, "user", variables)
1909
- ]);
1910
- return { system, user };
1960
+ if (!this.isInitialized) {
1961
+ logger.warn("PromptLoader not initialized, loading prompts on-demand (not recommended)");
1962
+ await this.initialize();
1963
+ }
1964
+ const template = this.promptCache.get(promptName);
1965
+ if (!template) {
1966
+ throw new Error(`Prompt template '${promptName}' not found in cache. Available prompts: ${Array.from(this.promptCache.keys()).join(", ")}`);
1967
+ }
1968
+ return {
1969
+ system: this.replaceVariables(template.system, variables),
1970
+ user: this.replaceVariables(template.user, variables)
1971
+ };
1911
1972
  }
1912
1973
  /**
1913
- * Set custom prompts directory
1974
+ * DEPRECATED: Use loadPrompts instead
1975
+ * Load a single prompt file and replace variables using {{VARIABLE_NAME}} pattern
1976
+ */
1977
+ async loadPrompt(promptName, promptType, variables) {
1978
+ const prompts = await this.loadPrompts(promptName, variables);
1979
+ return promptType === "system" ? prompts.system : prompts.user;
1980
+ }
1981
+ /**
1982
+ * Set custom prompts directory (requires re-initialization)
1914
1983
  * @param dir - Path to the prompts directory
1915
1984
  */
1916
1985
  setPromptsDir(dir) {
1917
1986
  this.promptsDir = dir;
1987
+ this.isInitialized = false;
1988
+ this.promptCache.clear();
1918
1989
  }
1919
1990
  /**
1920
1991
  * Get current prompts directory
@@ -1923,8 +1994,23 @@ var PromptLoader = class {
1923
1994
  getPromptsDir() {
1924
1995
  return this.promptsDir;
1925
1996
  }
1997
+ /**
1998
+ * Check if prompts are loaded in memory
1999
+ */
2000
+ isReady() {
2001
+ return this.isInitialized;
2002
+ }
2003
+ /**
2004
+ * Get the number of cached prompts
2005
+ */
2006
+ getCacheSize() {
2007
+ return this.promptCache.size;
2008
+ }
1926
2009
  };
1927
- var promptLoader = new PromptLoader();
2010
+ var defaultPromptsPath = process.env.PROMPTS_DIR || path3.join(process.cwd(), ".prompts");
2011
+ var promptLoader = new PromptLoader({
2012
+ promptsDir: defaultPromptsPath
2013
+ });
1928
2014
 
1929
2015
  // src/llm.ts
1930
2016
  import Anthropic from "@anthropic-ai/sdk";
@@ -1982,8 +2068,6 @@ var LLM = class {
1982
2068
  // ============================================================
1983
2069
  static async _anthropicText(messages, modelName, options) {
1984
2070
  const apiKey = options.apiKey || process.env.ANTHROPIC_API_KEY || "";
1985
- console.log("[LLM DEBUG] Anthropic Text - apiKey from options:", options.apiKey ? `${options.apiKey.substring(0, 10)}...` : "NOT SET");
1986
- console.log("[LLM DEBUG] Anthropic Text - final apiKey:", apiKey ? `${apiKey.substring(0, 10)}...` : "EMPTY STRING");
1987
2071
  const client = new Anthropic({
1988
2072
  apiKey
1989
2073
  });
@@ -2002,9 +2086,6 @@ var LLM = class {
2002
2086
  }
2003
2087
  static async _anthropicStream(messages, modelName, options, json) {
2004
2088
  const apiKey = options.apiKey || process.env.ANTHROPIC_API_KEY || "";
2005
- console.log("[LLM DEBUG] Anthropic - apiKey from options:", options.apiKey ? `${options.apiKey.substring(0, 10)}...` : "NOT SET");
2006
- console.log("[LLM DEBUG] Anthropic - apiKey from env:", process.env.ANTHROPIC_API_KEY ? `${process.env.ANTHROPIC_API_KEY.substring(0, 10)}...` : "NOT SET");
2007
- console.log("[LLM DEBUG] Anthropic - final apiKey:", apiKey ? `${apiKey.substring(0, 10)}...` : "EMPTY STRING");
2008
2089
  const client = new Anthropic({
2009
2090
  apiKey
2010
2091
  });
@@ -2054,9 +2135,6 @@ var LLM = class {
2054
2135
  }
2055
2136
  static async _groqStream(messages, modelName, options, json) {
2056
2137
  const apiKey = options.apiKey || process.env.GROQ_API_KEY || "";
2057
- console.log("[LLM DEBUG] Groq - apiKey from options:", options.apiKey ? `${options.apiKey.substring(0, 10)}...` : "NOT SET");
2058
- console.log("[LLM DEBUG] Groq - model:", modelName);
2059
- console.log("[LLM DEBUG] Groq - final apiKey:", apiKey ? `${apiKey.substring(0, 10)}...` : "EMPTY STRING");
2060
2138
  const client = new Groq({
2061
2139
  apiKey
2062
2140
  });
@@ -2128,11 +2206,8 @@ var BaseLLM = class {
2128
2206
  * Classify user question to determine the type and required visualizations
2129
2207
  */
2130
2208
  async classifyUserQuestion(userPrompt, apiKey, logCollector, conversationHistory) {
2131
- const schemaDoc = schema.generateSchemaDocumentation();
2132
- logger.info("Generating prompts...", userPrompt, conversationHistory);
2133
2209
  try {
2134
2210
  const prompts = await promptLoader.loadPrompts("classify", {
2135
- SCHEMA_DOC: schemaDoc || "No schema available",
2136
2211
  USER_PROMPT: userPrompt,
2137
2212
  CONVERSATION_HISTORY: conversationHistory || "No previous conversation"
2138
2213
  });
@@ -2187,6 +2262,7 @@ var BaseLLM = class {
2187
2262
  CURRENT_PROPS: JSON.stringify(originalProps, null, 2),
2188
2263
  CONVERSATION_HISTORY: conversationHistory || "No previous conversation"
2189
2264
  });
2265
+ logger.debug("props-modification: System prompt\n", prompts.system.substring(0, 100), "\n\n\n", "User prompt:", prompts.user.substring(0, 50));
2190
2266
  const result = await LLM.stream(
2191
2267
  {
2192
2268
  sys: prompts.system,
@@ -2234,21 +2310,47 @@ var BaseLLM = class {
2234
2310
  }
2235
2311
  }
2236
2312
  /**
2237
- * Generate a dynamic component for analytical questions when no matching component exists
2238
- * This creates a custom component with appropriate visualization and query
2313
+ * Match and select a component from available components filtered by type
2314
+ * This picks the best matching component based on user prompt and modifies its props
2239
2315
  */
2240
- async generateAnalyticalComponent(userPrompt, preferredVisualizationType, apiKey, logCollector, conversationHistory) {
2241
- const schemaDoc = schema.generateSchemaDocumentation();
2316
+ async generateAnalyticalComponent(userPrompt, components, preferredVisualizationType, apiKey, logCollector, conversationHistory) {
2242
2317
  try {
2318
+ const filteredComponents = preferredVisualizationType ? components.filter((c) => c.type === preferredVisualizationType) : components;
2319
+ if (filteredComponents.length === 0) {
2320
+ logCollector?.warn(
2321
+ `No components found of type ${preferredVisualizationType}`,
2322
+ "explanation",
2323
+ { reason: "No matching components available for this visualization type" }
2324
+ );
2325
+ return {
2326
+ component: null,
2327
+ reasoning: `No components available of type ${preferredVisualizationType}`,
2328
+ isGenerated: false
2329
+ };
2330
+ }
2331
+ const componentsText = filteredComponents.map((comp, idx) => {
2332
+ const keywords = comp.keywords ? comp.keywords.join(", ") : "";
2333
+ const category = comp.category || "general";
2334
+ const propsPreview = comp.props ? JSON.stringify(comp.props, null, 2) : "No props";
2335
+ return `${idx + 1}. ID: ${comp.id}
2336
+ Name: ${comp.name}
2337
+ Type: ${comp.type}
2338
+ Category: ${category}
2339
+ Description: ${comp.description || "No description"}
2340
+ Keywords: ${keywords}
2341
+ Props Preview: ${propsPreview}`;
2342
+ }).join("\n\n");
2243
2343
  const visualizationConstraint = preferredVisualizationType ? `
2244
- **IMPORTANT: The user has specifically requested a ${preferredVisualizationType} visualization. You MUST use this type.**
2344
+ **IMPORTANT: Components are filtered to type ${preferredVisualizationType}. Select the best match.**
2245
2345
  ` : "";
2246
2346
  const prompts = await promptLoader.loadPrompts("single-component", {
2247
- SCHEMA_DOC: schemaDoc || "No schema available",
2347
+ COMPONENT_TYPE: preferredVisualizationType || "any",
2348
+ COMPONENTS_LIST: componentsText,
2248
2349
  VISUALIZATION_CONSTRAINT: visualizationConstraint,
2249
2350
  USER_PROMPT: userPrompt,
2250
2351
  CONVERSATION_HISTORY: conversationHistory || "No previous conversation"
2251
2352
  });
2353
+ logger.debug("single-component: System prompt\n", prompts.system.substring(0, 100), "\n\n\n", "User prompt:", prompts.user.substring(0, 50));
2252
2354
  const result = await LLM.stream(
2253
2355
  {
2254
2356
  sys: prompts.system,
@@ -2263,53 +2365,63 @@ var BaseLLM = class {
2263
2365
  true
2264
2366
  // Parse as JSON
2265
2367
  );
2266
- if (!result.canGenerate) {
2368
+ if (!result.canGenerate || result.confidence < 50) {
2267
2369
  logCollector?.warn(
2268
- "Cannot generate component",
2370
+ "Cannot match component",
2269
2371
  "explanation",
2270
- { reason: result.reasoning || "Unable to generate component for this question" }
2372
+ { reason: result.reasoning || "Unable to find matching component for this question" }
2271
2373
  );
2272
2374
  return {
2273
2375
  component: null,
2274
- reasoning: result.reasoning || "Unable to generate component for this question",
2376
+ reasoning: result.reasoning || "Unable to find matching component for this question",
2275
2377
  isGenerated: false
2276
2378
  };
2277
2379
  }
2278
- const query = ensureQueryLimit(result.query, this.defaultLimit);
2279
- logCollector?.logQuery(
2280
- "Analytical component query generated",
2281
- query,
2282
- {
2283
- componentType: result.componentType,
2284
- visualization: preferredVisualizationType || result.componentType,
2285
- title: result.title
2286
- }
2380
+ const componentIndex = result.componentIndex;
2381
+ const componentId = result.componentId;
2382
+ let matchedComponent = null;
2383
+ if (componentId) {
2384
+ matchedComponent = filteredComponents.find((c) => c.id === componentId);
2385
+ }
2386
+ if (!matchedComponent && componentIndex) {
2387
+ matchedComponent = filteredComponents[componentIndex - 1];
2388
+ }
2389
+ if (!matchedComponent) {
2390
+ logCollector?.warn("Component not found in filtered list");
2391
+ return {
2392
+ component: null,
2393
+ reasoning: "Component not found in filtered list",
2394
+ isGenerated: false
2395
+ };
2396
+ }
2397
+ logCollector?.info(`Matched component: ${matchedComponent.name} (confidence: ${result.confidence}%)`);
2398
+ const propsValidation = await this.validateAndModifyProps(
2399
+ userPrompt,
2400
+ matchedComponent.props,
2401
+ matchedComponent.name,
2402
+ matchedComponent.type,
2403
+ matchedComponent.description,
2404
+ apiKey,
2405
+ logCollector,
2406
+ conversationHistory
2287
2407
  );
2408
+ const modifiedComponent = {
2409
+ ...matchedComponent,
2410
+ props: propsValidation.props
2411
+ };
2288
2412
  logCollector?.logExplanation(
2289
- "Analytical component generated",
2290
- result.reasoning || "Generated dynamic component based on analytical question",
2413
+ "Analytical component selected and modified",
2414
+ result.reasoning || "Selected component based on analytical question",
2291
2415
  {
2292
- componentType: result.componentType,
2293
- description: result.description
2416
+ componentName: matchedComponent.name,
2417
+ componentType: matchedComponent.type,
2418
+ confidence: result.confidence,
2419
+ propsModified: propsValidation.isModified
2294
2420
  }
2295
2421
  );
2296
- const dynamicComponent = {
2297
- id: `dynamic_${Date.now()}`,
2298
- name: `Dynamic${result.componentType}`,
2299
- type: result.componentType,
2300
- description: result.description,
2301
- category: "dynamic",
2302
- keywords: [],
2303
- props: {
2304
- query,
2305
- title: result.title,
2306
- description: result.description,
2307
- config: result.config || {}
2308
- }
2309
- };
2310
2422
  return {
2311
- component: dynamicComponent,
2312
- reasoning: result.reasoning || "Generated dynamic component based on analytical question",
2423
+ component: modifiedComponent,
2424
+ reasoning: result.reasoning || "Selected and modified component based on analytical question",
2313
2425
  isGenerated: true
2314
2426
  };
2315
2427
  } catch (error) {
@@ -2317,6 +2429,51 @@ var BaseLLM = class {
2317
2429
  throw error;
2318
2430
  }
2319
2431
  }
2432
+ /**
2433
+ * Generate container metadata (title and description) for multi-component dashboard
2434
+ */
2435
+ async generateContainerMetadata(userPrompt, visualizationTypes, apiKey, logCollector, conversationHistory) {
2436
+ try {
2437
+ const prompts = await promptLoader.loadPrompts("container-metadata", {
2438
+ USER_PROMPT: userPrompt,
2439
+ VISUALIZATION_TYPES: visualizationTypes.join(", "),
2440
+ CONVERSATION_HISTORY: conversationHistory || "No previous conversation"
2441
+ });
2442
+ const result = await LLM.stream(
2443
+ {
2444
+ sys: prompts.system,
2445
+ user: prompts.user
2446
+ },
2447
+ {
2448
+ model: this.model,
2449
+ maxTokens: 500,
2450
+ temperature: 0.3,
2451
+ apiKey: this.getApiKey(apiKey)
2452
+ },
2453
+ true
2454
+ // Parse as JSON
2455
+ );
2456
+ logCollector?.logExplanation(
2457
+ "Container metadata generated",
2458
+ `Generated title and description for multi-component dashboard`,
2459
+ {
2460
+ title: result.title,
2461
+ description: result.description,
2462
+ visualizationTypes
2463
+ }
2464
+ );
2465
+ return {
2466
+ title: result.title || `${userPrompt} - Dashboard`,
2467
+ description: result.description || `Multi-component dashboard showing ${visualizationTypes.join(", ")}`
2468
+ };
2469
+ } catch (error) {
2470
+ console.error("Error generating container metadata:", error);
2471
+ return {
2472
+ title: `${userPrompt} - Dashboard`,
2473
+ description: `Multi-component dashboard showing ${visualizationTypes.join(", ")}`
2474
+ };
2475
+ }
2476
+ }
2320
2477
  /**
2321
2478
  * Match component from a list with enhanced props modification
2322
2479
  */
@@ -2378,12 +2535,12 @@ var BaseLLM = class {
2378
2535
  const noMatchMsg = `No matching component found (confidence: ${confidence}%)`;
2379
2536
  console.log("\u2717", noMatchMsg);
2380
2537
  logCollector?.warn(noMatchMsg);
2381
- const genMsg = "Attempting to generate dynamic component from analytical question...";
2538
+ const genMsg = "Attempting to match component from analytical question...";
2382
2539
  console.log("\u2713", genMsg);
2383
2540
  logCollector?.info(genMsg);
2384
- const generatedResult = await this.generateAnalyticalComponent(userPrompt, void 0, apiKey, logCollector, conversationHistory);
2541
+ const generatedResult = await this.generateAnalyticalComponent(userPrompt, components, void 0, apiKey, logCollector, conversationHistory);
2385
2542
  if (generatedResult.component) {
2386
- const genSuccessMsg = `Successfully generated component: ${generatedResult.component.name}`;
2543
+ const genSuccessMsg = `Successfully matched component: ${generatedResult.component.name}`;
2387
2544
  logCollector?.info(genSuccessMsg);
2388
2545
  return {
2389
2546
  component: generatedResult.component,
@@ -2395,10 +2552,10 @@ var BaseLLM = class {
2395
2552
  queryModified: false
2396
2553
  };
2397
2554
  }
2398
- logCollector?.error("Failed to generate dynamic component");
2555
+ logCollector?.error("Failed to match component");
2399
2556
  return {
2400
2557
  component: null,
2401
- reasoning: result.reasoning || "No matching component found and unable to generate dynamic component",
2558
+ reasoning: result.reasoning || "No matching component found and unable to match component",
2402
2559
  method: `${this.getProviderName()}-llm`,
2403
2560
  confidence
2404
2561
  };
@@ -2446,15 +2603,15 @@ var BaseLLM = class {
2446
2603
  }
2447
2604
  }
2448
2605
  /**
2449
- * Generate multiple dynamic components for analytical questions
2606
+ * Match multiple components for analytical questions by visualization types
2450
2607
  * This is used when the user needs multiple visualizations
2451
2608
  */
2452
- async generateMultipleAnalyticalComponents(userPrompt, visualizationTypes, apiKey, logCollector, conversationHistory) {
2609
+ async generateMultipleAnalyticalComponents(userPrompt, availableComponents, visualizationTypes, apiKey, logCollector, conversationHistory) {
2453
2610
  try {
2454
- console.log("\u2713 Generating multiple components:", visualizationTypes);
2611
+ console.log("\u2713 Matching multiple components:", visualizationTypes);
2455
2612
  const components = [];
2456
2613
  for (const vizType of visualizationTypes) {
2457
- const result = await this.generateAnalyticalComponent(userPrompt, vizType, apiKey, logCollector, conversationHistory);
2614
+ const result = await this.generateAnalyticalComponent(userPrompt, availableComponents, vizType, apiKey, logCollector, conversationHistory);
2458
2615
  if (result.component) {
2459
2616
  components.push(result.component);
2460
2617
  }
@@ -2462,75 +2619,45 @@ var BaseLLM = class {
2462
2619
  if (components.length === 0) {
2463
2620
  return {
2464
2621
  components: [],
2465
- reasoning: "Failed to generate any components",
2622
+ reasoning: "Failed to match any components",
2466
2623
  isGenerated: false
2467
2624
  };
2468
2625
  }
2469
2626
  return {
2470
2627
  components,
2471
- reasoning: `Generated ${components.length} components: ${visualizationTypes.join(", ")}`,
2628
+ reasoning: `Matched ${components.length} components: ${visualizationTypes.join(", ")}`,
2472
2629
  isGenerated: true
2473
2630
  };
2474
2631
  } catch (error) {
2475
- console.error("Error generating multiple analytical components:", error);
2632
+ console.error("Error matching multiple analytical components:", error);
2476
2633
  return {
2477
2634
  components: [],
2478
- reasoning: "Error occurred while generating components",
2635
+ reasoning: "Error occurred while matching components",
2479
2636
  isGenerated: false
2480
2637
  };
2481
2638
  }
2482
2639
  }
2483
2640
  /**
2484
- * Generate a complete multi-component response with intelligent container and component props
2641
+ * Match multiple components and wrap them in a container
2485
2642
  */
2486
- async generateMultiComponentResponse(userPrompt, visualizationTypes, apiKey, logCollector, conversationHistory) {
2487
- const schemaDoc = schema.generateSchemaDocumentation();
2643
+ async generateMultiComponentResponse(userPrompt, availableComponents, visualizationTypes, apiKey, logCollector, conversationHistory) {
2488
2644
  try {
2489
- const prompts = await promptLoader.loadPrompts("mutli-component", {
2490
- SCHEMA_DOC: schemaDoc || "No schema available",
2491
- DEFAULT_LIMIT: this.defaultLimit,
2492
- USER_PROMPT: userPrompt,
2493
- VISUALIZATION_TYPES: visualizationTypes.join(", "),
2494
- CONVERSATION_HISTORY: conversationHistory || "No previous conversation"
2495
- });
2496
- const result = await LLM.stream(
2497
- {
2498
- sys: prompts.system,
2499
- user: prompts.user
2500
- },
2501
- {
2502
- model: this.model,
2503
- maxTokens: 3e3,
2504
- temperature: 0.2,
2505
- apiKey: this.getApiKey(apiKey)
2506
- },
2507
- true
2508
- // Parse as JSON
2645
+ const matchResult = await this.generateMultipleAnalyticalComponents(
2646
+ userPrompt,
2647
+ availableComponents,
2648
+ visualizationTypes,
2649
+ apiKey,
2650
+ logCollector,
2651
+ conversationHistory
2509
2652
  );
2510
- if (!result.canGenerate || !result.components || result.components.length === 0) {
2653
+ if (!matchResult.isGenerated || matchResult.components.length === 0) {
2511
2654
  return {
2512
2655
  containerComponent: null,
2513
- reasoning: result.reasoning || "Unable to generate multi-component dashboard",
2656
+ reasoning: matchResult.reasoning || "Unable to match multi-component dashboard",
2514
2657
  isGenerated: false
2515
2658
  };
2516
2659
  }
2517
- const generatedComponents = result.components.map((compData, index) => {
2518
- const query = ensureQueryLimit(compData.query, this.defaultLimit);
2519
- return {
2520
- id: `dynamic_${compData.componentType.toLowerCase()}_${Date.now()}_${index}`,
2521
- name: `Dynamic${compData.componentType}`,
2522
- type: compData.componentType,
2523
- description: compData.description,
2524
- category: "dynamic",
2525
- keywords: [],
2526
- props: {
2527
- query,
2528
- title: compData.title,
2529
- description: compData.description,
2530
- config: compData.config || {}
2531
- }
2532
- };
2533
- });
2660
+ const generatedComponents = matchResult.components;
2534
2661
  generatedComponents.forEach((component, index) => {
2535
2662
  if (component.props.query) {
2536
2663
  logCollector?.logQuery(
@@ -2545,21 +2672,24 @@ var BaseLLM = class {
2545
2672
  );
2546
2673
  }
2547
2674
  });
2675
+ const containerTitle = `${userPrompt} - Dashboard`;
2676
+ const containerDescription = `Multi-component dashboard showing ${visualizationTypes.join(", ")}`;
2548
2677
  logCollector?.logExplanation(
2549
- "Multi-component dashboard generated",
2550
- result.reasoning || `Generated ${generatedComponents.length} components for comprehensive analysis`,
2678
+ "Multi-component dashboard matched",
2679
+ matchResult.reasoning || `Matched ${generatedComponents.length} components for comprehensive analysis`,
2551
2680
  {
2552
2681
  totalComponents: generatedComponents.length,
2553
2682
  componentTypes: generatedComponents.map((c) => c.type),
2554
- containerTitle: result.containerTitle,
2555
- containerDescription: result.containerDescription
2683
+ componentNames: generatedComponents.map((c) => c.name),
2684
+ containerTitle,
2685
+ containerDescription
2556
2686
  }
2557
2687
  );
2558
2688
  const containerComponent = {
2559
2689
  id: `multi_container_${Date.now()}`,
2560
2690
  name: "MultiComponentContainer",
2561
2691
  type: "Container",
2562
- description: result.containerDescription,
2692
+ description: containerDescription,
2563
2693
  category: "dynamic",
2564
2694
  keywords: ["multi", "container", "dashboard"],
2565
2695
  props: {
@@ -2567,14 +2697,14 @@ var BaseLLM = class {
2567
2697
  components: generatedComponents,
2568
2698
  layout: "grid",
2569
2699
  spacing: 24,
2570
- title: result.containerTitle,
2571
- description: result.containerDescription
2700
+ title: containerTitle,
2701
+ description: containerDescription
2572
2702
  }
2573
2703
  }
2574
2704
  };
2575
2705
  return {
2576
2706
  containerComponent,
2577
- reasoning: result.reasoning || `Generated multi-component dashboard with ${generatedComponents.length} components`,
2707
+ reasoning: matchResult.reasoning || `Matched multi-component dashboard with ${generatedComponents.length} components`,
2578
2708
  isGenerated: true
2579
2709
  };
2580
2710
  } catch (error) {
@@ -2595,41 +2725,99 @@ var BaseLLM = class {
2595
2725
  const classInfo = `Question type: ${classification.questionType}, Visualizations: ${classification.visualizations.join(", ") || "None"}, Multiple components: ${classification.needsMultipleComponents}`;
2596
2726
  logCollector?.info(classInfo);
2597
2727
  if (classification.questionType === "analytical") {
2598
- if (classification.visualizations.length > 0) {
2599
- if (classification.needsMultipleComponents && classification.visualizations.length > 1) {
2600
- const multiMsg = "Generating multi-component dashboard...";
2601
- logCollector?.info(multiMsg);
2602
- const result = await this.generateMultiComponentResponse(
2728
+ if (classification.visualizations.length > 1) {
2729
+ const multiMsg = `Matching ${classification.visualizations.length} components for types: ${classification.visualizations.join(", ")}`;
2730
+ logCollector?.info(multiMsg);
2731
+ const componentPromises = classification.visualizations.map((vizType) => {
2732
+ logCollector?.info(`Matching component for type: ${vizType}`);
2733
+ return this.generateAnalyticalComponent(
2603
2734
  userPrompt,
2604
- classification.visualizations,
2735
+ components,
2736
+ vizType,
2605
2737
  apiKey,
2606
2738
  logCollector,
2607
2739
  conversationHistory
2608
- );
2740
+ ).then((result) => ({ vizType, result }));
2741
+ });
2742
+ const settledResults = await Promise.allSettled(componentPromises);
2743
+ const matchedComponents = [];
2744
+ for (const settledResult of settledResults) {
2745
+ if (settledResult.status === "fulfilled") {
2746
+ const { vizType, result } = settledResult.value;
2747
+ if (result.component) {
2748
+ matchedComponents.push(result.component);
2749
+ logCollector?.info(`Matched: ${result.component.name}`);
2750
+ logger.info("Component : ", result.component.name, " props: ", result.component.props);
2751
+ } else {
2752
+ logCollector?.warn(`Failed to match component for type: ${vizType}`);
2753
+ }
2754
+ } else {
2755
+ logCollector?.warn(`Error matching component: ${settledResult.reason?.message || "Unknown error"}`);
2756
+ }
2757
+ }
2758
+ console.log("matched components: after multi comp", matchedComponents.length);
2759
+ if (matchedComponents.length === 0) {
2609
2760
  return {
2610
- component: result.containerComponent,
2611
- reasoning: result.reasoning,
2612
- method: "classification-multi-generated",
2761
+ component: null,
2762
+ reasoning: "Failed to match any components for the requested visualization types",
2763
+ method: "classification-multi-failed",
2613
2764
  questionType: classification.questionType,
2614
2765
  needsMultipleComponents: true,
2615
2766
  propsModified: false,
2616
2767
  queryModified: false
2617
2768
  };
2618
- } else {
2619
- const vizType = classification.visualizations[0];
2620
- const result = await this.generateAnalyticalComponent(userPrompt, vizType, apiKey, logCollector, conversationHistory);
2621
- return {
2622
- component: result.component,
2623
- reasoning: result.reasoning,
2624
- method: "classification-generated",
2625
- questionType: classification.questionType,
2626
- needsMultipleComponents: false,
2627
- propsModified: false,
2628
- queryModified: false
2629
- };
2630
2769
  }
2770
+ logCollector?.info("Generating container metadata...");
2771
+ const containerMetadata = await this.generateContainerMetadata(
2772
+ userPrompt,
2773
+ classification.visualizations,
2774
+ apiKey,
2775
+ logCollector,
2776
+ conversationHistory
2777
+ );
2778
+ const containerComponent = {
2779
+ id: `multi_container_${Date.now()}`,
2780
+ name: "MultiComponentContainer",
2781
+ type: "Container",
2782
+ description: containerMetadata.description,
2783
+ category: "dynamic",
2784
+ keywords: ["multi", "container", "dashboard"],
2785
+ props: {
2786
+ config: {
2787
+ components: matchedComponents,
2788
+ layout: "grid",
2789
+ spacing: 24,
2790
+ title: containerMetadata.title,
2791
+ description: containerMetadata.description
2792
+ }
2793
+ }
2794
+ };
2795
+ logCollector?.info(`Created multi-component container with ${matchedComponents.length} components: "${containerMetadata.title}"`);
2796
+ return {
2797
+ component: containerComponent,
2798
+ reasoning: `Matched ${matchedComponents.length} components for visualization types: ${classification.visualizations.join(", ")}`,
2799
+ method: "classification-multi-generated",
2800
+ questionType: classification.questionType,
2801
+ needsMultipleComponents: true,
2802
+ propsModified: false,
2803
+ queryModified: false
2804
+ };
2805
+ } else if (classification.visualizations.length === 1) {
2806
+ const vizType = classification.visualizations[0];
2807
+ logCollector?.info(`Matching single component for type: ${vizType}`);
2808
+ const result = await this.generateAnalyticalComponent(userPrompt, components, vizType, apiKey, logCollector, conversationHistory);
2809
+ return {
2810
+ component: result.component,
2811
+ reasoning: result.reasoning,
2812
+ method: "classification-generated",
2813
+ questionType: classification.questionType,
2814
+ needsMultipleComponents: false,
2815
+ propsModified: false,
2816
+ queryModified: false
2817
+ };
2631
2818
  } else {
2632
- const result = await this.generateAnalyticalComponent(userPrompt, void 0, apiKey, logCollector, conversationHistory);
2819
+ logCollector?.info("No specific visualization type - matching from all components");
2820
+ const result = await this.generateAnalyticalComponent(userPrompt, components, void 0, apiKey, logCollector, conversationHistory);
2633
2821
  return {
2634
2822
  component: result.component,
2635
2823
  reasoning: result.reasoning,
@@ -2640,7 +2828,7 @@ var BaseLLM = class {
2640
2828
  queryModified: false
2641
2829
  };
2642
2830
  }
2643
- } else if (classification.questionType === "data_modification") {
2831
+ } else if (classification.questionType === "data_modification" || classification.questionType === "general") {
2644
2832
  const matchMsg = "Using component matching for data modification...";
2645
2833
  logCollector?.info(matchMsg);
2646
2834
  const matchResult = await this.matchComponent(userPrompt, components, apiKey, logCollector, conversationHistory);
@@ -2788,7 +2976,6 @@ var useAnthropicMethod = async (prompt, components, apiKey, logCollector, conver
2788
2976
  }
2789
2977
  try {
2790
2978
  const matchResult = await anthropicLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory);
2791
- logger.debug(`Anthropic method success: ${matchResult}`);
2792
2979
  return { success: true, data: matchResult };
2793
2980
  } catch (error) {
2794
2981
  const errorMsg = error instanceof Error ? error.message : String(error);
@@ -2842,7 +3029,6 @@ var get_user_response = async (prompt, components, anthropicApiKey, groqApiKey,
2842
3029
  let result;
2843
3030
  if (provider === "anthropic") {
2844
3031
  result = await useAnthropicMethod(prompt, components, anthropicApiKey, logCollector, conversationHistory);
2845
- logger.debug("Anthropic result:", result);
2846
3032
  } else if (provider === "groq") {
2847
3033
  result = await useGroqMethod(prompt, components, groqApiKey, logCollector, conversationHistory);
2848
3034
  } else {
@@ -2905,6 +3091,7 @@ var UILogCollector = class {
2905
3091
  };
2906
3092
  this.logs.push(log);
2907
3093
  this.sendLogImmediately(log);
3094
+ console.log("UILogCollector:", log);
2908
3095
  }
2909
3096
  /**
2910
3097
  * Send a single log to runtime immediately
@@ -3043,7 +3230,6 @@ async function handleUserPromptRequest(data, components, sendMessage, anthropicA
3043
3230
  const SA_RUNTIME = payload.SA_RUNTIME;
3044
3231
  const wsId = userPromptRequest.from.id || "unknown";
3045
3232
  logger.info(`[REQUEST ${id}] Processing user prompt: "${prompt.substring(0, 50)}..."`);
3046
- logger.info(`[REQUEST ${id}] Providers: ${llmProviders?.join(", ")}, Anthropic key: ${anthropicApiKey ? "SET" : "NOT SET"}, Groq key: ${groqApiKey ? "SET" : "NOT SET"}`);
3047
3233
  if (processedMessageIds.has(id)) {
3048
3234
  logger.warn(`[REQUEST ${id}] Duplicate request detected - ignoring`);
3049
3235
  return;
@@ -3093,18 +3279,18 @@ async function handleUserPromptRequest(data, components, sendMessage, anthropicA
3093
3279
  }, sendMessage, wsId);
3094
3280
  return;
3095
3281
  }
3096
- logCollector.info(`Starting user prompt request with ${components.length} components`);
3097
- logger.info(`components length: ${components.length}`);
3098
3282
  const threadManager = ThreadManager.getInstance();
3099
3283
  let thread = threadManager.getThread(threadId);
3100
3284
  if (!thread) {
3101
3285
  thread = threadManager.createThread(threadId);
3102
3286
  logger.info(`Created new thread: ${threadId}`);
3103
3287
  }
3288
+ logCollector.info(`Starting user prompt request with ${components.length} components`);
3104
3289
  const conversationHistory = thread.getConversationContext(CONTEXT_CONFIG.MAX_CONVERSATION_CONTEXT_BLOCKS, existingUiBlockId);
3290
+ logger.info("conversationHistory", conversationHistory);
3105
3291
  const userResponse = await get_user_response(prompt, components, anthropicApiKey, groqApiKey, llmProviders, logCollector, conversationHistory);
3292
+ logger.info("llm userResponse", userResponse);
3106
3293
  logCollector.info("User prompt request completed");
3107
- logger.info(`[REQUEST ${id}] Response success: ${userResponse.success}, reason: ${userResponse.success ? "N/A" : userResponse}`);
3108
3294
  if (userResponse.success && userResponse.data && typeof userResponse.data === "object" && "component" in userResponse.data) {
3109
3295
  const component = userResponse.data.component;
3110
3296
  const uiBlockId = existingUiBlockId;
@@ -3146,10 +3332,7 @@ function sendDataResponse4(id, res, sendMessage, clientId) {
3146
3332
  ...res
3147
3333
  }
3148
3334
  };
3149
- logger.info(`[REQUEST ${id}] Sending USER_PROMPT_RES with success=${res.success}`);
3150
- if (!res.success && res.reason) {
3151
- logger.info(`[REQUEST ${id}] Error reason: ${res.reason}`);
3152
- }
3335
+ logger.info("sending user prompt response", response);
3153
3336
  sendMessage(response);
3154
3337
  }
3155
3338
 
@@ -4848,6 +5031,9 @@ var SuperatomSDK = class {
4848
5031
  this.userManager = new UserManager(this.projectId, 5e3);
4849
5032
  this.dashboardManager = new DashboardManager(this.projectId);
4850
5033
  this.reportManager = new ReportManager(this.projectId);
5034
+ this.initializePromptLoader(config.promptsDir).catch((error) => {
5035
+ logger.error("Failed to initialize PromptLoader:", error);
5036
+ });
4851
5037
  this.initializeUserManager().catch((error) => {
4852
5038
  logger.error("Failed to initialize UserManager:", error);
4853
5039
  });
@@ -4857,6 +5043,21 @@ var SuperatomSDK = class {
4857
5043
  logger.error("Failed to connect to Superatom:", error);
4858
5044
  });
4859
5045
  }
5046
+ /**
5047
+ * Initialize PromptLoader and load prompts into memory
5048
+ */
5049
+ async initializePromptLoader(promptsDir) {
5050
+ try {
5051
+ if (promptsDir) {
5052
+ promptLoader.setPromptsDir(promptsDir);
5053
+ }
5054
+ await promptLoader.initialize();
5055
+ logger.info(`PromptLoader initialized with ${promptLoader.getCacheSize()} prompts from ${promptLoader.getPromptsDir()}`);
5056
+ } catch (error) {
5057
+ logger.error("Failed to initialize PromptLoader:", error);
5058
+ throw error;
5059
+ }
5060
+ }
4860
5061
  /**
4861
5062
  * Initialize UserManager for the project
4862
5063
  */