@superatomai/sdk-node 0.0.72 → 0.0.74
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +9 -13
- package/dist/index.d.ts +9 -13
- package/dist/index.js +314 -591
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +314 -590
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -34,7 +34,6 @@ __export(index_exports, {
|
|
|
34
34
|
CONTEXT_CONFIG: () => CONTEXT_CONFIG,
|
|
35
35
|
CleanupService: () => CleanupService,
|
|
36
36
|
LLM: () => LLM,
|
|
37
|
-
SDK_VERSION: () => SDK_VERSION,
|
|
38
37
|
STORAGE_CONFIG: () => STORAGE_CONFIG,
|
|
39
38
|
SuperatomSDK: () => SuperatomSDK,
|
|
40
39
|
Thread: () => Thread,
|
|
@@ -3368,24 +3367,20 @@ If adaptation is not possible or would fundamentally change the component:
|
|
|
3368
3367
|
"dash-comp-picker": {
|
|
3369
3368
|
system: `You are a component selection expert that picks the best dashboard component and generates complete props based on user requests.
|
|
3370
3369
|
|
|
3370
|
+
## CRITICAL - READ FIRST
|
|
3371
|
+
|
|
3372
|
+
1. Your ENTIRE response must be ONLY a raw JSON object - start with { end with }
|
|
3373
|
+
2. DO NOT explain or answer the user's question in natural language
|
|
3374
|
+
3. DO NOT use markdown code blocks (no \`\`\`)
|
|
3375
|
+
4. DO NOT add any text before or after the JSON
|
|
3376
|
+
5. After executing tools (if needed), return JSON with component selection - NOT a text summary of results
|
|
3377
|
+
|
|
3371
3378
|
## Your Task
|
|
3372
3379
|
|
|
3373
3380
|
Analyze the user's request and:
|
|
3374
3381
|
1. **Select the most appropriate component** from the available components list
|
|
3375
|
-
2. **Determine the data source**: Database query OR External tool
|
|
3376
|
-
3. **Generate complete props** for the selected component
|
|
3377
|
-
|
|
3378
|
-
## Available External Tools
|
|
3379
|
-
|
|
3380
|
-
The following external tools are available:
|
|
3381
|
-
|
|
3382
|
-
{{AVAILABLE_TOOLS}}
|
|
3383
|
-
|
|
3384
|
-
When a tool is needed to complete the user's request:
|
|
3385
|
-
1. **Analyze the request** to determine which tool is needed
|
|
3386
|
-
2. **Extract parameters** from the user's question
|
|
3387
|
-
3. **Execute the tool** by calling it with the extracted parameters
|
|
3388
|
-
4. **Use the results** to configure the component (field names for axes, columns, etc.)
|
|
3382
|
+
2. **Determine the data source**: Database query OR External tool (ERP)
|
|
3383
|
+
3. **Generate complete props** for the selected component including the data retrieval/modification method
|
|
3389
3384
|
|
|
3390
3385
|
## Component Selection Rules
|
|
3391
3386
|
|
|
@@ -3415,7 +3410,7 @@ The user prompt may contain an **existing component** to update. Detect this by
|
|
|
3415
3410
|
|
|
3416
3411
|
### Use DATABASE when:
|
|
3417
3412
|
- User asks about data that exists in the database schema
|
|
3418
|
-
- Questions about internal business data
|
|
3413
|
+
- Questions about internal business data
|
|
3419
3414
|
- CRUD operations on database tables
|
|
3420
3415
|
|
|
3421
3416
|
### Use EXTERNAL TOOL when:
|
|
@@ -3428,6 +3423,12 @@ The user prompt may contain an **existing component** to update. Detect this by
|
|
|
3428
3423
|
|
|
3429
3424
|
**CRITICAL**: Look at each component's "Props Structure" in the available components list. Generate ALL props that the component expects.
|
|
3430
3425
|
|
|
3426
|
+
**CRITICAL: Each component uses EXACTLY ONE data source - never both!**
|
|
3427
|
+
- If using \`query\`, set \`externalTool: null\`
|
|
3428
|
+
- If using \`externalTool\`, set \`query: null\`
|
|
3429
|
+
- NEVER copy placeholder/description text from component metadata as actual values
|
|
3430
|
+
- \`externalTool.parameters\` MUST be an object, never a string
|
|
3431
|
+
|
|
3431
3432
|
### For Data Viewing Components (charts, tables, KPIs):
|
|
3432
3433
|
|
|
3433
3434
|
**Option A: Database Query** (when data is in database)
|
|
@@ -3436,21 +3437,19 @@ The user prompt may contain an **existing component** to update. Detect this by
|
|
|
3436
3437
|
"query": {
|
|
3437
3438
|
"sql": "SELECT column1, column2 FROM table WHERE condition = $param LIMIT 32",
|
|
3438
3439
|
"params": { "param": "value" }
|
|
3439
|
-
}
|
|
3440
|
+
},
|
|
3441
|
+
"externalTool": null
|
|
3440
3442
|
}
|
|
3441
3443
|
\`\`\`
|
|
3442
3444
|
|
|
3443
3445
|
**Option B: External Tool** (when data is from ERP/external system)
|
|
3444
3446
|
\`\`\`json
|
|
3445
3447
|
{
|
|
3448
|
+
"query": null,
|
|
3446
3449
|
"externalTool": {
|
|
3447
3450
|
"toolId": "tool_id_from_list",
|
|
3448
3451
|
"toolName": "Tool Display Name",
|
|
3449
|
-
"
|
|
3450
|
-
"params": {
|
|
3451
|
-
"param1": "value1",
|
|
3452
|
-
"param2": "value2"
|
|
3453
|
-
}
|
|
3452
|
+
"parameters": { "param1": "value1", "param2": "value2" }
|
|
3454
3453
|
}
|
|
3455
3454
|
}
|
|
3456
3455
|
\`\`\`
|
|
@@ -3464,6 +3463,7 @@ The user prompt may contain an **existing component** to update. Detect this by
|
|
|
3464
3463
|
"sql": "INSERT INTO table (col1, col2) VALUES ($col1, $col2)",
|
|
3465
3464
|
"params": {}
|
|
3466
3465
|
},
|
|
3466
|
+
"externalTool": null,
|
|
3467
3467
|
"fields": [
|
|
3468
3468
|
{ "name": "col1", "type": "text", "required": true },
|
|
3469
3469
|
{ "name": "col2", "type": "number", "required": false }
|
|
@@ -3471,16 +3471,38 @@ The user prompt may contain an **existing component** to update. Detect this by
|
|
|
3471
3471
|
}
|
|
3472
3472
|
\`\`\`
|
|
3473
3473
|
|
|
3474
|
+
For UPDATE:
|
|
3475
|
+
\`\`\`json
|
|
3476
|
+
{
|
|
3477
|
+
"query": {
|
|
3478
|
+
"sql": "UPDATE table SET col1 = $col1, col2 = $col2 WHERE id = $id",
|
|
3479
|
+
"params": { "id": "record_id" }
|
|
3480
|
+
},
|
|
3481
|
+
"externalTool": null
|
|
3482
|
+
}
|
|
3483
|
+
\`\`\`
|
|
3484
|
+
|
|
3485
|
+
For DELETE:
|
|
3486
|
+
\`\`\`json
|
|
3487
|
+
{
|
|
3488
|
+
"query": {
|
|
3489
|
+
"sql": "DELETE FROM table WHERE id = $id",
|
|
3490
|
+
"params": { "id": "record_id" }
|
|
3491
|
+
},
|
|
3492
|
+
"externalTool": null,
|
|
3493
|
+
"submitButtonText": "Confirm Delete",
|
|
3494
|
+
"submitButtonColor": "danger"
|
|
3495
|
+
}
|
|
3496
|
+
\`\`\`
|
|
3497
|
+
|
|
3474
3498
|
**Option B: External Tool Mutation**
|
|
3475
3499
|
\`\`\`json
|
|
3476
3500
|
{
|
|
3501
|
+
"query": null,
|
|
3477
3502
|
"externalTool": {
|
|
3478
3503
|
"toolId": "tool_id_from_list",
|
|
3479
3504
|
"toolName": "Tool Display Name",
|
|
3480
|
-
"
|
|
3481
|
-
"params": {
|
|
3482
|
-
"param1": "value_or_placeholder"
|
|
3483
|
-
}
|
|
3505
|
+
"parameters": { "param1": "value_or_placeholder" }
|
|
3484
3506
|
},
|
|
3485
3507
|
"fields": [
|
|
3486
3508
|
{ "name": "param1", "type": "text", "required": true }
|
|
@@ -3495,6 +3517,7 @@ The user prompt may contain an **existing component** to update. Detect this by
|
|
|
3495
3517
|
|
|
3496
3518
|
You MUST respond with ONLY a valid JSON object (no markdown, no code blocks):
|
|
3497
3519
|
|
|
3520
|
+
\`\`\`json
|
|
3498
3521
|
{
|
|
3499
3522
|
"componentId": "id_from_available_list_or_existing_component_id",
|
|
3500
3523
|
"componentName": "name_of_component",
|
|
@@ -3509,6 +3532,7 @@ You MUST respond with ONLY a valid JSON object (no markdown, no code blocks):
|
|
|
3509
3532
|
// Include all other required props (title, description, config, fields, etc.)
|
|
3510
3533
|
}
|
|
3511
3534
|
}
|
|
3535
|
+
\`\`\`
|
|
3512
3536
|
|
|
3513
3537
|
**CRITICAL:**
|
|
3514
3538
|
- Return ONLY valid JSON (no markdown code blocks, no text before/after)
|
|
@@ -3531,7 +3555,8 @@ You MUST respond with ONLY a valid JSON object (no markdown, no code blocks):
|
|
|
3531
3555
|
|
|
3532
3556
|
---
|
|
3533
3557
|
|
|
3534
|
-
## CONTEXT
|
|
3558
|
+
## CONTEXT
|
|
3559
|
+
`,
|
|
3535
3560
|
user: `{{USER_PROMPT}}`
|
|
3536
3561
|
},
|
|
3537
3562
|
"dash-filter-picker": {
|
|
@@ -3677,9 +3702,7 @@ var PromptLoader = class {
|
|
|
3677
3702
|
this.databaseRulesCache = /* @__PURE__ */ new Map();
|
|
3678
3703
|
this.isInitialized = false;
|
|
3679
3704
|
this.databaseType = "postgresql";
|
|
3680
|
-
logger.debug("Initializing PromptLoader...");
|
|
3681
3705
|
this.promptsDir = config?.promptsDir || import_path.default.join(process.cwd(), ".prompts");
|
|
3682
|
-
logger.debug(`Prompts directory set to: ${this.promptsDir}`);
|
|
3683
3706
|
}
|
|
3684
3707
|
/**
|
|
3685
3708
|
* Load a prompt template from file system OR fallback to hardcoded prompts
|
|
@@ -3693,7 +3716,6 @@ var PromptLoader = class {
|
|
|
3693
3716
|
if (import_fs2.default.existsSync(systemPath) && import_fs2.default.existsSync(userPath)) {
|
|
3694
3717
|
const system = import_fs2.default.readFileSync(systemPath, "utf-8");
|
|
3695
3718
|
const user = import_fs2.default.readFileSync(userPath, "utf-8");
|
|
3696
|
-
logger.info(`\u2713 Loaded prompt '${promptName}' from file system: ${this.promptsDir}`);
|
|
3697
3719
|
return { system, user };
|
|
3698
3720
|
}
|
|
3699
3721
|
} catch (error) {
|
|
@@ -3701,7 +3723,6 @@ var PromptLoader = class {
|
|
|
3701
3723
|
}
|
|
3702
3724
|
const hardcodedPrompt = PROMPTS[promptName];
|
|
3703
3725
|
if (hardcodedPrompt) {
|
|
3704
|
-
logger.info(`\u2713 Loaded prompt '${promptName}' from hardcoded fallback`);
|
|
3705
3726
|
return hardcodedPrompt;
|
|
3706
3727
|
}
|
|
3707
3728
|
throw new Error(`Prompt template '${promptName}' not found in either ${this.promptsDir} or hardcoded prompts. Available prompts: ${Object.keys(PROMPTS).join(", ")}`);
|
|
@@ -3715,7 +3736,6 @@ var PromptLoader = class {
|
|
|
3715
3736
|
logger.debug("PromptLoader already initialized, skipping...");
|
|
3716
3737
|
return;
|
|
3717
3738
|
}
|
|
3718
|
-
logger.info("Loading prompts into memory...");
|
|
3719
3739
|
const promptTypes = Object.keys(PROMPTS);
|
|
3720
3740
|
for (const promptName of promptTypes) {
|
|
3721
3741
|
try {
|
|
@@ -3727,7 +3747,6 @@ var PromptLoader = class {
|
|
|
3727
3747
|
}
|
|
3728
3748
|
}
|
|
3729
3749
|
this.isInitialized = true;
|
|
3730
|
-
logger.info(`Successfully loaded ${this.promptCache.size} prompt templates into memory`);
|
|
3731
3750
|
}
|
|
3732
3751
|
/**
|
|
3733
3752
|
* Replace variables in a template string using {{VARIABLE_NAME}} pattern
|
|
@@ -3767,7 +3786,6 @@ var PromptLoader = class {
|
|
|
3767
3786
|
const processedContext = this.replaceVariables(contextMarker + contextPart, variables);
|
|
3768
3787
|
const staticLength = processedStatic.length;
|
|
3769
3788
|
const contextLength = processedContext.length;
|
|
3770
|
-
logger.debug(`\u2713 Prompt caching enabled for '${promptName}' (cached: ${staticLength} chars, dynamic: ${contextLength} chars)`);
|
|
3771
3789
|
return {
|
|
3772
3790
|
system: [
|
|
3773
3791
|
{
|
|
@@ -3804,7 +3822,6 @@ var PromptLoader = class {
|
|
|
3804
3822
|
this.promptsDir = dir;
|
|
3805
3823
|
this.isInitialized = false;
|
|
3806
3824
|
this.promptCache.clear();
|
|
3807
|
-
logger.debug(`Prompts directory changed to: ${dir}`);
|
|
3808
3825
|
}
|
|
3809
3826
|
/**
|
|
3810
3827
|
* Get current prompts directory
|
|
@@ -3832,7 +3849,6 @@ var PromptLoader = class {
|
|
|
3832
3849
|
setDatabaseType(type) {
|
|
3833
3850
|
this.databaseType = type;
|
|
3834
3851
|
this.databaseRulesCache.clear();
|
|
3835
|
-
logger.debug(`Database type set to: ${type}`);
|
|
3836
3852
|
}
|
|
3837
3853
|
/**
|
|
3838
3854
|
* Get current database type
|
|
@@ -3848,7 +3864,6 @@ var PromptLoader = class {
|
|
|
3848
3864
|
*/
|
|
3849
3865
|
async loadDatabaseRules() {
|
|
3850
3866
|
if (this.databaseRulesCache.has(this.databaseType)) {
|
|
3851
|
-
logger.debug(`\u2713 Database rules for '${this.databaseType}' loaded from cache`);
|
|
3852
3867
|
return this.databaseRulesCache.get(this.databaseType);
|
|
3853
3868
|
}
|
|
3854
3869
|
const rulesPath = import_path.default.join(this.promptsDir, "database-rules", `${this.databaseType}.md`);
|
|
@@ -3856,7 +3871,6 @@ var PromptLoader = class {
|
|
|
3856
3871
|
if (import_fs2.default.existsSync(rulesPath)) {
|
|
3857
3872
|
const rules = import_fs2.default.readFileSync(rulesPath, "utf-8");
|
|
3858
3873
|
this.databaseRulesCache.set(this.databaseType, rules);
|
|
3859
|
-
logger.info(`\u2713 Loaded database rules for '${this.databaseType}' from ${rulesPath}`);
|
|
3860
3874
|
return rules;
|
|
3861
3875
|
}
|
|
3862
3876
|
} catch (error) {
|
|
@@ -4132,7 +4146,6 @@ var Schema = class {
|
|
|
4132
4146
|
* @returns Parsed schema object or null if error occurs
|
|
4133
4147
|
*/
|
|
4134
4148
|
getDatabaseSchema() {
|
|
4135
|
-
logger.info(`SCHEMA_FILE_PATH: ${this.schemaFilePath}`);
|
|
4136
4149
|
try {
|
|
4137
4150
|
const dir = import_path2.default.dirname(this.schemaFilePath);
|
|
4138
4151
|
if (!import_fs3.default.existsSync(dir)) {
|
|
@@ -4401,14 +4414,6 @@ Format: [TIMESTAMP] [REQUEST_ID] [PROVIDER/MODEL] [METHOD]
|
|
|
4401
4414
|
Cost: $${entry.costUSD.toFixed(6)} | Time: ${entry.durationMs}ms${toolInfo}${errorInfo}${cacheStatus}
|
|
4402
4415
|
`;
|
|
4403
4416
|
this.logStream?.write(logLine);
|
|
4404
|
-
if (entry.cacheReadTokens && entry.cacheReadTokens > 0) {
|
|
4405
|
-
console.log(`[LLM] \u26A1 CACHE HIT: ${entry.cacheReadTokens.toLocaleString()} tokens read from cache (${entry.method})`);
|
|
4406
|
-
} else if (entry.cacheWriteTokens && entry.cacheWriteTokens > 0) {
|
|
4407
|
-
console.log(`[LLM] \u{1F4DD} CACHE WRITE: ${entry.cacheWriteTokens.toLocaleString()} tokens cached for future requests (${entry.method})`);
|
|
4408
|
-
}
|
|
4409
|
-
if (process.env.SUPERATOM_LOG_LEVEL === "verbose") {
|
|
4410
|
-
console.log("\n[LLM-Usage]", logLine);
|
|
4411
|
-
}
|
|
4412
4417
|
}
|
|
4413
4418
|
/**
|
|
4414
4419
|
* Log session summary (call at end of request)
|
|
@@ -4441,11 +4446,6 @@ Avg Time/Call: ${Math.round(this.sessionStats.totalDurationMs / this.sessionStat
|
|
|
4441
4446
|
|
|
4442
4447
|
`;
|
|
4443
4448
|
this.logStream?.write(summary);
|
|
4444
|
-
console.log("\n[LLM-Usage] Session Summary:");
|
|
4445
|
-
console.log(` Calls: ${this.sessionStats.totalCalls} | Tokens: ${(this.sessionStats.totalInputTokens + this.sessionStats.totalOutputTokens).toLocaleString()} | Cost: $${this.sessionStats.totalCostUSD.toFixed(4)} | Time: ${(this.sessionStats.totalDurationMs / 1e3).toFixed(2)}s`);
|
|
4446
|
-
if (hasCaching) {
|
|
4447
|
-
console.log(` Cache: ${this.sessionStats.totalCacheReadTokens.toLocaleString()} read, ${this.sessionStats.totalCacheWriteTokens.toLocaleString()} written | Savings: ~$${cacheReadSavings.toFixed(4)}`);
|
|
4448
|
-
}
|
|
4449
4449
|
}
|
|
4450
4450
|
/**
|
|
4451
4451
|
* Reset session stats (call at start of new user request)
|
|
@@ -4486,7 +4486,6 @@ Format: [TIMESTAMP] [REQUEST_ID] [PROVIDER/MODEL] [METHOD]
|
|
|
4486
4486
|
`;
|
|
4487
4487
|
this.logStream.write(header);
|
|
4488
4488
|
this.resetSession();
|
|
4489
|
-
console.log(`[LLM-Usage] Log file reset for new request: ${this.logPath}`);
|
|
4490
4489
|
} catch (error) {
|
|
4491
4490
|
console.error("[LLM-Usage-Logger] Failed to reset log file:", error);
|
|
4492
4491
|
}
|
|
@@ -5960,21 +5959,20 @@ var getKnowledgeBase = async ({
|
|
|
5960
5959
|
}) => {
|
|
5961
5960
|
try {
|
|
5962
5961
|
if (!collections || !collections["knowledge-base"] || !collections["knowledge-base"]["query"]) {
|
|
5963
|
-
logger.
|
|
5962
|
+
logger.warn("[KnowledgeBase] knowledge-base.query collection not registered, skipping");
|
|
5964
5963
|
return "";
|
|
5965
5964
|
}
|
|
5966
|
-
logger.info(`[KnowledgeBase] Querying knowledge base for: "${prompt.substring(0, 50)}..."`);
|
|
5967
5965
|
const result = await collections["knowledge-base"]["query"]({
|
|
5968
5966
|
prompt,
|
|
5969
5967
|
topK
|
|
5970
5968
|
});
|
|
5971
5969
|
if (!result || !result.content) {
|
|
5972
|
-
logger.
|
|
5970
|
+
logger.warn("[KnowledgeBase] No knowledge base results returned");
|
|
5973
5971
|
return "";
|
|
5974
5972
|
}
|
|
5975
5973
|
logger.info(`[KnowledgeBase] Retrieved knowledge base context (${result.content.length} chars)`);
|
|
5976
5974
|
if (result.metadata?.sources && result.metadata.sources.length > 0) {
|
|
5977
|
-
logger.
|
|
5975
|
+
logger.warn(`[KnowledgeBase] Sources: ${result.metadata.sources.map((s) => s.title).join(", ")}`);
|
|
5978
5976
|
}
|
|
5979
5977
|
return result.content;
|
|
5980
5978
|
} catch (error) {
|
|
@@ -5989,13 +5987,12 @@ var getGlobalKnowledgeBase = async ({
|
|
|
5989
5987
|
}) => {
|
|
5990
5988
|
try {
|
|
5991
5989
|
if (!collections || !collections["knowledge-base"] || !collections["knowledge-base"]["getGlobal"]) {
|
|
5992
|
-
logger.
|
|
5990
|
+
logger.warn("[KnowledgeBase] knowledge-base.getGlobal collection not registered, skipping");
|
|
5993
5991
|
return "";
|
|
5994
5992
|
}
|
|
5995
|
-
logger.info("[KnowledgeBase] Fetching global knowledge base nodes...");
|
|
5996
5993
|
const result = await collections["knowledge-base"]["getGlobal"]({ limit });
|
|
5997
5994
|
if (!result || !result.content) {
|
|
5998
|
-
logger.
|
|
5995
|
+
logger.warn("[KnowledgeBase] No global knowledge base nodes found");
|
|
5999
5996
|
return "";
|
|
6000
5997
|
}
|
|
6001
5998
|
logger.info(`[KnowledgeBase] Retrieved ${result.count || 0} global knowledge base nodes`);
|
|
@@ -6013,14 +6010,13 @@ var getUserKnowledgeBase = async ({
|
|
|
6013
6010
|
}) => {
|
|
6014
6011
|
try {
|
|
6015
6012
|
if (!userId) {
|
|
6016
|
-
logger.
|
|
6013
|
+
logger.warn("[KnowledgeBase] No userId provided, skipping user knowledge base");
|
|
6017
6014
|
return "";
|
|
6018
6015
|
}
|
|
6019
6016
|
if (!collections || !collections["knowledge-base"] || !collections["knowledge-base"]["getByUser"]) {
|
|
6020
|
-
logger.
|
|
6017
|
+
logger.warn("[KnowledgeBase] knowledge-base.getByUser collection not registered, skipping");
|
|
6021
6018
|
return "";
|
|
6022
6019
|
}
|
|
6023
|
-
logger.info(`[KnowledgeBase] Fetching user knowledge base nodes for userId: ${userId}...`);
|
|
6024
6020
|
const result = await collections["knowledge-base"]["getByUser"]({
|
|
6025
6021
|
userId: Number(userId),
|
|
6026
6022
|
limit
|
|
@@ -6043,7 +6039,6 @@ var getAllKnowledgeBase = async ({
|
|
|
6043
6039
|
userId,
|
|
6044
6040
|
topK = 3
|
|
6045
6041
|
}) => {
|
|
6046
|
-
logger.info("[KnowledgeBase] Fetching all knowledge base contexts...");
|
|
6047
6042
|
const [globalContext, userContext, queryContext] = await Promise.all([
|
|
6048
6043
|
getGlobalKnowledgeBase({ collections }),
|
|
6049
6044
|
getUserKnowledgeBase({ collections, userId }),
|
|
@@ -6065,7 +6060,6 @@ var getAllKnowledgeBase = async ({
|
|
|
6065
6060
|
combinedContext += "The following information is semantically relevant to the current query:\n\n";
|
|
6066
6061
|
combinedContext += queryContext + "\n\n";
|
|
6067
6062
|
}
|
|
6068
|
-
logger.info(`[KnowledgeBase] Combined knowledge base context: global=${globalContext.length} chars, user=${userContext.length} chars, query=${queryContext.length} chars`);
|
|
6069
6063
|
return {
|
|
6070
6064
|
globalContext,
|
|
6071
6065
|
userContext,
|
|
@@ -6302,11 +6296,11 @@ var searchConversationsWithReranking = async (options) => {
|
|
|
6302
6296
|
} = options;
|
|
6303
6297
|
try {
|
|
6304
6298
|
if (!collections || !collections["conversation-history"]) {
|
|
6305
|
-
logger.
|
|
6299
|
+
logger.warn("[ConversationSearch] conversation-history collection not registered, skipping");
|
|
6306
6300
|
return null;
|
|
6307
6301
|
}
|
|
6308
6302
|
if (!collections["conversation-history"]["searchMultiple"]) {
|
|
6309
|
-
logger.
|
|
6303
|
+
logger.warn("[ConversationSearch] searchMultiple not available, falling back to standard search");
|
|
6310
6304
|
return searchConversations({
|
|
6311
6305
|
userPrompt,
|
|
6312
6306
|
collections,
|
|
@@ -6314,9 +6308,6 @@ var searchConversationsWithReranking = async (options) => {
|
|
|
6314
6308
|
similarityThreshold
|
|
6315
6309
|
});
|
|
6316
6310
|
}
|
|
6317
|
-
logger.info(`[ConversationSearch] Hybrid search for: "${userPrompt.substring(0, 50)}..."`);
|
|
6318
|
-
logger.info(`[ConversationSearch] Fetching ${rerankCandidates} candidates for reranking`);
|
|
6319
|
-
logger.info(`[ConversationSearch] Weights - Semantic: ${hybridOptions.semanticWeight}, BM25: ${hybridOptions.bm25Weight}`);
|
|
6320
6311
|
const results = await collections["conversation-history"]["searchMultiple"]({
|
|
6321
6312
|
userPrompt,
|
|
6322
6313
|
userId,
|
|
@@ -6357,7 +6348,6 @@ var searchConversationsWithReranking = async (options) => {
|
|
|
6357
6348
|
logger.info(
|
|
6358
6349
|
`[ConversationSearch] \u2713 Found match with semantic score ${(semanticScore * 100).toFixed(2)}%`
|
|
6359
6350
|
);
|
|
6360
|
-
logger.info(` - Returning cached result for: "${matchedUserPrompt}"`);
|
|
6361
6351
|
return {
|
|
6362
6352
|
uiBlock: best.uiBlock,
|
|
6363
6353
|
similarity: semanticScore,
|
|
@@ -6799,10 +6789,9 @@ Fixed SQL query:`;
|
|
|
6799
6789
|
* @param component - The component to validate
|
|
6800
6790
|
* @param collections - Collections object containing database execute function
|
|
6801
6791
|
* @param apiKey - Optional API key for LLM calls
|
|
6802
|
-
* @param logCollector - Optional log collector for logging
|
|
6803
6792
|
* @returns Validation result with component, query key, and result
|
|
6804
6793
|
*/
|
|
6805
|
-
async validateSingleQuery(component, collections, apiKey
|
|
6794
|
+
async validateSingleQuery(component, collections, apiKey) {
|
|
6806
6795
|
const query = component.props?.query;
|
|
6807
6796
|
const originalQueryKey = this.getQueryCacheKey(query);
|
|
6808
6797
|
const queryStr = typeof query === "string" ? query : query?.sql || "";
|
|
@@ -6823,7 +6812,6 @@ Fixed SQL query:`;
|
|
|
6823
6812
|
validated = true;
|
|
6824
6813
|
queryCache.set(validationResult.cacheKey, result);
|
|
6825
6814
|
logger.info(`[${this.config.providerName}] \u2713 Query validated for ${component.name} (attempt ${attempts}) - cached for frontend`);
|
|
6826
|
-
logCollector?.info(`\u2713 Query validated for ${component.name}`);
|
|
6827
6815
|
if (currentQueryStr !== queryStr) {
|
|
6828
6816
|
const fixedQuery = typeof query === "string" ? currentQueryStr : { ...query, sql: currentQueryStr };
|
|
6829
6817
|
component.props = {
|
|
@@ -6836,14 +6824,11 @@ Fixed SQL query:`;
|
|
|
6836
6824
|
} catch (error) {
|
|
6837
6825
|
lastError = error instanceof Error ? error.message : String(error);
|
|
6838
6826
|
logger.warn(`[${this.config.providerName}] Query validation failed for ${component.name} (attempt ${attempts}/${MAX_QUERY_VALIDATION_RETRIES}): ${lastError}`);
|
|
6839
|
-
logCollector?.warn(`Query validation failed for ${component.name}: ${lastError}`);
|
|
6840
6827
|
if (attempts >= MAX_QUERY_VALIDATION_RETRIES) {
|
|
6841
6828
|
logger.error(`[${this.config.providerName}] \u2717 Max retries reached for ${component.name}, excluding from response`);
|
|
6842
|
-
logCollector?.error(`Max retries reached for ${component.name}, component excluded from response`);
|
|
6843
6829
|
break;
|
|
6844
6830
|
}
|
|
6845
6831
|
logger.info(`[${this.config.providerName}] Requesting query fix from LLM for ${component.name}...`);
|
|
6846
|
-
logCollector?.info(`Requesting query fix for ${component.name}...`);
|
|
6847
6832
|
try {
|
|
6848
6833
|
const fixedQueryStr = await this.requestQueryFix(
|
|
6849
6834
|
currentQueryStr,
|
|
@@ -6877,7 +6862,6 @@ Fixed SQL query:`;
|
|
|
6877
6862
|
}
|
|
6878
6863
|
if (!validated) {
|
|
6879
6864
|
logger.warn(`[${this.config.providerName}] Component ${component.name} excluded from response due to failed query validation`);
|
|
6880
|
-
logCollector?.warn(`Component ${component.name} excluded from response`);
|
|
6881
6865
|
}
|
|
6882
6866
|
return {
|
|
6883
6867
|
component: validated ? component : null,
|
|
@@ -6891,10 +6875,9 @@ Fixed SQL query:`;
|
|
|
6891
6875
|
* @param components - Array of components with potential queries
|
|
6892
6876
|
* @param collections - Collections object containing database execute function
|
|
6893
6877
|
* @param apiKey - Optional API key for LLM calls
|
|
6894
|
-
* @param logCollector - Optional log collector for logging
|
|
6895
6878
|
* @returns Object with validated components and query results map
|
|
6896
6879
|
*/
|
|
6897
|
-
async validateComponentQueries(components, collections, apiKey
|
|
6880
|
+
async validateComponentQueries(components, collections, apiKey) {
|
|
6898
6881
|
const queryResults = /* @__PURE__ */ new Map();
|
|
6899
6882
|
const validatedComponents = [];
|
|
6900
6883
|
const componentsWithoutQuery = [];
|
|
@@ -6911,9 +6894,8 @@ Fixed SQL query:`;
|
|
|
6911
6894
|
return { components: validatedComponents, queryResults };
|
|
6912
6895
|
}
|
|
6913
6896
|
logger.info(`[${this.config.providerName}] Validating ${componentsWithQuery.length} component queries in parallel...`);
|
|
6914
|
-
logCollector?.info(`Validating ${componentsWithQuery.length} component queries in parallel...`);
|
|
6915
6897
|
const validationPromises = componentsWithQuery.map(
|
|
6916
|
-
(component) => this.validateSingleQuery(component, collections, apiKey
|
|
6898
|
+
(component) => this.validateSingleQuery(component, collections, apiKey)
|
|
6917
6899
|
);
|
|
6918
6900
|
const results = await Promise.allSettled(validationPromises);
|
|
6919
6901
|
for (let i = 0; i < results.length; i++) {
|
|
@@ -6930,7 +6912,6 @@ Fixed SQL query:`;
|
|
|
6930
6912
|
}
|
|
6931
6913
|
} else {
|
|
6932
6914
|
logger.error(`[${this.config.providerName}] Unexpected error validating ${component.name}: ${result.reason}`);
|
|
6933
|
-
logCollector?.error(`Unexpected error validating ${component.name}: ${result.reason}`);
|
|
6934
6915
|
}
|
|
6935
6916
|
}
|
|
6936
6917
|
logger.info(`[${this.config.providerName}] Parallel validation complete: ${validatedComponents.length}/${components.length} components validated`);
|
|
@@ -6992,22 +6973,17 @@ var ToolExecutorService = class {
|
|
|
6992
6973
|
let sql = toolInput.sql;
|
|
6993
6974
|
const params = toolInput.params || {};
|
|
6994
6975
|
const reasoning = toolInput.reasoning;
|
|
6995
|
-
const { streamBuffer, collections,
|
|
6976
|
+
const { streamBuffer, collections, providerName } = this.config;
|
|
6996
6977
|
sql = ensureQueryLimit(sql, MAX_COMPONENT_QUERY_LIMIT, MAX_COMPONENT_QUERY_LIMIT);
|
|
6997
6978
|
const queryKey = sql.toLowerCase().replace(/\s+/g, " ").trim();
|
|
6998
6979
|
const attempts = (this.queryAttempts.get(queryKey) || 0) + 1;
|
|
6999
6980
|
this.queryAttempts.set(queryKey, attempts);
|
|
7000
|
-
logger.info(`[${providerName}] Executing query (attempt ${attempts}/${MAX_QUERY_ATTEMPTS}): ${sql.substring(0, 100)}...`);
|
|
7001
6981
|
if (Object.keys(params).length > 0) {
|
|
7002
6982
|
logger.info(`[${providerName}] Query params: ${JSON.stringify(params)}`);
|
|
7003
6983
|
}
|
|
7004
|
-
if (reasoning) {
|
|
7005
|
-
logCollector?.info(`Query reasoning: ${reasoning}`);
|
|
7006
|
-
}
|
|
7007
6984
|
if (attempts > MAX_QUERY_ATTEMPTS) {
|
|
7008
6985
|
const errorMsg = `Maximum query attempts (${MAX_QUERY_ATTEMPTS}) reached. Unable to generate a valid query for your question.`;
|
|
7009
6986
|
logger.error(`[${providerName}] ${errorMsg}`);
|
|
7010
|
-
logCollector?.error(errorMsg);
|
|
7011
6987
|
this.maxAttemptsReached = true;
|
|
7012
6988
|
if (streamBuffer.hasCallback()) {
|
|
7013
6989
|
streamBuffer.write(`
|
|
@@ -7067,11 +7043,6 @@ ${sql}
|
|
|
7067
7043
|
await streamDelay();
|
|
7068
7044
|
}
|
|
7069
7045
|
}
|
|
7070
|
-
logCollector?.logQuery?.(
|
|
7071
|
-
`Executing SQL query (attempt ${attempts})`,
|
|
7072
|
-
{ sql, params },
|
|
7073
|
-
{ reasoning, attempt: attempts }
|
|
7074
|
-
);
|
|
7075
7046
|
if (!collections?.["database"]?.["execute"]) {
|
|
7076
7047
|
throw new Error("Database collection not registered. Please register database.execute collection to execute queries.");
|
|
7077
7048
|
}
|
|
@@ -7083,8 +7054,6 @@ ${sql}
|
|
|
7083
7054
|
);
|
|
7084
7055
|
const data = result?.data || result;
|
|
7085
7056
|
const rowCount = result?.count ?? (Array.isArray(data) ? data.length : "N/A");
|
|
7086
|
-
logger.info(`[${providerName}] Query executed successfully, rows returned: ${rowCount}`);
|
|
7087
|
-
logCollector?.info(`Query successful, returned ${rowCount} rows`);
|
|
7088
7057
|
if (streamBuffer.hasCallback()) {
|
|
7089
7058
|
streamBuffer.write(`
|
|
7090
7059
|
\u2705 **Query executed successfully!**
|
|
@@ -7133,7 +7102,6 @@ ${sql}
|
|
|
7133
7102
|
maxRows: DEFAULT_MAX_ROWS_FOR_LLM,
|
|
7134
7103
|
maxCharsPerField: DEFAULT_MAX_CHARS_PER_FIELD2
|
|
7135
7104
|
});
|
|
7136
|
-
logger.info(`[${providerName}] Query result formatted: ${formattedResult.summary.recordsShown}/${formattedResult.summary.totalRecords} records`);
|
|
7137
7105
|
if (formattedResult.truncationNote) {
|
|
7138
7106
|
logger.info(`[${providerName}] Truncation: ${formattedResult.truncationNote}`);
|
|
7139
7107
|
}
|
|
@@ -7141,7 +7109,6 @@ ${sql}
|
|
|
7141
7109
|
} catch (error) {
|
|
7142
7110
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
7143
7111
|
logger.error(`[${providerName}] Query execution failed (attempt ${attempts}/${MAX_QUERY_ATTEMPTS}): ${errorMsg}`);
|
|
7144
|
-
logCollector?.error(`Query failed (attempt ${attempts}/${MAX_QUERY_ATTEMPTS}): ${errorMsg}`);
|
|
7145
7112
|
userPromptErrorLogger.logSqlError(sql, error instanceof Error ? error : new Error(errorMsg), Object.keys(params).length > 0 ? Object.values(params) : void 0);
|
|
7146
7113
|
if (streamBuffer.hasCallback()) {
|
|
7147
7114
|
streamBuffer.write(`\u274C **Query execution failed:**
|
|
@@ -7163,19 +7130,16 @@ ${errorMsg}
|
|
|
7163
7130
|
* Execute an external tool with retry tracking and streaming feedback
|
|
7164
7131
|
*/
|
|
7165
7132
|
async executeExternalTool(toolName, toolInput, externalTools) {
|
|
7166
|
-
const { streamBuffer,
|
|
7133
|
+
const { streamBuffer, providerName } = this.config;
|
|
7167
7134
|
const externalTool = externalTools?.find((t) => t.id === toolName);
|
|
7168
7135
|
if (!externalTool) {
|
|
7169
7136
|
throw new Error(`Unknown tool: ${toolName}`);
|
|
7170
7137
|
}
|
|
7171
7138
|
const attempts = (this.toolAttempts.get(toolName) || 0) + 1;
|
|
7172
7139
|
this.toolAttempts.set(toolName, attempts);
|
|
7173
|
-
logger.info(`[${providerName}] Executing external tool: ${externalTool.name} (attempt ${attempts}/${MAX_TOOL_ATTEMPTS})`);
|
|
7174
|
-
logCollector?.info(`Executing external tool: ${externalTool.name} (attempt ${attempts}/${MAX_TOOL_ATTEMPTS})...`);
|
|
7175
7140
|
if (attempts > MAX_TOOL_ATTEMPTS) {
|
|
7176
7141
|
const errorMsg = `Maximum attempts (${MAX_TOOL_ATTEMPTS}) reached for tool: ${externalTool.name}`;
|
|
7177
7142
|
logger.error(`[${providerName}] ${errorMsg}`);
|
|
7178
|
-
logCollector?.error(errorMsg);
|
|
7179
7143
|
if (streamBuffer.hasCallback()) {
|
|
7180
7144
|
streamBuffer.write(`
|
|
7181
7145
|
|
|
@@ -7210,8 +7174,6 @@ Please try rephrasing your request or contact support.
|
|
|
7210
7174
|
`Running ${externalTool.name}`,
|
|
7211
7175
|
streamBuffer
|
|
7212
7176
|
);
|
|
7213
|
-
logger.info(`[${providerName}] External tool ${externalTool.name} executed successfully`);
|
|
7214
|
-
logCollector?.info(`\u2713 ${externalTool.name} executed successfully`);
|
|
7215
7177
|
if (!this.executedToolsList.find((t) => t.id === externalTool.id)) {
|
|
7216
7178
|
const formattedForTracking = formatToolResultForLLM(result, {
|
|
7217
7179
|
toolName: externalTool.name,
|
|
@@ -7231,7 +7193,6 @@ Please try rephrasing your request or contact support.
|
|
|
7231
7193
|
},
|
|
7232
7194
|
outputSchema: externalTool.outputSchema
|
|
7233
7195
|
});
|
|
7234
|
-
logger.info(`[${providerName}] Tracked executed tool: ${externalTool.name} with ${formattedForTracking.summary.totalRecords} total records`);
|
|
7235
7196
|
}
|
|
7236
7197
|
if (streamBuffer.hasCallback()) {
|
|
7237
7198
|
streamBuffer.write(`\u2705 **${externalTool.name} completed successfully**
|
|
@@ -7245,7 +7206,6 @@ Please try rephrasing your request or contact support.
|
|
|
7245
7206
|
maxRows: DEFAULT_MAX_ROWS_FOR_LLM,
|
|
7246
7207
|
maxCharsPerField: DEFAULT_MAX_CHARS_PER_FIELD2
|
|
7247
7208
|
});
|
|
7248
|
-
logger.info(`[${providerName}] Tool result formatted: ${formattedToolResult.summary.recordsShown}/${formattedToolResult.summary.totalRecords} records`);
|
|
7249
7209
|
if (formattedToolResult.truncationNote) {
|
|
7250
7210
|
logger.info(`[${providerName}] Truncation: ${formattedToolResult.truncationNote}`);
|
|
7251
7211
|
}
|
|
@@ -7253,7 +7213,6 @@ Please try rephrasing your request or contact support.
|
|
|
7253
7213
|
} catch (error) {
|
|
7254
7214
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
7255
7215
|
logger.error(`[${providerName}] External tool ${externalTool.name} failed (attempt ${attempts}/${MAX_TOOL_ATTEMPTS}): ${errorMsg}`);
|
|
7256
|
-
logCollector?.error(`\u2717 ${externalTool.name} failed: ${errorMsg}`);
|
|
7257
7216
|
userPromptErrorLogger.logToolError(externalTool.name, toolInput, error instanceof Error ? error : new Error(errorMsg));
|
|
7258
7217
|
if (streamBuffer.hasCallback()) {
|
|
7259
7218
|
streamBuffer.write(`\u274C **${externalTool.name} failed:**
|
|
@@ -7331,7 +7290,6 @@ var BaseLLM = class {
|
|
|
7331
7290
|
return;
|
|
7332
7291
|
}
|
|
7333
7292
|
this.conversationSimilarityThreshold = threshold;
|
|
7334
|
-
logger.info(`[${this.getProviderName()}] Conversation similarity threshold set to: ${threshold}`);
|
|
7335
7293
|
}
|
|
7336
7294
|
/**
|
|
7337
7295
|
* Get the current conversation similarity threshold
|
|
@@ -7374,16 +7332,14 @@ var BaseLLM = class {
|
|
|
7374
7332
|
* @param analysisContent - The text response containing component suggestions
|
|
7375
7333
|
* @param components - List of available components
|
|
7376
7334
|
* @param apiKey - Optional API key
|
|
7377
|
-
* @param logCollector - Optional log collector
|
|
7378
7335
|
* @param componentStreamCallback - Optional callback to stream primary KPI component as soon as it's identified
|
|
7379
7336
|
* @returns Object containing matched components, layout title/description, and follow-up actions
|
|
7380
7337
|
*/
|
|
7381
|
-
async matchComponentsFromAnalysis(analysisContent, components, userPrompt, apiKey,
|
|
7338
|
+
async matchComponentsFromAnalysis(analysisContent, components, userPrompt, apiKey, componentStreamCallback, deferredTools, executedTools, collections, userId) {
|
|
7382
7339
|
const methodStartTime = Date.now();
|
|
7383
7340
|
const methodName = "matchComponentsFromAnalysis";
|
|
7384
7341
|
logger.info(`[${this.getProviderName()}] [TIMING] START ${methodName} | model: ${this.getModelForTask("complex")}`);
|
|
7385
7342
|
try {
|
|
7386
|
-
logger.debug(`[${this.getProviderName()}] Starting component matching from text response`);
|
|
7387
7343
|
let availableComponentsText = "No components available";
|
|
7388
7344
|
if (components && components.length > 0) {
|
|
7389
7345
|
availableComponentsText = components.map((comp, idx) => {
|
|
@@ -7399,7 +7355,6 @@ var BaseLLM = class {
|
|
|
7399
7355
|
}
|
|
7400
7356
|
let deferredToolsText = "No deferred external tools for this request.";
|
|
7401
7357
|
if (deferredTools && deferredTools.length > 0) {
|
|
7402
|
-
logger.info(`[${this.getProviderName()}] Passing ${deferredTools.length} deferred tools to component matching`);
|
|
7403
7358
|
deferredToolsText = "The following external tools need user input via a Form component.\n**IMPORTANT: Use these EXACT values when generating Form externalTool prop.**\n\n" + deferredTools.map((tool, idx) => {
|
|
7404
7359
|
return `${idx + 1}. **${tool.name}**
|
|
7405
7360
|
toolId: "${tool.id}" (USE THIS EXACT VALUE - do not modify!)
|
|
@@ -7411,7 +7366,6 @@ var BaseLLM = class {
|
|
|
7411
7366
|
}
|
|
7412
7367
|
let executedToolsText = "No external tools were executed for data fetching.";
|
|
7413
7368
|
if (executedTools && executedTools.length > 0) {
|
|
7414
|
-
logger.info(`[${this.getProviderName()}] Passing ${executedTools.length} executed tools to component matching`);
|
|
7415
7369
|
executedToolsText = "The following external tools were executed to fetch data.\n" + executedTools.map((tool, idx) => {
|
|
7416
7370
|
let outputSchemaText = "Not available";
|
|
7417
7371
|
let fieldNamesList = "";
|
|
@@ -7467,14 +7421,12 @@ ${fieldsText}`;
|
|
|
7467
7421
|
KNOWLEDGE_BASE_CONTEXT: knowledgeBaseContext,
|
|
7468
7422
|
CURRENT_DATETIME: getCurrentDateTimeForPrompt()
|
|
7469
7423
|
});
|
|
7470
|
-
logger.debug(`[${this.getProviderName()}] Loaded match-text-components prompts`);
|
|
7471
7424
|
logger.logLLMPrompt("matchComponentsFromAnalysis", "system", extractPromptText(prompts.system));
|
|
7472
7425
|
logger.logLLMPrompt("matchComponentsFromAnalysis", "user", `Text Analysis:
|
|
7473
7426
|
${analysisContent}
|
|
7474
7427
|
|
|
7475
7428
|
Executed Tools:
|
|
7476
7429
|
${executedToolsText}`);
|
|
7477
|
-
logCollector?.info("Matching components from text response...");
|
|
7478
7430
|
let fullResponseText = "";
|
|
7479
7431
|
let answerComponentExtracted = false;
|
|
7480
7432
|
const answerCallback = componentStreamCallback;
|
|
@@ -7534,18 +7486,7 @@ ${executedToolsText}`);
|
|
|
7534
7486
|
...answerComponentData.props
|
|
7535
7487
|
}
|
|
7536
7488
|
};
|
|
7537
|
-
const streamTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
7538
|
-
logger.info(`[${this.getProviderName()}] \u2713 [${streamTime}] Answer component detected in stream: ${answerComponent.name} (${answerComponent.type})`);
|
|
7539
|
-
logCollector?.info(`\u2713 Answer component: ${answerComponent.name} (${answerComponent.type}) - detected at ${streamTime}`);
|
|
7540
|
-
if (answerComponentData.props?.query) {
|
|
7541
|
-
logCollector?.logQuery(
|
|
7542
|
-
"Answer component query",
|
|
7543
|
-
answerComponentData.props.query,
|
|
7544
|
-
{ componentName: answerComponent.name, componentType: answerComponent.type, reasoning: answerComponentData.reasoning }
|
|
7545
|
-
);
|
|
7546
|
-
}
|
|
7547
7489
|
let answerQuery = answerComponent.props?.query;
|
|
7548
|
-
logger.info(`[${this.getProviderName()}] Answer component detected: ${answerComponent.name} (${answerComponent.type}), hasQuery: ${!!answerQuery}, hasDbExecute: ${!!collections?.["database"]?.["execute"]}`);
|
|
7549
7490
|
if (answerQuery) {
|
|
7550
7491
|
if (typeof answerQuery === "string") {
|
|
7551
7492
|
answerQuery = ensureQueryLimit(answerQuery, this.defaultLimit, MAX_COMPONENT_QUERY_LIMIT);
|
|
@@ -7563,24 +7504,18 @@ ${executedToolsText}`);
|
|
|
7563
7504
|
let currentQuery = answerQuery;
|
|
7564
7505
|
let currentQueryStr = typeof answerQuery === "string" ? answerQuery : answerQuery?.sql || "";
|
|
7565
7506
|
let lastError = "";
|
|
7566
|
-
logger.info(`[${this.getProviderName()}] Validating answer component query before streaming...`);
|
|
7567
7507
|
while (attempts < maxRetries && !validated) {
|
|
7568
7508
|
attempts++;
|
|
7569
7509
|
try {
|
|
7570
7510
|
const cacheKey = this.queryService.getQueryCacheKey(currentQuery);
|
|
7571
7511
|
if (cacheKey) {
|
|
7572
|
-
logger.debug(`[${this.getProviderName()}] Answer component query validation attempt ${attempts}/${maxRetries}`);
|
|
7573
7512
|
const result2 = await collections["database"]["execute"]({ sql: cacheKey });
|
|
7574
7513
|
queryCache.set(cacheKey, result2);
|
|
7575
7514
|
validated = true;
|
|
7576
7515
|
if (currentQuery !== answerQuery) {
|
|
7577
7516
|
answerComponent.props.query = currentQuery;
|
|
7578
7517
|
}
|
|
7579
|
-
logger.info(`[${this.getProviderName()}] \u2713 Answer component query validated (attempt ${attempts}) - STREAMING TO FRONTEND NOW`);
|
|
7580
|
-
logCollector?.info(`\u2713 Answer component query validated - streaming to frontend`);
|
|
7581
|
-
logger.info(`[${this.getProviderName()}] Calling answerCallback for: ${answerComponent.name}`);
|
|
7582
7518
|
answerCallback(answerComponent);
|
|
7583
|
-
logger.info(`[${this.getProviderName()}] answerCallback completed for: ${answerComponent.name}`);
|
|
7584
7519
|
}
|
|
7585
7520
|
} catch (validationError) {
|
|
7586
7521
|
lastError = validationError instanceof Error ? validationError.message : String(validationError);
|
|
@@ -7616,7 +7551,6 @@ ${executedToolsText}`);
|
|
|
7616
7551
|
}
|
|
7617
7552
|
if (!validated) {
|
|
7618
7553
|
logger.warn(`[${this.getProviderName()}] Answer component query validation failed after ${attempts} attempts - component will be excluded`);
|
|
7619
|
-
logCollector?.warn(`Answer component query validation failed: ${lastError} - component will be excluded from response`);
|
|
7620
7554
|
}
|
|
7621
7555
|
})();
|
|
7622
7556
|
} else {
|
|
@@ -7627,7 +7561,7 @@ ${executedToolsText}`);
|
|
|
7627
7561
|
}
|
|
7628
7562
|
}
|
|
7629
7563
|
} catch (e) {
|
|
7630
|
-
logger.
|
|
7564
|
+
logger.error(`[${this.getProviderName()}] Partial answerComponent parse failed, waiting for more data...`);
|
|
7631
7565
|
}
|
|
7632
7566
|
}
|
|
7633
7567
|
}
|
|
@@ -7657,18 +7591,6 @@ ${executedToolsText}`);
|
|
|
7657
7591
|
logger.file("\n=============================\nFull LLM response:", JSON.stringify(result, null, 2));
|
|
7658
7592
|
const rawActions = result.actions || [];
|
|
7659
7593
|
const actions = convertQuestionsToActions(rawActions);
|
|
7660
|
-
if (matchedComponents.length > 0) {
|
|
7661
|
-
matchedComponents.forEach((comp, idx) => {
|
|
7662
|
-
logCollector?.info(` ${idx + 1}. ${comp.componentName} (${comp.componentType}): ${comp.reasoning}`);
|
|
7663
|
-
if (comp.props?.query) {
|
|
7664
|
-
logCollector?.logQuery(
|
|
7665
|
-
`Component ${idx + 1} query`,
|
|
7666
|
-
comp.props.query,
|
|
7667
|
-
{ componentName: comp.componentName, title: comp.props.title }
|
|
7668
|
-
);
|
|
7669
|
-
}
|
|
7670
|
-
});
|
|
7671
|
-
}
|
|
7672
7594
|
const finalComponents = matchedComponents.map((mc) => {
|
|
7673
7595
|
const originalComponent = components.find((c) => c.id === mc.componentId);
|
|
7674
7596
|
if (!originalComponent) {
|
|
@@ -7693,27 +7615,22 @@ ${executedToolsText}`);
|
|
|
7693
7615
|
}).filter(Boolean);
|
|
7694
7616
|
let validatedComponents = finalComponents;
|
|
7695
7617
|
if (collections?.["database"]?.["execute"]) {
|
|
7696
|
-
logger.info(`[${this.getProviderName()}] Starting query validation for ${finalComponents.length} components...`);
|
|
7697
|
-
logCollector?.info(`Validating queries for ${finalComponents.length} components...`);
|
|
7698
7618
|
try {
|
|
7699
7619
|
const validationResult = await this.queryService.validateComponentQueries(
|
|
7700
7620
|
finalComponents,
|
|
7701
7621
|
collections,
|
|
7702
|
-
apiKey
|
|
7703
|
-
logCollector
|
|
7622
|
+
apiKey
|
|
7704
7623
|
);
|
|
7705
7624
|
validatedComponents = validationResult.components;
|
|
7706
7625
|
const queriedComponents = finalComponents.filter((c) => c.props?.query);
|
|
7707
7626
|
const validatedQueries = validatedComponents.filter((c) => c.props?.query);
|
|
7708
7627
|
logger.info(`[${this.getProviderName()}] Query validation complete: ${validatedQueries.length}/${queriedComponents.length} queries validated`);
|
|
7709
|
-
logCollector?.info(`Query validation complete: ${validatedQueries.length}/${queriedComponents.length} queries validated`);
|
|
7710
7628
|
} catch (validationError) {
|
|
7711
7629
|
const validationErrorMsg = validationError instanceof Error ? validationError.message : String(validationError);
|
|
7712
7630
|
logger.error(`[${this.getProviderName()}] Query validation error: ${validationErrorMsg}`);
|
|
7713
|
-
logCollector?.error(`Query validation error: ${validationErrorMsg}`);
|
|
7714
7631
|
}
|
|
7715
7632
|
} else {
|
|
7716
|
-
logger.
|
|
7633
|
+
logger.error(`[${this.getProviderName()}] Skipping query validation - database execute function not available`);
|
|
7717
7634
|
}
|
|
7718
7635
|
const methodDuration = Date.now() - methodStartTime;
|
|
7719
7636
|
logger.info(`[${this.getProviderName()}] [TIMING] DONE ${methodName} in ${methodDuration}ms | components: ${validatedComponents.length} | actions: ${actions.length}`);
|
|
@@ -7727,7 +7644,6 @@ ${executedToolsText}`);
|
|
|
7727
7644
|
const methodDuration = Date.now() - methodStartTime;
|
|
7728
7645
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
7729
7646
|
logger.error(`[${this.getProviderName()}] [TIMING] FAILED ${methodName} in ${methodDuration}ms | error: ${errorMsg}`);
|
|
7730
|
-
logCollector?.error(`Failed to match components: ${errorMsg}`);
|
|
7731
7647
|
return {
|
|
7732
7648
|
components: [],
|
|
7733
7649
|
layoutTitle: "Dashboard",
|
|
@@ -7740,7 +7656,7 @@ ${executedToolsText}`);
|
|
|
7740
7656
|
* Classify user question into category and detect external tools needed
|
|
7741
7657
|
* Determines if question is for data analysis, requires external tools, or needs text response
|
|
7742
7658
|
*/
|
|
7743
|
-
async classifyQuestionCategory(userPrompt, apiKey,
|
|
7659
|
+
async classifyQuestionCategory(userPrompt, apiKey, conversationHistory, externalTools) {
|
|
7744
7660
|
const methodStartTime = Date.now();
|
|
7745
7661
|
const methodName = "classifyQuestionCategory";
|
|
7746
7662
|
const promptPreview = userPrompt.substring(0, 50) + (userPrompt.length > 50 ? "..." : "");
|
|
@@ -7776,16 +7692,6 @@ ${executedToolsText}`);
|
|
|
7776
7692
|
true
|
|
7777
7693
|
// Parse as JSON
|
|
7778
7694
|
);
|
|
7779
|
-
logCollector?.logExplanation(
|
|
7780
|
-
"Question category classified",
|
|
7781
|
-
result.reasoning || "No reasoning provided",
|
|
7782
|
-
{
|
|
7783
|
-
category: result.category,
|
|
7784
|
-
externalTools: result.externalTools || [],
|
|
7785
|
-
dataAnalysisType: result.dataAnalysisType,
|
|
7786
|
-
confidence: result.confidence
|
|
7787
|
-
}
|
|
7788
|
-
);
|
|
7789
7695
|
const methodDuration = Date.now() - methodStartTime;
|
|
7790
7696
|
logger.info(`[${this.getProviderName()}] [TIMING] DONE ${methodName} in ${methodDuration}ms | category: ${result.category} | confidence: ${result.confidence}% | tools: ${(result.externalTools || []).length}`);
|
|
7791
7697
|
return {
|
|
@@ -7799,7 +7705,6 @@ ${executedToolsText}`);
|
|
|
7799
7705
|
const methodDuration = Date.now() - methodStartTime;
|
|
7800
7706
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
7801
7707
|
logger.error(`[${this.getProviderName()}] [TIMING] FAILED ${methodName} in ${methodDuration}ms | error: ${errorMsg}`);
|
|
7802
|
-
logger.debug(`[${this.getProviderName()}] Category classification error details:`, error);
|
|
7803
7708
|
throw error;
|
|
7804
7709
|
}
|
|
7805
7710
|
}
|
|
@@ -7808,7 +7713,7 @@ ${executedToolsText}`);
|
|
|
7808
7713
|
* Takes a matched UI block from semantic search and modifies its props to answer the new question
|
|
7809
7714
|
* Also adapts the cached text response to match the new question
|
|
7810
7715
|
*/
|
|
7811
|
-
async adaptUIBlockParameters(currentUserPrompt, originalUserPrompt, matchedUIBlock, apiKey,
|
|
7716
|
+
async adaptUIBlockParameters(currentUserPrompt, originalUserPrompt, matchedUIBlock, apiKey, cachedTextResponse) {
|
|
7812
7717
|
const methodStartTime = Date.now();
|
|
7813
7718
|
const methodName = "adaptUIBlockParameters";
|
|
7814
7719
|
const promptPreview = currentUserPrompt.substring(0, 50) + (currentUserPrompt.length > 50 ? "..." : "");
|
|
@@ -7852,11 +7757,6 @@ ${executedToolsText}`);
|
|
|
7852
7757
|
if (!result.success) {
|
|
7853
7758
|
const methodDuration2 = Date.now() - methodStartTime;
|
|
7854
7759
|
logger.info(`[${this.getProviderName()}] [TIMING] DONE ${methodName} in ${methodDuration2}ms | result: adaptation failed - ${result.reason}`);
|
|
7855
|
-
logCollector?.warn(
|
|
7856
|
-
"Could not adapt matched UI block",
|
|
7857
|
-
"explanation",
|
|
7858
|
-
{ reason: result.reason }
|
|
7859
|
-
);
|
|
7860
7760
|
return {
|
|
7861
7761
|
success: false,
|
|
7862
7762
|
explanation: result.explanation || "Adaptation not possible"
|
|
@@ -7868,14 +7768,6 @@ ${executedToolsText}`);
|
|
|
7868
7768
|
this.defaultLimit
|
|
7869
7769
|
);
|
|
7870
7770
|
}
|
|
7871
|
-
logCollector?.logExplanation(
|
|
7872
|
-
"UI block parameters adapted",
|
|
7873
|
-
result.explanation || "Parameters adapted successfully",
|
|
7874
|
-
{
|
|
7875
|
-
parametersChanged: result.parametersChanged || [],
|
|
7876
|
-
componentType: result.adaptedComponent?.type
|
|
7877
|
-
}
|
|
7878
|
-
);
|
|
7879
7771
|
const methodDuration = Date.now() - methodStartTime;
|
|
7880
7772
|
logger.info(`[${this.getProviderName()}] [TIMING] DONE ${methodName} in ${methodDuration}ms | result: success | changes: ${(result.parametersChanged || []).length}`);
|
|
7881
7773
|
return {
|
|
@@ -7889,7 +7781,6 @@ ${executedToolsText}`);
|
|
|
7889
7781
|
const methodDuration = Date.now() - methodStartTime;
|
|
7890
7782
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
7891
7783
|
logger.error(`[${this.getProviderName()}] [TIMING] FAILED ${methodName} in ${methodDuration}ms | error: ${errorMsg}`);
|
|
7892
|
-
logger.debug(`[${this.getProviderName()}] Adaptation error details:`, error);
|
|
7893
7784
|
return {
|
|
7894
7785
|
success: false,
|
|
7895
7786
|
explanation: `Error adapting parameters: ${errorMsg}`
|
|
@@ -7902,14 +7793,12 @@ ${executedToolsText}`);
|
|
|
7902
7793
|
* Supports tool calling for query execution with automatic retry on errors (max 3 attempts)
|
|
7903
7794
|
* After generating text response, if components are provided, matches suggested components
|
|
7904
7795
|
*/
|
|
7905
|
-
async generateTextResponse(userPrompt, apiKey,
|
|
7796
|
+
async generateTextResponse(userPrompt, apiKey, conversationHistory, streamCallback, collections, components, externalTools, category, userId) {
|
|
7906
7797
|
const methodStartTime = Date.now();
|
|
7907
7798
|
const methodName = "generateTextResponse";
|
|
7908
7799
|
const promptPreview = userPrompt.substring(0, 50) + (userPrompt.length > 50 ? "..." : "");
|
|
7909
7800
|
logger.info(`[${this.getProviderName()}] [TIMING] START ${methodName} | model: ${this.getModelForTask("complex")} | category: ${category} | prompt: "${promptPreview}"`);
|
|
7910
7801
|
const errors = [];
|
|
7911
|
-
logger.debug(`[${this.getProviderName()}] Starting text response generation`);
|
|
7912
|
-
logger.debug(`[${this.getProviderName()}] User prompt: "${userPrompt.substring(0, 50)}..."`);
|
|
7913
7802
|
try {
|
|
7914
7803
|
let availableToolsDoc = "No external tools are available for this request.";
|
|
7915
7804
|
if (externalTools && externalTools.length > 0) {
|
|
@@ -7973,9 +7862,6 @@ ${executedToolsText}`);
|
|
|
7973
7862
|
});
|
|
7974
7863
|
logger.logLLMPrompt("generateTextResponse", "system", extractPromptText(prompts.system));
|
|
7975
7864
|
logger.logLLMPrompt("generateTextResponse", "user", extractPromptText(prompts.user));
|
|
7976
|
-
logger.debug(`[${this.getProviderName()}] Loaded text-response prompts with schema`);
|
|
7977
|
-
logger.debug(`[${this.getProviderName()}] System prompt length: ${prompts.system.length}, User prompt length: ${prompts.user.length}`);
|
|
7978
|
-
logCollector?.info("Generating text response with query execution capability...");
|
|
7979
7865
|
const tools = [{
|
|
7980
7866
|
name: "execute_query",
|
|
7981
7867
|
description: "Executes a parameterized SQL query against the database. CRITICAL: NEVER hardcode literal values in WHERE/HAVING conditions - ALWAYS use $paramName placeholders and pass actual values in params object.",
|
|
@@ -8004,7 +7890,6 @@ ${executedToolsText}`);
|
|
|
8004
7890
|
const executableTools = externalTools.filter(
|
|
8005
7891
|
(t) => t.executionType === "immediate" || t.executionType === "deferred" && t.userProvidedData
|
|
8006
7892
|
);
|
|
8007
|
-
logger.info(`[${this.getProviderName()}] Executable tools: ${executableTools.length} of ${externalTools.length} total`);
|
|
8008
7893
|
const addedToolIds = /* @__PURE__ */ new Set();
|
|
8009
7894
|
executableTools.forEach((tool) => {
|
|
8010
7895
|
if (addedToolIds.has(tool.id)) {
|
|
@@ -8012,7 +7897,6 @@ ${executedToolsText}`);
|
|
|
8012
7897
|
return;
|
|
8013
7898
|
}
|
|
8014
7899
|
addedToolIds.add(tool.id);
|
|
8015
|
-
logger.info(`[${this.getProviderName()}] Processing executable tool:`, JSON.stringify(tool, null, 2));
|
|
8016
7900
|
const properties = {};
|
|
8017
7901
|
const required = [];
|
|
8018
7902
|
Object.entries(tool.params || {}).forEach(([key, typeOrValue]) => {
|
|
@@ -8082,14 +7966,13 @@ ${executedToolsText}`);
|
|
|
8082
7966
|
});
|
|
8083
7967
|
});
|
|
8084
7968
|
logger.info(`[${this.getProviderName()}] Added ${addedToolIds.size} unique tool definitions from ${executableTools.length} tool calls (${externalTools.length - executableTools.length} deferred tools await form input)`);
|
|
8085
|
-
logger.
|
|
7969
|
+
logger.debug(`[${this.getProviderName()}] Complete tools array:`, JSON.stringify(tools, null, 2));
|
|
8086
7970
|
}
|
|
8087
7971
|
const streamBuffer = new StreamBuffer(streamCallback);
|
|
8088
7972
|
const toolExecutor = new ToolExecutorService({
|
|
8089
7973
|
providerName: this.getProviderName(),
|
|
8090
7974
|
collections,
|
|
8091
|
-
streamBuffer
|
|
8092
|
-
logCollector
|
|
7975
|
+
streamBuffer
|
|
8093
7976
|
});
|
|
8094
7977
|
const executableExternalTools = externalTools?.map((t) => ({
|
|
8095
7978
|
id: t.id,
|
|
@@ -8118,12 +8001,10 @@ ${executedToolsText}`);
|
|
|
8118
8001
|
},
|
|
8119
8002
|
MAX_TOOL_CALLING_ITERATIONS
|
|
8120
8003
|
);
|
|
8121
|
-
logger.info(`[${this.getProviderName()}] Text response stream completed`);
|
|
8122
8004
|
const textResponse = streamBuffer.getFullText() || result || "I apologize, but I was unable to generate a response.";
|
|
8123
8005
|
if (toolExecutor.isMaxAttemptsReached()) {
|
|
8124
8006
|
const methodDuration2 = Date.now() - methodStartTime;
|
|
8125
8007
|
logger.warn(`[${this.getProviderName()}] [TIMING] DONE ${methodName} in ${methodDuration2}ms | result: max attempts reached`);
|
|
8126
|
-
logCollector?.error("Failed to generate valid query after maximum attempts");
|
|
8127
8008
|
return {
|
|
8128
8009
|
success: false,
|
|
8129
8010
|
errors: [`Maximum query attempts (${MAX_QUERY_ATTEMPTS}) reached. Unable to generate a valid query for your question.`],
|
|
@@ -8135,14 +8016,6 @@ ${executedToolsText}`);
|
|
|
8135
8016
|
}
|
|
8136
8017
|
};
|
|
8137
8018
|
}
|
|
8138
|
-
logCollector?.info(`Text response: ${textResponse.substring(0, 100)}${textResponse.length > 100 ? "..." : ""}`);
|
|
8139
|
-
logCollector?.logExplanation(
|
|
8140
|
-
"Text response generated",
|
|
8141
|
-
"Generated plain text response with component suggestions",
|
|
8142
|
-
{
|
|
8143
|
-
textLength: textResponse.length
|
|
8144
|
-
}
|
|
8145
|
-
);
|
|
8146
8019
|
streamBuffer.flush();
|
|
8147
8020
|
if (streamBuffer.hasCallback() && components && components.length > 0 && category !== "general") {
|
|
8148
8021
|
streamBuffer.write("\n\n\u{1F4CA} **Generating visualization components...**\n\n");
|
|
@@ -8154,8 +8027,6 @@ ${executedToolsText}`);
|
|
|
8154
8027
|
let actions = [];
|
|
8155
8028
|
if (category === "general") {
|
|
8156
8029
|
logger.info(`[${this.getProviderName()}] Skipping component generation for general/conversational question`);
|
|
8157
|
-
logCollector?.info("Skipping component generation for general question");
|
|
8158
|
-
logger.info(`[${this.getProviderName()}] Generating actions for general question...`);
|
|
8159
8030
|
const nextQuestions = await this.generateNextQuestions(
|
|
8160
8031
|
userPrompt,
|
|
8161
8032
|
null,
|
|
@@ -8163,23 +8034,16 @@ ${executedToolsText}`);
|
|
|
8163
8034
|
void 0,
|
|
8164
8035
|
// no component data
|
|
8165
8036
|
apiKey,
|
|
8166
|
-
logCollector,
|
|
8167
8037
|
conversationHistory,
|
|
8168
8038
|
textResponse
|
|
8169
8039
|
// pass text response as context
|
|
8170
8040
|
);
|
|
8171
8041
|
actions = convertQuestionsToActions(nextQuestions);
|
|
8172
|
-
logger.info(`[${this.getProviderName()}] Generated ${actions.length} follow-up actions for general question`);
|
|
8173
8042
|
} else if (components && components.length > 0) {
|
|
8174
|
-
|
|
8175
|
-
logger.info(`[${this.getProviderName()}] componentStreamCallback setup: hasCallback=${streamBuffer.hasCallback()}, category=${category}`);
|
|
8176
|
-
const componentStreamCallback = streamBuffer.hasCallback() && category !== "data_modification" ? (component) => {
|
|
8177
|
-
logger.info(`[${this.getProviderName()}] componentStreamCallback INVOKED for: ${component.name} (${component.type})`);
|
|
8043
|
+
const componentStreamCallback = streamBuffer.hasCallback() && category === "data_analysis" ? (component) => {
|
|
8178
8044
|
const answerMarker = `__ANSWER_COMPONENT_START__${JSON.stringify(component)}__ANSWER_COMPONENT_END__`;
|
|
8179
8045
|
streamBuffer.write(answerMarker);
|
|
8180
|
-
logger.info(`[${this.getProviderName()}] Streamed answer component to frontend: ${component.name} (${component.type})`);
|
|
8181
8046
|
} : void 0;
|
|
8182
|
-
logger.info(`[${this.getProviderName()}] componentStreamCallback created: ${!!componentStreamCallback}`);
|
|
8183
8047
|
const deferredTools = externalTools?.filter((t) => {
|
|
8184
8048
|
if (t.executionType === "deferred" && !t.userProvidedData) return true;
|
|
8185
8049
|
if (category === "data_modification" && !t.userProvidedData) {
|
|
@@ -8200,7 +8064,6 @@ ${executedToolsText}`);
|
|
|
8200
8064
|
components,
|
|
8201
8065
|
userPrompt,
|
|
8202
8066
|
apiKey,
|
|
8203
|
-
logCollector,
|
|
8204
8067
|
componentStreamCallback,
|
|
8205
8068
|
deferredTools,
|
|
8206
8069
|
toolExecutor.getExecutedTools(),
|
|
@@ -8214,8 +8077,6 @@ ${executedToolsText}`);
|
|
|
8214
8077
|
}
|
|
8215
8078
|
let container_componet = null;
|
|
8216
8079
|
if (matchedComponents.length > 0) {
|
|
8217
|
-
logger.info(`[${this.getProviderName()}] Created MultiComponentContainer: "${layoutTitle}" with ${matchedComponents.length} components and ${actions.length} actions`);
|
|
8218
|
-
logCollector?.info(`Created dashboard: "${layoutTitle}" with ${matchedComponents.length} components and ${actions.length} actions`);
|
|
8219
8080
|
container_componet = {
|
|
8220
8081
|
id: `container_${Date.now()}`,
|
|
8221
8082
|
name: "MultiComponentContainer",
|
|
@@ -8247,7 +8108,6 @@ ${executedToolsText}`);
|
|
|
8247
8108
|
const methodDuration = Date.now() - methodStartTime;
|
|
8248
8109
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
8249
8110
|
logger.error(`[${this.getProviderName()}] [TIMING] FAILED ${methodName} in ${methodDuration}ms | error: ${errorMsg}`);
|
|
8250
|
-
logCollector?.error(`Error generating text response: ${errorMsg}`);
|
|
8251
8111
|
userPromptErrorLogger.logLlmError(
|
|
8252
8112
|
this.getProviderName(),
|
|
8253
8113
|
this.model,
|
|
@@ -8275,14 +8135,11 @@ ${executedToolsText}`);
|
|
|
8275
8135
|
* 2. Category classification: Determine if data_analysis, requires_external_tools, or text_response
|
|
8276
8136
|
* 3. Route appropriately based on category and response mode
|
|
8277
8137
|
*/
|
|
8278
|
-
async handleUserRequest(userPrompt, components, apiKey,
|
|
8138
|
+
async handleUserRequest(userPrompt, components, apiKey, conversationHistory, responseMode = "text", streamCallback, collections, externalTools, userId) {
|
|
8279
8139
|
const startTime = Date.now();
|
|
8280
|
-
logger.info(`[${this.getProviderName()}] handleUserRequest called for user prompt: ${userPrompt}`);
|
|
8281
|
-
logCollector?.info(`Starting request processing with mode: ${responseMode}`);
|
|
8282
8140
|
logger.clearFile();
|
|
8283
8141
|
logger.logLLMPrompt("handleUserRequest", "user", `User Prompt: ${userPrompt}`);
|
|
8284
8142
|
try {
|
|
8285
|
-
logger.info(`[${this.getProviderName()}] Step 1: Searching previous conversations...`);
|
|
8286
8143
|
const conversationMatch = await conversation_search_default.searchConversationsWithReranking({
|
|
8287
8144
|
userPrompt,
|
|
8288
8145
|
collections,
|
|
@@ -8291,22 +8148,16 @@ ${executedToolsText}`);
|
|
|
8291
8148
|
});
|
|
8292
8149
|
if (conversationMatch) {
|
|
8293
8150
|
logger.info(`[${this.getProviderName()}] \u2713 Found matching conversation with ${(conversationMatch.similarity * 100).toFixed(2)}% similarity`);
|
|
8294
|
-
logCollector?.info(
|
|
8295
|
-
`\u2713 Found similar conversation (${(conversationMatch.similarity * 100).toFixed(2)}% match)`
|
|
8296
|
-
);
|
|
8297
8151
|
const rawComponent = conversationMatch.uiBlock?.component || conversationMatch.uiBlock?.generatedComponentMetadata;
|
|
8298
8152
|
const isValidComponent = rawComponent && typeof rawComponent === "object" && Object.keys(rawComponent).length > 0;
|
|
8299
8153
|
const component = isValidComponent ? rawComponent : null;
|
|
8300
8154
|
const cachedTextResponse = conversationMatch.uiBlock?.analysis || conversationMatch.uiBlock?.textResponse || conversationMatch.uiBlock?.text || "";
|
|
8301
8155
|
if (this.containsFormComponent(component)) {
|
|
8302
8156
|
logger.info(`[${this.getProviderName()}] Skipping cached result - Form components contain stale defaultValues, fetching fresh data`);
|
|
8303
|
-
logCollector?.info("Skipping cache for form - fetching current values from database...");
|
|
8304
8157
|
} else if (!component) {
|
|
8305
8158
|
if (conversationMatch.similarity >= EXACT_MATCH_SIMILARITY_THRESHOLD) {
|
|
8306
8159
|
const elapsedTime2 = Date.now() - startTime;
|
|
8307
|
-
logger.info(`[${this.getProviderName()}] \u2713 Exact match for general question - returning cached text response`);
|
|
8308
|
-
logCollector?.info(`\u2713 Exact match for general question - returning cached response`);
|
|
8309
|
-
logCollector?.info(`Total time taken: ${elapsedTime2}ms (${(elapsedTime2 / 1e3).toFixed(2)}s)`);
|
|
8160
|
+
logger.info(`[${this.getProviderName()}] \u2713 Exact match for general question - returning cached text response (${elapsedTime2}ms)`);
|
|
8310
8161
|
return {
|
|
8311
8162
|
success: true,
|
|
8312
8163
|
data: {
|
|
@@ -8321,14 +8172,11 @@ ${executedToolsText}`);
|
|
|
8321
8172
|
};
|
|
8322
8173
|
} else {
|
|
8323
8174
|
logger.info(`[${this.getProviderName()}] Similar match but no component (general question) - processing fresh`);
|
|
8324
|
-
logCollector?.info("Similar match found but was a general conversation - processing as new question");
|
|
8325
8175
|
}
|
|
8326
8176
|
} else {
|
|
8327
8177
|
if (conversationMatch.similarity >= EXACT_MATCH_SIMILARITY_THRESHOLD) {
|
|
8328
8178
|
const elapsedTime2 = Date.now() - startTime;
|
|
8329
|
-
logger.info(`[${this.getProviderName()}] \u2713 100% match - returning UI block directly without adaptation`);
|
|
8330
|
-
logCollector?.info(`\u2713 Exact match (${(conversationMatch.similarity * 100).toFixed(2)}%) - returning cached result`);
|
|
8331
|
-
logCollector?.info(`Total time taken: ${elapsedTime2}ms (${(elapsedTime2 / 1e3).toFixed(2)}s)`);
|
|
8179
|
+
logger.info(`[${this.getProviderName()}] \u2713 100% match - returning UI block directly without adaptation (${elapsedTime2}ms)`);
|
|
8332
8180
|
if (streamCallback && cachedTextResponse) {
|
|
8333
8181
|
logger.info(`[${this.getProviderName()}] Streaming cached text response to frontend`);
|
|
8334
8182
|
streamCallback(cachedTextResponse);
|
|
@@ -8347,22 +8195,18 @@ ${executedToolsText}`);
|
|
|
8347
8195
|
errors: []
|
|
8348
8196
|
};
|
|
8349
8197
|
}
|
|
8350
|
-
|
|
8198
|
+
logger.info(`[${this.getProviderName()}] Adapting parameters for similar question...`);
|
|
8351
8199
|
const originalPrompt = conversationMatch.metadata?.userPrompt || "Previous question";
|
|
8352
8200
|
const adaptResult = await this.adaptUIBlockParameters(
|
|
8353
8201
|
userPrompt,
|
|
8354
8202
|
originalPrompt,
|
|
8355
8203
|
conversationMatch.uiBlock,
|
|
8356
8204
|
apiKey,
|
|
8357
|
-
logCollector,
|
|
8358
8205
|
cachedTextResponse
|
|
8359
8206
|
);
|
|
8360
8207
|
if (adaptResult.success && adaptResult.adaptedComponent) {
|
|
8361
8208
|
const elapsedTime2 = Date.now() - startTime;
|
|
8362
|
-
logger.info(`[${this.getProviderName()}] \u2713 Successfully adapted UI block parameters`);
|
|
8363
|
-
logger.info(`[${this.getProviderName()}] Total time taken: ${elapsedTime2}ms (${(elapsedTime2 / 1e3).toFixed(2)}s)`);
|
|
8364
|
-
logCollector?.info(`\u2713 UI block adapted successfully`);
|
|
8365
|
-
logCollector?.info(`Total time taken: ${elapsedTime2}ms (${(elapsedTime2 / 1e3).toFixed(2)}s)`);
|
|
8209
|
+
logger.info(`[${this.getProviderName()}] \u2713 Successfully adapted UI block parameters (${elapsedTime2}ms)`);
|
|
8366
8210
|
const textResponseToUse = adaptResult.adaptedTextResponse || cachedTextResponse;
|
|
8367
8211
|
if (streamCallback && textResponseToUse) {
|
|
8368
8212
|
logger.info(`[${this.getProviderName()}] Streaming ${adaptResult.adaptedTextResponse ? "adapted" : "cached"} text response to frontend`);
|
|
@@ -8383,65 +8227,57 @@ ${executedToolsText}`);
|
|
|
8383
8227
|
errors: []
|
|
8384
8228
|
};
|
|
8385
8229
|
} else {
|
|
8386
|
-
logger.info(`[${this.getProviderName()}] Could not adapt matched conversation, continuing to category classification`);
|
|
8387
|
-
logCollector?.warn(`Could not adapt matched conversation: ${adaptResult.explanation}`);
|
|
8230
|
+
logger.info(`[${this.getProviderName()}] Could not adapt matched conversation: ${adaptResult.explanation}, continuing to category classification`);
|
|
8388
8231
|
}
|
|
8389
8232
|
}
|
|
8390
8233
|
} else {
|
|
8391
8234
|
logger.info(`[${this.getProviderName()}] No matching previous conversations found, proceeding to category classification`);
|
|
8392
|
-
logCollector?.info("No similar previous conversations found. Proceeding to category classification...");
|
|
8393
8235
|
}
|
|
8394
8236
|
logger.info(`[${this.getProviderName()}] Step 2: Classifying question category...`);
|
|
8395
|
-
logCollector?.info("Step 2: Classifying question category...");
|
|
8396
8237
|
const categoryClassification = await this.classifyQuestionCategory(
|
|
8397
8238
|
userPrompt,
|
|
8398
8239
|
apiKey,
|
|
8399
|
-
logCollector,
|
|
8400
8240
|
conversationHistory,
|
|
8401
8241
|
externalTools
|
|
8402
8242
|
);
|
|
8403
8243
|
logger.info(
|
|
8404
8244
|
`[${this.getProviderName()}] Question classified as: ${categoryClassification.category} (confidence: ${categoryClassification.confidence}%)`
|
|
8405
8245
|
);
|
|
8406
|
-
logCollector?.info(
|
|
8407
|
-
`Category: ${categoryClassification.category} | Confidence: ${categoryClassification.confidence}%`
|
|
8408
|
-
);
|
|
8409
8246
|
let toolsToUse = [];
|
|
8410
8247
|
if (categoryClassification.externalTools && categoryClassification.externalTools.length > 0) {
|
|
8411
|
-
logger.info(`[${this.getProviderName()}] Identified ${categoryClassification.externalTools.length} external tools needed`);
|
|
8412
|
-
|
|
8413
|
-
|
|
8414
|
-
toolsToUse = categoryClassification.externalTools?.map((t) => {
|
|
8248
|
+
logger.info(`[${this.getProviderName()}] Identified ${categoryClassification.externalTools.length} external tools needed: ${categoryClassification.externalTools.map((t) => t.name || t.type).join(", ")}`);
|
|
8249
|
+
logger.debug(`[${this.getProviderName()}] Raw external tools from classification: ${JSON.stringify(categoryClassification.externalTools, null, 2)}`);
|
|
8250
|
+
toolsToUse = categoryClassification.externalTools.reduce((acc, t) => {
|
|
8415
8251
|
const realTool = externalTools?.find((tool) => tool.id === t.type);
|
|
8416
|
-
|
|
8417
|
-
|
|
8252
|
+
if (!realTool) {
|
|
8253
|
+
logger.warn(`[${this.getProviderName()}] Tool ${t.type} (${t.name}) not found in registered tools - skipping (likely hallucinated)`);
|
|
8254
|
+
return acc;
|
|
8255
|
+
}
|
|
8256
|
+
acc.push({
|
|
8418
8257
|
id: t.type,
|
|
8419
8258
|
name: t.name,
|
|
8420
8259
|
description: t.description,
|
|
8421
8260
|
params: t.parameters || {},
|
|
8422
|
-
//
|
|
8261
|
+
// Include execution type info from category classification
|
|
8423
8262
|
executionType: t.executionType || "immediate",
|
|
8424
8263
|
executionReason: t.executionReason || "",
|
|
8425
8264
|
requiredFields: t.requiredFields || [],
|
|
8426
8265
|
userProvidedData: t.userProvidedData || null,
|
|
8427
|
-
//
|
|
8428
|
-
outputSchema: realTool
|
|
8429
|
-
fn:
|
|
8430
|
-
|
|
8431
|
-
|
|
8432
|
-
|
|
8433
|
-
|
|
8434
|
-
|
|
8435
|
-
|
|
8436
|
-
|
|
8437
|
-
|
|
8438
|
-
};
|
|
8439
|
-
}) || [];
|
|
8266
|
+
// Include outputSchema from real tool for component config generation
|
|
8267
|
+
outputSchema: realTool.outputSchema,
|
|
8268
|
+
fn: realTool.fn
|
|
8269
|
+
});
|
|
8270
|
+
return acc;
|
|
8271
|
+
}, []);
|
|
8272
|
+
const validCount = toolsToUse.length;
|
|
8273
|
+
const hallucinatedCount = categoryClassification.externalTools.length - validCount;
|
|
8274
|
+
if (hallucinatedCount > 0) {
|
|
8275
|
+
logger.warn(`[${this.getProviderName()}] Filtered out ${hallucinatedCount} hallucinated/non-existent tools, ${validCount} valid tools remaining`);
|
|
8276
|
+
}
|
|
8440
8277
|
}
|
|
8441
8278
|
const textResponse = await this.generateTextResponse(
|
|
8442
8279
|
userPrompt,
|
|
8443
8280
|
apiKey,
|
|
8444
|
-
logCollector,
|
|
8445
8281
|
conversationHistory,
|
|
8446
8282
|
streamCallback,
|
|
8447
8283
|
collections,
|
|
@@ -8452,13 +8288,10 @@ ${executedToolsText}`);
|
|
|
8452
8288
|
);
|
|
8453
8289
|
const elapsedTime = Date.now() - startTime;
|
|
8454
8290
|
logger.info(`[${this.getProviderName()}] Total time taken: ${elapsedTime}ms (${(elapsedTime / 1e3).toFixed(2)}s)`);
|
|
8455
|
-
logCollector?.info(`Total time taken: ${elapsedTime}ms (${(elapsedTime / 1e3).toFixed(2)}s)`);
|
|
8456
8291
|
return textResponse;
|
|
8457
8292
|
} catch (error) {
|
|
8458
8293
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
8459
8294
|
logger.error(`[${this.getProviderName()}] Error in handleUserRequest: ${errorMsg}`);
|
|
8460
|
-
logger.debug(`[${this.getProviderName()}] Error details:`, error);
|
|
8461
|
-
logCollector?.error(`Error processing request: ${errorMsg}`);
|
|
8462
8295
|
userPromptErrorLogger.logError(
|
|
8463
8296
|
"handleUserRequest",
|
|
8464
8297
|
error instanceof Error ? error : new Error(errorMsg),
|
|
@@ -8466,7 +8299,6 @@ ${executedToolsText}`);
|
|
|
8466
8299
|
);
|
|
8467
8300
|
const elapsedTime = Date.now() - startTime;
|
|
8468
8301
|
logger.info(`[${this.getProviderName()}] Total time taken: ${elapsedTime}ms (${(elapsedTime / 1e3).toFixed(2)}s)`);
|
|
8469
|
-
logCollector?.info(`Total time taken: ${elapsedTime}ms (${(elapsedTime / 1e3).toFixed(2)}s)`);
|
|
8470
8302
|
return {
|
|
8471
8303
|
success: false,
|
|
8472
8304
|
errors: [errorMsg],
|
|
@@ -8482,7 +8314,7 @@ ${executedToolsText}`);
|
|
|
8482
8314
|
* This helps provide intelligent suggestions for follow-up queries
|
|
8483
8315
|
* For general/conversational questions without components, pass textResponse instead
|
|
8484
8316
|
*/
|
|
8485
|
-
async generateNextQuestions(originalUserPrompt, component, componentData, apiKey,
|
|
8317
|
+
async generateNextQuestions(originalUserPrompt, component, componentData, apiKey, conversationHistory, textResponse) {
|
|
8486
8318
|
const methodStartTime = Date.now();
|
|
8487
8319
|
const methodName = "generateNextQuestions";
|
|
8488
8320
|
const promptPreview = originalUserPrompt.substring(0, 50) + (originalUserPrompt.length > 50 ? "..." : "");
|
|
@@ -8527,14 +8359,6 @@ ${executedToolsText}`);
|
|
|
8527
8359
|
// Parse as JSON
|
|
8528
8360
|
);
|
|
8529
8361
|
const nextQuestions = result.nextQuestions || [];
|
|
8530
|
-
logCollector?.logExplanation(
|
|
8531
|
-
"Next questions generated",
|
|
8532
|
-
"Generated intelligent follow-up questions based on component",
|
|
8533
|
-
{
|
|
8534
|
-
count: nextQuestions.length,
|
|
8535
|
-
questions: nextQuestions
|
|
8536
|
-
}
|
|
8537
|
-
);
|
|
8538
8362
|
const methodDuration = Date.now() - methodStartTime;
|
|
8539
8363
|
logger.info(`[${this.getProviderName()}] [TIMING] DONE ${methodName} in ${methodDuration}ms | questions: ${nextQuestions.length}`);
|
|
8540
8364
|
return nextQuestions;
|
|
@@ -8542,8 +8366,6 @@ ${executedToolsText}`);
|
|
|
8542
8366
|
const methodDuration = Date.now() - methodStartTime;
|
|
8543
8367
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
8544
8368
|
logger.error(`[${this.getProviderName()}] [TIMING] FAILED ${methodName} in ${methodDuration}ms | error: ${errorMsg}`);
|
|
8545
|
-
logger.debug(`[${this.getProviderName()}] Next questions generation error details:`, error);
|
|
8546
|
-
logCollector?.error(`Error generating next questions: ${errorMsg}`);
|
|
8547
8369
|
return [];
|
|
8548
8370
|
}
|
|
8549
8371
|
}
|
|
@@ -8657,332 +8479,96 @@ function getLLMProviders() {
|
|
|
8657
8479
|
return DEFAULT_PROVIDERS;
|
|
8658
8480
|
}
|
|
8659
8481
|
}
|
|
8660
|
-
var useAnthropicMethod = async (prompt, components, apiKey,
|
|
8661
|
-
logger.debug("[useAnthropicMethod] Initializing Anthropic Claude matching method");
|
|
8662
|
-
logger.debug(`[useAnthropicMethod] Response mode: ${responseMode}`);
|
|
8663
|
-
const msg = `Using Anthropic Claude ${responseMode === "text" ? "text response" : "matching"} method...`;
|
|
8664
|
-
logCollector?.info(msg);
|
|
8482
|
+
var useAnthropicMethod = async (prompt, components, apiKey, conversationHistory, responseMode = "component", streamCallback, collections, externalTools, userId) => {
|
|
8665
8483
|
if (responseMode === "component" && components.length === 0) {
|
|
8666
8484
|
const emptyMsg = "Components not loaded in memory. Please ensure components are fetched first.";
|
|
8667
8485
|
logger.error("[useAnthropicMethod] No components available");
|
|
8668
|
-
logCollector?.error(emptyMsg);
|
|
8669
8486
|
return { success: false, errors: [emptyMsg] };
|
|
8670
8487
|
}
|
|
8671
|
-
|
|
8672
|
-
const matchResult = await anthropicLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory, responseMode, streamCallback, collections, externalTools, userId);
|
|
8488
|
+
const matchResult = await anthropicLLM.handleUserRequest(prompt, components, apiKey, conversationHistory, responseMode, streamCallback, collections, externalTools, userId);
|
|
8673
8489
|
logger.info(`[useAnthropicMethod] Successfully generated ${responseMode} using Anthropic`);
|
|
8674
8490
|
return matchResult;
|
|
8675
8491
|
};
|
|
8676
|
-
var useGroqMethod = async (prompt, components, apiKey,
|
|
8492
|
+
var useGroqMethod = async (prompt, components, apiKey, conversationHistory, responseMode = "component", streamCallback, collections, externalTools, userId) => {
|
|
8677
8493
|
logger.debug("[useGroqMethod] Initializing Groq LLM matching method");
|
|
8678
8494
|
logger.debug(`[useGroqMethod] Response mode: ${responseMode}`);
|
|
8679
|
-
const msg = `Using Groq LLM ${responseMode === "text" ? "text response" : "matching"} method...`;
|
|
8680
|
-
logger.info(msg);
|
|
8681
|
-
logCollector?.info(msg);
|
|
8682
8495
|
if (responseMode === "component" && components.length === 0) {
|
|
8683
8496
|
const emptyMsg = "Components not loaded in memory. Please ensure components are fetched first.";
|
|
8684
8497
|
logger.error("[useGroqMethod] No components available");
|
|
8685
|
-
logCollector?.error(emptyMsg);
|
|
8686
8498
|
return { success: false, errors: [emptyMsg] };
|
|
8687
8499
|
}
|
|
8688
8500
|
logger.debug(`[useGroqMethod] Processing with ${components.length} components`);
|
|
8689
|
-
const matchResult = await groqLLM.handleUserRequest(prompt, components, apiKey,
|
|
8501
|
+
const matchResult = await groqLLM.handleUserRequest(prompt, components, apiKey, conversationHistory, responseMode, streamCallback, collections, externalTools, userId);
|
|
8690
8502
|
logger.info(`[useGroqMethod] Successfully generated ${responseMode} using Groq`);
|
|
8691
8503
|
return matchResult;
|
|
8692
8504
|
};
|
|
8693
|
-
var useGeminiMethod = async (prompt, components, apiKey,
|
|
8505
|
+
var useGeminiMethod = async (prompt, components, apiKey, conversationHistory, responseMode = "component", streamCallback, collections, externalTools, userId) => {
|
|
8694
8506
|
logger.debug("[useGeminiMethod] Initializing Gemini LLM matching method");
|
|
8695
8507
|
logger.debug(`[useGeminiMethod] Response mode: ${responseMode}`);
|
|
8696
|
-
const msg = `Using Gemini LLM ${responseMode === "text" ? "text response" : "matching"} method...`;
|
|
8697
|
-
logger.info(msg);
|
|
8698
|
-
logCollector?.info(msg);
|
|
8699
8508
|
if (responseMode === "component" && components.length === 0) {
|
|
8700
8509
|
const emptyMsg = "Components not loaded in memory. Please ensure components are fetched first.";
|
|
8701
8510
|
logger.error("[useGeminiMethod] No components available");
|
|
8702
|
-
logCollector?.error(emptyMsg);
|
|
8703
8511
|
return { success: false, errors: [emptyMsg] };
|
|
8704
8512
|
}
|
|
8705
8513
|
logger.debug(`[useGeminiMethod] Processing with ${components.length} components`);
|
|
8706
|
-
const matchResult = await geminiLLM.handleUserRequest(prompt, components, apiKey,
|
|
8514
|
+
const matchResult = await geminiLLM.handleUserRequest(prompt, components, apiKey, conversationHistory, responseMode, streamCallback, collections, externalTools, userId);
|
|
8707
8515
|
logger.info(`[useGeminiMethod] Successfully generated ${responseMode} using Gemini`);
|
|
8708
8516
|
return matchResult;
|
|
8709
8517
|
};
|
|
8710
|
-
var useOpenAIMethod = async (prompt, components, apiKey,
|
|
8518
|
+
var useOpenAIMethod = async (prompt, components, apiKey, conversationHistory, responseMode = "component", streamCallback, collections, externalTools, userId) => {
|
|
8711
8519
|
logger.debug("[useOpenAIMethod] Initializing OpenAI GPT matching method");
|
|
8712
8520
|
logger.debug(`[useOpenAIMethod] Response mode: ${responseMode}`);
|
|
8713
|
-
const msg = `Using OpenAI GPT ${responseMode === "text" ? "text response" : "matching"} method...`;
|
|
8714
|
-
logger.info(msg);
|
|
8715
|
-
logCollector?.info(msg);
|
|
8716
8521
|
if (responseMode === "component" && components.length === 0) {
|
|
8717
8522
|
const emptyMsg = "Components not loaded in memory. Please ensure components are fetched first.";
|
|
8718
8523
|
logger.error("[useOpenAIMethod] No components available");
|
|
8719
|
-
logCollector?.error(emptyMsg);
|
|
8720
8524
|
return { success: false, errors: [emptyMsg] };
|
|
8721
8525
|
}
|
|
8722
8526
|
logger.debug(`[useOpenAIMethod] Processing with ${components.length} components`);
|
|
8723
|
-
const matchResult = await openaiLLM.handleUserRequest(prompt, components, apiKey,
|
|
8527
|
+
const matchResult = await openaiLLM.handleUserRequest(prompt, components, apiKey, conversationHistory, responseMode, streamCallback, collections, externalTools, userId);
|
|
8724
8528
|
logger.info(`[useOpenAIMethod] Successfully generated ${responseMode} using OpenAI`);
|
|
8725
8529
|
return matchResult;
|
|
8726
8530
|
};
|
|
8727
|
-
var
|
|
8728
|
-
return false;
|
|
8729
|
-
};
|
|
8730
|
-
var get_user_response = async (prompt, components, anthropicApiKey, groqApiKey, geminiApiKey, openaiApiKey, llmProviders, logCollector, conversationHistory, responseMode = "component", streamCallback, collections, externalTools, userId) => {
|
|
8731
|
-
logger.debug(`[get_user_response] Starting user response generation for prompt: "${prompt.substring(0, 50)}..."`);
|
|
8732
|
-
logger.debug(`[get_user_response] Response mode: ${responseMode}`);
|
|
8733
|
-
logger.debug("[get_user_response] Checking cache for existing response");
|
|
8734
|
-
const userResponse = await getUserResponseFromCache(prompt);
|
|
8735
|
-
if (userResponse) {
|
|
8736
|
-
logger.info("[get_user_response] User response found in cache - returning cached result");
|
|
8737
|
-
logCollector?.info("User response found in cache");
|
|
8738
|
-
return {
|
|
8739
|
-
success: true,
|
|
8740
|
-
data: userResponse,
|
|
8741
|
-
errors: []
|
|
8742
|
-
};
|
|
8743
|
-
}
|
|
8744
|
-
logger.debug("[get_user_response] No cached response found, proceeding with LLM providers");
|
|
8531
|
+
var get_user_response = async (prompt, components, anthropicApiKey, groqApiKey, geminiApiKey, openaiApiKey, llmProviders, conversationHistory, responseMode = "component", streamCallback, collections, externalTools, userId) => {
|
|
8745
8532
|
const providers = llmProviders || getLLMProviders();
|
|
8746
8533
|
const errors = [];
|
|
8747
|
-
|
|
8748
|
-
logCollector?.info(`LLM Provider order: [${providerOrder}]`);
|
|
8749
|
-
if (conversationHistory && conversationHistory.length > 0) {
|
|
8750
|
-
const exchangeCount = conversationHistory.split("\n").filter((l) => l.startsWith("Q")).length;
|
|
8751
|
-
logger.debug(`[get_user_response] Using conversation history with ${exchangeCount} previous exchanges`);
|
|
8752
|
-
logCollector?.info(`Using conversation history with ${exchangeCount} previous exchanges`);
|
|
8753
|
-
} else {
|
|
8754
|
-
logger.debug("[get_user_response] No conversation history available");
|
|
8755
|
-
}
|
|
8534
|
+
logger.info(`[get_user_response] LLM Provider order: [${providers.join(", ")}]`);
|
|
8756
8535
|
for (let i = 0; i < providers.length; i++) {
|
|
8757
8536
|
const provider = providers[i];
|
|
8758
8537
|
const isLastProvider = i === providers.length - 1;
|
|
8759
|
-
|
|
8760
|
-
logCollector?.info(attemptMsg);
|
|
8538
|
+
logger.info(`[get_user_response] Attempting provider: ${provider} (${i + 1}/${providers.length})`);
|
|
8761
8539
|
let result;
|
|
8762
8540
|
if (provider === "anthropic") {
|
|
8763
|
-
result = await useAnthropicMethod(prompt, components, anthropicApiKey,
|
|
8541
|
+
result = await useAnthropicMethod(prompt, components, anthropicApiKey, conversationHistory, responseMode, streamCallback, collections, externalTools, userId);
|
|
8764
8542
|
} else if (provider === "groq") {
|
|
8765
|
-
result = await useGroqMethod(prompt, components, groqApiKey,
|
|
8543
|
+
result = await useGroqMethod(prompt, components, groqApiKey, conversationHistory, responseMode, streamCallback, collections, externalTools, userId);
|
|
8766
8544
|
} else if (provider === "gemini") {
|
|
8767
|
-
result = await useGeminiMethod(prompt, components, geminiApiKey,
|
|
8545
|
+
result = await useGeminiMethod(prompt, components, geminiApiKey, conversationHistory, responseMode, streamCallback, collections, externalTools, userId);
|
|
8768
8546
|
} else if (provider === "openai") {
|
|
8769
|
-
result = await useOpenAIMethod(prompt, components, openaiApiKey,
|
|
8547
|
+
result = await useOpenAIMethod(prompt, components, openaiApiKey, conversationHistory, responseMode, streamCallback, collections, externalTools, userId);
|
|
8770
8548
|
} else {
|
|
8771
8549
|
logger.warn(`[get_user_response] Unknown provider: ${provider} - skipping`);
|
|
8772
8550
|
errors.push(`Unknown provider: ${provider}`);
|
|
8773
8551
|
continue;
|
|
8774
8552
|
}
|
|
8775
8553
|
if (result.success) {
|
|
8776
|
-
|
|
8777
|
-
logger.info(`${successMsg}`);
|
|
8778
|
-
logCollector?.info(successMsg);
|
|
8554
|
+
logger.info(`[get_user_response] Success with provider: ${provider}`);
|
|
8779
8555
|
return result;
|
|
8780
8556
|
} else {
|
|
8781
8557
|
const providerErrors = result.errors.map((err) => `${provider}: ${err}`);
|
|
8782
8558
|
errors.push(...providerErrors);
|
|
8783
|
-
|
|
8784
|
-
logger.warn(`[get_user_response] ${warnMsg}`);
|
|
8785
|
-
logCollector?.warn(warnMsg);
|
|
8559
|
+
logger.warn(`[get_user_response] Provider ${provider} returned unsuccessful result: ${result.errors.join(", ")}`);
|
|
8786
8560
|
if (!isLastProvider) {
|
|
8787
|
-
|
|
8788
|
-
logger.info(`[get_user_response] ${fallbackMsg}`);
|
|
8789
|
-
logCollector?.info(fallbackMsg);
|
|
8561
|
+
logger.info("[get_user_response] Falling back to next provider...");
|
|
8790
8562
|
}
|
|
8791
8563
|
}
|
|
8792
8564
|
}
|
|
8793
|
-
|
|
8794
|
-
logger.error(`[get_user_response] ${failureMsg}. Errors: ${errors.join("; ")}`);
|
|
8795
|
-
logCollector?.error(`${failureMsg}. Errors: ${errors.join("; ")}`);
|
|
8565
|
+
logger.error(`[get_user_response] All LLM providers failed. Errors: ${errors.join("; ")}`);
|
|
8796
8566
|
return {
|
|
8797
8567
|
success: false,
|
|
8798
8568
|
errors
|
|
8799
8569
|
};
|
|
8800
8570
|
};
|
|
8801
8571
|
|
|
8802
|
-
// src/utils/log-collector.ts
|
|
8803
|
-
var LOG_LEVEL_PRIORITY2 = {
|
|
8804
|
-
errors: 0,
|
|
8805
|
-
warnings: 1,
|
|
8806
|
-
info: 2,
|
|
8807
|
-
verbose: 3
|
|
8808
|
-
};
|
|
8809
|
-
var MESSAGE_LEVEL_PRIORITY2 = {
|
|
8810
|
-
error: 0,
|
|
8811
|
-
warn: 1,
|
|
8812
|
-
info: 2,
|
|
8813
|
-
debug: 3
|
|
8814
|
-
};
|
|
8815
|
-
var UILogCollector = class {
|
|
8816
|
-
constructor(clientId, sendMessage, uiBlockId) {
|
|
8817
|
-
this.logs = [];
|
|
8818
|
-
this.uiBlockId = uiBlockId || null;
|
|
8819
|
-
this.clientId = clientId;
|
|
8820
|
-
this.sendMessage = sendMessage;
|
|
8821
|
-
this.currentLogLevel = logger.getLogLevel();
|
|
8822
|
-
}
|
|
8823
|
-
/**
|
|
8824
|
-
* Check if logging is enabled (uiBlockId is provided)
|
|
8825
|
-
*/
|
|
8826
|
-
isEnabled() {
|
|
8827
|
-
return this.uiBlockId !== null;
|
|
8828
|
-
}
|
|
8829
|
-
/**
|
|
8830
|
-
* Check if a message should be logged based on current log level
|
|
8831
|
-
*/
|
|
8832
|
-
shouldLog(messageLevel) {
|
|
8833
|
-
const currentLevelPriority = LOG_LEVEL_PRIORITY2[this.currentLogLevel];
|
|
8834
|
-
const messagePriority = MESSAGE_LEVEL_PRIORITY2[messageLevel];
|
|
8835
|
-
return messagePriority <= currentLevelPriority;
|
|
8836
|
-
}
|
|
8837
|
-
/**
|
|
8838
|
-
* Add a log entry with timestamp and immediately send to runtime
|
|
8839
|
-
* Only logs that pass the log level filter are captured and sent
|
|
8840
|
-
*/
|
|
8841
|
-
addLog(level, message, type, data) {
|
|
8842
|
-
if (!this.shouldLog(level)) {
|
|
8843
|
-
return;
|
|
8844
|
-
}
|
|
8845
|
-
const log = {
|
|
8846
|
-
timestamp: Date.now(),
|
|
8847
|
-
level,
|
|
8848
|
-
message,
|
|
8849
|
-
...type && { type },
|
|
8850
|
-
...data && { data }
|
|
8851
|
-
};
|
|
8852
|
-
this.logs.push(log);
|
|
8853
|
-
this.sendLogImmediately(log);
|
|
8854
|
-
switch (level) {
|
|
8855
|
-
case "error":
|
|
8856
|
-
logger.error("UILogCollector:", log);
|
|
8857
|
-
break;
|
|
8858
|
-
case "warn":
|
|
8859
|
-
logger.warn("UILogCollector:", log);
|
|
8860
|
-
break;
|
|
8861
|
-
case "info":
|
|
8862
|
-
logger.info("UILogCollector:", log);
|
|
8863
|
-
break;
|
|
8864
|
-
case "debug":
|
|
8865
|
-
logger.debug("UILogCollector:", log);
|
|
8866
|
-
break;
|
|
8867
|
-
}
|
|
8868
|
-
}
|
|
8869
|
-
/**
|
|
8870
|
-
* Send a single log to runtime immediately
|
|
8871
|
-
*/
|
|
8872
|
-
sendLogImmediately(log) {
|
|
8873
|
-
if (!this.isEnabled()) {
|
|
8874
|
-
return;
|
|
8875
|
-
}
|
|
8876
|
-
const response = {
|
|
8877
|
-
id: this.uiBlockId,
|
|
8878
|
-
type: "UI_LOGS",
|
|
8879
|
-
from: { type: "data-agent" },
|
|
8880
|
-
to: {
|
|
8881
|
-
type: "runtime",
|
|
8882
|
-
id: this.clientId
|
|
8883
|
-
},
|
|
8884
|
-
payload: {
|
|
8885
|
-
logs: [log]
|
|
8886
|
-
// Send single log in array
|
|
8887
|
-
}
|
|
8888
|
-
};
|
|
8889
|
-
this.sendMessage(response);
|
|
8890
|
-
}
|
|
8891
|
-
/**
|
|
8892
|
-
* Log info message
|
|
8893
|
-
*/
|
|
8894
|
-
info(message, type, data) {
|
|
8895
|
-
if (this.isEnabled()) {
|
|
8896
|
-
this.addLog("info", message, type, data);
|
|
8897
|
-
}
|
|
8898
|
-
}
|
|
8899
|
-
/**
|
|
8900
|
-
* Log error message
|
|
8901
|
-
*/
|
|
8902
|
-
error(message, type, data) {
|
|
8903
|
-
if (this.isEnabled()) {
|
|
8904
|
-
this.addLog("error", message, type, data);
|
|
8905
|
-
}
|
|
8906
|
-
}
|
|
8907
|
-
/**
|
|
8908
|
-
* Log warning message
|
|
8909
|
-
*/
|
|
8910
|
-
warn(message, type, data) {
|
|
8911
|
-
if (this.isEnabled()) {
|
|
8912
|
-
this.addLog("warn", message, type, data);
|
|
8913
|
-
}
|
|
8914
|
-
}
|
|
8915
|
-
/**
|
|
8916
|
-
* Log debug message
|
|
8917
|
-
*/
|
|
8918
|
-
debug(message, type, data) {
|
|
8919
|
-
if (this.isEnabled()) {
|
|
8920
|
-
this.addLog("debug", message, type, data);
|
|
8921
|
-
}
|
|
8922
|
-
}
|
|
8923
|
-
/**
|
|
8924
|
-
* Log LLM explanation with typed metadata
|
|
8925
|
-
*/
|
|
8926
|
-
logExplanation(message, explanation, data) {
|
|
8927
|
-
if (this.isEnabled()) {
|
|
8928
|
-
this.addLog("info", message, "explanation", {
|
|
8929
|
-
explanation,
|
|
8930
|
-
...data
|
|
8931
|
-
});
|
|
8932
|
-
}
|
|
8933
|
-
}
|
|
8934
|
-
/**
|
|
8935
|
-
* Log generated query with typed metadata
|
|
8936
|
-
*/
|
|
8937
|
-
logQuery(message, query, data) {
|
|
8938
|
-
if (this.isEnabled()) {
|
|
8939
|
-
this.addLog("info", message, "query", {
|
|
8940
|
-
query,
|
|
8941
|
-
...data
|
|
8942
|
-
});
|
|
8943
|
-
}
|
|
8944
|
-
}
|
|
8945
|
-
/**
|
|
8946
|
-
* Send all collected logs at once (optional, for final summary)
|
|
8947
|
-
*/
|
|
8948
|
-
sendAllLogs() {
|
|
8949
|
-
if (!this.isEnabled() || this.logs.length === 0) {
|
|
8950
|
-
return;
|
|
8951
|
-
}
|
|
8952
|
-
const response = {
|
|
8953
|
-
id: this.uiBlockId,
|
|
8954
|
-
type: "UI_LOGS",
|
|
8955
|
-
from: { type: "data-agent" },
|
|
8956
|
-
to: {
|
|
8957
|
-
type: "runtime",
|
|
8958
|
-
id: this.clientId
|
|
8959
|
-
},
|
|
8960
|
-
payload: {
|
|
8961
|
-
logs: this.logs
|
|
8962
|
-
}
|
|
8963
|
-
};
|
|
8964
|
-
this.sendMessage(response);
|
|
8965
|
-
}
|
|
8966
|
-
/**
|
|
8967
|
-
* Get all collected logs
|
|
8968
|
-
*/
|
|
8969
|
-
getLogs() {
|
|
8970
|
-
return [...this.logs];
|
|
8971
|
-
}
|
|
8972
|
-
/**
|
|
8973
|
-
* Clear all logs
|
|
8974
|
-
*/
|
|
8975
|
-
clearLogs() {
|
|
8976
|
-
this.logs = [];
|
|
8977
|
-
}
|
|
8978
|
-
/**
|
|
8979
|
-
* Set uiBlockId (in case it's provided later)
|
|
8980
|
-
*/
|
|
8981
|
-
setUIBlockId(uiBlockId) {
|
|
8982
|
-
this.uiBlockId = uiBlockId;
|
|
8983
|
-
}
|
|
8984
|
-
};
|
|
8985
|
-
|
|
8986
8572
|
// src/utils/conversation-saver.ts
|
|
8987
8573
|
function transformUIBlockForDB(uiblock, userPrompt, uiBlockId) {
|
|
8988
8574
|
const component = uiblock?.generatedComponentMetadata && Object.keys(uiblock.generatedComponentMetadata).length > 0 ? uiblock.generatedComponentMetadata : null;
|
|
@@ -9113,7 +8699,6 @@ var CONTEXT_CONFIG = {
|
|
|
9113
8699
|
// src/handlers/user-prompt-request.ts
|
|
9114
8700
|
var get_user_request = async (data, components, sendMessage, anthropicApiKey, groqApiKey, geminiApiKey, openaiApiKey, llmProviders, collections, externalTools) => {
|
|
9115
8701
|
const errors = [];
|
|
9116
|
-
logger.debug("[USER_PROMPT_REQ] Parsing incoming message data");
|
|
9117
8702
|
const parseResult = UserPromptRequestMessageSchema.safeParse(data);
|
|
9118
8703
|
if (!parseResult.success) {
|
|
9119
8704
|
const zodError = parseResult.error;
|
|
@@ -9145,27 +8730,23 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
|
|
|
9145
8730
|
if (!prompt) {
|
|
9146
8731
|
errors.push("Prompt not found");
|
|
9147
8732
|
}
|
|
9148
|
-
logger.debug(`[REQUEST ${id}] Full request details - uiBlockId: ${existingUiBlockId}, threadId: ${threadId}, prompt: ${prompt}`);
|
|
9149
8733
|
if (errors.length > 0) {
|
|
9150
8734
|
return { success: false, errors, id, wsId };
|
|
9151
8735
|
}
|
|
9152
|
-
const logCollector = new UILogCollector(wsId, sendMessage, existingUiBlockId);
|
|
9153
8736
|
const threadManager = ThreadManager.getInstance();
|
|
9154
8737
|
let thread = threadManager.getThread(threadId);
|
|
9155
8738
|
if (!thread) {
|
|
9156
8739
|
thread = threadManager.createThread(threadId);
|
|
9157
8740
|
logger.info(`Created new thread: ${threadId}`);
|
|
9158
8741
|
}
|
|
9159
|
-
|
|
8742
|
+
logger.info(`Starting user prompt request with ${components.length} components`);
|
|
9160
8743
|
const conversationHistory = thread.getConversationContext(CONTEXT_CONFIG.MAX_CONVERSATION_CONTEXT_BLOCKS, existingUiBlockId);
|
|
9161
8744
|
const responseMode = payload.responseMode || "component";
|
|
9162
|
-
logger.info("responseMode", responseMode);
|
|
9163
8745
|
let streamCallback;
|
|
9164
8746
|
let accumulatedStreamResponse = "";
|
|
9165
8747
|
if (responseMode === "text") {
|
|
9166
8748
|
streamCallback = (chunk) => {
|
|
9167
8749
|
accumulatedStreamResponse += chunk;
|
|
9168
|
-
logger.debug(`[STREAM] Sending chunk (${chunk.length} chars): "${chunk.substring(0, 20)}..."`);
|
|
9169
8750
|
const streamMessage = {
|
|
9170
8751
|
id: `stream_${existingUiBlockId}`,
|
|
9171
8752
|
// Different ID pattern for streaming
|
|
@@ -9181,7 +8762,6 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
|
|
|
9181
8762
|
}
|
|
9182
8763
|
};
|
|
9183
8764
|
sendMessage(streamMessage);
|
|
9184
|
-
logger.debug(`[STREAM] Chunk sent to wsId: ${wsId}`);
|
|
9185
8765
|
};
|
|
9186
8766
|
}
|
|
9187
8767
|
const userResponse = await get_user_response(
|
|
@@ -9192,7 +8772,6 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
|
|
|
9192
8772
|
geminiApiKey,
|
|
9193
8773
|
openaiApiKey,
|
|
9194
8774
|
llmProviders,
|
|
9195
|
-
logCollector,
|
|
9196
8775
|
conversationHistory,
|
|
9197
8776
|
responseMode,
|
|
9198
8777
|
streamCallback,
|
|
@@ -9200,7 +8779,7 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
|
|
|
9200
8779
|
externalTools,
|
|
9201
8780
|
userId
|
|
9202
8781
|
);
|
|
9203
|
-
|
|
8782
|
+
logger.info("User prompt request completed");
|
|
9204
8783
|
const uiBlockId = existingUiBlockId;
|
|
9205
8784
|
if (!userResponse.success) {
|
|
9206
8785
|
logger.error(`User prompt request failed with errors: ${userResponse.errors.join(", ")}`);
|
|
@@ -9267,9 +8846,6 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
|
|
|
9267
8846
|
logger.info(
|
|
9268
8847
|
`Skipping conversation save - response from exact semantic match (${(semanticSimilarity * 100).toFixed(2)}% similarity)`
|
|
9269
8848
|
);
|
|
9270
|
-
logCollector.info(
|
|
9271
|
-
`Using exact cached result (${(semanticSimilarity * 100).toFixed(2)}% match) - not saving duplicate conversation`
|
|
9272
|
-
);
|
|
9273
8849
|
} else {
|
|
9274
8850
|
const uiBlockData = uiBlock.toJSON();
|
|
9275
8851
|
const saveResult = await saveConversation({
|
|
@@ -9477,7 +9053,7 @@ function sendResponse(id, res, sendMessage, clientId) {
|
|
|
9477
9053
|
}
|
|
9478
9054
|
|
|
9479
9055
|
// src/userResponse/next-questions.ts
|
|
9480
|
-
async function generateNextQuestions(originalUserPrompt, component, componentData, anthropicApiKey, groqApiKey, geminiApiKey, openaiApiKey, llmProviders,
|
|
9056
|
+
async function generateNextQuestions(originalUserPrompt, component, componentData, anthropicApiKey, groqApiKey, geminiApiKey, openaiApiKey, llmProviders, conversationHistory) {
|
|
9481
9057
|
try {
|
|
9482
9058
|
logger.debug("[generateNextQuestions] Starting next questions generation");
|
|
9483
9059
|
logger.debug(`[generateNextQuestions] User prompt: "${originalUserPrompt?.substring(0, 50)}..."`);
|
|
@@ -9496,7 +9072,6 @@ async function generateNextQuestions(originalUserPrompt, component, componentDat
|
|
|
9496
9072
|
const isLastProvider = i === providers.length - 1;
|
|
9497
9073
|
try {
|
|
9498
9074
|
logger.info(`[generateNextQuestions] Attempting provider: ${provider} (${i + 1}/${providers.length})`);
|
|
9499
|
-
logCollector?.info(`Generating questions with ${provider}...`);
|
|
9500
9075
|
let result = [];
|
|
9501
9076
|
if (provider === "groq") {
|
|
9502
9077
|
logger.debug("[generateNextQuestions] Using Groq LLM for next questions");
|
|
@@ -9505,7 +9080,6 @@ async function generateNextQuestions(originalUserPrompt, component, componentDat
|
|
|
9505
9080
|
component,
|
|
9506
9081
|
componentData,
|
|
9507
9082
|
groqApiKey,
|
|
9508
|
-
logCollector,
|
|
9509
9083
|
conversationHistory
|
|
9510
9084
|
);
|
|
9511
9085
|
} else if (provider === "gemini") {
|
|
@@ -9515,7 +9089,6 @@ async function generateNextQuestions(originalUserPrompt, component, componentDat
|
|
|
9515
9089
|
component,
|
|
9516
9090
|
componentData,
|
|
9517
9091
|
geminiApiKey,
|
|
9518
|
-
logCollector,
|
|
9519
9092
|
conversationHistory
|
|
9520
9093
|
);
|
|
9521
9094
|
} else if (provider === "openai") {
|
|
@@ -9525,7 +9098,6 @@ async function generateNextQuestions(originalUserPrompt, component, componentDat
|
|
|
9525
9098
|
component,
|
|
9526
9099
|
componentData,
|
|
9527
9100
|
openaiApiKey,
|
|
9528
|
-
logCollector,
|
|
9529
9101
|
conversationHistory
|
|
9530
9102
|
);
|
|
9531
9103
|
} else {
|
|
@@ -9535,44 +9107,32 @@ async function generateNextQuestions(originalUserPrompt, component, componentDat
|
|
|
9535
9107
|
component,
|
|
9536
9108
|
componentData,
|
|
9537
9109
|
anthropicApiKey,
|
|
9538
|
-
logCollector,
|
|
9539
9110
|
conversationHistory
|
|
9540
9111
|
);
|
|
9541
9112
|
}
|
|
9542
9113
|
if (result && result.length > 0) {
|
|
9543
9114
|
logger.info(`[generateNextQuestions] Successfully generated ${result.length} questions with ${provider}`);
|
|
9544
9115
|
logger.debug(`[generateNextQuestions] Questions: ${JSON.stringify(result)}`);
|
|
9545
|
-
logCollector?.info(`Generated ${result.length} follow-up questions`);
|
|
9546
9116
|
return result;
|
|
9547
9117
|
}
|
|
9548
|
-
|
|
9549
|
-
logger.warn(`[generateNextQuestions] ${warnMsg}`);
|
|
9550
|
-
if (!isLastProvider) {
|
|
9551
|
-
logCollector?.warn(warnMsg);
|
|
9552
|
-
}
|
|
9118
|
+
logger.warn(`[generateNextQuestions] No questions generated from ${provider}${!isLastProvider ? ", trying next provider..." : ""}`);
|
|
9553
9119
|
} catch (providerError) {
|
|
9554
9120
|
const errorMsg = providerError instanceof Error ? providerError.message : String(providerError);
|
|
9555
9121
|
logger.error(`[generateNextQuestions] Provider ${provider} failed: ${errorMsg}`);
|
|
9556
9122
|
logger.debug(`[generateNextQuestions] Provider error details:`, providerError);
|
|
9557
9123
|
if (!isLastProvider) {
|
|
9558
|
-
|
|
9559
|
-
logger.info(`[generateNextQuestions] ${fallbackMsg}`);
|
|
9560
|
-
logCollector?.warn(fallbackMsg);
|
|
9561
|
-
} else {
|
|
9562
|
-
logCollector?.error(`Failed to generate questions with ${provider}`);
|
|
9124
|
+
logger.info(`[generateNextQuestions] Provider ${provider} failed, trying next provider...`);
|
|
9563
9125
|
}
|
|
9564
9126
|
continue;
|
|
9565
9127
|
}
|
|
9566
9128
|
}
|
|
9567
9129
|
logger.warn("[generateNextQuestions] All providers failed or returned no questions");
|
|
9568
|
-
logCollector?.warn("Unable to generate follow-up questions");
|
|
9569
9130
|
return [];
|
|
9570
9131
|
} catch (error) {
|
|
9571
9132
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
9572
9133
|
const errorStack = error instanceof Error ? error.stack : void 0;
|
|
9573
9134
|
logger.error(`[generateNextQuestions] Error generating next questions: ${errorMsg}`);
|
|
9574
9135
|
logger.debug("[generateNextQuestions] Error stack trace:", errorStack);
|
|
9575
|
-
logCollector?.error(`Error generating next questions: ${errorMsg}`);
|
|
9576
9136
|
return [];
|
|
9577
9137
|
}
|
|
9578
9138
|
}
|
|
@@ -9620,9 +9180,6 @@ async function handleActionsRequest(data, sendMessage, anthropicApiKey, groqApiK
|
|
|
9620
9180
|
return;
|
|
9621
9181
|
}
|
|
9622
9182
|
logger.info(`[ACTIONS_REQ ${id}] UIBlock retrieved successfully`);
|
|
9623
|
-
logger.debug(`[ACTIONS_REQ ${id}] Creating UILogCollector for uiBlockId: ${uiBlockId}`);
|
|
9624
|
-
const logCollector = new UILogCollector(wsId, sendMessage, uiBlockId);
|
|
9625
|
-
logger.info(`[ACTIONS_REQ ${id}] UILogCollector initialized`);
|
|
9626
9183
|
logger.debug(`[ACTIONS_REQ ${id}] Extracting data from UIBlock`);
|
|
9627
9184
|
const userQuestion = uiBlock.getUserQuestion();
|
|
9628
9185
|
const component = uiBlock.getComponentMetadata();
|
|
@@ -9636,13 +9193,11 @@ async function handleActionsRequest(data, sendMessage, anthropicApiKey, groqApiK
|
|
|
9636
9193
|
logger.info(`[ACTIONS_REQ ${id}] Conversation history extracted: ${historyLineCount} lines`);
|
|
9637
9194
|
logger.debug(`[ACTIONS_REQ ${id}] Conversation history preview:
|
|
9638
9195
|
${conversationHistory.substring(0, 200)}...`);
|
|
9639
|
-
|
|
9640
|
-
logger.info(`[ACTIONS_REQ ${id}] Generating actions for component: ${component?.name || "unknown"}`);
|
|
9196
|
+
logger.info(`[ACTIONS_REQ ${id}] Generating actions for UIBlock: ${uiBlockId}, component: ${component?.name || "unknown"}`);
|
|
9641
9197
|
logger.debug(`[ACTIONS_REQ ${id}] Checking if actions are already cached`);
|
|
9642
9198
|
const startTime = Date.now();
|
|
9643
9199
|
const actions = await uiBlock.getOrFetchActions(async () => {
|
|
9644
9200
|
logger.info(`[ACTIONS_REQ ${id}] Actions not cached, generating new actions...`);
|
|
9645
|
-
logCollector.info("Generating follow-up questions...");
|
|
9646
9201
|
logger.info(`[ACTIONS_REQ ${id}] Starting next questions generation with ${llmProviders?.join(", ") || "default"} providers`);
|
|
9647
9202
|
const nextQuestions = await generateNextQuestions(
|
|
9648
9203
|
userQuestion,
|
|
@@ -9653,7 +9208,6 @@ ${conversationHistory.substring(0, 200)}...`);
|
|
|
9653
9208
|
geminiApiKey,
|
|
9654
9209
|
openaiApiKey,
|
|
9655
9210
|
llmProviders,
|
|
9656
|
-
logCollector,
|
|
9657
9211
|
conversationHistory
|
|
9658
9212
|
);
|
|
9659
9213
|
logger.info(`[ACTIONS_REQ ${id}] Generated ${nextQuestions.length} questions`);
|
|
@@ -9671,11 +9225,10 @@ ${conversationHistory.substring(0, 200)}...`);
|
|
|
9671
9225
|
const processingTime = Date.now() - startTime;
|
|
9672
9226
|
logger.info(`[ACTIONS_REQ ${id}] Actions retrieved in ${processingTime}ms - ${actions.length} actions total`);
|
|
9673
9227
|
if (actions.length > 0) {
|
|
9674
|
-
|
|
9228
|
+
logger.info(`[ACTIONS_REQ ${id}] Generated ${actions.length} follow-up questions successfully`);
|
|
9675
9229
|
logger.debug(`[ACTIONS_REQ ${id}] Actions: ${actions.map((a) => a.name).join(", ")}`);
|
|
9676
9230
|
} else {
|
|
9677
9231
|
logger.warn(`[ACTIONS_REQ ${id}] No actions generated`);
|
|
9678
|
-
logCollector.warn("No follow-up questions could be generated");
|
|
9679
9232
|
}
|
|
9680
9233
|
logger.debug(`[ACTIONS_REQ ${id}] Sending successful response to client`);
|
|
9681
9234
|
sendResponse2(id, {
|
|
@@ -9694,15 +9247,6 @@ ${conversationHistory.substring(0, 200)}...`);
|
|
|
9694
9247
|
const errorStack = error instanceof Error ? error.stack : void 0;
|
|
9695
9248
|
logger.error(`[ACTIONS_REQ] Failed to handle actions request: ${errorMessage}`);
|
|
9696
9249
|
logger.debug(`[ACTIONS_REQ] Error stack trace:`, errorStack);
|
|
9697
|
-
try {
|
|
9698
|
-
const parsedData = data;
|
|
9699
|
-
if (parsedData?.id && parsedData?.from?.id) {
|
|
9700
|
-
const logCollector = parsedData?.payload?.SA_RUNTIME?.uiBlockId ? new UILogCollector(parsedData.from.id, sendMessage, parsedData.payload.SA_RUNTIME.uiBlockId) : void 0;
|
|
9701
|
-
logCollector?.error(`Failed to generate actions: ${errorMessage}`);
|
|
9702
|
-
}
|
|
9703
|
-
} catch (logError) {
|
|
9704
|
-
logger.debug("[ACTIONS_REQ] Failed to send error logs to UI:", logError);
|
|
9705
|
-
}
|
|
9706
9250
|
sendResponse2(null, {
|
|
9707
9251
|
success: false,
|
|
9708
9252
|
error: errorMessage
|
|
@@ -10283,7 +9827,6 @@ function sendResponse3(id, res, sendMessage, clientId) {
|
|
|
10283
9827
|
var dashboardManager = null;
|
|
10284
9828
|
function setDashboardManager(manager) {
|
|
10285
9829
|
dashboardManager = manager;
|
|
10286
|
-
logger.info("DashboardManager instance set");
|
|
10287
9830
|
}
|
|
10288
9831
|
function getDashboardManager() {
|
|
10289
9832
|
if (!dashboardManager) {
|
|
@@ -13610,6 +13153,190 @@ var ReportManager = class {
|
|
|
13610
13153
|
}
|
|
13611
13154
|
};
|
|
13612
13155
|
|
|
13156
|
+
// src/utils/log-collector.ts
|
|
13157
|
+
var LOG_LEVEL_PRIORITY2 = {
|
|
13158
|
+
errors: 0,
|
|
13159
|
+
warnings: 1,
|
|
13160
|
+
info: 2,
|
|
13161
|
+
verbose: 3
|
|
13162
|
+
};
|
|
13163
|
+
var MESSAGE_LEVEL_PRIORITY2 = {
|
|
13164
|
+
error: 0,
|
|
13165
|
+
warn: 1,
|
|
13166
|
+
info: 2,
|
|
13167
|
+
debug: 3
|
|
13168
|
+
};
|
|
13169
|
+
var UILogCollector = class {
|
|
13170
|
+
constructor(clientId, sendMessage, uiBlockId) {
|
|
13171
|
+
this.logs = [];
|
|
13172
|
+
this.uiBlockId = uiBlockId || null;
|
|
13173
|
+
this.clientId = clientId;
|
|
13174
|
+
this.sendMessage = sendMessage;
|
|
13175
|
+
this.currentLogLevel = logger.getLogLevel();
|
|
13176
|
+
}
|
|
13177
|
+
/**
|
|
13178
|
+
* Check if logging is enabled (uiBlockId is provided)
|
|
13179
|
+
*/
|
|
13180
|
+
isEnabled() {
|
|
13181
|
+
return this.uiBlockId !== null;
|
|
13182
|
+
}
|
|
13183
|
+
/**
|
|
13184
|
+
* Check if a message should be logged based on current log level
|
|
13185
|
+
*/
|
|
13186
|
+
shouldLog(messageLevel) {
|
|
13187
|
+
const currentLevelPriority = LOG_LEVEL_PRIORITY2[this.currentLogLevel];
|
|
13188
|
+
const messagePriority = MESSAGE_LEVEL_PRIORITY2[messageLevel];
|
|
13189
|
+
return messagePriority <= currentLevelPriority;
|
|
13190
|
+
}
|
|
13191
|
+
/**
|
|
13192
|
+
* Add a log entry with timestamp and immediately send to runtime
|
|
13193
|
+
* Only logs that pass the log level filter are captured and sent
|
|
13194
|
+
*/
|
|
13195
|
+
addLog(level, message, type, data) {
|
|
13196
|
+
if (!this.shouldLog(level)) {
|
|
13197
|
+
return;
|
|
13198
|
+
}
|
|
13199
|
+
const log = {
|
|
13200
|
+
timestamp: Date.now(),
|
|
13201
|
+
level,
|
|
13202
|
+
message,
|
|
13203
|
+
...type && { type },
|
|
13204
|
+
...data && { data }
|
|
13205
|
+
};
|
|
13206
|
+
this.logs.push(log);
|
|
13207
|
+
this.sendLogImmediately(log);
|
|
13208
|
+
switch (level) {
|
|
13209
|
+
case "error":
|
|
13210
|
+
logger.error("UILogCollector:", log);
|
|
13211
|
+
break;
|
|
13212
|
+
case "warn":
|
|
13213
|
+
logger.warn("UILogCollector:", log);
|
|
13214
|
+
break;
|
|
13215
|
+
case "info":
|
|
13216
|
+
logger.info("UILogCollector:", log);
|
|
13217
|
+
break;
|
|
13218
|
+
case "debug":
|
|
13219
|
+
logger.debug("UILogCollector:", log);
|
|
13220
|
+
break;
|
|
13221
|
+
}
|
|
13222
|
+
}
|
|
13223
|
+
/**
|
|
13224
|
+
* Send a single log to runtime immediately
|
|
13225
|
+
*/
|
|
13226
|
+
sendLogImmediately(log) {
|
|
13227
|
+
if (!this.isEnabled()) {
|
|
13228
|
+
return;
|
|
13229
|
+
}
|
|
13230
|
+
const response = {
|
|
13231
|
+
id: this.uiBlockId,
|
|
13232
|
+
type: "UI_LOGS",
|
|
13233
|
+
from: { type: "data-agent" },
|
|
13234
|
+
to: {
|
|
13235
|
+
type: "runtime",
|
|
13236
|
+
id: this.clientId
|
|
13237
|
+
},
|
|
13238
|
+
payload: {
|
|
13239
|
+
logs: [log]
|
|
13240
|
+
// Send single log in array
|
|
13241
|
+
}
|
|
13242
|
+
};
|
|
13243
|
+
this.sendMessage(response);
|
|
13244
|
+
}
|
|
13245
|
+
/**
|
|
13246
|
+
* Log info message
|
|
13247
|
+
*/
|
|
13248
|
+
info(message, type, data) {
|
|
13249
|
+
if (this.isEnabled()) {
|
|
13250
|
+
this.addLog("info", message, type, data);
|
|
13251
|
+
}
|
|
13252
|
+
}
|
|
13253
|
+
/**
|
|
13254
|
+
* Log error message
|
|
13255
|
+
*/
|
|
13256
|
+
error(message, type, data) {
|
|
13257
|
+
if (this.isEnabled()) {
|
|
13258
|
+
this.addLog("error", message, type, data);
|
|
13259
|
+
}
|
|
13260
|
+
}
|
|
13261
|
+
/**
|
|
13262
|
+
* Log warning message
|
|
13263
|
+
*/
|
|
13264
|
+
warn(message, type, data) {
|
|
13265
|
+
if (this.isEnabled()) {
|
|
13266
|
+
this.addLog("warn", message, type, data);
|
|
13267
|
+
}
|
|
13268
|
+
}
|
|
13269
|
+
/**
|
|
13270
|
+
* Log debug message
|
|
13271
|
+
*/
|
|
13272
|
+
debug(message, type, data) {
|
|
13273
|
+
if (this.isEnabled()) {
|
|
13274
|
+
this.addLog("debug", message, type, data);
|
|
13275
|
+
}
|
|
13276
|
+
}
|
|
13277
|
+
/**
|
|
13278
|
+
* Log LLM explanation with typed metadata
|
|
13279
|
+
*/
|
|
13280
|
+
logExplanation(message, explanation, data) {
|
|
13281
|
+
if (this.isEnabled()) {
|
|
13282
|
+
this.addLog("info", message, "explanation", {
|
|
13283
|
+
explanation,
|
|
13284
|
+
...data
|
|
13285
|
+
});
|
|
13286
|
+
}
|
|
13287
|
+
}
|
|
13288
|
+
/**
|
|
13289
|
+
* Log generated query with typed metadata
|
|
13290
|
+
*/
|
|
13291
|
+
logQuery(message, query, data) {
|
|
13292
|
+
if (this.isEnabled()) {
|
|
13293
|
+
this.addLog("info", message, "query", {
|
|
13294
|
+
query,
|
|
13295
|
+
...data
|
|
13296
|
+
});
|
|
13297
|
+
}
|
|
13298
|
+
}
|
|
13299
|
+
/**
|
|
13300
|
+
* Send all collected logs at once (optional, for final summary)
|
|
13301
|
+
*/
|
|
13302
|
+
sendAllLogs() {
|
|
13303
|
+
if (!this.isEnabled() || this.logs.length === 0) {
|
|
13304
|
+
return;
|
|
13305
|
+
}
|
|
13306
|
+
const response = {
|
|
13307
|
+
id: this.uiBlockId,
|
|
13308
|
+
type: "UI_LOGS",
|
|
13309
|
+
from: { type: "data-agent" },
|
|
13310
|
+
to: {
|
|
13311
|
+
type: "runtime",
|
|
13312
|
+
id: this.clientId
|
|
13313
|
+
},
|
|
13314
|
+
payload: {
|
|
13315
|
+
logs: this.logs
|
|
13316
|
+
}
|
|
13317
|
+
};
|
|
13318
|
+
this.sendMessage(response);
|
|
13319
|
+
}
|
|
13320
|
+
/**
|
|
13321
|
+
* Get all collected logs
|
|
13322
|
+
*/
|
|
13323
|
+
getLogs() {
|
|
13324
|
+
return [...this.logs];
|
|
13325
|
+
}
|
|
13326
|
+
/**
|
|
13327
|
+
* Clear all logs
|
|
13328
|
+
*/
|
|
13329
|
+
clearLogs() {
|
|
13330
|
+
this.logs = [];
|
|
13331
|
+
}
|
|
13332
|
+
/**
|
|
13333
|
+
* Set uiBlockId (in case it's provided later)
|
|
13334
|
+
*/
|
|
13335
|
+
setUIBlockId(uiBlockId) {
|
|
13336
|
+
this.uiBlockId = uiBlockId;
|
|
13337
|
+
}
|
|
13338
|
+
};
|
|
13339
|
+
|
|
13613
13340
|
// src/services/cleanup-service.ts
|
|
13614
13341
|
var CleanupService = class _CleanupService {
|
|
13615
13342
|
constructor() {
|
|
@@ -13790,7 +13517,6 @@ var CleanupService = class _CleanupService {
|
|
|
13790
13517
|
};
|
|
13791
13518
|
|
|
13792
13519
|
// src/index.ts
|
|
13793
|
-
var SDK_VERSION = "0.0.8";
|
|
13794
13520
|
var DEFAULT_WS_URL = "wss://ws.superatom.ai/websocket";
|
|
13795
13521
|
var SuperatomSDK = class {
|
|
13796
13522
|
// 3.5 minutes (PING_INTERVAL + 30s grace)
|
|
@@ -13831,7 +13557,7 @@ var SuperatomSDK = class {
|
|
|
13831
13557
|
if (config.queryCacheTTL !== void 0) {
|
|
13832
13558
|
queryCache.setTTL(config.queryCacheTTL);
|
|
13833
13559
|
}
|
|
13834
|
-
logger.info(`Initializing Superatom SDK
|
|
13560
|
+
logger.info(`Initializing Superatom SDK for project ${this.projectId}, llm providers: ${this.llmProviders.join(", ")}, database type: ${this.databaseType}, model strategy: ${this.modelStrategy}, query cache TTL: ${queryCache.getTTL()} minutes`);
|
|
13835
13561
|
this.userManager = new UserManager(this.projectId, 5e3);
|
|
13836
13562
|
this.dashboardManager = new DashboardManager(this.projectId);
|
|
13837
13563
|
this.reportManager = new ReportManager(this.projectId);
|
|
@@ -13885,7 +13611,6 @@ var SuperatomSDK = class {
|
|
|
13885
13611
|
*/
|
|
13886
13612
|
initializeDashboardManager() {
|
|
13887
13613
|
setDashboardManager(this.dashboardManager);
|
|
13888
|
-
logger.info(`DashboardManager initialized for project: ${this.projectId}`);
|
|
13889
13614
|
}
|
|
13890
13615
|
/**
|
|
13891
13616
|
* Get the DashboardManager instance for this SDK
|
|
@@ -13898,7 +13623,6 @@ var SuperatomSDK = class {
|
|
|
13898
13623
|
*/
|
|
13899
13624
|
initializeReportManager() {
|
|
13900
13625
|
setReportManager(this.reportManager);
|
|
13901
|
-
logger.info(`ReportManager initialized for project: ${this.projectId}`);
|
|
13902
13626
|
}
|
|
13903
13627
|
/**
|
|
13904
13628
|
* Get the ReportManager instance for this SDK
|
|
@@ -14276,7 +14000,6 @@ var SuperatomSDK = class {
|
|
|
14276
14000
|
CONTEXT_CONFIG,
|
|
14277
14001
|
CleanupService,
|
|
14278
14002
|
LLM,
|
|
14279
|
-
SDK_VERSION,
|
|
14280
14003
|
STORAGE_CONFIG,
|
|
14281
14004
|
SuperatomSDK,
|
|
14282
14005
|
Thread,
|