@superatomai/sdk-node 0.0.5 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -10,9 +10,16 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
10
10
  if (typeof require !== "undefined") return require.apply(this, arguments);
11
11
  throw Error('Dynamic require of "' + x + '" is not supported');
12
12
  });
13
+ var __esm = (fn, res) => function __init() {
14
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
15
+ };
13
16
  var __commonJS = (cb, mod) => function __require2() {
14
17
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
15
18
  };
19
+ var __export = (target, all) => {
20
+ for (var name in all)
21
+ __defProp(target, name, { get: all[name], enumerable: true });
22
+ };
16
23
  var __copyProps = (to, from, except, desc) => {
17
24
  if (from && typeof from === "object" || typeof from === "function") {
18
25
  for (let key of __getOwnPropNames(from))
@@ -102,7 +109,7 @@ var require_package = __commonJS({
102
109
  var require_main = __commonJS({
103
110
  "node_modules/dotenv/lib/main.js"(exports, module) {
104
111
  "use strict";
105
- var fs7 = __require("fs");
112
+ var fs8 = __require("fs");
106
113
  var path7 = __require("path");
107
114
  var os4 = __require("os");
108
115
  var crypto2 = __require("crypto");
@@ -244,7 +251,7 @@ var require_main = __commonJS({
244
251
  if (options && options.path && options.path.length > 0) {
245
252
  if (Array.isArray(options.path)) {
246
253
  for (const filepath of options.path) {
247
- if (fs7.existsSync(filepath)) {
254
+ if (fs8.existsSync(filepath)) {
248
255
  possibleVaultPath = filepath.endsWith(".vault") ? filepath : `${filepath}.vault`;
249
256
  }
250
257
  }
@@ -254,7 +261,7 @@ var require_main = __commonJS({
254
261
  } else {
255
262
  possibleVaultPath = path7.resolve(process.cwd(), ".env.vault");
256
263
  }
257
- if (fs7.existsSync(possibleVaultPath)) {
264
+ if (fs8.existsSync(possibleVaultPath)) {
258
265
  return possibleVaultPath;
259
266
  }
260
267
  return null;
@@ -307,7 +314,7 @@ var require_main = __commonJS({
307
314
  const parsedAll = {};
308
315
  for (const path8 of optionPaths) {
309
316
  try {
310
- const parsed = DotenvModule.parse(fs7.readFileSync(path8, { encoding }));
317
+ const parsed = DotenvModule.parse(fs8.readFileSync(path8, { encoding }));
311
318
  DotenvModule.populate(parsedAll, parsed, options);
312
319
  } catch (e) {
313
320
  if (debug) {
@@ -428,6 +435,128 @@ var require_main = __commonJS({
428
435
  }
429
436
  });
430
437
 
438
+ // src/userResponse/utils.ts
439
+ var utils_exports = {};
440
+ __export(utils_exports, {
441
+ convertTopToLimit: () => convertTopToLimit,
442
+ ensureQueryLimit: () => ensureQueryLimit,
443
+ fixScalarSubqueries: () => fixScalarSubqueries,
444
+ getJsonSizeInBytes: () => getJsonSizeInBytes,
445
+ validateMessageSize: () => validateMessageSize
446
+ });
447
+ function convertTopToLimit(query) {
448
+ if (!query || query.trim().length === 0) {
449
+ return query;
450
+ }
451
+ let modifiedQuery = query.replace(/\bSELECT\s+TOP\s+(\d+)\b/gi, "SELECT");
452
+ if (modifiedQuery !== query) {
453
+ console.warn(`\u26A0\uFE0F Query had TOP syntax. Converting to LIMIT for Snowflake compatibility.`);
454
+ }
455
+ return modifiedQuery;
456
+ }
457
+ function ensureQueryLimit(query, defaultLimit = 32, maxLimit = 32) {
458
+ if (!query || query.trim().length === 0) {
459
+ return query;
460
+ }
461
+ let trimmedQuery = query.trim();
462
+ const isSelectQuery = /^\s*SELECT\b/i.test(trimmedQuery) || /^\s*WITH\b.*\bSELECT\b/is.test(trimmedQuery);
463
+ if (!isSelectQuery) {
464
+ return query;
465
+ }
466
+ trimmedQuery = convertTopToLimit(trimmedQuery);
467
+ const hadSemicolon = trimmedQuery.endsWith(";");
468
+ if (hadSemicolon) {
469
+ trimmedQuery = trimmedQuery.slice(0, -1).trim();
470
+ }
471
+ const limitMatches = trimmedQuery.match(/\bLIMIT\s+(\d+)\b/gi);
472
+ if (limitMatches && limitMatches.length > 0) {
473
+ if (limitMatches.length > 1) {
474
+ console.warn(`\u26A0\uFE0F Query had ${limitMatches.length} LIMIT clauses. Removing duplicates...`);
475
+ trimmedQuery = trimmedQuery.replace(/\s*\bLIMIT\s+\d+\b/gi, "").trim();
476
+ } else {
477
+ const existingLimitMatch = trimmedQuery.match(/\bLIMIT\s+(\d+)\b/i);
478
+ if (existingLimitMatch) {
479
+ const existingLimit = parseInt(existingLimitMatch[1], 10);
480
+ if (existingLimit <= maxLimit) {
481
+ if (hadSemicolon) {
482
+ trimmedQuery += ";";
483
+ }
484
+ return trimmedQuery;
485
+ }
486
+ console.warn(`\u26A0\uFE0F Query LIMIT ${existingLimit} exceeds maximum of ${maxLimit}. Reducing to ${maxLimit}...`);
487
+ trimmedQuery = trimmedQuery.replace(/\bLIMIT\s+\d+\b/i, `LIMIT ${maxLimit}`);
488
+ if (hadSemicolon) {
489
+ trimmedQuery += ";";
490
+ }
491
+ return trimmedQuery;
492
+ }
493
+ }
494
+ }
495
+ trimmedQuery = `${trimmedQuery} LIMIT ${defaultLimit}`;
496
+ if (hadSemicolon) {
497
+ trimmedQuery += ";";
498
+ }
499
+ return trimmedQuery;
500
+ }
501
+ function fixScalarSubqueries(query) {
502
+ if (!query || query.trim().length === 0) {
503
+ return query;
504
+ }
505
+ let modifiedQuery = query;
506
+ let hasChanges = false;
507
+ const scalarOperatorPattern = /([=<>!]=?|<>)\s*\(\s*SELECT\s/gi;
508
+ const matches = [...modifiedQuery.matchAll(scalarOperatorPattern)];
509
+ for (let i = matches.length - 1; i >= 0; i--) {
510
+ const match = matches[i];
511
+ const startPos = match.index + match[0].length - "SELECT ".length;
512
+ let parenDepth = 1;
513
+ let endPos = startPos;
514
+ let foundEnd = false;
515
+ for (let j = startPos; j < modifiedQuery.length; j++) {
516
+ const char = modifiedQuery[j];
517
+ if (char === "(") parenDepth++;
518
+ if (char === ")") {
519
+ parenDepth--;
520
+ if (parenDepth === 0) {
521
+ endPos = j;
522
+ foundEnd = true;
523
+ break;
524
+ }
525
+ }
526
+ }
527
+ if (!foundEnd) continue;
528
+ const subquery = modifiedQuery.substring(startPos, endPos);
529
+ if (/\bLIMIT\s+\d+/i.test(subquery)) {
530
+ continue;
531
+ }
532
+ const fixedSubquery = subquery.trim() + " LIMIT 1";
533
+ modifiedQuery = modifiedQuery.substring(0, startPos) + fixedSubquery + modifiedQuery.substring(endPos);
534
+ hasChanges = true;
535
+ console.warn(`\u26A0\uFE0F Fixed scalar subquery: added LIMIT 1 to prevent multiple row error`);
536
+ }
537
+ if (hasChanges) {
538
+ console.log("\u2713 Query validated and fixed for PostgreSQL scalar subquery compatibility");
539
+ }
540
+ return modifiedQuery;
541
+ }
542
+ function getJsonSizeInBytes(obj) {
543
+ const jsonString = JSON.stringify(obj);
544
+ return Buffer.byteLength(jsonString, "utf8");
545
+ }
546
+ function validateMessageSize(message, maxSize = 1048576) {
547
+ const size = getJsonSizeInBytes(message);
548
+ return {
549
+ isValid: size <= maxSize,
550
+ size,
551
+ maxSize
552
+ };
553
+ }
554
+ var init_utils = __esm({
555
+ "src/userResponse/utils.ts"() {
556
+ "use strict";
557
+ }
558
+ });
559
+
431
560
  // src/websocket.ts
432
561
  import WebSocket from "ws";
433
562
  function createWebSocket(url) {
@@ -689,7 +818,8 @@ var UserPromptRequestPayloadSchema = z3.object({
689
818
  SA_RUNTIME: z3.object({
690
819
  threadId: z3.string(),
691
820
  uiBlockId: z3.string()
692
- }).optional()
821
+ }).optional(),
822
+ responseMode: z3.enum(["component", "text"]).optional()
693
823
  });
694
824
  var UserPromptRequestMessageSchema = z3.object({
695
825
  id: z3.string(),
@@ -804,7 +934,9 @@ var ReportsRequestMessageSchema = z3.object({
804
934
  });
805
935
 
806
936
  // src/utils/logger.ts
937
+ import fs from "fs";
807
938
  var PREFIX = "[SuperatomSDK]";
939
+ var LOGSTREAM = fs.createWriteStream("superatom-sdk.log", { flags: "a" });
808
940
  var LOG_LEVEL_PRIORITY = {
809
941
  errors: 0,
810
942
  warnings: 1,
@@ -888,6 +1020,9 @@ var Logger = class {
888
1020
  console.log(PREFIX, "[DEBUG]", ...args);
889
1021
  }
890
1022
  }
1023
+ file(...args) {
1024
+ LOGSTREAM.write(args.join(" ") + "\n");
1025
+ }
891
1026
  };
892
1027
  var logger = new Logger();
893
1028
 
@@ -959,6 +1094,9 @@ var UIBlock = class {
959
1094
  getComponentMetadata() {
960
1095
  return this.generatedComponentMetadata;
961
1096
  }
1097
+ getTextResponse() {
1098
+ return this.textResponse || "";
1099
+ }
962
1100
  /**
963
1101
  * Set or update component metadata
964
1102
  */
@@ -1051,12 +1189,6 @@ var UIBlock = class {
1051
1189
  const processedData = this.processDataForStorage(data);
1052
1190
  this.componentData = { ...this.componentData, ...processedData };
1053
1191
  }
1054
- /**
1055
- * Get text response
1056
- */
1057
- getTextResponse() {
1058
- return this.textResponse;
1059
- }
1060
1192
  /**
1061
1193
  * Set or update text response
1062
1194
  */
@@ -1247,8 +1379,11 @@ var Thread = class {
1247
1379
  const questionNum = index + 1;
1248
1380
  const question = block.getUserQuestion();
1249
1381
  const metadata = block.getComponentMetadata();
1250
- let componentSummary = "No component generated";
1251
- if (metadata && Object.keys(metadata).length > 0) {
1382
+ const textResponse = block.getTextResponse();
1383
+ let assistantResponse = "";
1384
+ const hasComponent = metadata && Object.keys(metadata).length > 0 && metadata.type;
1385
+ const hasTextResponse = textResponse && textResponse.trim().length > 0;
1386
+ if (hasComponent) {
1252
1387
  const parts = [];
1253
1388
  if (metadata.type) {
1254
1389
  parts.push(`Component Type: ${metadata.type}`);
@@ -1268,11 +1403,17 @@ var Thread = class {
1268
1403
  const componentTypes = metadata.props.config.components.map((c) => c.type).join(", ");
1269
1404
  parts.push(`Multi-component with: ${componentTypes}`);
1270
1405
  }
1271
- componentSummary = parts.join(", ");
1406
+ assistantResponse = parts.join(", ");
1407
+ } else if (hasTextResponse) {
1408
+ assistantResponse = textResponse;
1409
+ } else {
1410
+ assistantResponse = "No response generated";
1272
1411
  }
1273
- contextLines.push(`Q${questionNum}: ${question}`);
1274
- contextLines.push(`A${questionNum}: ${componentSummary}`);
1275
- contextLines.push("");
1412
+ contextLines.push(`User:
1413
+ ${question}`);
1414
+ contextLines.push(`Assistant:
1415
+ ${assistantResponse}`);
1416
+ contextLines.push("---");
1276
1417
  });
1277
1418
  return contextLines.join("\n").trim();
1278
1419
  }
@@ -1448,7 +1589,7 @@ function sendDataResponse(id, collection, op, data, meta, sendMessage) {
1448
1589
  }
1449
1590
 
1450
1591
  // src/bundle.ts
1451
- import * as fs from "fs";
1592
+ import * as fs2 from "fs";
1452
1593
  import * as path from "path";
1453
1594
  function getBundleDir(configDir) {
1454
1595
  const bundleDir = configDir || process.env.SA_BUNDLE_DIR;
@@ -1461,16 +1602,16 @@ function getBundleDir(configDir) {
1461
1602
  }
1462
1603
  function getJS(bundleDir) {
1463
1604
  try {
1464
- if (!fs.existsSync(bundleDir)) {
1605
+ if (!fs2.existsSync(bundleDir)) {
1465
1606
  throw new Error(`Bundle directory does not exist: ${bundleDir}`);
1466
1607
  }
1467
- const stats = fs.statSync(bundleDir);
1608
+ const stats = fs2.statSync(bundleDir);
1468
1609
  if (!stats.isDirectory()) {
1469
1610
  throw new Error(`Bundle path is not a directory: ${bundleDir}`);
1470
1611
  }
1471
1612
  let files;
1472
1613
  try {
1473
- files = fs.readdirSync(bundleDir);
1614
+ files = fs2.readdirSync(bundleDir);
1474
1615
  } catch (error) {
1475
1616
  const errorMessage = error instanceof Error ? error.message : String(error);
1476
1617
  throw new Error(`Failed to read bundle directory: ${errorMessage}`);
@@ -1485,7 +1626,7 @@ function getJS(bundleDir) {
1485
1626
  const filePath = path.join(bundleDir, indexFile);
1486
1627
  logger.info(`Loading bundle from ${filePath}`);
1487
1628
  try {
1488
- return fs.readFileSync(filePath, "utf8");
1629
+ return fs2.readFileSync(filePath, "utf8");
1489
1630
  } catch (error) {
1490
1631
  const errorMessage = error instanceof Error ? error.message : String(error);
1491
1632
  throw new Error(`Failed to read bundle file: ${errorMessage}`);
@@ -1891,94 +2032,12 @@ function sendDataResponse3(id, res, sendMessage, clientId) {
1891
2032
  // src/userResponse/groq.ts
1892
2033
  var import_dotenv = __toESM(require_main());
1893
2034
 
1894
- // src/userResponse/utils.ts
1895
- function convertTopToLimit(query) {
1896
- if (!query || query.trim().length === 0) {
1897
- return query;
1898
- }
1899
- let modifiedQuery = query.replace(/\bSELECT\s+TOP\s+(\d+)\b/gi, "SELECT");
1900
- if (modifiedQuery !== query) {
1901
- console.warn(`\u26A0\uFE0F Query had TOP syntax. Converting to LIMIT for Snowflake compatibility.`);
1902
- }
1903
- return modifiedQuery;
1904
- }
1905
- function ensureQueryLimit(query, defaultLimit = 50) {
1906
- if (!query || query.trim().length === 0) {
1907
- return query;
1908
- }
1909
- let trimmedQuery = query.trim();
1910
- const isSelectQuery = /^\s*SELECT\b/i.test(trimmedQuery) || /^\s*WITH\b.*\bSELECT\b/is.test(trimmedQuery);
1911
- if (!isSelectQuery) {
1912
- return query;
1913
- }
1914
- trimmedQuery = convertTopToLimit(trimmedQuery);
1915
- const hadSemicolon = trimmedQuery.endsWith(";");
1916
- if (hadSemicolon) {
1917
- trimmedQuery = trimmedQuery.slice(0, -1).trim();
1918
- }
1919
- const limitMatches = trimmedQuery.match(/\bLIMIT\s+\d+\b/gi);
1920
- if (limitMatches && limitMatches.length > 0) {
1921
- if (limitMatches.length > 1) {
1922
- console.warn(`\u26A0\uFE0F Query had ${limitMatches.length} LIMIT clauses. Removing duplicates...`);
1923
- trimmedQuery = trimmedQuery.replace(/\s*\bLIMIT\s+\d+\b/gi, "").trim();
1924
- } else {
1925
- if (hadSemicolon) {
1926
- trimmedQuery += ";";
1927
- }
1928
- return trimmedQuery;
1929
- }
1930
- }
1931
- trimmedQuery = `${trimmedQuery} LIMIT ${defaultLimit}`;
1932
- if (hadSemicolon) {
1933
- trimmedQuery += ";";
1934
- }
1935
- return trimmedQuery;
1936
- }
1937
- function fixScalarSubqueries(query) {
1938
- if (!query || query.trim().length === 0) {
1939
- return query;
1940
- }
1941
- let modifiedQuery = query;
1942
- let hasChanges = false;
1943
- const scalarOperatorPattern = /([=<>!]=?|<>)\s*\(\s*SELECT\s/gi;
1944
- const matches = [...modifiedQuery.matchAll(scalarOperatorPattern)];
1945
- for (let i = matches.length - 1; i >= 0; i--) {
1946
- const match = matches[i];
1947
- const startPos = match.index + match[0].length - "SELECT ".length;
1948
- let parenDepth = 1;
1949
- let endPos = startPos;
1950
- let foundEnd = false;
1951
- for (let j = startPos; j < modifiedQuery.length; j++) {
1952
- const char = modifiedQuery[j];
1953
- if (char === "(") parenDepth++;
1954
- if (char === ")") {
1955
- parenDepth--;
1956
- if (parenDepth === 0) {
1957
- endPos = j;
1958
- foundEnd = true;
1959
- break;
1960
- }
1961
- }
1962
- }
1963
- if (!foundEnd) continue;
1964
- const subquery = modifiedQuery.substring(startPos, endPos);
1965
- if (/\bLIMIT\s+\d+/i.test(subquery)) {
1966
- continue;
1967
- }
1968
- const fixedSubquery = subquery.trim() + " LIMIT 1";
1969
- modifiedQuery = modifiedQuery.substring(0, startPos) + fixedSubquery + modifiedQuery.substring(endPos);
1970
- hasChanges = true;
1971
- console.warn(`\u26A0\uFE0F Fixed scalar subquery: added LIMIT 1 to prevent multiple row error`);
1972
- }
1973
- if (hasChanges) {
1974
- console.log("\u2713 Query validated and fixed for PostgreSQL scalar subquery compatibility");
1975
- }
1976
- return modifiedQuery;
1977
- }
2035
+ // src/userResponse/base-llm.ts
2036
+ init_utils();
1978
2037
 
1979
2038
  // src/userResponse/schema.ts
1980
2039
  import path2 from "path";
1981
- import fs2 from "fs";
2040
+ import fs3 from "fs";
1982
2041
  var Schema = class {
1983
2042
  constructor(schemaFilePath) {
1984
2043
  this.cachedSchema = null;
@@ -1992,11 +2051,11 @@ var Schema = class {
1992
2051
  logger.info(`SCHEMA_FILE_PATH: ${this.schemaFilePath}`);
1993
2052
  try {
1994
2053
  const dir = path2.dirname(this.schemaFilePath);
1995
- if (!fs2.existsSync(dir)) {
2054
+ if (!fs3.existsSync(dir)) {
1996
2055
  logger.info(`Creating directory structure: ${dir}`);
1997
- fs2.mkdirSync(dir, { recursive: true });
2056
+ fs3.mkdirSync(dir, { recursive: true });
1998
2057
  }
1999
- if (!fs2.existsSync(this.schemaFilePath)) {
2058
+ if (!fs3.existsSync(this.schemaFilePath)) {
2000
2059
  logger.info(`Schema file does not exist at ${this.schemaFilePath}, creating with empty schema`);
2001
2060
  const initialSchema = {
2002
2061
  database: "",
@@ -2005,11 +2064,11 @@ var Schema = class {
2005
2064
  tables: [],
2006
2065
  relationships: []
2007
2066
  };
2008
- fs2.writeFileSync(this.schemaFilePath, JSON.stringify(initialSchema, null, 4));
2067
+ fs3.writeFileSync(this.schemaFilePath, JSON.stringify(initialSchema, null, 4));
2009
2068
  this.cachedSchema = initialSchema;
2010
2069
  return initialSchema;
2011
2070
  }
2012
- const fileContent = fs2.readFileSync(this.schemaFilePath, "utf-8");
2071
+ const fileContent = fs3.readFileSync(this.schemaFilePath, "utf-8");
2013
2072
  const schema2 = JSON.parse(fileContent);
2014
2073
  this.cachedSchema = schema2;
2015
2074
  return schema2;
@@ -2110,7 +2169,7 @@ var Schema = class {
2110
2169
  var schema = new Schema();
2111
2170
 
2112
2171
  // src/userResponse/prompt-loader.ts
2113
- import fs3 from "fs";
2172
+ import fs4 from "fs";
2114
2173
  import path3 from "path";
2115
2174
  var PromptLoader = class {
2116
2175
  constructor(config) {
@@ -2138,7 +2197,8 @@ var PromptLoader = class {
2138
2197
  "mutli-component",
2139
2198
  "actions",
2140
2199
  "container-metadata",
2141
- "text-response"
2200
+ "text-response",
2201
+ "match-text-components"
2142
2202
  ];
2143
2203
  for (const promptType of promptTypes) {
2144
2204
  try {
@@ -2163,9 +2223,9 @@ var PromptLoader = class {
2163
2223
  try {
2164
2224
  const systemPath = path3.join(dir, promptName, "system.md");
2165
2225
  const userPath = path3.join(dir, promptName, "user.md");
2166
- if (fs3.existsSync(systemPath) && fs3.existsSync(userPath)) {
2167
- const system = fs3.readFileSync(systemPath, "utf-8");
2168
- const user = fs3.readFileSync(userPath, "utf-8");
2226
+ if (fs4.existsSync(systemPath) && fs4.existsSync(userPath)) {
2227
+ const system = fs4.readFileSync(systemPath, "utf-8");
2228
+ const user = fs4.readFileSync(userPath, "utf-8");
2169
2229
  logger.debug(`Loaded prompt '${promptName}' from ${dir}`);
2170
2230
  return { system, user };
2171
2231
  }
@@ -2287,6 +2347,14 @@ var LLM = class {
2287
2347
  throw new Error(`Unsupported provider: ${provider}. Use "anthropic" or "groq"`);
2288
2348
  }
2289
2349
  }
2350
+ /* Stream response with tool calling support (Anthropic only for now) */
2351
+ static async streamWithTools(messages, tools, toolHandler, options = {}, maxIterations = 3) {
2352
+ const [provider, modelName] = this._parseModel(options.model);
2353
+ if (provider !== "anthropic") {
2354
+ throw new Error(`Tool calling is only supported for Anthropic models`);
2355
+ }
2356
+ return this._anthropicStreamWithTools(messages, tools, toolHandler, modelName, options, maxIterations);
2357
+ }
2290
2358
  // ============================================================
2291
2359
  // PRIVATE HELPER METHODS
2292
2360
  // ============================================================
@@ -2364,6 +2432,86 @@ var LLM = class {
2364
2432
  }
2365
2433
  return fullText;
2366
2434
  }
2435
+ static async _anthropicStreamWithTools(messages, tools, toolHandler, modelName, options, maxIterations) {
2436
+ const apiKey = options.apiKey || process.env.ANTHROPIC_API_KEY || "";
2437
+ const client = new Anthropic({
2438
+ apiKey
2439
+ });
2440
+ const conversationMessages = [{
2441
+ role: "user",
2442
+ content: messages.user
2443
+ }];
2444
+ let iterations = 0;
2445
+ let finalText = "";
2446
+ while (iterations < maxIterations) {
2447
+ iterations++;
2448
+ const response = await client.messages.create({
2449
+ model: modelName,
2450
+ max_tokens: options.maxTokens || 4e3,
2451
+ temperature: options.temperature,
2452
+ system: messages.sys,
2453
+ messages: conversationMessages,
2454
+ tools
2455
+ });
2456
+ if (response.stop_reason === "end_turn") {
2457
+ const textBlock = response.content.find((block) => block.type === "text");
2458
+ if (textBlock && textBlock.type === "text") {
2459
+ finalText = textBlock.text;
2460
+ if (options.partial) {
2461
+ options.partial(finalText);
2462
+ }
2463
+ }
2464
+ break;
2465
+ }
2466
+ if (response.stop_reason === "tool_use") {
2467
+ const toolUses = response.content.filter((block) => block.type === "tool_use");
2468
+ if (toolUses.length === 0) {
2469
+ break;
2470
+ }
2471
+ conversationMessages.push({
2472
+ role: "assistant",
2473
+ content: response.content
2474
+ });
2475
+ const toolResults = {
2476
+ role: "user",
2477
+ content: []
2478
+ };
2479
+ for (const toolUse of toolUses) {
2480
+ if (toolUse.type === "tool_use") {
2481
+ try {
2482
+ const result = await toolHandler(toolUse.name, toolUse.input);
2483
+ toolResults.content.push({
2484
+ type: "tool_result",
2485
+ tool_use_id: toolUse.id,
2486
+ content: typeof result === "string" ? result : JSON.stringify(result)
2487
+ });
2488
+ } catch (error) {
2489
+ toolResults.content.push({
2490
+ type: "tool_result",
2491
+ tool_use_id: toolUse.id,
2492
+ content: error instanceof Error ? error.message : String(error),
2493
+ is_error: true
2494
+ });
2495
+ }
2496
+ }
2497
+ }
2498
+ conversationMessages.push(toolResults);
2499
+ } else {
2500
+ const textBlock = response.content.find((block) => block.type === "text");
2501
+ if (textBlock && textBlock.type === "text") {
2502
+ finalText = textBlock.text;
2503
+ if (options.partial) {
2504
+ options.partial(finalText);
2505
+ }
2506
+ }
2507
+ break;
2508
+ }
2509
+ }
2510
+ if (iterations >= maxIterations) {
2511
+ throw new Error(`Max iterations (${maxIterations}) reached in tool calling loop`);
2512
+ }
2513
+ return finalText;
2514
+ }
2367
2515
  // ============================================================
2368
2516
  // GROQ IMPLEMENTATION
2369
2517
  // ============================================================
@@ -2976,62 +3124,460 @@ var BaseLLM = class {
2976
3124
  throw error;
2977
3125
  }
2978
3126
  }
3127
+ /**
3128
+ * Match components from text response suggestions
3129
+ * Takes a text response with component suggestions (c1:type format) and matches with available components
3130
+ * @param textResponse - The text response containing component suggestions
3131
+ * @param components - List of available components
3132
+ * @param apiKey - Optional API key
3133
+ * @param logCollector - Optional log collector
3134
+ * @returns Object containing matched components, selected layout, and reasoning
3135
+ */
3136
+ async matchComponentsFromTextResponse(textResponse, components, apiKey, logCollector) {
3137
+ try {
3138
+ logger.debug(`[${this.getProviderName()}] Starting component matching from text response`);
3139
+ let availableComponentsText = "No components available";
3140
+ if (components && components.length > 0) {
3141
+ availableComponentsText = components.map((comp, idx) => {
3142
+ const keywords = comp.keywords ? comp.keywords.join(", ") : "";
3143
+ const propsPreview = comp.props ? JSON.stringify(comp.props, null, 2) : "No props";
3144
+ return `${idx + 1}. ID: ${comp.id}
3145
+ Name: ${comp.name}
3146
+ Type: ${comp.type}
3147
+ Description: ${comp.description || "No description"}
3148
+ Keywords: ${keywords}
3149
+ Props Structure: ${propsPreview}`;
3150
+ }).join("\n\n");
3151
+ }
3152
+ const schemaDoc = schema.generateSchemaDocumentation();
3153
+ const prompts = await promptLoader.loadPrompts("match-text-components", {
3154
+ TEXT_RESPONSE: textResponse,
3155
+ AVAILABLE_COMPONENTS: availableComponentsText,
3156
+ SCHEMA_DOC: schemaDoc
3157
+ });
3158
+ logger.debug(`[${this.getProviderName()}] Loaded match-text-components prompts`);
3159
+ logger.file("\n=============================\nmatch text components system prompt:", prompts.system);
3160
+ logCollector?.info("Matching components from text response...");
3161
+ const rawResponse = await LLM.stream(
3162
+ {
3163
+ sys: prompts.system,
3164
+ user: prompts.user
3165
+ },
3166
+ {
3167
+ model: this.model,
3168
+ maxTokens: 3e3,
3169
+ temperature: 0.2,
3170
+ apiKey: this.getApiKey(apiKey)
3171
+ },
3172
+ false
3173
+ // Don't parse as JSON yet, get raw response
3174
+ );
3175
+ logger.debug(`[${this.getProviderName()}] Raw component matching response length: ${rawResponse?.length || 0}`);
3176
+ logger.file(`[${this.getProviderName()}] Component matching raw response:`, rawResponse);
3177
+ let result;
3178
+ try {
3179
+ let cleanedResponse = rawResponse || "";
3180
+ cleanedResponse = cleanedResponse.replace(/```json\n?/g, "").replace(/```\n?/g, "").trim();
3181
+ const jsonMatch = cleanedResponse.match(/\{[\s\S]*\}/);
3182
+ if (jsonMatch) {
3183
+ cleanedResponse = jsonMatch[0];
3184
+ }
3185
+ result = JSON.parse(cleanedResponse);
3186
+ } catch (parseError) {
3187
+ logger.error(`[${this.getProviderName()}] Failed to parse component matching JSON response`);
3188
+ const errorMsg = parseError instanceof Error ? parseError.message : String(parseError);
3189
+ const posMatch = errorMsg.match(/position (\d+)/);
3190
+ const errorPos = posMatch ? parseInt(posMatch[1]) : -1;
3191
+ if (errorPos > 0 && rawResponse) {
3192
+ const start = Math.max(0, errorPos - 200);
3193
+ const end = Math.min(rawResponse.length, errorPos + 200);
3194
+ logger.debug(`[${this.getProviderName()}] Error context (position ${errorPos}):`);
3195
+ logger.debug(rawResponse.substring(start, end));
3196
+ logger.debug(" ".repeat(Math.min(200, errorPos - start)) + "^--- Error here");
3197
+ }
3198
+ logger.debug(`[${this.getProviderName()}] Raw response (first 2000 chars):`, rawResponse?.substring(0, 2e3));
3199
+ logger.debug(`[${this.getProviderName()}] Parse error:`, parseError);
3200
+ logCollector?.error(`Failed to parse component matching response: ${errorMsg}`);
3201
+ try {
3202
+ logger.info(`[${this.getProviderName()}] Attempting aggressive JSON cleanup...`);
3203
+ let aggressive = rawResponse || "";
3204
+ aggressive = aggressive.replace(/```json\n?/g, "").replace(/```\n?/g, "").trim();
3205
+ const match = aggressive.match(/\{[\s\S]*\}/);
3206
+ if (match) {
3207
+ aggressive = match[0];
3208
+ }
3209
+ aggressive = aggressive.replace(/,(\s*[}\]])/g, "$1");
3210
+ result = JSON.parse(aggressive);
3211
+ logger.info(`[${this.getProviderName()}] Aggressive cleanup succeeded!`);
3212
+ } catch (secondError) {
3213
+ logger.error(`[${this.getProviderName()}] Aggressive cleanup also failed`);
3214
+ return {
3215
+ components: [],
3216
+ selectedLayout: "MultiComponentContainer",
3217
+ selectedLayoutId: "",
3218
+ selectedLayoutComponent: null,
3219
+ layoutReasoning: "No layout selected"
3220
+ };
3221
+ }
3222
+ }
3223
+ const matchedComponents = result.matchedComponents || [];
3224
+ const selectedLayout = result.selectedLayout || "MultiComponentContainer";
3225
+ const selectedLayoutId = result.selectedLayoutId || "";
3226
+ const layoutReasoning = result.layoutReasoning || "No layout reasoning provided";
3227
+ let selectedLayoutComponent = null;
3228
+ if (selectedLayoutId) {
3229
+ selectedLayoutComponent = components.find((c) => c.id === selectedLayoutId) || null;
3230
+ if (!selectedLayoutComponent) {
3231
+ logger.warn(`[${this.getProviderName()}] Layout component ${selectedLayoutId} not found in available components`);
3232
+ }
3233
+ }
3234
+ logger.info(`[${this.getProviderName()}] Matched ${matchedComponents.length} components from text response`);
3235
+ logger.info(`[${this.getProviderName()}] Selected layout: ${selectedLayout} (ID: ${selectedLayoutId})`);
3236
+ logger.info(`[${this.getProviderName()}] Layout reasoning: ${layoutReasoning}`);
3237
+ if (matchedComponents.length > 0) {
3238
+ logCollector?.info(`Matched ${matchedComponents.length} components for visualization using ${selectedLayout}`);
3239
+ logCollector?.info(`Layout reasoning: ${layoutReasoning}`);
3240
+ matchedComponents.forEach((comp, idx) => {
3241
+ logCollector?.info(` ${idx + 1}. ${comp.componentName} (${comp.componentType}): ${comp.reasoning}`);
3242
+ if (comp.props?.query) {
3243
+ logCollector?.logQuery(
3244
+ `Component ${idx + 1} query`,
3245
+ comp.props.query,
3246
+ { componentName: comp.componentName, title: comp.props.title }
3247
+ );
3248
+ }
3249
+ });
3250
+ }
3251
+ const finalComponents = matchedComponents.map((mc) => {
3252
+ const originalComponent = components.find((c) => c.id === mc.componentId);
3253
+ if (!originalComponent) {
3254
+ logger.warn(`[${this.getProviderName()}] Component ${mc.componentId} not found in available components`);
3255
+ return null;
3256
+ }
3257
+ return {
3258
+ ...originalComponent,
3259
+ props: {
3260
+ ...originalComponent.props,
3261
+ ...mc.props
3262
+ }
3263
+ };
3264
+ }).filter(Boolean);
3265
+ return {
3266
+ components: finalComponents,
3267
+ selectedLayout,
3268
+ selectedLayoutId,
3269
+ selectedLayoutComponent,
3270
+ layoutReasoning
3271
+ };
3272
+ } catch (error) {
3273
+ const errorMsg = error instanceof Error ? error.message : String(error);
3274
+ logger.error(`[${this.getProviderName()}] Error matching components from text response: ${errorMsg}`);
3275
+ logger.debug(`[${this.getProviderName()}] Component matching error details:`, error);
3276
+ logCollector?.error(`Error matching components: ${errorMsg}`);
3277
+ return {
3278
+ components: [],
3279
+ selectedLayout: "MultiComponentContainer",
3280
+ selectedLayoutId: "",
3281
+ selectedLayoutComponent: null,
3282
+ layoutReasoning: "Error occurred during component matching"
3283
+ };
3284
+ }
3285
+ }
2979
3286
  /**
2980
3287
  * Generate text-based response for user question
2981
3288
  * This provides conversational text responses instead of component generation
3289
+ * Supports tool calling for query execution with automatic retry on errors (max 3 attempts)
3290
+ * After generating text response, if components are provided, matches suggested components
3291
+ * @param streamCallback - Optional callback function to receive text chunks as they stream
3292
+ * @param collections - Collection registry for executing database queries via database.execute
3293
+ * @param components - Optional list of available components for matching suggestions
2982
3294
  */
2983
- async generateTextResponse(userPrompt, apiKey, logCollector, conversationHistory) {
3295
+ async generateTextResponse(userPrompt, apiKey, logCollector, conversationHistory, streamCallback, collections, components) {
2984
3296
  const errors = [];
2985
3297
  logger.debug(`[${this.getProviderName()}] Starting text response generation`);
2986
3298
  logger.debug(`[${this.getProviderName()}] User prompt: "${userPrompt.substring(0, 50)}..."`);
2987
3299
  try {
3300
+ const schemaDoc = schema.generateSchemaDocumentation();
2988
3301
  const prompts = await promptLoader.loadPrompts("text-response", {
2989
3302
  USER_PROMPT: userPrompt,
2990
- CONVERSATION_HISTORY: conversationHistory || "No previous conversation"
3303
+ CONVERSATION_HISTORY: conversationHistory || "No previous conversation",
3304
+ SCHEMA_DOC: schemaDoc
2991
3305
  });
2992
- logger.debug(`[${this.getProviderName()}] Loaded text-response prompts`);
3306
+ logger.file("\n=============================\nsystem prompt:", prompts.system);
3307
+ logger.file("\n=============================\nuser prompt:", prompts.user);
3308
+ logger.debug(`[${this.getProviderName()}] Loaded text-response prompts with schema`);
2993
3309
  logger.debug(`[${this.getProviderName()}] System prompt length: ${prompts.system.length}, User prompt length: ${prompts.user.length}`);
2994
- logCollector?.info("Generating text response...");
2995
- const result = await LLM.stream(
3310
+ logCollector?.info("Generating text response with query execution capability...");
3311
+ const tools = [{
3312
+ name: "execute_query",
3313
+ description: "Executes a SQL query against the database and returns the results. Use this when the user asks for data. If the query fails, you will receive the error and can retry with a corrected query.",
3314
+ input_schema: {
3315
+ type: "object",
3316
+ properties: {
3317
+ query: {
3318
+ type: "string",
3319
+ description: "The SQL query to execute. Must be valid SQL syntax using table and column names from the schema."
3320
+ },
3321
+ reasoning: {
3322
+ type: "string",
3323
+ description: "Brief explanation of what this query does and why it answers the user's question."
3324
+ }
3325
+ },
3326
+ required: ["query"]
3327
+ }
3328
+ }];
3329
+ const queryAttempts = /* @__PURE__ */ new Map();
3330
+ const MAX_QUERY_ATTEMPTS = 6;
3331
+ let maxAttemptsReached = false;
3332
+ let fullStreamedText = "";
3333
+ const wrappedStreamCallback = streamCallback ? (chunk) => {
3334
+ fullStreamedText += chunk;
3335
+ streamCallback(chunk);
3336
+ } : void 0;
3337
+ const toolHandler = async (toolName, toolInput) => {
3338
+ if (toolName === "execute_query") {
3339
+ let query = toolInput.query;
3340
+ const reasoning = toolInput.reasoning;
3341
+ const { ensureQueryLimit: ensureQueryLimit2 } = await Promise.resolve().then(() => (init_utils(), utils_exports));
3342
+ query = ensureQueryLimit2(query, 32, 32);
3343
+ const queryKey = query.toLowerCase().replace(/\s+/g, " ").trim();
3344
+ const attempts = (queryAttempts.get(queryKey) || 0) + 1;
3345
+ queryAttempts.set(queryKey, attempts);
3346
+ logger.info(`[${this.getProviderName()}] Executing query (attempt ${attempts}/${MAX_QUERY_ATTEMPTS}): ${query.substring(0, 100)}...`);
3347
+ if (reasoning) {
3348
+ logCollector?.info(`Query reasoning: ${reasoning}`);
3349
+ }
3350
+ if (attempts > MAX_QUERY_ATTEMPTS) {
3351
+ const errorMsg = `Maximum query attempts (${MAX_QUERY_ATTEMPTS}) reached. Unable to generate a valid query for your question.`;
3352
+ logger.error(`[${this.getProviderName()}] ${errorMsg}`);
3353
+ logCollector?.error(errorMsg);
3354
+ maxAttemptsReached = true;
3355
+ if (wrappedStreamCallback) {
3356
+ wrappedStreamCallback(`
3357
+
3358
+ \u274C ${errorMsg}
3359
+
3360
+ Please try rephrasing your question or simplifying your request.
3361
+
3362
+ `);
3363
+ }
3364
+ throw new Error(errorMsg);
3365
+ }
3366
+ try {
3367
+ if (wrappedStreamCallback) {
3368
+ if (attempts === 1) {
3369
+ wrappedStreamCallback(`
3370
+
3371
+ \u{1F50D} **Analyzing your question...**
3372
+
3373
+ `);
3374
+ if (reasoning) {
3375
+ wrappedStreamCallback(`\u{1F4AD} ${reasoning}
3376
+
3377
+ `);
3378
+ }
3379
+ wrappedStreamCallback(`\u{1F4DD} **Generated SQL Query:**
3380
+ \`\`\`sql
3381
+ ${query}
3382
+ \`\`\`
3383
+
3384
+ `);
3385
+ wrappedStreamCallback(`\u26A1 **Executing query...**
3386
+
3387
+ `);
3388
+ } else {
3389
+ wrappedStreamCallback(`
3390
+
3391
+ \u{1F504} **Retrying with corrected query (attempt ${attempts}/${MAX_QUERY_ATTEMPTS})...**
3392
+
3393
+ `);
3394
+ if (reasoning) {
3395
+ wrappedStreamCallback(`\u{1F4AD} ${reasoning}
3396
+
3397
+ `);
3398
+ }
3399
+ wrappedStreamCallback(`\u{1F4DD} **Corrected SQL Query:**
3400
+ \`\`\`sql
3401
+ ${query}
3402
+ \`\`\`
3403
+
3404
+ `);
3405
+ wrappedStreamCallback(`\u26A1 **Executing query...**
3406
+
3407
+ `);
3408
+ }
3409
+ }
3410
+ logCollector?.logQuery(
3411
+ `Executing SQL query (attempt ${attempts})`,
3412
+ query,
3413
+ { reasoning, attempt: attempts }
3414
+ );
3415
+ if (!collections || !collections["database"] || !collections["database"]["execute"]) {
3416
+ throw new Error("Database collection not registered. Please register database.execute collection to execute queries.");
3417
+ }
3418
+ const result2 = await collections["database"]["execute"]({ sql: query });
3419
+ const data = result2?.data || result2;
3420
+ const rowCount = result2?.count ?? (Array.isArray(data) ? data.length : "N/A");
3421
+ logger.info(`[${this.getProviderName()}] Query executed successfully, rows returned: ${rowCount}`);
3422
+ logCollector?.info(`Query successful, returned ${rowCount} rows`);
3423
+ if (wrappedStreamCallback) {
3424
+ wrappedStreamCallback(`\u2705 **Query executed successfully!**
3425
+
3426
+ `);
3427
+ if (Array.isArray(data) && data.length > 0) {
3428
+ const firstRow = data[0];
3429
+ const columns = Object.keys(firstRow);
3430
+ if (data.length === 1 && columns.length === 1) {
3431
+ const value = firstRow[columns[0]];
3432
+ wrappedStreamCallback(`**Result:** ${value}
3433
+
3434
+ `);
3435
+ } else if (data.length > 0) {
3436
+ wrappedStreamCallback(`**Retrieved ${rowCount} rows**
3437
+
3438
+ `);
3439
+ wrappedStreamCallback(`<DataTable>${JSON.stringify(data)}</DataTable>
3440
+
3441
+ `);
3442
+ }
3443
+ } else if (Array.isArray(data) && data.length === 0) {
3444
+ wrappedStreamCallback(`**No rows returned.**
3445
+
3446
+ `);
3447
+ }
3448
+ wrappedStreamCallback(`\u{1F4CA} **Analyzing results...**
3449
+
3450
+ `);
3451
+ }
3452
+ return JSON.stringify(data, null, 2);
3453
+ } catch (error) {
3454
+ const errorMsg = error instanceof Error ? error.message : String(error);
3455
+ logger.error(`[${this.getProviderName()}] Query execution failed (attempt ${attempts}/${MAX_QUERY_ATTEMPTS}): ${errorMsg}`);
3456
+ logCollector?.error(`Query failed (attempt ${attempts}/${MAX_QUERY_ATTEMPTS}): ${errorMsg}`);
3457
+ if (wrappedStreamCallback) {
3458
+ wrappedStreamCallback(`\u274C **Query execution failed:**
3459
+ \`\`\`
3460
+ ${errorMsg}
3461
+ \`\`\`
3462
+
3463
+ `);
3464
+ if (attempts < MAX_QUERY_ATTEMPTS) {
3465
+ wrappedStreamCallback(`\u{1F527} **Generating corrected query...**
3466
+
3467
+ `);
3468
+ }
3469
+ }
3470
+ throw new Error(`Query execution failed: ${errorMsg}`);
3471
+ }
3472
+ }
3473
+ throw new Error(`Unknown tool: ${toolName}`);
3474
+ };
3475
+ const result = await LLM.streamWithTools(
2996
3476
  {
2997
3477
  sys: prompts.system,
2998
3478
  user: prompts.user
2999
3479
  },
3480
+ tools,
3481
+ toolHandler,
3000
3482
  {
3001
3483
  model: this.model,
3002
- maxTokens: 1e3,
3484
+ maxTokens: 4e3,
3003
3485
  temperature: 0.7,
3004
- apiKey: this.getApiKey(apiKey)
3486
+ apiKey: this.getApiKey(apiKey),
3487
+ partial: wrappedStreamCallback
3488
+ // Pass the wrapped streaming callback to LLM
3005
3489
  },
3006
- true
3007
- // Parse as JSON
3490
+ 10
3491
+ // max iterations: allows for 6 retries + final response + buffer
3008
3492
  );
3009
- logger.info(`[${this.getProviderName()}] Text response generated successfully`);
3010
- logger.debug(`[${this.getProviderName()}] Response type: ${result.responseType}, Confidence: ${result.confidence}`);
3011
- logCollector?.info(`Text response: ${result.text?.substring(0, 100)}${result.text?.length > 100 ? "..." : ""}`);
3493
+ logger.info(`[${this.getProviderName()}] Text response stream completed`);
3494
+ const textResponse = fullStreamedText || result || "I apologize, but I was unable to generate a response.";
3495
+ if (maxAttemptsReached) {
3496
+ logger.warn(`[${this.getProviderName()}] Max query attempts reached, returning failure response`);
3497
+ logCollector?.error("Failed to generate valid query after maximum attempts");
3498
+ return {
3499
+ success: false,
3500
+ errors: [`Maximum query attempts (${MAX_QUERY_ATTEMPTS}) reached. Unable to generate a valid query for your question.`],
3501
+ data: {
3502
+ text: textResponse,
3503
+ // Include the streamed text showing all attempts
3504
+ matchedComponents: [],
3505
+ method: `${this.getProviderName()}-text-response-max-attempts`
3506
+ }
3507
+ };
3508
+ }
3509
+ logCollector?.info(`Text response: ${textResponse.substring(0, 100)}${textResponse.length > 100 ? "..." : ""}`);
3012
3510
  logCollector?.logExplanation(
3013
3511
  "Text response generated",
3014
- result.reasoning || "Generated text response for user question",
3512
+ "Generated plain text response with component suggestions",
3015
3513
  {
3016
- responseType: result.responseType,
3017
- confidence: result.confidence,
3018
- textLength: result.text?.length || 0
3514
+ textLength: textResponse.length
3019
3515
  }
3020
3516
  );
3517
+ let matchedComponents = [];
3518
+ let selectedLayoutComponent = null;
3519
+ let layoutReasoning = "No layout selected";
3520
+ if (components && components.length > 0) {
3521
+ logger.info(`[${this.getProviderName()}] Matching components from text response...`);
3522
+ const matchResult = await this.matchComponentsFromTextResponse(
3523
+ textResponse,
3524
+ components,
3525
+ apiKey,
3526
+ logCollector
3527
+ );
3528
+ matchedComponents = matchResult.components;
3529
+ selectedLayoutComponent = matchResult.selectedLayoutComponent;
3530
+ layoutReasoning = matchResult.layoutReasoning;
3531
+ }
3532
+ let container_componet = null;
3533
+ if (matchedComponents.length > 0) {
3534
+ if (selectedLayoutComponent) {
3535
+ container_componet = {
3536
+ ...selectedLayoutComponent,
3537
+ id: `${selectedLayoutComponent.id}_${Date.now()}`,
3538
+ description: layoutReasoning,
3539
+ props: {
3540
+ ...selectedLayoutComponent.props,
3541
+ config: {
3542
+ ...selectedLayoutComponent.props?.config || {},
3543
+ components: matchedComponents
3544
+ }
3545
+ }
3546
+ };
3547
+ logger.info(`[${this.getProviderName()}] Created ${selectedLayoutComponent.name} (${selectedLayoutComponent.type}) container with ${matchedComponents.length} components`);
3548
+ logCollector?.info(`Created ${selectedLayoutComponent.name} with ${matchedComponents.length} components: ${layoutReasoning}`);
3549
+ } else {
3550
+ container_componet = {
3551
+ id: `multi_container_${Date.now()}`,
3552
+ name: "MultiComponentContainer",
3553
+ type: "Container",
3554
+ description: layoutReasoning,
3555
+ category: "dynamic",
3556
+ keywords: ["dashboard", "layout", "container"],
3557
+ props: {
3558
+ config: {
3559
+ components: matchedComponents
3560
+ }
3561
+ }
3562
+ };
3563
+ logger.info(`[${this.getProviderName()}] Created fallback MultiComponentContainer with ${matchedComponents.length} components`);
3564
+ logCollector?.info(`Created MultiComponentContainer with ${matchedComponents.length} components: ${layoutReasoning}`);
3565
+ }
3566
+ }
3021
3567
  return {
3022
3568
  success: true,
3023
3569
  data: {
3024
- text: result.text || "I apologize, but I was unable to generate a response.",
3025
- responseType: result.responseType || "answer",
3026
- confidence: result.confidence || 0.5,
3027
- reasoning: result.reasoning || "No reasoning provided"
3570
+ text: textResponse,
3571
+ matchedComponents,
3572
+ component: container_componet,
3573
+ layoutReasoning,
3574
+ method: `${this.getProviderName()}-text-response`
3028
3575
  },
3029
3576
  errors: []
3030
3577
  };
3031
3578
  } catch (error) {
3032
3579
  const errorMsg = error instanceof Error ? error.message : String(error);
3033
3580
  logger.error(`[${this.getProviderName()}] Error generating text response: ${errorMsg}`);
3034
- logger.debug(`[${this.getProviderName()}] Text response generation error details:`, error);
3035
3581
  logCollector?.error(`Error generating text response: ${errorMsg}`);
3036
3582
  errors.push(errorMsg);
3037
3583
  return {
@@ -3039,9 +3585,8 @@ var BaseLLM = class {
3039
3585
  errors,
3040
3586
  data: {
3041
3587
  text: "I apologize, but I encountered an error while processing your question. Please try rephrasing or ask something else.",
3042
- responseType: "error",
3043
- confidence: 0,
3044
- reasoning: `Error: ${errorMsg}`
3588
+ matchedComponents: [],
3589
+ method: `${this.getProviderName()}-text-response-error`
3045
3590
  }
3046
3591
  };
3047
3592
  }
@@ -3232,8 +3777,11 @@ var BaseLLM = class {
3232
3777
  * Supports both component generation and text response modes
3233
3778
  *
3234
3779
  * @param responseMode - 'component' for component generation (default), 'text' for text responses
3780
+ * @param streamCallback - Optional callback function to receive text chunks as they stream (only for text mode)
3781
+ * @param collections - Collection registry for executing database queries (required for text mode)
3235
3782
  */
3236
- async handleUserRequest(userPrompt, components, apiKey, logCollector, conversationHistory, responseMode = "component") {
3783
+ async handleUserRequest(userPrompt, components, apiKey, logCollector, conversationHistory, responseMode = "component", streamCallback, collections) {
3784
+ const startTime = Date.now();
3237
3785
  logger.info(`[${this.getProviderName()}] handleUserRequest called with responseMode: ${responseMode}`);
3238
3786
  if (responseMode === "text") {
3239
3787
  logger.info(`[${this.getProviderName()}] Using text response mode`);
@@ -3242,25 +3790,23 @@ var BaseLLM = class {
3242
3790
  userPrompt,
3243
3791
  apiKey,
3244
3792
  logCollector,
3245
- conversationHistory
3793
+ conversationHistory,
3794
+ streamCallback,
3795
+ collections,
3796
+ components
3246
3797
  );
3247
3798
  if (!textResponse.success) {
3799
+ const elapsedTime3 = Date.now() - startTime;
3248
3800
  logger.error(`[${this.getProviderName()}] Text response generation failed`);
3801
+ logger.info(`[${this.getProviderName()}] Total time taken: ${elapsedTime3}ms (${(elapsedTime3 / 1e3).toFixed(2)}s)`);
3802
+ logCollector?.info(`Total time taken: ${elapsedTime3}ms (${(elapsedTime3 / 1e3).toFixed(2)}s)`);
3249
3803
  return textResponse;
3250
3804
  }
3805
+ const elapsedTime2 = Date.now() - startTime;
3251
3806
  logger.info(`[${this.getProviderName()}] Text response generated successfully`);
3252
- return {
3253
- success: true,
3254
- data: {
3255
- textResponse: textResponse.data.text,
3256
- text: textResponse.data.text,
3257
- responseType: textResponse.data.responseType,
3258
- confidence: textResponse.data.confidence,
3259
- reasoning: textResponse.data.reasoning,
3260
- method: `${this.getProviderName()}-text-response`
3261
- },
3262
- errors: []
3263
- };
3807
+ logger.info(`[${this.getProviderName()}] Total time taken: ${elapsedTime2}ms (${(elapsedTime2 / 1e3).toFixed(2)}s)`);
3808
+ logCollector?.info(`Total time taken: ${elapsedTime2}ms (${(elapsedTime2 / 1e3).toFixed(2)}s)`);
3809
+ return textResponse;
3264
3810
  }
3265
3811
  const componentResponse = await this.generateComponentResponse(
3266
3812
  userPrompt,
@@ -3270,24 +3816,17 @@ var BaseLLM = class {
3270
3816
  conversationHistory
3271
3817
  );
3272
3818
  if (!componentResponse.success) {
3819
+ const elapsedTime2 = Date.now() - startTime;
3273
3820
  logger.error(`[${this.getProviderName()}] Component response generation failed`);
3821
+ logger.info(`[${this.getProviderName()}] Total time taken: ${elapsedTime2}ms (${(elapsedTime2 / 1e3).toFixed(2)}s)`);
3822
+ logCollector?.info(`Total time taken: ${elapsedTime2}ms (${(elapsedTime2 / 1e3).toFixed(2)}s)`);
3274
3823
  return componentResponse;
3275
3824
  }
3825
+ const elapsedTime = Date.now() - startTime;
3276
3826
  logger.info(`[${this.getProviderName()}] Component response generated successfully`);
3277
- return {
3278
- success: true,
3279
- data: {
3280
- component: componentResponse.data.component,
3281
- reasoning: componentResponse.data.reasoning,
3282
- method: componentResponse.data.method,
3283
- // Preserve the original method name
3284
- questionType: componentResponse.data.questionType,
3285
- needsMultipleComponents: componentResponse.data.needsMultipleComponents,
3286
- propsModified: componentResponse.data.propsModified,
3287
- queryModified: componentResponse.data.queryModified
3288
- },
3289
- errors: []
3290
- };
3827
+ logger.info(`[${this.getProviderName()}] Total time taken: ${elapsedTime}ms (${(elapsedTime / 1e3).toFixed(2)}s)`);
3828
+ logCollector?.info(`Total time taken: ${elapsedTime}ms (${(elapsedTime / 1e3).toFixed(2)}s)`);
3829
+ return componentResponse;
3291
3830
  }
3292
3831
  /**
3293
3832
  * Generate next questions that the user might ask based on the original prompt and generated component
@@ -3400,7 +3939,7 @@ function getLLMProviders() {
3400
3939
  return DEFAULT_PROVIDERS;
3401
3940
  }
3402
3941
  }
3403
- var useAnthropicMethod = async (prompt, components, apiKey, logCollector, conversationHistory, responseMode = "component") => {
3942
+ var useAnthropicMethod = async (prompt, components, apiKey, logCollector, conversationHistory, responseMode = "component", streamCallback, collections) => {
3404
3943
  logger.debug("[useAnthropicMethod] Initializing Anthropic Claude matching method");
3405
3944
  logger.debug(`[useAnthropicMethod] Response mode: ${responseMode}`);
3406
3945
  const msg = `Using Anthropic Claude ${responseMode === "text" ? "text response" : "matching"} method...`;
@@ -3412,11 +3951,11 @@ var useAnthropicMethod = async (prompt, components, apiKey, logCollector, conver
3412
3951
  return { success: false, errors: [emptyMsg] };
3413
3952
  }
3414
3953
  logger.debug(`[useAnthropicMethod] Processing with ${components.length} components`);
3415
- const matchResult = await anthropicLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory, responseMode);
3954
+ const matchResult = await anthropicLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory, responseMode, streamCallback, collections);
3416
3955
  logger.info(`[useAnthropicMethod] Successfully generated ${responseMode} using Anthropic`);
3417
3956
  return matchResult;
3418
3957
  };
3419
- var useGroqMethod = async (prompt, components, apiKey, logCollector, conversationHistory, responseMode = "component") => {
3958
+ var useGroqMethod = async (prompt, components, apiKey, logCollector, conversationHistory, responseMode = "component", streamCallback, collections) => {
3420
3959
  logger.debug("[useGroqMethod] Initializing Groq LLM matching method");
3421
3960
  logger.debug(`[useGroqMethod] Response mode: ${responseMode}`);
3422
3961
  const msg = `Using Groq LLM ${responseMode === "text" ? "text response" : "matching"} method...`;
@@ -3429,16 +3968,16 @@ var useGroqMethod = async (prompt, components, apiKey, logCollector, conversatio
3429
3968
  return { success: false, errors: [emptyMsg] };
3430
3969
  }
3431
3970
  logger.debug(`[useGroqMethod] Processing with ${components.length} components`);
3432
- const matchResult = await groqLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory, responseMode);
3971
+ const matchResult = await groqLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory, responseMode, streamCallback, collections);
3433
3972
  logger.info(`[useGroqMethod] Successfully generated ${responseMode} using Groq`);
3434
3973
  return matchResult;
3435
3974
  };
3436
3975
  var getUserResponseFromCache = async (prompt) => {
3437
3976
  return false;
3438
3977
  };
3439
- var get_user_response = async (prompt, components, anthropicApiKey, groqApiKey, llmProviders, logCollector, conversationHistory) => {
3440
- const responseMode = "component";
3978
+ var get_user_response = async (prompt, components, anthropicApiKey, groqApiKey, llmProviders, logCollector, conversationHistory, responseMode = "component", streamCallback, collections) => {
3441
3979
  logger.debug(`[get_user_response] Starting user response generation for prompt: "${prompt.substring(0, 50)}..."`);
3980
+ logger.debug(`[get_user_response] Response mode: ${responseMode}`);
3442
3981
  logger.debug("[get_user_response] Checking cache for existing response");
3443
3982
  const userResponse = await getUserResponseFromCache(prompt);
3444
3983
  if (userResponse) {
@@ -3469,16 +4008,16 @@ var get_user_response = async (prompt, components, anthropicApiKey, groqApiKey,
3469
4008
  logCollector?.info(attemptMsg);
3470
4009
  let result;
3471
4010
  if (provider === "anthropic") {
3472
- result = await useAnthropicMethod(prompt, components, anthropicApiKey, logCollector, conversationHistory, responseMode);
4011
+ result = await useAnthropicMethod(prompt, components, anthropicApiKey, logCollector, conversationHistory, responseMode, streamCallback, collections);
3473
4012
  } else if (provider === "groq") {
3474
- result = await useGroqMethod(prompt, components, groqApiKey, logCollector, conversationHistory, responseMode);
4013
+ result = await useGroqMethod(prompt, components, groqApiKey, logCollector, conversationHistory, responseMode, streamCallback, collections);
3475
4014
  } else {
3476
4015
  logger.warn(`[get_user_response] Unknown provider: ${provider} - skipping`);
3477
4016
  errors.push(`Unknown provider: ${provider}`);
3478
4017
  continue;
3479
4018
  }
3480
4019
  if (result.success) {
3481
- const successMsg = `Success with provider: ${provider} result: ${JSON.stringify(result)}`;
4020
+ const successMsg = `Success with provider: ${provider}`;
3482
4021
  logger.info(`${successMsg}`);
3483
4022
  logCollector?.info(successMsg);
3484
4023
  return result;
@@ -3699,8 +4238,7 @@ var CONTEXT_CONFIG = {
3699
4238
  };
3700
4239
 
3701
4240
  // src/handlers/user-prompt-request.ts
3702
- var processedMessageIds = /* @__PURE__ */ new Set();
3703
- var get_user_request = async (data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders) => {
4241
+ var get_user_request = async (data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders, collections) => {
3704
4242
  const errors = [];
3705
4243
  logger.debug("[USER_PROMPT_REQ] Parsing incoming message data");
3706
4244
  const parseResult = UserPromptRequestMessageSchema.safeParse(data);
@@ -3716,19 +4254,6 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
3716
4254
  const prompt = payload.prompt;
3717
4255
  const SA_RUNTIME = payload.SA_RUNTIME;
3718
4256
  const wsId = userPromptRequest.from.id || "unknown";
3719
- logger.debug(`[REQUEST ${id}] Full request details - wsId: ${wsId}, prompt length: ${prompt.length}`);
3720
- if (processedMessageIds.has(id)) {
3721
- logger.warn(`[REQUEST ${id}] Duplicate request detected - ignoring`);
3722
- }
3723
- processedMessageIds.add(id);
3724
- logger.debug(`[REQUEST ${id}] Message ID marked as processed (${processedMessageIds.size} total)`);
3725
- if (processedMessageIds.size > 100) {
3726
- const firstId = processedMessageIds.values().next().value;
3727
- if (firstId) {
3728
- processedMessageIds.delete(firstId);
3729
- logger.debug(`[REQUEST ${id}] Cleaned up old message ID from cache`);
3730
- }
3731
- }
3732
4257
  if (!SA_RUNTIME) {
3733
4258
  errors.push("SA_RUNTIME is required");
3734
4259
  }
@@ -3743,9 +4268,7 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
3743
4268
  if (!prompt) {
3744
4269
  errors.push("Prompt not found");
3745
4270
  }
3746
- if (!components || components.length === 0) {
3747
- errors.push("Components not found");
3748
- }
4271
+ logger.debug(`[REQUEST ${id}] Full request details - uiBlockId: ${existingUiBlockId}, threadId: ${threadId}, prompt: ${prompt}`);
3749
4272
  if (errors.length > 0) {
3750
4273
  return { success: false, errors, id, wsId };
3751
4274
  }
@@ -3759,8 +4282,44 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
3759
4282
  logCollector.info(`Starting user prompt request with ${components.length} components`);
3760
4283
  const conversationHistory = thread.getConversationContext(CONTEXT_CONFIG.MAX_CONVERSATION_CONTEXT_BLOCKS, existingUiBlockId);
3761
4284
  logger.info("conversationHistory", conversationHistory);
3762
- const userResponse = await get_user_response(prompt, components, anthropicApiKey, groqApiKey, llmProviders, logCollector, conversationHistory);
3763
- logger.info("llm userResponse", userResponse);
4285
+ const responseMode = payload.responseMode || "component";
4286
+ logger.info("responseMode", responseMode);
4287
+ let streamCallback;
4288
+ let accumulatedStreamResponse = "";
4289
+ if (responseMode === "text") {
4290
+ streamCallback = (chunk) => {
4291
+ accumulatedStreamResponse += chunk;
4292
+ logger.debug(`[STREAM] Sending chunk (${chunk.length} chars): "${chunk.substring(0, 20)}..."`);
4293
+ const streamMessage = {
4294
+ id: `stream_${existingUiBlockId}`,
4295
+ // Different ID pattern for streaming
4296
+ type: "USER_PROMPT_STREAM",
4297
+ from: { type: "data-agent" },
4298
+ to: {
4299
+ type: "runtime",
4300
+ id: wsId
4301
+ },
4302
+ payload: {
4303
+ uiBlockId: existingUiBlockId,
4304
+ chunk
4305
+ }
4306
+ };
4307
+ sendMessage(streamMessage);
4308
+ logger.debug(`[STREAM] Chunk sent to wsId: ${wsId}`);
4309
+ };
4310
+ }
4311
+ const userResponse = await get_user_response(
4312
+ prompt,
4313
+ components,
4314
+ anthropicApiKey,
4315
+ groqApiKey,
4316
+ llmProviders,
4317
+ logCollector,
4318
+ conversationHistory,
4319
+ responseMode,
4320
+ streamCallback,
4321
+ collections
4322
+ );
3764
4323
  logCollector.info("User prompt request completed");
3765
4324
  const uiBlockId = existingUiBlockId;
3766
4325
  if (!userResponse.success) {
@@ -3783,10 +4342,11 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
3783
4342
  component = userResponse.data.component;
3784
4343
  }
3785
4344
  if ("textResponse" in userResponse.data) {
3786
- textResponse = userResponse.data.textResponse;
4345
+ textResponse = userResponse.data.text;
3787
4346
  }
3788
4347
  }
3789
4348
  }
4349
+ const finalTextResponse = responseMode === "text" && accumulatedStreamResponse ? accumulatedStreamResponse : textResponse;
3790
4350
  const uiBlock = new UIBlock(
3791
4351
  prompt,
3792
4352
  {},
@@ -3796,8 +4356,8 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
3796
4356
  [],
3797
4357
  // actions: empty initially
3798
4358
  uiBlockId,
3799
- textResponse
3800
- // textResponse: text response from LLM
4359
+ finalTextResponse
4360
+ // textResponse: FULL streaming response including all intermediate messages
3801
4361
  );
3802
4362
  thread.addUIBlock(uiBlock);
3803
4363
  logger.info(`Created UIBlock: ${uiBlockId} in Thread: ${threadId}`);
@@ -3811,8 +4371,8 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
3811
4371
  wsId
3812
4372
  };
3813
4373
  };
3814
- async function handleUserPromptRequest(data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders) {
3815
- const response = await get_user_request(data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders);
4374
+ async function handleUserPromptRequest(data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders, collections) {
4375
+ const response = await get_user_request(data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders, collections);
3816
4376
  sendDataResponse4(
3817
4377
  response.id || data.id,
3818
4378
  {
@@ -4888,7 +5448,7 @@ function sendResponse5(id, res, sendMessage, clientId) {
4888
5448
  }
4889
5449
 
4890
5450
  // src/auth/user-manager.ts
4891
- import fs4 from "fs";
5451
+ import fs5 from "fs";
4892
5452
  import path4 from "path";
4893
5453
  import os from "os";
4894
5454
  var UserManager = class {
@@ -4928,19 +5488,19 @@ var UserManager = class {
4928
5488
  async loadUsersFromFile() {
4929
5489
  try {
4930
5490
  const dir = path4.dirname(this.filePath);
4931
- if (!fs4.existsSync(dir)) {
5491
+ if (!fs5.existsSync(dir)) {
4932
5492
  logger.info(`Creating directory structure: ${dir}`);
4933
- fs4.mkdirSync(dir, { recursive: true });
5493
+ fs5.mkdirSync(dir, { recursive: true });
4934
5494
  }
4935
- if (!fs4.existsSync(this.filePath)) {
5495
+ if (!fs5.existsSync(this.filePath)) {
4936
5496
  logger.info(`Users file does not exist at ${this.filePath}, creating with empty users`);
4937
5497
  const initialData = { users: [] };
4938
- fs4.writeFileSync(this.filePath, JSON.stringify(initialData, null, 4));
5498
+ fs5.writeFileSync(this.filePath, JSON.stringify(initialData, null, 4));
4939
5499
  this.users = [];
4940
5500
  this.hasChanged = false;
4941
5501
  return;
4942
5502
  }
4943
- const fileContent = fs4.readFileSync(this.filePath, "utf-8");
5503
+ const fileContent = fs5.readFileSync(this.filePath, "utf-8");
4944
5504
  const rawData = JSON.parse(fileContent);
4945
5505
  const validatedData = UsersDataSchema.parse(rawData);
4946
5506
  this.users = validatedData.users;
@@ -4960,15 +5520,15 @@ var UserManager = class {
4960
5520
  }
4961
5521
  try {
4962
5522
  const dir = path4.dirname(this.filePath);
4963
- if (!fs4.existsSync(dir)) {
4964
- fs4.mkdirSync(dir, { recursive: true });
5523
+ if (!fs5.existsSync(dir)) {
5524
+ fs5.mkdirSync(dir, { recursive: true });
4965
5525
  }
4966
5526
  const usersToSave = this.users.map((user) => {
4967
5527
  const { wsIds, ...userWithoutWsIds } = user;
4968
5528
  return userWithoutWsIds;
4969
5529
  });
4970
5530
  const data = { users: usersToSave };
4971
- fs4.writeFileSync(this.filePath, JSON.stringify(data, null, 4));
5531
+ fs5.writeFileSync(this.filePath, JSON.stringify(data, null, 4));
4972
5532
  this.hasChanged = false;
4973
5533
  logger.debug(`Synced ${this.users.length} users to file (wsIds excluded)`);
4974
5534
  } catch (error) {
@@ -5186,7 +5746,7 @@ var UserManager = class {
5186
5746
  };
5187
5747
 
5188
5748
  // src/dashboards/dashboard-manager.ts
5189
- import fs5 from "fs";
5749
+ import fs6 from "fs";
5190
5750
  import path5 from "path";
5191
5751
  import os2 from "os";
5192
5752
  var DashboardManager = class {
@@ -5221,12 +5781,12 @@ var DashboardManager = class {
5221
5781
  createDashboard(dashboardId, dashboard) {
5222
5782
  const dashboardPath = this.getDashboardPath(dashboardId);
5223
5783
  const dashboardDir = path5.dirname(dashboardPath);
5224
- if (fs5.existsSync(dashboardPath)) {
5784
+ if (fs6.existsSync(dashboardPath)) {
5225
5785
  throw new Error(`Dashboard '${dashboardId}' already exists`);
5226
5786
  }
5227
5787
  const validated = DSLRendererPropsSchema.parse(dashboard);
5228
- fs5.mkdirSync(dashboardDir, { recursive: true });
5229
- fs5.writeFileSync(dashboardPath, JSON.stringify(validated, null, 4));
5788
+ fs6.mkdirSync(dashboardDir, { recursive: true });
5789
+ fs6.writeFileSync(dashboardPath, JSON.stringify(validated, null, 4));
5230
5790
  logger.info(`Dashboard created: ${dashboardId}`);
5231
5791
  return validated;
5232
5792
  }
@@ -5237,12 +5797,12 @@ var DashboardManager = class {
5237
5797
  */
5238
5798
  getDashboard(dashboardId) {
5239
5799
  const dashboardPath = this.getDashboardPath(dashboardId);
5240
- if (!fs5.existsSync(dashboardPath)) {
5800
+ if (!fs6.existsSync(dashboardPath)) {
5241
5801
  logger.warn(`Dashboard not found: ${dashboardId}`);
5242
5802
  return null;
5243
5803
  }
5244
5804
  try {
5245
- const fileContent = fs5.readFileSync(dashboardPath, "utf-8");
5805
+ const fileContent = fs6.readFileSync(dashboardPath, "utf-8");
5246
5806
  const dashboard = JSON.parse(fileContent);
5247
5807
  const validated = DSLRendererPropsSchema.parse(dashboard);
5248
5808
  return validated;
@@ -5256,16 +5816,16 @@ var DashboardManager = class {
5256
5816
  * @returns Array of dashboard objects with their IDs
5257
5817
  */
5258
5818
  getAllDashboards() {
5259
- if (!fs5.existsSync(this.dashboardsBasePath)) {
5260
- fs5.mkdirSync(this.dashboardsBasePath, { recursive: true });
5819
+ if (!fs6.existsSync(this.dashboardsBasePath)) {
5820
+ fs6.mkdirSync(this.dashboardsBasePath, { recursive: true });
5261
5821
  return [];
5262
5822
  }
5263
5823
  const dashboards = [];
5264
5824
  try {
5265
- const dashboardDirs = fs5.readdirSync(this.dashboardsBasePath);
5825
+ const dashboardDirs = fs6.readdirSync(this.dashboardsBasePath);
5266
5826
  for (const dashboardId of dashboardDirs) {
5267
5827
  const dashboardPath = this.getDashboardPath(dashboardId);
5268
- if (fs5.existsSync(dashboardPath)) {
5828
+ if (fs6.existsSync(dashboardPath)) {
5269
5829
  const dashboard = this.getDashboard(dashboardId);
5270
5830
  if (dashboard) {
5271
5831
  dashboards.push({ dashboardId, dashboard });
@@ -5287,13 +5847,13 @@ var DashboardManager = class {
5287
5847
  */
5288
5848
  updateDashboard(dashboardId, dashboard) {
5289
5849
  const dashboardPath = this.getDashboardPath(dashboardId);
5290
- if (!fs5.existsSync(dashboardPath)) {
5850
+ if (!fs6.existsSync(dashboardPath)) {
5291
5851
  logger.warn(`Dashboard not found for update: ${dashboardId}`);
5292
5852
  return null;
5293
5853
  }
5294
5854
  try {
5295
5855
  const validated = DSLRendererPropsSchema.parse(dashboard);
5296
- fs5.writeFileSync(dashboardPath, JSON.stringify(validated, null, 4));
5856
+ fs6.writeFileSync(dashboardPath, JSON.stringify(validated, null, 4));
5297
5857
  logger.info(`Dashboard updated: ${dashboardId}`);
5298
5858
  return validated;
5299
5859
  } catch (error) {
@@ -5309,12 +5869,12 @@ var DashboardManager = class {
5309
5869
  deleteDashboard(dashboardId) {
5310
5870
  const dashboardPath = this.getDashboardPath(dashboardId);
5311
5871
  const dashboardDir = path5.dirname(dashboardPath);
5312
- if (!fs5.existsSync(dashboardPath)) {
5872
+ if (!fs6.existsSync(dashboardPath)) {
5313
5873
  logger.warn(`Dashboard not found for deletion: ${dashboardId}`);
5314
5874
  return false;
5315
5875
  }
5316
5876
  try {
5317
- fs5.rmSync(dashboardDir, { recursive: true, force: true });
5877
+ fs6.rmSync(dashboardDir, { recursive: true, force: true });
5318
5878
  logger.info(`Dashboard deleted: ${dashboardId}`);
5319
5879
  return true;
5320
5880
  } catch (error) {
@@ -5329,21 +5889,21 @@ var DashboardManager = class {
5329
5889
  */
5330
5890
  dashboardExists(dashboardId) {
5331
5891
  const dashboardPath = this.getDashboardPath(dashboardId);
5332
- return fs5.existsSync(dashboardPath);
5892
+ return fs6.existsSync(dashboardPath);
5333
5893
  }
5334
5894
  /**
5335
5895
  * Get dashboard count
5336
5896
  * @returns Number of dashboards
5337
5897
  */
5338
5898
  getDashboardCount() {
5339
- if (!fs5.existsSync(this.dashboardsBasePath)) {
5899
+ if (!fs6.existsSync(this.dashboardsBasePath)) {
5340
5900
  return 0;
5341
5901
  }
5342
5902
  try {
5343
- const dashboardDirs = fs5.readdirSync(this.dashboardsBasePath);
5903
+ const dashboardDirs = fs6.readdirSync(this.dashboardsBasePath);
5344
5904
  return dashboardDirs.filter((dir) => {
5345
5905
  const dashboardPath = this.getDashboardPath(dir);
5346
- return fs5.existsSync(dashboardPath);
5906
+ return fs6.existsSync(dashboardPath);
5347
5907
  }).length;
5348
5908
  } catch (error) {
5349
5909
  logger.error("Failed to get dashboard count:", error);
@@ -5353,7 +5913,7 @@ var DashboardManager = class {
5353
5913
  };
5354
5914
 
5355
5915
  // src/reports/report-manager.ts
5356
- import fs6 from "fs";
5916
+ import fs7 from "fs";
5357
5917
  import path6 from "path";
5358
5918
  import os3 from "os";
5359
5919
  var ReportManager = class {
@@ -5388,12 +5948,12 @@ var ReportManager = class {
5388
5948
  createReport(reportId, report) {
5389
5949
  const reportPath = this.getReportPath(reportId);
5390
5950
  const reportDir = path6.dirname(reportPath);
5391
- if (fs6.existsSync(reportPath)) {
5951
+ if (fs7.existsSync(reportPath)) {
5392
5952
  throw new Error(`Report '${reportId}' already exists`);
5393
5953
  }
5394
5954
  const validated = DSLRendererPropsSchema2.parse(report);
5395
- fs6.mkdirSync(reportDir, { recursive: true });
5396
- fs6.writeFileSync(reportPath, JSON.stringify(validated, null, 4));
5955
+ fs7.mkdirSync(reportDir, { recursive: true });
5956
+ fs7.writeFileSync(reportPath, JSON.stringify(validated, null, 4));
5397
5957
  logger.info(`Report created: ${reportId}`);
5398
5958
  return validated;
5399
5959
  }
@@ -5404,12 +5964,12 @@ var ReportManager = class {
5404
5964
  */
5405
5965
  getReport(reportId) {
5406
5966
  const reportPath = this.getReportPath(reportId);
5407
- if (!fs6.existsSync(reportPath)) {
5967
+ if (!fs7.existsSync(reportPath)) {
5408
5968
  logger.warn(`Report not found: ${reportId}`);
5409
5969
  return null;
5410
5970
  }
5411
5971
  try {
5412
- const fileContent = fs6.readFileSync(reportPath, "utf-8");
5972
+ const fileContent = fs7.readFileSync(reportPath, "utf-8");
5413
5973
  const report = JSON.parse(fileContent);
5414
5974
  const validated = DSLRendererPropsSchema2.parse(report);
5415
5975
  return validated;
@@ -5423,16 +5983,16 @@ var ReportManager = class {
5423
5983
  * @returns Array of report objects with their IDs
5424
5984
  */
5425
5985
  getAllReports() {
5426
- if (!fs6.existsSync(this.reportsBasePath)) {
5427
- fs6.mkdirSync(this.reportsBasePath, { recursive: true });
5986
+ if (!fs7.existsSync(this.reportsBasePath)) {
5987
+ fs7.mkdirSync(this.reportsBasePath, { recursive: true });
5428
5988
  return [];
5429
5989
  }
5430
5990
  const reports = [];
5431
5991
  try {
5432
- const reportDirs = fs6.readdirSync(this.reportsBasePath);
5992
+ const reportDirs = fs7.readdirSync(this.reportsBasePath);
5433
5993
  for (const reportId of reportDirs) {
5434
5994
  const reportPath = this.getReportPath(reportId);
5435
- if (fs6.existsSync(reportPath)) {
5995
+ if (fs7.existsSync(reportPath)) {
5436
5996
  const report = this.getReport(reportId);
5437
5997
  if (report) {
5438
5998
  reports.push({ reportId, report });
@@ -5454,13 +6014,13 @@ var ReportManager = class {
5454
6014
  */
5455
6015
  updateReport(reportId, report) {
5456
6016
  const reportPath = this.getReportPath(reportId);
5457
- if (!fs6.existsSync(reportPath)) {
6017
+ if (!fs7.existsSync(reportPath)) {
5458
6018
  logger.warn(`Report not found for update: ${reportId}`);
5459
6019
  return null;
5460
6020
  }
5461
6021
  try {
5462
6022
  const validated = DSLRendererPropsSchema2.parse(report);
5463
- fs6.writeFileSync(reportPath, JSON.stringify(validated, null, 4));
6023
+ fs7.writeFileSync(reportPath, JSON.stringify(validated, null, 4));
5464
6024
  logger.info(`Report updated: ${reportId}`);
5465
6025
  return validated;
5466
6026
  } catch (error) {
@@ -5476,12 +6036,12 @@ var ReportManager = class {
5476
6036
  deleteReport(reportId) {
5477
6037
  const reportPath = this.getReportPath(reportId);
5478
6038
  const reportDir = path6.dirname(reportPath);
5479
- if (!fs6.existsSync(reportPath)) {
6039
+ if (!fs7.existsSync(reportPath)) {
5480
6040
  logger.warn(`Report not found for deletion: ${reportId}`);
5481
6041
  return false;
5482
6042
  }
5483
6043
  try {
5484
- fs6.rmSync(reportDir, { recursive: true, force: true });
6044
+ fs7.rmSync(reportDir, { recursive: true, force: true });
5485
6045
  logger.info(`Report deleted: ${reportId}`);
5486
6046
  return true;
5487
6047
  } catch (error) {
@@ -5496,21 +6056,21 @@ var ReportManager = class {
5496
6056
  */
5497
6057
  reportExists(reportId) {
5498
6058
  const reportPath = this.getReportPath(reportId);
5499
- return fs6.existsSync(reportPath);
6059
+ return fs7.existsSync(reportPath);
5500
6060
  }
5501
6061
  /**
5502
6062
  * Get report count
5503
6063
  * @returns Number of reports
5504
6064
  */
5505
6065
  getReportCount() {
5506
- if (!fs6.existsSync(this.reportsBasePath)) {
6066
+ if (!fs7.existsSync(this.reportsBasePath)) {
5507
6067
  return 0;
5508
6068
  }
5509
6069
  try {
5510
- const reportDirs = fs6.readdirSync(this.reportsBasePath);
6070
+ const reportDirs = fs7.readdirSync(this.reportsBasePath);
5511
6071
  return reportDirs.filter((dir) => {
5512
6072
  const reportPath = this.getReportPath(dir);
5513
- return fs6.existsSync(reportPath);
6073
+ return fs7.existsSync(reportPath);
5514
6074
  }).length;
5515
6075
  } catch (error) {
5516
6076
  logger.error("Failed to get report count:", error);
@@ -5734,9 +6294,6 @@ var SuperatomSDK = class {
5734
6294
  });
5735
6295
  this.initializeDashboardManager();
5736
6296
  this.initializeReportManager();
5737
- this.connect().catch((error) => {
5738
- logger.error("Failed to connect to Superatom:", error);
5739
- });
5740
6297
  }
5741
6298
  /**
5742
6299
  * Initialize PromptLoader and load prompts into memory
@@ -5802,6 +6359,10 @@ var SuperatomSDK = class {
5802
6359
  * Connect to the Superatom WebSocket service
5803
6360
  */
5804
6361
  async connect() {
6362
+ if (this.connected && this.ws && this.ws.readyState === this.ws.OPEN) {
6363
+ logger.info("Already connected to WebSocket");
6364
+ return Promise.resolve();
6365
+ }
5805
6366
  return new Promise((resolve, reject) => {
5806
6367
  try {
5807
6368
  const url = new URL(this.url);
@@ -5864,7 +6425,7 @@ var SuperatomSDK = class {
5864
6425
  });
5865
6426
  break;
5866
6427
  case "USER_PROMPT_REQ":
5867
- handleUserPromptRequest(parsed, this.components, (msg) => this.send(msg), this.anthropicApiKey, this.groqApiKey, this.llmProviders).catch((error) => {
6428
+ handleUserPromptRequest(parsed, this.components, (msg) => this.send(msg), this.anthropicApiKey, this.groqApiKey, this.llmProviders, this.collections).catch((error) => {
5868
6429
  logger.error("Failed to handle user prompt request:", error);
5869
6430
  });
5870
6431
  break;