@superatomai/sdk-node 0.0.5 → 0.0.7
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/README.md +251 -33
- package/dist/index.d.mts +17 -4
- package/dist/index.d.ts +17 -4
- package/dist/index.js +897 -255
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +901 -255
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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
|
|
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 (
|
|
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 (
|
|
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(
|
|
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,137 @@ var require_main = __commonJS({
|
|
|
428
435
|
}
|
|
429
436
|
});
|
|
430
437
|
|
|
438
|
+
// src/userResponse/utils.ts
|
|
439
|
+
var utils_exports = {};
|
|
440
|
+
__export(utils_exports, {
|
|
441
|
+
convertQuestionsToActions: () => convertQuestionsToActions,
|
|
442
|
+
convertTopToLimit: () => convertTopToLimit,
|
|
443
|
+
ensureQueryLimit: () => ensureQueryLimit,
|
|
444
|
+
fixScalarSubqueries: () => fixScalarSubqueries,
|
|
445
|
+
getJsonSizeInBytes: () => getJsonSizeInBytes,
|
|
446
|
+
validateMessageSize: () => validateMessageSize
|
|
447
|
+
});
|
|
448
|
+
function convertTopToLimit(query) {
|
|
449
|
+
if (!query || query.trim().length === 0) {
|
|
450
|
+
return query;
|
|
451
|
+
}
|
|
452
|
+
let modifiedQuery = query.replace(/\bSELECT\s+TOP\s+(\d+)\b/gi, "SELECT");
|
|
453
|
+
if (modifiedQuery !== query) {
|
|
454
|
+
console.warn(`\u26A0\uFE0F Query had TOP syntax. Converting to LIMIT for Snowflake compatibility.`);
|
|
455
|
+
}
|
|
456
|
+
return modifiedQuery;
|
|
457
|
+
}
|
|
458
|
+
function ensureQueryLimit(query, defaultLimit = 32, maxLimit = 32) {
|
|
459
|
+
if (!query || query.trim().length === 0) {
|
|
460
|
+
return query;
|
|
461
|
+
}
|
|
462
|
+
let trimmedQuery = query.trim();
|
|
463
|
+
const isSelectQuery = /^\s*SELECT\b/i.test(trimmedQuery) || /^\s*WITH\b.*\bSELECT\b/is.test(trimmedQuery);
|
|
464
|
+
if (!isSelectQuery) {
|
|
465
|
+
return query;
|
|
466
|
+
}
|
|
467
|
+
trimmedQuery = convertTopToLimit(trimmedQuery);
|
|
468
|
+
const hadSemicolon = trimmedQuery.endsWith(";");
|
|
469
|
+
if (hadSemicolon) {
|
|
470
|
+
trimmedQuery = trimmedQuery.slice(0, -1).trim();
|
|
471
|
+
}
|
|
472
|
+
const limitMatches = trimmedQuery.match(/\bLIMIT\s+(\d+)\b/gi);
|
|
473
|
+
if (limitMatches && limitMatches.length > 0) {
|
|
474
|
+
if (limitMatches.length > 1) {
|
|
475
|
+
console.warn(`\u26A0\uFE0F Query had ${limitMatches.length} LIMIT clauses. Removing duplicates...`);
|
|
476
|
+
trimmedQuery = trimmedQuery.replace(/\s*\bLIMIT\s+\d+\b/gi, "").trim();
|
|
477
|
+
} else {
|
|
478
|
+
const existingLimitMatch = trimmedQuery.match(/\bLIMIT\s+(\d+)\b/i);
|
|
479
|
+
if (existingLimitMatch) {
|
|
480
|
+
const existingLimit = parseInt(existingLimitMatch[1], 10);
|
|
481
|
+
if (existingLimit <= maxLimit) {
|
|
482
|
+
if (hadSemicolon) {
|
|
483
|
+
trimmedQuery += ";";
|
|
484
|
+
}
|
|
485
|
+
return trimmedQuery;
|
|
486
|
+
}
|
|
487
|
+
console.warn(`\u26A0\uFE0F Query LIMIT ${existingLimit} exceeds maximum of ${maxLimit}. Reducing to ${maxLimit}...`);
|
|
488
|
+
trimmedQuery = trimmedQuery.replace(/\bLIMIT\s+\d+\b/i, `LIMIT ${maxLimit}`);
|
|
489
|
+
if (hadSemicolon) {
|
|
490
|
+
trimmedQuery += ";";
|
|
491
|
+
}
|
|
492
|
+
return trimmedQuery;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
trimmedQuery = `${trimmedQuery} LIMIT ${defaultLimit}`;
|
|
497
|
+
if (hadSemicolon) {
|
|
498
|
+
trimmedQuery += ";";
|
|
499
|
+
}
|
|
500
|
+
return trimmedQuery;
|
|
501
|
+
}
|
|
502
|
+
function fixScalarSubqueries(query) {
|
|
503
|
+
if (!query || query.trim().length === 0) {
|
|
504
|
+
return query;
|
|
505
|
+
}
|
|
506
|
+
let modifiedQuery = query;
|
|
507
|
+
let hasChanges = false;
|
|
508
|
+
const scalarOperatorPattern = /([=<>!]=?|<>)\s*\(\s*SELECT\s/gi;
|
|
509
|
+
const matches = [...modifiedQuery.matchAll(scalarOperatorPattern)];
|
|
510
|
+
for (let i = matches.length - 1; i >= 0; i--) {
|
|
511
|
+
const match = matches[i];
|
|
512
|
+
const startPos = match.index + match[0].length - "SELECT ".length;
|
|
513
|
+
let parenDepth = 1;
|
|
514
|
+
let endPos = startPos;
|
|
515
|
+
let foundEnd = false;
|
|
516
|
+
for (let j = startPos; j < modifiedQuery.length; j++) {
|
|
517
|
+
const char = modifiedQuery[j];
|
|
518
|
+
if (char === "(") parenDepth++;
|
|
519
|
+
if (char === ")") {
|
|
520
|
+
parenDepth--;
|
|
521
|
+
if (parenDepth === 0) {
|
|
522
|
+
endPos = j;
|
|
523
|
+
foundEnd = true;
|
|
524
|
+
break;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
if (!foundEnd) continue;
|
|
529
|
+
const subquery = modifiedQuery.substring(startPos, endPos);
|
|
530
|
+
if (/\bLIMIT\s+\d+/i.test(subquery)) {
|
|
531
|
+
continue;
|
|
532
|
+
}
|
|
533
|
+
const fixedSubquery = subquery.trim() + " LIMIT 1";
|
|
534
|
+
modifiedQuery = modifiedQuery.substring(0, startPos) + fixedSubquery + modifiedQuery.substring(endPos);
|
|
535
|
+
hasChanges = true;
|
|
536
|
+
console.warn(`\u26A0\uFE0F Fixed scalar subquery: added LIMIT 1 to prevent multiple row error`);
|
|
537
|
+
}
|
|
538
|
+
if (hasChanges) {
|
|
539
|
+
console.log("\u2713 Query validated and fixed for PostgreSQL scalar subquery compatibility");
|
|
540
|
+
}
|
|
541
|
+
return modifiedQuery;
|
|
542
|
+
}
|
|
543
|
+
function convertQuestionsToActions(questions) {
|
|
544
|
+
return questions.map((question, index) => ({
|
|
545
|
+
id: `action_${index}_${Date.now()}`,
|
|
546
|
+
name: question,
|
|
547
|
+
type: "next_question",
|
|
548
|
+
question
|
|
549
|
+
}));
|
|
550
|
+
}
|
|
551
|
+
function getJsonSizeInBytes(obj) {
|
|
552
|
+
const jsonString = JSON.stringify(obj);
|
|
553
|
+
return Buffer.byteLength(jsonString, "utf8");
|
|
554
|
+
}
|
|
555
|
+
function validateMessageSize(message, maxSize = 1048576) {
|
|
556
|
+
const size = getJsonSizeInBytes(message);
|
|
557
|
+
return {
|
|
558
|
+
isValid: size <= maxSize,
|
|
559
|
+
size,
|
|
560
|
+
maxSize
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
var init_utils = __esm({
|
|
564
|
+
"src/userResponse/utils.ts"() {
|
|
565
|
+
"use strict";
|
|
566
|
+
}
|
|
567
|
+
});
|
|
568
|
+
|
|
431
569
|
// src/websocket.ts
|
|
432
570
|
import WebSocket from "ws";
|
|
433
571
|
function createWebSocket(url) {
|
|
@@ -689,7 +827,8 @@ var UserPromptRequestPayloadSchema = z3.object({
|
|
|
689
827
|
SA_RUNTIME: z3.object({
|
|
690
828
|
threadId: z3.string(),
|
|
691
829
|
uiBlockId: z3.string()
|
|
692
|
-
}).optional()
|
|
830
|
+
}).optional(),
|
|
831
|
+
responseMode: z3.enum(["component", "text"]).optional()
|
|
693
832
|
});
|
|
694
833
|
var UserPromptRequestMessageSchema = z3.object({
|
|
695
834
|
id: z3.string(),
|
|
@@ -711,7 +850,8 @@ var ComponentPropsSchema = z3.object({
|
|
|
711
850
|
query: z3.string().optional(),
|
|
712
851
|
title: z3.string().optional(),
|
|
713
852
|
description: z3.string().optional(),
|
|
714
|
-
config: z3.record(z3.unknown()).optional()
|
|
853
|
+
config: z3.record(z3.unknown()).optional(),
|
|
854
|
+
actions: z3.array(z3.any()).optional()
|
|
715
855
|
});
|
|
716
856
|
var ComponentSchema = z3.object({
|
|
717
857
|
id: z3.string(),
|
|
@@ -804,7 +944,9 @@ var ReportsRequestMessageSchema = z3.object({
|
|
|
804
944
|
});
|
|
805
945
|
|
|
806
946
|
// src/utils/logger.ts
|
|
947
|
+
import fs from "fs";
|
|
807
948
|
var PREFIX = "[SuperatomSDK]";
|
|
949
|
+
var LOGSTREAM = fs.createWriteStream("superatom-sdk.log", { flags: "a" });
|
|
808
950
|
var LOG_LEVEL_PRIORITY = {
|
|
809
951
|
errors: 0,
|
|
810
952
|
warnings: 1,
|
|
@@ -888,6 +1030,9 @@ var Logger = class {
|
|
|
888
1030
|
console.log(PREFIX, "[DEBUG]", ...args);
|
|
889
1031
|
}
|
|
890
1032
|
}
|
|
1033
|
+
file(...args) {
|
|
1034
|
+
LOGSTREAM.write(args.join(" ") + "\n");
|
|
1035
|
+
}
|
|
891
1036
|
};
|
|
892
1037
|
var logger = new Logger();
|
|
893
1038
|
|
|
@@ -959,6 +1104,9 @@ var UIBlock = class {
|
|
|
959
1104
|
getComponentMetadata() {
|
|
960
1105
|
return this.generatedComponentMetadata;
|
|
961
1106
|
}
|
|
1107
|
+
getTextResponse() {
|
|
1108
|
+
return this.textResponse || "";
|
|
1109
|
+
}
|
|
962
1110
|
/**
|
|
963
1111
|
* Set or update component metadata
|
|
964
1112
|
*/
|
|
@@ -1051,12 +1199,6 @@ var UIBlock = class {
|
|
|
1051
1199
|
const processedData = this.processDataForStorage(data);
|
|
1052
1200
|
this.componentData = { ...this.componentData, ...processedData };
|
|
1053
1201
|
}
|
|
1054
|
-
/**
|
|
1055
|
-
* Get text response
|
|
1056
|
-
*/
|
|
1057
|
-
getTextResponse() {
|
|
1058
|
-
return this.textResponse;
|
|
1059
|
-
}
|
|
1060
1202
|
/**
|
|
1061
1203
|
* Set or update text response
|
|
1062
1204
|
*/
|
|
@@ -1092,6 +1234,12 @@ var UIBlock = class {
|
|
|
1092
1234
|
throw error;
|
|
1093
1235
|
}
|
|
1094
1236
|
}
|
|
1237
|
+
/**
|
|
1238
|
+
* Set or replace all actions
|
|
1239
|
+
*/
|
|
1240
|
+
setActions(actions) {
|
|
1241
|
+
this.actions = actions;
|
|
1242
|
+
}
|
|
1095
1243
|
/**
|
|
1096
1244
|
* Add a single action (only if actions are resolved)
|
|
1097
1245
|
*/
|
|
@@ -1247,8 +1395,11 @@ var Thread = class {
|
|
|
1247
1395
|
const questionNum = index + 1;
|
|
1248
1396
|
const question = block.getUserQuestion();
|
|
1249
1397
|
const metadata = block.getComponentMetadata();
|
|
1250
|
-
|
|
1251
|
-
|
|
1398
|
+
const textResponse = block.getTextResponse();
|
|
1399
|
+
let assistantResponse = "";
|
|
1400
|
+
const hasComponent = metadata && Object.keys(metadata).length > 0 && metadata.type;
|
|
1401
|
+
const hasTextResponse = textResponse && textResponse.trim().length > 0;
|
|
1402
|
+
if (hasComponent) {
|
|
1252
1403
|
const parts = [];
|
|
1253
1404
|
if (metadata.type) {
|
|
1254
1405
|
parts.push(`Component Type: ${metadata.type}`);
|
|
@@ -1268,11 +1419,17 @@ var Thread = class {
|
|
|
1268
1419
|
const componentTypes = metadata.props.config.components.map((c) => c.type).join(", ");
|
|
1269
1420
|
parts.push(`Multi-component with: ${componentTypes}`);
|
|
1270
1421
|
}
|
|
1271
|
-
|
|
1422
|
+
assistantResponse = parts.join(", ");
|
|
1423
|
+
} else if (hasTextResponse) {
|
|
1424
|
+
assistantResponse = textResponse;
|
|
1425
|
+
} else {
|
|
1426
|
+
assistantResponse = "No response generated";
|
|
1272
1427
|
}
|
|
1273
|
-
contextLines.push(`
|
|
1274
|
-
|
|
1275
|
-
contextLines.push(
|
|
1428
|
+
contextLines.push(`User:
|
|
1429
|
+
${question}`);
|
|
1430
|
+
contextLines.push(`Assistant:
|
|
1431
|
+
${assistantResponse}`);
|
|
1432
|
+
contextLines.push("---");
|
|
1276
1433
|
});
|
|
1277
1434
|
return contextLines.join("\n").trim();
|
|
1278
1435
|
}
|
|
@@ -1448,7 +1605,7 @@ function sendDataResponse(id, collection, op, data, meta, sendMessage) {
|
|
|
1448
1605
|
}
|
|
1449
1606
|
|
|
1450
1607
|
// src/bundle.ts
|
|
1451
|
-
import * as
|
|
1608
|
+
import * as fs2 from "fs";
|
|
1452
1609
|
import * as path from "path";
|
|
1453
1610
|
function getBundleDir(configDir) {
|
|
1454
1611
|
const bundleDir = configDir || process.env.SA_BUNDLE_DIR;
|
|
@@ -1461,16 +1618,16 @@ function getBundleDir(configDir) {
|
|
|
1461
1618
|
}
|
|
1462
1619
|
function getJS(bundleDir) {
|
|
1463
1620
|
try {
|
|
1464
|
-
if (!
|
|
1621
|
+
if (!fs2.existsSync(bundleDir)) {
|
|
1465
1622
|
throw new Error(`Bundle directory does not exist: ${bundleDir}`);
|
|
1466
1623
|
}
|
|
1467
|
-
const stats =
|
|
1624
|
+
const stats = fs2.statSync(bundleDir);
|
|
1468
1625
|
if (!stats.isDirectory()) {
|
|
1469
1626
|
throw new Error(`Bundle path is not a directory: ${bundleDir}`);
|
|
1470
1627
|
}
|
|
1471
1628
|
let files;
|
|
1472
1629
|
try {
|
|
1473
|
-
files =
|
|
1630
|
+
files = fs2.readdirSync(bundleDir);
|
|
1474
1631
|
} catch (error) {
|
|
1475
1632
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1476
1633
|
throw new Error(`Failed to read bundle directory: ${errorMessage}`);
|
|
@@ -1485,7 +1642,7 @@ function getJS(bundleDir) {
|
|
|
1485
1642
|
const filePath = path.join(bundleDir, indexFile);
|
|
1486
1643
|
logger.info(`Loading bundle from ${filePath}`);
|
|
1487
1644
|
try {
|
|
1488
|
-
return
|
|
1645
|
+
return fs2.readFileSync(filePath, "utf8");
|
|
1489
1646
|
} catch (error) {
|
|
1490
1647
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1491
1648
|
throw new Error(`Failed to read bundle file: ${errorMessage}`);
|
|
@@ -1891,94 +2048,12 @@ function sendDataResponse3(id, res, sendMessage, clientId) {
|
|
|
1891
2048
|
// src/userResponse/groq.ts
|
|
1892
2049
|
var import_dotenv = __toESM(require_main());
|
|
1893
2050
|
|
|
1894
|
-
// src/userResponse/
|
|
1895
|
-
|
|
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
|
-
}
|
|
2051
|
+
// src/userResponse/base-llm.ts
|
|
2052
|
+
init_utils();
|
|
1978
2053
|
|
|
1979
2054
|
// src/userResponse/schema.ts
|
|
1980
2055
|
import path2 from "path";
|
|
1981
|
-
import
|
|
2056
|
+
import fs3 from "fs";
|
|
1982
2057
|
var Schema = class {
|
|
1983
2058
|
constructor(schemaFilePath) {
|
|
1984
2059
|
this.cachedSchema = null;
|
|
@@ -1992,11 +2067,11 @@ var Schema = class {
|
|
|
1992
2067
|
logger.info(`SCHEMA_FILE_PATH: ${this.schemaFilePath}`);
|
|
1993
2068
|
try {
|
|
1994
2069
|
const dir = path2.dirname(this.schemaFilePath);
|
|
1995
|
-
if (!
|
|
2070
|
+
if (!fs3.existsSync(dir)) {
|
|
1996
2071
|
logger.info(`Creating directory structure: ${dir}`);
|
|
1997
|
-
|
|
2072
|
+
fs3.mkdirSync(dir, { recursive: true });
|
|
1998
2073
|
}
|
|
1999
|
-
if (!
|
|
2074
|
+
if (!fs3.existsSync(this.schemaFilePath)) {
|
|
2000
2075
|
logger.info(`Schema file does not exist at ${this.schemaFilePath}, creating with empty schema`);
|
|
2001
2076
|
const initialSchema = {
|
|
2002
2077
|
database: "",
|
|
@@ -2005,11 +2080,11 @@ var Schema = class {
|
|
|
2005
2080
|
tables: [],
|
|
2006
2081
|
relationships: []
|
|
2007
2082
|
};
|
|
2008
|
-
|
|
2083
|
+
fs3.writeFileSync(this.schemaFilePath, JSON.stringify(initialSchema, null, 4));
|
|
2009
2084
|
this.cachedSchema = initialSchema;
|
|
2010
2085
|
return initialSchema;
|
|
2011
2086
|
}
|
|
2012
|
-
const fileContent =
|
|
2087
|
+
const fileContent = fs3.readFileSync(this.schemaFilePath, "utf-8");
|
|
2013
2088
|
const schema2 = JSON.parse(fileContent);
|
|
2014
2089
|
this.cachedSchema = schema2;
|
|
2015
2090
|
return schema2;
|
|
@@ -2110,7 +2185,7 @@ var Schema = class {
|
|
|
2110
2185
|
var schema = new Schema();
|
|
2111
2186
|
|
|
2112
2187
|
// src/userResponse/prompt-loader.ts
|
|
2113
|
-
import
|
|
2188
|
+
import fs4 from "fs";
|
|
2114
2189
|
import path3 from "path";
|
|
2115
2190
|
var PromptLoader = class {
|
|
2116
2191
|
constructor(config) {
|
|
@@ -2138,7 +2213,8 @@ var PromptLoader = class {
|
|
|
2138
2213
|
"mutli-component",
|
|
2139
2214
|
"actions",
|
|
2140
2215
|
"container-metadata",
|
|
2141
|
-
"text-response"
|
|
2216
|
+
"text-response",
|
|
2217
|
+
"match-text-components"
|
|
2142
2218
|
];
|
|
2143
2219
|
for (const promptType of promptTypes) {
|
|
2144
2220
|
try {
|
|
@@ -2163,9 +2239,9 @@ var PromptLoader = class {
|
|
|
2163
2239
|
try {
|
|
2164
2240
|
const systemPath = path3.join(dir, promptName, "system.md");
|
|
2165
2241
|
const userPath = path3.join(dir, promptName, "user.md");
|
|
2166
|
-
if (
|
|
2167
|
-
const system =
|
|
2168
|
-
const user =
|
|
2242
|
+
if (fs4.existsSync(systemPath) && fs4.existsSync(userPath)) {
|
|
2243
|
+
const system = fs4.readFileSync(systemPath, "utf-8");
|
|
2244
|
+
const user = fs4.readFileSync(userPath, "utf-8");
|
|
2169
2245
|
logger.debug(`Loaded prompt '${promptName}' from ${dir}`);
|
|
2170
2246
|
return { system, user };
|
|
2171
2247
|
}
|
|
@@ -2287,6 +2363,14 @@ var LLM = class {
|
|
|
2287
2363
|
throw new Error(`Unsupported provider: ${provider}. Use "anthropic" or "groq"`);
|
|
2288
2364
|
}
|
|
2289
2365
|
}
|
|
2366
|
+
/* Stream response with tool calling support (Anthropic only for now) */
|
|
2367
|
+
static async streamWithTools(messages, tools, toolHandler, options = {}, maxIterations = 3) {
|
|
2368
|
+
const [provider, modelName] = this._parseModel(options.model);
|
|
2369
|
+
if (provider !== "anthropic") {
|
|
2370
|
+
throw new Error(`Tool calling is only supported for Anthropic models`);
|
|
2371
|
+
}
|
|
2372
|
+
return this._anthropicStreamWithTools(messages, tools, toolHandler, modelName, options, maxIterations);
|
|
2373
|
+
}
|
|
2290
2374
|
// ============================================================
|
|
2291
2375
|
// PRIVATE HELPER METHODS
|
|
2292
2376
|
// ============================================================
|
|
@@ -2364,6 +2448,86 @@ var LLM = class {
|
|
|
2364
2448
|
}
|
|
2365
2449
|
return fullText;
|
|
2366
2450
|
}
|
|
2451
|
+
static async _anthropicStreamWithTools(messages, tools, toolHandler, modelName, options, maxIterations) {
|
|
2452
|
+
const apiKey = options.apiKey || process.env.ANTHROPIC_API_KEY || "";
|
|
2453
|
+
const client = new Anthropic({
|
|
2454
|
+
apiKey
|
|
2455
|
+
});
|
|
2456
|
+
const conversationMessages = [{
|
|
2457
|
+
role: "user",
|
|
2458
|
+
content: messages.user
|
|
2459
|
+
}];
|
|
2460
|
+
let iterations = 0;
|
|
2461
|
+
let finalText = "";
|
|
2462
|
+
while (iterations < maxIterations) {
|
|
2463
|
+
iterations++;
|
|
2464
|
+
const response = await client.messages.create({
|
|
2465
|
+
model: modelName,
|
|
2466
|
+
max_tokens: options.maxTokens || 4e3,
|
|
2467
|
+
temperature: options.temperature,
|
|
2468
|
+
system: messages.sys,
|
|
2469
|
+
messages: conversationMessages,
|
|
2470
|
+
tools
|
|
2471
|
+
});
|
|
2472
|
+
if (response.stop_reason === "end_turn") {
|
|
2473
|
+
const textBlock = response.content.find((block) => block.type === "text");
|
|
2474
|
+
if (textBlock && textBlock.type === "text") {
|
|
2475
|
+
finalText = textBlock.text;
|
|
2476
|
+
if (options.partial) {
|
|
2477
|
+
options.partial(finalText);
|
|
2478
|
+
}
|
|
2479
|
+
}
|
|
2480
|
+
break;
|
|
2481
|
+
}
|
|
2482
|
+
if (response.stop_reason === "tool_use") {
|
|
2483
|
+
const toolUses = response.content.filter((block) => block.type === "tool_use");
|
|
2484
|
+
if (toolUses.length === 0) {
|
|
2485
|
+
break;
|
|
2486
|
+
}
|
|
2487
|
+
conversationMessages.push({
|
|
2488
|
+
role: "assistant",
|
|
2489
|
+
content: response.content
|
|
2490
|
+
});
|
|
2491
|
+
const toolResults = {
|
|
2492
|
+
role: "user",
|
|
2493
|
+
content: []
|
|
2494
|
+
};
|
|
2495
|
+
for (const toolUse of toolUses) {
|
|
2496
|
+
if (toolUse.type === "tool_use") {
|
|
2497
|
+
try {
|
|
2498
|
+
const result = await toolHandler(toolUse.name, toolUse.input);
|
|
2499
|
+
toolResults.content.push({
|
|
2500
|
+
type: "tool_result",
|
|
2501
|
+
tool_use_id: toolUse.id,
|
|
2502
|
+
content: typeof result === "string" ? result : JSON.stringify(result)
|
|
2503
|
+
});
|
|
2504
|
+
} catch (error) {
|
|
2505
|
+
toolResults.content.push({
|
|
2506
|
+
type: "tool_result",
|
|
2507
|
+
tool_use_id: toolUse.id,
|
|
2508
|
+
content: error instanceof Error ? error.message : String(error),
|
|
2509
|
+
is_error: true
|
|
2510
|
+
});
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2513
|
+
}
|
|
2514
|
+
conversationMessages.push(toolResults);
|
|
2515
|
+
} else {
|
|
2516
|
+
const textBlock = response.content.find((block) => block.type === "text");
|
|
2517
|
+
if (textBlock && textBlock.type === "text") {
|
|
2518
|
+
finalText = textBlock.text;
|
|
2519
|
+
if (options.partial) {
|
|
2520
|
+
options.partial(finalText);
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
break;
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
if (iterations >= maxIterations) {
|
|
2527
|
+
throw new Error(`Max iterations (${maxIterations}) reached in tool calling loop`);
|
|
2528
|
+
}
|
|
2529
|
+
return finalText;
|
|
2530
|
+
}
|
|
2367
2531
|
// ============================================================
|
|
2368
2532
|
// GROQ IMPLEMENTATION
|
|
2369
2533
|
// ============================================================
|
|
@@ -2438,6 +2602,42 @@ var LLM = class {
|
|
|
2438
2602
|
}
|
|
2439
2603
|
};
|
|
2440
2604
|
|
|
2605
|
+
// src/userResponse/knowledge-base.ts
|
|
2606
|
+
var getKnowledgeBase = async ({
|
|
2607
|
+
prompt,
|
|
2608
|
+
collections,
|
|
2609
|
+
topK = 1
|
|
2610
|
+
}) => {
|
|
2611
|
+
try {
|
|
2612
|
+
if (!collections || !collections["knowledge-base"] || !collections["knowledge-base"]["query"]) {
|
|
2613
|
+
logger.info("[KnowledgeBase] knowledge-base.query collection not registered, skipping");
|
|
2614
|
+
return "";
|
|
2615
|
+
}
|
|
2616
|
+
logger.info(`[KnowledgeBase] Querying knowledge base for: "${prompt.substring(0, 50)}..."`);
|
|
2617
|
+
const result = await collections["knowledge-base"]["query"]({
|
|
2618
|
+
prompt,
|
|
2619
|
+
topK
|
|
2620
|
+
});
|
|
2621
|
+
if (!result || !result.content) {
|
|
2622
|
+
logger.error("[KnowledgeBase] No knowledge base results returned");
|
|
2623
|
+
return "";
|
|
2624
|
+
}
|
|
2625
|
+
logger.info(`[KnowledgeBase] Retrieved knowledge base context (${result.content.length} chars)`);
|
|
2626
|
+
if (result.metadata?.sources && result.metadata.sources.length > 0) {
|
|
2627
|
+
logger.debug(`[KnowledgeBase] Sources: ${result.metadata.sources.map((s) => s.title).join(", ")}`);
|
|
2628
|
+
}
|
|
2629
|
+
return result.content;
|
|
2630
|
+
} catch (error) {
|
|
2631
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
2632
|
+
logger.warn(`[KnowledgeBase] Error querying knowledge base: ${errorMsg}`);
|
|
2633
|
+
return "";
|
|
2634
|
+
}
|
|
2635
|
+
};
|
|
2636
|
+
var KB = {
|
|
2637
|
+
getKnowledgeBase
|
|
2638
|
+
};
|
|
2639
|
+
var knowledge_base_default = KB;
|
|
2640
|
+
|
|
2441
2641
|
// src/userResponse/base-llm.ts
|
|
2442
2642
|
var BaseLLM = class {
|
|
2443
2643
|
constructor(config) {
|
|
@@ -2976,62 +3176,485 @@ var BaseLLM = class {
|
|
|
2976
3176
|
throw error;
|
|
2977
3177
|
}
|
|
2978
3178
|
}
|
|
3179
|
+
/**
|
|
3180
|
+
* Match components from text response suggestions and generate follow-up questions
|
|
3181
|
+
* Takes a text response with component suggestions (c1:type format) and matches with available components
|
|
3182
|
+
* Also generates intelligent follow-up questions (actions) based on the analysis
|
|
3183
|
+
* @param textResponse - The text response containing component suggestions
|
|
3184
|
+
* @param components - List of available components
|
|
3185
|
+
* @param apiKey - Optional API key
|
|
3186
|
+
* @param logCollector - Optional log collector
|
|
3187
|
+
* @returns Object containing matched components, selected layout, reasoning, and follow-up actions
|
|
3188
|
+
*/
|
|
3189
|
+
async matchComponentsFromTextResponse(textResponse, components, apiKey, logCollector) {
|
|
3190
|
+
try {
|
|
3191
|
+
logger.debug(`[${this.getProviderName()}] Starting component matching from text response`);
|
|
3192
|
+
let availableComponentsText = "No components available";
|
|
3193
|
+
if (components && components.length > 0) {
|
|
3194
|
+
availableComponentsText = components.map((comp, idx) => {
|
|
3195
|
+
const keywords = comp.keywords ? comp.keywords.join(", ") : "";
|
|
3196
|
+
const propsPreview = comp.props ? JSON.stringify(comp.props, null, 2) : "No props";
|
|
3197
|
+
return `${idx + 1}. ID: ${comp.id}
|
|
3198
|
+
Name: ${comp.name}
|
|
3199
|
+
Type: ${comp.type}
|
|
3200
|
+
Description: ${comp.description || "No description"}
|
|
3201
|
+
Keywords: ${keywords}
|
|
3202
|
+
Props Structure: ${propsPreview}`;
|
|
3203
|
+
}).join("\n\n");
|
|
3204
|
+
}
|
|
3205
|
+
const schemaDoc = schema.generateSchemaDocumentation();
|
|
3206
|
+
const prompts = await promptLoader.loadPrompts("match-text-components", {
|
|
3207
|
+
TEXT_RESPONSE: textResponse,
|
|
3208
|
+
AVAILABLE_COMPONENTS: availableComponentsText,
|
|
3209
|
+
SCHEMA_DOC: schemaDoc
|
|
3210
|
+
});
|
|
3211
|
+
logger.debug(`[${this.getProviderName()}] Loaded match-text-components prompts`);
|
|
3212
|
+
logger.file("\n=============================\nmatch text components system prompt:", prompts.system);
|
|
3213
|
+
logCollector?.info("Matching components from text response...");
|
|
3214
|
+
const rawResponse = await LLM.stream(
|
|
3215
|
+
{
|
|
3216
|
+
sys: prompts.system,
|
|
3217
|
+
user: prompts.user
|
|
3218
|
+
},
|
|
3219
|
+
{
|
|
3220
|
+
model: this.model,
|
|
3221
|
+
maxTokens: 3e3,
|
|
3222
|
+
temperature: 0.2,
|
|
3223
|
+
apiKey: this.getApiKey(apiKey)
|
|
3224
|
+
},
|
|
3225
|
+
false
|
|
3226
|
+
// Don't parse as JSON yet, get raw response
|
|
3227
|
+
);
|
|
3228
|
+
logger.debug(`[${this.getProviderName()}] Raw component matching response length: ${rawResponse?.length || 0}`);
|
|
3229
|
+
let result;
|
|
3230
|
+
try {
|
|
3231
|
+
let cleanedResponse = rawResponse || "";
|
|
3232
|
+
cleanedResponse = cleanedResponse.replace(/```json\n?/g, "").replace(/```\n?/g, "").trim();
|
|
3233
|
+
const jsonMatch = cleanedResponse.match(/\{[\s\S]*\}/);
|
|
3234
|
+
if (jsonMatch) {
|
|
3235
|
+
cleanedResponse = jsonMatch[0];
|
|
3236
|
+
}
|
|
3237
|
+
result = JSON.parse(cleanedResponse);
|
|
3238
|
+
} catch (parseError) {
|
|
3239
|
+
logger.error(`[${this.getProviderName()}] Failed to parse component matching JSON response`);
|
|
3240
|
+
const errorMsg = parseError instanceof Error ? parseError.message : String(parseError);
|
|
3241
|
+
const posMatch = errorMsg.match(/position (\d+)/);
|
|
3242
|
+
const errorPos = posMatch ? parseInt(posMatch[1]) : -1;
|
|
3243
|
+
if (errorPos > 0 && rawResponse) {
|
|
3244
|
+
const start = Math.max(0, errorPos - 200);
|
|
3245
|
+
const end = Math.min(rawResponse.length, errorPos + 200);
|
|
3246
|
+
logger.debug(`[${this.getProviderName()}] Error context (position ${errorPos}):`);
|
|
3247
|
+
logger.debug(rawResponse.substring(start, end));
|
|
3248
|
+
logger.debug(" ".repeat(Math.min(200, errorPos - start)) + "^--- Error here");
|
|
3249
|
+
}
|
|
3250
|
+
logger.debug(`[${this.getProviderName()}] Raw response (first 2000 chars):`, rawResponse?.substring(0, 2e3));
|
|
3251
|
+
logger.debug(`[${this.getProviderName()}] Parse error:`, parseError);
|
|
3252
|
+
logCollector?.error(`Failed to parse component matching response: ${errorMsg}`);
|
|
3253
|
+
try {
|
|
3254
|
+
logger.info(`[${this.getProviderName()}] Attempting aggressive JSON cleanup...`);
|
|
3255
|
+
let aggressive = rawResponse || "";
|
|
3256
|
+
aggressive = aggressive.replace(/```json\n?/g, "").replace(/```\n?/g, "").trim();
|
|
3257
|
+
const match = aggressive.match(/\{[\s\S]*\}/);
|
|
3258
|
+
if (match) {
|
|
3259
|
+
aggressive = match[0];
|
|
3260
|
+
}
|
|
3261
|
+
aggressive = aggressive.replace(/,(\s*[}\]])/g, "$1");
|
|
3262
|
+
result = JSON.parse(aggressive);
|
|
3263
|
+
logger.info(`[${this.getProviderName()}] Aggressive cleanup succeeded!`);
|
|
3264
|
+
} catch (secondError) {
|
|
3265
|
+
logger.error(`[${this.getProviderName()}] Aggressive cleanup also failed`);
|
|
3266
|
+
return {
|
|
3267
|
+
components: [],
|
|
3268
|
+
selectedLayout: "MultiComponentContainer",
|
|
3269
|
+
selectedLayoutId: "",
|
|
3270
|
+
selectedLayoutComponent: null,
|
|
3271
|
+
layoutReasoning: "No layout selected",
|
|
3272
|
+
actions: []
|
|
3273
|
+
};
|
|
3274
|
+
}
|
|
3275
|
+
}
|
|
3276
|
+
const matchedComponents = result.matchedComponents || [];
|
|
3277
|
+
const selectedLayout = result.selectedLayout || "MultiComponentContainer";
|
|
3278
|
+
const selectedLayoutId = result.selectedLayoutId || "";
|
|
3279
|
+
const layoutReasoning = result.layoutReasoning || "No layout reasoning provided";
|
|
3280
|
+
const rawActions = result.actions || [];
|
|
3281
|
+
const actions = convertQuestionsToActions(rawActions);
|
|
3282
|
+
let selectedLayoutComponent = null;
|
|
3283
|
+
if (selectedLayoutId) {
|
|
3284
|
+
selectedLayoutComponent = components.find((c) => c.id === selectedLayoutId) || null;
|
|
3285
|
+
if (!selectedLayoutComponent) {
|
|
3286
|
+
logger.warn(`[${this.getProviderName()}] Layout component ${selectedLayoutId} not found in available components`);
|
|
3287
|
+
}
|
|
3288
|
+
}
|
|
3289
|
+
logger.info(`[${this.getProviderName()}] Matched ${matchedComponents.length} components from text response`);
|
|
3290
|
+
logger.info(`[${this.getProviderName()}] Selected layout: ${selectedLayout} (ID: ${selectedLayoutId})`);
|
|
3291
|
+
logger.info(`[${this.getProviderName()}] Layout reasoning: ${layoutReasoning}`);
|
|
3292
|
+
logger.info(`[${this.getProviderName()}] Generated ${actions.length} follow-up actions`);
|
|
3293
|
+
if (matchedComponents.length > 0) {
|
|
3294
|
+
logCollector?.info(`Matched ${matchedComponents.length} components for visualization using ${selectedLayout}`);
|
|
3295
|
+
logCollector?.info(`Layout reasoning: ${layoutReasoning}`);
|
|
3296
|
+
matchedComponents.forEach((comp, idx) => {
|
|
3297
|
+
logCollector?.info(` ${idx + 1}. ${comp.componentName} (${comp.componentType}): ${comp.reasoning}`);
|
|
3298
|
+
if (comp.props?.query) {
|
|
3299
|
+
logCollector?.logQuery(
|
|
3300
|
+
`Component ${idx + 1} query`,
|
|
3301
|
+
comp.props.query,
|
|
3302
|
+
{ componentName: comp.componentName, title: comp.props.title }
|
|
3303
|
+
);
|
|
3304
|
+
}
|
|
3305
|
+
});
|
|
3306
|
+
}
|
|
3307
|
+
if (actions.length > 0) {
|
|
3308
|
+
logCollector?.info(`Generated ${actions.length} follow-up questions`);
|
|
3309
|
+
actions.forEach((action, idx) => {
|
|
3310
|
+
logCollector?.info(` ${idx + 1}. ${action.name}`);
|
|
3311
|
+
});
|
|
3312
|
+
}
|
|
3313
|
+
const finalComponents = matchedComponents.map((mc) => {
|
|
3314
|
+
const originalComponent = components.find((c) => c.id === mc.componentId);
|
|
3315
|
+
if (!originalComponent) {
|
|
3316
|
+
logger.warn(`[${this.getProviderName()}] Component ${mc.componentId} not found in available components`);
|
|
3317
|
+
return null;
|
|
3318
|
+
}
|
|
3319
|
+
return {
|
|
3320
|
+
...originalComponent,
|
|
3321
|
+
props: {
|
|
3322
|
+
...originalComponent.props,
|
|
3323
|
+
...mc.props
|
|
3324
|
+
}
|
|
3325
|
+
};
|
|
3326
|
+
}).filter(Boolean);
|
|
3327
|
+
return {
|
|
3328
|
+
components: finalComponents,
|
|
3329
|
+
selectedLayout,
|
|
3330
|
+
selectedLayoutId,
|
|
3331
|
+
selectedLayoutComponent,
|
|
3332
|
+
layoutReasoning,
|
|
3333
|
+
actions
|
|
3334
|
+
};
|
|
3335
|
+
} catch (error) {
|
|
3336
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
3337
|
+
logger.error(`[${this.getProviderName()}] Error matching components from text response: ${errorMsg}`);
|
|
3338
|
+
logger.debug(`[${this.getProviderName()}] Component matching error details:`, error);
|
|
3339
|
+
logCollector?.error(`Error matching components: ${errorMsg}`);
|
|
3340
|
+
return {
|
|
3341
|
+
components: [],
|
|
3342
|
+
selectedLayout: "MultiComponentContainer",
|
|
3343
|
+
selectedLayoutId: "",
|
|
3344
|
+
selectedLayoutComponent: null,
|
|
3345
|
+
layoutReasoning: "Error occurred during component matching",
|
|
3346
|
+
actions: []
|
|
3347
|
+
};
|
|
3348
|
+
}
|
|
3349
|
+
}
|
|
2979
3350
|
/**
|
|
2980
3351
|
* Generate text-based response for user question
|
|
2981
3352
|
* This provides conversational text responses instead of component generation
|
|
3353
|
+
* Supports tool calling for query execution with automatic retry on errors (max 3 attempts)
|
|
3354
|
+
* After generating text response, if components are provided, matches suggested components
|
|
3355
|
+
* @param streamCallback - Optional callback function to receive text chunks as they stream
|
|
3356
|
+
* @param collections - Collection registry for executing database queries via database.execute
|
|
3357
|
+
* @param components - Optional list of available components for matching suggestions
|
|
2982
3358
|
*/
|
|
2983
|
-
async generateTextResponse(userPrompt, apiKey, logCollector, conversationHistory) {
|
|
3359
|
+
async generateTextResponse(userPrompt, apiKey, logCollector, conversationHistory, streamCallback, collections, components) {
|
|
2984
3360
|
const errors = [];
|
|
2985
3361
|
logger.debug(`[${this.getProviderName()}] Starting text response generation`);
|
|
2986
3362
|
logger.debug(`[${this.getProviderName()}] User prompt: "${userPrompt.substring(0, 50)}..."`);
|
|
2987
3363
|
try {
|
|
3364
|
+
const schemaDoc = schema.generateSchemaDocumentation();
|
|
3365
|
+
const knowledgeBaseContext = await knowledge_base_default.getKnowledgeBase({
|
|
3366
|
+
prompt: userPrompt,
|
|
3367
|
+
collections,
|
|
3368
|
+
topK: 1
|
|
3369
|
+
});
|
|
3370
|
+
logger.file("\n=============================\nknowledge base context:", knowledgeBaseContext);
|
|
2988
3371
|
const prompts = await promptLoader.loadPrompts("text-response", {
|
|
2989
3372
|
USER_PROMPT: userPrompt,
|
|
2990
|
-
CONVERSATION_HISTORY: conversationHistory || "No previous conversation"
|
|
3373
|
+
CONVERSATION_HISTORY: conversationHistory || "No previous conversation",
|
|
3374
|
+
SCHEMA_DOC: schemaDoc,
|
|
3375
|
+
KNOWLEDGE_BASE_CONTEXT: knowledgeBaseContext || "No additional knowledge base context available."
|
|
2991
3376
|
});
|
|
2992
|
-
logger.
|
|
3377
|
+
logger.file("\n=============================\nsystem prompt:", prompts.system);
|
|
3378
|
+
logger.file("\n=============================\nuser prompt:", prompts.user);
|
|
3379
|
+
logger.debug(`[${this.getProviderName()}] Loaded text-response prompts with schema`);
|
|
2993
3380
|
logger.debug(`[${this.getProviderName()}] System prompt length: ${prompts.system.length}, User prompt length: ${prompts.user.length}`);
|
|
2994
|
-
logCollector?.info("Generating text response...");
|
|
2995
|
-
const
|
|
3381
|
+
logCollector?.info("Generating text response with query execution capability...");
|
|
3382
|
+
const tools = [{
|
|
3383
|
+
name: "execute_query",
|
|
3384
|
+
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.",
|
|
3385
|
+
input_schema: {
|
|
3386
|
+
type: "object",
|
|
3387
|
+
properties: {
|
|
3388
|
+
query: {
|
|
3389
|
+
type: "string",
|
|
3390
|
+
description: "The SQL query to execute. Must be valid SQL syntax using table and column names from the schema."
|
|
3391
|
+
},
|
|
3392
|
+
reasoning: {
|
|
3393
|
+
type: "string",
|
|
3394
|
+
description: "Brief explanation of what this query does and why it answers the user's question."
|
|
3395
|
+
}
|
|
3396
|
+
},
|
|
3397
|
+
required: ["query"]
|
|
3398
|
+
}
|
|
3399
|
+
}];
|
|
3400
|
+
const queryAttempts = /* @__PURE__ */ new Map();
|
|
3401
|
+
const MAX_QUERY_ATTEMPTS = 6;
|
|
3402
|
+
let maxAttemptsReached = false;
|
|
3403
|
+
let fullStreamedText = "";
|
|
3404
|
+
const wrappedStreamCallback = streamCallback ? (chunk) => {
|
|
3405
|
+
fullStreamedText += chunk;
|
|
3406
|
+
streamCallback(chunk);
|
|
3407
|
+
} : void 0;
|
|
3408
|
+
const toolHandler = async (toolName, toolInput) => {
|
|
3409
|
+
if (toolName === "execute_query") {
|
|
3410
|
+
let query = toolInput.query;
|
|
3411
|
+
const reasoning = toolInput.reasoning;
|
|
3412
|
+
const { ensureQueryLimit: ensureQueryLimit2 } = await Promise.resolve().then(() => (init_utils(), utils_exports));
|
|
3413
|
+
query = ensureQueryLimit2(query, 32, 32);
|
|
3414
|
+
const queryKey = query.toLowerCase().replace(/\s+/g, " ").trim();
|
|
3415
|
+
const attempts = (queryAttempts.get(queryKey) || 0) + 1;
|
|
3416
|
+
queryAttempts.set(queryKey, attempts);
|
|
3417
|
+
logger.info(`[${this.getProviderName()}] Executing query (attempt ${attempts}/${MAX_QUERY_ATTEMPTS}): ${query.substring(0, 100)}...`);
|
|
3418
|
+
if (reasoning) {
|
|
3419
|
+
logCollector?.info(`Query reasoning: ${reasoning}`);
|
|
3420
|
+
}
|
|
3421
|
+
if (attempts > MAX_QUERY_ATTEMPTS) {
|
|
3422
|
+
const errorMsg = `Maximum query attempts (${MAX_QUERY_ATTEMPTS}) reached. Unable to generate a valid query for your question.`;
|
|
3423
|
+
logger.error(`[${this.getProviderName()}] ${errorMsg}`);
|
|
3424
|
+
logCollector?.error(errorMsg);
|
|
3425
|
+
maxAttemptsReached = true;
|
|
3426
|
+
if (wrappedStreamCallback) {
|
|
3427
|
+
wrappedStreamCallback(`
|
|
3428
|
+
|
|
3429
|
+
\u274C ${errorMsg}
|
|
3430
|
+
|
|
3431
|
+
Please try rephrasing your question or simplifying your request.
|
|
3432
|
+
|
|
3433
|
+
`);
|
|
3434
|
+
}
|
|
3435
|
+
throw new Error(errorMsg);
|
|
3436
|
+
}
|
|
3437
|
+
try {
|
|
3438
|
+
if (wrappedStreamCallback) {
|
|
3439
|
+
if (attempts === 1) {
|
|
3440
|
+
wrappedStreamCallback(`
|
|
3441
|
+
|
|
3442
|
+
\u{1F50D} **Analyzing your question...**
|
|
3443
|
+
|
|
3444
|
+
`);
|
|
3445
|
+
if (reasoning) {
|
|
3446
|
+
wrappedStreamCallback(`\u{1F4AD} ${reasoning}
|
|
3447
|
+
|
|
3448
|
+
`);
|
|
3449
|
+
}
|
|
3450
|
+
wrappedStreamCallback(`\u{1F4DD} **Generated SQL Query:**
|
|
3451
|
+
\`\`\`sql
|
|
3452
|
+
${query}
|
|
3453
|
+
\`\`\`
|
|
3454
|
+
|
|
3455
|
+
`);
|
|
3456
|
+
wrappedStreamCallback(`\u26A1 **Executing query...**
|
|
3457
|
+
|
|
3458
|
+
`);
|
|
3459
|
+
} else {
|
|
3460
|
+
wrappedStreamCallback(`
|
|
3461
|
+
|
|
3462
|
+
\u{1F504} **Retrying with corrected query (attempt ${attempts}/${MAX_QUERY_ATTEMPTS})...**
|
|
3463
|
+
|
|
3464
|
+
`);
|
|
3465
|
+
if (reasoning) {
|
|
3466
|
+
wrappedStreamCallback(`\u{1F4AD} ${reasoning}
|
|
3467
|
+
|
|
3468
|
+
`);
|
|
3469
|
+
}
|
|
3470
|
+
wrappedStreamCallback(`\u{1F4DD} **Corrected SQL Query:**
|
|
3471
|
+
\`\`\`sql
|
|
3472
|
+
${query}
|
|
3473
|
+
\`\`\`
|
|
3474
|
+
|
|
3475
|
+
`);
|
|
3476
|
+
wrappedStreamCallback(`\u26A1 **Executing query...**
|
|
3477
|
+
|
|
3478
|
+
`);
|
|
3479
|
+
}
|
|
3480
|
+
}
|
|
3481
|
+
logCollector?.logQuery(
|
|
3482
|
+
`Executing SQL query (attempt ${attempts})`,
|
|
3483
|
+
query,
|
|
3484
|
+
{ reasoning, attempt: attempts }
|
|
3485
|
+
);
|
|
3486
|
+
if (!collections || !collections["database"] || !collections["database"]["execute"]) {
|
|
3487
|
+
throw new Error("Database collection not registered. Please register database.execute collection to execute queries.");
|
|
3488
|
+
}
|
|
3489
|
+
const result2 = await collections["database"]["execute"]({ sql: query });
|
|
3490
|
+
const data = result2?.data || result2;
|
|
3491
|
+
const rowCount = result2?.count ?? (Array.isArray(data) ? data.length : "N/A");
|
|
3492
|
+
logger.info(`[${this.getProviderName()}] Query executed successfully, rows returned: ${rowCount}`);
|
|
3493
|
+
logCollector?.info(`Query successful, returned ${rowCount} rows`);
|
|
3494
|
+
if (wrappedStreamCallback) {
|
|
3495
|
+
wrappedStreamCallback(`\u2705 **Query executed successfully!**
|
|
3496
|
+
|
|
3497
|
+
`);
|
|
3498
|
+
if (Array.isArray(data) && data.length > 0) {
|
|
3499
|
+
const firstRow = data[0];
|
|
3500
|
+
const columns = Object.keys(firstRow);
|
|
3501
|
+
if (data.length === 1 && columns.length === 1) {
|
|
3502
|
+
const value = firstRow[columns[0]];
|
|
3503
|
+
wrappedStreamCallback(`**Result:** ${value}
|
|
3504
|
+
|
|
3505
|
+
`);
|
|
3506
|
+
} else if (data.length > 0) {
|
|
3507
|
+
wrappedStreamCallback(`**Retrieved ${rowCount} rows**
|
|
3508
|
+
|
|
3509
|
+
`);
|
|
3510
|
+
wrappedStreamCallback(`<DataTable>${JSON.stringify(data)}</DataTable>
|
|
3511
|
+
|
|
3512
|
+
`);
|
|
3513
|
+
}
|
|
3514
|
+
} else if (Array.isArray(data) && data.length === 0) {
|
|
3515
|
+
wrappedStreamCallback(`**No rows returned.**
|
|
3516
|
+
|
|
3517
|
+
`);
|
|
3518
|
+
}
|
|
3519
|
+
wrappedStreamCallback(`\u{1F4CA} **Analyzing results...**
|
|
3520
|
+
|
|
3521
|
+
`);
|
|
3522
|
+
}
|
|
3523
|
+
return JSON.stringify(data, null, 2);
|
|
3524
|
+
} catch (error) {
|
|
3525
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
3526
|
+
logger.error(`[${this.getProviderName()}] Query execution failed (attempt ${attempts}/${MAX_QUERY_ATTEMPTS}): ${errorMsg}`);
|
|
3527
|
+
logCollector?.error(`Query failed (attempt ${attempts}/${MAX_QUERY_ATTEMPTS}): ${errorMsg}`);
|
|
3528
|
+
if (wrappedStreamCallback) {
|
|
3529
|
+
wrappedStreamCallback(`\u274C **Query execution failed:**
|
|
3530
|
+
\`\`\`
|
|
3531
|
+
${errorMsg}
|
|
3532
|
+
\`\`\`
|
|
3533
|
+
|
|
3534
|
+
`);
|
|
3535
|
+
if (attempts < MAX_QUERY_ATTEMPTS) {
|
|
3536
|
+
wrappedStreamCallback(`\u{1F527} **Generating corrected query...**
|
|
3537
|
+
|
|
3538
|
+
`);
|
|
3539
|
+
}
|
|
3540
|
+
}
|
|
3541
|
+
throw new Error(`Query execution failed: ${errorMsg}`);
|
|
3542
|
+
}
|
|
3543
|
+
}
|
|
3544
|
+
throw new Error(`Unknown tool: ${toolName}`);
|
|
3545
|
+
};
|
|
3546
|
+
const result = await LLM.streamWithTools(
|
|
2996
3547
|
{
|
|
2997
3548
|
sys: prompts.system,
|
|
2998
3549
|
user: prompts.user
|
|
2999
3550
|
},
|
|
3551
|
+
tools,
|
|
3552
|
+
toolHandler,
|
|
3000
3553
|
{
|
|
3001
3554
|
model: this.model,
|
|
3002
|
-
maxTokens:
|
|
3555
|
+
maxTokens: 4e3,
|
|
3003
3556
|
temperature: 0.7,
|
|
3004
|
-
apiKey: this.getApiKey(apiKey)
|
|
3557
|
+
apiKey: this.getApiKey(apiKey),
|
|
3558
|
+
partial: wrappedStreamCallback
|
|
3559
|
+
// Pass the wrapped streaming callback to LLM
|
|
3005
3560
|
},
|
|
3006
|
-
|
|
3007
|
-
//
|
|
3561
|
+
10
|
|
3562
|
+
// max iterations: allows for 6 retries + final response + buffer
|
|
3008
3563
|
);
|
|
3009
|
-
logger.info(`[${this.getProviderName()}] Text response
|
|
3010
|
-
|
|
3011
|
-
|
|
3564
|
+
logger.info(`[${this.getProviderName()}] Text response stream completed`);
|
|
3565
|
+
const textResponse = fullStreamedText || result || "I apologize, but I was unable to generate a response.";
|
|
3566
|
+
if (maxAttemptsReached) {
|
|
3567
|
+
logger.warn(`[${this.getProviderName()}] Max query attempts reached, returning failure response`);
|
|
3568
|
+
logCollector?.error("Failed to generate valid query after maximum attempts");
|
|
3569
|
+
return {
|
|
3570
|
+
success: false,
|
|
3571
|
+
errors: [`Maximum query attempts (${MAX_QUERY_ATTEMPTS}) reached. Unable to generate a valid query for your question.`],
|
|
3572
|
+
data: {
|
|
3573
|
+
text: textResponse,
|
|
3574
|
+
// Include the streamed text showing all attempts
|
|
3575
|
+
matchedComponents: [],
|
|
3576
|
+
actions: [],
|
|
3577
|
+
method: `${this.getProviderName()}-text-response-max-attempts`
|
|
3578
|
+
}
|
|
3579
|
+
};
|
|
3580
|
+
}
|
|
3581
|
+
logCollector?.info(`Text response: ${textResponse.substring(0, 100)}${textResponse.length > 100 ? "..." : ""}`);
|
|
3012
3582
|
logCollector?.logExplanation(
|
|
3013
3583
|
"Text response generated",
|
|
3014
|
-
|
|
3584
|
+
"Generated plain text response with component suggestions",
|
|
3015
3585
|
{
|
|
3016
|
-
|
|
3017
|
-
confidence: result.confidence,
|
|
3018
|
-
textLength: result.text?.length || 0
|
|
3586
|
+
textLength: textResponse.length
|
|
3019
3587
|
}
|
|
3020
3588
|
);
|
|
3589
|
+
let matchedComponents = [];
|
|
3590
|
+
let selectedLayoutComponent = null;
|
|
3591
|
+
let layoutReasoning = "No layout selected";
|
|
3592
|
+
let actions = [];
|
|
3593
|
+
if (components && components.length > 0) {
|
|
3594
|
+
logger.info(`[${this.getProviderName()}] Matching components from text response...`);
|
|
3595
|
+
const matchResult = await this.matchComponentsFromTextResponse(
|
|
3596
|
+
textResponse,
|
|
3597
|
+
components,
|
|
3598
|
+
apiKey,
|
|
3599
|
+
logCollector
|
|
3600
|
+
);
|
|
3601
|
+
matchedComponents = matchResult.components;
|
|
3602
|
+
selectedLayoutComponent = matchResult.selectedLayoutComponent;
|
|
3603
|
+
layoutReasoning = matchResult.layoutReasoning;
|
|
3604
|
+
actions = matchResult.actions;
|
|
3605
|
+
}
|
|
3606
|
+
let container_componet = null;
|
|
3607
|
+
if (matchedComponents.length > 0) {
|
|
3608
|
+
if (selectedLayoutComponent) {
|
|
3609
|
+
container_componet = {
|
|
3610
|
+
...selectedLayoutComponent,
|
|
3611
|
+
id: `${selectedLayoutComponent.id}_${Date.now()}`,
|
|
3612
|
+
description: layoutReasoning,
|
|
3613
|
+
props: {
|
|
3614
|
+
...selectedLayoutComponent.props,
|
|
3615
|
+
config: {
|
|
3616
|
+
...selectedLayoutComponent.props?.config || {},
|
|
3617
|
+
components: matchedComponents
|
|
3618
|
+
},
|
|
3619
|
+
actions
|
|
3620
|
+
}
|
|
3621
|
+
};
|
|
3622
|
+
logger.info(`[${this.getProviderName()}] Created ${selectedLayoutComponent.name} (${selectedLayoutComponent.type}) container with ${matchedComponents.length} components and ${actions.length} actions`);
|
|
3623
|
+
logCollector?.info(`Created ${selectedLayoutComponent.name} with ${matchedComponents.length} components and ${actions.length} actions: ${layoutReasoning}`);
|
|
3624
|
+
} else {
|
|
3625
|
+
container_componet = {
|
|
3626
|
+
id: `multi_container_${Date.now()}`,
|
|
3627
|
+
name: "MultiComponentContainer",
|
|
3628
|
+
type: "Container",
|
|
3629
|
+
description: layoutReasoning,
|
|
3630
|
+
category: "dynamic",
|
|
3631
|
+
keywords: ["dashboard", "layout", "container"],
|
|
3632
|
+
props: {
|
|
3633
|
+
config: {
|
|
3634
|
+
components: matchedComponents
|
|
3635
|
+
},
|
|
3636
|
+
actions
|
|
3637
|
+
}
|
|
3638
|
+
};
|
|
3639
|
+
logger.info(`[${this.getProviderName()}] Created fallback MultiComponentContainer with ${matchedComponents.length} components and ${actions.length} actions`);
|
|
3640
|
+
logCollector?.info(`Created MultiComponentContainer with ${matchedComponents.length} components and ${actions.length} actions: ${layoutReasoning}`);
|
|
3641
|
+
}
|
|
3642
|
+
}
|
|
3021
3643
|
return {
|
|
3022
3644
|
success: true,
|
|
3023
3645
|
data: {
|
|
3024
|
-
text:
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3646
|
+
text: textResponse,
|
|
3647
|
+
matchedComponents,
|
|
3648
|
+
component: container_componet,
|
|
3649
|
+
layoutReasoning,
|
|
3650
|
+
actions,
|
|
3651
|
+
method: `${this.getProviderName()}-text-response`
|
|
3028
3652
|
},
|
|
3029
3653
|
errors: []
|
|
3030
3654
|
};
|
|
3031
3655
|
} catch (error) {
|
|
3032
3656
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
3033
3657
|
logger.error(`[${this.getProviderName()}] Error generating text response: ${errorMsg}`);
|
|
3034
|
-
logger.debug(`[${this.getProviderName()}] Text response generation error details:`, error);
|
|
3035
3658
|
logCollector?.error(`Error generating text response: ${errorMsg}`);
|
|
3036
3659
|
errors.push(errorMsg);
|
|
3037
3660
|
return {
|
|
@@ -3039,9 +3662,9 @@ var BaseLLM = class {
|
|
|
3039
3662
|
errors,
|
|
3040
3663
|
data: {
|
|
3041
3664
|
text: "I apologize, but I encountered an error while processing your question. Please try rephrasing or ask something else.",
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3665
|
+
matchedComponents: [],
|
|
3666
|
+
actions: [],
|
|
3667
|
+
method: `${this.getProviderName()}-text-response-error`
|
|
3045
3668
|
}
|
|
3046
3669
|
};
|
|
3047
3670
|
}
|
|
@@ -3232,8 +3855,11 @@ var BaseLLM = class {
|
|
|
3232
3855
|
* Supports both component generation and text response modes
|
|
3233
3856
|
*
|
|
3234
3857
|
* @param responseMode - 'component' for component generation (default), 'text' for text responses
|
|
3858
|
+
* @param streamCallback - Optional callback function to receive text chunks as they stream (only for text mode)
|
|
3859
|
+
* @param collections - Collection registry for executing database queries (required for text mode)
|
|
3235
3860
|
*/
|
|
3236
|
-
async handleUserRequest(userPrompt, components, apiKey, logCollector, conversationHistory, responseMode = "component") {
|
|
3861
|
+
async handleUserRequest(userPrompt, components, apiKey, logCollector, conversationHistory, responseMode = "component", streamCallback, collections) {
|
|
3862
|
+
const startTime = Date.now();
|
|
3237
3863
|
logger.info(`[${this.getProviderName()}] handleUserRequest called with responseMode: ${responseMode}`);
|
|
3238
3864
|
if (responseMode === "text") {
|
|
3239
3865
|
logger.info(`[${this.getProviderName()}] Using text response mode`);
|
|
@@ -3242,25 +3868,23 @@ var BaseLLM = class {
|
|
|
3242
3868
|
userPrompt,
|
|
3243
3869
|
apiKey,
|
|
3244
3870
|
logCollector,
|
|
3245
|
-
conversationHistory
|
|
3871
|
+
conversationHistory,
|
|
3872
|
+
streamCallback,
|
|
3873
|
+
collections,
|
|
3874
|
+
components
|
|
3246
3875
|
);
|
|
3247
3876
|
if (!textResponse.success) {
|
|
3877
|
+
const elapsedTime3 = Date.now() - startTime;
|
|
3248
3878
|
logger.error(`[${this.getProviderName()}] Text response generation failed`);
|
|
3879
|
+
logger.info(`[${this.getProviderName()}] Total time taken: ${elapsedTime3}ms (${(elapsedTime3 / 1e3).toFixed(2)}s)`);
|
|
3880
|
+
logCollector?.info(`Total time taken: ${elapsedTime3}ms (${(elapsedTime3 / 1e3).toFixed(2)}s)`);
|
|
3249
3881
|
return textResponse;
|
|
3250
3882
|
}
|
|
3883
|
+
const elapsedTime2 = Date.now() - startTime;
|
|
3251
3884
|
logger.info(`[${this.getProviderName()}] Text response generated successfully`);
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
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
|
-
};
|
|
3885
|
+
logger.info(`[${this.getProviderName()}] Total time taken: ${elapsedTime2}ms (${(elapsedTime2 / 1e3).toFixed(2)}s)`);
|
|
3886
|
+
logCollector?.info(`Total time taken: ${elapsedTime2}ms (${(elapsedTime2 / 1e3).toFixed(2)}s)`);
|
|
3887
|
+
return textResponse;
|
|
3264
3888
|
}
|
|
3265
3889
|
const componentResponse = await this.generateComponentResponse(
|
|
3266
3890
|
userPrompt,
|
|
@@ -3270,24 +3894,17 @@ var BaseLLM = class {
|
|
|
3270
3894
|
conversationHistory
|
|
3271
3895
|
);
|
|
3272
3896
|
if (!componentResponse.success) {
|
|
3897
|
+
const elapsedTime2 = Date.now() - startTime;
|
|
3273
3898
|
logger.error(`[${this.getProviderName()}] Component response generation failed`);
|
|
3899
|
+
logger.info(`[${this.getProviderName()}] Total time taken: ${elapsedTime2}ms (${(elapsedTime2 / 1e3).toFixed(2)}s)`);
|
|
3900
|
+
logCollector?.info(`Total time taken: ${elapsedTime2}ms (${(elapsedTime2 / 1e3).toFixed(2)}s)`);
|
|
3274
3901
|
return componentResponse;
|
|
3275
3902
|
}
|
|
3903
|
+
const elapsedTime = Date.now() - startTime;
|
|
3276
3904
|
logger.info(`[${this.getProviderName()}] Component response generated successfully`);
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
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
|
-
};
|
|
3905
|
+
logger.info(`[${this.getProviderName()}] Total time taken: ${elapsedTime}ms (${(elapsedTime / 1e3).toFixed(2)}s)`);
|
|
3906
|
+
logCollector?.info(`Total time taken: ${elapsedTime}ms (${(elapsedTime / 1e3).toFixed(2)}s)`);
|
|
3907
|
+
return componentResponse;
|
|
3291
3908
|
}
|
|
3292
3909
|
/**
|
|
3293
3910
|
* Generate next questions that the user might ask based on the original prompt and generated component
|
|
@@ -3400,7 +4017,7 @@ function getLLMProviders() {
|
|
|
3400
4017
|
return DEFAULT_PROVIDERS;
|
|
3401
4018
|
}
|
|
3402
4019
|
}
|
|
3403
|
-
var useAnthropicMethod = async (prompt, components, apiKey, logCollector, conversationHistory, responseMode = "component") => {
|
|
4020
|
+
var useAnthropicMethod = async (prompt, components, apiKey, logCollector, conversationHistory, responseMode = "component", streamCallback, collections) => {
|
|
3404
4021
|
logger.debug("[useAnthropicMethod] Initializing Anthropic Claude matching method");
|
|
3405
4022
|
logger.debug(`[useAnthropicMethod] Response mode: ${responseMode}`);
|
|
3406
4023
|
const msg = `Using Anthropic Claude ${responseMode === "text" ? "text response" : "matching"} method...`;
|
|
@@ -3412,11 +4029,11 @@ var useAnthropicMethod = async (prompt, components, apiKey, logCollector, conver
|
|
|
3412
4029
|
return { success: false, errors: [emptyMsg] };
|
|
3413
4030
|
}
|
|
3414
4031
|
logger.debug(`[useAnthropicMethod] Processing with ${components.length} components`);
|
|
3415
|
-
const matchResult = await anthropicLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory, responseMode);
|
|
4032
|
+
const matchResult = await anthropicLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory, responseMode, streamCallback, collections);
|
|
3416
4033
|
logger.info(`[useAnthropicMethod] Successfully generated ${responseMode} using Anthropic`);
|
|
3417
4034
|
return matchResult;
|
|
3418
4035
|
};
|
|
3419
|
-
var useGroqMethod = async (prompt, components, apiKey, logCollector, conversationHistory, responseMode = "component") => {
|
|
4036
|
+
var useGroqMethod = async (prompt, components, apiKey, logCollector, conversationHistory, responseMode = "component", streamCallback, collections) => {
|
|
3420
4037
|
logger.debug("[useGroqMethod] Initializing Groq LLM matching method");
|
|
3421
4038
|
logger.debug(`[useGroqMethod] Response mode: ${responseMode}`);
|
|
3422
4039
|
const msg = `Using Groq LLM ${responseMode === "text" ? "text response" : "matching"} method...`;
|
|
@@ -3429,16 +4046,16 @@ var useGroqMethod = async (prompt, components, apiKey, logCollector, conversatio
|
|
|
3429
4046
|
return { success: false, errors: [emptyMsg] };
|
|
3430
4047
|
}
|
|
3431
4048
|
logger.debug(`[useGroqMethod] Processing with ${components.length} components`);
|
|
3432
|
-
const matchResult = await groqLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory, responseMode);
|
|
4049
|
+
const matchResult = await groqLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory, responseMode, streamCallback, collections);
|
|
3433
4050
|
logger.info(`[useGroqMethod] Successfully generated ${responseMode} using Groq`);
|
|
3434
4051
|
return matchResult;
|
|
3435
4052
|
};
|
|
3436
4053
|
var getUserResponseFromCache = async (prompt) => {
|
|
3437
4054
|
return false;
|
|
3438
4055
|
};
|
|
3439
|
-
var get_user_response = async (prompt, components, anthropicApiKey, groqApiKey, llmProviders, logCollector, conversationHistory) => {
|
|
3440
|
-
const responseMode = "component";
|
|
4056
|
+
var get_user_response = async (prompt, components, anthropicApiKey, groqApiKey, llmProviders, logCollector, conversationHistory, responseMode = "component", streamCallback, collections) => {
|
|
3441
4057
|
logger.debug(`[get_user_response] Starting user response generation for prompt: "${prompt.substring(0, 50)}..."`);
|
|
4058
|
+
logger.debug(`[get_user_response] Response mode: ${responseMode}`);
|
|
3442
4059
|
logger.debug("[get_user_response] Checking cache for existing response");
|
|
3443
4060
|
const userResponse = await getUserResponseFromCache(prompt);
|
|
3444
4061
|
if (userResponse) {
|
|
@@ -3469,16 +4086,16 @@ var get_user_response = async (prompt, components, anthropicApiKey, groqApiKey,
|
|
|
3469
4086
|
logCollector?.info(attemptMsg);
|
|
3470
4087
|
let result;
|
|
3471
4088
|
if (provider === "anthropic") {
|
|
3472
|
-
result = await useAnthropicMethod(prompt, components, anthropicApiKey, logCollector, conversationHistory, responseMode);
|
|
4089
|
+
result = await useAnthropicMethod(prompt, components, anthropicApiKey, logCollector, conversationHistory, responseMode, streamCallback, collections);
|
|
3473
4090
|
} else if (provider === "groq") {
|
|
3474
|
-
result = await useGroqMethod(prompt, components, groqApiKey, logCollector, conversationHistory, responseMode);
|
|
4091
|
+
result = await useGroqMethod(prompt, components, groqApiKey, logCollector, conversationHistory, responseMode, streamCallback, collections);
|
|
3475
4092
|
} else {
|
|
3476
4093
|
logger.warn(`[get_user_response] Unknown provider: ${provider} - skipping`);
|
|
3477
4094
|
errors.push(`Unknown provider: ${provider}`);
|
|
3478
4095
|
continue;
|
|
3479
4096
|
}
|
|
3480
4097
|
if (result.success) {
|
|
3481
|
-
const successMsg = `Success with provider: ${provider}
|
|
4098
|
+
const successMsg = `Success with provider: ${provider}`;
|
|
3482
4099
|
logger.info(`${successMsg}`);
|
|
3483
4100
|
logCollector?.info(successMsg);
|
|
3484
4101
|
return result;
|
|
@@ -3699,8 +4316,7 @@ var CONTEXT_CONFIG = {
|
|
|
3699
4316
|
};
|
|
3700
4317
|
|
|
3701
4318
|
// src/handlers/user-prompt-request.ts
|
|
3702
|
-
var
|
|
3703
|
-
var get_user_request = async (data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders) => {
|
|
4319
|
+
var get_user_request = async (data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders, collections) => {
|
|
3704
4320
|
const errors = [];
|
|
3705
4321
|
logger.debug("[USER_PROMPT_REQ] Parsing incoming message data");
|
|
3706
4322
|
const parseResult = UserPromptRequestMessageSchema.safeParse(data);
|
|
@@ -3716,19 +4332,6 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
|
|
|
3716
4332
|
const prompt = payload.prompt;
|
|
3717
4333
|
const SA_RUNTIME = payload.SA_RUNTIME;
|
|
3718
4334
|
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
4335
|
if (!SA_RUNTIME) {
|
|
3733
4336
|
errors.push("SA_RUNTIME is required");
|
|
3734
4337
|
}
|
|
@@ -3743,9 +4346,7 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
|
|
|
3743
4346
|
if (!prompt) {
|
|
3744
4347
|
errors.push("Prompt not found");
|
|
3745
4348
|
}
|
|
3746
|
-
|
|
3747
|
-
errors.push("Components not found");
|
|
3748
|
-
}
|
|
4349
|
+
logger.debug(`[REQUEST ${id}] Full request details - uiBlockId: ${existingUiBlockId}, threadId: ${threadId}, prompt: ${prompt}`);
|
|
3749
4350
|
if (errors.length > 0) {
|
|
3750
4351
|
return { success: false, errors, id, wsId };
|
|
3751
4352
|
}
|
|
@@ -3759,8 +4360,44 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
|
|
|
3759
4360
|
logCollector.info(`Starting user prompt request with ${components.length} components`);
|
|
3760
4361
|
const conversationHistory = thread.getConversationContext(CONTEXT_CONFIG.MAX_CONVERSATION_CONTEXT_BLOCKS, existingUiBlockId);
|
|
3761
4362
|
logger.info("conversationHistory", conversationHistory);
|
|
3762
|
-
const
|
|
3763
|
-
logger.info("
|
|
4363
|
+
const responseMode = payload.responseMode || "component";
|
|
4364
|
+
logger.info("responseMode", responseMode);
|
|
4365
|
+
let streamCallback;
|
|
4366
|
+
let accumulatedStreamResponse = "";
|
|
4367
|
+
if (responseMode === "text") {
|
|
4368
|
+
streamCallback = (chunk) => {
|
|
4369
|
+
accumulatedStreamResponse += chunk;
|
|
4370
|
+
logger.debug(`[STREAM] Sending chunk (${chunk.length} chars): "${chunk.substring(0, 20)}..."`);
|
|
4371
|
+
const streamMessage = {
|
|
4372
|
+
id: `stream_${existingUiBlockId}`,
|
|
4373
|
+
// Different ID pattern for streaming
|
|
4374
|
+
type: "USER_PROMPT_STREAM",
|
|
4375
|
+
from: { type: "data-agent" },
|
|
4376
|
+
to: {
|
|
4377
|
+
type: "runtime",
|
|
4378
|
+
id: wsId
|
|
4379
|
+
},
|
|
4380
|
+
payload: {
|
|
4381
|
+
uiBlockId: existingUiBlockId,
|
|
4382
|
+
chunk
|
|
4383
|
+
}
|
|
4384
|
+
};
|
|
4385
|
+
sendMessage(streamMessage);
|
|
4386
|
+
logger.debug(`[STREAM] Chunk sent to wsId: ${wsId}`);
|
|
4387
|
+
};
|
|
4388
|
+
}
|
|
4389
|
+
const userResponse = await get_user_response(
|
|
4390
|
+
prompt,
|
|
4391
|
+
components,
|
|
4392
|
+
anthropicApiKey,
|
|
4393
|
+
groqApiKey,
|
|
4394
|
+
llmProviders,
|
|
4395
|
+
logCollector,
|
|
4396
|
+
conversationHistory,
|
|
4397
|
+
responseMode,
|
|
4398
|
+
streamCallback,
|
|
4399
|
+
collections
|
|
4400
|
+
);
|
|
3764
4401
|
logCollector.info("User prompt request completed");
|
|
3765
4402
|
const uiBlockId = existingUiBlockId;
|
|
3766
4403
|
if (!userResponse.success) {
|
|
@@ -3777,16 +4414,21 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
|
|
|
3777
4414
|
}
|
|
3778
4415
|
let component = null;
|
|
3779
4416
|
let textResponse = null;
|
|
4417
|
+
let actions = [];
|
|
3780
4418
|
if (userResponse.data) {
|
|
3781
4419
|
if (typeof userResponse.data === "object") {
|
|
3782
4420
|
if ("component" in userResponse.data) {
|
|
3783
4421
|
component = userResponse.data.component;
|
|
3784
4422
|
}
|
|
3785
4423
|
if ("textResponse" in userResponse.data) {
|
|
3786
|
-
textResponse = userResponse.data.
|
|
4424
|
+
textResponse = userResponse.data.text;
|
|
4425
|
+
}
|
|
4426
|
+
if ("actions" in userResponse.data) {
|
|
4427
|
+
actions = userResponse.data.actions || [];
|
|
3787
4428
|
}
|
|
3788
4429
|
}
|
|
3789
4430
|
}
|
|
4431
|
+
const finalTextResponse = responseMode === "text" && accumulatedStreamResponse ? accumulatedStreamResponse : textResponse;
|
|
3790
4432
|
const uiBlock = new UIBlock(
|
|
3791
4433
|
prompt,
|
|
3792
4434
|
{},
|
|
@@ -3794,11 +4436,15 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
|
|
|
3794
4436
|
component,
|
|
3795
4437
|
// generatedComponentMetadata: full component object (ComponentSchema)
|
|
3796
4438
|
[],
|
|
3797
|
-
// actions: empty initially
|
|
4439
|
+
// actions: empty initially, will be set below
|
|
3798
4440
|
uiBlockId,
|
|
3799
|
-
|
|
3800
|
-
// textResponse:
|
|
4441
|
+
finalTextResponse
|
|
4442
|
+
// textResponse: FULL streaming response including all intermediate messages
|
|
3801
4443
|
);
|
|
4444
|
+
if (actions.length > 0) {
|
|
4445
|
+
uiBlock.setActions(actions);
|
|
4446
|
+
logger.info(`Stored ${actions.length} actions in UIBlock: ${uiBlockId}`);
|
|
4447
|
+
}
|
|
3802
4448
|
thread.addUIBlock(uiBlock);
|
|
3803
4449
|
logger.info(`Created UIBlock: ${uiBlockId} in Thread: ${threadId}`);
|
|
3804
4450
|
return {
|
|
@@ -3811,8 +4457,8 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
|
|
|
3811
4457
|
wsId
|
|
3812
4458
|
};
|
|
3813
4459
|
};
|
|
3814
|
-
async function handleUserPromptRequest(data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders) {
|
|
3815
|
-
const response = await get_user_request(data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders);
|
|
4460
|
+
async function handleUserPromptRequest(data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders, collections) {
|
|
4461
|
+
const response = await get_user_request(data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders, collections);
|
|
3816
4462
|
sendDataResponse4(
|
|
3817
4463
|
response.id || data.id,
|
|
3818
4464
|
{
|
|
@@ -4036,7 +4682,6 @@ async function handleActionsRequest(data, sendMessage, anthropicApiKey, groqApiK
|
|
|
4036
4682
|
const uiBlockId = SA_RUNTIME.uiBlockId;
|
|
4037
4683
|
const threadId = SA_RUNTIME.threadId;
|
|
4038
4684
|
logger.debug(`[ACTIONS_REQ ${id}] SA_RUNTIME validated - threadId: ${threadId}, uiBlockId: ${uiBlockId}`);
|
|
4039
|
-
logger.debug(`[ACTIONS_REQ ${id}] Retrieving thread: ${threadId}`);
|
|
4040
4685
|
const threadManager = ThreadManager.getInstance();
|
|
4041
4686
|
const thread = threadManager.getThread(threadId);
|
|
4042
4687
|
if (!thread) {
|
|
@@ -4888,7 +5533,7 @@ function sendResponse5(id, res, sendMessage, clientId) {
|
|
|
4888
5533
|
}
|
|
4889
5534
|
|
|
4890
5535
|
// src/auth/user-manager.ts
|
|
4891
|
-
import
|
|
5536
|
+
import fs5 from "fs";
|
|
4892
5537
|
import path4 from "path";
|
|
4893
5538
|
import os from "os";
|
|
4894
5539
|
var UserManager = class {
|
|
@@ -4928,19 +5573,19 @@ var UserManager = class {
|
|
|
4928
5573
|
async loadUsersFromFile() {
|
|
4929
5574
|
try {
|
|
4930
5575
|
const dir = path4.dirname(this.filePath);
|
|
4931
|
-
if (!
|
|
5576
|
+
if (!fs5.existsSync(dir)) {
|
|
4932
5577
|
logger.info(`Creating directory structure: ${dir}`);
|
|
4933
|
-
|
|
5578
|
+
fs5.mkdirSync(dir, { recursive: true });
|
|
4934
5579
|
}
|
|
4935
|
-
if (!
|
|
5580
|
+
if (!fs5.existsSync(this.filePath)) {
|
|
4936
5581
|
logger.info(`Users file does not exist at ${this.filePath}, creating with empty users`);
|
|
4937
5582
|
const initialData = { users: [] };
|
|
4938
|
-
|
|
5583
|
+
fs5.writeFileSync(this.filePath, JSON.stringify(initialData, null, 4));
|
|
4939
5584
|
this.users = [];
|
|
4940
5585
|
this.hasChanged = false;
|
|
4941
5586
|
return;
|
|
4942
5587
|
}
|
|
4943
|
-
const fileContent =
|
|
5588
|
+
const fileContent = fs5.readFileSync(this.filePath, "utf-8");
|
|
4944
5589
|
const rawData = JSON.parse(fileContent);
|
|
4945
5590
|
const validatedData = UsersDataSchema.parse(rawData);
|
|
4946
5591
|
this.users = validatedData.users;
|
|
@@ -4960,15 +5605,15 @@ var UserManager = class {
|
|
|
4960
5605
|
}
|
|
4961
5606
|
try {
|
|
4962
5607
|
const dir = path4.dirname(this.filePath);
|
|
4963
|
-
if (!
|
|
4964
|
-
|
|
5608
|
+
if (!fs5.existsSync(dir)) {
|
|
5609
|
+
fs5.mkdirSync(dir, { recursive: true });
|
|
4965
5610
|
}
|
|
4966
5611
|
const usersToSave = this.users.map((user) => {
|
|
4967
5612
|
const { wsIds, ...userWithoutWsIds } = user;
|
|
4968
5613
|
return userWithoutWsIds;
|
|
4969
5614
|
});
|
|
4970
5615
|
const data = { users: usersToSave };
|
|
4971
|
-
|
|
5616
|
+
fs5.writeFileSync(this.filePath, JSON.stringify(data, null, 4));
|
|
4972
5617
|
this.hasChanged = false;
|
|
4973
5618
|
logger.debug(`Synced ${this.users.length} users to file (wsIds excluded)`);
|
|
4974
5619
|
} catch (error) {
|
|
@@ -5186,7 +5831,7 @@ var UserManager = class {
|
|
|
5186
5831
|
};
|
|
5187
5832
|
|
|
5188
5833
|
// src/dashboards/dashboard-manager.ts
|
|
5189
|
-
import
|
|
5834
|
+
import fs6 from "fs";
|
|
5190
5835
|
import path5 from "path";
|
|
5191
5836
|
import os2 from "os";
|
|
5192
5837
|
var DashboardManager = class {
|
|
@@ -5221,12 +5866,12 @@ var DashboardManager = class {
|
|
|
5221
5866
|
createDashboard(dashboardId, dashboard) {
|
|
5222
5867
|
const dashboardPath = this.getDashboardPath(dashboardId);
|
|
5223
5868
|
const dashboardDir = path5.dirname(dashboardPath);
|
|
5224
|
-
if (
|
|
5869
|
+
if (fs6.existsSync(dashboardPath)) {
|
|
5225
5870
|
throw new Error(`Dashboard '${dashboardId}' already exists`);
|
|
5226
5871
|
}
|
|
5227
5872
|
const validated = DSLRendererPropsSchema.parse(dashboard);
|
|
5228
|
-
|
|
5229
|
-
|
|
5873
|
+
fs6.mkdirSync(dashboardDir, { recursive: true });
|
|
5874
|
+
fs6.writeFileSync(dashboardPath, JSON.stringify(validated, null, 4));
|
|
5230
5875
|
logger.info(`Dashboard created: ${dashboardId}`);
|
|
5231
5876
|
return validated;
|
|
5232
5877
|
}
|
|
@@ -5237,12 +5882,12 @@ var DashboardManager = class {
|
|
|
5237
5882
|
*/
|
|
5238
5883
|
getDashboard(dashboardId) {
|
|
5239
5884
|
const dashboardPath = this.getDashboardPath(dashboardId);
|
|
5240
|
-
if (!
|
|
5885
|
+
if (!fs6.existsSync(dashboardPath)) {
|
|
5241
5886
|
logger.warn(`Dashboard not found: ${dashboardId}`);
|
|
5242
5887
|
return null;
|
|
5243
5888
|
}
|
|
5244
5889
|
try {
|
|
5245
|
-
const fileContent =
|
|
5890
|
+
const fileContent = fs6.readFileSync(dashboardPath, "utf-8");
|
|
5246
5891
|
const dashboard = JSON.parse(fileContent);
|
|
5247
5892
|
const validated = DSLRendererPropsSchema.parse(dashboard);
|
|
5248
5893
|
return validated;
|
|
@@ -5256,16 +5901,16 @@ var DashboardManager = class {
|
|
|
5256
5901
|
* @returns Array of dashboard objects with their IDs
|
|
5257
5902
|
*/
|
|
5258
5903
|
getAllDashboards() {
|
|
5259
|
-
if (!
|
|
5260
|
-
|
|
5904
|
+
if (!fs6.existsSync(this.dashboardsBasePath)) {
|
|
5905
|
+
fs6.mkdirSync(this.dashboardsBasePath, { recursive: true });
|
|
5261
5906
|
return [];
|
|
5262
5907
|
}
|
|
5263
5908
|
const dashboards = [];
|
|
5264
5909
|
try {
|
|
5265
|
-
const dashboardDirs =
|
|
5910
|
+
const dashboardDirs = fs6.readdirSync(this.dashboardsBasePath);
|
|
5266
5911
|
for (const dashboardId of dashboardDirs) {
|
|
5267
5912
|
const dashboardPath = this.getDashboardPath(dashboardId);
|
|
5268
|
-
if (
|
|
5913
|
+
if (fs6.existsSync(dashboardPath)) {
|
|
5269
5914
|
const dashboard = this.getDashboard(dashboardId);
|
|
5270
5915
|
if (dashboard) {
|
|
5271
5916
|
dashboards.push({ dashboardId, dashboard });
|
|
@@ -5287,13 +5932,13 @@ var DashboardManager = class {
|
|
|
5287
5932
|
*/
|
|
5288
5933
|
updateDashboard(dashboardId, dashboard) {
|
|
5289
5934
|
const dashboardPath = this.getDashboardPath(dashboardId);
|
|
5290
|
-
if (!
|
|
5935
|
+
if (!fs6.existsSync(dashboardPath)) {
|
|
5291
5936
|
logger.warn(`Dashboard not found for update: ${dashboardId}`);
|
|
5292
5937
|
return null;
|
|
5293
5938
|
}
|
|
5294
5939
|
try {
|
|
5295
5940
|
const validated = DSLRendererPropsSchema.parse(dashboard);
|
|
5296
|
-
|
|
5941
|
+
fs6.writeFileSync(dashboardPath, JSON.stringify(validated, null, 4));
|
|
5297
5942
|
logger.info(`Dashboard updated: ${dashboardId}`);
|
|
5298
5943
|
return validated;
|
|
5299
5944
|
} catch (error) {
|
|
@@ -5309,12 +5954,12 @@ var DashboardManager = class {
|
|
|
5309
5954
|
deleteDashboard(dashboardId) {
|
|
5310
5955
|
const dashboardPath = this.getDashboardPath(dashboardId);
|
|
5311
5956
|
const dashboardDir = path5.dirname(dashboardPath);
|
|
5312
|
-
if (!
|
|
5957
|
+
if (!fs6.existsSync(dashboardPath)) {
|
|
5313
5958
|
logger.warn(`Dashboard not found for deletion: ${dashboardId}`);
|
|
5314
5959
|
return false;
|
|
5315
5960
|
}
|
|
5316
5961
|
try {
|
|
5317
|
-
|
|
5962
|
+
fs6.rmSync(dashboardDir, { recursive: true, force: true });
|
|
5318
5963
|
logger.info(`Dashboard deleted: ${dashboardId}`);
|
|
5319
5964
|
return true;
|
|
5320
5965
|
} catch (error) {
|
|
@@ -5329,21 +5974,21 @@ var DashboardManager = class {
|
|
|
5329
5974
|
*/
|
|
5330
5975
|
dashboardExists(dashboardId) {
|
|
5331
5976
|
const dashboardPath = this.getDashboardPath(dashboardId);
|
|
5332
|
-
return
|
|
5977
|
+
return fs6.existsSync(dashboardPath);
|
|
5333
5978
|
}
|
|
5334
5979
|
/**
|
|
5335
5980
|
* Get dashboard count
|
|
5336
5981
|
* @returns Number of dashboards
|
|
5337
5982
|
*/
|
|
5338
5983
|
getDashboardCount() {
|
|
5339
|
-
if (!
|
|
5984
|
+
if (!fs6.existsSync(this.dashboardsBasePath)) {
|
|
5340
5985
|
return 0;
|
|
5341
5986
|
}
|
|
5342
5987
|
try {
|
|
5343
|
-
const dashboardDirs =
|
|
5988
|
+
const dashboardDirs = fs6.readdirSync(this.dashboardsBasePath);
|
|
5344
5989
|
return dashboardDirs.filter((dir) => {
|
|
5345
5990
|
const dashboardPath = this.getDashboardPath(dir);
|
|
5346
|
-
return
|
|
5991
|
+
return fs6.existsSync(dashboardPath);
|
|
5347
5992
|
}).length;
|
|
5348
5993
|
} catch (error) {
|
|
5349
5994
|
logger.error("Failed to get dashboard count:", error);
|
|
@@ -5353,7 +5998,7 @@ var DashboardManager = class {
|
|
|
5353
5998
|
};
|
|
5354
5999
|
|
|
5355
6000
|
// src/reports/report-manager.ts
|
|
5356
|
-
import
|
|
6001
|
+
import fs7 from "fs";
|
|
5357
6002
|
import path6 from "path";
|
|
5358
6003
|
import os3 from "os";
|
|
5359
6004
|
var ReportManager = class {
|
|
@@ -5388,12 +6033,12 @@ var ReportManager = class {
|
|
|
5388
6033
|
createReport(reportId, report) {
|
|
5389
6034
|
const reportPath = this.getReportPath(reportId);
|
|
5390
6035
|
const reportDir = path6.dirname(reportPath);
|
|
5391
|
-
if (
|
|
6036
|
+
if (fs7.existsSync(reportPath)) {
|
|
5392
6037
|
throw new Error(`Report '${reportId}' already exists`);
|
|
5393
6038
|
}
|
|
5394
6039
|
const validated = DSLRendererPropsSchema2.parse(report);
|
|
5395
|
-
|
|
5396
|
-
|
|
6040
|
+
fs7.mkdirSync(reportDir, { recursive: true });
|
|
6041
|
+
fs7.writeFileSync(reportPath, JSON.stringify(validated, null, 4));
|
|
5397
6042
|
logger.info(`Report created: ${reportId}`);
|
|
5398
6043
|
return validated;
|
|
5399
6044
|
}
|
|
@@ -5404,12 +6049,12 @@ var ReportManager = class {
|
|
|
5404
6049
|
*/
|
|
5405
6050
|
getReport(reportId) {
|
|
5406
6051
|
const reportPath = this.getReportPath(reportId);
|
|
5407
|
-
if (!
|
|
6052
|
+
if (!fs7.existsSync(reportPath)) {
|
|
5408
6053
|
logger.warn(`Report not found: ${reportId}`);
|
|
5409
6054
|
return null;
|
|
5410
6055
|
}
|
|
5411
6056
|
try {
|
|
5412
|
-
const fileContent =
|
|
6057
|
+
const fileContent = fs7.readFileSync(reportPath, "utf-8");
|
|
5413
6058
|
const report = JSON.parse(fileContent);
|
|
5414
6059
|
const validated = DSLRendererPropsSchema2.parse(report);
|
|
5415
6060
|
return validated;
|
|
@@ -5423,16 +6068,16 @@ var ReportManager = class {
|
|
|
5423
6068
|
* @returns Array of report objects with their IDs
|
|
5424
6069
|
*/
|
|
5425
6070
|
getAllReports() {
|
|
5426
|
-
if (!
|
|
5427
|
-
|
|
6071
|
+
if (!fs7.existsSync(this.reportsBasePath)) {
|
|
6072
|
+
fs7.mkdirSync(this.reportsBasePath, { recursive: true });
|
|
5428
6073
|
return [];
|
|
5429
6074
|
}
|
|
5430
6075
|
const reports = [];
|
|
5431
6076
|
try {
|
|
5432
|
-
const reportDirs =
|
|
6077
|
+
const reportDirs = fs7.readdirSync(this.reportsBasePath);
|
|
5433
6078
|
for (const reportId of reportDirs) {
|
|
5434
6079
|
const reportPath = this.getReportPath(reportId);
|
|
5435
|
-
if (
|
|
6080
|
+
if (fs7.existsSync(reportPath)) {
|
|
5436
6081
|
const report = this.getReport(reportId);
|
|
5437
6082
|
if (report) {
|
|
5438
6083
|
reports.push({ reportId, report });
|
|
@@ -5454,13 +6099,13 @@ var ReportManager = class {
|
|
|
5454
6099
|
*/
|
|
5455
6100
|
updateReport(reportId, report) {
|
|
5456
6101
|
const reportPath = this.getReportPath(reportId);
|
|
5457
|
-
if (!
|
|
6102
|
+
if (!fs7.existsSync(reportPath)) {
|
|
5458
6103
|
logger.warn(`Report not found for update: ${reportId}`);
|
|
5459
6104
|
return null;
|
|
5460
6105
|
}
|
|
5461
6106
|
try {
|
|
5462
6107
|
const validated = DSLRendererPropsSchema2.parse(report);
|
|
5463
|
-
|
|
6108
|
+
fs7.writeFileSync(reportPath, JSON.stringify(validated, null, 4));
|
|
5464
6109
|
logger.info(`Report updated: ${reportId}`);
|
|
5465
6110
|
return validated;
|
|
5466
6111
|
} catch (error) {
|
|
@@ -5476,12 +6121,12 @@ var ReportManager = class {
|
|
|
5476
6121
|
deleteReport(reportId) {
|
|
5477
6122
|
const reportPath = this.getReportPath(reportId);
|
|
5478
6123
|
const reportDir = path6.dirname(reportPath);
|
|
5479
|
-
if (!
|
|
6124
|
+
if (!fs7.existsSync(reportPath)) {
|
|
5480
6125
|
logger.warn(`Report not found for deletion: ${reportId}`);
|
|
5481
6126
|
return false;
|
|
5482
6127
|
}
|
|
5483
6128
|
try {
|
|
5484
|
-
|
|
6129
|
+
fs7.rmSync(reportDir, { recursive: true, force: true });
|
|
5485
6130
|
logger.info(`Report deleted: ${reportId}`);
|
|
5486
6131
|
return true;
|
|
5487
6132
|
} catch (error) {
|
|
@@ -5496,21 +6141,21 @@ var ReportManager = class {
|
|
|
5496
6141
|
*/
|
|
5497
6142
|
reportExists(reportId) {
|
|
5498
6143
|
const reportPath = this.getReportPath(reportId);
|
|
5499
|
-
return
|
|
6144
|
+
return fs7.existsSync(reportPath);
|
|
5500
6145
|
}
|
|
5501
6146
|
/**
|
|
5502
6147
|
* Get report count
|
|
5503
6148
|
* @returns Number of reports
|
|
5504
6149
|
*/
|
|
5505
6150
|
getReportCount() {
|
|
5506
|
-
if (!
|
|
6151
|
+
if (!fs7.existsSync(this.reportsBasePath)) {
|
|
5507
6152
|
return 0;
|
|
5508
6153
|
}
|
|
5509
6154
|
try {
|
|
5510
|
-
const reportDirs =
|
|
6155
|
+
const reportDirs = fs7.readdirSync(this.reportsBasePath);
|
|
5511
6156
|
return reportDirs.filter((dir) => {
|
|
5512
6157
|
const reportPath = this.getReportPath(dir);
|
|
5513
|
-
return
|
|
6158
|
+
return fs7.existsSync(reportPath);
|
|
5514
6159
|
}).length;
|
|
5515
6160
|
} catch (error) {
|
|
5516
6161
|
logger.error("Failed to get report count:", error);
|
|
@@ -5734,9 +6379,6 @@ var SuperatomSDK = class {
|
|
|
5734
6379
|
});
|
|
5735
6380
|
this.initializeDashboardManager();
|
|
5736
6381
|
this.initializeReportManager();
|
|
5737
|
-
this.connect().catch((error) => {
|
|
5738
|
-
logger.error("Failed to connect to Superatom:", error);
|
|
5739
|
-
});
|
|
5740
6382
|
}
|
|
5741
6383
|
/**
|
|
5742
6384
|
* Initialize PromptLoader and load prompts into memory
|
|
@@ -5802,6 +6444,10 @@ var SuperatomSDK = class {
|
|
|
5802
6444
|
* Connect to the Superatom WebSocket service
|
|
5803
6445
|
*/
|
|
5804
6446
|
async connect() {
|
|
6447
|
+
if (this.connected && this.ws && this.ws.readyState === this.ws.OPEN) {
|
|
6448
|
+
logger.info("Already connected to WebSocket");
|
|
6449
|
+
return Promise.resolve();
|
|
6450
|
+
}
|
|
5805
6451
|
return new Promise((resolve, reject) => {
|
|
5806
6452
|
try {
|
|
5807
6453
|
const url = new URL(this.url);
|
|
@@ -5864,7 +6510,7 @@ var SuperatomSDK = class {
|
|
|
5864
6510
|
});
|
|
5865
6511
|
break;
|
|
5866
6512
|
case "USER_PROMPT_REQ":
|
|
5867
|
-
handleUserPromptRequest(parsed, this.components, (msg) => this.send(msg), this.anthropicApiKey, this.groqApiKey, this.llmProviders).catch((error) => {
|
|
6513
|
+
handleUserPromptRequest(parsed, this.components, (msg) => this.send(msg), this.anthropicApiKey, this.groqApiKey, this.llmProviders, this.collections).catch((error) => {
|
|
5868
6514
|
logger.error("Failed to handle user prompt request:", error);
|
|
5869
6515
|
});
|
|
5870
6516
|
break;
|