@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.js
CHANGED
|
@@ -5,6 +5,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __esm = (fn, res) => function __init() {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
};
|
|
8
11
|
var __commonJS = (cb, mod) => function __require() {
|
|
9
12
|
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
10
13
|
};
|
|
@@ -102,7 +105,7 @@ var require_package = __commonJS({
|
|
|
102
105
|
var require_main = __commonJS({
|
|
103
106
|
"node_modules/dotenv/lib/main.js"(exports2, module2) {
|
|
104
107
|
"use strict";
|
|
105
|
-
var
|
|
108
|
+
var fs8 = require("fs");
|
|
106
109
|
var path7 = require("path");
|
|
107
110
|
var os4 = require("os");
|
|
108
111
|
var crypto2 = require("crypto");
|
|
@@ -244,7 +247,7 @@ var require_main = __commonJS({
|
|
|
244
247
|
if (options && options.path && options.path.length > 0) {
|
|
245
248
|
if (Array.isArray(options.path)) {
|
|
246
249
|
for (const filepath of options.path) {
|
|
247
|
-
if (
|
|
250
|
+
if (fs8.existsSync(filepath)) {
|
|
248
251
|
possibleVaultPath = filepath.endsWith(".vault") ? filepath : `${filepath}.vault`;
|
|
249
252
|
}
|
|
250
253
|
}
|
|
@@ -254,7 +257,7 @@ var require_main = __commonJS({
|
|
|
254
257
|
} else {
|
|
255
258
|
possibleVaultPath = path7.resolve(process.cwd(), ".env.vault");
|
|
256
259
|
}
|
|
257
|
-
if (
|
|
260
|
+
if (fs8.existsSync(possibleVaultPath)) {
|
|
258
261
|
return possibleVaultPath;
|
|
259
262
|
}
|
|
260
263
|
return null;
|
|
@@ -307,7 +310,7 @@ var require_main = __commonJS({
|
|
|
307
310
|
const parsedAll = {};
|
|
308
311
|
for (const path8 of optionPaths) {
|
|
309
312
|
try {
|
|
310
|
-
const parsed = DotenvModule.parse(
|
|
313
|
+
const parsed = DotenvModule.parse(fs8.readFileSync(path8, { encoding }));
|
|
311
314
|
DotenvModule.populate(parsedAll, parsed, options);
|
|
312
315
|
} catch (e) {
|
|
313
316
|
if (debug) {
|
|
@@ -428,6 +431,137 @@ var require_main = __commonJS({
|
|
|
428
431
|
}
|
|
429
432
|
});
|
|
430
433
|
|
|
434
|
+
// src/userResponse/utils.ts
|
|
435
|
+
var utils_exports = {};
|
|
436
|
+
__export(utils_exports, {
|
|
437
|
+
convertQuestionsToActions: () => convertQuestionsToActions,
|
|
438
|
+
convertTopToLimit: () => convertTopToLimit,
|
|
439
|
+
ensureQueryLimit: () => ensureQueryLimit,
|
|
440
|
+
fixScalarSubqueries: () => fixScalarSubqueries,
|
|
441
|
+
getJsonSizeInBytes: () => getJsonSizeInBytes,
|
|
442
|
+
validateMessageSize: () => validateMessageSize
|
|
443
|
+
});
|
|
444
|
+
function convertTopToLimit(query) {
|
|
445
|
+
if (!query || query.trim().length === 0) {
|
|
446
|
+
return query;
|
|
447
|
+
}
|
|
448
|
+
let modifiedQuery = query.replace(/\bSELECT\s+TOP\s+(\d+)\b/gi, "SELECT");
|
|
449
|
+
if (modifiedQuery !== query) {
|
|
450
|
+
console.warn(`\u26A0\uFE0F Query had TOP syntax. Converting to LIMIT for Snowflake compatibility.`);
|
|
451
|
+
}
|
|
452
|
+
return modifiedQuery;
|
|
453
|
+
}
|
|
454
|
+
function ensureQueryLimit(query, defaultLimit = 32, maxLimit = 32) {
|
|
455
|
+
if (!query || query.trim().length === 0) {
|
|
456
|
+
return query;
|
|
457
|
+
}
|
|
458
|
+
let trimmedQuery = query.trim();
|
|
459
|
+
const isSelectQuery = /^\s*SELECT\b/i.test(trimmedQuery) || /^\s*WITH\b.*\bSELECT\b/is.test(trimmedQuery);
|
|
460
|
+
if (!isSelectQuery) {
|
|
461
|
+
return query;
|
|
462
|
+
}
|
|
463
|
+
trimmedQuery = convertTopToLimit(trimmedQuery);
|
|
464
|
+
const hadSemicolon = trimmedQuery.endsWith(";");
|
|
465
|
+
if (hadSemicolon) {
|
|
466
|
+
trimmedQuery = trimmedQuery.slice(0, -1).trim();
|
|
467
|
+
}
|
|
468
|
+
const limitMatches = trimmedQuery.match(/\bLIMIT\s+(\d+)\b/gi);
|
|
469
|
+
if (limitMatches && limitMatches.length > 0) {
|
|
470
|
+
if (limitMatches.length > 1) {
|
|
471
|
+
console.warn(`\u26A0\uFE0F Query had ${limitMatches.length} LIMIT clauses. Removing duplicates...`);
|
|
472
|
+
trimmedQuery = trimmedQuery.replace(/\s*\bLIMIT\s+\d+\b/gi, "").trim();
|
|
473
|
+
} else {
|
|
474
|
+
const existingLimitMatch = trimmedQuery.match(/\bLIMIT\s+(\d+)\b/i);
|
|
475
|
+
if (existingLimitMatch) {
|
|
476
|
+
const existingLimit = parseInt(existingLimitMatch[1], 10);
|
|
477
|
+
if (existingLimit <= maxLimit) {
|
|
478
|
+
if (hadSemicolon) {
|
|
479
|
+
trimmedQuery += ";";
|
|
480
|
+
}
|
|
481
|
+
return trimmedQuery;
|
|
482
|
+
}
|
|
483
|
+
console.warn(`\u26A0\uFE0F Query LIMIT ${existingLimit} exceeds maximum of ${maxLimit}. Reducing to ${maxLimit}...`);
|
|
484
|
+
trimmedQuery = trimmedQuery.replace(/\bLIMIT\s+\d+\b/i, `LIMIT ${maxLimit}`);
|
|
485
|
+
if (hadSemicolon) {
|
|
486
|
+
trimmedQuery += ";";
|
|
487
|
+
}
|
|
488
|
+
return trimmedQuery;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
trimmedQuery = `${trimmedQuery} LIMIT ${defaultLimit}`;
|
|
493
|
+
if (hadSemicolon) {
|
|
494
|
+
trimmedQuery += ";";
|
|
495
|
+
}
|
|
496
|
+
return trimmedQuery;
|
|
497
|
+
}
|
|
498
|
+
function fixScalarSubqueries(query) {
|
|
499
|
+
if (!query || query.trim().length === 0) {
|
|
500
|
+
return query;
|
|
501
|
+
}
|
|
502
|
+
let modifiedQuery = query;
|
|
503
|
+
let hasChanges = false;
|
|
504
|
+
const scalarOperatorPattern = /([=<>!]=?|<>)\s*\(\s*SELECT\s/gi;
|
|
505
|
+
const matches = [...modifiedQuery.matchAll(scalarOperatorPattern)];
|
|
506
|
+
for (let i = matches.length - 1; i >= 0; i--) {
|
|
507
|
+
const match = matches[i];
|
|
508
|
+
const startPos = match.index + match[0].length - "SELECT ".length;
|
|
509
|
+
let parenDepth = 1;
|
|
510
|
+
let endPos = startPos;
|
|
511
|
+
let foundEnd = false;
|
|
512
|
+
for (let j = startPos; j < modifiedQuery.length; j++) {
|
|
513
|
+
const char = modifiedQuery[j];
|
|
514
|
+
if (char === "(") parenDepth++;
|
|
515
|
+
if (char === ")") {
|
|
516
|
+
parenDepth--;
|
|
517
|
+
if (parenDepth === 0) {
|
|
518
|
+
endPos = j;
|
|
519
|
+
foundEnd = true;
|
|
520
|
+
break;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
if (!foundEnd) continue;
|
|
525
|
+
const subquery = modifiedQuery.substring(startPos, endPos);
|
|
526
|
+
if (/\bLIMIT\s+\d+/i.test(subquery)) {
|
|
527
|
+
continue;
|
|
528
|
+
}
|
|
529
|
+
const fixedSubquery = subquery.trim() + " LIMIT 1";
|
|
530
|
+
modifiedQuery = modifiedQuery.substring(0, startPos) + fixedSubquery + modifiedQuery.substring(endPos);
|
|
531
|
+
hasChanges = true;
|
|
532
|
+
console.warn(`\u26A0\uFE0F Fixed scalar subquery: added LIMIT 1 to prevent multiple row error`);
|
|
533
|
+
}
|
|
534
|
+
if (hasChanges) {
|
|
535
|
+
console.log("\u2713 Query validated and fixed for PostgreSQL scalar subquery compatibility");
|
|
536
|
+
}
|
|
537
|
+
return modifiedQuery;
|
|
538
|
+
}
|
|
539
|
+
function convertQuestionsToActions(questions) {
|
|
540
|
+
return questions.map((question, index) => ({
|
|
541
|
+
id: `action_${index}_${Date.now()}`,
|
|
542
|
+
name: question,
|
|
543
|
+
type: "next_question",
|
|
544
|
+
question
|
|
545
|
+
}));
|
|
546
|
+
}
|
|
547
|
+
function getJsonSizeInBytes(obj) {
|
|
548
|
+
const jsonString = JSON.stringify(obj);
|
|
549
|
+
return Buffer.byteLength(jsonString, "utf8");
|
|
550
|
+
}
|
|
551
|
+
function validateMessageSize(message, maxSize = 1048576) {
|
|
552
|
+
const size = getJsonSizeInBytes(message);
|
|
553
|
+
return {
|
|
554
|
+
isValid: size <= maxSize,
|
|
555
|
+
size,
|
|
556
|
+
maxSize
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
var init_utils = __esm({
|
|
560
|
+
"src/userResponse/utils.ts"() {
|
|
561
|
+
"use strict";
|
|
562
|
+
}
|
|
563
|
+
});
|
|
564
|
+
|
|
431
565
|
// src/index.ts
|
|
432
566
|
var index_exports = {};
|
|
433
567
|
__export(index_exports, {
|
|
@@ -707,7 +841,8 @@ var UserPromptRequestPayloadSchema = import_zod3.z.object({
|
|
|
707
841
|
SA_RUNTIME: import_zod3.z.object({
|
|
708
842
|
threadId: import_zod3.z.string(),
|
|
709
843
|
uiBlockId: import_zod3.z.string()
|
|
710
|
-
}).optional()
|
|
844
|
+
}).optional(),
|
|
845
|
+
responseMode: import_zod3.z.enum(["component", "text"]).optional()
|
|
711
846
|
});
|
|
712
847
|
var UserPromptRequestMessageSchema = import_zod3.z.object({
|
|
713
848
|
id: import_zod3.z.string(),
|
|
@@ -729,7 +864,8 @@ var ComponentPropsSchema = import_zod3.z.object({
|
|
|
729
864
|
query: import_zod3.z.string().optional(),
|
|
730
865
|
title: import_zod3.z.string().optional(),
|
|
731
866
|
description: import_zod3.z.string().optional(),
|
|
732
|
-
config: import_zod3.z.record(import_zod3.z.unknown()).optional()
|
|
867
|
+
config: import_zod3.z.record(import_zod3.z.unknown()).optional(),
|
|
868
|
+
actions: import_zod3.z.array(import_zod3.z.any()).optional()
|
|
733
869
|
});
|
|
734
870
|
var ComponentSchema = import_zod3.z.object({
|
|
735
871
|
id: import_zod3.z.string(),
|
|
@@ -822,7 +958,9 @@ var ReportsRequestMessageSchema = import_zod3.z.object({
|
|
|
822
958
|
});
|
|
823
959
|
|
|
824
960
|
// src/utils/logger.ts
|
|
961
|
+
var import_fs = __toESM(require("fs"));
|
|
825
962
|
var PREFIX = "[SuperatomSDK]";
|
|
963
|
+
var LOGSTREAM = import_fs.default.createWriteStream("superatom-sdk.log", { flags: "a" });
|
|
826
964
|
var LOG_LEVEL_PRIORITY = {
|
|
827
965
|
errors: 0,
|
|
828
966
|
warnings: 1,
|
|
@@ -906,6 +1044,9 @@ var Logger = class {
|
|
|
906
1044
|
console.log(PREFIX, "[DEBUG]", ...args);
|
|
907
1045
|
}
|
|
908
1046
|
}
|
|
1047
|
+
file(...args) {
|
|
1048
|
+
LOGSTREAM.write(args.join(" ") + "\n");
|
|
1049
|
+
}
|
|
909
1050
|
};
|
|
910
1051
|
var logger = new Logger();
|
|
911
1052
|
|
|
@@ -977,6 +1118,9 @@ var UIBlock = class {
|
|
|
977
1118
|
getComponentMetadata() {
|
|
978
1119
|
return this.generatedComponentMetadata;
|
|
979
1120
|
}
|
|
1121
|
+
getTextResponse() {
|
|
1122
|
+
return this.textResponse || "";
|
|
1123
|
+
}
|
|
980
1124
|
/**
|
|
981
1125
|
* Set or update component metadata
|
|
982
1126
|
*/
|
|
@@ -1069,12 +1213,6 @@ var UIBlock = class {
|
|
|
1069
1213
|
const processedData = this.processDataForStorage(data);
|
|
1070
1214
|
this.componentData = { ...this.componentData, ...processedData };
|
|
1071
1215
|
}
|
|
1072
|
-
/**
|
|
1073
|
-
* Get text response
|
|
1074
|
-
*/
|
|
1075
|
-
getTextResponse() {
|
|
1076
|
-
return this.textResponse;
|
|
1077
|
-
}
|
|
1078
1216
|
/**
|
|
1079
1217
|
* Set or update text response
|
|
1080
1218
|
*/
|
|
@@ -1110,6 +1248,12 @@ var UIBlock = class {
|
|
|
1110
1248
|
throw error;
|
|
1111
1249
|
}
|
|
1112
1250
|
}
|
|
1251
|
+
/**
|
|
1252
|
+
* Set or replace all actions
|
|
1253
|
+
*/
|
|
1254
|
+
setActions(actions) {
|
|
1255
|
+
this.actions = actions;
|
|
1256
|
+
}
|
|
1113
1257
|
/**
|
|
1114
1258
|
* Add a single action (only if actions are resolved)
|
|
1115
1259
|
*/
|
|
@@ -1265,8 +1409,11 @@ var Thread = class {
|
|
|
1265
1409
|
const questionNum = index + 1;
|
|
1266
1410
|
const question = block.getUserQuestion();
|
|
1267
1411
|
const metadata = block.getComponentMetadata();
|
|
1268
|
-
|
|
1269
|
-
|
|
1412
|
+
const textResponse = block.getTextResponse();
|
|
1413
|
+
let assistantResponse = "";
|
|
1414
|
+
const hasComponent = metadata && Object.keys(metadata).length > 0 && metadata.type;
|
|
1415
|
+
const hasTextResponse = textResponse && textResponse.trim().length > 0;
|
|
1416
|
+
if (hasComponent) {
|
|
1270
1417
|
const parts = [];
|
|
1271
1418
|
if (metadata.type) {
|
|
1272
1419
|
parts.push(`Component Type: ${metadata.type}`);
|
|
@@ -1286,11 +1433,17 @@ var Thread = class {
|
|
|
1286
1433
|
const componentTypes = metadata.props.config.components.map((c) => c.type).join(", ");
|
|
1287
1434
|
parts.push(`Multi-component with: ${componentTypes}`);
|
|
1288
1435
|
}
|
|
1289
|
-
|
|
1436
|
+
assistantResponse = parts.join(", ");
|
|
1437
|
+
} else if (hasTextResponse) {
|
|
1438
|
+
assistantResponse = textResponse;
|
|
1439
|
+
} else {
|
|
1440
|
+
assistantResponse = "No response generated";
|
|
1290
1441
|
}
|
|
1291
|
-
contextLines.push(`
|
|
1292
|
-
|
|
1293
|
-
contextLines.push(
|
|
1442
|
+
contextLines.push(`User:
|
|
1443
|
+
${question}`);
|
|
1444
|
+
contextLines.push(`Assistant:
|
|
1445
|
+
${assistantResponse}`);
|
|
1446
|
+
contextLines.push("---");
|
|
1294
1447
|
});
|
|
1295
1448
|
return contextLines.join("\n").trim();
|
|
1296
1449
|
}
|
|
@@ -1466,7 +1619,7 @@ function sendDataResponse(id, collection, op, data, meta, sendMessage) {
|
|
|
1466
1619
|
}
|
|
1467
1620
|
|
|
1468
1621
|
// src/bundle.ts
|
|
1469
|
-
var
|
|
1622
|
+
var fs2 = __toESM(require("fs"));
|
|
1470
1623
|
var path = __toESM(require("path"));
|
|
1471
1624
|
function getBundleDir(configDir) {
|
|
1472
1625
|
const bundleDir = configDir || process.env.SA_BUNDLE_DIR;
|
|
@@ -1479,16 +1632,16 @@ function getBundleDir(configDir) {
|
|
|
1479
1632
|
}
|
|
1480
1633
|
function getJS(bundleDir) {
|
|
1481
1634
|
try {
|
|
1482
|
-
if (!
|
|
1635
|
+
if (!fs2.existsSync(bundleDir)) {
|
|
1483
1636
|
throw new Error(`Bundle directory does not exist: ${bundleDir}`);
|
|
1484
1637
|
}
|
|
1485
|
-
const stats =
|
|
1638
|
+
const stats = fs2.statSync(bundleDir);
|
|
1486
1639
|
if (!stats.isDirectory()) {
|
|
1487
1640
|
throw new Error(`Bundle path is not a directory: ${bundleDir}`);
|
|
1488
1641
|
}
|
|
1489
1642
|
let files;
|
|
1490
1643
|
try {
|
|
1491
|
-
files =
|
|
1644
|
+
files = fs2.readdirSync(bundleDir);
|
|
1492
1645
|
} catch (error) {
|
|
1493
1646
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1494
1647
|
throw new Error(`Failed to read bundle directory: ${errorMessage}`);
|
|
@@ -1503,7 +1656,7 @@ function getJS(bundleDir) {
|
|
|
1503
1656
|
const filePath = path.join(bundleDir, indexFile);
|
|
1504
1657
|
logger.info(`Loading bundle from ${filePath}`);
|
|
1505
1658
|
try {
|
|
1506
|
-
return
|
|
1659
|
+
return fs2.readFileSync(filePath, "utf8");
|
|
1507
1660
|
} catch (error) {
|
|
1508
1661
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1509
1662
|
throw new Error(`Failed to read bundle file: ${errorMessage}`);
|
|
@@ -1909,94 +2062,12 @@ function sendDataResponse3(id, res, sendMessage, clientId) {
|
|
|
1909
2062
|
// src/userResponse/groq.ts
|
|
1910
2063
|
var import_dotenv = __toESM(require_main());
|
|
1911
2064
|
|
|
1912
|
-
// src/userResponse/
|
|
1913
|
-
|
|
1914
|
-
if (!query || query.trim().length === 0) {
|
|
1915
|
-
return query;
|
|
1916
|
-
}
|
|
1917
|
-
let modifiedQuery = query.replace(/\bSELECT\s+TOP\s+(\d+)\b/gi, "SELECT");
|
|
1918
|
-
if (modifiedQuery !== query) {
|
|
1919
|
-
console.warn(`\u26A0\uFE0F Query had TOP syntax. Converting to LIMIT for Snowflake compatibility.`);
|
|
1920
|
-
}
|
|
1921
|
-
return modifiedQuery;
|
|
1922
|
-
}
|
|
1923
|
-
function ensureQueryLimit(query, defaultLimit = 50) {
|
|
1924
|
-
if (!query || query.trim().length === 0) {
|
|
1925
|
-
return query;
|
|
1926
|
-
}
|
|
1927
|
-
let trimmedQuery = query.trim();
|
|
1928
|
-
const isSelectQuery = /^\s*SELECT\b/i.test(trimmedQuery) || /^\s*WITH\b.*\bSELECT\b/is.test(trimmedQuery);
|
|
1929
|
-
if (!isSelectQuery) {
|
|
1930
|
-
return query;
|
|
1931
|
-
}
|
|
1932
|
-
trimmedQuery = convertTopToLimit(trimmedQuery);
|
|
1933
|
-
const hadSemicolon = trimmedQuery.endsWith(";");
|
|
1934
|
-
if (hadSemicolon) {
|
|
1935
|
-
trimmedQuery = trimmedQuery.slice(0, -1).trim();
|
|
1936
|
-
}
|
|
1937
|
-
const limitMatches = trimmedQuery.match(/\bLIMIT\s+\d+\b/gi);
|
|
1938
|
-
if (limitMatches && limitMatches.length > 0) {
|
|
1939
|
-
if (limitMatches.length > 1) {
|
|
1940
|
-
console.warn(`\u26A0\uFE0F Query had ${limitMatches.length} LIMIT clauses. Removing duplicates...`);
|
|
1941
|
-
trimmedQuery = trimmedQuery.replace(/\s*\bLIMIT\s+\d+\b/gi, "").trim();
|
|
1942
|
-
} else {
|
|
1943
|
-
if (hadSemicolon) {
|
|
1944
|
-
trimmedQuery += ";";
|
|
1945
|
-
}
|
|
1946
|
-
return trimmedQuery;
|
|
1947
|
-
}
|
|
1948
|
-
}
|
|
1949
|
-
trimmedQuery = `${trimmedQuery} LIMIT ${defaultLimit}`;
|
|
1950
|
-
if (hadSemicolon) {
|
|
1951
|
-
trimmedQuery += ";";
|
|
1952
|
-
}
|
|
1953
|
-
return trimmedQuery;
|
|
1954
|
-
}
|
|
1955
|
-
function fixScalarSubqueries(query) {
|
|
1956
|
-
if (!query || query.trim().length === 0) {
|
|
1957
|
-
return query;
|
|
1958
|
-
}
|
|
1959
|
-
let modifiedQuery = query;
|
|
1960
|
-
let hasChanges = false;
|
|
1961
|
-
const scalarOperatorPattern = /([=<>!]=?|<>)\s*\(\s*SELECT\s/gi;
|
|
1962
|
-
const matches = [...modifiedQuery.matchAll(scalarOperatorPattern)];
|
|
1963
|
-
for (let i = matches.length - 1; i >= 0; i--) {
|
|
1964
|
-
const match = matches[i];
|
|
1965
|
-
const startPos = match.index + match[0].length - "SELECT ".length;
|
|
1966
|
-
let parenDepth = 1;
|
|
1967
|
-
let endPos = startPos;
|
|
1968
|
-
let foundEnd = false;
|
|
1969
|
-
for (let j = startPos; j < modifiedQuery.length; j++) {
|
|
1970
|
-
const char = modifiedQuery[j];
|
|
1971
|
-
if (char === "(") parenDepth++;
|
|
1972
|
-
if (char === ")") {
|
|
1973
|
-
parenDepth--;
|
|
1974
|
-
if (parenDepth === 0) {
|
|
1975
|
-
endPos = j;
|
|
1976
|
-
foundEnd = true;
|
|
1977
|
-
break;
|
|
1978
|
-
}
|
|
1979
|
-
}
|
|
1980
|
-
}
|
|
1981
|
-
if (!foundEnd) continue;
|
|
1982
|
-
const subquery = modifiedQuery.substring(startPos, endPos);
|
|
1983
|
-
if (/\bLIMIT\s+\d+/i.test(subquery)) {
|
|
1984
|
-
continue;
|
|
1985
|
-
}
|
|
1986
|
-
const fixedSubquery = subquery.trim() + " LIMIT 1";
|
|
1987
|
-
modifiedQuery = modifiedQuery.substring(0, startPos) + fixedSubquery + modifiedQuery.substring(endPos);
|
|
1988
|
-
hasChanges = true;
|
|
1989
|
-
console.warn(`\u26A0\uFE0F Fixed scalar subquery: added LIMIT 1 to prevent multiple row error`);
|
|
1990
|
-
}
|
|
1991
|
-
if (hasChanges) {
|
|
1992
|
-
console.log("\u2713 Query validated and fixed for PostgreSQL scalar subquery compatibility");
|
|
1993
|
-
}
|
|
1994
|
-
return modifiedQuery;
|
|
1995
|
-
}
|
|
2065
|
+
// src/userResponse/base-llm.ts
|
|
2066
|
+
init_utils();
|
|
1996
2067
|
|
|
1997
2068
|
// src/userResponse/schema.ts
|
|
1998
2069
|
var import_path = __toESM(require("path"));
|
|
1999
|
-
var
|
|
2070
|
+
var import_fs2 = __toESM(require("fs"));
|
|
2000
2071
|
var Schema = class {
|
|
2001
2072
|
constructor(schemaFilePath) {
|
|
2002
2073
|
this.cachedSchema = null;
|
|
@@ -2010,11 +2081,11 @@ var Schema = class {
|
|
|
2010
2081
|
logger.info(`SCHEMA_FILE_PATH: ${this.schemaFilePath}`);
|
|
2011
2082
|
try {
|
|
2012
2083
|
const dir = import_path.default.dirname(this.schemaFilePath);
|
|
2013
|
-
if (!
|
|
2084
|
+
if (!import_fs2.default.existsSync(dir)) {
|
|
2014
2085
|
logger.info(`Creating directory structure: ${dir}`);
|
|
2015
|
-
|
|
2086
|
+
import_fs2.default.mkdirSync(dir, { recursive: true });
|
|
2016
2087
|
}
|
|
2017
|
-
if (!
|
|
2088
|
+
if (!import_fs2.default.existsSync(this.schemaFilePath)) {
|
|
2018
2089
|
logger.info(`Schema file does not exist at ${this.schemaFilePath}, creating with empty schema`);
|
|
2019
2090
|
const initialSchema = {
|
|
2020
2091
|
database: "",
|
|
@@ -2023,11 +2094,11 @@ var Schema = class {
|
|
|
2023
2094
|
tables: [],
|
|
2024
2095
|
relationships: []
|
|
2025
2096
|
};
|
|
2026
|
-
|
|
2097
|
+
import_fs2.default.writeFileSync(this.schemaFilePath, JSON.stringify(initialSchema, null, 4));
|
|
2027
2098
|
this.cachedSchema = initialSchema;
|
|
2028
2099
|
return initialSchema;
|
|
2029
2100
|
}
|
|
2030
|
-
const fileContent =
|
|
2101
|
+
const fileContent = import_fs2.default.readFileSync(this.schemaFilePath, "utf-8");
|
|
2031
2102
|
const schema2 = JSON.parse(fileContent);
|
|
2032
2103
|
this.cachedSchema = schema2;
|
|
2033
2104
|
return schema2;
|
|
@@ -2128,7 +2199,7 @@ var Schema = class {
|
|
|
2128
2199
|
var schema = new Schema();
|
|
2129
2200
|
|
|
2130
2201
|
// src/userResponse/prompt-loader.ts
|
|
2131
|
-
var
|
|
2202
|
+
var import_fs3 = __toESM(require("fs"));
|
|
2132
2203
|
var import_path2 = __toESM(require("path"));
|
|
2133
2204
|
var PromptLoader = class {
|
|
2134
2205
|
constructor(config) {
|
|
@@ -2156,7 +2227,8 @@ var PromptLoader = class {
|
|
|
2156
2227
|
"mutli-component",
|
|
2157
2228
|
"actions",
|
|
2158
2229
|
"container-metadata",
|
|
2159
|
-
"text-response"
|
|
2230
|
+
"text-response",
|
|
2231
|
+
"match-text-components"
|
|
2160
2232
|
];
|
|
2161
2233
|
for (const promptType of promptTypes) {
|
|
2162
2234
|
try {
|
|
@@ -2181,9 +2253,9 @@ var PromptLoader = class {
|
|
|
2181
2253
|
try {
|
|
2182
2254
|
const systemPath = import_path2.default.join(dir, promptName, "system.md");
|
|
2183
2255
|
const userPath = import_path2.default.join(dir, promptName, "user.md");
|
|
2184
|
-
if (
|
|
2185
|
-
const system =
|
|
2186
|
-
const user =
|
|
2256
|
+
if (import_fs3.default.existsSync(systemPath) && import_fs3.default.existsSync(userPath)) {
|
|
2257
|
+
const system = import_fs3.default.readFileSync(systemPath, "utf-8");
|
|
2258
|
+
const user = import_fs3.default.readFileSync(userPath, "utf-8");
|
|
2187
2259
|
logger.debug(`Loaded prompt '${promptName}' from ${dir}`);
|
|
2188
2260
|
return { system, user };
|
|
2189
2261
|
}
|
|
@@ -2305,6 +2377,14 @@ var LLM = class {
|
|
|
2305
2377
|
throw new Error(`Unsupported provider: ${provider}. Use "anthropic" or "groq"`);
|
|
2306
2378
|
}
|
|
2307
2379
|
}
|
|
2380
|
+
/* Stream response with tool calling support (Anthropic only for now) */
|
|
2381
|
+
static async streamWithTools(messages, tools, toolHandler, options = {}, maxIterations = 3) {
|
|
2382
|
+
const [provider, modelName] = this._parseModel(options.model);
|
|
2383
|
+
if (provider !== "anthropic") {
|
|
2384
|
+
throw new Error(`Tool calling is only supported for Anthropic models`);
|
|
2385
|
+
}
|
|
2386
|
+
return this._anthropicStreamWithTools(messages, tools, toolHandler, modelName, options, maxIterations);
|
|
2387
|
+
}
|
|
2308
2388
|
// ============================================================
|
|
2309
2389
|
// PRIVATE HELPER METHODS
|
|
2310
2390
|
// ============================================================
|
|
@@ -2382,6 +2462,86 @@ var LLM = class {
|
|
|
2382
2462
|
}
|
|
2383
2463
|
return fullText;
|
|
2384
2464
|
}
|
|
2465
|
+
static async _anthropicStreamWithTools(messages, tools, toolHandler, modelName, options, maxIterations) {
|
|
2466
|
+
const apiKey = options.apiKey || process.env.ANTHROPIC_API_KEY || "";
|
|
2467
|
+
const client = new import_sdk.default({
|
|
2468
|
+
apiKey
|
|
2469
|
+
});
|
|
2470
|
+
const conversationMessages = [{
|
|
2471
|
+
role: "user",
|
|
2472
|
+
content: messages.user
|
|
2473
|
+
}];
|
|
2474
|
+
let iterations = 0;
|
|
2475
|
+
let finalText = "";
|
|
2476
|
+
while (iterations < maxIterations) {
|
|
2477
|
+
iterations++;
|
|
2478
|
+
const response = await client.messages.create({
|
|
2479
|
+
model: modelName,
|
|
2480
|
+
max_tokens: options.maxTokens || 4e3,
|
|
2481
|
+
temperature: options.temperature,
|
|
2482
|
+
system: messages.sys,
|
|
2483
|
+
messages: conversationMessages,
|
|
2484
|
+
tools
|
|
2485
|
+
});
|
|
2486
|
+
if (response.stop_reason === "end_turn") {
|
|
2487
|
+
const textBlock = response.content.find((block) => block.type === "text");
|
|
2488
|
+
if (textBlock && textBlock.type === "text") {
|
|
2489
|
+
finalText = textBlock.text;
|
|
2490
|
+
if (options.partial) {
|
|
2491
|
+
options.partial(finalText);
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2494
|
+
break;
|
|
2495
|
+
}
|
|
2496
|
+
if (response.stop_reason === "tool_use") {
|
|
2497
|
+
const toolUses = response.content.filter((block) => block.type === "tool_use");
|
|
2498
|
+
if (toolUses.length === 0) {
|
|
2499
|
+
break;
|
|
2500
|
+
}
|
|
2501
|
+
conversationMessages.push({
|
|
2502
|
+
role: "assistant",
|
|
2503
|
+
content: response.content
|
|
2504
|
+
});
|
|
2505
|
+
const toolResults = {
|
|
2506
|
+
role: "user",
|
|
2507
|
+
content: []
|
|
2508
|
+
};
|
|
2509
|
+
for (const toolUse of toolUses) {
|
|
2510
|
+
if (toolUse.type === "tool_use") {
|
|
2511
|
+
try {
|
|
2512
|
+
const result = await toolHandler(toolUse.name, toolUse.input);
|
|
2513
|
+
toolResults.content.push({
|
|
2514
|
+
type: "tool_result",
|
|
2515
|
+
tool_use_id: toolUse.id,
|
|
2516
|
+
content: typeof result === "string" ? result : JSON.stringify(result)
|
|
2517
|
+
});
|
|
2518
|
+
} catch (error) {
|
|
2519
|
+
toolResults.content.push({
|
|
2520
|
+
type: "tool_result",
|
|
2521
|
+
tool_use_id: toolUse.id,
|
|
2522
|
+
content: error instanceof Error ? error.message : String(error),
|
|
2523
|
+
is_error: true
|
|
2524
|
+
});
|
|
2525
|
+
}
|
|
2526
|
+
}
|
|
2527
|
+
}
|
|
2528
|
+
conversationMessages.push(toolResults);
|
|
2529
|
+
} else {
|
|
2530
|
+
const textBlock = response.content.find((block) => block.type === "text");
|
|
2531
|
+
if (textBlock && textBlock.type === "text") {
|
|
2532
|
+
finalText = textBlock.text;
|
|
2533
|
+
if (options.partial) {
|
|
2534
|
+
options.partial(finalText);
|
|
2535
|
+
}
|
|
2536
|
+
}
|
|
2537
|
+
break;
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
if (iterations >= maxIterations) {
|
|
2541
|
+
throw new Error(`Max iterations (${maxIterations}) reached in tool calling loop`);
|
|
2542
|
+
}
|
|
2543
|
+
return finalText;
|
|
2544
|
+
}
|
|
2385
2545
|
// ============================================================
|
|
2386
2546
|
// GROQ IMPLEMENTATION
|
|
2387
2547
|
// ============================================================
|
|
@@ -2456,6 +2616,42 @@ var LLM = class {
|
|
|
2456
2616
|
}
|
|
2457
2617
|
};
|
|
2458
2618
|
|
|
2619
|
+
// src/userResponse/knowledge-base.ts
|
|
2620
|
+
var getKnowledgeBase = async ({
|
|
2621
|
+
prompt,
|
|
2622
|
+
collections,
|
|
2623
|
+
topK = 1
|
|
2624
|
+
}) => {
|
|
2625
|
+
try {
|
|
2626
|
+
if (!collections || !collections["knowledge-base"] || !collections["knowledge-base"]["query"]) {
|
|
2627
|
+
logger.info("[KnowledgeBase] knowledge-base.query collection not registered, skipping");
|
|
2628
|
+
return "";
|
|
2629
|
+
}
|
|
2630
|
+
logger.info(`[KnowledgeBase] Querying knowledge base for: "${prompt.substring(0, 50)}..."`);
|
|
2631
|
+
const result = await collections["knowledge-base"]["query"]({
|
|
2632
|
+
prompt,
|
|
2633
|
+
topK
|
|
2634
|
+
});
|
|
2635
|
+
if (!result || !result.content) {
|
|
2636
|
+
logger.error("[KnowledgeBase] No knowledge base results returned");
|
|
2637
|
+
return "";
|
|
2638
|
+
}
|
|
2639
|
+
logger.info(`[KnowledgeBase] Retrieved knowledge base context (${result.content.length} chars)`);
|
|
2640
|
+
if (result.metadata?.sources && result.metadata.sources.length > 0) {
|
|
2641
|
+
logger.debug(`[KnowledgeBase] Sources: ${result.metadata.sources.map((s) => s.title).join(", ")}`);
|
|
2642
|
+
}
|
|
2643
|
+
return result.content;
|
|
2644
|
+
} catch (error) {
|
|
2645
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
2646
|
+
logger.warn(`[KnowledgeBase] Error querying knowledge base: ${errorMsg}`);
|
|
2647
|
+
return "";
|
|
2648
|
+
}
|
|
2649
|
+
};
|
|
2650
|
+
var KB = {
|
|
2651
|
+
getKnowledgeBase
|
|
2652
|
+
};
|
|
2653
|
+
var knowledge_base_default = KB;
|
|
2654
|
+
|
|
2459
2655
|
// src/userResponse/base-llm.ts
|
|
2460
2656
|
var BaseLLM = class {
|
|
2461
2657
|
constructor(config) {
|
|
@@ -2994,62 +3190,485 @@ var BaseLLM = class {
|
|
|
2994
3190
|
throw error;
|
|
2995
3191
|
}
|
|
2996
3192
|
}
|
|
3193
|
+
/**
|
|
3194
|
+
* Match components from text response suggestions and generate follow-up questions
|
|
3195
|
+
* Takes a text response with component suggestions (c1:type format) and matches with available components
|
|
3196
|
+
* Also generates intelligent follow-up questions (actions) based on the analysis
|
|
3197
|
+
* @param textResponse - The text response containing component suggestions
|
|
3198
|
+
* @param components - List of available components
|
|
3199
|
+
* @param apiKey - Optional API key
|
|
3200
|
+
* @param logCollector - Optional log collector
|
|
3201
|
+
* @returns Object containing matched components, selected layout, reasoning, and follow-up actions
|
|
3202
|
+
*/
|
|
3203
|
+
async matchComponentsFromTextResponse(textResponse, components, apiKey, logCollector) {
|
|
3204
|
+
try {
|
|
3205
|
+
logger.debug(`[${this.getProviderName()}] Starting component matching from text response`);
|
|
3206
|
+
let availableComponentsText = "No components available";
|
|
3207
|
+
if (components && components.length > 0) {
|
|
3208
|
+
availableComponentsText = components.map((comp, idx) => {
|
|
3209
|
+
const keywords = comp.keywords ? comp.keywords.join(", ") : "";
|
|
3210
|
+
const propsPreview = comp.props ? JSON.stringify(comp.props, null, 2) : "No props";
|
|
3211
|
+
return `${idx + 1}. ID: ${comp.id}
|
|
3212
|
+
Name: ${comp.name}
|
|
3213
|
+
Type: ${comp.type}
|
|
3214
|
+
Description: ${comp.description || "No description"}
|
|
3215
|
+
Keywords: ${keywords}
|
|
3216
|
+
Props Structure: ${propsPreview}`;
|
|
3217
|
+
}).join("\n\n");
|
|
3218
|
+
}
|
|
3219
|
+
const schemaDoc = schema.generateSchemaDocumentation();
|
|
3220
|
+
const prompts = await promptLoader.loadPrompts("match-text-components", {
|
|
3221
|
+
TEXT_RESPONSE: textResponse,
|
|
3222
|
+
AVAILABLE_COMPONENTS: availableComponentsText,
|
|
3223
|
+
SCHEMA_DOC: schemaDoc
|
|
3224
|
+
});
|
|
3225
|
+
logger.debug(`[${this.getProviderName()}] Loaded match-text-components prompts`);
|
|
3226
|
+
logger.file("\n=============================\nmatch text components system prompt:", prompts.system);
|
|
3227
|
+
logCollector?.info("Matching components from text response...");
|
|
3228
|
+
const rawResponse = await LLM.stream(
|
|
3229
|
+
{
|
|
3230
|
+
sys: prompts.system,
|
|
3231
|
+
user: prompts.user
|
|
3232
|
+
},
|
|
3233
|
+
{
|
|
3234
|
+
model: this.model,
|
|
3235
|
+
maxTokens: 3e3,
|
|
3236
|
+
temperature: 0.2,
|
|
3237
|
+
apiKey: this.getApiKey(apiKey)
|
|
3238
|
+
},
|
|
3239
|
+
false
|
|
3240
|
+
// Don't parse as JSON yet, get raw response
|
|
3241
|
+
);
|
|
3242
|
+
logger.debug(`[${this.getProviderName()}] Raw component matching response length: ${rawResponse?.length || 0}`);
|
|
3243
|
+
let result;
|
|
3244
|
+
try {
|
|
3245
|
+
let cleanedResponse = rawResponse || "";
|
|
3246
|
+
cleanedResponse = cleanedResponse.replace(/```json\n?/g, "").replace(/```\n?/g, "").trim();
|
|
3247
|
+
const jsonMatch = cleanedResponse.match(/\{[\s\S]*\}/);
|
|
3248
|
+
if (jsonMatch) {
|
|
3249
|
+
cleanedResponse = jsonMatch[0];
|
|
3250
|
+
}
|
|
3251
|
+
result = JSON.parse(cleanedResponse);
|
|
3252
|
+
} catch (parseError) {
|
|
3253
|
+
logger.error(`[${this.getProviderName()}] Failed to parse component matching JSON response`);
|
|
3254
|
+
const errorMsg = parseError instanceof Error ? parseError.message : String(parseError);
|
|
3255
|
+
const posMatch = errorMsg.match(/position (\d+)/);
|
|
3256
|
+
const errorPos = posMatch ? parseInt(posMatch[1]) : -1;
|
|
3257
|
+
if (errorPos > 0 && rawResponse) {
|
|
3258
|
+
const start = Math.max(0, errorPos - 200);
|
|
3259
|
+
const end = Math.min(rawResponse.length, errorPos + 200);
|
|
3260
|
+
logger.debug(`[${this.getProviderName()}] Error context (position ${errorPos}):`);
|
|
3261
|
+
logger.debug(rawResponse.substring(start, end));
|
|
3262
|
+
logger.debug(" ".repeat(Math.min(200, errorPos - start)) + "^--- Error here");
|
|
3263
|
+
}
|
|
3264
|
+
logger.debug(`[${this.getProviderName()}] Raw response (first 2000 chars):`, rawResponse?.substring(0, 2e3));
|
|
3265
|
+
logger.debug(`[${this.getProviderName()}] Parse error:`, parseError);
|
|
3266
|
+
logCollector?.error(`Failed to parse component matching response: ${errorMsg}`);
|
|
3267
|
+
try {
|
|
3268
|
+
logger.info(`[${this.getProviderName()}] Attempting aggressive JSON cleanup...`);
|
|
3269
|
+
let aggressive = rawResponse || "";
|
|
3270
|
+
aggressive = aggressive.replace(/```json\n?/g, "").replace(/```\n?/g, "").trim();
|
|
3271
|
+
const match = aggressive.match(/\{[\s\S]*\}/);
|
|
3272
|
+
if (match) {
|
|
3273
|
+
aggressive = match[0];
|
|
3274
|
+
}
|
|
3275
|
+
aggressive = aggressive.replace(/,(\s*[}\]])/g, "$1");
|
|
3276
|
+
result = JSON.parse(aggressive);
|
|
3277
|
+
logger.info(`[${this.getProviderName()}] Aggressive cleanup succeeded!`);
|
|
3278
|
+
} catch (secondError) {
|
|
3279
|
+
logger.error(`[${this.getProviderName()}] Aggressive cleanup also failed`);
|
|
3280
|
+
return {
|
|
3281
|
+
components: [],
|
|
3282
|
+
selectedLayout: "MultiComponentContainer",
|
|
3283
|
+
selectedLayoutId: "",
|
|
3284
|
+
selectedLayoutComponent: null,
|
|
3285
|
+
layoutReasoning: "No layout selected",
|
|
3286
|
+
actions: []
|
|
3287
|
+
};
|
|
3288
|
+
}
|
|
3289
|
+
}
|
|
3290
|
+
const matchedComponents = result.matchedComponents || [];
|
|
3291
|
+
const selectedLayout = result.selectedLayout || "MultiComponentContainer";
|
|
3292
|
+
const selectedLayoutId = result.selectedLayoutId || "";
|
|
3293
|
+
const layoutReasoning = result.layoutReasoning || "No layout reasoning provided";
|
|
3294
|
+
const rawActions = result.actions || [];
|
|
3295
|
+
const actions = convertQuestionsToActions(rawActions);
|
|
3296
|
+
let selectedLayoutComponent = null;
|
|
3297
|
+
if (selectedLayoutId) {
|
|
3298
|
+
selectedLayoutComponent = components.find((c) => c.id === selectedLayoutId) || null;
|
|
3299
|
+
if (!selectedLayoutComponent) {
|
|
3300
|
+
logger.warn(`[${this.getProviderName()}] Layout component ${selectedLayoutId} not found in available components`);
|
|
3301
|
+
}
|
|
3302
|
+
}
|
|
3303
|
+
logger.info(`[${this.getProviderName()}] Matched ${matchedComponents.length} components from text response`);
|
|
3304
|
+
logger.info(`[${this.getProviderName()}] Selected layout: ${selectedLayout} (ID: ${selectedLayoutId})`);
|
|
3305
|
+
logger.info(`[${this.getProviderName()}] Layout reasoning: ${layoutReasoning}`);
|
|
3306
|
+
logger.info(`[${this.getProviderName()}] Generated ${actions.length} follow-up actions`);
|
|
3307
|
+
if (matchedComponents.length > 0) {
|
|
3308
|
+
logCollector?.info(`Matched ${matchedComponents.length} components for visualization using ${selectedLayout}`);
|
|
3309
|
+
logCollector?.info(`Layout reasoning: ${layoutReasoning}`);
|
|
3310
|
+
matchedComponents.forEach((comp, idx) => {
|
|
3311
|
+
logCollector?.info(` ${idx + 1}. ${comp.componentName} (${comp.componentType}): ${comp.reasoning}`);
|
|
3312
|
+
if (comp.props?.query) {
|
|
3313
|
+
logCollector?.logQuery(
|
|
3314
|
+
`Component ${idx + 1} query`,
|
|
3315
|
+
comp.props.query,
|
|
3316
|
+
{ componentName: comp.componentName, title: comp.props.title }
|
|
3317
|
+
);
|
|
3318
|
+
}
|
|
3319
|
+
});
|
|
3320
|
+
}
|
|
3321
|
+
if (actions.length > 0) {
|
|
3322
|
+
logCollector?.info(`Generated ${actions.length} follow-up questions`);
|
|
3323
|
+
actions.forEach((action, idx) => {
|
|
3324
|
+
logCollector?.info(` ${idx + 1}. ${action.name}`);
|
|
3325
|
+
});
|
|
3326
|
+
}
|
|
3327
|
+
const finalComponents = matchedComponents.map((mc) => {
|
|
3328
|
+
const originalComponent = components.find((c) => c.id === mc.componentId);
|
|
3329
|
+
if (!originalComponent) {
|
|
3330
|
+
logger.warn(`[${this.getProviderName()}] Component ${mc.componentId} not found in available components`);
|
|
3331
|
+
return null;
|
|
3332
|
+
}
|
|
3333
|
+
return {
|
|
3334
|
+
...originalComponent,
|
|
3335
|
+
props: {
|
|
3336
|
+
...originalComponent.props,
|
|
3337
|
+
...mc.props
|
|
3338
|
+
}
|
|
3339
|
+
};
|
|
3340
|
+
}).filter(Boolean);
|
|
3341
|
+
return {
|
|
3342
|
+
components: finalComponents,
|
|
3343
|
+
selectedLayout,
|
|
3344
|
+
selectedLayoutId,
|
|
3345
|
+
selectedLayoutComponent,
|
|
3346
|
+
layoutReasoning,
|
|
3347
|
+
actions
|
|
3348
|
+
};
|
|
3349
|
+
} catch (error) {
|
|
3350
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
3351
|
+
logger.error(`[${this.getProviderName()}] Error matching components from text response: ${errorMsg}`);
|
|
3352
|
+
logger.debug(`[${this.getProviderName()}] Component matching error details:`, error);
|
|
3353
|
+
logCollector?.error(`Error matching components: ${errorMsg}`);
|
|
3354
|
+
return {
|
|
3355
|
+
components: [],
|
|
3356
|
+
selectedLayout: "MultiComponentContainer",
|
|
3357
|
+
selectedLayoutId: "",
|
|
3358
|
+
selectedLayoutComponent: null,
|
|
3359
|
+
layoutReasoning: "Error occurred during component matching",
|
|
3360
|
+
actions: []
|
|
3361
|
+
};
|
|
3362
|
+
}
|
|
3363
|
+
}
|
|
2997
3364
|
/**
|
|
2998
3365
|
* Generate text-based response for user question
|
|
2999
3366
|
* This provides conversational text responses instead of component generation
|
|
3367
|
+
* Supports tool calling for query execution with automatic retry on errors (max 3 attempts)
|
|
3368
|
+
* After generating text response, if components are provided, matches suggested components
|
|
3369
|
+
* @param streamCallback - Optional callback function to receive text chunks as they stream
|
|
3370
|
+
* @param collections - Collection registry for executing database queries via database.execute
|
|
3371
|
+
* @param components - Optional list of available components for matching suggestions
|
|
3000
3372
|
*/
|
|
3001
|
-
async generateTextResponse(userPrompt, apiKey, logCollector, conversationHistory) {
|
|
3373
|
+
async generateTextResponse(userPrompt, apiKey, logCollector, conversationHistory, streamCallback, collections, components) {
|
|
3002
3374
|
const errors = [];
|
|
3003
3375
|
logger.debug(`[${this.getProviderName()}] Starting text response generation`);
|
|
3004
3376
|
logger.debug(`[${this.getProviderName()}] User prompt: "${userPrompt.substring(0, 50)}..."`);
|
|
3005
3377
|
try {
|
|
3378
|
+
const schemaDoc = schema.generateSchemaDocumentation();
|
|
3379
|
+
const knowledgeBaseContext = await knowledge_base_default.getKnowledgeBase({
|
|
3380
|
+
prompt: userPrompt,
|
|
3381
|
+
collections,
|
|
3382
|
+
topK: 1
|
|
3383
|
+
});
|
|
3384
|
+
logger.file("\n=============================\nknowledge base context:", knowledgeBaseContext);
|
|
3006
3385
|
const prompts = await promptLoader.loadPrompts("text-response", {
|
|
3007
3386
|
USER_PROMPT: userPrompt,
|
|
3008
|
-
CONVERSATION_HISTORY: conversationHistory || "No previous conversation"
|
|
3387
|
+
CONVERSATION_HISTORY: conversationHistory || "No previous conversation",
|
|
3388
|
+
SCHEMA_DOC: schemaDoc,
|
|
3389
|
+
KNOWLEDGE_BASE_CONTEXT: knowledgeBaseContext || "No additional knowledge base context available."
|
|
3009
3390
|
});
|
|
3010
|
-
logger.
|
|
3391
|
+
logger.file("\n=============================\nsystem prompt:", prompts.system);
|
|
3392
|
+
logger.file("\n=============================\nuser prompt:", prompts.user);
|
|
3393
|
+
logger.debug(`[${this.getProviderName()}] Loaded text-response prompts with schema`);
|
|
3011
3394
|
logger.debug(`[${this.getProviderName()}] System prompt length: ${prompts.system.length}, User prompt length: ${prompts.user.length}`);
|
|
3012
|
-
logCollector?.info("Generating text response...");
|
|
3013
|
-
const
|
|
3395
|
+
logCollector?.info("Generating text response with query execution capability...");
|
|
3396
|
+
const tools = [{
|
|
3397
|
+
name: "execute_query",
|
|
3398
|
+
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.",
|
|
3399
|
+
input_schema: {
|
|
3400
|
+
type: "object",
|
|
3401
|
+
properties: {
|
|
3402
|
+
query: {
|
|
3403
|
+
type: "string",
|
|
3404
|
+
description: "The SQL query to execute. Must be valid SQL syntax using table and column names from the schema."
|
|
3405
|
+
},
|
|
3406
|
+
reasoning: {
|
|
3407
|
+
type: "string",
|
|
3408
|
+
description: "Brief explanation of what this query does and why it answers the user's question."
|
|
3409
|
+
}
|
|
3410
|
+
},
|
|
3411
|
+
required: ["query"]
|
|
3412
|
+
}
|
|
3413
|
+
}];
|
|
3414
|
+
const queryAttempts = /* @__PURE__ */ new Map();
|
|
3415
|
+
const MAX_QUERY_ATTEMPTS = 6;
|
|
3416
|
+
let maxAttemptsReached = false;
|
|
3417
|
+
let fullStreamedText = "";
|
|
3418
|
+
const wrappedStreamCallback = streamCallback ? (chunk) => {
|
|
3419
|
+
fullStreamedText += chunk;
|
|
3420
|
+
streamCallback(chunk);
|
|
3421
|
+
} : void 0;
|
|
3422
|
+
const toolHandler = async (toolName, toolInput) => {
|
|
3423
|
+
if (toolName === "execute_query") {
|
|
3424
|
+
let query = toolInput.query;
|
|
3425
|
+
const reasoning = toolInput.reasoning;
|
|
3426
|
+
const { ensureQueryLimit: ensureQueryLimit2 } = await Promise.resolve().then(() => (init_utils(), utils_exports));
|
|
3427
|
+
query = ensureQueryLimit2(query, 32, 32);
|
|
3428
|
+
const queryKey = query.toLowerCase().replace(/\s+/g, " ").trim();
|
|
3429
|
+
const attempts = (queryAttempts.get(queryKey) || 0) + 1;
|
|
3430
|
+
queryAttempts.set(queryKey, attempts);
|
|
3431
|
+
logger.info(`[${this.getProviderName()}] Executing query (attempt ${attempts}/${MAX_QUERY_ATTEMPTS}): ${query.substring(0, 100)}...`);
|
|
3432
|
+
if (reasoning) {
|
|
3433
|
+
logCollector?.info(`Query reasoning: ${reasoning}`);
|
|
3434
|
+
}
|
|
3435
|
+
if (attempts > MAX_QUERY_ATTEMPTS) {
|
|
3436
|
+
const errorMsg = `Maximum query attempts (${MAX_QUERY_ATTEMPTS}) reached. Unable to generate a valid query for your question.`;
|
|
3437
|
+
logger.error(`[${this.getProviderName()}] ${errorMsg}`);
|
|
3438
|
+
logCollector?.error(errorMsg);
|
|
3439
|
+
maxAttemptsReached = true;
|
|
3440
|
+
if (wrappedStreamCallback) {
|
|
3441
|
+
wrappedStreamCallback(`
|
|
3442
|
+
|
|
3443
|
+
\u274C ${errorMsg}
|
|
3444
|
+
|
|
3445
|
+
Please try rephrasing your question or simplifying your request.
|
|
3446
|
+
|
|
3447
|
+
`);
|
|
3448
|
+
}
|
|
3449
|
+
throw new Error(errorMsg);
|
|
3450
|
+
}
|
|
3451
|
+
try {
|
|
3452
|
+
if (wrappedStreamCallback) {
|
|
3453
|
+
if (attempts === 1) {
|
|
3454
|
+
wrappedStreamCallback(`
|
|
3455
|
+
|
|
3456
|
+
\u{1F50D} **Analyzing your question...**
|
|
3457
|
+
|
|
3458
|
+
`);
|
|
3459
|
+
if (reasoning) {
|
|
3460
|
+
wrappedStreamCallback(`\u{1F4AD} ${reasoning}
|
|
3461
|
+
|
|
3462
|
+
`);
|
|
3463
|
+
}
|
|
3464
|
+
wrappedStreamCallback(`\u{1F4DD} **Generated SQL Query:**
|
|
3465
|
+
\`\`\`sql
|
|
3466
|
+
${query}
|
|
3467
|
+
\`\`\`
|
|
3468
|
+
|
|
3469
|
+
`);
|
|
3470
|
+
wrappedStreamCallback(`\u26A1 **Executing query...**
|
|
3471
|
+
|
|
3472
|
+
`);
|
|
3473
|
+
} else {
|
|
3474
|
+
wrappedStreamCallback(`
|
|
3475
|
+
|
|
3476
|
+
\u{1F504} **Retrying with corrected query (attempt ${attempts}/${MAX_QUERY_ATTEMPTS})...**
|
|
3477
|
+
|
|
3478
|
+
`);
|
|
3479
|
+
if (reasoning) {
|
|
3480
|
+
wrappedStreamCallback(`\u{1F4AD} ${reasoning}
|
|
3481
|
+
|
|
3482
|
+
`);
|
|
3483
|
+
}
|
|
3484
|
+
wrappedStreamCallback(`\u{1F4DD} **Corrected SQL Query:**
|
|
3485
|
+
\`\`\`sql
|
|
3486
|
+
${query}
|
|
3487
|
+
\`\`\`
|
|
3488
|
+
|
|
3489
|
+
`);
|
|
3490
|
+
wrappedStreamCallback(`\u26A1 **Executing query...**
|
|
3491
|
+
|
|
3492
|
+
`);
|
|
3493
|
+
}
|
|
3494
|
+
}
|
|
3495
|
+
logCollector?.logQuery(
|
|
3496
|
+
`Executing SQL query (attempt ${attempts})`,
|
|
3497
|
+
query,
|
|
3498
|
+
{ reasoning, attempt: attempts }
|
|
3499
|
+
);
|
|
3500
|
+
if (!collections || !collections["database"] || !collections["database"]["execute"]) {
|
|
3501
|
+
throw new Error("Database collection not registered. Please register database.execute collection to execute queries.");
|
|
3502
|
+
}
|
|
3503
|
+
const result2 = await collections["database"]["execute"]({ sql: query });
|
|
3504
|
+
const data = result2?.data || result2;
|
|
3505
|
+
const rowCount = result2?.count ?? (Array.isArray(data) ? data.length : "N/A");
|
|
3506
|
+
logger.info(`[${this.getProviderName()}] Query executed successfully, rows returned: ${rowCount}`);
|
|
3507
|
+
logCollector?.info(`Query successful, returned ${rowCount} rows`);
|
|
3508
|
+
if (wrappedStreamCallback) {
|
|
3509
|
+
wrappedStreamCallback(`\u2705 **Query executed successfully!**
|
|
3510
|
+
|
|
3511
|
+
`);
|
|
3512
|
+
if (Array.isArray(data) && data.length > 0) {
|
|
3513
|
+
const firstRow = data[0];
|
|
3514
|
+
const columns = Object.keys(firstRow);
|
|
3515
|
+
if (data.length === 1 && columns.length === 1) {
|
|
3516
|
+
const value = firstRow[columns[0]];
|
|
3517
|
+
wrappedStreamCallback(`**Result:** ${value}
|
|
3518
|
+
|
|
3519
|
+
`);
|
|
3520
|
+
} else if (data.length > 0) {
|
|
3521
|
+
wrappedStreamCallback(`**Retrieved ${rowCount} rows**
|
|
3522
|
+
|
|
3523
|
+
`);
|
|
3524
|
+
wrappedStreamCallback(`<DataTable>${JSON.stringify(data)}</DataTable>
|
|
3525
|
+
|
|
3526
|
+
`);
|
|
3527
|
+
}
|
|
3528
|
+
} else if (Array.isArray(data) && data.length === 0) {
|
|
3529
|
+
wrappedStreamCallback(`**No rows returned.**
|
|
3530
|
+
|
|
3531
|
+
`);
|
|
3532
|
+
}
|
|
3533
|
+
wrappedStreamCallback(`\u{1F4CA} **Analyzing results...**
|
|
3534
|
+
|
|
3535
|
+
`);
|
|
3536
|
+
}
|
|
3537
|
+
return JSON.stringify(data, null, 2);
|
|
3538
|
+
} catch (error) {
|
|
3539
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
3540
|
+
logger.error(`[${this.getProviderName()}] Query execution failed (attempt ${attempts}/${MAX_QUERY_ATTEMPTS}): ${errorMsg}`);
|
|
3541
|
+
logCollector?.error(`Query failed (attempt ${attempts}/${MAX_QUERY_ATTEMPTS}): ${errorMsg}`);
|
|
3542
|
+
if (wrappedStreamCallback) {
|
|
3543
|
+
wrappedStreamCallback(`\u274C **Query execution failed:**
|
|
3544
|
+
\`\`\`
|
|
3545
|
+
${errorMsg}
|
|
3546
|
+
\`\`\`
|
|
3547
|
+
|
|
3548
|
+
`);
|
|
3549
|
+
if (attempts < MAX_QUERY_ATTEMPTS) {
|
|
3550
|
+
wrappedStreamCallback(`\u{1F527} **Generating corrected query...**
|
|
3551
|
+
|
|
3552
|
+
`);
|
|
3553
|
+
}
|
|
3554
|
+
}
|
|
3555
|
+
throw new Error(`Query execution failed: ${errorMsg}`);
|
|
3556
|
+
}
|
|
3557
|
+
}
|
|
3558
|
+
throw new Error(`Unknown tool: ${toolName}`);
|
|
3559
|
+
};
|
|
3560
|
+
const result = await LLM.streamWithTools(
|
|
3014
3561
|
{
|
|
3015
3562
|
sys: prompts.system,
|
|
3016
3563
|
user: prompts.user
|
|
3017
3564
|
},
|
|
3565
|
+
tools,
|
|
3566
|
+
toolHandler,
|
|
3018
3567
|
{
|
|
3019
3568
|
model: this.model,
|
|
3020
|
-
maxTokens:
|
|
3569
|
+
maxTokens: 4e3,
|
|
3021
3570
|
temperature: 0.7,
|
|
3022
|
-
apiKey: this.getApiKey(apiKey)
|
|
3571
|
+
apiKey: this.getApiKey(apiKey),
|
|
3572
|
+
partial: wrappedStreamCallback
|
|
3573
|
+
// Pass the wrapped streaming callback to LLM
|
|
3023
3574
|
},
|
|
3024
|
-
|
|
3025
|
-
//
|
|
3575
|
+
10
|
|
3576
|
+
// max iterations: allows for 6 retries + final response + buffer
|
|
3026
3577
|
);
|
|
3027
|
-
logger.info(`[${this.getProviderName()}] Text response
|
|
3028
|
-
|
|
3029
|
-
|
|
3578
|
+
logger.info(`[${this.getProviderName()}] Text response stream completed`);
|
|
3579
|
+
const textResponse = fullStreamedText || result || "I apologize, but I was unable to generate a response.";
|
|
3580
|
+
if (maxAttemptsReached) {
|
|
3581
|
+
logger.warn(`[${this.getProviderName()}] Max query attempts reached, returning failure response`);
|
|
3582
|
+
logCollector?.error("Failed to generate valid query after maximum attempts");
|
|
3583
|
+
return {
|
|
3584
|
+
success: false,
|
|
3585
|
+
errors: [`Maximum query attempts (${MAX_QUERY_ATTEMPTS}) reached. Unable to generate a valid query for your question.`],
|
|
3586
|
+
data: {
|
|
3587
|
+
text: textResponse,
|
|
3588
|
+
// Include the streamed text showing all attempts
|
|
3589
|
+
matchedComponents: [],
|
|
3590
|
+
actions: [],
|
|
3591
|
+
method: `${this.getProviderName()}-text-response-max-attempts`
|
|
3592
|
+
}
|
|
3593
|
+
};
|
|
3594
|
+
}
|
|
3595
|
+
logCollector?.info(`Text response: ${textResponse.substring(0, 100)}${textResponse.length > 100 ? "..." : ""}`);
|
|
3030
3596
|
logCollector?.logExplanation(
|
|
3031
3597
|
"Text response generated",
|
|
3032
|
-
|
|
3598
|
+
"Generated plain text response with component suggestions",
|
|
3033
3599
|
{
|
|
3034
|
-
|
|
3035
|
-
confidence: result.confidence,
|
|
3036
|
-
textLength: result.text?.length || 0
|
|
3600
|
+
textLength: textResponse.length
|
|
3037
3601
|
}
|
|
3038
3602
|
);
|
|
3603
|
+
let matchedComponents = [];
|
|
3604
|
+
let selectedLayoutComponent = null;
|
|
3605
|
+
let layoutReasoning = "No layout selected";
|
|
3606
|
+
let actions = [];
|
|
3607
|
+
if (components && components.length > 0) {
|
|
3608
|
+
logger.info(`[${this.getProviderName()}] Matching components from text response...`);
|
|
3609
|
+
const matchResult = await this.matchComponentsFromTextResponse(
|
|
3610
|
+
textResponse,
|
|
3611
|
+
components,
|
|
3612
|
+
apiKey,
|
|
3613
|
+
logCollector
|
|
3614
|
+
);
|
|
3615
|
+
matchedComponents = matchResult.components;
|
|
3616
|
+
selectedLayoutComponent = matchResult.selectedLayoutComponent;
|
|
3617
|
+
layoutReasoning = matchResult.layoutReasoning;
|
|
3618
|
+
actions = matchResult.actions;
|
|
3619
|
+
}
|
|
3620
|
+
let container_componet = null;
|
|
3621
|
+
if (matchedComponents.length > 0) {
|
|
3622
|
+
if (selectedLayoutComponent) {
|
|
3623
|
+
container_componet = {
|
|
3624
|
+
...selectedLayoutComponent,
|
|
3625
|
+
id: `${selectedLayoutComponent.id}_${Date.now()}`,
|
|
3626
|
+
description: layoutReasoning,
|
|
3627
|
+
props: {
|
|
3628
|
+
...selectedLayoutComponent.props,
|
|
3629
|
+
config: {
|
|
3630
|
+
...selectedLayoutComponent.props?.config || {},
|
|
3631
|
+
components: matchedComponents
|
|
3632
|
+
},
|
|
3633
|
+
actions
|
|
3634
|
+
}
|
|
3635
|
+
};
|
|
3636
|
+
logger.info(`[${this.getProviderName()}] Created ${selectedLayoutComponent.name} (${selectedLayoutComponent.type}) container with ${matchedComponents.length} components and ${actions.length} actions`);
|
|
3637
|
+
logCollector?.info(`Created ${selectedLayoutComponent.name} with ${matchedComponents.length} components and ${actions.length} actions: ${layoutReasoning}`);
|
|
3638
|
+
} else {
|
|
3639
|
+
container_componet = {
|
|
3640
|
+
id: `multi_container_${Date.now()}`,
|
|
3641
|
+
name: "MultiComponentContainer",
|
|
3642
|
+
type: "Container",
|
|
3643
|
+
description: layoutReasoning,
|
|
3644
|
+
category: "dynamic",
|
|
3645
|
+
keywords: ["dashboard", "layout", "container"],
|
|
3646
|
+
props: {
|
|
3647
|
+
config: {
|
|
3648
|
+
components: matchedComponents
|
|
3649
|
+
},
|
|
3650
|
+
actions
|
|
3651
|
+
}
|
|
3652
|
+
};
|
|
3653
|
+
logger.info(`[${this.getProviderName()}] Created fallback MultiComponentContainer with ${matchedComponents.length} components and ${actions.length} actions`);
|
|
3654
|
+
logCollector?.info(`Created MultiComponentContainer with ${matchedComponents.length} components and ${actions.length} actions: ${layoutReasoning}`);
|
|
3655
|
+
}
|
|
3656
|
+
}
|
|
3039
3657
|
return {
|
|
3040
3658
|
success: true,
|
|
3041
3659
|
data: {
|
|
3042
|
-
text:
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3660
|
+
text: textResponse,
|
|
3661
|
+
matchedComponents,
|
|
3662
|
+
component: container_componet,
|
|
3663
|
+
layoutReasoning,
|
|
3664
|
+
actions,
|
|
3665
|
+
method: `${this.getProviderName()}-text-response`
|
|
3046
3666
|
},
|
|
3047
3667
|
errors: []
|
|
3048
3668
|
};
|
|
3049
3669
|
} catch (error) {
|
|
3050
3670
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
3051
3671
|
logger.error(`[${this.getProviderName()}] Error generating text response: ${errorMsg}`);
|
|
3052
|
-
logger.debug(`[${this.getProviderName()}] Text response generation error details:`, error);
|
|
3053
3672
|
logCollector?.error(`Error generating text response: ${errorMsg}`);
|
|
3054
3673
|
errors.push(errorMsg);
|
|
3055
3674
|
return {
|
|
@@ -3057,9 +3676,9 @@ var BaseLLM = class {
|
|
|
3057
3676
|
errors,
|
|
3058
3677
|
data: {
|
|
3059
3678
|
text: "I apologize, but I encountered an error while processing your question. Please try rephrasing or ask something else.",
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3679
|
+
matchedComponents: [],
|
|
3680
|
+
actions: [],
|
|
3681
|
+
method: `${this.getProviderName()}-text-response-error`
|
|
3063
3682
|
}
|
|
3064
3683
|
};
|
|
3065
3684
|
}
|
|
@@ -3250,8 +3869,11 @@ var BaseLLM = class {
|
|
|
3250
3869
|
* Supports both component generation and text response modes
|
|
3251
3870
|
*
|
|
3252
3871
|
* @param responseMode - 'component' for component generation (default), 'text' for text responses
|
|
3872
|
+
* @param streamCallback - Optional callback function to receive text chunks as they stream (only for text mode)
|
|
3873
|
+
* @param collections - Collection registry for executing database queries (required for text mode)
|
|
3253
3874
|
*/
|
|
3254
|
-
async handleUserRequest(userPrompt, components, apiKey, logCollector, conversationHistory, responseMode = "component") {
|
|
3875
|
+
async handleUserRequest(userPrompt, components, apiKey, logCollector, conversationHistory, responseMode = "component", streamCallback, collections) {
|
|
3876
|
+
const startTime = Date.now();
|
|
3255
3877
|
logger.info(`[${this.getProviderName()}] handleUserRequest called with responseMode: ${responseMode}`);
|
|
3256
3878
|
if (responseMode === "text") {
|
|
3257
3879
|
logger.info(`[${this.getProviderName()}] Using text response mode`);
|
|
@@ -3260,25 +3882,23 @@ var BaseLLM = class {
|
|
|
3260
3882
|
userPrompt,
|
|
3261
3883
|
apiKey,
|
|
3262
3884
|
logCollector,
|
|
3263
|
-
conversationHistory
|
|
3885
|
+
conversationHistory,
|
|
3886
|
+
streamCallback,
|
|
3887
|
+
collections,
|
|
3888
|
+
components
|
|
3264
3889
|
);
|
|
3265
3890
|
if (!textResponse.success) {
|
|
3891
|
+
const elapsedTime3 = Date.now() - startTime;
|
|
3266
3892
|
logger.error(`[${this.getProviderName()}] Text response generation failed`);
|
|
3893
|
+
logger.info(`[${this.getProviderName()}] Total time taken: ${elapsedTime3}ms (${(elapsedTime3 / 1e3).toFixed(2)}s)`);
|
|
3894
|
+
logCollector?.info(`Total time taken: ${elapsedTime3}ms (${(elapsedTime3 / 1e3).toFixed(2)}s)`);
|
|
3267
3895
|
return textResponse;
|
|
3268
3896
|
}
|
|
3897
|
+
const elapsedTime2 = Date.now() - startTime;
|
|
3269
3898
|
logger.info(`[${this.getProviderName()}] Text response generated successfully`);
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
textResponse: textResponse.data.text,
|
|
3274
|
-
text: textResponse.data.text,
|
|
3275
|
-
responseType: textResponse.data.responseType,
|
|
3276
|
-
confidence: textResponse.data.confidence,
|
|
3277
|
-
reasoning: textResponse.data.reasoning,
|
|
3278
|
-
method: `${this.getProviderName()}-text-response`
|
|
3279
|
-
},
|
|
3280
|
-
errors: []
|
|
3281
|
-
};
|
|
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)`);
|
|
3901
|
+
return textResponse;
|
|
3282
3902
|
}
|
|
3283
3903
|
const componentResponse = await this.generateComponentResponse(
|
|
3284
3904
|
userPrompt,
|
|
@@ -3288,24 +3908,17 @@ var BaseLLM = class {
|
|
|
3288
3908
|
conversationHistory
|
|
3289
3909
|
);
|
|
3290
3910
|
if (!componentResponse.success) {
|
|
3911
|
+
const elapsedTime2 = Date.now() - startTime;
|
|
3291
3912
|
logger.error(`[${this.getProviderName()}] Component response generation failed`);
|
|
3913
|
+
logger.info(`[${this.getProviderName()}] Total time taken: ${elapsedTime2}ms (${(elapsedTime2 / 1e3).toFixed(2)}s)`);
|
|
3914
|
+
logCollector?.info(`Total time taken: ${elapsedTime2}ms (${(elapsedTime2 / 1e3).toFixed(2)}s)`);
|
|
3292
3915
|
return componentResponse;
|
|
3293
3916
|
}
|
|
3917
|
+
const elapsedTime = Date.now() - startTime;
|
|
3294
3918
|
logger.info(`[${this.getProviderName()}] Component response generated successfully`);
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
component: componentResponse.data.component,
|
|
3299
|
-
reasoning: componentResponse.data.reasoning,
|
|
3300
|
-
method: componentResponse.data.method,
|
|
3301
|
-
// Preserve the original method name
|
|
3302
|
-
questionType: componentResponse.data.questionType,
|
|
3303
|
-
needsMultipleComponents: componentResponse.data.needsMultipleComponents,
|
|
3304
|
-
propsModified: componentResponse.data.propsModified,
|
|
3305
|
-
queryModified: componentResponse.data.queryModified
|
|
3306
|
-
},
|
|
3307
|
-
errors: []
|
|
3308
|
-
};
|
|
3919
|
+
logger.info(`[${this.getProviderName()}] Total time taken: ${elapsedTime}ms (${(elapsedTime / 1e3).toFixed(2)}s)`);
|
|
3920
|
+
logCollector?.info(`Total time taken: ${elapsedTime}ms (${(elapsedTime / 1e3).toFixed(2)}s)`);
|
|
3921
|
+
return componentResponse;
|
|
3309
3922
|
}
|
|
3310
3923
|
/**
|
|
3311
3924
|
* Generate next questions that the user might ask based on the original prompt and generated component
|
|
@@ -3418,7 +4031,7 @@ function getLLMProviders() {
|
|
|
3418
4031
|
return DEFAULT_PROVIDERS;
|
|
3419
4032
|
}
|
|
3420
4033
|
}
|
|
3421
|
-
var useAnthropicMethod = async (prompt, components, apiKey, logCollector, conversationHistory, responseMode = "component") => {
|
|
4034
|
+
var useAnthropicMethod = async (prompt, components, apiKey, logCollector, conversationHistory, responseMode = "component", streamCallback, collections) => {
|
|
3422
4035
|
logger.debug("[useAnthropicMethod] Initializing Anthropic Claude matching method");
|
|
3423
4036
|
logger.debug(`[useAnthropicMethod] Response mode: ${responseMode}`);
|
|
3424
4037
|
const msg = `Using Anthropic Claude ${responseMode === "text" ? "text response" : "matching"} method...`;
|
|
@@ -3430,11 +4043,11 @@ var useAnthropicMethod = async (prompt, components, apiKey, logCollector, conver
|
|
|
3430
4043
|
return { success: false, errors: [emptyMsg] };
|
|
3431
4044
|
}
|
|
3432
4045
|
logger.debug(`[useAnthropicMethod] Processing with ${components.length} components`);
|
|
3433
|
-
const matchResult = await anthropicLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory, responseMode);
|
|
4046
|
+
const matchResult = await anthropicLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory, responseMode, streamCallback, collections);
|
|
3434
4047
|
logger.info(`[useAnthropicMethod] Successfully generated ${responseMode} using Anthropic`);
|
|
3435
4048
|
return matchResult;
|
|
3436
4049
|
};
|
|
3437
|
-
var useGroqMethod = async (prompt, components, apiKey, logCollector, conversationHistory, responseMode = "component") => {
|
|
4050
|
+
var useGroqMethod = async (prompt, components, apiKey, logCollector, conversationHistory, responseMode = "component", streamCallback, collections) => {
|
|
3438
4051
|
logger.debug("[useGroqMethod] Initializing Groq LLM matching method");
|
|
3439
4052
|
logger.debug(`[useGroqMethod] Response mode: ${responseMode}`);
|
|
3440
4053
|
const msg = `Using Groq LLM ${responseMode === "text" ? "text response" : "matching"} method...`;
|
|
@@ -3447,16 +4060,16 @@ var useGroqMethod = async (prompt, components, apiKey, logCollector, conversatio
|
|
|
3447
4060
|
return { success: false, errors: [emptyMsg] };
|
|
3448
4061
|
}
|
|
3449
4062
|
logger.debug(`[useGroqMethod] Processing with ${components.length} components`);
|
|
3450
|
-
const matchResult = await groqLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory, responseMode);
|
|
4063
|
+
const matchResult = await groqLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory, responseMode, streamCallback, collections);
|
|
3451
4064
|
logger.info(`[useGroqMethod] Successfully generated ${responseMode} using Groq`);
|
|
3452
4065
|
return matchResult;
|
|
3453
4066
|
};
|
|
3454
4067
|
var getUserResponseFromCache = async (prompt) => {
|
|
3455
4068
|
return false;
|
|
3456
4069
|
};
|
|
3457
|
-
var get_user_response = async (prompt, components, anthropicApiKey, groqApiKey, llmProviders, logCollector, conversationHistory) => {
|
|
3458
|
-
const responseMode = "component";
|
|
4070
|
+
var get_user_response = async (prompt, components, anthropicApiKey, groqApiKey, llmProviders, logCollector, conversationHistory, responseMode = "component", streamCallback, collections) => {
|
|
3459
4071
|
logger.debug(`[get_user_response] Starting user response generation for prompt: "${prompt.substring(0, 50)}..."`);
|
|
4072
|
+
logger.debug(`[get_user_response] Response mode: ${responseMode}`);
|
|
3460
4073
|
logger.debug("[get_user_response] Checking cache for existing response");
|
|
3461
4074
|
const userResponse = await getUserResponseFromCache(prompt);
|
|
3462
4075
|
if (userResponse) {
|
|
@@ -3487,16 +4100,16 @@ var get_user_response = async (prompt, components, anthropicApiKey, groqApiKey,
|
|
|
3487
4100
|
logCollector?.info(attemptMsg);
|
|
3488
4101
|
let result;
|
|
3489
4102
|
if (provider === "anthropic") {
|
|
3490
|
-
result = await useAnthropicMethod(prompt, components, anthropicApiKey, logCollector, conversationHistory, responseMode);
|
|
4103
|
+
result = await useAnthropicMethod(prompt, components, anthropicApiKey, logCollector, conversationHistory, responseMode, streamCallback, collections);
|
|
3491
4104
|
} else if (provider === "groq") {
|
|
3492
|
-
result = await useGroqMethod(prompt, components, groqApiKey, logCollector, conversationHistory, responseMode);
|
|
4105
|
+
result = await useGroqMethod(prompt, components, groqApiKey, logCollector, conversationHistory, responseMode, streamCallback, collections);
|
|
3493
4106
|
} else {
|
|
3494
4107
|
logger.warn(`[get_user_response] Unknown provider: ${provider} - skipping`);
|
|
3495
4108
|
errors.push(`Unknown provider: ${provider}`);
|
|
3496
4109
|
continue;
|
|
3497
4110
|
}
|
|
3498
4111
|
if (result.success) {
|
|
3499
|
-
const successMsg = `Success with provider: ${provider}
|
|
4112
|
+
const successMsg = `Success with provider: ${provider}`;
|
|
3500
4113
|
logger.info(`${successMsg}`);
|
|
3501
4114
|
logCollector?.info(successMsg);
|
|
3502
4115
|
return result;
|
|
@@ -3717,8 +4330,7 @@ var CONTEXT_CONFIG = {
|
|
|
3717
4330
|
};
|
|
3718
4331
|
|
|
3719
4332
|
// src/handlers/user-prompt-request.ts
|
|
3720
|
-
var
|
|
3721
|
-
var get_user_request = async (data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders) => {
|
|
4333
|
+
var get_user_request = async (data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders, collections) => {
|
|
3722
4334
|
const errors = [];
|
|
3723
4335
|
logger.debug("[USER_PROMPT_REQ] Parsing incoming message data");
|
|
3724
4336
|
const parseResult = UserPromptRequestMessageSchema.safeParse(data);
|
|
@@ -3734,19 +4346,6 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
|
|
|
3734
4346
|
const prompt = payload.prompt;
|
|
3735
4347
|
const SA_RUNTIME = payload.SA_RUNTIME;
|
|
3736
4348
|
const wsId = userPromptRequest.from.id || "unknown";
|
|
3737
|
-
logger.debug(`[REQUEST ${id}] Full request details - wsId: ${wsId}, prompt length: ${prompt.length}`);
|
|
3738
|
-
if (processedMessageIds.has(id)) {
|
|
3739
|
-
logger.warn(`[REQUEST ${id}] Duplicate request detected - ignoring`);
|
|
3740
|
-
}
|
|
3741
|
-
processedMessageIds.add(id);
|
|
3742
|
-
logger.debug(`[REQUEST ${id}] Message ID marked as processed (${processedMessageIds.size} total)`);
|
|
3743
|
-
if (processedMessageIds.size > 100) {
|
|
3744
|
-
const firstId = processedMessageIds.values().next().value;
|
|
3745
|
-
if (firstId) {
|
|
3746
|
-
processedMessageIds.delete(firstId);
|
|
3747
|
-
logger.debug(`[REQUEST ${id}] Cleaned up old message ID from cache`);
|
|
3748
|
-
}
|
|
3749
|
-
}
|
|
3750
4349
|
if (!SA_RUNTIME) {
|
|
3751
4350
|
errors.push("SA_RUNTIME is required");
|
|
3752
4351
|
}
|
|
@@ -3761,9 +4360,7 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
|
|
|
3761
4360
|
if (!prompt) {
|
|
3762
4361
|
errors.push("Prompt not found");
|
|
3763
4362
|
}
|
|
3764
|
-
|
|
3765
|
-
errors.push("Components not found");
|
|
3766
|
-
}
|
|
4363
|
+
logger.debug(`[REQUEST ${id}] Full request details - uiBlockId: ${existingUiBlockId}, threadId: ${threadId}, prompt: ${prompt}`);
|
|
3767
4364
|
if (errors.length > 0) {
|
|
3768
4365
|
return { success: false, errors, id, wsId };
|
|
3769
4366
|
}
|
|
@@ -3777,8 +4374,44 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
|
|
|
3777
4374
|
logCollector.info(`Starting user prompt request with ${components.length} components`);
|
|
3778
4375
|
const conversationHistory = thread.getConversationContext(CONTEXT_CONFIG.MAX_CONVERSATION_CONTEXT_BLOCKS, existingUiBlockId);
|
|
3779
4376
|
logger.info("conversationHistory", conversationHistory);
|
|
3780
|
-
const
|
|
3781
|
-
logger.info("
|
|
4377
|
+
const responseMode = payload.responseMode || "component";
|
|
4378
|
+
logger.info("responseMode", responseMode);
|
|
4379
|
+
let streamCallback;
|
|
4380
|
+
let accumulatedStreamResponse = "";
|
|
4381
|
+
if (responseMode === "text") {
|
|
4382
|
+
streamCallback = (chunk) => {
|
|
4383
|
+
accumulatedStreamResponse += chunk;
|
|
4384
|
+
logger.debug(`[STREAM] Sending chunk (${chunk.length} chars): "${chunk.substring(0, 20)}..."`);
|
|
4385
|
+
const streamMessage = {
|
|
4386
|
+
id: `stream_${existingUiBlockId}`,
|
|
4387
|
+
// Different ID pattern for streaming
|
|
4388
|
+
type: "USER_PROMPT_STREAM",
|
|
4389
|
+
from: { type: "data-agent" },
|
|
4390
|
+
to: {
|
|
4391
|
+
type: "runtime",
|
|
4392
|
+
id: wsId
|
|
4393
|
+
},
|
|
4394
|
+
payload: {
|
|
4395
|
+
uiBlockId: existingUiBlockId,
|
|
4396
|
+
chunk
|
|
4397
|
+
}
|
|
4398
|
+
};
|
|
4399
|
+
sendMessage(streamMessage);
|
|
4400
|
+
logger.debug(`[STREAM] Chunk sent to wsId: ${wsId}`);
|
|
4401
|
+
};
|
|
4402
|
+
}
|
|
4403
|
+
const userResponse = await get_user_response(
|
|
4404
|
+
prompt,
|
|
4405
|
+
components,
|
|
4406
|
+
anthropicApiKey,
|
|
4407
|
+
groqApiKey,
|
|
4408
|
+
llmProviders,
|
|
4409
|
+
logCollector,
|
|
4410
|
+
conversationHistory,
|
|
4411
|
+
responseMode,
|
|
4412
|
+
streamCallback,
|
|
4413
|
+
collections
|
|
4414
|
+
);
|
|
3782
4415
|
logCollector.info("User prompt request completed");
|
|
3783
4416
|
const uiBlockId = existingUiBlockId;
|
|
3784
4417
|
if (!userResponse.success) {
|
|
@@ -3795,16 +4428,21 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
|
|
|
3795
4428
|
}
|
|
3796
4429
|
let component = null;
|
|
3797
4430
|
let textResponse = null;
|
|
4431
|
+
let actions = [];
|
|
3798
4432
|
if (userResponse.data) {
|
|
3799
4433
|
if (typeof userResponse.data === "object") {
|
|
3800
4434
|
if ("component" in userResponse.data) {
|
|
3801
4435
|
component = userResponse.data.component;
|
|
3802
4436
|
}
|
|
3803
4437
|
if ("textResponse" in userResponse.data) {
|
|
3804
|
-
textResponse = userResponse.data.
|
|
4438
|
+
textResponse = userResponse.data.text;
|
|
4439
|
+
}
|
|
4440
|
+
if ("actions" in userResponse.data) {
|
|
4441
|
+
actions = userResponse.data.actions || [];
|
|
3805
4442
|
}
|
|
3806
4443
|
}
|
|
3807
4444
|
}
|
|
4445
|
+
const finalTextResponse = responseMode === "text" && accumulatedStreamResponse ? accumulatedStreamResponse : textResponse;
|
|
3808
4446
|
const uiBlock = new UIBlock(
|
|
3809
4447
|
prompt,
|
|
3810
4448
|
{},
|
|
@@ -3812,11 +4450,15 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
|
|
|
3812
4450
|
component,
|
|
3813
4451
|
// generatedComponentMetadata: full component object (ComponentSchema)
|
|
3814
4452
|
[],
|
|
3815
|
-
// actions: empty initially
|
|
4453
|
+
// actions: empty initially, will be set below
|
|
3816
4454
|
uiBlockId,
|
|
3817
|
-
|
|
3818
|
-
// textResponse:
|
|
4455
|
+
finalTextResponse
|
|
4456
|
+
// textResponse: FULL streaming response including all intermediate messages
|
|
3819
4457
|
);
|
|
4458
|
+
if (actions.length > 0) {
|
|
4459
|
+
uiBlock.setActions(actions);
|
|
4460
|
+
logger.info(`Stored ${actions.length} actions in UIBlock: ${uiBlockId}`);
|
|
4461
|
+
}
|
|
3820
4462
|
thread.addUIBlock(uiBlock);
|
|
3821
4463
|
logger.info(`Created UIBlock: ${uiBlockId} in Thread: ${threadId}`);
|
|
3822
4464
|
return {
|
|
@@ -3829,8 +4471,8 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
|
|
|
3829
4471
|
wsId
|
|
3830
4472
|
};
|
|
3831
4473
|
};
|
|
3832
|
-
async function handleUserPromptRequest(data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders) {
|
|
3833
|
-
const response = await get_user_request(data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders);
|
|
4474
|
+
async function handleUserPromptRequest(data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders, collections) {
|
|
4475
|
+
const response = await get_user_request(data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders, collections);
|
|
3834
4476
|
sendDataResponse4(
|
|
3835
4477
|
response.id || data.id,
|
|
3836
4478
|
{
|
|
@@ -4054,7 +4696,6 @@ async function handleActionsRequest(data, sendMessage, anthropicApiKey, groqApiK
|
|
|
4054
4696
|
const uiBlockId = SA_RUNTIME.uiBlockId;
|
|
4055
4697
|
const threadId = SA_RUNTIME.threadId;
|
|
4056
4698
|
logger.debug(`[ACTIONS_REQ ${id}] SA_RUNTIME validated - threadId: ${threadId}, uiBlockId: ${uiBlockId}`);
|
|
4057
|
-
logger.debug(`[ACTIONS_REQ ${id}] Retrieving thread: ${threadId}`);
|
|
4058
4699
|
const threadManager = ThreadManager.getInstance();
|
|
4059
4700
|
const thread = threadManager.getThread(threadId);
|
|
4060
4701
|
if (!thread) {
|
|
@@ -4906,7 +5547,7 @@ function sendResponse5(id, res, sendMessage, clientId) {
|
|
|
4906
5547
|
}
|
|
4907
5548
|
|
|
4908
5549
|
// src/auth/user-manager.ts
|
|
4909
|
-
var
|
|
5550
|
+
var import_fs4 = __toESM(require("fs"));
|
|
4910
5551
|
var import_path3 = __toESM(require("path"));
|
|
4911
5552
|
var import_os = __toESM(require("os"));
|
|
4912
5553
|
var UserManager = class {
|
|
@@ -4946,19 +5587,19 @@ var UserManager = class {
|
|
|
4946
5587
|
async loadUsersFromFile() {
|
|
4947
5588
|
try {
|
|
4948
5589
|
const dir = import_path3.default.dirname(this.filePath);
|
|
4949
|
-
if (!
|
|
5590
|
+
if (!import_fs4.default.existsSync(dir)) {
|
|
4950
5591
|
logger.info(`Creating directory structure: ${dir}`);
|
|
4951
|
-
|
|
5592
|
+
import_fs4.default.mkdirSync(dir, { recursive: true });
|
|
4952
5593
|
}
|
|
4953
|
-
if (!
|
|
5594
|
+
if (!import_fs4.default.existsSync(this.filePath)) {
|
|
4954
5595
|
logger.info(`Users file does not exist at ${this.filePath}, creating with empty users`);
|
|
4955
5596
|
const initialData = { users: [] };
|
|
4956
|
-
|
|
5597
|
+
import_fs4.default.writeFileSync(this.filePath, JSON.stringify(initialData, null, 4));
|
|
4957
5598
|
this.users = [];
|
|
4958
5599
|
this.hasChanged = false;
|
|
4959
5600
|
return;
|
|
4960
5601
|
}
|
|
4961
|
-
const fileContent =
|
|
5602
|
+
const fileContent = import_fs4.default.readFileSync(this.filePath, "utf-8");
|
|
4962
5603
|
const rawData = JSON.parse(fileContent);
|
|
4963
5604
|
const validatedData = UsersDataSchema.parse(rawData);
|
|
4964
5605
|
this.users = validatedData.users;
|
|
@@ -4978,15 +5619,15 @@ var UserManager = class {
|
|
|
4978
5619
|
}
|
|
4979
5620
|
try {
|
|
4980
5621
|
const dir = import_path3.default.dirname(this.filePath);
|
|
4981
|
-
if (!
|
|
4982
|
-
|
|
5622
|
+
if (!import_fs4.default.existsSync(dir)) {
|
|
5623
|
+
import_fs4.default.mkdirSync(dir, { recursive: true });
|
|
4983
5624
|
}
|
|
4984
5625
|
const usersToSave = this.users.map((user) => {
|
|
4985
5626
|
const { wsIds, ...userWithoutWsIds } = user;
|
|
4986
5627
|
return userWithoutWsIds;
|
|
4987
5628
|
});
|
|
4988
5629
|
const data = { users: usersToSave };
|
|
4989
|
-
|
|
5630
|
+
import_fs4.default.writeFileSync(this.filePath, JSON.stringify(data, null, 4));
|
|
4990
5631
|
this.hasChanged = false;
|
|
4991
5632
|
logger.debug(`Synced ${this.users.length} users to file (wsIds excluded)`);
|
|
4992
5633
|
} catch (error) {
|
|
@@ -5204,7 +5845,7 @@ var UserManager = class {
|
|
|
5204
5845
|
};
|
|
5205
5846
|
|
|
5206
5847
|
// src/dashboards/dashboard-manager.ts
|
|
5207
|
-
var
|
|
5848
|
+
var import_fs5 = __toESM(require("fs"));
|
|
5208
5849
|
var import_path4 = __toESM(require("path"));
|
|
5209
5850
|
var import_os2 = __toESM(require("os"));
|
|
5210
5851
|
var DashboardManager = class {
|
|
@@ -5239,12 +5880,12 @@ var DashboardManager = class {
|
|
|
5239
5880
|
createDashboard(dashboardId, dashboard) {
|
|
5240
5881
|
const dashboardPath = this.getDashboardPath(dashboardId);
|
|
5241
5882
|
const dashboardDir = import_path4.default.dirname(dashboardPath);
|
|
5242
|
-
if (
|
|
5883
|
+
if (import_fs5.default.existsSync(dashboardPath)) {
|
|
5243
5884
|
throw new Error(`Dashboard '${dashboardId}' already exists`);
|
|
5244
5885
|
}
|
|
5245
5886
|
const validated = DSLRendererPropsSchema.parse(dashboard);
|
|
5246
|
-
|
|
5247
|
-
|
|
5887
|
+
import_fs5.default.mkdirSync(dashboardDir, { recursive: true });
|
|
5888
|
+
import_fs5.default.writeFileSync(dashboardPath, JSON.stringify(validated, null, 4));
|
|
5248
5889
|
logger.info(`Dashboard created: ${dashboardId}`);
|
|
5249
5890
|
return validated;
|
|
5250
5891
|
}
|
|
@@ -5255,12 +5896,12 @@ var DashboardManager = class {
|
|
|
5255
5896
|
*/
|
|
5256
5897
|
getDashboard(dashboardId) {
|
|
5257
5898
|
const dashboardPath = this.getDashboardPath(dashboardId);
|
|
5258
|
-
if (!
|
|
5899
|
+
if (!import_fs5.default.existsSync(dashboardPath)) {
|
|
5259
5900
|
logger.warn(`Dashboard not found: ${dashboardId}`);
|
|
5260
5901
|
return null;
|
|
5261
5902
|
}
|
|
5262
5903
|
try {
|
|
5263
|
-
const fileContent =
|
|
5904
|
+
const fileContent = import_fs5.default.readFileSync(dashboardPath, "utf-8");
|
|
5264
5905
|
const dashboard = JSON.parse(fileContent);
|
|
5265
5906
|
const validated = DSLRendererPropsSchema.parse(dashboard);
|
|
5266
5907
|
return validated;
|
|
@@ -5274,16 +5915,16 @@ var DashboardManager = class {
|
|
|
5274
5915
|
* @returns Array of dashboard objects with their IDs
|
|
5275
5916
|
*/
|
|
5276
5917
|
getAllDashboards() {
|
|
5277
|
-
if (!
|
|
5278
|
-
|
|
5918
|
+
if (!import_fs5.default.existsSync(this.dashboardsBasePath)) {
|
|
5919
|
+
import_fs5.default.mkdirSync(this.dashboardsBasePath, { recursive: true });
|
|
5279
5920
|
return [];
|
|
5280
5921
|
}
|
|
5281
5922
|
const dashboards = [];
|
|
5282
5923
|
try {
|
|
5283
|
-
const dashboardDirs =
|
|
5924
|
+
const dashboardDirs = import_fs5.default.readdirSync(this.dashboardsBasePath);
|
|
5284
5925
|
for (const dashboardId of dashboardDirs) {
|
|
5285
5926
|
const dashboardPath = this.getDashboardPath(dashboardId);
|
|
5286
|
-
if (
|
|
5927
|
+
if (import_fs5.default.existsSync(dashboardPath)) {
|
|
5287
5928
|
const dashboard = this.getDashboard(dashboardId);
|
|
5288
5929
|
if (dashboard) {
|
|
5289
5930
|
dashboards.push({ dashboardId, dashboard });
|
|
@@ -5305,13 +5946,13 @@ var DashboardManager = class {
|
|
|
5305
5946
|
*/
|
|
5306
5947
|
updateDashboard(dashboardId, dashboard) {
|
|
5307
5948
|
const dashboardPath = this.getDashboardPath(dashboardId);
|
|
5308
|
-
if (!
|
|
5949
|
+
if (!import_fs5.default.existsSync(dashboardPath)) {
|
|
5309
5950
|
logger.warn(`Dashboard not found for update: ${dashboardId}`);
|
|
5310
5951
|
return null;
|
|
5311
5952
|
}
|
|
5312
5953
|
try {
|
|
5313
5954
|
const validated = DSLRendererPropsSchema.parse(dashboard);
|
|
5314
|
-
|
|
5955
|
+
import_fs5.default.writeFileSync(dashboardPath, JSON.stringify(validated, null, 4));
|
|
5315
5956
|
logger.info(`Dashboard updated: ${dashboardId}`);
|
|
5316
5957
|
return validated;
|
|
5317
5958
|
} catch (error) {
|
|
@@ -5327,12 +5968,12 @@ var DashboardManager = class {
|
|
|
5327
5968
|
deleteDashboard(dashboardId) {
|
|
5328
5969
|
const dashboardPath = this.getDashboardPath(dashboardId);
|
|
5329
5970
|
const dashboardDir = import_path4.default.dirname(dashboardPath);
|
|
5330
|
-
if (!
|
|
5971
|
+
if (!import_fs5.default.existsSync(dashboardPath)) {
|
|
5331
5972
|
logger.warn(`Dashboard not found for deletion: ${dashboardId}`);
|
|
5332
5973
|
return false;
|
|
5333
5974
|
}
|
|
5334
5975
|
try {
|
|
5335
|
-
|
|
5976
|
+
import_fs5.default.rmSync(dashboardDir, { recursive: true, force: true });
|
|
5336
5977
|
logger.info(`Dashboard deleted: ${dashboardId}`);
|
|
5337
5978
|
return true;
|
|
5338
5979
|
} catch (error) {
|
|
@@ -5347,21 +5988,21 @@ var DashboardManager = class {
|
|
|
5347
5988
|
*/
|
|
5348
5989
|
dashboardExists(dashboardId) {
|
|
5349
5990
|
const dashboardPath = this.getDashboardPath(dashboardId);
|
|
5350
|
-
return
|
|
5991
|
+
return import_fs5.default.existsSync(dashboardPath);
|
|
5351
5992
|
}
|
|
5352
5993
|
/**
|
|
5353
5994
|
* Get dashboard count
|
|
5354
5995
|
* @returns Number of dashboards
|
|
5355
5996
|
*/
|
|
5356
5997
|
getDashboardCount() {
|
|
5357
|
-
if (!
|
|
5998
|
+
if (!import_fs5.default.existsSync(this.dashboardsBasePath)) {
|
|
5358
5999
|
return 0;
|
|
5359
6000
|
}
|
|
5360
6001
|
try {
|
|
5361
|
-
const dashboardDirs =
|
|
6002
|
+
const dashboardDirs = import_fs5.default.readdirSync(this.dashboardsBasePath);
|
|
5362
6003
|
return dashboardDirs.filter((dir) => {
|
|
5363
6004
|
const dashboardPath = this.getDashboardPath(dir);
|
|
5364
|
-
return
|
|
6005
|
+
return import_fs5.default.existsSync(dashboardPath);
|
|
5365
6006
|
}).length;
|
|
5366
6007
|
} catch (error) {
|
|
5367
6008
|
logger.error("Failed to get dashboard count:", error);
|
|
@@ -5371,7 +6012,7 @@ var DashboardManager = class {
|
|
|
5371
6012
|
};
|
|
5372
6013
|
|
|
5373
6014
|
// src/reports/report-manager.ts
|
|
5374
|
-
var
|
|
6015
|
+
var import_fs6 = __toESM(require("fs"));
|
|
5375
6016
|
var import_path5 = __toESM(require("path"));
|
|
5376
6017
|
var import_os3 = __toESM(require("os"));
|
|
5377
6018
|
var ReportManager = class {
|
|
@@ -5406,12 +6047,12 @@ var ReportManager = class {
|
|
|
5406
6047
|
createReport(reportId, report) {
|
|
5407
6048
|
const reportPath = this.getReportPath(reportId);
|
|
5408
6049
|
const reportDir = import_path5.default.dirname(reportPath);
|
|
5409
|
-
if (
|
|
6050
|
+
if (import_fs6.default.existsSync(reportPath)) {
|
|
5410
6051
|
throw new Error(`Report '${reportId}' already exists`);
|
|
5411
6052
|
}
|
|
5412
6053
|
const validated = DSLRendererPropsSchema2.parse(report);
|
|
5413
|
-
|
|
5414
|
-
|
|
6054
|
+
import_fs6.default.mkdirSync(reportDir, { recursive: true });
|
|
6055
|
+
import_fs6.default.writeFileSync(reportPath, JSON.stringify(validated, null, 4));
|
|
5415
6056
|
logger.info(`Report created: ${reportId}`);
|
|
5416
6057
|
return validated;
|
|
5417
6058
|
}
|
|
@@ -5422,12 +6063,12 @@ var ReportManager = class {
|
|
|
5422
6063
|
*/
|
|
5423
6064
|
getReport(reportId) {
|
|
5424
6065
|
const reportPath = this.getReportPath(reportId);
|
|
5425
|
-
if (!
|
|
6066
|
+
if (!import_fs6.default.existsSync(reportPath)) {
|
|
5426
6067
|
logger.warn(`Report not found: ${reportId}`);
|
|
5427
6068
|
return null;
|
|
5428
6069
|
}
|
|
5429
6070
|
try {
|
|
5430
|
-
const fileContent =
|
|
6071
|
+
const fileContent = import_fs6.default.readFileSync(reportPath, "utf-8");
|
|
5431
6072
|
const report = JSON.parse(fileContent);
|
|
5432
6073
|
const validated = DSLRendererPropsSchema2.parse(report);
|
|
5433
6074
|
return validated;
|
|
@@ -5441,16 +6082,16 @@ var ReportManager = class {
|
|
|
5441
6082
|
* @returns Array of report objects with their IDs
|
|
5442
6083
|
*/
|
|
5443
6084
|
getAllReports() {
|
|
5444
|
-
if (!
|
|
5445
|
-
|
|
6085
|
+
if (!import_fs6.default.existsSync(this.reportsBasePath)) {
|
|
6086
|
+
import_fs6.default.mkdirSync(this.reportsBasePath, { recursive: true });
|
|
5446
6087
|
return [];
|
|
5447
6088
|
}
|
|
5448
6089
|
const reports = [];
|
|
5449
6090
|
try {
|
|
5450
|
-
const reportDirs =
|
|
6091
|
+
const reportDirs = import_fs6.default.readdirSync(this.reportsBasePath);
|
|
5451
6092
|
for (const reportId of reportDirs) {
|
|
5452
6093
|
const reportPath = this.getReportPath(reportId);
|
|
5453
|
-
if (
|
|
6094
|
+
if (import_fs6.default.existsSync(reportPath)) {
|
|
5454
6095
|
const report = this.getReport(reportId);
|
|
5455
6096
|
if (report) {
|
|
5456
6097
|
reports.push({ reportId, report });
|
|
@@ -5472,13 +6113,13 @@ var ReportManager = class {
|
|
|
5472
6113
|
*/
|
|
5473
6114
|
updateReport(reportId, report) {
|
|
5474
6115
|
const reportPath = this.getReportPath(reportId);
|
|
5475
|
-
if (!
|
|
6116
|
+
if (!import_fs6.default.existsSync(reportPath)) {
|
|
5476
6117
|
logger.warn(`Report not found for update: ${reportId}`);
|
|
5477
6118
|
return null;
|
|
5478
6119
|
}
|
|
5479
6120
|
try {
|
|
5480
6121
|
const validated = DSLRendererPropsSchema2.parse(report);
|
|
5481
|
-
|
|
6122
|
+
import_fs6.default.writeFileSync(reportPath, JSON.stringify(validated, null, 4));
|
|
5482
6123
|
logger.info(`Report updated: ${reportId}`);
|
|
5483
6124
|
return validated;
|
|
5484
6125
|
} catch (error) {
|
|
@@ -5494,12 +6135,12 @@ var ReportManager = class {
|
|
|
5494
6135
|
deleteReport(reportId) {
|
|
5495
6136
|
const reportPath = this.getReportPath(reportId);
|
|
5496
6137
|
const reportDir = import_path5.default.dirname(reportPath);
|
|
5497
|
-
if (!
|
|
6138
|
+
if (!import_fs6.default.existsSync(reportPath)) {
|
|
5498
6139
|
logger.warn(`Report not found for deletion: ${reportId}`);
|
|
5499
6140
|
return false;
|
|
5500
6141
|
}
|
|
5501
6142
|
try {
|
|
5502
|
-
|
|
6143
|
+
import_fs6.default.rmSync(reportDir, { recursive: true, force: true });
|
|
5503
6144
|
logger.info(`Report deleted: ${reportId}`);
|
|
5504
6145
|
return true;
|
|
5505
6146
|
} catch (error) {
|
|
@@ -5514,21 +6155,21 @@ var ReportManager = class {
|
|
|
5514
6155
|
*/
|
|
5515
6156
|
reportExists(reportId) {
|
|
5516
6157
|
const reportPath = this.getReportPath(reportId);
|
|
5517
|
-
return
|
|
6158
|
+
return import_fs6.default.existsSync(reportPath);
|
|
5518
6159
|
}
|
|
5519
6160
|
/**
|
|
5520
6161
|
* Get report count
|
|
5521
6162
|
* @returns Number of reports
|
|
5522
6163
|
*/
|
|
5523
6164
|
getReportCount() {
|
|
5524
|
-
if (!
|
|
6165
|
+
if (!import_fs6.default.existsSync(this.reportsBasePath)) {
|
|
5525
6166
|
return 0;
|
|
5526
6167
|
}
|
|
5527
6168
|
try {
|
|
5528
|
-
const reportDirs =
|
|
6169
|
+
const reportDirs = import_fs6.default.readdirSync(this.reportsBasePath);
|
|
5529
6170
|
return reportDirs.filter((dir) => {
|
|
5530
6171
|
const reportPath = this.getReportPath(dir);
|
|
5531
|
-
return
|
|
6172
|
+
return import_fs6.default.existsSync(reportPath);
|
|
5532
6173
|
}).length;
|
|
5533
6174
|
} catch (error) {
|
|
5534
6175
|
logger.error("Failed to get report count:", error);
|
|
@@ -5752,9 +6393,6 @@ var SuperatomSDK = class {
|
|
|
5752
6393
|
});
|
|
5753
6394
|
this.initializeDashboardManager();
|
|
5754
6395
|
this.initializeReportManager();
|
|
5755
|
-
this.connect().catch((error) => {
|
|
5756
|
-
logger.error("Failed to connect to Superatom:", error);
|
|
5757
|
-
});
|
|
5758
6396
|
}
|
|
5759
6397
|
/**
|
|
5760
6398
|
* Initialize PromptLoader and load prompts into memory
|
|
@@ -5820,6 +6458,10 @@ var SuperatomSDK = class {
|
|
|
5820
6458
|
* Connect to the Superatom WebSocket service
|
|
5821
6459
|
*/
|
|
5822
6460
|
async connect() {
|
|
6461
|
+
if (this.connected && this.ws && this.ws.readyState === this.ws.OPEN) {
|
|
6462
|
+
logger.info("Already connected to WebSocket");
|
|
6463
|
+
return Promise.resolve();
|
|
6464
|
+
}
|
|
5823
6465
|
return new Promise((resolve, reject) => {
|
|
5824
6466
|
try {
|
|
5825
6467
|
const url = new URL(this.url);
|
|
@@ -5882,7 +6524,7 @@ var SuperatomSDK = class {
|
|
|
5882
6524
|
});
|
|
5883
6525
|
break;
|
|
5884
6526
|
case "USER_PROMPT_REQ":
|
|
5885
|
-
handleUserPromptRequest(parsed, this.components, (msg) => this.send(msg), this.anthropicApiKey, this.groqApiKey, this.llmProviders).catch((error) => {
|
|
6527
|
+
handleUserPromptRequest(parsed, this.components, (msg) => this.send(msg), this.anthropicApiKey, this.groqApiKey, this.llmProviders, this.collections).catch((error) => {
|
|
5886
6528
|
logger.error("Failed to handle user prompt request:", error);
|
|
5887
6529
|
});
|
|
5888
6530
|
break;
|