@superatomai/sdk-node 0.0.4 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +324 -57
- package/dist/index.d.mts +159 -11
- package/dist/index.d.ts +159 -11
- package/dist/index.js +1726 -472
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1712 -455
- 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,128 @@ var require_main = __commonJS({
|
|
|
428
431
|
}
|
|
429
432
|
});
|
|
430
433
|
|
|
434
|
+
// src/userResponse/utils.ts
|
|
435
|
+
var utils_exports = {};
|
|
436
|
+
__export(utils_exports, {
|
|
437
|
+
convertTopToLimit: () => convertTopToLimit,
|
|
438
|
+
ensureQueryLimit: () => ensureQueryLimit,
|
|
439
|
+
fixScalarSubqueries: () => fixScalarSubqueries,
|
|
440
|
+
getJsonSizeInBytes: () => getJsonSizeInBytes,
|
|
441
|
+
validateMessageSize: () => validateMessageSize
|
|
442
|
+
});
|
|
443
|
+
function convertTopToLimit(query) {
|
|
444
|
+
if (!query || query.trim().length === 0) {
|
|
445
|
+
return query;
|
|
446
|
+
}
|
|
447
|
+
let modifiedQuery = query.replace(/\bSELECT\s+TOP\s+(\d+)\b/gi, "SELECT");
|
|
448
|
+
if (modifiedQuery !== query) {
|
|
449
|
+
console.warn(`\u26A0\uFE0F Query had TOP syntax. Converting to LIMIT for Snowflake compatibility.`);
|
|
450
|
+
}
|
|
451
|
+
return modifiedQuery;
|
|
452
|
+
}
|
|
453
|
+
function ensureQueryLimit(query, defaultLimit = 32, maxLimit = 32) {
|
|
454
|
+
if (!query || query.trim().length === 0) {
|
|
455
|
+
return query;
|
|
456
|
+
}
|
|
457
|
+
let trimmedQuery = query.trim();
|
|
458
|
+
const isSelectQuery = /^\s*SELECT\b/i.test(trimmedQuery) || /^\s*WITH\b.*\bSELECT\b/is.test(trimmedQuery);
|
|
459
|
+
if (!isSelectQuery) {
|
|
460
|
+
return query;
|
|
461
|
+
}
|
|
462
|
+
trimmedQuery = convertTopToLimit(trimmedQuery);
|
|
463
|
+
const hadSemicolon = trimmedQuery.endsWith(";");
|
|
464
|
+
if (hadSemicolon) {
|
|
465
|
+
trimmedQuery = trimmedQuery.slice(0, -1).trim();
|
|
466
|
+
}
|
|
467
|
+
const limitMatches = trimmedQuery.match(/\bLIMIT\s+(\d+)\b/gi);
|
|
468
|
+
if (limitMatches && limitMatches.length > 0) {
|
|
469
|
+
if (limitMatches.length > 1) {
|
|
470
|
+
console.warn(`\u26A0\uFE0F Query had ${limitMatches.length} LIMIT clauses. Removing duplicates...`);
|
|
471
|
+
trimmedQuery = trimmedQuery.replace(/\s*\bLIMIT\s+\d+\b/gi, "").trim();
|
|
472
|
+
} else {
|
|
473
|
+
const existingLimitMatch = trimmedQuery.match(/\bLIMIT\s+(\d+)\b/i);
|
|
474
|
+
if (existingLimitMatch) {
|
|
475
|
+
const existingLimit = parseInt(existingLimitMatch[1], 10);
|
|
476
|
+
if (existingLimit <= maxLimit) {
|
|
477
|
+
if (hadSemicolon) {
|
|
478
|
+
trimmedQuery += ";";
|
|
479
|
+
}
|
|
480
|
+
return trimmedQuery;
|
|
481
|
+
}
|
|
482
|
+
console.warn(`\u26A0\uFE0F Query LIMIT ${existingLimit} exceeds maximum of ${maxLimit}. Reducing to ${maxLimit}...`);
|
|
483
|
+
trimmedQuery = trimmedQuery.replace(/\bLIMIT\s+\d+\b/i, `LIMIT ${maxLimit}`);
|
|
484
|
+
if (hadSemicolon) {
|
|
485
|
+
trimmedQuery += ";";
|
|
486
|
+
}
|
|
487
|
+
return trimmedQuery;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
trimmedQuery = `${trimmedQuery} LIMIT ${defaultLimit}`;
|
|
492
|
+
if (hadSemicolon) {
|
|
493
|
+
trimmedQuery += ";";
|
|
494
|
+
}
|
|
495
|
+
return trimmedQuery;
|
|
496
|
+
}
|
|
497
|
+
function fixScalarSubqueries(query) {
|
|
498
|
+
if (!query || query.trim().length === 0) {
|
|
499
|
+
return query;
|
|
500
|
+
}
|
|
501
|
+
let modifiedQuery = query;
|
|
502
|
+
let hasChanges = false;
|
|
503
|
+
const scalarOperatorPattern = /([=<>!]=?|<>)\s*\(\s*SELECT\s/gi;
|
|
504
|
+
const matches = [...modifiedQuery.matchAll(scalarOperatorPattern)];
|
|
505
|
+
for (let i = matches.length - 1; i >= 0; i--) {
|
|
506
|
+
const match = matches[i];
|
|
507
|
+
const startPos = match.index + match[0].length - "SELECT ".length;
|
|
508
|
+
let parenDepth = 1;
|
|
509
|
+
let endPos = startPos;
|
|
510
|
+
let foundEnd = false;
|
|
511
|
+
for (let j = startPos; j < modifiedQuery.length; j++) {
|
|
512
|
+
const char = modifiedQuery[j];
|
|
513
|
+
if (char === "(") parenDepth++;
|
|
514
|
+
if (char === ")") {
|
|
515
|
+
parenDepth--;
|
|
516
|
+
if (parenDepth === 0) {
|
|
517
|
+
endPos = j;
|
|
518
|
+
foundEnd = true;
|
|
519
|
+
break;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
if (!foundEnd) continue;
|
|
524
|
+
const subquery = modifiedQuery.substring(startPos, endPos);
|
|
525
|
+
if (/\bLIMIT\s+\d+/i.test(subquery)) {
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
528
|
+
const fixedSubquery = subquery.trim() + " LIMIT 1";
|
|
529
|
+
modifiedQuery = modifiedQuery.substring(0, startPos) + fixedSubquery + modifiedQuery.substring(endPos);
|
|
530
|
+
hasChanges = true;
|
|
531
|
+
console.warn(`\u26A0\uFE0F Fixed scalar subquery: added LIMIT 1 to prevent multiple row error`);
|
|
532
|
+
}
|
|
533
|
+
if (hasChanges) {
|
|
534
|
+
console.log("\u2713 Query validated and fixed for PostgreSQL scalar subquery compatibility");
|
|
535
|
+
}
|
|
536
|
+
return modifiedQuery;
|
|
537
|
+
}
|
|
538
|
+
function getJsonSizeInBytes(obj) {
|
|
539
|
+
const jsonString = JSON.stringify(obj);
|
|
540
|
+
return Buffer.byteLength(jsonString, "utf8");
|
|
541
|
+
}
|
|
542
|
+
function validateMessageSize(message, maxSize = 1048576) {
|
|
543
|
+
const size = getJsonSizeInBytes(message);
|
|
544
|
+
return {
|
|
545
|
+
isValid: size <= maxSize,
|
|
546
|
+
size,
|
|
547
|
+
maxSize
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
var init_utils = __esm({
|
|
551
|
+
"src/userResponse/utils.ts"() {
|
|
552
|
+
"use strict";
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
|
|
431
556
|
// src/index.ts
|
|
432
557
|
var index_exports = {};
|
|
433
558
|
__export(index_exports, {
|
|
@@ -441,7 +566,8 @@ __export(index_exports, {
|
|
|
441
566
|
ThreadManager: () => ThreadManager,
|
|
442
567
|
UIBlock: () => UIBlock,
|
|
443
568
|
UILogCollector: () => UILogCollector,
|
|
444
|
-
UserManager: () => UserManager
|
|
569
|
+
UserManager: () => UserManager,
|
|
570
|
+
logger: () => logger
|
|
445
571
|
});
|
|
446
572
|
module.exports = __toCommonJS(index_exports);
|
|
447
573
|
|
|
@@ -641,6 +767,18 @@ var DSLRendererPropsSchema2 = import_zod2.z.object({
|
|
|
641
767
|
});
|
|
642
768
|
|
|
643
769
|
// src/types.ts
|
|
770
|
+
var UserSchema = import_zod3.z.object({
|
|
771
|
+
username: import_zod3.z.string().min(1, "Username is required"),
|
|
772
|
+
email: import_zod3.z.string().email("Invalid email format").optional(),
|
|
773
|
+
password: import_zod3.z.string().min(1, "Password is required"),
|
|
774
|
+
fullname: import_zod3.z.string().optional(),
|
|
775
|
+
role: import_zod3.z.string().optional(),
|
|
776
|
+
wsIds: import_zod3.z.array(import_zod3.z.string()).optional()
|
|
777
|
+
// Only in memory, not persisted to file
|
|
778
|
+
});
|
|
779
|
+
var UsersDataSchema = import_zod3.z.object({
|
|
780
|
+
users: import_zod3.z.array(UserSchema)
|
|
781
|
+
});
|
|
644
782
|
var MessageParticipantSchema = import_zod3.z.object({
|
|
645
783
|
id: import_zod3.z.string().optional(),
|
|
646
784
|
type: import_zod3.z.string().optional()
|
|
@@ -694,7 +832,8 @@ var UserPromptRequestPayloadSchema = import_zod3.z.object({
|
|
|
694
832
|
SA_RUNTIME: import_zod3.z.object({
|
|
695
833
|
threadId: import_zod3.z.string(),
|
|
696
834
|
uiBlockId: import_zod3.z.string()
|
|
697
|
-
}).optional()
|
|
835
|
+
}).optional(),
|
|
836
|
+
responseMode: import_zod3.z.enum(["component", "text"]).optional()
|
|
698
837
|
});
|
|
699
838
|
var UserPromptRequestMessageSchema = import_zod3.z.object({
|
|
700
839
|
id: import_zod3.z.string(),
|
|
@@ -741,7 +880,10 @@ var UsersRequestPayloadSchema = import_zod3.z.object({
|
|
|
741
880
|
operation: import_zod3.z.enum(["create", "update", "delete", "getAll", "getOne"]),
|
|
742
881
|
data: import_zod3.z.object({
|
|
743
882
|
username: import_zod3.z.string().optional(),
|
|
744
|
-
|
|
883
|
+
email: import_zod3.z.string().email("Invalid email format").optional(),
|
|
884
|
+
password: import_zod3.z.string().optional(),
|
|
885
|
+
fullname: import_zod3.z.string().optional(),
|
|
886
|
+
role: import_zod3.z.string().optional()
|
|
745
887
|
}).optional()
|
|
746
888
|
});
|
|
747
889
|
var UsersRequestMessageSchema = import_zod3.z.object({
|
|
@@ -806,21 +948,97 @@ var ReportsRequestMessageSchema = import_zod3.z.object({
|
|
|
806
948
|
});
|
|
807
949
|
|
|
808
950
|
// src/utils/logger.ts
|
|
951
|
+
var import_fs = __toESM(require("fs"));
|
|
809
952
|
var PREFIX = "[SuperatomSDK]";
|
|
810
|
-
var
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
953
|
+
var LOGSTREAM = import_fs.default.createWriteStream("superatom-sdk.log", { flags: "a" });
|
|
954
|
+
var LOG_LEVEL_PRIORITY = {
|
|
955
|
+
errors: 0,
|
|
956
|
+
warnings: 1,
|
|
957
|
+
info: 2,
|
|
958
|
+
verbose: 3
|
|
959
|
+
};
|
|
960
|
+
var MESSAGE_LEVEL_PRIORITY = {
|
|
961
|
+
error: 0,
|
|
962
|
+
warn: 1,
|
|
963
|
+
info: 2,
|
|
964
|
+
debug: 3
|
|
965
|
+
};
|
|
966
|
+
var Logger = class {
|
|
967
|
+
constructor() {
|
|
968
|
+
const envLevel = (process.env.SUPERATOM_LOG_LEVEL || "info").toLowerCase();
|
|
969
|
+
if (this.isValidLogLevel(envLevel)) {
|
|
970
|
+
this.currentLevel = envLevel;
|
|
971
|
+
} else {
|
|
972
|
+
this.currentLevel = "info";
|
|
973
|
+
console.warn(
|
|
974
|
+
`${PREFIX} Invalid log level "${envLevel}". Using default "info". Valid levels: errors, warnings, info, verbose`
|
|
975
|
+
);
|
|
976
|
+
}
|
|
977
|
+
this.currentLevelPriority = LOG_LEVEL_PRIORITY[this.currentLevel];
|
|
978
|
+
}
|
|
979
|
+
/**
|
|
980
|
+
* Check if a string is a valid log level
|
|
981
|
+
*/
|
|
982
|
+
isValidLogLevel(level) {
|
|
983
|
+
return level === "errors" || level === "warnings" || level === "info" || level === "verbose";
|
|
984
|
+
}
|
|
985
|
+
/**
|
|
986
|
+
* Check if a message should be logged based on current log level
|
|
987
|
+
*/
|
|
988
|
+
shouldLog(messageLevel) {
|
|
989
|
+
const messagePriority = MESSAGE_LEVEL_PRIORITY[messageLevel];
|
|
990
|
+
return messagePriority <= this.currentLevelPriority;
|
|
991
|
+
}
|
|
992
|
+
/**
|
|
993
|
+
* Get current log level
|
|
994
|
+
*/
|
|
995
|
+
getLogLevel() {
|
|
996
|
+
return this.currentLevel;
|
|
997
|
+
}
|
|
998
|
+
/**
|
|
999
|
+
* Set log level programmatically
|
|
1000
|
+
*/
|
|
1001
|
+
setLogLevel(level) {
|
|
1002
|
+
this.currentLevel = level;
|
|
1003
|
+
this.currentLevelPriority = LOG_LEVEL_PRIORITY[level];
|
|
1004
|
+
}
|
|
1005
|
+
/**
|
|
1006
|
+
* Log info message (shown for info and verbose levels)
|
|
1007
|
+
*/
|
|
1008
|
+
info(...args) {
|
|
1009
|
+
if (this.shouldLog("info")) {
|
|
1010
|
+
console.log(PREFIX, ...args);
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
/**
|
|
1014
|
+
* Log error message (shown for all levels)
|
|
1015
|
+
*/
|
|
1016
|
+
error(...args) {
|
|
1017
|
+
if (this.shouldLog("error")) {
|
|
1018
|
+
console.error(PREFIX, ...args);
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
/**
|
|
1022
|
+
* Log warning message (shown for warnings, info, and verbose levels)
|
|
1023
|
+
*/
|
|
1024
|
+
warn(...args) {
|
|
1025
|
+
if (this.shouldLog("warn")) {
|
|
1026
|
+
console.warn(PREFIX, ...args);
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
/**
|
|
1030
|
+
* Log debug message (only shown for verbose level)
|
|
1031
|
+
*/
|
|
1032
|
+
debug(...args) {
|
|
1033
|
+
if (this.shouldLog("debug")) {
|
|
1034
|
+
console.log(PREFIX, "[DEBUG]", ...args);
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
file(...args) {
|
|
1038
|
+
LOGSTREAM.write(args.join(" ") + "\n");
|
|
822
1039
|
}
|
|
823
1040
|
};
|
|
1041
|
+
var logger = new Logger();
|
|
824
1042
|
|
|
825
1043
|
// src/threads/uiblock.ts
|
|
826
1044
|
var import_crypto = require("crypto");
|
|
@@ -855,13 +1073,15 @@ var UIBlock = class {
|
|
|
855
1073
|
* @param generatedComponentMetadata - Optional metadata about the generated component
|
|
856
1074
|
* @param actions - Optional array of available actions
|
|
857
1075
|
* @param id - Optional custom ID, generates UUID if not provided
|
|
1076
|
+
* @param textResponse - Optional text response from LLM
|
|
858
1077
|
*/
|
|
859
|
-
constructor(userQuestion, componentData = {}, generatedComponentMetadata = {}, actions = [], id) {
|
|
1078
|
+
constructor(userQuestion, componentData = {}, generatedComponentMetadata = {}, actions = [], id, textResponse = null) {
|
|
860
1079
|
this.id = id || (0, import_crypto.randomUUID)();
|
|
861
1080
|
this.userQuestion = userQuestion;
|
|
862
1081
|
this.componentData = componentData;
|
|
863
1082
|
this.generatedComponentMetadata = generatedComponentMetadata;
|
|
864
1083
|
this.actions = actions;
|
|
1084
|
+
this.textResponse = textResponse;
|
|
865
1085
|
this.createdAt = /* @__PURE__ */ new Date();
|
|
866
1086
|
}
|
|
867
1087
|
/**
|
|
@@ -888,6 +1108,9 @@ var UIBlock = class {
|
|
|
888
1108
|
getComponentMetadata() {
|
|
889
1109
|
return this.generatedComponentMetadata;
|
|
890
1110
|
}
|
|
1111
|
+
getTextResponse() {
|
|
1112
|
+
return this.textResponse || "";
|
|
1113
|
+
}
|
|
891
1114
|
/**
|
|
892
1115
|
* Set or update component metadata
|
|
893
1116
|
*/
|
|
@@ -980,6 +1203,12 @@ var UIBlock = class {
|
|
|
980
1203
|
const processedData = this.processDataForStorage(data);
|
|
981
1204
|
this.componentData = { ...this.componentData, ...processedData };
|
|
982
1205
|
}
|
|
1206
|
+
/**
|
|
1207
|
+
* Set or update text response
|
|
1208
|
+
*/
|
|
1209
|
+
setTextResponse(textResponse) {
|
|
1210
|
+
this.textResponse = textResponse;
|
|
1211
|
+
}
|
|
983
1212
|
/**
|
|
984
1213
|
* Get all actions (only if they are resolved, not if fetching)
|
|
985
1214
|
*/
|
|
@@ -1063,6 +1292,7 @@ var UIBlock = class {
|
|
|
1063
1292
|
userQuestion: this.userQuestion,
|
|
1064
1293
|
generatedComponentMetadata: this.generatedComponentMetadata,
|
|
1065
1294
|
componentData: this.componentData,
|
|
1295
|
+
textResponse: this.textResponse,
|
|
1066
1296
|
actions: actionsValue,
|
|
1067
1297
|
isFetchingActions: this.actions instanceof Promise,
|
|
1068
1298
|
createdAt: this.createdAt.toISOString()
|
|
@@ -1163,8 +1393,11 @@ var Thread = class {
|
|
|
1163
1393
|
const questionNum = index + 1;
|
|
1164
1394
|
const question = block.getUserQuestion();
|
|
1165
1395
|
const metadata = block.getComponentMetadata();
|
|
1166
|
-
|
|
1167
|
-
|
|
1396
|
+
const textResponse = block.getTextResponse();
|
|
1397
|
+
let assistantResponse = "";
|
|
1398
|
+
const hasComponent = metadata && Object.keys(metadata).length > 0 && metadata.type;
|
|
1399
|
+
const hasTextResponse = textResponse && textResponse.trim().length > 0;
|
|
1400
|
+
if (hasComponent) {
|
|
1168
1401
|
const parts = [];
|
|
1169
1402
|
if (metadata.type) {
|
|
1170
1403
|
parts.push(`Component Type: ${metadata.type}`);
|
|
@@ -1184,11 +1417,17 @@ var Thread = class {
|
|
|
1184
1417
|
const componentTypes = metadata.props.config.components.map((c) => c.type).join(", ");
|
|
1185
1418
|
parts.push(`Multi-component with: ${componentTypes}`);
|
|
1186
1419
|
}
|
|
1187
|
-
|
|
1420
|
+
assistantResponse = parts.join(", ");
|
|
1421
|
+
} else if (hasTextResponse) {
|
|
1422
|
+
assistantResponse = textResponse;
|
|
1423
|
+
} else {
|
|
1424
|
+
assistantResponse = "No response generated";
|
|
1188
1425
|
}
|
|
1189
|
-
contextLines.push(`
|
|
1190
|
-
|
|
1191
|
-
contextLines.push(
|
|
1426
|
+
contextLines.push(`User:
|
|
1427
|
+
${question}`);
|
|
1428
|
+
contextLines.push(`Assistant:
|
|
1429
|
+
${assistantResponse}`);
|
|
1430
|
+
contextLines.push("---");
|
|
1192
1431
|
});
|
|
1193
1432
|
return contextLines.join("\n").trim();
|
|
1194
1433
|
}
|
|
@@ -1364,7 +1603,7 @@ function sendDataResponse(id, collection, op, data, meta, sendMessage) {
|
|
|
1364
1603
|
}
|
|
1365
1604
|
|
|
1366
1605
|
// src/bundle.ts
|
|
1367
|
-
var
|
|
1606
|
+
var fs2 = __toESM(require("fs"));
|
|
1368
1607
|
var path = __toESM(require("path"));
|
|
1369
1608
|
function getBundleDir(configDir) {
|
|
1370
1609
|
const bundleDir = configDir || process.env.SA_BUNDLE_DIR;
|
|
@@ -1377,16 +1616,16 @@ function getBundleDir(configDir) {
|
|
|
1377
1616
|
}
|
|
1378
1617
|
function getJS(bundleDir) {
|
|
1379
1618
|
try {
|
|
1380
|
-
if (!
|
|
1619
|
+
if (!fs2.existsSync(bundleDir)) {
|
|
1381
1620
|
throw new Error(`Bundle directory does not exist: ${bundleDir}`);
|
|
1382
1621
|
}
|
|
1383
|
-
const stats =
|
|
1622
|
+
const stats = fs2.statSync(bundleDir);
|
|
1384
1623
|
if (!stats.isDirectory()) {
|
|
1385
1624
|
throw new Error(`Bundle path is not a directory: ${bundleDir}`);
|
|
1386
1625
|
}
|
|
1387
1626
|
let files;
|
|
1388
1627
|
try {
|
|
1389
|
-
files =
|
|
1628
|
+
files = fs2.readdirSync(bundleDir);
|
|
1390
1629
|
} catch (error) {
|
|
1391
1630
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1392
1631
|
throw new Error(`Failed to read bundle directory: ${errorMessage}`);
|
|
@@ -1401,7 +1640,7 @@ function getJS(bundleDir) {
|
|
|
1401
1640
|
const filePath = path.join(bundleDir, indexFile);
|
|
1402
1641
|
logger.info(`Loading bundle from ${filePath}`);
|
|
1403
1642
|
try {
|
|
1404
|
-
return
|
|
1643
|
+
return fs2.readFileSync(filePath, "utf8");
|
|
1405
1644
|
} catch (error) {
|
|
1406
1645
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1407
1646
|
throw new Error(`Failed to read bundle file: ${errorMessage}`);
|
|
@@ -1497,9 +1736,9 @@ function getUserManager() {
|
|
|
1497
1736
|
}
|
|
1498
1737
|
return currentUserManager;
|
|
1499
1738
|
}
|
|
1500
|
-
function
|
|
1739
|
+
function findUserByUsernameOrEmail(identifier) {
|
|
1501
1740
|
const manager = getUserManager();
|
|
1502
|
-
const user = manager.
|
|
1741
|
+
const user = manager.getUserByUsernameOrEmail(identifier);
|
|
1503
1742
|
return user || null;
|
|
1504
1743
|
}
|
|
1505
1744
|
function addWsIdToUser(username, wsId) {
|
|
@@ -1521,60 +1760,81 @@ async function cleanupUserStorage() {
|
|
|
1521
1760
|
|
|
1522
1761
|
// src/auth/validator.ts
|
|
1523
1762
|
function validateUser(credentials) {
|
|
1524
|
-
const { username, password } = credentials;
|
|
1525
|
-
|
|
1526
|
-
|
|
1763
|
+
const { username, email, password } = credentials;
|
|
1764
|
+
const identifier = username || email;
|
|
1765
|
+
logger.debug("[validateUser] Starting user validation");
|
|
1766
|
+
logger.debug(`[validateUser] Username provided: ${username ? "\u2713" : "\u2717"}, Email provided: ${email ? "\u2713" : "\u2717"}, Password provided: ${password ? "\u2713" : "\u2717"}`);
|
|
1767
|
+
if (!identifier || !password) {
|
|
1768
|
+
logger.warn("[validateUser] Validation failed: Username/email and password are required");
|
|
1527
1769
|
return {
|
|
1528
1770
|
success: false,
|
|
1529
|
-
error: "Username and password are required"
|
|
1771
|
+
error: "Username or email and password are required"
|
|
1530
1772
|
};
|
|
1531
1773
|
}
|
|
1532
|
-
|
|
1774
|
+
logger.debug(`[validateUser] Looking up user by identifier: ${identifier}`);
|
|
1775
|
+
const user = findUserByUsernameOrEmail(identifier);
|
|
1533
1776
|
if (!user) {
|
|
1534
|
-
logger.warn(`Validation failed: User not found - ${
|
|
1777
|
+
logger.warn(`[validateUser] Validation failed: User not found - ${identifier}`);
|
|
1535
1778
|
return {
|
|
1536
1779
|
success: false,
|
|
1537
|
-
error: "Invalid username"
|
|
1780
|
+
error: "Invalid username or email"
|
|
1538
1781
|
};
|
|
1539
1782
|
}
|
|
1783
|
+
logger.debug(`[validateUser] User found: ${user.username}, verifying password`);
|
|
1540
1784
|
const hashedPassword = hashPassword(user.password);
|
|
1541
1785
|
if (hashedPassword !== password) {
|
|
1542
|
-
logger.warn(`Validation failed: Invalid password for user - ${username}`);
|
|
1786
|
+
logger.warn(`[validateUser] Validation failed: Invalid password for user - ${user.username}`);
|
|
1787
|
+
logger.debug(`[validateUser] Password hash mismatch for user: ${user.username}`);
|
|
1543
1788
|
return {
|
|
1544
1789
|
success: false,
|
|
1545
1790
|
error: "Invalid password"
|
|
1546
1791
|
};
|
|
1547
1792
|
}
|
|
1548
|
-
logger.
|
|
1793
|
+
logger.info(`[validateUser] \u2713 User validated successfully: ${user.username}`);
|
|
1794
|
+
logger.debug(`[validateUser] Returning user data for: ${user.username}`);
|
|
1549
1795
|
return {
|
|
1550
1796
|
success: true,
|
|
1551
|
-
data: user.username
|
|
1797
|
+
data: user.username,
|
|
1798
|
+
username: user.username
|
|
1552
1799
|
};
|
|
1553
1800
|
}
|
|
1554
1801
|
function authenticateAndStoreWsId(credentials, wsId) {
|
|
1802
|
+
const identifier = credentials.username || credentials.email;
|
|
1803
|
+
logger.debug("[authenticateAndStoreWsId] Starting authentication and WebSocket ID storage");
|
|
1804
|
+
logger.debug("[authenticateAndStoreWsId] Validating user credentials");
|
|
1555
1805
|
const validationResult = validateUser(credentials);
|
|
1556
1806
|
if (!validationResult.success) {
|
|
1807
|
+
logger.warn(`[authenticateAndStoreWsId] User validation failed for: ${identifier}`);
|
|
1557
1808
|
return validationResult;
|
|
1558
1809
|
}
|
|
1559
|
-
const
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
error: "Failed to store user session"
|
|
1565
|
-
};
|
|
1566
|
-
}
|
|
1567
|
-
logger.info(`WebSocket ID stored for user: ${credentials.username}`);
|
|
1810
|
+
const username = validationResult.username;
|
|
1811
|
+
logger.info(`[authenticateAndStoreWsId] User ${username} validated, storing WebSocket ID`);
|
|
1812
|
+
logger.debug(`[authenticateAndStoreWsId] Calling addWsIdToUser for ${username}`);
|
|
1813
|
+
addWsIdToUser(username, wsId);
|
|
1814
|
+
logger.debug(`[authenticateAndStoreWsId] WebSocket ID ${wsId} associated with user ${username}`);
|
|
1568
1815
|
return validationResult;
|
|
1569
1816
|
}
|
|
1570
1817
|
function verifyAuthToken(authToken) {
|
|
1571
1818
|
try {
|
|
1819
|
+
logger.debug("[verifyAuthToken] Starting token verification");
|
|
1820
|
+
logger.debug("[verifyAuthToken] Decoding base64 token");
|
|
1572
1821
|
const decodedString = Buffer.from(authToken, "base64").toString("utf-8");
|
|
1822
|
+
logger.debug("[verifyAuthToken] Parsing decoded token as JSON");
|
|
1573
1823
|
const credentials = JSON.parse(decodedString);
|
|
1574
|
-
logger.debug("Token decoded and parsed successfully");
|
|
1575
|
-
|
|
1824
|
+
logger.debug("[verifyAuthToken] Token decoded and parsed successfully");
|
|
1825
|
+
logger.debug(`[verifyAuthToken] Token contains username: ${credentials.username ? "\u2713" : "\u2717"}`);
|
|
1826
|
+
logger.debug("[verifyAuthToken] Validating credentials from token");
|
|
1827
|
+
const result = validateUser(credentials);
|
|
1828
|
+
if (result.success) {
|
|
1829
|
+
logger.info(`[verifyAuthToken] \u2713 Token verified successfully for user: ${credentials.username || "unknown"}`);
|
|
1830
|
+
} else {
|
|
1831
|
+
logger.warn(`[verifyAuthToken] Token verification failed: ${result.error}`);
|
|
1832
|
+
}
|
|
1833
|
+
return result;
|
|
1576
1834
|
} catch (error) {
|
|
1577
|
-
|
|
1835
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
1836
|
+
logger.error(`[verifyAuthToken] Failed to verify auth token: ${errorMsg}`);
|
|
1837
|
+
logger.debug("[verifyAuthToken] Token verification error details:", error);
|
|
1578
1838
|
return {
|
|
1579
1839
|
success: false,
|
|
1580
1840
|
error: "Invalid token format"
|
|
@@ -1585,36 +1845,49 @@ function verifyAuthToken(authToken) {
|
|
|
1585
1845
|
// src/handlers/auth-login-requests.ts
|
|
1586
1846
|
async function handleAuthLoginRequest(data, sendMessage) {
|
|
1587
1847
|
try {
|
|
1848
|
+
logger.debug("[AUTH_LOGIN_REQ] Parsing incoming auth login request");
|
|
1588
1849
|
const authRequest = AuthLoginRequestMessageSchema.parse(data);
|
|
1589
1850
|
const { id, payload } = authRequest;
|
|
1590
1851
|
const login_data = payload.login_data;
|
|
1591
1852
|
const wsId = authRequest.from.id;
|
|
1853
|
+
logger.info(`[AUTH_LOGIN_REQ ${id}] Processing auth login request from client: ${wsId}`);
|
|
1854
|
+
logger.debug(`[AUTH_LOGIN_REQ ${id}] Login data present: ${!!login_data}`);
|
|
1592
1855
|
if (!login_data) {
|
|
1856
|
+
logger.error(`[AUTH_LOGIN_REQ ${id}] Login data not found in request`);
|
|
1593
1857
|
sendDataResponse2(id, {
|
|
1594
1858
|
success: false,
|
|
1595
1859
|
error: "Login data not found"
|
|
1596
1860
|
}, sendMessage, wsId);
|
|
1597
1861
|
return;
|
|
1598
1862
|
}
|
|
1863
|
+
logger.debug(`[AUTH_LOGIN_REQ ${id}] Decoding base64 login data`);
|
|
1599
1864
|
let loginData;
|
|
1600
1865
|
try {
|
|
1601
1866
|
loginData = decodeBase64ToJson(login_data);
|
|
1867
|
+
logger.debug(`[AUTH_LOGIN_REQ ${id}] Login data decoded successfully`);
|
|
1602
1868
|
} catch (error) {
|
|
1869
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
1870
|
+
logger.error(`[AUTH_LOGIN_REQ ${id}] Failed to decode login data: ${errorMsg}`);
|
|
1871
|
+
logger.debug(`[AUTH_LOGIN_REQ ${id}] Decode error details:`, error);
|
|
1603
1872
|
sendDataResponse2(id, {
|
|
1604
1873
|
success: false,
|
|
1605
1874
|
error: "Invalid login data format"
|
|
1606
1875
|
}, sendMessage, wsId);
|
|
1607
1876
|
return;
|
|
1608
1877
|
}
|
|
1609
|
-
const { username, password } = loginData;
|
|
1610
|
-
|
|
1878
|
+
const { username, email, password } = loginData;
|
|
1879
|
+
const identifier = username || email;
|
|
1880
|
+
logger.debug(`[AUTH_LOGIN_REQ ${id}] Validating credentials - username: ${username ? "\u2713" : "\u2717"}, email: ${email ? "\u2713" : "\u2717"}, password: ${password ? "\u2713" : "\u2717"}`);
|
|
1881
|
+
if (!identifier) {
|
|
1882
|
+
logger.error(`[AUTH_LOGIN_REQ ${id}] Username or email not found in login data`);
|
|
1611
1883
|
sendDataResponse2(id, {
|
|
1612
1884
|
success: false,
|
|
1613
|
-
error: "Username
|
|
1885
|
+
error: "Username or email is required"
|
|
1614
1886
|
}, sendMessage, wsId);
|
|
1615
1887
|
return;
|
|
1616
1888
|
}
|
|
1617
1889
|
if (!password) {
|
|
1890
|
+
logger.error(`[AUTH_LOGIN_REQ ${id}] Password not found in login data`);
|
|
1618
1891
|
sendDataResponse2(id, {
|
|
1619
1892
|
success: false,
|
|
1620
1893
|
error: "Password not found in login data"
|
|
@@ -1622,20 +1895,46 @@ async function handleAuthLoginRequest(data, sendMessage) {
|
|
|
1622
1895
|
return;
|
|
1623
1896
|
}
|
|
1624
1897
|
if (!wsId) {
|
|
1898
|
+
logger.error(`[AUTH_LOGIN_REQ ${id}] WebSocket ID not found in request`);
|
|
1625
1899
|
sendDataResponse2(id, {
|
|
1626
1900
|
success: false,
|
|
1627
1901
|
error: "WebSocket ID not found"
|
|
1628
1902
|
}, sendMessage, wsId);
|
|
1629
1903
|
return;
|
|
1630
1904
|
}
|
|
1905
|
+
logger.info(`[AUTH_LOGIN_REQ ${id}] Credentials validated, authenticating user: ${identifier}`);
|
|
1906
|
+
logger.debug(`[AUTH_LOGIN_REQ ${id}] WebSocket ID: ${wsId}`);
|
|
1907
|
+
logger.debug(`[AUTH_LOGIN_REQ ${id}] Calling authenticateAndStoreWsId for user: ${identifier}`);
|
|
1631
1908
|
const authResult = authenticateAndStoreWsId(
|
|
1632
|
-
{ username, password },
|
|
1909
|
+
{ username, email, password },
|
|
1633
1910
|
wsId
|
|
1634
1911
|
);
|
|
1912
|
+
logger.info(`[AUTH_LOGIN_REQ ${id}] Authentication result for ${identifier}: ${authResult.success ? "success" : "failed"}`);
|
|
1913
|
+
if (!authResult.success) {
|
|
1914
|
+
logger.warn(`[AUTH_LOGIN_REQ ${id}] Authentication failed for ${identifier}: ${authResult.error}`);
|
|
1915
|
+
} else {
|
|
1916
|
+
logger.info(`[AUTH_LOGIN_REQ ${id}] User ${authResult.username || identifier} authenticated successfully`);
|
|
1917
|
+
}
|
|
1918
|
+
logger.debug(`[AUTH_LOGIN_REQ ${id}] Sending auth response to client`);
|
|
1635
1919
|
sendDataResponse2(id, authResult, sendMessage, wsId);
|
|
1920
|
+
logger.info(`[AUTH_LOGIN_REQ ${id}] ${authResult.success ? "\u2713" : "\u2717"} Auth login request completed`);
|
|
1636
1921
|
return;
|
|
1637
1922
|
} catch (error) {
|
|
1638
|
-
|
|
1923
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
1924
|
+
const errorStack = error instanceof Error ? error.stack : void 0;
|
|
1925
|
+
logger.error(`[AUTH_LOGIN_REQ] Failed to handle auth login request: ${errorMessage}`);
|
|
1926
|
+
logger.debug(`[AUTH_LOGIN_REQ] Error stack trace:`, errorStack);
|
|
1927
|
+
try {
|
|
1928
|
+
const parsedData = data;
|
|
1929
|
+
if (parsedData?.id) {
|
|
1930
|
+
sendDataResponse2(parsedData.id, {
|
|
1931
|
+
success: false,
|
|
1932
|
+
error: `Internal error: ${errorMessage}`
|
|
1933
|
+
}, sendMessage, parsedData.from?.id);
|
|
1934
|
+
}
|
|
1935
|
+
} catch (sendError) {
|
|
1936
|
+
logger.error("[AUTH_LOGIN_REQ] Failed to send error response:", sendError);
|
|
1937
|
+
}
|
|
1639
1938
|
}
|
|
1640
1939
|
}
|
|
1641
1940
|
function sendDataResponse2(id, res, sendMessage, clientId) {
|
|
@@ -1651,17 +1950,27 @@ function sendDataResponse2(id, res, sendMessage, clientId) {
|
|
|
1651
1950
|
...res
|
|
1652
1951
|
}
|
|
1653
1952
|
};
|
|
1953
|
+
logger.debug(`[AUTH_LOGIN_RES ${id}] Sending ${res.success ? "successful" : "failed"} auth response to client: ${clientId}`);
|
|
1954
|
+
logger.debug(`[AUTH_LOGIN_RES ${id}] Response payload size: ${JSON.stringify(response).length} bytes`);
|
|
1955
|
+
if (res.error) {
|
|
1956
|
+
logger.debug(`[AUTH_LOGIN_RES ${id}] Error message: ${res.error}`);
|
|
1957
|
+
}
|
|
1654
1958
|
sendMessage(response);
|
|
1655
1959
|
}
|
|
1656
1960
|
|
|
1657
1961
|
// src/handlers/auth-verify-request.ts
|
|
1658
1962
|
async function handleAuthVerifyRequest(data, sendMessage) {
|
|
1659
1963
|
try {
|
|
1964
|
+
logger.debug("[AUTH_VERIFY_REQ] Parsing incoming auth verify request");
|
|
1660
1965
|
const authRequest = AuthVerifyRequestMessageSchema.parse(data);
|
|
1661
1966
|
const { id, payload } = authRequest;
|
|
1662
1967
|
const token = payload.token;
|
|
1663
1968
|
const wsId = authRequest.from.id;
|
|
1969
|
+
logger.info(`[AUTH_VERIFY_REQ ${id}] Processing auth verify request from client: ${wsId}`);
|
|
1970
|
+
logger.debug(`[AUTH_VERIFY_REQ ${id}] Token present: ${!!token}`);
|
|
1971
|
+
logger.debug(`[AUTH_VERIFY_REQ ${id}] Token length: ${token ? token.length : 0} characters`);
|
|
1664
1972
|
if (!token) {
|
|
1973
|
+
logger.error(`[AUTH_VERIFY_REQ ${id}] Token not found in request`);
|
|
1665
1974
|
sendDataResponse3(id, {
|
|
1666
1975
|
success: false,
|
|
1667
1976
|
error: "Token not found"
|
|
@@ -1669,17 +1978,45 @@ async function handleAuthVerifyRequest(data, sendMessage) {
|
|
|
1669
1978
|
return;
|
|
1670
1979
|
}
|
|
1671
1980
|
if (!wsId) {
|
|
1981
|
+
logger.error(`[AUTH_VERIFY_REQ ${id}] WebSocket ID not found in request`);
|
|
1672
1982
|
sendDataResponse3(id, {
|
|
1673
1983
|
success: false,
|
|
1674
1984
|
error: "WebSocket ID not found"
|
|
1675
1985
|
}, sendMessage, wsId);
|
|
1676
1986
|
return;
|
|
1677
1987
|
}
|
|
1988
|
+
logger.info(`[AUTH_VERIFY_REQ ${id}] Token validation starting`);
|
|
1989
|
+
logger.debug(`[AUTH_VERIFY_REQ ${id}] WebSocket ID: ${wsId}`);
|
|
1990
|
+
logger.debug(`[AUTH_VERIFY_REQ ${id}] Calling verifyAuthToken`);
|
|
1991
|
+
const startTime = Date.now();
|
|
1678
1992
|
const authResult = verifyAuthToken(token);
|
|
1993
|
+
const verificationTime = Date.now() - startTime;
|
|
1994
|
+
logger.info(`[AUTH_VERIFY_REQ ${id}] Token verification completed in ${verificationTime}ms - ${authResult.success ? "valid" : "invalid"}`);
|
|
1995
|
+
if (!authResult.success) {
|
|
1996
|
+
logger.warn(`[AUTH_VERIFY_REQ ${id}] Token verification failed: ${authResult.error}`);
|
|
1997
|
+
} else {
|
|
1998
|
+
logger.info(`[AUTH_VERIFY_REQ ${id}] Token verified successfully for user: ${authResult.data || "unknown"}`);
|
|
1999
|
+
}
|
|
2000
|
+
logger.debug(`[AUTH_VERIFY_REQ ${id}] Sending verification response to client`);
|
|
1679
2001
|
sendDataResponse3(id, authResult, sendMessage, wsId);
|
|
2002
|
+
logger.info(`[AUTH_VERIFY_REQ ${id}] ${authResult.success ? "\u2713" : "\u2717"} Auth verify request completed`);
|
|
1680
2003
|
return;
|
|
1681
2004
|
} catch (error) {
|
|
1682
|
-
|
|
2005
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
2006
|
+
const errorStack = error instanceof Error ? error.stack : void 0;
|
|
2007
|
+
logger.error(`[AUTH_VERIFY_REQ] Failed to handle auth verify request: ${errorMessage}`);
|
|
2008
|
+
logger.debug(`[AUTH_VERIFY_REQ] Error stack trace:`, errorStack);
|
|
2009
|
+
try {
|
|
2010
|
+
const parsedData = data;
|
|
2011
|
+
if (parsedData?.id) {
|
|
2012
|
+
sendDataResponse3(parsedData.id, {
|
|
2013
|
+
success: false,
|
|
2014
|
+
error: `Internal error: ${errorMessage}`
|
|
2015
|
+
}, sendMessage, parsedData.from?.id);
|
|
2016
|
+
}
|
|
2017
|
+
} catch (sendError) {
|
|
2018
|
+
logger.error("[AUTH_VERIFY_REQ] Failed to send error response:", sendError);
|
|
2019
|
+
}
|
|
1683
2020
|
}
|
|
1684
2021
|
}
|
|
1685
2022
|
function sendDataResponse3(id, res, sendMessage, clientId) {
|
|
@@ -1695,59 +2032,26 @@ function sendDataResponse3(id, res, sendMessage, clientId) {
|
|
|
1695
2032
|
...res
|
|
1696
2033
|
}
|
|
1697
2034
|
};
|
|
2035
|
+
logger.debug(`[AUTH_VERIFY_RES ${id}] Sending ${res.success ? "successful" : "failed"} verification response to client: ${clientId}`);
|
|
2036
|
+
logger.debug(`[AUTH_VERIFY_RES ${id}] Response payload size: ${JSON.stringify(response).length} bytes`);
|
|
2037
|
+
if (res.error) {
|
|
2038
|
+
logger.debug(`[AUTH_VERIFY_RES ${id}] Error message: ${res.error}`);
|
|
2039
|
+
}
|
|
2040
|
+
if (res.data) {
|
|
2041
|
+
logger.debug(`[AUTH_VERIFY_RES ${id}] User verified: ${res.data}`);
|
|
2042
|
+
}
|
|
1698
2043
|
sendMessage(response);
|
|
1699
2044
|
}
|
|
1700
2045
|
|
|
1701
2046
|
// src/userResponse/groq.ts
|
|
1702
2047
|
var import_dotenv = __toESM(require_main());
|
|
1703
2048
|
|
|
1704
|
-
// src/userResponse/
|
|
1705
|
-
|
|
1706
|
-
if (!query || query.trim().length === 0) {
|
|
1707
|
-
return query;
|
|
1708
|
-
}
|
|
1709
|
-
let modifiedQuery = query.replace(/\bSELECT\s+TOP\s+(\d+)\b/gi, "SELECT");
|
|
1710
|
-
if (modifiedQuery !== query) {
|
|
1711
|
-
console.warn(`\u26A0\uFE0F Query had TOP syntax. Converting to LIMIT for Snowflake compatibility.`);
|
|
1712
|
-
}
|
|
1713
|
-
return modifiedQuery;
|
|
1714
|
-
}
|
|
1715
|
-
function ensureQueryLimit(query, defaultLimit = 50) {
|
|
1716
|
-
if (!query || query.trim().length === 0) {
|
|
1717
|
-
return query;
|
|
1718
|
-
}
|
|
1719
|
-
let trimmedQuery = query.trim();
|
|
1720
|
-
const isSelectQuery = /^\s*SELECT\b/i.test(trimmedQuery) || /^\s*WITH\b.*\bSELECT\b/is.test(trimmedQuery);
|
|
1721
|
-
if (!isSelectQuery) {
|
|
1722
|
-
return query;
|
|
1723
|
-
}
|
|
1724
|
-
trimmedQuery = convertTopToLimit(trimmedQuery);
|
|
1725
|
-
const hadSemicolon = trimmedQuery.endsWith(";");
|
|
1726
|
-
if (hadSemicolon) {
|
|
1727
|
-
trimmedQuery = trimmedQuery.slice(0, -1).trim();
|
|
1728
|
-
}
|
|
1729
|
-
const limitMatches = trimmedQuery.match(/\bLIMIT\s+\d+\b/gi);
|
|
1730
|
-
if (limitMatches && limitMatches.length > 0) {
|
|
1731
|
-
if (limitMatches.length > 1) {
|
|
1732
|
-
console.warn(`\u26A0\uFE0F Query had ${limitMatches.length} LIMIT clauses. Removing duplicates...`);
|
|
1733
|
-
trimmedQuery = trimmedQuery.replace(/\s*\bLIMIT\s+\d+\b/gi, "").trim();
|
|
1734
|
-
} else {
|
|
1735
|
-
if (hadSemicolon) {
|
|
1736
|
-
trimmedQuery += ";";
|
|
1737
|
-
}
|
|
1738
|
-
return trimmedQuery;
|
|
1739
|
-
}
|
|
1740
|
-
}
|
|
1741
|
-
trimmedQuery = `${trimmedQuery} LIMIT ${defaultLimit}`;
|
|
1742
|
-
if (hadSemicolon) {
|
|
1743
|
-
trimmedQuery += ";";
|
|
1744
|
-
}
|
|
1745
|
-
return trimmedQuery;
|
|
1746
|
-
}
|
|
2049
|
+
// src/userResponse/base-llm.ts
|
|
2050
|
+
init_utils();
|
|
1747
2051
|
|
|
1748
2052
|
// src/userResponse/schema.ts
|
|
1749
2053
|
var import_path = __toESM(require("path"));
|
|
1750
|
-
var
|
|
2054
|
+
var import_fs2 = __toESM(require("fs"));
|
|
1751
2055
|
var Schema = class {
|
|
1752
2056
|
constructor(schemaFilePath) {
|
|
1753
2057
|
this.cachedSchema = null;
|
|
@@ -1761,11 +2065,11 @@ var Schema = class {
|
|
|
1761
2065
|
logger.info(`SCHEMA_FILE_PATH: ${this.schemaFilePath}`);
|
|
1762
2066
|
try {
|
|
1763
2067
|
const dir = import_path.default.dirname(this.schemaFilePath);
|
|
1764
|
-
if (!
|
|
2068
|
+
if (!import_fs2.default.existsSync(dir)) {
|
|
1765
2069
|
logger.info(`Creating directory structure: ${dir}`);
|
|
1766
|
-
|
|
2070
|
+
import_fs2.default.mkdirSync(dir, { recursive: true });
|
|
1767
2071
|
}
|
|
1768
|
-
if (!
|
|
2072
|
+
if (!import_fs2.default.existsSync(this.schemaFilePath)) {
|
|
1769
2073
|
logger.info(`Schema file does not exist at ${this.schemaFilePath}, creating with empty schema`);
|
|
1770
2074
|
const initialSchema = {
|
|
1771
2075
|
database: "",
|
|
@@ -1774,11 +2078,11 @@ var Schema = class {
|
|
|
1774
2078
|
tables: [],
|
|
1775
2079
|
relationships: []
|
|
1776
2080
|
};
|
|
1777
|
-
|
|
2081
|
+
import_fs2.default.writeFileSync(this.schemaFilePath, JSON.stringify(initialSchema, null, 4));
|
|
1778
2082
|
this.cachedSchema = initialSchema;
|
|
1779
2083
|
return initialSchema;
|
|
1780
2084
|
}
|
|
1781
|
-
const fileContent =
|
|
2085
|
+
const fileContent = import_fs2.default.readFileSync(this.schemaFilePath, "utf-8");
|
|
1782
2086
|
const schema2 = JSON.parse(fileContent);
|
|
1783
2087
|
this.cachedSchema = schema2;
|
|
1784
2088
|
return schema2;
|
|
@@ -1879,7 +2183,7 @@ var Schema = class {
|
|
|
1879
2183
|
var schema = new Schema();
|
|
1880
2184
|
|
|
1881
2185
|
// src/userResponse/prompt-loader.ts
|
|
1882
|
-
var
|
|
2186
|
+
var import_fs3 = __toESM(require("fs"));
|
|
1883
2187
|
var import_path2 = __toESM(require("path"));
|
|
1884
2188
|
var PromptLoader = class {
|
|
1885
2189
|
constructor(config) {
|
|
@@ -1906,7 +2210,9 @@ var PromptLoader = class {
|
|
|
1906
2210
|
"single-component",
|
|
1907
2211
|
"mutli-component",
|
|
1908
2212
|
"actions",
|
|
1909
|
-
"container-metadata"
|
|
2213
|
+
"container-metadata",
|
|
2214
|
+
"text-response",
|
|
2215
|
+
"match-text-components"
|
|
1910
2216
|
];
|
|
1911
2217
|
for (const promptType of promptTypes) {
|
|
1912
2218
|
try {
|
|
@@ -1931,9 +2237,9 @@ var PromptLoader = class {
|
|
|
1931
2237
|
try {
|
|
1932
2238
|
const systemPath = import_path2.default.join(dir, promptName, "system.md");
|
|
1933
2239
|
const userPath = import_path2.default.join(dir, promptName, "user.md");
|
|
1934
|
-
if (
|
|
1935
|
-
const system =
|
|
1936
|
-
const user =
|
|
2240
|
+
if (import_fs3.default.existsSync(systemPath) && import_fs3.default.existsSync(userPath)) {
|
|
2241
|
+
const system = import_fs3.default.readFileSync(systemPath, "utf-8");
|
|
2242
|
+
const user = import_fs3.default.readFileSync(userPath, "utf-8");
|
|
1937
2243
|
logger.debug(`Loaded prompt '${promptName}' from ${dir}`);
|
|
1938
2244
|
return { system, user };
|
|
1939
2245
|
}
|
|
@@ -2055,6 +2361,14 @@ var LLM = class {
|
|
|
2055
2361
|
throw new Error(`Unsupported provider: ${provider}. Use "anthropic" or "groq"`);
|
|
2056
2362
|
}
|
|
2057
2363
|
}
|
|
2364
|
+
/* Stream response with tool calling support (Anthropic only for now) */
|
|
2365
|
+
static async streamWithTools(messages, tools, toolHandler, options = {}, maxIterations = 3) {
|
|
2366
|
+
const [provider, modelName] = this._parseModel(options.model);
|
|
2367
|
+
if (provider !== "anthropic") {
|
|
2368
|
+
throw new Error(`Tool calling is only supported for Anthropic models`);
|
|
2369
|
+
}
|
|
2370
|
+
return this._anthropicStreamWithTools(messages, tools, toolHandler, modelName, options, maxIterations);
|
|
2371
|
+
}
|
|
2058
2372
|
// ============================================================
|
|
2059
2373
|
// PRIVATE HELPER METHODS
|
|
2060
2374
|
// ============================================================
|
|
@@ -2132,6 +2446,86 @@ var LLM = class {
|
|
|
2132
2446
|
}
|
|
2133
2447
|
return fullText;
|
|
2134
2448
|
}
|
|
2449
|
+
static async _anthropicStreamWithTools(messages, tools, toolHandler, modelName, options, maxIterations) {
|
|
2450
|
+
const apiKey = options.apiKey || process.env.ANTHROPIC_API_KEY || "";
|
|
2451
|
+
const client = new import_sdk.default({
|
|
2452
|
+
apiKey
|
|
2453
|
+
});
|
|
2454
|
+
const conversationMessages = [{
|
|
2455
|
+
role: "user",
|
|
2456
|
+
content: messages.user
|
|
2457
|
+
}];
|
|
2458
|
+
let iterations = 0;
|
|
2459
|
+
let finalText = "";
|
|
2460
|
+
while (iterations < maxIterations) {
|
|
2461
|
+
iterations++;
|
|
2462
|
+
const response = await client.messages.create({
|
|
2463
|
+
model: modelName,
|
|
2464
|
+
max_tokens: options.maxTokens || 4e3,
|
|
2465
|
+
temperature: options.temperature,
|
|
2466
|
+
system: messages.sys,
|
|
2467
|
+
messages: conversationMessages,
|
|
2468
|
+
tools
|
|
2469
|
+
});
|
|
2470
|
+
if (response.stop_reason === "end_turn") {
|
|
2471
|
+
const textBlock = response.content.find((block) => block.type === "text");
|
|
2472
|
+
if (textBlock && textBlock.type === "text") {
|
|
2473
|
+
finalText = textBlock.text;
|
|
2474
|
+
if (options.partial) {
|
|
2475
|
+
options.partial(finalText);
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
2478
|
+
break;
|
|
2479
|
+
}
|
|
2480
|
+
if (response.stop_reason === "tool_use") {
|
|
2481
|
+
const toolUses = response.content.filter((block) => block.type === "tool_use");
|
|
2482
|
+
if (toolUses.length === 0) {
|
|
2483
|
+
break;
|
|
2484
|
+
}
|
|
2485
|
+
conversationMessages.push({
|
|
2486
|
+
role: "assistant",
|
|
2487
|
+
content: response.content
|
|
2488
|
+
});
|
|
2489
|
+
const toolResults = {
|
|
2490
|
+
role: "user",
|
|
2491
|
+
content: []
|
|
2492
|
+
};
|
|
2493
|
+
for (const toolUse of toolUses) {
|
|
2494
|
+
if (toolUse.type === "tool_use") {
|
|
2495
|
+
try {
|
|
2496
|
+
const result = await toolHandler(toolUse.name, toolUse.input);
|
|
2497
|
+
toolResults.content.push({
|
|
2498
|
+
type: "tool_result",
|
|
2499
|
+
tool_use_id: toolUse.id,
|
|
2500
|
+
content: typeof result === "string" ? result : JSON.stringify(result)
|
|
2501
|
+
});
|
|
2502
|
+
} catch (error) {
|
|
2503
|
+
toolResults.content.push({
|
|
2504
|
+
type: "tool_result",
|
|
2505
|
+
tool_use_id: toolUse.id,
|
|
2506
|
+
content: error instanceof Error ? error.message : String(error),
|
|
2507
|
+
is_error: true
|
|
2508
|
+
});
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
conversationMessages.push(toolResults);
|
|
2513
|
+
} else {
|
|
2514
|
+
const textBlock = response.content.find((block) => block.type === "text");
|
|
2515
|
+
if (textBlock && textBlock.type === "text") {
|
|
2516
|
+
finalText = textBlock.text;
|
|
2517
|
+
if (options.partial) {
|
|
2518
|
+
options.partial(finalText);
|
|
2519
|
+
}
|
|
2520
|
+
}
|
|
2521
|
+
break;
|
|
2522
|
+
}
|
|
2523
|
+
}
|
|
2524
|
+
if (iterations >= maxIterations) {
|
|
2525
|
+
throw new Error(`Max iterations (${maxIterations}) reached in tool calling loop`);
|
|
2526
|
+
}
|
|
2527
|
+
return finalText;
|
|
2528
|
+
}
|
|
2135
2529
|
// ============================================================
|
|
2136
2530
|
// GROQ IMPLEMENTATION
|
|
2137
2531
|
// ============================================================
|
|
@@ -2258,7 +2652,9 @@ var BaseLLM = class {
|
|
|
2258
2652
|
needsMultipleComponents: result.needsMultipleComponents || false
|
|
2259
2653
|
};
|
|
2260
2654
|
} catch (error) {
|
|
2261
|
-
|
|
2655
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
2656
|
+
logger.error(`[${this.getProviderName()}] Error classifying user question: ${errorMsg}`);
|
|
2657
|
+
logger.debug(`[${this.getProviderName()}] Classification error details:`, error);
|
|
2262
2658
|
throw error;
|
|
2263
2659
|
}
|
|
2264
2660
|
}
|
|
@@ -2296,6 +2692,7 @@ var BaseLLM = class {
|
|
|
2296
2692
|
);
|
|
2297
2693
|
const props = result.props || originalProps;
|
|
2298
2694
|
if (props && props.query) {
|
|
2695
|
+
props.query = fixScalarSubqueries(props.query);
|
|
2299
2696
|
props.query = ensureQueryLimit(props.query, this.defaultLimit);
|
|
2300
2697
|
}
|
|
2301
2698
|
if (props && props.query) {
|
|
@@ -2322,7 +2719,9 @@ var BaseLLM = class {
|
|
|
2322
2719
|
modifications: result.modifications || []
|
|
2323
2720
|
};
|
|
2324
2721
|
} catch (error) {
|
|
2325
|
-
|
|
2722
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
2723
|
+
logger.error(`[${this.getProviderName()}] Error validating/modifying props: ${errorMsg}`);
|
|
2724
|
+
logger.debug(`[${this.getProviderName()}] Props validation error details:`, error);
|
|
2326
2725
|
throw error;
|
|
2327
2726
|
}
|
|
2328
2727
|
}
|
|
@@ -2442,7 +2841,9 @@ var BaseLLM = class {
|
|
|
2442
2841
|
isGenerated: true
|
|
2443
2842
|
};
|
|
2444
2843
|
} catch (error) {
|
|
2445
|
-
|
|
2844
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
2845
|
+
logger.error(`[${this.getProviderName()}] Error generating analytical component: ${errorMsg}`);
|
|
2846
|
+
logger.debug(`[${this.getProviderName()}] Analytical component generation error details:`, error);
|
|
2446
2847
|
throw error;
|
|
2447
2848
|
}
|
|
2448
2849
|
}
|
|
@@ -2484,7 +2885,9 @@ var BaseLLM = class {
|
|
|
2484
2885
|
description: result.description || `Multi-component dashboard showing ${visualizationTypes.join(", ")}`
|
|
2485
2886
|
};
|
|
2486
2887
|
} catch (error) {
|
|
2487
|
-
|
|
2888
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
2889
|
+
logger.error(`[${this.getProviderName()}] Error generating container metadata: ${errorMsg}`);
|
|
2890
|
+
logger.debug(`[${this.getProviderName()}] Container metadata error details:`, error);
|
|
2488
2891
|
return {
|
|
2489
2892
|
title: `${userPrompt} - Dashboard`,
|
|
2490
2893
|
description: `Multi-component dashboard showing ${visualizationTypes.join(", ")}`
|
|
@@ -2536,24 +2939,24 @@ var BaseLLM = class {
|
|
|
2536
2939
|
component = components[componentIndex - 1];
|
|
2537
2940
|
}
|
|
2538
2941
|
const matchedMsg = `${this.getProviderName()} matched component: ${component?.name || "None"}`;
|
|
2539
|
-
|
|
2942
|
+
logger.info(`[${this.getProviderName()}] \u2713 ${matchedMsg}`);
|
|
2540
2943
|
logCollector?.info(matchedMsg);
|
|
2541
2944
|
if (result.alternativeMatches && result.alternativeMatches.length > 0) {
|
|
2542
|
-
|
|
2945
|
+
logger.debug(`[${this.getProviderName()}] Alternative matches found: ${result.alternativeMatches.length}`);
|
|
2543
2946
|
const altMatches = result.alternativeMatches.map(
|
|
2544
2947
|
(alt) => `${components[alt.index - 1]?.name} (${alt.score}%): ${alt.reason}`
|
|
2545
2948
|
).join(" | ");
|
|
2546
2949
|
logCollector?.info(`Alternative matches: ${altMatches}`);
|
|
2547
2950
|
result.alternativeMatches.forEach((alt) => {
|
|
2548
|
-
|
|
2951
|
+
logger.debug(`[${this.getProviderName()}] - ${components[alt.index - 1]?.name} (${alt.score}%): ${alt.reason}`);
|
|
2549
2952
|
});
|
|
2550
2953
|
}
|
|
2551
2954
|
if (!component) {
|
|
2552
2955
|
const noMatchMsg = `No matching component found (confidence: ${confidence}%)`;
|
|
2553
|
-
|
|
2956
|
+
logger.warn(`[${this.getProviderName()}] \u2717 ${noMatchMsg}`);
|
|
2554
2957
|
logCollector?.warn(noMatchMsg);
|
|
2555
2958
|
const genMsg = "Attempting to match component from analytical question...";
|
|
2556
|
-
|
|
2959
|
+
logger.info(`[${this.getProviderName()}] \u2713 ${genMsg}`);
|
|
2557
2960
|
logCollector?.info(genMsg);
|
|
2558
2961
|
const generatedResult = await this.generateAnalyticalComponent(userPrompt, components, void 0, apiKey, logCollector, conversationHistory);
|
|
2559
2962
|
if (generatedResult.component) {
|
|
@@ -2614,8 +3017,10 @@ var BaseLLM = class {
|
|
|
2614
3017
|
confidence
|
|
2615
3018
|
};
|
|
2616
3019
|
} catch (error) {
|
|
2617
|
-
|
|
2618
|
-
|
|
3020
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
3021
|
+
logger.error(`[${this.getProviderName()}] Error matching component: ${errorMsg}`);
|
|
3022
|
+
logger.debug(`[${this.getProviderName()}] Component matching error details:`, error);
|
|
3023
|
+
logCollector?.error(`Error matching component: ${errorMsg}`);
|
|
2619
3024
|
throw error;
|
|
2620
3025
|
}
|
|
2621
3026
|
}
|
|
@@ -2646,7 +3051,9 @@ var BaseLLM = class {
|
|
|
2646
3051
|
isGenerated: true
|
|
2647
3052
|
};
|
|
2648
3053
|
} catch (error) {
|
|
2649
|
-
|
|
3054
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
3055
|
+
logger.error(`[${this.getProviderName()}] Error matching multiple analytical components: ${errorMsg}`);
|
|
3056
|
+
logger.debug(`[${this.getProviderName()}] Multiple components matching error details:`, error);
|
|
2650
3057
|
return {
|
|
2651
3058
|
components: [],
|
|
2652
3059
|
reasoning: "Error occurred while matching components",
|
|
@@ -2725,76 +3132,551 @@ var BaseLLM = class {
|
|
|
2725
3132
|
isGenerated: true
|
|
2726
3133
|
};
|
|
2727
3134
|
} catch (error) {
|
|
2728
|
-
|
|
3135
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
3136
|
+
logger.error(`[${this.getProviderName()}] Error generating multi-component response: ${errorMsg}`);
|
|
3137
|
+
logger.debug(`[${this.getProviderName()}] Multi-component response error details:`, error);
|
|
2729
3138
|
throw error;
|
|
2730
3139
|
}
|
|
2731
3140
|
}
|
|
2732
3141
|
/**
|
|
2733
|
-
*
|
|
2734
|
-
*
|
|
2735
|
-
*
|
|
3142
|
+
* Match components from text response suggestions
|
|
3143
|
+
* Takes a text response with component suggestions (c1:type format) and matches with available components
|
|
3144
|
+
* @param textResponse - The text response containing component suggestions
|
|
3145
|
+
* @param components - List of available components
|
|
3146
|
+
* @param apiKey - Optional API key
|
|
3147
|
+
* @param logCollector - Optional log collector
|
|
3148
|
+
* @returns Object containing matched components, selected layout, and reasoning
|
|
2736
3149
|
*/
|
|
2737
|
-
async
|
|
3150
|
+
async matchComponentsFromTextResponse(textResponse, components, apiKey, logCollector) {
|
|
2738
3151
|
try {
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
);
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
3152
|
+
logger.debug(`[${this.getProviderName()}] Starting component matching from text response`);
|
|
3153
|
+
let availableComponentsText = "No components available";
|
|
3154
|
+
if (components && components.length > 0) {
|
|
3155
|
+
availableComponentsText = components.map((comp, idx) => {
|
|
3156
|
+
const keywords = comp.keywords ? comp.keywords.join(", ") : "";
|
|
3157
|
+
const propsPreview = comp.props ? JSON.stringify(comp.props, null, 2) : "No props";
|
|
3158
|
+
return `${idx + 1}. ID: ${comp.id}
|
|
3159
|
+
Name: ${comp.name}
|
|
3160
|
+
Type: ${comp.type}
|
|
3161
|
+
Description: ${comp.description || "No description"}
|
|
3162
|
+
Keywords: ${keywords}
|
|
3163
|
+
Props Structure: ${propsPreview}`;
|
|
3164
|
+
}).join("\n\n");
|
|
3165
|
+
}
|
|
3166
|
+
const schemaDoc = schema.generateSchemaDocumentation();
|
|
3167
|
+
const prompts = await promptLoader.loadPrompts("match-text-components", {
|
|
3168
|
+
TEXT_RESPONSE: textResponse,
|
|
3169
|
+
AVAILABLE_COMPONENTS: availableComponentsText,
|
|
3170
|
+
SCHEMA_DOC: schemaDoc
|
|
3171
|
+
});
|
|
3172
|
+
logger.debug(`[${this.getProviderName()}] Loaded match-text-components prompts`);
|
|
3173
|
+
logger.file("\n=============================\nmatch text components system prompt:", prompts.system);
|
|
3174
|
+
logCollector?.info("Matching components from text response...");
|
|
3175
|
+
const rawResponse = await LLM.stream(
|
|
3176
|
+
{
|
|
3177
|
+
sys: prompts.system,
|
|
3178
|
+
user: prompts.user
|
|
3179
|
+
},
|
|
3180
|
+
{
|
|
3181
|
+
model: this.model,
|
|
3182
|
+
maxTokens: 3e3,
|
|
3183
|
+
temperature: 0.2,
|
|
3184
|
+
apiKey: this.getApiKey(apiKey)
|
|
3185
|
+
},
|
|
3186
|
+
false
|
|
3187
|
+
// Don't parse as JSON yet, get raw response
|
|
3188
|
+
);
|
|
3189
|
+
logger.debug(`[${this.getProviderName()}] Raw component matching response length: ${rawResponse?.length || 0}`);
|
|
3190
|
+
logger.file(`[${this.getProviderName()}] Component matching raw response:`, rawResponse);
|
|
3191
|
+
let result;
|
|
3192
|
+
try {
|
|
3193
|
+
let cleanedResponse = rawResponse || "";
|
|
3194
|
+
cleanedResponse = cleanedResponse.replace(/```json\n?/g, "").replace(/```\n?/g, "").trim();
|
|
3195
|
+
const jsonMatch = cleanedResponse.match(/\{[\s\S]*\}/);
|
|
3196
|
+
if (jsonMatch) {
|
|
3197
|
+
cleanedResponse = jsonMatch[0];
|
|
3198
|
+
}
|
|
3199
|
+
result = JSON.parse(cleanedResponse);
|
|
3200
|
+
} catch (parseError) {
|
|
3201
|
+
logger.error(`[${this.getProviderName()}] Failed to parse component matching JSON response`);
|
|
3202
|
+
const errorMsg = parseError instanceof Error ? parseError.message : String(parseError);
|
|
3203
|
+
const posMatch = errorMsg.match(/position (\d+)/);
|
|
3204
|
+
const errorPos = posMatch ? parseInt(posMatch[1]) : -1;
|
|
3205
|
+
if (errorPos > 0 && rawResponse) {
|
|
3206
|
+
const start = Math.max(0, errorPos - 200);
|
|
3207
|
+
const end = Math.min(rawResponse.length, errorPos + 200);
|
|
3208
|
+
logger.debug(`[${this.getProviderName()}] Error context (position ${errorPos}):`);
|
|
3209
|
+
logger.debug(rawResponse.substring(start, end));
|
|
3210
|
+
logger.debug(" ".repeat(Math.min(200, errorPos - start)) + "^--- Error here");
|
|
3211
|
+
}
|
|
3212
|
+
logger.debug(`[${this.getProviderName()}] Raw response (first 2000 chars):`, rawResponse?.substring(0, 2e3));
|
|
3213
|
+
logger.debug(`[${this.getProviderName()}] Parse error:`, parseError);
|
|
3214
|
+
logCollector?.error(`Failed to parse component matching response: ${errorMsg}`);
|
|
3215
|
+
try {
|
|
3216
|
+
logger.info(`[${this.getProviderName()}] Attempting aggressive JSON cleanup...`);
|
|
3217
|
+
let aggressive = rawResponse || "";
|
|
3218
|
+
aggressive = aggressive.replace(/```json\n?/g, "").replace(/```\n?/g, "").trim();
|
|
3219
|
+
const match = aggressive.match(/\{[\s\S]*\}/);
|
|
3220
|
+
if (match) {
|
|
3221
|
+
aggressive = match[0];
|
|
3222
|
+
}
|
|
3223
|
+
aggressive = aggressive.replace(/,(\s*[}\]])/g, "$1");
|
|
3224
|
+
result = JSON.parse(aggressive);
|
|
3225
|
+
logger.info(`[${this.getProviderName()}] Aggressive cleanup succeeded!`);
|
|
3226
|
+
} catch (secondError) {
|
|
3227
|
+
logger.error(`[${this.getProviderName()}] Aggressive cleanup also failed`);
|
|
3228
|
+
return {
|
|
3229
|
+
components: [],
|
|
3230
|
+
selectedLayout: "MultiComponentContainer",
|
|
3231
|
+
selectedLayoutId: "",
|
|
3232
|
+
selectedLayoutComponent: null,
|
|
3233
|
+
layoutReasoning: "No layout selected"
|
|
3234
|
+
};
|
|
3235
|
+
}
|
|
3236
|
+
}
|
|
3237
|
+
const matchedComponents = result.matchedComponents || [];
|
|
3238
|
+
const selectedLayout = result.selectedLayout || "MultiComponentContainer";
|
|
3239
|
+
const selectedLayoutId = result.selectedLayoutId || "";
|
|
3240
|
+
const layoutReasoning = result.layoutReasoning || "No layout reasoning provided";
|
|
3241
|
+
let selectedLayoutComponent = null;
|
|
3242
|
+
if (selectedLayoutId) {
|
|
3243
|
+
selectedLayoutComponent = components.find((c) => c.id === selectedLayoutId) || null;
|
|
3244
|
+
if (!selectedLayoutComponent) {
|
|
3245
|
+
logger.warn(`[${this.getProviderName()}] Layout component ${selectedLayoutId} not found in available components`);
|
|
3246
|
+
}
|
|
3247
|
+
}
|
|
3248
|
+
logger.info(`[${this.getProviderName()}] Matched ${matchedComponents.length} components from text response`);
|
|
3249
|
+
logger.info(`[${this.getProviderName()}] Selected layout: ${selectedLayout} (ID: ${selectedLayoutId})`);
|
|
3250
|
+
logger.info(`[${this.getProviderName()}] Layout reasoning: ${layoutReasoning}`);
|
|
3251
|
+
if (matchedComponents.length > 0) {
|
|
3252
|
+
logCollector?.info(`Matched ${matchedComponents.length} components for visualization using ${selectedLayout}`);
|
|
3253
|
+
logCollector?.info(`Layout reasoning: ${layoutReasoning}`);
|
|
3254
|
+
matchedComponents.forEach((comp, idx) => {
|
|
3255
|
+
logCollector?.info(` ${idx + 1}. ${comp.componentName} (${comp.componentType}): ${comp.reasoning}`);
|
|
3256
|
+
if (comp.props?.query) {
|
|
3257
|
+
logCollector?.logQuery(
|
|
3258
|
+
`Component ${idx + 1} query`,
|
|
3259
|
+
comp.props.query,
|
|
3260
|
+
{ componentName: comp.componentName, title: comp.props.title }
|
|
3261
|
+
);
|
|
3262
|
+
}
|
|
3263
|
+
});
|
|
3264
|
+
}
|
|
3265
|
+
const finalComponents = matchedComponents.map((mc) => {
|
|
3266
|
+
const originalComponent = components.find((c) => c.id === mc.componentId);
|
|
3267
|
+
if (!originalComponent) {
|
|
3268
|
+
logger.warn(`[${this.getProviderName()}] Component ${mc.componentId} not found in available components`);
|
|
3269
|
+
return null;
|
|
3270
|
+
}
|
|
3271
|
+
return {
|
|
3272
|
+
...originalComponent,
|
|
3273
|
+
props: {
|
|
3274
|
+
...originalComponent.props,
|
|
3275
|
+
...mc.props
|
|
3276
|
+
}
|
|
3277
|
+
};
|
|
3278
|
+
}).filter(Boolean);
|
|
3279
|
+
return {
|
|
3280
|
+
components: finalComponents,
|
|
3281
|
+
selectedLayout,
|
|
3282
|
+
selectedLayoutId,
|
|
3283
|
+
selectedLayoutComponent,
|
|
3284
|
+
layoutReasoning
|
|
3285
|
+
};
|
|
3286
|
+
} catch (error) {
|
|
3287
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
3288
|
+
logger.error(`[${this.getProviderName()}] Error matching components from text response: ${errorMsg}`);
|
|
3289
|
+
logger.debug(`[${this.getProviderName()}] Component matching error details:`, error);
|
|
3290
|
+
logCollector?.error(`Error matching components: ${errorMsg}`);
|
|
3291
|
+
return {
|
|
3292
|
+
components: [],
|
|
3293
|
+
selectedLayout: "MultiComponentContainer",
|
|
3294
|
+
selectedLayoutId: "",
|
|
3295
|
+
selectedLayoutComponent: null,
|
|
3296
|
+
layoutReasoning: "Error occurred during component matching"
|
|
3297
|
+
};
|
|
3298
|
+
}
|
|
3299
|
+
}
|
|
3300
|
+
/**
|
|
3301
|
+
* Generate text-based response for user question
|
|
3302
|
+
* This provides conversational text responses instead of component generation
|
|
3303
|
+
* Supports tool calling for query execution with automatic retry on errors (max 3 attempts)
|
|
3304
|
+
* After generating text response, if components are provided, matches suggested components
|
|
3305
|
+
* @param streamCallback - Optional callback function to receive text chunks as they stream
|
|
3306
|
+
* @param collections - Collection registry for executing database queries via database.execute
|
|
3307
|
+
* @param components - Optional list of available components for matching suggestions
|
|
3308
|
+
*/
|
|
3309
|
+
async generateTextResponse(userPrompt, apiKey, logCollector, conversationHistory, streamCallback, collections, components) {
|
|
3310
|
+
const errors = [];
|
|
3311
|
+
logger.debug(`[${this.getProviderName()}] Starting text response generation`);
|
|
3312
|
+
logger.debug(`[${this.getProviderName()}] User prompt: "${userPrompt.substring(0, 50)}..."`);
|
|
3313
|
+
try {
|
|
3314
|
+
const schemaDoc = schema.generateSchemaDocumentation();
|
|
3315
|
+
const prompts = await promptLoader.loadPrompts("text-response", {
|
|
3316
|
+
USER_PROMPT: userPrompt,
|
|
3317
|
+
CONVERSATION_HISTORY: conversationHistory || "No previous conversation",
|
|
3318
|
+
SCHEMA_DOC: schemaDoc
|
|
3319
|
+
});
|
|
3320
|
+
logger.file("\n=============================\nsystem prompt:", prompts.system);
|
|
3321
|
+
logger.file("\n=============================\nuser prompt:", prompts.user);
|
|
3322
|
+
logger.debug(`[${this.getProviderName()}] Loaded text-response prompts with schema`);
|
|
3323
|
+
logger.debug(`[${this.getProviderName()}] System prompt length: ${prompts.system.length}, User prompt length: ${prompts.user.length}`);
|
|
3324
|
+
logCollector?.info("Generating text response with query execution capability...");
|
|
3325
|
+
const tools = [{
|
|
3326
|
+
name: "execute_query",
|
|
3327
|
+
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.",
|
|
3328
|
+
input_schema: {
|
|
3329
|
+
type: "object",
|
|
3330
|
+
properties: {
|
|
3331
|
+
query: {
|
|
3332
|
+
type: "string",
|
|
3333
|
+
description: "The SQL query to execute. Must be valid SQL syntax using table and column names from the schema."
|
|
3334
|
+
},
|
|
3335
|
+
reasoning: {
|
|
3336
|
+
type: "string",
|
|
3337
|
+
description: "Brief explanation of what this query does and why it answers the user's question."
|
|
3338
|
+
}
|
|
3339
|
+
},
|
|
3340
|
+
required: ["query"]
|
|
3341
|
+
}
|
|
3342
|
+
}];
|
|
3343
|
+
const queryAttempts = /* @__PURE__ */ new Map();
|
|
3344
|
+
const MAX_QUERY_ATTEMPTS = 6;
|
|
3345
|
+
let maxAttemptsReached = false;
|
|
3346
|
+
let fullStreamedText = "";
|
|
3347
|
+
const wrappedStreamCallback = streamCallback ? (chunk) => {
|
|
3348
|
+
fullStreamedText += chunk;
|
|
3349
|
+
streamCallback(chunk);
|
|
3350
|
+
} : void 0;
|
|
3351
|
+
const toolHandler = async (toolName, toolInput) => {
|
|
3352
|
+
if (toolName === "execute_query") {
|
|
3353
|
+
let query = toolInput.query;
|
|
3354
|
+
const reasoning = toolInput.reasoning;
|
|
3355
|
+
const { ensureQueryLimit: ensureQueryLimit2 } = await Promise.resolve().then(() => (init_utils(), utils_exports));
|
|
3356
|
+
query = ensureQueryLimit2(query, 32, 32);
|
|
3357
|
+
const queryKey = query.toLowerCase().replace(/\s+/g, " ").trim();
|
|
3358
|
+
const attempts = (queryAttempts.get(queryKey) || 0) + 1;
|
|
3359
|
+
queryAttempts.set(queryKey, attempts);
|
|
3360
|
+
logger.info(`[${this.getProviderName()}] Executing query (attempt ${attempts}/${MAX_QUERY_ATTEMPTS}): ${query.substring(0, 100)}...`);
|
|
3361
|
+
if (reasoning) {
|
|
3362
|
+
logCollector?.info(`Query reasoning: ${reasoning}`);
|
|
3363
|
+
}
|
|
3364
|
+
if (attempts > MAX_QUERY_ATTEMPTS) {
|
|
3365
|
+
const errorMsg = `Maximum query attempts (${MAX_QUERY_ATTEMPTS}) reached. Unable to generate a valid query for your question.`;
|
|
3366
|
+
logger.error(`[${this.getProviderName()}] ${errorMsg}`);
|
|
3367
|
+
logCollector?.error(errorMsg);
|
|
3368
|
+
maxAttemptsReached = true;
|
|
3369
|
+
if (wrappedStreamCallback) {
|
|
3370
|
+
wrappedStreamCallback(`
|
|
3371
|
+
|
|
3372
|
+
\u274C ${errorMsg}
|
|
3373
|
+
|
|
3374
|
+
Please try rephrasing your question or simplifying your request.
|
|
3375
|
+
|
|
3376
|
+
`);
|
|
3377
|
+
}
|
|
3378
|
+
throw new Error(errorMsg);
|
|
3379
|
+
}
|
|
3380
|
+
try {
|
|
3381
|
+
if (wrappedStreamCallback) {
|
|
3382
|
+
if (attempts === 1) {
|
|
3383
|
+
wrappedStreamCallback(`
|
|
3384
|
+
|
|
3385
|
+
\u{1F50D} **Analyzing your question...**
|
|
3386
|
+
|
|
3387
|
+
`);
|
|
3388
|
+
if (reasoning) {
|
|
3389
|
+
wrappedStreamCallback(`\u{1F4AD} ${reasoning}
|
|
3390
|
+
|
|
3391
|
+
`);
|
|
3392
|
+
}
|
|
3393
|
+
wrappedStreamCallback(`\u{1F4DD} **Generated SQL Query:**
|
|
3394
|
+
\`\`\`sql
|
|
3395
|
+
${query}
|
|
3396
|
+
\`\`\`
|
|
3397
|
+
|
|
3398
|
+
`);
|
|
3399
|
+
wrappedStreamCallback(`\u26A1 **Executing query...**
|
|
3400
|
+
|
|
3401
|
+
`);
|
|
3402
|
+
} else {
|
|
3403
|
+
wrappedStreamCallback(`
|
|
3404
|
+
|
|
3405
|
+
\u{1F504} **Retrying with corrected query (attempt ${attempts}/${MAX_QUERY_ATTEMPTS})...**
|
|
3406
|
+
|
|
3407
|
+
`);
|
|
3408
|
+
if (reasoning) {
|
|
3409
|
+
wrappedStreamCallback(`\u{1F4AD} ${reasoning}
|
|
3410
|
+
|
|
3411
|
+
`);
|
|
3412
|
+
}
|
|
3413
|
+
wrappedStreamCallback(`\u{1F4DD} **Corrected SQL Query:**
|
|
3414
|
+
\`\`\`sql
|
|
3415
|
+
${query}
|
|
3416
|
+
\`\`\`
|
|
3417
|
+
|
|
3418
|
+
`);
|
|
3419
|
+
wrappedStreamCallback(`\u26A1 **Executing query...**
|
|
3420
|
+
|
|
3421
|
+
`);
|
|
3422
|
+
}
|
|
3423
|
+
}
|
|
3424
|
+
logCollector?.logQuery(
|
|
3425
|
+
`Executing SQL query (attempt ${attempts})`,
|
|
3426
|
+
query,
|
|
3427
|
+
{ reasoning, attempt: attempts }
|
|
3428
|
+
);
|
|
3429
|
+
if (!collections || !collections["database"] || !collections["database"]["execute"]) {
|
|
3430
|
+
throw new Error("Database collection not registered. Please register database.execute collection to execute queries.");
|
|
3431
|
+
}
|
|
3432
|
+
const result2 = await collections["database"]["execute"]({ sql: query });
|
|
3433
|
+
const data = result2?.data || result2;
|
|
3434
|
+
const rowCount = result2?.count ?? (Array.isArray(data) ? data.length : "N/A");
|
|
3435
|
+
logger.info(`[${this.getProviderName()}] Query executed successfully, rows returned: ${rowCount}`);
|
|
3436
|
+
logCollector?.info(`Query successful, returned ${rowCount} rows`);
|
|
3437
|
+
if (wrappedStreamCallback) {
|
|
3438
|
+
wrappedStreamCallback(`\u2705 **Query executed successfully!**
|
|
3439
|
+
|
|
3440
|
+
`);
|
|
3441
|
+
if (Array.isArray(data) && data.length > 0) {
|
|
3442
|
+
const firstRow = data[0];
|
|
3443
|
+
const columns = Object.keys(firstRow);
|
|
3444
|
+
if (data.length === 1 && columns.length === 1) {
|
|
3445
|
+
const value = firstRow[columns[0]];
|
|
3446
|
+
wrappedStreamCallback(`**Result:** ${value}
|
|
3447
|
+
|
|
3448
|
+
`);
|
|
3449
|
+
} else if (data.length > 0) {
|
|
3450
|
+
wrappedStreamCallback(`**Retrieved ${rowCount} rows**
|
|
3451
|
+
|
|
3452
|
+
`);
|
|
3453
|
+
wrappedStreamCallback(`<DataTable>${JSON.stringify(data)}</DataTable>
|
|
3454
|
+
|
|
3455
|
+
`);
|
|
3456
|
+
}
|
|
3457
|
+
} else if (Array.isArray(data) && data.length === 0) {
|
|
3458
|
+
wrappedStreamCallback(`**No rows returned.**
|
|
3459
|
+
|
|
3460
|
+
`);
|
|
3461
|
+
}
|
|
3462
|
+
wrappedStreamCallback(`\u{1F4CA} **Analyzing results...**
|
|
3463
|
+
|
|
3464
|
+
`);
|
|
3465
|
+
}
|
|
3466
|
+
return JSON.stringify(data, null, 2);
|
|
3467
|
+
} catch (error) {
|
|
3468
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
3469
|
+
logger.error(`[${this.getProviderName()}] Query execution failed (attempt ${attempts}/${MAX_QUERY_ATTEMPTS}): ${errorMsg}`);
|
|
3470
|
+
logCollector?.error(`Query failed (attempt ${attempts}/${MAX_QUERY_ATTEMPTS}): ${errorMsg}`);
|
|
3471
|
+
if (wrappedStreamCallback) {
|
|
3472
|
+
wrappedStreamCallback(`\u274C **Query execution failed:**
|
|
3473
|
+
\`\`\`
|
|
3474
|
+
${errorMsg}
|
|
3475
|
+
\`\`\`
|
|
3476
|
+
|
|
3477
|
+
`);
|
|
3478
|
+
if (attempts < MAX_QUERY_ATTEMPTS) {
|
|
3479
|
+
wrappedStreamCallback(`\u{1F527} **Generating corrected query...**
|
|
3480
|
+
|
|
3481
|
+
`);
|
|
3482
|
+
}
|
|
3483
|
+
}
|
|
3484
|
+
throw new Error(`Query execution failed: ${errorMsg}`);
|
|
3485
|
+
}
|
|
3486
|
+
}
|
|
3487
|
+
throw new Error(`Unknown tool: ${toolName}`);
|
|
3488
|
+
};
|
|
3489
|
+
const result = await LLM.streamWithTools(
|
|
3490
|
+
{
|
|
3491
|
+
sys: prompts.system,
|
|
3492
|
+
user: prompts.user
|
|
3493
|
+
},
|
|
3494
|
+
tools,
|
|
3495
|
+
toolHandler,
|
|
3496
|
+
{
|
|
3497
|
+
model: this.model,
|
|
3498
|
+
maxTokens: 4e3,
|
|
3499
|
+
temperature: 0.7,
|
|
3500
|
+
apiKey: this.getApiKey(apiKey),
|
|
3501
|
+
partial: wrappedStreamCallback
|
|
3502
|
+
// Pass the wrapped streaming callback to LLM
|
|
3503
|
+
},
|
|
3504
|
+
10
|
|
3505
|
+
// max iterations: allows for 6 retries + final response + buffer
|
|
3506
|
+
);
|
|
3507
|
+
logger.info(`[${this.getProviderName()}] Text response stream completed`);
|
|
3508
|
+
const textResponse = fullStreamedText || result || "I apologize, but I was unable to generate a response.";
|
|
3509
|
+
if (maxAttemptsReached) {
|
|
3510
|
+
logger.warn(`[${this.getProviderName()}] Max query attempts reached, returning failure response`);
|
|
3511
|
+
logCollector?.error("Failed to generate valid query after maximum attempts");
|
|
3512
|
+
return {
|
|
3513
|
+
success: false,
|
|
3514
|
+
errors: [`Maximum query attempts (${MAX_QUERY_ATTEMPTS}) reached. Unable to generate a valid query for your question.`],
|
|
3515
|
+
data: {
|
|
3516
|
+
text: textResponse,
|
|
3517
|
+
// Include the streamed text showing all attempts
|
|
3518
|
+
matchedComponents: [],
|
|
3519
|
+
method: `${this.getProviderName()}-text-response-max-attempts`
|
|
3520
|
+
}
|
|
3521
|
+
};
|
|
3522
|
+
}
|
|
3523
|
+
logCollector?.info(`Text response: ${textResponse.substring(0, 100)}${textResponse.length > 100 ? "..." : ""}`);
|
|
3524
|
+
logCollector?.logExplanation(
|
|
3525
|
+
"Text response generated",
|
|
3526
|
+
"Generated plain text response with component suggestions",
|
|
3527
|
+
{
|
|
3528
|
+
textLength: textResponse.length
|
|
3529
|
+
}
|
|
3530
|
+
);
|
|
3531
|
+
let matchedComponents = [];
|
|
3532
|
+
let selectedLayoutComponent = null;
|
|
3533
|
+
let layoutReasoning = "No layout selected";
|
|
3534
|
+
if (components && components.length > 0) {
|
|
3535
|
+
logger.info(`[${this.getProviderName()}] Matching components from text response...`);
|
|
3536
|
+
const matchResult = await this.matchComponentsFromTextResponse(
|
|
3537
|
+
textResponse,
|
|
3538
|
+
components,
|
|
3539
|
+
apiKey,
|
|
3540
|
+
logCollector
|
|
3541
|
+
);
|
|
3542
|
+
matchedComponents = matchResult.components;
|
|
3543
|
+
selectedLayoutComponent = matchResult.selectedLayoutComponent;
|
|
3544
|
+
layoutReasoning = matchResult.layoutReasoning;
|
|
3545
|
+
}
|
|
3546
|
+
let container_componet = null;
|
|
3547
|
+
if (matchedComponents.length > 0) {
|
|
3548
|
+
if (selectedLayoutComponent) {
|
|
3549
|
+
container_componet = {
|
|
3550
|
+
...selectedLayoutComponent,
|
|
3551
|
+
id: `${selectedLayoutComponent.id}_${Date.now()}`,
|
|
3552
|
+
description: layoutReasoning,
|
|
3553
|
+
props: {
|
|
3554
|
+
...selectedLayoutComponent.props,
|
|
3555
|
+
config: {
|
|
3556
|
+
...selectedLayoutComponent.props?.config || {},
|
|
3557
|
+
components: matchedComponents
|
|
3558
|
+
}
|
|
3559
|
+
}
|
|
3560
|
+
};
|
|
3561
|
+
logger.info(`[${this.getProviderName()}] Created ${selectedLayoutComponent.name} (${selectedLayoutComponent.type}) container with ${matchedComponents.length} components`);
|
|
3562
|
+
logCollector?.info(`Created ${selectedLayoutComponent.name} with ${matchedComponents.length} components: ${layoutReasoning}`);
|
|
3563
|
+
} else {
|
|
3564
|
+
container_componet = {
|
|
3565
|
+
id: `multi_container_${Date.now()}`,
|
|
3566
|
+
name: "MultiComponentContainer",
|
|
3567
|
+
type: "Container",
|
|
3568
|
+
description: layoutReasoning,
|
|
3569
|
+
category: "dynamic",
|
|
3570
|
+
keywords: ["dashboard", "layout", "container"],
|
|
3571
|
+
props: {
|
|
3572
|
+
config: {
|
|
3573
|
+
components: matchedComponents
|
|
3574
|
+
}
|
|
3575
|
+
}
|
|
3576
|
+
};
|
|
3577
|
+
logger.info(`[${this.getProviderName()}] Created fallback MultiComponentContainer with ${matchedComponents.length} components`);
|
|
3578
|
+
logCollector?.info(`Created MultiComponentContainer with ${matchedComponents.length} components: ${layoutReasoning}`);
|
|
3579
|
+
}
|
|
3580
|
+
}
|
|
3581
|
+
return {
|
|
3582
|
+
success: true,
|
|
3583
|
+
data: {
|
|
3584
|
+
text: textResponse,
|
|
3585
|
+
matchedComponents,
|
|
3586
|
+
component: container_componet,
|
|
3587
|
+
layoutReasoning,
|
|
3588
|
+
method: `${this.getProviderName()}-text-response`
|
|
3589
|
+
},
|
|
3590
|
+
errors: []
|
|
3591
|
+
};
|
|
3592
|
+
} catch (error) {
|
|
3593
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
3594
|
+
logger.error(`[${this.getProviderName()}] Error generating text response: ${errorMsg}`);
|
|
3595
|
+
logCollector?.error(`Error generating text response: ${errorMsg}`);
|
|
3596
|
+
errors.push(errorMsg);
|
|
3597
|
+
return {
|
|
3598
|
+
success: false,
|
|
3599
|
+
errors,
|
|
3600
|
+
data: {
|
|
3601
|
+
text: "I apologize, but I encountered an error while processing your question. Please try rephrasing or ask something else.",
|
|
3602
|
+
matchedComponents: [],
|
|
3603
|
+
method: `${this.getProviderName()}-text-response-error`
|
|
3604
|
+
}
|
|
3605
|
+
};
|
|
3606
|
+
}
|
|
3607
|
+
}
|
|
3608
|
+
/**
|
|
3609
|
+
* Generate component response for user question
|
|
3610
|
+
* This provides conversational component suggestions based on user question
|
|
3611
|
+
* Supports component generation and matching
|
|
3612
|
+
*/
|
|
3613
|
+
async generateComponentResponse(userPrompt, components, apiKey, logCollector, conversationHistory) {
|
|
3614
|
+
const errors = [];
|
|
3615
|
+
try {
|
|
3616
|
+
logger.info(`[${this.getProviderName()}] Using component response mode`);
|
|
3617
|
+
const classifyMsg = "Classifying user question...";
|
|
3618
|
+
logCollector?.info(classifyMsg);
|
|
3619
|
+
const classification = await this.classifyUserQuestion(userPrompt, apiKey, logCollector, conversationHistory);
|
|
3620
|
+
const classInfo = `Question type: ${classification.questionType}, Visualizations: ${classification.visualizations.join(", ") || "None"}, Multiple components: ${classification.needsMultipleComponents}`;
|
|
3621
|
+
logCollector?.info(classInfo);
|
|
3622
|
+
if (classification.questionType === "analytical") {
|
|
3623
|
+
if (classification.visualizations.length > 1) {
|
|
3624
|
+
const multiMsg = `Matching ${classification.visualizations.length} components for types: ${classification.visualizations.join(", ")}`;
|
|
3625
|
+
logCollector?.info(multiMsg);
|
|
3626
|
+
const componentPromises = classification.visualizations.map((vizType) => {
|
|
3627
|
+
logCollector?.info(`Matching component for type: ${vizType}`);
|
|
3628
|
+
return this.generateAnalyticalComponent(
|
|
3629
|
+
userPrompt,
|
|
3630
|
+
components,
|
|
3631
|
+
vizType,
|
|
3632
|
+
apiKey,
|
|
3633
|
+
logCollector,
|
|
3634
|
+
conversationHistory
|
|
3635
|
+
).then((result) => ({ vizType, result }));
|
|
3636
|
+
});
|
|
3637
|
+
const settledResults = await Promise.allSettled(componentPromises);
|
|
3638
|
+
const matchedComponents = [];
|
|
3639
|
+
for (const settledResult of settledResults) {
|
|
3640
|
+
if (settledResult.status === "fulfilled") {
|
|
3641
|
+
const { vizType, result } = settledResult.value;
|
|
3642
|
+
if (result.component) {
|
|
3643
|
+
matchedComponents.push(result.component);
|
|
3644
|
+
logCollector?.info(`Matched: ${result.component.name}`);
|
|
3645
|
+
logger.info("Component : ", result.component.name, " props: ", result.component.props);
|
|
3646
|
+
} else {
|
|
3647
|
+
logCollector?.warn(`Failed to match component for type: ${vizType}`);
|
|
3648
|
+
}
|
|
3649
|
+
} else {
|
|
3650
|
+
logCollector?.warn(`Error matching component: ${settledResult.reason?.message || "Unknown error"}`);
|
|
3651
|
+
}
|
|
3652
|
+
}
|
|
3653
|
+
logger.debug(`[${this.getProviderName()}] Matched ${matchedComponents.length} components for multi-component container`);
|
|
3654
|
+
if (matchedComponents.length === 0) {
|
|
3655
|
+
return {
|
|
3656
|
+
success: true,
|
|
3657
|
+
data: {
|
|
3658
|
+
component: null,
|
|
3659
|
+
reasoning: "Failed to match any components for the requested visualization types",
|
|
3660
|
+
method: "classification-multi-failed",
|
|
3661
|
+
questionType: classification.questionType,
|
|
3662
|
+
needsMultipleComponents: true,
|
|
3663
|
+
propsModified: false,
|
|
3664
|
+
queryModified: false
|
|
3665
|
+
},
|
|
3666
|
+
errors: []
|
|
3667
|
+
};
|
|
3668
|
+
}
|
|
3669
|
+
logCollector?.info("Generating container metadata...");
|
|
3670
|
+
const containerMetadata = await this.generateContainerMetadata(
|
|
3671
|
+
userPrompt,
|
|
3672
|
+
classification.visualizations,
|
|
3673
|
+
apiKey,
|
|
3674
|
+
logCollector,
|
|
3675
|
+
conversationHistory
|
|
3676
|
+
);
|
|
3677
|
+
const containerComponent = {
|
|
3678
|
+
id: `multi_container_${Date.now()}`,
|
|
3679
|
+
name: "MultiComponentContainer",
|
|
2798
3680
|
type: "Container",
|
|
2799
3681
|
description: containerMetadata.description,
|
|
2800
3682
|
category: "dynamic",
|
|
@@ -2811,38 +3693,50 @@ var BaseLLM = class {
|
|
|
2811
3693
|
};
|
|
2812
3694
|
logCollector?.info(`Created multi-component container with ${matchedComponents.length} components: "${containerMetadata.title}"`);
|
|
2813
3695
|
return {
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
3696
|
+
success: true,
|
|
3697
|
+
data: {
|
|
3698
|
+
component: containerComponent,
|
|
3699
|
+
reasoning: `Matched ${matchedComponents.length} components for visualization types: ${classification.visualizations.join(", ")}`,
|
|
3700
|
+
method: "classification-multi-generated",
|
|
3701
|
+
questionType: classification.questionType,
|
|
3702
|
+
needsMultipleComponents: true,
|
|
3703
|
+
propsModified: false,
|
|
3704
|
+
queryModified: false
|
|
3705
|
+
},
|
|
3706
|
+
errors: []
|
|
2821
3707
|
};
|
|
2822
3708
|
} else if (classification.visualizations.length === 1) {
|
|
2823
3709
|
const vizType = classification.visualizations[0];
|
|
2824
3710
|
logCollector?.info(`Matching single component for type: ${vizType}`);
|
|
2825
3711
|
const result = await this.generateAnalyticalComponent(userPrompt, components, vizType, apiKey, logCollector, conversationHistory);
|
|
2826
3712
|
return {
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
3713
|
+
success: true,
|
|
3714
|
+
data: {
|
|
3715
|
+
component: result.component,
|
|
3716
|
+
reasoning: result.reasoning,
|
|
3717
|
+
method: "classification-generated",
|
|
3718
|
+
questionType: classification.questionType,
|
|
3719
|
+
needsMultipleComponents: false,
|
|
3720
|
+
propsModified: false,
|
|
3721
|
+
queryModified: false
|
|
3722
|
+
},
|
|
3723
|
+
errors: []
|
|
2834
3724
|
};
|
|
2835
3725
|
} else {
|
|
2836
3726
|
logCollector?.info("No specific visualization type - matching from all components");
|
|
2837
3727
|
const result = await this.generateAnalyticalComponent(userPrompt, components, void 0, apiKey, logCollector, conversationHistory);
|
|
2838
3728
|
return {
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
3729
|
+
success: true,
|
|
3730
|
+
data: {
|
|
3731
|
+
component: result.component,
|
|
3732
|
+
reasoning: result.reasoning,
|
|
3733
|
+
method: "classification-generated-auto",
|
|
3734
|
+
questionType: classification.questionType,
|
|
3735
|
+
needsMultipleComponents: false,
|
|
3736
|
+
propsModified: false,
|
|
3737
|
+
queryModified: false
|
|
3738
|
+
},
|
|
3739
|
+
errors: []
|
|
2846
3740
|
};
|
|
2847
3741
|
}
|
|
2848
3742
|
} else if (classification.questionType === "data_modification" || classification.questionType === "general") {
|
|
@@ -2850,28 +3744,103 @@ var BaseLLM = class {
|
|
|
2850
3744
|
logCollector?.info(matchMsg);
|
|
2851
3745
|
const matchResult = await this.matchComponent(userPrompt, components, apiKey, logCollector, conversationHistory);
|
|
2852
3746
|
return {
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
3747
|
+
success: true,
|
|
3748
|
+
data: {
|
|
3749
|
+
component: matchResult.component,
|
|
3750
|
+
reasoning: matchResult.reasoning,
|
|
3751
|
+
method: "classification-matched",
|
|
3752
|
+
questionType: classification.questionType,
|
|
3753
|
+
needsMultipleComponents: false,
|
|
3754
|
+
propsModified: matchResult.propsModified,
|
|
3755
|
+
queryModified: matchResult.queryModified
|
|
3756
|
+
},
|
|
3757
|
+
errors: []
|
|
2860
3758
|
};
|
|
2861
3759
|
} else {
|
|
2862
3760
|
logCollector?.info("General question - no component needed");
|
|
2863
3761
|
return {
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
3762
|
+
success: true,
|
|
3763
|
+
data: {
|
|
3764
|
+
component: null,
|
|
3765
|
+
reasoning: "General question - no component needed",
|
|
3766
|
+
method: "classification-general",
|
|
3767
|
+
questionType: classification.questionType,
|
|
3768
|
+
needsMultipleComponents: false,
|
|
3769
|
+
propsModified: false,
|
|
3770
|
+
queryModified: false
|
|
3771
|
+
},
|
|
3772
|
+
errors: []
|
|
2869
3773
|
};
|
|
2870
3774
|
}
|
|
2871
3775
|
} catch (error) {
|
|
2872
|
-
|
|
2873
|
-
|
|
3776
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
3777
|
+
logger.error(`[${this.getProviderName()}] Error generating component response: ${errorMsg}`);
|
|
3778
|
+
logger.debug(`[${this.getProviderName()}] Component response generation error details:`, error);
|
|
3779
|
+
logCollector?.error(`Error generating component response: ${errorMsg}`);
|
|
3780
|
+
errors.push(errorMsg);
|
|
3781
|
+
return {
|
|
3782
|
+
success: false,
|
|
3783
|
+
errors,
|
|
3784
|
+
data: void 0
|
|
3785
|
+
};
|
|
3786
|
+
}
|
|
3787
|
+
}
|
|
3788
|
+
/**
|
|
3789
|
+
* Main orchestration function that classifies question and routes to appropriate handler
|
|
3790
|
+
* This is the NEW recommended entry point for handling user requests
|
|
3791
|
+
* Supports both component generation and text response modes
|
|
3792
|
+
*
|
|
3793
|
+
* @param responseMode - 'component' for component generation (default), 'text' for text responses
|
|
3794
|
+
* @param streamCallback - Optional callback function to receive text chunks as they stream (only for text mode)
|
|
3795
|
+
* @param collections - Collection registry for executing database queries (required for text mode)
|
|
3796
|
+
*/
|
|
3797
|
+
async handleUserRequest(userPrompt, components, apiKey, logCollector, conversationHistory, responseMode = "component", streamCallback, collections) {
|
|
3798
|
+
const startTime = Date.now();
|
|
3799
|
+
logger.info(`[${this.getProviderName()}] handleUserRequest called with responseMode: ${responseMode}`);
|
|
3800
|
+
if (responseMode === "text") {
|
|
3801
|
+
logger.info(`[${this.getProviderName()}] Using text response mode`);
|
|
3802
|
+
logCollector?.info("Generating text response...");
|
|
3803
|
+
const textResponse = await this.generateTextResponse(
|
|
3804
|
+
userPrompt,
|
|
3805
|
+
apiKey,
|
|
3806
|
+
logCollector,
|
|
3807
|
+
conversationHistory,
|
|
3808
|
+
streamCallback,
|
|
3809
|
+
collections,
|
|
3810
|
+
components
|
|
3811
|
+
);
|
|
3812
|
+
if (!textResponse.success) {
|
|
3813
|
+
const elapsedTime3 = Date.now() - startTime;
|
|
3814
|
+
logger.error(`[${this.getProviderName()}] Text response generation failed`);
|
|
3815
|
+
logger.info(`[${this.getProviderName()}] Total time taken: ${elapsedTime3}ms (${(elapsedTime3 / 1e3).toFixed(2)}s)`);
|
|
3816
|
+
logCollector?.info(`Total time taken: ${elapsedTime3}ms (${(elapsedTime3 / 1e3).toFixed(2)}s)`);
|
|
3817
|
+
return textResponse;
|
|
3818
|
+
}
|
|
3819
|
+
const elapsedTime2 = Date.now() - startTime;
|
|
3820
|
+
logger.info(`[${this.getProviderName()}] Text response generated successfully`);
|
|
3821
|
+
logger.info(`[${this.getProviderName()}] Total time taken: ${elapsedTime2}ms (${(elapsedTime2 / 1e3).toFixed(2)}s)`);
|
|
3822
|
+
logCollector?.info(`Total time taken: ${elapsedTime2}ms (${(elapsedTime2 / 1e3).toFixed(2)}s)`);
|
|
3823
|
+
return textResponse;
|
|
3824
|
+
}
|
|
3825
|
+
const componentResponse = await this.generateComponentResponse(
|
|
3826
|
+
userPrompt,
|
|
3827
|
+
components,
|
|
3828
|
+
apiKey,
|
|
3829
|
+
logCollector,
|
|
3830
|
+
conversationHistory
|
|
3831
|
+
);
|
|
3832
|
+
if (!componentResponse.success) {
|
|
3833
|
+
const elapsedTime2 = Date.now() - startTime;
|
|
3834
|
+
logger.error(`[${this.getProviderName()}] Component response generation failed`);
|
|
3835
|
+
logger.info(`[${this.getProviderName()}] Total time taken: ${elapsedTime2}ms (${(elapsedTime2 / 1e3).toFixed(2)}s)`);
|
|
3836
|
+
logCollector?.info(`Total time taken: ${elapsedTime2}ms (${(elapsedTime2 / 1e3).toFixed(2)}s)`);
|
|
3837
|
+
return componentResponse;
|
|
2874
3838
|
}
|
|
3839
|
+
const elapsedTime = Date.now() - startTime;
|
|
3840
|
+
logger.info(`[${this.getProviderName()}] Component response generated successfully`);
|
|
3841
|
+
logger.info(`[${this.getProviderName()}] Total time taken: ${elapsedTime}ms (${(elapsedTime / 1e3).toFixed(2)}s)`);
|
|
3842
|
+
logCollector?.info(`Total time taken: ${elapsedTime}ms (${(elapsedTime / 1e3).toFixed(2)}s)`);
|
|
3843
|
+
return componentResponse;
|
|
2875
3844
|
}
|
|
2876
3845
|
/**
|
|
2877
3846
|
* Generate next questions that the user might ask based on the original prompt and generated component
|
|
@@ -2917,8 +3886,10 @@ var BaseLLM = class {
|
|
|
2917
3886
|
);
|
|
2918
3887
|
return nextQuestions;
|
|
2919
3888
|
} catch (error) {
|
|
2920
|
-
|
|
2921
|
-
|
|
3889
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
3890
|
+
logger.error(`[${this.getProviderName()}] Error generating next questions: ${errorMsg}`);
|
|
3891
|
+
logger.debug(`[${this.getProviderName()}] Next questions generation error details:`, error);
|
|
3892
|
+
logCollector?.error(`Error generating next questions: ${errorMsg}`);
|
|
2922
3893
|
return [];
|
|
2923
3894
|
}
|
|
2924
3895
|
}
|
|
@@ -2982,112 +3953,130 @@ function getLLMProviders() {
|
|
|
2982
3953
|
return DEFAULT_PROVIDERS;
|
|
2983
3954
|
}
|
|
2984
3955
|
}
|
|
2985
|
-
var useAnthropicMethod = async (prompt, components, apiKey, logCollector, conversationHistory) => {
|
|
2986
|
-
|
|
2987
|
-
|
|
3956
|
+
var useAnthropicMethod = async (prompt, components, apiKey, logCollector, conversationHistory, responseMode = "component", streamCallback, collections) => {
|
|
3957
|
+
logger.debug("[useAnthropicMethod] Initializing Anthropic Claude matching method");
|
|
3958
|
+
logger.debug(`[useAnthropicMethod] Response mode: ${responseMode}`);
|
|
3959
|
+
const msg = `Using Anthropic Claude ${responseMode === "text" ? "text response" : "matching"} method...`;
|
|
2988
3960
|
logCollector?.info(msg);
|
|
2989
|
-
if (components.length === 0) {
|
|
3961
|
+
if (responseMode === "component" && components.length === 0) {
|
|
2990
3962
|
const emptyMsg = "Components not loaded in memory. Please ensure components are fetched first.";
|
|
3963
|
+
logger.error("[useAnthropicMethod] No components available");
|
|
2991
3964
|
logCollector?.error(emptyMsg);
|
|
2992
|
-
return { success: false,
|
|
2993
|
-
}
|
|
2994
|
-
try {
|
|
2995
|
-
const matchResult = await anthropicLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory);
|
|
2996
|
-
return { success: true, data: matchResult };
|
|
2997
|
-
} catch (error) {
|
|
2998
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
2999
|
-
logCollector?.error(`Anthropic method failed: ${errorMsg}`);
|
|
3000
|
-
throw error;
|
|
3965
|
+
return { success: false, errors: [emptyMsg] };
|
|
3001
3966
|
}
|
|
3967
|
+
logger.debug(`[useAnthropicMethod] Processing with ${components.length} components`);
|
|
3968
|
+
const matchResult = await anthropicLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory, responseMode, streamCallback, collections);
|
|
3969
|
+
logger.info(`[useAnthropicMethod] Successfully generated ${responseMode} using Anthropic`);
|
|
3970
|
+
return matchResult;
|
|
3002
3971
|
};
|
|
3003
|
-
var useGroqMethod = async (prompt, components, apiKey, logCollector, conversationHistory) => {
|
|
3004
|
-
|
|
3005
|
-
|
|
3972
|
+
var useGroqMethod = async (prompt, components, apiKey, logCollector, conversationHistory, responseMode = "component", streamCallback, collections) => {
|
|
3973
|
+
logger.debug("[useGroqMethod] Initializing Groq LLM matching method");
|
|
3974
|
+
logger.debug(`[useGroqMethod] Response mode: ${responseMode}`);
|
|
3975
|
+
const msg = `Using Groq LLM ${responseMode === "text" ? "text response" : "matching"} method...`;
|
|
3976
|
+
logger.info(msg);
|
|
3006
3977
|
logCollector?.info(msg);
|
|
3007
|
-
if (components.length === 0) {
|
|
3978
|
+
if (responseMode === "component" && components.length === 0) {
|
|
3008
3979
|
const emptyMsg = "Components not loaded in memory. Please ensure components are fetched first.";
|
|
3980
|
+
logger.error("[useGroqMethod] No components available");
|
|
3009
3981
|
logCollector?.error(emptyMsg);
|
|
3010
|
-
return { success: false,
|
|
3011
|
-
}
|
|
3012
|
-
try {
|
|
3013
|
-
const matchResult = await groqLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory);
|
|
3014
|
-
return { success: true, data: matchResult };
|
|
3015
|
-
} catch (error) {
|
|
3016
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
3017
|
-
logCollector?.error(`Groq method failed: ${errorMsg}`);
|
|
3018
|
-
throw error;
|
|
3982
|
+
return { success: false, errors: [emptyMsg] };
|
|
3019
3983
|
}
|
|
3984
|
+
logger.debug(`[useGroqMethod] Processing with ${components.length} components`);
|
|
3985
|
+
const matchResult = await groqLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory, responseMode, streamCallback, collections);
|
|
3986
|
+
logger.info(`[useGroqMethod] Successfully generated ${responseMode} using Groq`);
|
|
3987
|
+
return matchResult;
|
|
3020
3988
|
};
|
|
3021
3989
|
var getUserResponseFromCache = async (prompt) => {
|
|
3022
3990
|
return false;
|
|
3023
3991
|
};
|
|
3024
|
-
var get_user_response = async (prompt, components, anthropicApiKey, groqApiKey, llmProviders, logCollector, conversationHistory) => {
|
|
3992
|
+
var get_user_response = async (prompt, components, anthropicApiKey, groqApiKey, llmProviders, logCollector, conversationHistory, responseMode = "component", streamCallback, collections) => {
|
|
3993
|
+
logger.debug(`[get_user_response] Starting user response generation for prompt: "${prompt.substring(0, 50)}..."`);
|
|
3994
|
+
logger.debug(`[get_user_response] Response mode: ${responseMode}`);
|
|
3995
|
+
logger.debug("[get_user_response] Checking cache for existing response");
|
|
3025
3996
|
const userResponse = await getUserResponseFromCache(prompt);
|
|
3026
3997
|
if (userResponse) {
|
|
3998
|
+
logger.info("[get_user_response] User response found in cache - returning cached result");
|
|
3027
3999
|
logCollector?.info("User response found in cache");
|
|
3028
4000
|
return {
|
|
3029
4001
|
success: true,
|
|
3030
|
-
data: userResponse
|
|
4002
|
+
data: userResponse,
|
|
4003
|
+
errors: []
|
|
3031
4004
|
};
|
|
3032
4005
|
}
|
|
4006
|
+
logger.debug("[get_user_response] No cached response found, proceeding with LLM providers");
|
|
3033
4007
|
const providers = llmProviders || getLLMProviders();
|
|
3034
4008
|
const errors = [];
|
|
3035
4009
|
const providerOrder = providers.join(", ");
|
|
3036
4010
|
logCollector?.info(`LLM Provider order: [${providerOrder}]`);
|
|
3037
4011
|
if (conversationHistory && conversationHistory.length > 0) {
|
|
3038
|
-
|
|
4012
|
+
const exchangeCount = conversationHistory.split("\n").filter((l) => l.startsWith("Q")).length;
|
|
4013
|
+
logger.debug(`[get_user_response] Using conversation history with ${exchangeCount} previous exchanges`);
|
|
4014
|
+
logCollector?.info(`Using conversation history with ${exchangeCount} previous exchanges`);
|
|
4015
|
+
} else {
|
|
4016
|
+
logger.debug("[get_user_response] No conversation history available");
|
|
3039
4017
|
}
|
|
3040
4018
|
for (let i = 0; i < providers.length; i++) {
|
|
3041
4019
|
const provider = providers[i];
|
|
3042
4020
|
const isLastProvider = i === providers.length - 1;
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
}
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
const errorMsg = `Provider ${provider} failed: ${errorMessage}`;
|
|
3067
|
-
logCollector?.error(errorMsg);
|
|
4021
|
+
const attemptMsg = `Attempting provider: ${provider} (${i + 1}/${providers.length})`;
|
|
4022
|
+
logCollector?.info(attemptMsg);
|
|
4023
|
+
let result;
|
|
4024
|
+
if (provider === "anthropic") {
|
|
4025
|
+
result = await useAnthropicMethod(prompt, components, anthropicApiKey, logCollector, conversationHistory, responseMode, streamCallback, collections);
|
|
4026
|
+
} else if (provider === "groq") {
|
|
4027
|
+
result = await useGroqMethod(prompt, components, groqApiKey, logCollector, conversationHistory, responseMode, streamCallback, collections);
|
|
4028
|
+
} else {
|
|
4029
|
+
logger.warn(`[get_user_response] Unknown provider: ${provider} - skipping`);
|
|
4030
|
+
errors.push(`Unknown provider: ${provider}`);
|
|
4031
|
+
continue;
|
|
4032
|
+
}
|
|
4033
|
+
if (result.success) {
|
|
4034
|
+
const successMsg = `Success with provider: ${provider}`;
|
|
4035
|
+
logger.info(`${successMsg}`);
|
|
4036
|
+
logCollector?.info(successMsg);
|
|
4037
|
+
return result;
|
|
4038
|
+
} else {
|
|
4039
|
+
const providerErrors = result.errors.map((err) => `${provider}: ${err}`);
|
|
4040
|
+
errors.push(...providerErrors);
|
|
4041
|
+
const warnMsg = `Provider ${provider} returned unsuccessful result: ${result.errors.join(", ")}`;
|
|
4042
|
+
logger.warn(`[get_user_response] ${warnMsg}`);
|
|
4043
|
+
logCollector?.warn(warnMsg);
|
|
3068
4044
|
if (!isLastProvider) {
|
|
3069
4045
|
const fallbackMsg = "Falling back to next provider...";
|
|
4046
|
+
logger.info(`[get_user_response] ${fallbackMsg}`);
|
|
3070
4047
|
logCollector?.info(fallbackMsg);
|
|
3071
|
-
continue;
|
|
3072
4048
|
}
|
|
3073
4049
|
}
|
|
3074
4050
|
}
|
|
3075
|
-
const
|
|
3076
|
-
|
|
3077
|
-
logCollector?.error(failureMsg);
|
|
4051
|
+
const failureMsg = `All LLM providers failed`;
|
|
4052
|
+
logger.error(`[get_user_response] ${failureMsg}. Errors: ${errors.join("; ")}`);
|
|
4053
|
+
logCollector?.error(`${failureMsg}. Errors: ${errors.join("; ")}`);
|
|
3078
4054
|
return {
|
|
3079
4055
|
success: false,
|
|
3080
|
-
|
|
4056
|
+
errors
|
|
3081
4057
|
};
|
|
3082
4058
|
};
|
|
3083
4059
|
|
|
3084
4060
|
// src/utils/log-collector.ts
|
|
4061
|
+
var LOG_LEVEL_PRIORITY2 = {
|
|
4062
|
+
errors: 0,
|
|
4063
|
+
warnings: 1,
|
|
4064
|
+
info: 2,
|
|
4065
|
+
verbose: 3
|
|
4066
|
+
};
|
|
4067
|
+
var MESSAGE_LEVEL_PRIORITY2 = {
|
|
4068
|
+
error: 0,
|
|
4069
|
+
warn: 1,
|
|
4070
|
+
info: 2,
|
|
4071
|
+
debug: 3
|
|
4072
|
+
};
|
|
3085
4073
|
var UILogCollector = class {
|
|
3086
4074
|
constructor(clientId, sendMessage, uiBlockId) {
|
|
3087
4075
|
this.logs = [];
|
|
3088
4076
|
this.uiBlockId = uiBlockId || null;
|
|
3089
4077
|
this.clientId = clientId;
|
|
3090
4078
|
this.sendMessage = sendMessage;
|
|
4079
|
+
this.currentLogLevel = logger.getLogLevel();
|
|
3091
4080
|
}
|
|
3092
4081
|
/**
|
|
3093
4082
|
* Check if logging is enabled (uiBlockId is provided)
|
|
@@ -3095,10 +4084,22 @@ var UILogCollector = class {
|
|
|
3095
4084
|
isEnabled() {
|
|
3096
4085
|
return this.uiBlockId !== null;
|
|
3097
4086
|
}
|
|
4087
|
+
/**
|
|
4088
|
+
* Check if a message should be logged based on current log level
|
|
4089
|
+
*/
|
|
4090
|
+
shouldLog(messageLevel) {
|
|
4091
|
+
const currentLevelPriority = LOG_LEVEL_PRIORITY2[this.currentLogLevel];
|
|
4092
|
+
const messagePriority = MESSAGE_LEVEL_PRIORITY2[messageLevel];
|
|
4093
|
+
return messagePriority <= currentLevelPriority;
|
|
4094
|
+
}
|
|
3098
4095
|
/**
|
|
3099
4096
|
* Add a log entry with timestamp and immediately send to runtime
|
|
4097
|
+
* Only logs that pass the log level filter are captured and sent
|
|
3100
4098
|
*/
|
|
3101
4099
|
addLog(level, message, type, data) {
|
|
4100
|
+
if (!this.shouldLog(level)) {
|
|
4101
|
+
return;
|
|
4102
|
+
}
|
|
3102
4103
|
const log = {
|
|
3103
4104
|
timestamp: Date.now(),
|
|
3104
4105
|
level,
|
|
@@ -3108,7 +4109,20 @@ var UILogCollector = class {
|
|
|
3108
4109
|
};
|
|
3109
4110
|
this.logs.push(log);
|
|
3110
4111
|
this.sendLogImmediately(log);
|
|
3111
|
-
|
|
4112
|
+
switch (level) {
|
|
4113
|
+
case "error":
|
|
4114
|
+
logger.error("UILogCollector:", log);
|
|
4115
|
+
break;
|
|
4116
|
+
case "warn":
|
|
4117
|
+
logger.warn("UILogCollector:", log);
|
|
4118
|
+
break;
|
|
4119
|
+
case "info":
|
|
4120
|
+
logger.info("UILogCollector:", log);
|
|
4121
|
+
break;
|
|
4122
|
+
case "debug":
|
|
4123
|
+
logger.debug("UILogCollector:", log);
|
|
4124
|
+
break;
|
|
4125
|
+
}
|
|
3112
4126
|
}
|
|
3113
4127
|
/**
|
|
3114
4128
|
* Send a single log to runtime immediately
|
|
@@ -3238,103 +4252,153 @@ var CONTEXT_CONFIG = {
|
|
|
3238
4252
|
};
|
|
3239
4253
|
|
|
3240
4254
|
// src/handlers/user-prompt-request.ts
|
|
3241
|
-
var
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
const
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
4255
|
+
var get_user_request = async (data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders, collections) => {
|
|
4256
|
+
const errors = [];
|
|
4257
|
+
logger.debug("[USER_PROMPT_REQ] Parsing incoming message data");
|
|
4258
|
+
const parseResult = UserPromptRequestMessageSchema.safeParse(data);
|
|
4259
|
+
if (!parseResult.success) {
|
|
4260
|
+
const zodError = parseResult.error;
|
|
4261
|
+
zodError.errors.forEach((err) => {
|
|
4262
|
+
errors.push(`${err.path.join(".")}: ${err.message}`);
|
|
4263
|
+
});
|
|
4264
|
+
return { success: false, errors };
|
|
4265
|
+
}
|
|
4266
|
+
const userPromptRequest = parseResult.data;
|
|
4267
|
+
const { id, payload } = userPromptRequest;
|
|
4268
|
+
const prompt = payload.prompt;
|
|
4269
|
+
const SA_RUNTIME = payload.SA_RUNTIME;
|
|
4270
|
+
const wsId = userPromptRequest.from.id || "unknown";
|
|
4271
|
+
if (!SA_RUNTIME) {
|
|
4272
|
+
errors.push("SA_RUNTIME is required");
|
|
4273
|
+
}
|
|
4274
|
+
const threadId = SA_RUNTIME?.threadId;
|
|
4275
|
+
const existingUiBlockId = SA_RUNTIME?.uiBlockId;
|
|
4276
|
+
if (!threadId) {
|
|
4277
|
+
errors.push("threadId in SA_RUNTIME is required");
|
|
4278
|
+
}
|
|
4279
|
+
if (!existingUiBlockId) {
|
|
4280
|
+
errors.push("uiBlockId in SA_RUNTIME is required");
|
|
4281
|
+
}
|
|
4282
|
+
if (!prompt) {
|
|
4283
|
+
errors.push("Prompt not found");
|
|
4284
|
+
}
|
|
4285
|
+
logger.debug(`[REQUEST ${id}] Full request details - uiBlockId: ${existingUiBlockId}, threadId: ${threadId}, prompt: ${prompt}`);
|
|
4286
|
+
if (errors.length > 0) {
|
|
4287
|
+
return { success: false, errors, id, wsId };
|
|
4288
|
+
}
|
|
4289
|
+
const logCollector = new UILogCollector(wsId, sendMessage, existingUiBlockId);
|
|
4290
|
+
const threadManager = ThreadManager.getInstance();
|
|
4291
|
+
let thread = threadManager.getThread(threadId);
|
|
4292
|
+
if (!thread) {
|
|
4293
|
+
thread = threadManager.createThread(threadId);
|
|
4294
|
+
logger.info(`Created new thread: ${threadId}`);
|
|
4295
|
+
}
|
|
4296
|
+
logCollector.info(`Starting user prompt request with ${components.length} components`);
|
|
4297
|
+
const conversationHistory = thread.getConversationContext(CONTEXT_CONFIG.MAX_CONVERSATION_CONTEXT_BLOCKS, existingUiBlockId);
|
|
4298
|
+
logger.info("conversationHistory", conversationHistory);
|
|
4299
|
+
const responseMode = payload.responseMode || "component";
|
|
4300
|
+
logger.info("responseMode", responseMode);
|
|
4301
|
+
let streamCallback;
|
|
4302
|
+
let accumulatedStreamResponse = "";
|
|
4303
|
+
if (responseMode === "text") {
|
|
4304
|
+
streamCallback = (chunk) => {
|
|
4305
|
+
accumulatedStreamResponse += chunk;
|
|
4306
|
+
logger.debug(`[STREAM] Sending chunk (${chunk.length} chars): "${chunk.substring(0, 20)}..."`);
|
|
4307
|
+
const streamMessage = {
|
|
4308
|
+
id: `stream_${existingUiBlockId}`,
|
|
4309
|
+
// Different ID pattern for streaming
|
|
4310
|
+
type: "USER_PROMPT_STREAM",
|
|
4311
|
+
from: { type: "data-agent" },
|
|
4312
|
+
to: {
|
|
4313
|
+
type: "runtime",
|
|
4314
|
+
id: wsId
|
|
4315
|
+
},
|
|
4316
|
+
payload: {
|
|
4317
|
+
uiBlockId: existingUiBlockId,
|
|
4318
|
+
chunk
|
|
4319
|
+
}
|
|
4320
|
+
};
|
|
4321
|
+
sendMessage(streamMessage);
|
|
4322
|
+
logger.debug(`[STREAM] Chunk sent to wsId: ${wsId}`);
|
|
4323
|
+
};
|
|
4324
|
+
}
|
|
4325
|
+
const userResponse = await get_user_response(
|
|
4326
|
+
prompt,
|
|
4327
|
+
components,
|
|
4328
|
+
anthropicApiKey,
|
|
4329
|
+
groqApiKey,
|
|
4330
|
+
llmProviders,
|
|
4331
|
+
logCollector,
|
|
4332
|
+
conversationHistory,
|
|
4333
|
+
responseMode,
|
|
4334
|
+
streamCallback,
|
|
4335
|
+
collections
|
|
4336
|
+
);
|
|
4337
|
+
logCollector.info("User prompt request completed");
|
|
4338
|
+
const uiBlockId = existingUiBlockId;
|
|
4339
|
+
if (!userResponse.success) {
|
|
4340
|
+
logger.error(`User prompt request failed with errors: ${userResponse.errors.join(", ")}`);
|
|
4341
|
+
return {
|
|
4342
|
+
success: false,
|
|
4343
|
+
data: userResponse.data,
|
|
4344
|
+
errors: userResponse.errors,
|
|
4345
|
+
uiBlockId,
|
|
4346
|
+
threadId,
|
|
4347
|
+
id,
|
|
4348
|
+
wsId
|
|
4349
|
+
};
|
|
4350
|
+
}
|
|
4351
|
+
let component = null;
|
|
4352
|
+
let textResponse = null;
|
|
4353
|
+
if (userResponse.data) {
|
|
4354
|
+
if (typeof userResponse.data === "object") {
|
|
4355
|
+
if ("component" in userResponse.data) {
|
|
4356
|
+
component = userResponse.data.component;
|
|
4357
|
+
}
|
|
4358
|
+
if ("textResponse" in userResponse.data) {
|
|
4359
|
+
textResponse = userResponse.data.text;
|
|
3259
4360
|
}
|
|
3260
4361
|
}
|
|
3261
|
-
if (!SA_RUNTIME) {
|
|
3262
|
-
sendDataResponse4(id, {
|
|
3263
|
-
success: false,
|
|
3264
|
-
error: "SA_RUNTIME is required"
|
|
3265
|
-
}, sendMessage, wsId);
|
|
3266
|
-
return;
|
|
3267
|
-
}
|
|
3268
|
-
const threadId = SA_RUNTIME.threadId;
|
|
3269
|
-
const existingUiBlockId = SA_RUNTIME.uiBlockId;
|
|
3270
|
-
if (!threadId) {
|
|
3271
|
-
sendDataResponse4(id, {
|
|
3272
|
-
success: false,
|
|
3273
|
-
error: "threadId in SA_RUNTIME is required"
|
|
3274
|
-
}, sendMessage, wsId);
|
|
3275
|
-
return;
|
|
3276
|
-
}
|
|
3277
|
-
if (!existingUiBlockId) {
|
|
3278
|
-
sendDataResponse4(id, {
|
|
3279
|
-
success: false,
|
|
3280
|
-
error: "uiBlockId in SA_RUNTIME is required"
|
|
3281
|
-
}, sendMessage, wsId);
|
|
3282
|
-
return;
|
|
3283
|
-
}
|
|
3284
|
-
const logCollector = new UILogCollector(wsId, sendMessage, existingUiBlockId);
|
|
3285
|
-
if (!prompt) {
|
|
3286
|
-
sendDataResponse4(id, {
|
|
3287
|
-
success: false,
|
|
3288
|
-
error: "Prompt not found"
|
|
3289
|
-
}, sendMessage, wsId);
|
|
3290
|
-
return;
|
|
3291
|
-
}
|
|
3292
|
-
if (!components || components.length === 0) {
|
|
3293
|
-
sendDataResponse4(id, {
|
|
3294
|
-
success: false,
|
|
3295
|
-
error: "Components not found"
|
|
3296
|
-
}, sendMessage, wsId);
|
|
3297
|
-
return;
|
|
3298
|
-
}
|
|
3299
|
-
const threadManager = ThreadManager.getInstance();
|
|
3300
|
-
let thread = threadManager.getThread(threadId);
|
|
3301
|
-
if (!thread) {
|
|
3302
|
-
thread = threadManager.createThread(threadId);
|
|
3303
|
-
logger.info(`Created new thread: ${threadId}`);
|
|
3304
|
-
}
|
|
3305
|
-
logCollector.info(`Starting user prompt request with ${components.length} components`);
|
|
3306
|
-
const conversationHistory = thread.getConversationContext(CONTEXT_CONFIG.MAX_CONVERSATION_CONTEXT_BLOCKS, existingUiBlockId);
|
|
3307
|
-
logger.info("conversationHistory", conversationHistory);
|
|
3308
|
-
const userResponse = await get_user_response(prompt, components, anthropicApiKey, groqApiKey, llmProviders, logCollector, conversationHistory);
|
|
3309
|
-
logger.info("llm userResponse", userResponse);
|
|
3310
|
-
logCollector.info("User prompt request completed");
|
|
3311
|
-
if (userResponse.success && userResponse.data && typeof userResponse.data === "object" && "component" in userResponse.data) {
|
|
3312
|
-
const component = userResponse.data.component;
|
|
3313
|
-
const uiBlockId = existingUiBlockId;
|
|
3314
|
-
const uiBlock = new UIBlock(
|
|
3315
|
-
prompt,
|
|
3316
|
-
{},
|
|
3317
|
-
// componentData: initially empty, will be filled later
|
|
3318
|
-
component || {},
|
|
3319
|
-
// generatedComponentMetadata: full component object (ComponentSchema)
|
|
3320
|
-
[],
|
|
3321
|
-
// actions: empty initially
|
|
3322
|
-
uiBlockId
|
|
3323
|
-
);
|
|
3324
|
-
thread.addUIBlock(uiBlock);
|
|
3325
|
-
logger.info(`Created UIBlock: ${uiBlockId} in Thread: ${threadId}`);
|
|
3326
|
-
sendDataResponse4(id, {
|
|
3327
|
-
...userResponse,
|
|
3328
|
-
uiBlockId,
|
|
3329
|
-
threadId
|
|
3330
|
-
}, sendMessage, wsId);
|
|
3331
|
-
} else {
|
|
3332
|
-
sendDataResponse4(id, userResponse, sendMessage, wsId);
|
|
3333
|
-
}
|
|
3334
|
-
return;
|
|
3335
|
-
} catch (error) {
|
|
3336
|
-
logger.error("Failed to handle user prompt request:", error);
|
|
3337
4362
|
}
|
|
4363
|
+
const finalTextResponse = responseMode === "text" && accumulatedStreamResponse ? accumulatedStreamResponse : textResponse;
|
|
4364
|
+
const uiBlock = new UIBlock(
|
|
4365
|
+
prompt,
|
|
4366
|
+
{},
|
|
4367
|
+
// componentData: initially empty, will be filled later
|
|
4368
|
+
component,
|
|
4369
|
+
// generatedComponentMetadata: full component object (ComponentSchema)
|
|
4370
|
+
[],
|
|
4371
|
+
// actions: empty initially
|
|
4372
|
+
uiBlockId,
|
|
4373
|
+
finalTextResponse
|
|
4374
|
+
// textResponse: FULL streaming response including all intermediate messages
|
|
4375
|
+
);
|
|
4376
|
+
thread.addUIBlock(uiBlock);
|
|
4377
|
+
logger.info(`Created UIBlock: ${uiBlockId} in Thread: ${threadId}`);
|
|
4378
|
+
return {
|
|
4379
|
+
success: userResponse.success,
|
|
4380
|
+
data: userResponse.data,
|
|
4381
|
+
errors: userResponse.errors,
|
|
4382
|
+
uiBlockId,
|
|
4383
|
+
threadId,
|
|
4384
|
+
id,
|
|
4385
|
+
wsId
|
|
4386
|
+
};
|
|
4387
|
+
};
|
|
4388
|
+
async function handleUserPromptRequest(data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders, collections) {
|
|
4389
|
+
const response = await get_user_request(data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders, collections);
|
|
4390
|
+
sendDataResponse4(
|
|
4391
|
+
response.id || data.id,
|
|
4392
|
+
{
|
|
4393
|
+
success: response.success,
|
|
4394
|
+
errors: response.errors,
|
|
4395
|
+
data: response.data,
|
|
4396
|
+
uiBlockId: response.uiBlockId,
|
|
4397
|
+
threadId: response.threadId
|
|
4398
|
+
},
|
|
4399
|
+
sendMessage,
|
|
4400
|
+
response.wsId || data.from?.id
|
|
4401
|
+
);
|
|
3338
4402
|
}
|
|
3339
4403
|
function sendDataResponse4(id, res, sendMessage, clientId) {
|
|
3340
4404
|
const response = {
|
|
@@ -3447,12 +4511,27 @@ function sendResponse(id, res, sendMessage, clientId) {
|
|
|
3447
4511
|
// src/userResponse/next-questions.ts
|
|
3448
4512
|
async function generateNextQuestions(originalUserPrompt, component, componentData, anthropicApiKey, groqApiKey, llmProviders, logCollector, conversationHistory) {
|
|
3449
4513
|
try {
|
|
4514
|
+
logger.debug("[generateNextQuestions] Starting next questions generation");
|
|
4515
|
+
logger.debug(`[generateNextQuestions] User prompt: "${originalUserPrompt?.substring(0, 50)}..."`);
|
|
4516
|
+
logger.debug(`[generateNextQuestions] Component: ${component?.name || "unknown"} (${component?.type || "unknown"})`);
|
|
4517
|
+
logger.debug(`[generateNextQuestions] Component data available: ${componentData ? "yes" : "no"}`);
|
|
3450
4518
|
const providers = llmProviders || ["anthropic"];
|
|
3451
|
-
|
|
4519
|
+
logger.info(`[generateNextQuestions] Using LLM providers: [${providers.join(", ")}]`);
|
|
4520
|
+
if (conversationHistory && conversationHistory.length > 0) {
|
|
4521
|
+
const exchangeCount = conversationHistory.split("\n").filter((l) => l.startsWith("Q")).length;
|
|
4522
|
+
logger.debug(`[generateNextQuestions] Using conversation history with ${exchangeCount} previous exchanges`);
|
|
4523
|
+
} else {
|
|
4524
|
+
logger.debug("[generateNextQuestions] No conversation history available");
|
|
4525
|
+
}
|
|
4526
|
+
for (let i = 0; i < providers.length; i++) {
|
|
4527
|
+
const provider = providers[i];
|
|
4528
|
+
const isLastProvider = i === providers.length - 1;
|
|
3452
4529
|
try {
|
|
3453
|
-
logger.info(`
|
|
4530
|
+
logger.info(`[generateNextQuestions] Attempting provider: ${provider} (${i + 1}/${providers.length})`);
|
|
4531
|
+
logCollector?.info(`Generating questions with ${provider}...`);
|
|
3454
4532
|
let result = [];
|
|
3455
4533
|
if (provider === "groq") {
|
|
4534
|
+
logger.debug("[generateNextQuestions] Using Groq LLM for next questions");
|
|
3456
4535
|
result = await groqLLM.generateNextQuestions(
|
|
3457
4536
|
originalUserPrompt,
|
|
3458
4537
|
component,
|
|
@@ -3462,6 +4541,7 @@ async function generateNextQuestions(originalUserPrompt, component, componentDat
|
|
|
3462
4541
|
conversationHistory
|
|
3463
4542
|
);
|
|
3464
4543
|
} else {
|
|
4544
|
+
logger.debug("[generateNextQuestions] Using Anthropic LLM for next questions");
|
|
3465
4545
|
result = await anthropicLLM.generateNextQuestions(
|
|
3466
4546
|
originalUserPrompt,
|
|
3467
4547
|
component,
|
|
@@ -3472,21 +4552,39 @@ async function generateNextQuestions(originalUserPrompt, component, componentDat
|
|
|
3472
4552
|
);
|
|
3473
4553
|
}
|
|
3474
4554
|
if (result && result.length > 0) {
|
|
3475
|
-
logger.info(`Successfully generated ${result.length} questions with ${provider}`);
|
|
4555
|
+
logger.info(`[generateNextQuestions] Successfully generated ${result.length} questions with ${provider}`);
|
|
4556
|
+
logger.debug(`[generateNextQuestions] Questions: ${JSON.stringify(result)}`);
|
|
4557
|
+
logCollector?.info(`Generated ${result.length} follow-up questions`);
|
|
3476
4558
|
return result;
|
|
3477
4559
|
}
|
|
3478
|
-
|
|
4560
|
+
const warnMsg = `No questions generated from ${provider}${!isLastProvider ? ", trying next provider..." : ""}`;
|
|
4561
|
+
logger.warn(`[generateNextQuestions] ${warnMsg}`);
|
|
4562
|
+
if (!isLastProvider) {
|
|
4563
|
+
logCollector?.warn(warnMsg);
|
|
4564
|
+
}
|
|
3479
4565
|
} catch (providerError) {
|
|
3480
|
-
|
|
3481
|
-
|
|
4566
|
+
const errorMsg = providerError instanceof Error ? providerError.message : String(providerError);
|
|
4567
|
+
logger.error(`[generateNextQuestions] Provider ${provider} failed: ${errorMsg}`);
|
|
4568
|
+
logger.debug(`[generateNextQuestions] Provider error details:`, providerError);
|
|
4569
|
+
if (!isLastProvider) {
|
|
4570
|
+
const fallbackMsg = `Provider ${provider} failed, trying next provider...`;
|
|
4571
|
+
logger.info(`[generateNextQuestions] ${fallbackMsg}`);
|
|
4572
|
+
logCollector?.warn(fallbackMsg);
|
|
4573
|
+
} else {
|
|
4574
|
+
logCollector?.error(`Failed to generate questions with ${provider}`);
|
|
4575
|
+
}
|
|
3482
4576
|
continue;
|
|
3483
4577
|
}
|
|
3484
4578
|
}
|
|
3485
|
-
logger.warn("All providers failed or returned no questions");
|
|
4579
|
+
logger.warn("[generateNextQuestions] All providers failed or returned no questions");
|
|
4580
|
+
logCollector?.warn("Unable to generate follow-up questions");
|
|
3486
4581
|
return [];
|
|
3487
4582
|
} catch (error) {
|
|
3488
|
-
|
|
3489
|
-
|
|
4583
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
4584
|
+
const errorStack = error instanceof Error ? error.stack : void 0;
|
|
4585
|
+
logger.error(`[generateNextQuestions] Error generating next questions: ${errorMsg}`);
|
|
4586
|
+
logger.debug("[generateNextQuestions] Error stack trace:", errorStack);
|
|
4587
|
+
logCollector?.error(`Error generating next questions: ${errorMsg}`);
|
|
3490
4588
|
return [];
|
|
3491
4589
|
}
|
|
3492
4590
|
}
|
|
@@ -3494,11 +4592,15 @@ async function generateNextQuestions(originalUserPrompt, component, componentDat
|
|
|
3494
4592
|
// src/handlers/actions-request.ts
|
|
3495
4593
|
async function handleActionsRequest(data, sendMessage, anthropicApiKey, groqApiKey, llmProviders) {
|
|
3496
4594
|
try {
|
|
4595
|
+
logger.debug("[ACTIONS_REQ] Parsing incoming actions request");
|
|
3497
4596
|
const actionsRequest = ActionsRequestMessageSchema.parse(data);
|
|
3498
4597
|
const { id, payload } = actionsRequest;
|
|
3499
4598
|
const { SA_RUNTIME } = payload;
|
|
3500
4599
|
const wsId = actionsRequest.from.id || "unknown";
|
|
4600
|
+
logger.info(`[ACTIONS_REQ ${id}] Processing actions request from client: ${wsId}`);
|
|
4601
|
+
logger.debug(`[ACTIONS_REQ ${id}] Request payload:`, JSON.stringify(payload, null, 2).substring(0, 200));
|
|
3501
4602
|
if (!SA_RUNTIME) {
|
|
4603
|
+
logger.error(`[ACTIONS_REQ ${id}] SA_RUNTIME missing from request`);
|
|
3502
4604
|
sendResponse2(id, {
|
|
3503
4605
|
success: false,
|
|
3504
4606
|
error: "SA_RUNTIME with threadId and uiBlockId is required"
|
|
@@ -3507,31 +4609,54 @@ async function handleActionsRequest(data, sendMessage, anthropicApiKey, groqApiK
|
|
|
3507
4609
|
}
|
|
3508
4610
|
const uiBlockId = SA_RUNTIME.uiBlockId;
|
|
3509
4611
|
const threadId = SA_RUNTIME.threadId;
|
|
4612
|
+
logger.debug(`[ACTIONS_REQ ${id}] SA_RUNTIME validated - threadId: ${threadId}, uiBlockId: ${uiBlockId}`);
|
|
4613
|
+
logger.debug(`[ACTIONS_REQ ${id}] Retrieving thread: ${threadId}`);
|
|
3510
4614
|
const threadManager = ThreadManager.getInstance();
|
|
3511
4615
|
const thread = threadManager.getThread(threadId);
|
|
3512
4616
|
if (!thread) {
|
|
4617
|
+
logger.error(`[ACTIONS_REQ ${id}] Thread '${threadId}' not found`);
|
|
3513
4618
|
sendResponse2(id, {
|
|
3514
4619
|
success: false,
|
|
3515
4620
|
error: `Thread '${threadId}' not found`
|
|
3516
4621
|
}, sendMessage, wsId);
|
|
3517
4622
|
return;
|
|
3518
4623
|
}
|
|
4624
|
+
logger.debug(`[ACTIONS_REQ ${id}] Thread found with ${thread.getUIBlocks().length} UIBlocks`);
|
|
4625
|
+
logger.debug(`[ACTIONS_REQ ${id}] Retrieving UIBlock: ${uiBlockId}`);
|
|
3519
4626
|
const uiBlock = thread.getUIBlock(uiBlockId);
|
|
3520
4627
|
if (!uiBlock) {
|
|
4628
|
+
logger.error(`[ACTIONS_REQ ${id}] UIBlock '${uiBlockId}' not found in thread '${threadId}'`);
|
|
3521
4629
|
sendResponse2(id, {
|
|
3522
4630
|
success: false,
|
|
3523
4631
|
error: `UIBlock '${uiBlockId}' not found in thread '${threadId}'`
|
|
3524
4632
|
}, sendMessage, wsId);
|
|
3525
4633
|
return;
|
|
3526
4634
|
}
|
|
4635
|
+
logger.info(`[ACTIONS_REQ ${id}] UIBlock retrieved successfully`);
|
|
4636
|
+
logger.debug(`[ACTIONS_REQ ${id}] Creating UILogCollector for uiBlockId: ${uiBlockId}`);
|
|
3527
4637
|
const logCollector = new UILogCollector(wsId, sendMessage, uiBlockId);
|
|
4638
|
+
logger.info(`[ACTIONS_REQ ${id}] UILogCollector initialized`);
|
|
4639
|
+
logger.debug(`[ACTIONS_REQ ${id}] Extracting data from UIBlock`);
|
|
3528
4640
|
const userQuestion = uiBlock.getUserQuestion();
|
|
3529
4641
|
const component = uiBlock.getComponentMetadata();
|
|
3530
4642
|
const componentData = uiBlock.getComponentData();
|
|
4643
|
+
logger.debug(`[ACTIONS_REQ ${id}] User question: "${userQuestion?.substring(0, 50)}..."`);
|
|
4644
|
+
logger.debug(`[ACTIONS_REQ ${id}] Component: ${component?.name || "unknown"} (${component?.type || "unknown"})`);
|
|
4645
|
+
logger.debug(`[ACTIONS_REQ ${id}] Component data available: ${componentData ? "yes" : "no"}`);
|
|
4646
|
+
logger.debug(`[ACTIONS_REQ ${id}] Extracting conversation history (max ${CONTEXT_CONFIG.MAX_CONVERSATION_CONTEXT_BLOCKS} blocks)`);
|
|
3531
4647
|
const conversationHistory = thread.getConversationContext(CONTEXT_CONFIG.MAX_CONVERSATION_CONTEXT_BLOCKS, uiBlockId);
|
|
4648
|
+
const historyLineCount = conversationHistory.split("\n").filter((l) => l.trim()).length;
|
|
4649
|
+
logger.info(`[ACTIONS_REQ ${id}] Conversation history extracted: ${historyLineCount} lines`);
|
|
4650
|
+
logger.debug(`[ACTIONS_REQ ${id}] Conversation history preview:
|
|
4651
|
+
${conversationHistory.substring(0, 200)}...`);
|
|
3532
4652
|
logCollector.info(`Generating actions for UIBlock: ${uiBlockId}`);
|
|
3533
|
-
logger.info(`Generating actions for component: ${component?.name || "unknown"}`);
|
|
4653
|
+
logger.info(`[ACTIONS_REQ ${id}] Generating actions for component: ${component?.name || "unknown"}`);
|
|
4654
|
+
logger.debug(`[ACTIONS_REQ ${id}] Checking if actions are already cached`);
|
|
4655
|
+
const startTime = Date.now();
|
|
3534
4656
|
const actions = await uiBlock.getOrFetchActions(async () => {
|
|
4657
|
+
logger.info(`[ACTIONS_REQ ${id}] Actions not cached, generating new actions...`);
|
|
4658
|
+
logCollector.info("Generating follow-up questions...");
|
|
4659
|
+
logger.info(`[ACTIONS_REQ ${id}] Starting next questions generation with ${llmProviders?.join(", ") || "default"} providers`);
|
|
3535
4660
|
const nextQuestions = await generateNextQuestions(
|
|
3536
4661
|
userQuestion,
|
|
3537
4662
|
component,
|
|
@@ -3542,14 +4667,28 @@ async function handleActionsRequest(data, sendMessage, anthropicApiKey, groqApiK
|
|
|
3542
4667
|
logCollector,
|
|
3543
4668
|
conversationHistory
|
|
3544
4669
|
);
|
|
3545
|
-
|
|
4670
|
+
logger.info(`[ACTIONS_REQ ${id}] Generated ${nextQuestions.length} questions`);
|
|
4671
|
+
logger.debug(`[ACTIONS_REQ ${id}] Questions: ${JSON.stringify(nextQuestions)}`);
|
|
4672
|
+
logger.debug(`[ACTIONS_REQ ${id}] Converting questions to actions format`);
|
|
4673
|
+
const convertedActions = nextQuestions.map((question, index) => ({
|
|
3546
4674
|
id: `action_${index}_${Date.now()}`,
|
|
3547
4675
|
name: question,
|
|
3548
4676
|
type: "next_question",
|
|
3549
4677
|
question
|
|
3550
4678
|
}));
|
|
4679
|
+
logger.debug(`[ACTIONS_REQ ${id}] Converted ${convertedActions.length} actions`);
|
|
4680
|
+
return convertedActions;
|
|
3551
4681
|
});
|
|
3552
|
-
|
|
4682
|
+
const processingTime = Date.now() - startTime;
|
|
4683
|
+
logger.info(`[ACTIONS_REQ ${id}] Actions retrieved in ${processingTime}ms - ${actions.length} actions total`);
|
|
4684
|
+
if (actions.length > 0) {
|
|
4685
|
+
logCollector.info(`Generated ${actions.length} follow-up questions successfully`);
|
|
4686
|
+
logger.debug(`[ACTIONS_REQ ${id}] Actions: ${actions.map((a) => a.name).join(", ")}`);
|
|
4687
|
+
} else {
|
|
4688
|
+
logger.warn(`[ACTIONS_REQ ${id}] No actions generated`);
|
|
4689
|
+
logCollector.warn("No follow-up questions could be generated");
|
|
4690
|
+
}
|
|
4691
|
+
logger.debug(`[ACTIONS_REQ ${id}] Sending successful response to client`);
|
|
3553
4692
|
sendResponse2(id, {
|
|
3554
4693
|
success: true,
|
|
3555
4694
|
data: {
|
|
@@ -3560,12 +4699,26 @@ async function handleActionsRequest(data, sendMessage, anthropicApiKey, groqApiK
|
|
|
3560
4699
|
threadId
|
|
3561
4700
|
}
|
|
3562
4701
|
}, sendMessage, wsId);
|
|
4702
|
+
logger.info(`[ACTIONS_REQ ${id}] \u2713 Actions request completed successfully`);
|
|
3563
4703
|
} catch (error) {
|
|
3564
|
-
|
|
4704
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
4705
|
+
const errorStack = error instanceof Error ? error.stack : void 0;
|
|
4706
|
+
logger.error(`[ACTIONS_REQ] Failed to handle actions request: ${errorMessage}`);
|
|
4707
|
+
logger.debug(`[ACTIONS_REQ] Error stack trace:`, errorStack);
|
|
4708
|
+
try {
|
|
4709
|
+
const parsedData = data;
|
|
4710
|
+
if (parsedData?.id && parsedData?.from?.id) {
|
|
4711
|
+
const logCollector = parsedData?.payload?.SA_RUNTIME?.uiBlockId ? new UILogCollector(parsedData.from.id, sendMessage, parsedData.payload.SA_RUNTIME.uiBlockId) : void 0;
|
|
4712
|
+
logCollector?.error(`Failed to generate actions: ${errorMessage}`);
|
|
4713
|
+
}
|
|
4714
|
+
} catch (logError) {
|
|
4715
|
+
logger.debug("[ACTIONS_REQ] Failed to send error logs to UI:", logError);
|
|
4716
|
+
}
|
|
3565
4717
|
sendResponse2(null, {
|
|
3566
4718
|
success: false,
|
|
3567
|
-
error:
|
|
4719
|
+
error: errorMessage
|
|
3568
4720
|
}, sendMessage);
|
|
4721
|
+
logger.info("[ACTIONS_REQ] \u2717 Actions request completed with errors");
|
|
3569
4722
|
}
|
|
3570
4723
|
}
|
|
3571
4724
|
function sendResponse2(id, res, sendMessage, clientId) {
|
|
@@ -3581,6 +4734,11 @@ function sendResponse2(id, res, sendMessage, clientId) {
|
|
|
3581
4734
|
...res
|
|
3582
4735
|
}
|
|
3583
4736
|
};
|
|
4737
|
+
logger.debug(`[ACTIONS_RES ${id || "unknown"}] Sending ${res.success ? "successful" : "failed"} response to client`);
|
|
4738
|
+
logger.debug(`[ACTIONS_RES ${id || "unknown"}] Response payload size: ${JSON.stringify(response).length} bytes`);
|
|
4739
|
+
if (res.data?.actions) {
|
|
4740
|
+
logger.debug(`[ACTIONS_RES ${id || "unknown"}] Sending ${res.data.actions.length} actions`);
|
|
4741
|
+
}
|
|
3584
4742
|
sendMessage(response);
|
|
3585
4743
|
}
|
|
3586
4744
|
|
|
@@ -3609,7 +4767,10 @@ async function handleUsersRequest(data, sendMessage) {
|
|
|
3609
4767
|
const { id, payload, from } = request;
|
|
3610
4768
|
const { operation, data: requestData } = payload;
|
|
3611
4769
|
const username = requestData?.username;
|
|
4770
|
+
const email = requestData?.email;
|
|
3612
4771
|
const password = requestData?.password;
|
|
4772
|
+
const fullname = requestData?.fullname;
|
|
4773
|
+
const role = requestData?.role;
|
|
3613
4774
|
if (from.type !== "admin") {
|
|
3614
4775
|
sendResponse3(id, {
|
|
3615
4776
|
success: false,
|
|
@@ -3621,10 +4782,10 @@ async function handleUsersRequest(data, sendMessage) {
|
|
|
3621
4782
|
const userManager = getUserManager();
|
|
3622
4783
|
switch (operation) {
|
|
3623
4784
|
case "create":
|
|
3624
|
-
await handleCreate(id, username, password, userManager, sendMessage, from.id);
|
|
4785
|
+
await handleCreate(id, { username, email, password, fullname, role }, userManager, sendMessage, from.id);
|
|
3625
4786
|
break;
|
|
3626
4787
|
case "update":
|
|
3627
|
-
await handleUpdate(id, username, password, userManager, sendMessage, from.id);
|
|
4788
|
+
await handleUpdate(id, { username, email, password, fullname, role }, userManager, sendMessage, from.id);
|
|
3628
4789
|
break;
|
|
3629
4790
|
case "delete":
|
|
3630
4791
|
await handleDelete(id, username, userManager, sendMessage, from.id);
|
|
@@ -3649,7 +4810,8 @@ async function handleUsersRequest(data, sendMessage) {
|
|
|
3649
4810
|
}, sendMessage);
|
|
3650
4811
|
}
|
|
3651
4812
|
}
|
|
3652
|
-
async function handleCreate(id,
|
|
4813
|
+
async function handleCreate(id, userData, userManager, sendMessage, clientId) {
|
|
4814
|
+
const { username, email, password, fullname, role } = userData;
|
|
3653
4815
|
if (!username || username.trim().length === 0) {
|
|
3654
4816
|
sendResponse3(id, {
|
|
3655
4817
|
success: false,
|
|
@@ -3664,6 +4826,16 @@ async function handleCreate(id, username, password, userManager, sendMessage, cl
|
|
|
3664
4826
|
}, sendMessage, clientId);
|
|
3665
4827
|
return;
|
|
3666
4828
|
}
|
|
4829
|
+
if (email && email.trim().length > 0) {
|
|
4830
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
4831
|
+
if (!emailRegex.test(email)) {
|
|
4832
|
+
sendResponse3(id, {
|
|
4833
|
+
success: false,
|
|
4834
|
+
error: "Invalid email format"
|
|
4835
|
+
}, sendMessage, clientId);
|
|
4836
|
+
return;
|
|
4837
|
+
}
|
|
4838
|
+
}
|
|
3667
4839
|
if (userManager.userExists(username)) {
|
|
3668
4840
|
sendResponse3(id, {
|
|
3669
4841
|
success: false,
|
|
@@ -3671,25 +4843,41 @@ async function handleCreate(id, username, password, userManager, sendMessage, cl
|
|
|
3671
4843
|
}, sendMessage, clientId);
|
|
3672
4844
|
return;
|
|
3673
4845
|
}
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
4846
|
+
if (email && userManager.getUserByEmail(email)) {
|
|
4847
|
+
sendResponse3(id, {
|
|
4848
|
+
success: false,
|
|
4849
|
+
error: `User with email '${email}' already exists`
|
|
4850
|
+
}, sendMessage, clientId);
|
|
4851
|
+
return;
|
|
3677
4852
|
}
|
|
3678
|
-
const
|
|
4853
|
+
const newUserData = {
|
|
3679
4854
|
username,
|
|
3680
|
-
password
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
4855
|
+
password
|
|
4856
|
+
};
|
|
4857
|
+
if (email && email.trim().length > 0) {
|
|
4858
|
+
newUserData.email = email.trim();
|
|
4859
|
+
}
|
|
4860
|
+
if (fullname && fullname.trim().length > 0) {
|
|
4861
|
+
newUserData.fullname = fullname.trim();
|
|
4862
|
+
}
|
|
4863
|
+
if (role && role.trim().length > 0) {
|
|
4864
|
+
newUserData.role = role.trim();
|
|
4865
|
+
}
|
|
4866
|
+
const newUser = userManager.createUser(newUserData);
|
|
4867
|
+
logger.info(`User created by admin: ${username}${email ? ` (${email})` : ""}`);
|
|
3684
4868
|
sendResponse3(id, {
|
|
3685
4869
|
success: true,
|
|
3686
4870
|
data: {
|
|
3687
4871
|
username: newUser.username,
|
|
4872
|
+
email: newUser.email,
|
|
4873
|
+
fullname: newUser.fullname,
|
|
4874
|
+
role: newUser.role,
|
|
3688
4875
|
message: `User '${username}' created successfully`
|
|
3689
4876
|
}
|
|
3690
4877
|
}, sendMessage, clientId);
|
|
3691
4878
|
}
|
|
3692
|
-
async function handleUpdate(id,
|
|
4879
|
+
async function handleUpdate(id, userData, userManager, sendMessage, clientId) {
|
|
4880
|
+
const { username, email, password, fullname, role } = userData;
|
|
3693
4881
|
if (!username || username.trim().length === 0) {
|
|
3694
4882
|
sendResponse3(id, {
|
|
3695
4883
|
success: false,
|
|
@@ -3705,13 +4893,42 @@ async function handleUpdate(id, username, password, userManager, sendMessage, cl
|
|
|
3705
4893
|
return;
|
|
3706
4894
|
}
|
|
3707
4895
|
const updates = {};
|
|
3708
|
-
if (
|
|
3709
|
-
|
|
4896
|
+
if (email !== void 0) {
|
|
4897
|
+
if (email.trim().length > 0) {
|
|
4898
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
4899
|
+
if (!emailRegex.test(email)) {
|
|
4900
|
+
sendResponse3(id, {
|
|
4901
|
+
success: false,
|
|
4902
|
+
error: "Invalid email format"
|
|
4903
|
+
}, sendMessage, clientId);
|
|
4904
|
+
return;
|
|
4905
|
+
}
|
|
4906
|
+
const existingUser = userManager.getUserByEmail(email);
|
|
4907
|
+
if (existingUser && existingUser.username !== username) {
|
|
4908
|
+
sendResponse3(id, {
|
|
4909
|
+
success: false,
|
|
4910
|
+
error: `Email '${email}' is already used by another user`
|
|
4911
|
+
}, sendMessage, clientId);
|
|
4912
|
+
return;
|
|
4913
|
+
}
|
|
4914
|
+
updates.email = email.trim();
|
|
4915
|
+
} else {
|
|
4916
|
+
updates.email = void 0;
|
|
4917
|
+
}
|
|
4918
|
+
}
|
|
4919
|
+
if (password !== void 0 && password.trim().length > 0) {
|
|
4920
|
+
updates.password = password.trim();
|
|
4921
|
+
}
|
|
4922
|
+
if (fullname !== void 0) {
|
|
4923
|
+
updates.fullname = fullname.trim().length > 0 ? fullname.trim() : void 0;
|
|
4924
|
+
}
|
|
4925
|
+
if (role !== void 0) {
|
|
4926
|
+
updates.role = role.trim().length > 0 ? role.trim() : void 0;
|
|
3710
4927
|
}
|
|
3711
4928
|
if (Object.keys(updates).length === 0) {
|
|
3712
4929
|
sendResponse3(id, {
|
|
3713
4930
|
success: false,
|
|
3714
|
-
error: "No fields to update. Please provide
|
|
4931
|
+
error: "No fields to update. Please provide at least one field to update."
|
|
3715
4932
|
}, sendMessage, clientId);
|
|
3716
4933
|
return;
|
|
3717
4934
|
}
|
|
@@ -3721,6 +4938,9 @@ async function handleUpdate(id, username, password, userManager, sendMessage, cl
|
|
|
3721
4938
|
success: true,
|
|
3722
4939
|
data: {
|
|
3723
4940
|
username: updatedUser.username,
|
|
4941
|
+
email: updatedUser.email,
|
|
4942
|
+
fullname: updatedUser.fullname,
|
|
4943
|
+
role: updatedUser.role,
|
|
3724
4944
|
message: `User '${username}' updated successfully`
|
|
3725
4945
|
}
|
|
3726
4946
|
}, sendMessage, clientId);
|
|
@@ -3761,6 +4981,9 @@ async function handleGetAll(id, userManager, sendMessage, clientId) {
|
|
|
3761
4981
|
const users = userManager.getAllUsers();
|
|
3762
4982
|
const sanitizedUsers = users.map((user) => ({
|
|
3763
4983
|
username: user.username,
|
|
4984
|
+
email: user.email,
|
|
4985
|
+
fullname: user.fullname,
|
|
4986
|
+
role: user.role,
|
|
3764
4987
|
wsIds: user.wsIds || []
|
|
3765
4988
|
}));
|
|
3766
4989
|
logger.info(`Admin retrieved all users (count: ${sanitizedUsers.length})`);
|
|
@@ -3791,6 +5014,9 @@ async function handleGetOne(id, username, userManager, sendMessage, clientId) {
|
|
|
3791
5014
|
const user = userManager.getUser(username);
|
|
3792
5015
|
const sanitizedUser = {
|
|
3793
5016
|
username: user.username,
|
|
5017
|
+
email: user.email,
|
|
5018
|
+
fullname: user.fullname,
|
|
5019
|
+
role: user.role,
|
|
3794
5020
|
wsIds: user.wsIds || []
|
|
3795
5021
|
};
|
|
3796
5022
|
logger.info(`Admin retrieved user: ${username}`);
|
|
@@ -4236,7 +5462,7 @@ function sendResponse5(id, res, sendMessage, clientId) {
|
|
|
4236
5462
|
}
|
|
4237
5463
|
|
|
4238
5464
|
// src/auth/user-manager.ts
|
|
4239
|
-
var
|
|
5465
|
+
var import_fs4 = __toESM(require("fs"));
|
|
4240
5466
|
var import_path3 = __toESM(require("path"));
|
|
4241
5467
|
var import_os = __toESM(require("os"));
|
|
4242
5468
|
var UserManager = class {
|
|
@@ -4276,21 +5502,22 @@ var UserManager = class {
|
|
|
4276
5502
|
async loadUsersFromFile() {
|
|
4277
5503
|
try {
|
|
4278
5504
|
const dir = import_path3.default.dirname(this.filePath);
|
|
4279
|
-
if (!
|
|
5505
|
+
if (!import_fs4.default.existsSync(dir)) {
|
|
4280
5506
|
logger.info(`Creating directory structure: ${dir}`);
|
|
4281
|
-
|
|
5507
|
+
import_fs4.default.mkdirSync(dir, { recursive: true });
|
|
4282
5508
|
}
|
|
4283
|
-
if (!
|
|
5509
|
+
if (!import_fs4.default.existsSync(this.filePath)) {
|
|
4284
5510
|
logger.info(`Users file does not exist at ${this.filePath}, creating with empty users`);
|
|
4285
5511
|
const initialData = { users: [] };
|
|
4286
|
-
|
|
5512
|
+
import_fs4.default.writeFileSync(this.filePath, JSON.stringify(initialData, null, 4));
|
|
4287
5513
|
this.users = [];
|
|
4288
5514
|
this.hasChanged = false;
|
|
4289
5515
|
return;
|
|
4290
5516
|
}
|
|
4291
|
-
const fileContent =
|
|
4292
|
-
const
|
|
4293
|
-
|
|
5517
|
+
const fileContent = import_fs4.default.readFileSync(this.filePath, "utf-8");
|
|
5518
|
+
const rawData = JSON.parse(fileContent);
|
|
5519
|
+
const validatedData = UsersDataSchema.parse(rawData);
|
|
5520
|
+
this.users = validatedData.users;
|
|
4294
5521
|
this.hasChanged = false;
|
|
4295
5522
|
logger.debug(`Loaded ${this.users.length} users from file`);
|
|
4296
5523
|
} catch (error) {
|
|
@@ -4307,13 +5534,17 @@ var UserManager = class {
|
|
|
4307
5534
|
}
|
|
4308
5535
|
try {
|
|
4309
5536
|
const dir = import_path3.default.dirname(this.filePath);
|
|
4310
|
-
if (!
|
|
4311
|
-
|
|
5537
|
+
if (!import_fs4.default.existsSync(dir)) {
|
|
5538
|
+
import_fs4.default.mkdirSync(dir, { recursive: true });
|
|
4312
5539
|
}
|
|
4313
|
-
const
|
|
4314
|
-
|
|
5540
|
+
const usersToSave = this.users.map((user) => {
|
|
5541
|
+
const { wsIds, ...userWithoutWsIds } = user;
|
|
5542
|
+
return userWithoutWsIds;
|
|
5543
|
+
});
|
|
5544
|
+
const data = { users: usersToSave };
|
|
5545
|
+
import_fs4.default.writeFileSync(this.filePath, JSON.stringify(data, null, 4));
|
|
4315
5546
|
this.hasChanged = false;
|
|
4316
|
-
logger.debug(`Synced ${this.users.length} users to file`);
|
|
5547
|
+
logger.debug(`Synced ${this.users.length} users to file (wsIds excluded)`);
|
|
4317
5548
|
} catch (error) {
|
|
4318
5549
|
logger.error("Failed to save users to file:", error);
|
|
4319
5550
|
throw new Error(`Failed to save users to file: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
@@ -4360,13 +5591,17 @@ var UserManager = class {
|
|
|
4360
5591
|
* @returns The created user
|
|
4361
5592
|
*/
|
|
4362
5593
|
createUser(user) {
|
|
4363
|
-
|
|
4364
|
-
|
|
5594
|
+
const validatedUser = UserSchema.parse(user);
|
|
5595
|
+
if (this.users.some((u) => u.username === validatedUser.username)) {
|
|
5596
|
+
throw new Error(`User with username ${validatedUser.username} already exists`);
|
|
5597
|
+
}
|
|
5598
|
+
if (validatedUser.email && this.users.some((u) => u.email === validatedUser.email)) {
|
|
5599
|
+
throw new Error(`User with email ${validatedUser.email} already exists`);
|
|
4365
5600
|
}
|
|
4366
|
-
this.users.push(
|
|
5601
|
+
this.users.push(validatedUser);
|
|
4367
5602
|
this.hasChanged = true;
|
|
4368
|
-
logger.debug(`User created: ${
|
|
4369
|
-
return
|
|
5603
|
+
logger.debug(`User created: ${validatedUser.username}`);
|
|
5604
|
+
return validatedUser;
|
|
4370
5605
|
}
|
|
4371
5606
|
/**
|
|
4372
5607
|
* Read a user by username
|
|
@@ -4376,6 +5611,22 @@ var UserManager = class {
|
|
|
4376
5611
|
getUser(username) {
|
|
4377
5612
|
return this.users.find((u) => u.username === username);
|
|
4378
5613
|
}
|
|
5614
|
+
/**
|
|
5615
|
+
* Read a user by email
|
|
5616
|
+
* @param email - Email to retrieve
|
|
5617
|
+
* @returns The user if found, undefined otherwise
|
|
5618
|
+
*/
|
|
5619
|
+
getUserByEmail(email) {
|
|
5620
|
+
return this.users.find((u) => u.email === email);
|
|
5621
|
+
}
|
|
5622
|
+
/**
|
|
5623
|
+
* Find user by username or email
|
|
5624
|
+
* @param identifier - Username or email to search for
|
|
5625
|
+
* @returns The user if found, undefined otherwise
|
|
5626
|
+
*/
|
|
5627
|
+
getUserByUsernameOrEmail(identifier) {
|
|
5628
|
+
return this.users.find((u) => u.username === identifier || u.email === identifier);
|
|
5629
|
+
}
|
|
4379
5630
|
/**
|
|
4380
5631
|
* Read all users
|
|
4381
5632
|
* @returns Array of all users
|
|
@@ -4464,7 +5715,6 @@ var UserManager = class {
|
|
|
4464
5715
|
}
|
|
4465
5716
|
if (!user.wsIds.includes(wsId)) {
|
|
4466
5717
|
user.wsIds.push(wsId);
|
|
4467
|
-
this.hasChanged = true;
|
|
4468
5718
|
logger.debug(`WebSocket ID added to user ${username}: ${wsId}`);
|
|
4469
5719
|
}
|
|
4470
5720
|
return true;
|
|
@@ -4486,7 +5736,6 @@ var UserManager = class {
|
|
|
4486
5736
|
const initialLength = user.wsIds.length;
|
|
4487
5737
|
user.wsIds = user.wsIds.filter((id) => id !== wsId);
|
|
4488
5738
|
if (user.wsIds.length < initialLength) {
|
|
4489
|
-
this.hasChanged = true;
|
|
4490
5739
|
logger.debug(`WebSocket ID removed from user ${username}: ${wsId}`);
|
|
4491
5740
|
}
|
|
4492
5741
|
return true;
|
|
@@ -4511,7 +5760,7 @@ var UserManager = class {
|
|
|
4511
5760
|
};
|
|
4512
5761
|
|
|
4513
5762
|
// src/dashboards/dashboard-manager.ts
|
|
4514
|
-
var
|
|
5763
|
+
var import_fs5 = __toESM(require("fs"));
|
|
4515
5764
|
var import_path4 = __toESM(require("path"));
|
|
4516
5765
|
var import_os2 = __toESM(require("os"));
|
|
4517
5766
|
var DashboardManager = class {
|
|
@@ -4546,12 +5795,12 @@ var DashboardManager = class {
|
|
|
4546
5795
|
createDashboard(dashboardId, dashboard) {
|
|
4547
5796
|
const dashboardPath = this.getDashboardPath(dashboardId);
|
|
4548
5797
|
const dashboardDir = import_path4.default.dirname(dashboardPath);
|
|
4549
|
-
if (
|
|
5798
|
+
if (import_fs5.default.existsSync(dashboardPath)) {
|
|
4550
5799
|
throw new Error(`Dashboard '${dashboardId}' already exists`);
|
|
4551
5800
|
}
|
|
4552
5801
|
const validated = DSLRendererPropsSchema.parse(dashboard);
|
|
4553
|
-
|
|
4554
|
-
|
|
5802
|
+
import_fs5.default.mkdirSync(dashboardDir, { recursive: true });
|
|
5803
|
+
import_fs5.default.writeFileSync(dashboardPath, JSON.stringify(validated, null, 4));
|
|
4555
5804
|
logger.info(`Dashboard created: ${dashboardId}`);
|
|
4556
5805
|
return validated;
|
|
4557
5806
|
}
|
|
@@ -4562,12 +5811,12 @@ var DashboardManager = class {
|
|
|
4562
5811
|
*/
|
|
4563
5812
|
getDashboard(dashboardId) {
|
|
4564
5813
|
const dashboardPath = this.getDashboardPath(dashboardId);
|
|
4565
|
-
if (!
|
|
5814
|
+
if (!import_fs5.default.existsSync(dashboardPath)) {
|
|
4566
5815
|
logger.warn(`Dashboard not found: ${dashboardId}`);
|
|
4567
5816
|
return null;
|
|
4568
5817
|
}
|
|
4569
5818
|
try {
|
|
4570
|
-
const fileContent =
|
|
5819
|
+
const fileContent = import_fs5.default.readFileSync(dashboardPath, "utf-8");
|
|
4571
5820
|
const dashboard = JSON.parse(fileContent);
|
|
4572
5821
|
const validated = DSLRendererPropsSchema.parse(dashboard);
|
|
4573
5822
|
return validated;
|
|
@@ -4581,16 +5830,16 @@ var DashboardManager = class {
|
|
|
4581
5830
|
* @returns Array of dashboard objects with their IDs
|
|
4582
5831
|
*/
|
|
4583
5832
|
getAllDashboards() {
|
|
4584
|
-
if (!
|
|
4585
|
-
|
|
5833
|
+
if (!import_fs5.default.existsSync(this.dashboardsBasePath)) {
|
|
5834
|
+
import_fs5.default.mkdirSync(this.dashboardsBasePath, { recursive: true });
|
|
4586
5835
|
return [];
|
|
4587
5836
|
}
|
|
4588
5837
|
const dashboards = [];
|
|
4589
5838
|
try {
|
|
4590
|
-
const dashboardDirs =
|
|
5839
|
+
const dashboardDirs = import_fs5.default.readdirSync(this.dashboardsBasePath);
|
|
4591
5840
|
for (const dashboardId of dashboardDirs) {
|
|
4592
5841
|
const dashboardPath = this.getDashboardPath(dashboardId);
|
|
4593
|
-
if (
|
|
5842
|
+
if (import_fs5.default.existsSync(dashboardPath)) {
|
|
4594
5843
|
const dashboard = this.getDashboard(dashboardId);
|
|
4595
5844
|
if (dashboard) {
|
|
4596
5845
|
dashboards.push({ dashboardId, dashboard });
|
|
@@ -4612,13 +5861,13 @@ var DashboardManager = class {
|
|
|
4612
5861
|
*/
|
|
4613
5862
|
updateDashboard(dashboardId, dashboard) {
|
|
4614
5863
|
const dashboardPath = this.getDashboardPath(dashboardId);
|
|
4615
|
-
if (!
|
|
5864
|
+
if (!import_fs5.default.existsSync(dashboardPath)) {
|
|
4616
5865
|
logger.warn(`Dashboard not found for update: ${dashboardId}`);
|
|
4617
5866
|
return null;
|
|
4618
5867
|
}
|
|
4619
5868
|
try {
|
|
4620
5869
|
const validated = DSLRendererPropsSchema.parse(dashboard);
|
|
4621
|
-
|
|
5870
|
+
import_fs5.default.writeFileSync(dashboardPath, JSON.stringify(validated, null, 4));
|
|
4622
5871
|
logger.info(`Dashboard updated: ${dashboardId}`);
|
|
4623
5872
|
return validated;
|
|
4624
5873
|
} catch (error) {
|
|
@@ -4634,12 +5883,12 @@ var DashboardManager = class {
|
|
|
4634
5883
|
deleteDashboard(dashboardId) {
|
|
4635
5884
|
const dashboardPath = this.getDashboardPath(dashboardId);
|
|
4636
5885
|
const dashboardDir = import_path4.default.dirname(dashboardPath);
|
|
4637
|
-
if (!
|
|
5886
|
+
if (!import_fs5.default.existsSync(dashboardPath)) {
|
|
4638
5887
|
logger.warn(`Dashboard not found for deletion: ${dashboardId}`);
|
|
4639
5888
|
return false;
|
|
4640
5889
|
}
|
|
4641
5890
|
try {
|
|
4642
|
-
|
|
5891
|
+
import_fs5.default.rmSync(dashboardDir, { recursive: true, force: true });
|
|
4643
5892
|
logger.info(`Dashboard deleted: ${dashboardId}`);
|
|
4644
5893
|
return true;
|
|
4645
5894
|
} catch (error) {
|
|
@@ -4654,21 +5903,21 @@ var DashboardManager = class {
|
|
|
4654
5903
|
*/
|
|
4655
5904
|
dashboardExists(dashboardId) {
|
|
4656
5905
|
const dashboardPath = this.getDashboardPath(dashboardId);
|
|
4657
|
-
return
|
|
5906
|
+
return import_fs5.default.existsSync(dashboardPath);
|
|
4658
5907
|
}
|
|
4659
5908
|
/**
|
|
4660
5909
|
* Get dashboard count
|
|
4661
5910
|
* @returns Number of dashboards
|
|
4662
5911
|
*/
|
|
4663
5912
|
getDashboardCount() {
|
|
4664
|
-
if (!
|
|
5913
|
+
if (!import_fs5.default.existsSync(this.dashboardsBasePath)) {
|
|
4665
5914
|
return 0;
|
|
4666
5915
|
}
|
|
4667
5916
|
try {
|
|
4668
|
-
const dashboardDirs =
|
|
5917
|
+
const dashboardDirs = import_fs5.default.readdirSync(this.dashboardsBasePath);
|
|
4669
5918
|
return dashboardDirs.filter((dir) => {
|
|
4670
5919
|
const dashboardPath = this.getDashboardPath(dir);
|
|
4671
|
-
return
|
|
5920
|
+
return import_fs5.default.existsSync(dashboardPath);
|
|
4672
5921
|
}).length;
|
|
4673
5922
|
} catch (error) {
|
|
4674
5923
|
logger.error("Failed to get dashboard count:", error);
|
|
@@ -4678,7 +5927,7 @@ var DashboardManager = class {
|
|
|
4678
5927
|
};
|
|
4679
5928
|
|
|
4680
5929
|
// src/reports/report-manager.ts
|
|
4681
|
-
var
|
|
5930
|
+
var import_fs6 = __toESM(require("fs"));
|
|
4682
5931
|
var import_path5 = __toESM(require("path"));
|
|
4683
5932
|
var import_os3 = __toESM(require("os"));
|
|
4684
5933
|
var ReportManager = class {
|
|
@@ -4713,12 +5962,12 @@ var ReportManager = class {
|
|
|
4713
5962
|
createReport(reportId, report) {
|
|
4714
5963
|
const reportPath = this.getReportPath(reportId);
|
|
4715
5964
|
const reportDir = import_path5.default.dirname(reportPath);
|
|
4716
|
-
if (
|
|
5965
|
+
if (import_fs6.default.existsSync(reportPath)) {
|
|
4717
5966
|
throw new Error(`Report '${reportId}' already exists`);
|
|
4718
5967
|
}
|
|
4719
5968
|
const validated = DSLRendererPropsSchema2.parse(report);
|
|
4720
|
-
|
|
4721
|
-
|
|
5969
|
+
import_fs6.default.mkdirSync(reportDir, { recursive: true });
|
|
5970
|
+
import_fs6.default.writeFileSync(reportPath, JSON.stringify(validated, null, 4));
|
|
4722
5971
|
logger.info(`Report created: ${reportId}`);
|
|
4723
5972
|
return validated;
|
|
4724
5973
|
}
|
|
@@ -4729,12 +5978,12 @@ var ReportManager = class {
|
|
|
4729
5978
|
*/
|
|
4730
5979
|
getReport(reportId) {
|
|
4731
5980
|
const reportPath = this.getReportPath(reportId);
|
|
4732
|
-
if (!
|
|
5981
|
+
if (!import_fs6.default.existsSync(reportPath)) {
|
|
4733
5982
|
logger.warn(`Report not found: ${reportId}`);
|
|
4734
5983
|
return null;
|
|
4735
5984
|
}
|
|
4736
5985
|
try {
|
|
4737
|
-
const fileContent =
|
|
5986
|
+
const fileContent = import_fs6.default.readFileSync(reportPath, "utf-8");
|
|
4738
5987
|
const report = JSON.parse(fileContent);
|
|
4739
5988
|
const validated = DSLRendererPropsSchema2.parse(report);
|
|
4740
5989
|
return validated;
|
|
@@ -4748,16 +5997,16 @@ var ReportManager = class {
|
|
|
4748
5997
|
* @returns Array of report objects with their IDs
|
|
4749
5998
|
*/
|
|
4750
5999
|
getAllReports() {
|
|
4751
|
-
if (!
|
|
4752
|
-
|
|
6000
|
+
if (!import_fs6.default.existsSync(this.reportsBasePath)) {
|
|
6001
|
+
import_fs6.default.mkdirSync(this.reportsBasePath, { recursive: true });
|
|
4753
6002
|
return [];
|
|
4754
6003
|
}
|
|
4755
6004
|
const reports = [];
|
|
4756
6005
|
try {
|
|
4757
|
-
const reportDirs =
|
|
6006
|
+
const reportDirs = import_fs6.default.readdirSync(this.reportsBasePath);
|
|
4758
6007
|
for (const reportId of reportDirs) {
|
|
4759
6008
|
const reportPath = this.getReportPath(reportId);
|
|
4760
|
-
if (
|
|
6009
|
+
if (import_fs6.default.existsSync(reportPath)) {
|
|
4761
6010
|
const report = this.getReport(reportId);
|
|
4762
6011
|
if (report) {
|
|
4763
6012
|
reports.push({ reportId, report });
|
|
@@ -4779,13 +6028,13 @@ var ReportManager = class {
|
|
|
4779
6028
|
*/
|
|
4780
6029
|
updateReport(reportId, report) {
|
|
4781
6030
|
const reportPath = this.getReportPath(reportId);
|
|
4782
|
-
if (!
|
|
6031
|
+
if (!import_fs6.default.existsSync(reportPath)) {
|
|
4783
6032
|
logger.warn(`Report not found for update: ${reportId}`);
|
|
4784
6033
|
return null;
|
|
4785
6034
|
}
|
|
4786
6035
|
try {
|
|
4787
6036
|
const validated = DSLRendererPropsSchema2.parse(report);
|
|
4788
|
-
|
|
6037
|
+
import_fs6.default.writeFileSync(reportPath, JSON.stringify(validated, null, 4));
|
|
4789
6038
|
logger.info(`Report updated: ${reportId}`);
|
|
4790
6039
|
return validated;
|
|
4791
6040
|
} catch (error) {
|
|
@@ -4801,12 +6050,12 @@ var ReportManager = class {
|
|
|
4801
6050
|
deleteReport(reportId) {
|
|
4802
6051
|
const reportPath = this.getReportPath(reportId);
|
|
4803
6052
|
const reportDir = import_path5.default.dirname(reportPath);
|
|
4804
|
-
if (!
|
|
6053
|
+
if (!import_fs6.default.existsSync(reportPath)) {
|
|
4805
6054
|
logger.warn(`Report not found for deletion: ${reportId}`);
|
|
4806
6055
|
return false;
|
|
4807
6056
|
}
|
|
4808
6057
|
try {
|
|
4809
|
-
|
|
6058
|
+
import_fs6.default.rmSync(reportDir, { recursive: true, force: true });
|
|
4810
6059
|
logger.info(`Report deleted: ${reportId}`);
|
|
4811
6060
|
return true;
|
|
4812
6061
|
} catch (error) {
|
|
@@ -4821,21 +6070,21 @@ var ReportManager = class {
|
|
|
4821
6070
|
*/
|
|
4822
6071
|
reportExists(reportId) {
|
|
4823
6072
|
const reportPath = this.getReportPath(reportId);
|
|
4824
|
-
return
|
|
6073
|
+
return import_fs6.default.existsSync(reportPath);
|
|
4825
6074
|
}
|
|
4826
6075
|
/**
|
|
4827
6076
|
* Get report count
|
|
4828
6077
|
* @returns Number of reports
|
|
4829
6078
|
*/
|
|
4830
6079
|
getReportCount() {
|
|
4831
|
-
if (!
|
|
6080
|
+
if (!import_fs6.default.existsSync(this.reportsBasePath)) {
|
|
4832
6081
|
return 0;
|
|
4833
6082
|
}
|
|
4834
6083
|
try {
|
|
4835
|
-
const reportDirs =
|
|
6084
|
+
const reportDirs = import_fs6.default.readdirSync(this.reportsBasePath);
|
|
4836
6085
|
return reportDirs.filter((dir) => {
|
|
4837
6086
|
const reportPath = this.getReportPath(dir);
|
|
4838
|
-
return
|
|
6087
|
+
return import_fs6.default.existsSync(reportPath);
|
|
4839
6088
|
}).length;
|
|
4840
6089
|
} catch (error) {
|
|
4841
6090
|
logger.error("Failed to get report count:", error);
|
|
@@ -5036,6 +6285,9 @@ var SuperatomSDK = class {
|
|
|
5036
6285
|
this.maxReconnectAttempts = 5;
|
|
5037
6286
|
this.collections = {};
|
|
5038
6287
|
this.components = [];
|
|
6288
|
+
if (config.logLevel) {
|
|
6289
|
+
logger.setLogLevel(config.logLevel);
|
|
6290
|
+
}
|
|
5039
6291
|
this.apiKey = config.apiKey;
|
|
5040
6292
|
this.projectId = config.projectId;
|
|
5041
6293
|
this.userId = config.userId || "anonymous";
|
|
@@ -5056,9 +6308,6 @@ var SuperatomSDK = class {
|
|
|
5056
6308
|
});
|
|
5057
6309
|
this.initializeDashboardManager();
|
|
5058
6310
|
this.initializeReportManager();
|
|
5059
|
-
this.connect().catch((error) => {
|
|
5060
|
-
logger.error("Failed to connect to Superatom:", error);
|
|
5061
|
-
});
|
|
5062
6311
|
}
|
|
5063
6312
|
/**
|
|
5064
6313
|
* Initialize PromptLoader and load prompts into memory
|
|
@@ -5124,6 +6373,10 @@ var SuperatomSDK = class {
|
|
|
5124
6373
|
* Connect to the Superatom WebSocket service
|
|
5125
6374
|
*/
|
|
5126
6375
|
async connect() {
|
|
6376
|
+
if (this.connected && this.ws && this.ws.readyState === this.ws.OPEN) {
|
|
6377
|
+
logger.info("Already connected to WebSocket");
|
|
6378
|
+
return Promise.resolve();
|
|
6379
|
+
}
|
|
5127
6380
|
return new Promise((resolve, reject) => {
|
|
5128
6381
|
try {
|
|
5129
6382
|
const url = new URL(this.url);
|
|
@@ -5186,7 +6439,7 @@ var SuperatomSDK = class {
|
|
|
5186
6439
|
});
|
|
5187
6440
|
break;
|
|
5188
6441
|
case "USER_PROMPT_REQ":
|
|
5189
|
-
handleUserPromptRequest(parsed, this.components, (msg) => this.send(msg), this.anthropicApiKey, this.groqApiKey, this.llmProviders).catch((error) => {
|
|
6442
|
+
handleUserPromptRequest(parsed, this.components, (msg) => this.send(msg), this.anthropicApiKey, this.groqApiKey, this.llmProviders, this.collections).catch((error) => {
|
|
5190
6443
|
logger.error("Failed to handle user prompt request:", error);
|
|
5191
6444
|
});
|
|
5192
6445
|
break;
|
|
@@ -5339,6 +6592,7 @@ var SuperatomSDK = class {
|
|
|
5339
6592
|
ThreadManager,
|
|
5340
6593
|
UIBlock,
|
|
5341
6594
|
UILogCollector,
|
|
5342
|
-
UserManager
|
|
6595
|
+
UserManager,
|
|
6596
|
+
logger
|
|
5343
6597
|
});
|
|
5344
6598
|
//# sourceMappingURL=index.js.map
|