@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.mjs
CHANGED
|
@@ -3310,24 +3310,20 @@ If adaptation is not possible or would fundamentally change the component:
|
|
|
3310
3310
|
"dash-comp-picker": {
|
|
3311
3311
|
system: `You are a component selection expert that picks the best dashboard component and generates complete props based on user requests.
|
|
3312
3312
|
|
|
3313
|
+
## CRITICAL - READ FIRST
|
|
3314
|
+
|
|
3315
|
+
1. Your ENTIRE response must be ONLY a raw JSON object - start with { end with }
|
|
3316
|
+
2. DO NOT explain or answer the user's question in natural language
|
|
3317
|
+
3. DO NOT use markdown code blocks (no \`\`\`)
|
|
3318
|
+
4. DO NOT add any text before or after the JSON
|
|
3319
|
+
5. After executing tools (if needed), return JSON with component selection - NOT a text summary of results
|
|
3320
|
+
|
|
3313
3321
|
## Your Task
|
|
3314
3322
|
|
|
3315
3323
|
Analyze the user's request and:
|
|
3316
3324
|
1. **Select the most appropriate component** from the available components list
|
|
3317
|
-
2. **Determine the data source**: Database query OR External tool
|
|
3318
|
-
3. **Generate complete props** for the selected component
|
|
3319
|
-
|
|
3320
|
-
## Available External Tools
|
|
3321
|
-
|
|
3322
|
-
The following external tools are available:
|
|
3323
|
-
|
|
3324
|
-
{{AVAILABLE_TOOLS}}
|
|
3325
|
-
|
|
3326
|
-
When a tool is needed to complete the user's request:
|
|
3327
|
-
1. **Analyze the request** to determine which tool is needed
|
|
3328
|
-
2. **Extract parameters** from the user's question
|
|
3329
|
-
3. **Execute the tool** by calling it with the extracted parameters
|
|
3330
|
-
4. **Use the results** to configure the component (field names for axes, columns, etc.)
|
|
3325
|
+
2. **Determine the data source**: Database query OR External tool (ERP)
|
|
3326
|
+
3. **Generate complete props** for the selected component including the data retrieval/modification method
|
|
3331
3327
|
|
|
3332
3328
|
## Component Selection Rules
|
|
3333
3329
|
|
|
@@ -3357,7 +3353,7 @@ The user prompt may contain an **existing component** to update. Detect this by
|
|
|
3357
3353
|
|
|
3358
3354
|
### Use DATABASE when:
|
|
3359
3355
|
- User asks about data that exists in the database schema
|
|
3360
|
-
- Questions about internal business data
|
|
3356
|
+
- Questions about internal business data
|
|
3361
3357
|
- CRUD operations on database tables
|
|
3362
3358
|
|
|
3363
3359
|
### Use EXTERNAL TOOL when:
|
|
@@ -3370,6 +3366,12 @@ The user prompt may contain an **existing component** to update. Detect this by
|
|
|
3370
3366
|
|
|
3371
3367
|
**CRITICAL**: Look at each component's "Props Structure" in the available components list. Generate ALL props that the component expects.
|
|
3372
3368
|
|
|
3369
|
+
**CRITICAL: Each component uses EXACTLY ONE data source - never both!**
|
|
3370
|
+
- If using \`query\`, set \`externalTool: null\`
|
|
3371
|
+
- If using \`externalTool\`, set \`query: null\`
|
|
3372
|
+
- NEVER copy placeholder/description text from component metadata as actual values
|
|
3373
|
+
- \`externalTool.parameters\` MUST be an object, never a string
|
|
3374
|
+
|
|
3373
3375
|
### For Data Viewing Components (charts, tables, KPIs):
|
|
3374
3376
|
|
|
3375
3377
|
**Option A: Database Query** (when data is in database)
|
|
@@ -3378,21 +3380,19 @@ The user prompt may contain an **existing component** to update. Detect this by
|
|
|
3378
3380
|
"query": {
|
|
3379
3381
|
"sql": "SELECT column1, column2 FROM table WHERE condition = $param LIMIT 32",
|
|
3380
3382
|
"params": { "param": "value" }
|
|
3381
|
-
}
|
|
3383
|
+
},
|
|
3384
|
+
"externalTool": null
|
|
3382
3385
|
}
|
|
3383
3386
|
\`\`\`
|
|
3384
3387
|
|
|
3385
3388
|
**Option B: External Tool** (when data is from ERP/external system)
|
|
3386
3389
|
\`\`\`json
|
|
3387
3390
|
{
|
|
3391
|
+
"query": null,
|
|
3388
3392
|
"externalTool": {
|
|
3389
3393
|
"toolId": "tool_id_from_list",
|
|
3390
3394
|
"toolName": "Tool Display Name",
|
|
3391
|
-
"
|
|
3392
|
-
"params": {
|
|
3393
|
-
"param1": "value1",
|
|
3394
|
-
"param2": "value2"
|
|
3395
|
-
}
|
|
3395
|
+
"parameters": { "param1": "value1", "param2": "value2" }
|
|
3396
3396
|
}
|
|
3397
3397
|
}
|
|
3398
3398
|
\`\`\`
|
|
@@ -3406,6 +3406,7 @@ The user prompt may contain an **existing component** to update. Detect this by
|
|
|
3406
3406
|
"sql": "INSERT INTO table (col1, col2) VALUES ($col1, $col2)",
|
|
3407
3407
|
"params": {}
|
|
3408
3408
|
},
|
|
3409
|
+
"externalTool": null,
|
|
3409
3410
|
"fields": [
|
|
3410
3411
|
{ "name": "col1", "type": "text", "required": true },
|
|
3411
3412
|
{ "name": "col2", "type": "number", "required": false }
|
|
@@ -3413,16 +3414,38 @@ The user prompt may contain an **existing component** to update. Detect this by
|
|
|
3413
3414
|
}
|
|
3414
3415
|
\`\`\`
|
|
3415
3416
|
|
|
3417
|
+
For UPDATE:
|
|
3418
|
+
\`\`\`json
|
|
3419
|
+
{
|
|
3420
|
+
"query": {
|
|
3421
|
+
"sql": "UPDATE table SET col1 = $col1, col2 = $col2 WHERE id = $id",
|
|
3422
|
+
"params": { "id": "record_id" }
|
|
3423
|
+
},
|
|
3424
|
+
"externalTool": null
|
|
3425
|
+
}
|
|
3426
|
+
\`\`\`
|
|
3427
|
+
|
|
3428
|
+
For DELETE:
|
|
3429
|
+
\`\`\`json
|
|
3430
|
+
{
|
|
3431
|
+
"query": {
|
|
3432
|
+
"sql": "DELETE FROM table WHERE id = $id",
|
|
3433
|
+
"params": { "id": "record_id" }
|
|
3434
|
+
},
|
|
3435
|
+
"externalTool": null,
|
|
3436
|
+
"submitButtonText": "Confirm Delete",
|
|
3437
|
+
"submitButtonColor": "danger"
|
|
3438
|
+
}
|
|
3439
|
+
\`\`\`
|
|
3440
|
+
|
|
3416
3441
|
**Option B: External Tool Mutation**
|
|
3417
3442
|
\`\`\`json
|
|
3418
3443
|
{
|
|
3444
|
+
"query": null,
|
|
3419
3445
|
"externalTool": {
|
|
3420
3446
|
"toolId": "tool_id_from_list",
|
|
3421
3447
|
"toolName": "Tool Display Name",
|
|
3422
|
-
"
|
|
3423
|
-
"params": {
|
|
3424
|
-
"param1": "value_or_placeholder"
|
|
3425
|
-
}
|
|
3448
|
+
"parameters": { "param1": "value_or_placeholder" }
|
|
3426
3449
|
},
|
|
3427
3450
|
"fields": [
|
|
3428
3451
|
{ "name": "param1", "type": "text", "required": true }
|
|
@@ -3437,6 +3460,7 @@ The user prompt may contain an **existing component** to update. Detect this by
|
|
|
3437
3460
|
|
|
3438
3461
|
You MUST respond with ONLY a valid JSON object (no markdown, no code blocks):
|
|
3439
3462
|
|
|
3463
|
+
\`\`\`json
|
|
3440
3464
|
{
|
|
3441
3465
|
"componentId": "id_from_available_list_or_existing_component_id",
|
|
3442
3466
|
"componentName": "name_of_component",
|
|
@@ -3451,6 +3475,7 @@ You MUST respond with ONLY a valid JSON object (no markdown, no code blocks):
|
|
|
3451
3475
|
// Include all other required props (title, description, config, fields, etc.)
|
|
3452
3476
|
}
|
|
3453
3477
|
}
|
|
3478
|
+
\`\`\`
|
|
3454
3479
|
|
|
3455
3480
|
**CRITICAL:**
|
|
3456
3481
|
- Return ONLY valid JSON (no markdown code blocks, no text before/after)
|
|
@@ -3473,7 +3498,8 @@ You MUST respond with ONLY a valid JSON object (no markdown, no code blocks):
|
|
|
3473
3498
|
|
|
3474
3499
|
---
|
|
3475
3500
|
|
|
3476
|
-
## CONTEXT
|
|
3501
|
+
## CONTEXT
|
|
3502
|
+
`,
|
|
3477
3503
|
user: `{{USER_PROMPT}}`
|
|
3478
3504
|
},
|
|
3479
3505
|
"dash-filter-picker": {
|
|
@@ -3619,9 +3645,7 @@ var PromptLoader = class {
|
|
|
3619
3645
|
this.databaseRulesCache = /* @__PURE__ */ new Map();
|
|
3620
3646
|
this.isInitialized = false;
|
|
3621
3647
|
this.databaseType = "postgresql";
|
|
3622
|
-
logger.debug("Initializing PromptLoader...");
|
|
3623
3648
|
this.promptsDir = config?.promptsDir || path2.join(process.cwd(), ".prompts");
|
|
3624
|
-
logger.debug(`Prompts directory set to: ${this.promptsDir}`);
|
|
3625
3649
|
}
|
|
3626
3650
|
/**
|
|
3627
3651
|
* Load a prompt template from file system OR fallback to hardcoded prompts
|
|
@@ -3635,7 +3659,6 @@ var PromptLoader = class {
|
|
|
3635
3659
|
if (fs3.existsSync(systemPath) && fs3.existsSync(userPath)) {
|
|
3636
3660
|
const system = fs3.readFileSync(systemPath, "utf-8");
|
|
3637
3661
|
const user = fs3.readFileSync(userPath, "utf-8");
|
|
3638
|
-
logger.info(`\u2713 Loaded prompt '${promptName}' from file system: ${this.promptsDir}`);
|
|
3639
3662
|
return { system, user };
|
|
3640
3663
|
}
|
|
3641
3664
|
} catch (error) {
|
|
@@ -3643,7 +3666,6 @@ var PromptLoader = class {
|
|
|
3643
3666
|
}
|
|
3644
3667
|
const hardcodedPrompt = PROMPTS[promptName];
|
|
3645
3668
|
if (hardcodedPrompt) {
|
|
3646
|
-
logger.info(`\u2713 Loaded prompt '${promptName}' from hardcoded fallback`);
|
|
3647
3669
|
return hardcodedPrompt;
|
|
3648
3670
|
}
|
|
3649
3671
|
throw new Error(`Prompt template '${promptName}' not found in either ${this.promptsDir} or hardcoded prompts. Available prompts: ${Object.keys(PROMPTS).join(", ")}`);
|
|
@@ -3657,7 +3679,6 @@ var PromptLoader = class {
|
|
|
3657
3679
|
logger.debug("PromptLoader already initialized, skipping...");
|
|
3658
3680
|
return;
|
|
3659
3681
|
}
|
|
3660
|
-
logger.info("Loading prompts into memory...");
|
|
3661
3682
|
const promptTypes = Object.keys(PROMPTS);
|
|
3662
3683
|
for (const promptName of promptTypes) {
|
|
3663
3684
|
try {
|
|
@@ -3669,7 +3690,6 @@ var PromptLoader = class {
|
|
|
3669
3690
|
}
|
|
3670
3691
|
}
|
|
3671
3692
|
this.isInitialized = true;
|
|
3672
|
-
logger.info(`Successfully loaded ${this.promptCache.size} prompt templates into memory`);
|
|
3673
3693
|
}
|
|
3674
3694
|
/**
|
|
3675
3695
|
* Replace variables in a template string using {{VARIABLE_NAME}} pattern
|
|
@@ -3709,7 +3729,6 @@ var PromptLoader = class {
|
|
|
3709
3729
|
const processedContext = this.replaceVariables(contextMarker + contextPart, variables);
|
|
3710
3730
|
const staticLength = processedStatic.length;
|
|
3711
3731
|
const contextLength = processedContext.length;
|
|
3712
|
-
logger.debug(`\u2713 Prompt caching enabled for '${promptName}' (cached: ${staticLength} chars, dynamic: ${contextLength} chars)`);
|
|
3713
3732
|
return {
|
|
3714
3733
|
system: [
|
|
3715
3734
|
{
|
|
@@ -3746,7 +3765,6 @@ var PromptLoader = class {
|
|
|
3746
3765
|
this.promptsDir = dir;
|
|
3747
3766
|
this.isInitialized = false;
|
|
3748
3767
|
this.promptCache.clear();
|
|
3749
|
-
logger.debug(`Prompts directory changed to: ${dir}`);
|
|
3750
3768
|
}
|
|
3751
3769
|
/**
|
|
3752
3770
|
* Get current prompts directory
|
|
@@ -3774,7 +3792,6 @@ var PromptLoader = class {
|
|
|
3774
3792
|
setDatabaseType(type) {
|
|
3775
3793
|
this.databaseType = type;
|
|
3776
3794
|
this.databaseRulesCache.clear();
|
|
3777
|
-
logger.debug(`Database type set to: ${type}`);
|
|
3778
3795
|
}
|
|
3779
3796
|
/**
|
|
3780
3797
|
* Get current database type
|
|
@@ -3790,7 +3807,6 @@ var PromptLoader = class {
|
|
|
3790
3807
|
*/
|
|
3791
3808
|
async loadDatabaseRules() {
|
|
3792
3809
|
if (this.databaseRulesCache.has(this.databaseType)) {
|
|
3793
|
-
logger.debug(`\u2713 Database rules for '${this.databaseType}' loaded from cache`);
|
|
3794
3810
|
return this.databaseRulesCache.get(this.databaseType);
|
|
3795
3811
|
}
|
|
3796
3812
|
const rulesPath = path2.join(this.promptsDir, "database-rules", `${this.databaseType}.md`);
|
|
@@ -3798,7 +3814,6 @@ var PromptLoader = class {
|
|
|
3798
3814
|
if (fs3.existsSync(rulesPath)) {
|
|
3799
3815
|
const rules = fs3.readFileSync(rulesPath, "utf-8");
|
|
3800
3816
|
this.databaseRulesCache.set(this.databaseType, rules);
|
|
3801
|
-
logger.info(`\u2713 Loaded database rules for '${this.databaseType}' from ${rulesPath}`);
|
|
3802
3817
|
return rules;
|
|
3803
3818
|
}
|
|
3804
3819
|
} catch (error) {
|
|
@@ -4074,7 +4089,6 @@ var Schema = class {
|
|
|
4074
4089
|
* @returns Parsed schema object or null if error occurs
|
|
4075
4090
|
*/
|
|
4076
4091
|
getDatabaseSchema() {
|
|
4077
|
-
logger.info(`SCHEMA_FILE_PATH: ${this.schemaFilePath}`);
|
|
4078
4092
|
try {
|
|
4079
4093
|
const dir = path3.dirname(this.schemaFilePath);
|
|
4080
4094
|
if (!fs4.existsSync(dir)) {
|
|
@@ -4343,14 +4357,6 @@ Format: [TIMESTAMP] [REQUEST_ID] [PROVIDER/MODEL] [METHOD]
|
|
|
4343
4357
|
Cost: $${entry.costUSD.toFixed(6)} | Time: ${entry.durationMs}ms${toolInfo}${errorInfo}${cacheStatus}
|
|
4344
4358
|
`;
|
|
4345
4359
|
this.logStream?.write(logLine);
|
|
4346
|
-
if (entry.cacheReadTokens && entry.cacheReadTokens > 0) {
|
|
4347
|
-
console.log(`[LLM] \u26A1 CACHE HIT: ${entry.cacheReadTokens.toLocaleString()} tokens read from cache (${entry.method})`);
|
|
4348
|
-
} else if (entry.cacheWriteTokens && entry.cacheWriteTokens > 0) {
|
|
4349
|
-
console.log(`[LLM] \u{1F4DD} CACHE WRITE: ${entry.cacheWriteTokens.toLocaleString()} tokens cached for future requests (${entry.method})`);
|
|
4350
|
-
}
|
|
4351
|
-
if (process.env.SUPERATOM_LOG_LEVEL === "verbose") {
|
|
4352
|
-
console.log("\n[LLM-Usage]", logLine);
|
|
4353
|
-
}
|
|
4354
4360
|
}
|
|
4355
4361
|
/**
|
|
4356
4362
|
* Log session summary (call at end of request)
|
|
@@ -4383,11 +4389,6 @@ Avg Time/Call: ${Math.round(this.sessionStats.totalDurationMs / this.sessionStat
|
|
|
4383
4389
|
|
|
4384
4390
|
`;
|
|
4385
4391
|
this.logStream?.write(summary);
|
|
4386
|
-
console.log("\n[LLM-Usage] Session Summary:");
|
|
4387
|
-
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`);
|
|
4388
|
-
if (hasCaching) {
|
|
4389
|
-
console.log(` Cache: ${this.sessionStats.totalCacheReadTokens.toLocaleString()} read, ${this.sessionStats.totalCacheWriteTokens.toLocaleString()} written | Savings: ~$${cacheReadSavings.toFixed(4)}`);
|
|
4390
|
-
}
|
|
4391
4392
|
}
|
|
4392
4393
|
/**
|
|
4393
4394
|
* Reset session stats (call at start of new user request)
|
|
@@ -4428,7 +4429,6 @@ Format: [TIMESTAMP] [REQUEST_ID] [PROVIDER/MODEL] [METHOD]
|
|
|
4428
4429
|
`;
|
|
4429
4430
|
this.logStream.write(header);
|
|
4430
4431
|
this.resetSession();
|
|
4431
|
-
console.log(`[LLM-Usage] Log file reset for new request: ${this.logPath}`);
|
|
4432
4432
|
} catch (error) {
|
|
4433
4433
|
console.error("[LLM-Usage-Logger] Failed to reset log file:", error);
|
|
4434
4434
|
}
|
|
@@ -5902,21 +5902,20 @@ var getKnowledgeBase = async ({
|
|
|
5902
5902
|
}) => {
|
|
5903
5903
|
try {
|
|
5904
5904
|
if (!collections || !collections["knowledge-base"] || !collections["knowledge-base"]["query"]) {
|
|
5905
|
-
logger.
|
|
5905
|
+
logger.warn("[KnowledgeBase] knowledge-base.query collection not registered, skipping");
|
|
5906
5906
|
return "";
|
|
5907
5907
|
}
|
|
5908
|
-
logger.info(`[KnowledgeBase] Querying knowledge base for: "${prompt.substring(0, 50)}..."`);
|
|
5909
5908
|
const result = await collections["knowledge-base"]["query"]({
|
|
5910
5909
|
prompt,
|
|
5911
5910
|
topK
|
|
5912
5911
|
});
|
|
5913
5912
|
if (!result || !result.content) {
|
|
5914
|
-
logger.
|
|
5913
|
+
logger.warn("[KnowledgeBase] No knowledge base results returned");
|
|
5915
5914
|
return "";
|
|
5916
5915
|
}
|
|
5917
5916
|
logger.info(`[KnowledgeBase] Retrieved knowledge base context (${result.content.length} chars)`);
|
|
5918
5917
|
if (result.metadata?.sources && result.metadata.sources.length > 0) {
|
|
5919
|
-
logger.
|
|
5918
|
+
logger.warn(`[KnowledgeBase] Sources: ${result.metadata.sources.map((s) => s.title).join(", ")}`);
|
|
5920
5919
|
}
|
|
5921
5920
|
return result.content;
|
|
5922
5921
|
} catch (error) {
|
|
@@ -5931,13 +5930,12 @@ var getGlobalKnowledgeBase = async ({
|
|
|
5931
5930
|
}) => {
|
|
5932
5931
|
try {
|
|
5933
5932
|
if (!collections || !collections["knowledge-base"] || !collections["knowledge-base"]["getGlobal"]) {
|
|
5934
|
-
logger.
|
|
5933
|
+
logger.warn("[KnowledgeBase] knowledge-base.getGlobal collection not registered, skipping");
|
|
5935
5934
|
return "";
|
|
5936
5935
|
}
|
|
5937
|
-
logger.info("[KnowledgeBase] Fetching global knowledge base nodes...");
|
|
5938
5936
|
const result = await collections["knowledge-base"]["getGlobal"]({ limit });
|
|
5939
5937
|
if (!result || !result.content) {
|
|
5940
|
-
logger.
|
|
5938
|
+
logger.warn("[KnowledgeBase] No global knowledge base nodes found");
|
|
5941
5939
|
return "";
|
|
5942
5940
|
}
|
|
5943
5941
|
logger.info(`[KnowledgeBase] Retrieved ${result.count || 0} global knowledge base nodes`);
|
|
@@ -5955,14 +5953,13 @@ var getUserKnowledgeBase = async ({
|
|
|
5955
5953
|
}) => {
|
|
5956
5954
|
try {
|
|
5957
5955
|
if (!userId) {
|
|
5958
|
-
logger.
|
|
5956
|
+
logger.warn("[KnowledgeBase] No userId provided, skipping user knowledge base");
|
|
5959
5957
|
return "";
|
|
5960
5958
|
}
|
|
5961
5959
|
if (!collections || !collections["knowledge-base"] || !collections["knowledge-base"]["getByUser"]) {
|
|
5962
|
-
logger.
|
|
5960
|
+
logger.warn("[KnowledgeBase] knowledge-base.getByUser collection not registered, skipping");
|
|
5963
5961
|
return "";
|
|
5964
5962
|
}
|
|
5965
|
-
logger.info(`[KnowledgeBase] Fetching user knowledge base nodes for userId: ${userId}...`);
|
|
5966
5963
|
const result = await collections["knowledge-base"]["getByUser"]({
|
|
5967
5964
|
userId: Number(userId),
|
|
5968
5965
|
limit
|
|
@@ -5985,7 +5982,6 @@ var getAllKnowledgeBase = async ({
|
|
|
5985
5982
|
userId,
|
|
5986
5983
|
topK = 3
|
|
5987
5984
|
}) => {
|
|
5988
|
-
logger.info("[KnowledgeBase] Fetching all knowledge base contexts...");
|
|
5989
5985
|
const [globalContext, userContext, queryContext] = await Promise.all([
|
|
5990
5986
|
getGlobalKnowledgeBase({ collections }),
|
|
5991
5987
|
getUserKnowledgeBase({ collections, userId }),
|
|
@@ -6007,7 +6003,6 @@ var getAllKnowledgeBase = async ({
|
|
|
6007
6003
|
combinedContext += "The following information is semantically relevant to the current query:\n\n";
|
|
6008
6004
|
combinedContext += queryContext + "\n\n";
|
|
6009
6005
|
}
|
|
6010
|
-
logger.info(`[KnowledgeBase] Combined knowledge base context: global=${globalContext.length} chars, user=${userContext.length} chars, query=${queryContext.length} chars`);
|
|
6011
6006
|
return {
|
|
6012
6007
|
globalContext,
|
|
6013
6008
|
userContext,
|
|
@@ -6244,11 +6239,11 @@ var searchConversationsWithReranking = async (options) => {
|
|
|
6244
6239
|
} = options;
|
|
6245
6240
|
try {
|
|
6246
6241
|
if (!collections || !collections["conversation-history"]) {
|
|
6247
|
-
logger.
|
|
6242
|
+
logger.warn("[ConversationSearch] conversation-history collection not registered, skipping");
|
|
6248
6243
|
return null;
|
|
6249
6244
|
}
|
|
6250
6245
|
if (!collections["conversation-history"]["searchMultiple"]) {
|
|
6251
|
-
logger.
|
|
6246
|
+
logger.warn("[ConversationSearch] searchMultiple not available, falling back to standard search");
|
|
6252
6247
|
return searchConversations({
|
|
6253
6248
|
userPrompt,
|
|
6254
6249
|
collections,
|
|
@@ -6256,9 +6251,6 @@ var searchConversationsWithReranking = async (options) => {
|
|
|
6256
6251
|
similarityThreshold
|
|
6257
6252
|
});
|
|
6258
6253
|
}
|
|
6259
|
-
logger.info(`[ConversationSearch] Hybrid search for: "${userPrompt.substring(0, 50)}..."`);
|
|
6260
|
-
logger.info(`[ConversationSearch] Fetching ${rerankCandidates} candidates for reranking`);
|
|
6261
|
-
logger.info(`[ConversationSearch] Weights - Semantic: ${hybridOptions.semanticWeight}, BM25: ${hybridOptions.bm25Weight}`);
|
|
6262
6254
|
const results = await collections["conversation-history"]["searchMultiple"]({
|
|
6263
6255
|
userPrompt,
|
|
6264
6256
|
userId,
|
|
@@ -6299,7 +6291,6 @@ var searchConversationsWithReranking = async (options) => {
|
|
|
6299
6291
|
logger.info(
|
|
6300
6292
|
`[ConversationSearch] \u2713 Found match with semantic score ${(semanticScore * 100).toFixed(2)}%`
|
|
6301
6293
|
);
|
|
6302
|
-
logger.info(` - Returning cached result for: "${matchedUserPrompt}"`);
|
|
6303
6294
|
return {
|
|
6304
6295
|
uiBlock: best.uiBlock,
|
|
6305
6296
|
similarity: semanticScore,
|
|
@@ -6741,10 +6732,9 @@ Fixed SQL query:`;
|
|
|
6741
6732
|
* @param component - The component to validate
|
|
6742
6733
|
* @param collections - Collections object containing database execute function
|
|
6743
6734
|
* @param apiKey - Optional API key for LLM calls
|
|
6744
|
-
* @param logCollector - Optional log collector for logging
|
|
6745
6735
|
* @returns Validation result with component, query key, and result
|
|
6746
6736
|
*/
|
|
6747
|
-
async validateSingleQuery(component, collections, apiKey
|
|
6737
|
+
async validateSingleQuery(component, collections, apiKey) {
|
|
6748
6738
|
const query = component.props?.query;
|
|
6749
6739
|
const originalQueryKey = this.getQueryCacheKey(query);
|
|
6750
6740
|
const queryStr = typeof query === "string" ? query : query?.sql || "";
|
|
@@ -6765,7 +6755,6 @@ Fixed SQL query:`;
|
|
|
6765
6755
|
validated = true;
|
|
6766
6756
|
queryCache.set(validationResult.cacheKey, result);
|
|
6767
6757
|
logger.info(`[${this.config.providerName}] \u2713 Query validated for ${component.name} (attempt ${attempts}) - cached for frontend`);
|
|
6768
|
-
logCollector?.info(`\u2713 Query validated for ${component.name}`);
|
|
6769
6758
|
if (currentQueryStr !== queryStr) {
|
|
6770
6759
|
const fixedQuery = typeof query === "string" ? currentQueryStr : { ...query, sql: currentQueryStr };
|
|
6771
6760
|
component.props = {
|
|
@@ -6778,14 +6767,11 @@ Fixed SQL query:`;
|
|
|
6778
6767
|
} catch (error) {
|
|
6779
6768
|
lastError = error instanceof Error ? error.message : String(error);
|
|
6780
6769
|
logger.warn(`[${this.config.providerName}] Query validation failed for ${component.name} (attempt ${attempts}/${MAX_QUERY_VALIDATION_RETRIES}): ${lastError}`);
|
|
6781
|
-
logCollector?.warn(`Query validation failed for ${component.name}: ${lastError}`);
|
|
6782
6770
|
if (attempts >= MAX_QUERY_VALIDATION_RETRIES) {
|
|
6783
6771
|
logger.error(`[${this.config.providerName}] \u2717 Max retries reached for ${component.name}, excluding from response`);
|
|
6784
|
-
logCollector?.error(`Max retries reached for ${component.name}, component excluded from response`);
|
|
6785
6772
|
break;
|
|
6786
6773
|
}
|
|
6787
6774
|
logger.info(`[${this.config.providerName}] Requesting query fix from LLM for ${component.name}...`);
|
|
6788
|
-
logCollector?.info(`Requesting query fix for ${component.name}...`);
|
|
6789
6775
|
try {
|
|
6790
6776
|
const fixedQueryStr = await this.requestQueryFix(
|
|
6791
6777
|
currentQueryStr,
|
|
@@ -6819,7 +6805,6 @@ Fixed SQL query:`;
|
|
|
6819
6805
|
}
|
|
6820
6806
|
if (!validated) {
|
|
6821
6807
|
logger.warn(`[${this.config.providerName}] Component ${component.name} excluded from response due to failed query validation`);
|
|
6822
|
-
logCollector?.warn(`Component ${component.name} excluded from response`);
|
|
6823
6808
|
}
|
|
6824
6809
|
return {
|
|
6825
6810
|
component: validated ? component : null,
|
|
@@ -6833,10 +6818,9 @@ Fixed SQL query:`;
|
|
|
6833
6818
|
* @param components - Array of components with potential queries
|
|
6834
6819
|
* @param collections - Collections object containing database execute function
|
|
6835
6820
|
* @param apiKey - Optional API key for LLM calls
|
|
6836
|
-
* @param logCollector - Optional log collector for logging
|
|
6837
6821
|
* @returns Object with validated components and query results map
|
|
6838
6822
|
*/
|
|
6839
|
-
async validateComponentQueries(components, collections, apiKey
|
|
6823
|
+
async validateComponentQueries(components, collections, apiKey) {
|
|
6840
6824
|
const queryResults = /* @__PURE__ */ new Map();
|
|
6841
6825
|
const validatedComponents = [];
|
|
6842
6826
|
const componentsWithoutQuery = [];
|
|
@@ -6853,9 +6837,8 @@ Fixed SQL query:`;
|
|
|
6853
6837
|
return { components: validatedComponents, queryResults };
|
|
6854
6838
|
}
|
|
6855
6839
|
logger.info(`[${this.config.providerName}] Validating ${componentsWithQuery.length} component queries in parallel...`);
|
|
6856
|
-
logCollector?.info(`Validating ${componentsWithQuery.length} component queries in parallel...`);
|
|
6857
6840
|
const validationPromises = componentsWithQuery.map(
|
|
6858
|
-
(component) => this.validateSingleQuery(component, collections, apiKey
|
|
6841
|
+
(component) => this.validateSingleQuery(component, collections, apiKey)
|
|
6859
6842
|
);
|
|
6860
6843
|
const results = await Promise.allSettled(validationPromises);
|
|
6861
6844
|
for (let i = 0; i < results.length; i++) {
|
|
@@ -6872,7 +6855,6 @@ Fixed SQL query:`;
|
|
|
6872
6855
|
}
|
|
6873
6856
|
} else {
|
|
6874
6857
|
logger.error(`[${this.config.providerName}] Unexpected error validating ${component.name}: ${result.reason}`);
|
|
6875
|
-
logCollector?.error(`Unexpected error validating ${component.name}: ${result.reason}`);
|
|
6876
6858
|
}
|
|
6877
6859
|
}
|
|
6878
6860
|
logger.info(`[${this.config.providerName}] Parallel validation complete: ${validatedComponents.length}/${components.length} components validated`);
|
|
@@ -6934,22 +6916,17 @@ var ToolExecutorService = class {
|
|
|
6934
6916
|
let sql = toolInput.sql;
|
|
6935
6917
|
const params = toolInput.params || {};
|
|
6936
6918
|
const reasoning = toolInput.reasoning;
|
|
6937
|
-
const { streamBuffer, collections,
|
|
6919
|
+
const { streamBuffer, collections, providerName } = this.config;
|
|
6938
6920
|
sql = ensureQueryLimit(sql, MAX_COMPONENT_QUERY_LIMIT, MAX_COMPONENT_QUERY_LIMIT);
|
|
6939
6921
|
const queryKey = sql.toLowerCase().replace(/\s+/g, " ").trim();
|
|
6940
6922
|
const attempts = (this.queryAttempts.get(queryKey) || 0) + 1;
|
|
6941
6923
|
this.queryAttempts.set(queryKey, attempts);
|
|
6942
|
-
logger.info(`[${providerName}] Executing query (attempt ${attempts}/${MAX_QUERY_ATTEMPTS}): ${sql.substring(0, 100)}...`);
|
|
6943
6924
|
if (Object.keys(params).length > 0) {
|
|
6944
6925
|
logger.info(`[${providerName}] Query params: ${JSON.stringify(params)}`);
|
|
6945
6926
|
}
|
|
6946
|
-
if (reasoning) {
|
|
6947
|
-
logCollector?.info(`Query reasoning: ${reasoning}`);
|
|
6948
|
-
}
|
|
6949
6927
|
if (attempts > MAX_QUERY_ATTEMPTS) {
|
|
6950
6928
|
const errorMsg = `Maximum query attempts (${MAX_QUERY_ATTEMPTS}) reached. Unable to generate a valid query for your question.`;
|
|
6951
6929
|
logger.error(`[${providerName}] ${errorMsg}`);
|
|
6952
|
-
logCollector?.error(errorMsg);
|
|
6953
6930
|
this.maxAttemptsReached = true;
|
|
6954
6931
|
if (streamBuffer.hasCallback()) {
|
|
6955
6932
|
streamBuffer.write(`
|
|
@@ -7009,11 +6986,6 @@ ${sql}
|
|
|
7009
6986
|
await streamDelay();
|
|
7010
6987
|
}
|
|
7011
6988
|
}
|
|
7012
|
-
logCollector?.logQuery?.(
|
|
7013
|
-
`Executing SQL query (attempt ${attempts})`,
|
|
7014
|
-
{ sql, params },
|
|
7015
|
-
{ reasoning, attempt: attempts }
|
|
7016
|
-
);
|
|
7017
6989
|
if (!collections?.["database"]?.["execute"]) {
|
|
7018
6990
|
throw new Error("Database collection not registered. Please register database.execute collection to execute queries.");
|
|
7019
6991
|
}
|
|
@@ -7025,8 +6997,6 @@ ${sql}
|
|
|
7025
6997
|
);
|
|
7026
6998
|
const data = result?.data || result;
|
|
7027
6999
|
const rowCount = result?.count ?? (Array.isArray(data) ? data.length : "N/A");
|
|
7028
|
-
logger.info(`[${providerName}] Query executed successfully, rows returned: ${rowCount}`);
|
|
7029
|
-
logCollector?.info(`Query successful, returned ${rowCount} rows`);
|
|
7030
7000
|
if (streamBuffer.hasCallback()) {
|
|
7031
7001
|
streamBuffer.write(`
|
|
7032
7002
|
\u2705 **Query executed successfully!**
|
|
@@ -7075,7 +7045,6 @@ ${sql}
|
|
|
7075
7045
|
maxRows: DEFAULT_MAX_ROWS_FOR_LLM,
|
|
7076
7046
|
maxCharsPerField: DEFAULT_MAX_CHARS_PER_FIELD2
|
|
7077
7047
|
});
|
|
7078
|
-
logger.info(`[${providerName}] Query result formatted: ${formattedResult.summary.recordsShown}/${formattedResult.summary.totalRecords} records`);
|
|
7079
7048
|
if (formattedResult.truncationNote) {
|
|
7080
7049
|
logger.info(`[${providerName}] Truncation: ${formattedResult.truncationNote}`);
|
|
7081
7050
|
}
|
|
@@ -7083,7 +7052,6 @@ ${sql}
|
|
|
7083
7052
|
} catch (error) {
|
|
7084
7053
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
7085
7054
|
logger.error(`[${providerName}] Query execution failed (attempt ${attempts}/${MAX_QUERY_ATTEMPTS}): ${errorMsg}`);
|
|
7086
|
-
logCollector?.error(`Query failed (attempt ${attempts}/${MAX_QUERY_ATTEMPTS}): ${errorMsg}`);
|
|
7087
7055
|
userPromptErrorLogger.logSqlError(sql, error instanceof Error ? error : new Error(errorMsg), Object.keys(params).length > 0 ? Object.values(params) : void 0);
|
|
7088
7056
|
if (streamBuffer.hasCallback()) {
|
|
7089
7057
|
streamBuffer.write(`\u274C **Query execution failed:**
|
|
@@ -7105,19 +7073,16 @@ ${errorMsg}
|
|
|
7105
7073
|
* Execute an external tool with retry tracking and streaming feedback
|
|
7106
7074
|
*/
|
|
7107
7075
|
async executeExternalTool(toolName, toolInput, externalTools) {
|
|
7108
|
-
const { streamBuffer,
|
|
7076
|
+
const { streamBuffer, providerName } = this.config;
|
|
7109
7077
|
const externalTool = externalTools?.find((t) => t.id === toolName);
|
|
7110
7078
|
if (!externalTool) {
|
|
7111
7079
|
throw new Error(`Unknown tool: ${toolName}`);
|
|
7112
7080
|
}
|
|
7113
7081
|
const attempts = (this.toolAttempts.get(toolName) || 0) + 1;
|
|
7114
7082
|
this.toolAttempts.set(toolName, attempts);
|
|
7115
|
-
logger.info(`[${providerName}] Executing external tool: ${externalTool.name} (attempt ${attempts}/${MAX_TOOL_ATTEMPTS})`);
|
|
7116
|
-
logCollector?.info(`Executing external tool: ${externalTool.name} (attempt ${attempts}/${MAX_TOOL_ATTEMPTS})...`);
|
|
7117
7083
|
if (attempts > MAX_TOOL_ATTEMPTS) {
|
|
7118
7084
|
const errorMsg = `Maximum attempts (${MAX_TOOL_ATTEMPTS}) reached for tool: ${externalTool.name}`;
|
|
7119
7085
|
logger.error(`[${providerName}] ${errorMsg}`);
|
|
7120
|
-
logCollector?.error(errorMsg);
|
|
7121
7086
|
if (streamBuffer.hasCallback()) {
|
|
7122
7087
|
streamBuffer.write(`
|
|
7123
7088
|
|
|
@@ -7152,8 +7117,6 @@ Please try rephrasing your request or contact support.
|
|
|
7152
7117
|
`Running ${externalTool.name}`,
|
|
7153
7118
|
streamBuffer
|
|
7154
7119
|
);
|
|
7155
|
-
logger.info(`[${providerName}] External tool ${externalTool.name} executed successfully`);
|
|
7156
|
-
logCollector?.info(`\u2713 ${externalTool.name} executed successfully`);
|
|
7157
7120
|
if (!this.executedToolsList.find((t) => t.id === externalTool.id)) {
|
|
7158
7121
|
const formattedForTracking = formatToolResultForLLM(result, {
|
|
7159
7122
|
toolName: externalTool.name,
|
|
@@ -7173,7 +7136,6 @@ Please try rephrasing your request or contact support.
|
|
|
7173
7136
|
},
|
|
7174
7137
|
outputSchema: externalTool.outputSchema
|
|
7175
7138
|
});
|
|
7176
|
-
logger.info(`[${providerName}] Tracked executed tool: ${externalTool.name} with ${formattedForTracking.summary.totalRecords} total records`);
|
|
7177
7139
|
}
|
|
7178
7140
|
if (streamBuffer.hasCallback()) {
|
|
7179
7141
|
streamBuffer.write(`\u2705 **${externalTool.name} completed successfully**
|
|
@@ -7187,7 +7149,6 @@ Please try rephrasing your request or contact support.
|
|
|
7187
7149
|
maxRows: DEFAULT_MAX_ROWS_FOR_LLM,
|
|
7188
7150
|
maxCharsPerField: DEFAULT_MAX_CHARS_PER_FIELD2
|
|
7189
7151
|
});
|
|
7190
|
-
logger.info(`[${providerName}] Tool result formatted: ${formattedToolResult.summary.recordsShown}/${formattedToolResult.summary.totalRecords} records`);
|
|
7191
7152
|
if (formattedToolResult.truncationNote) {
|
|
7192
7153
|
logger.info(`[${providerName}] Truncation: ${formattedToolResult.truncationNote}`);
|
|
7193
7154
|
}
|
|
@@ -7195,7 +7156,6 @@ Please try rephrasing your request or contact support.
|
|
|
7195
7156
|
} catch (error) {
|
|
7196
7157
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
7197
7158
|
logger.error(`[${providerName}] External tool ${externalTool.name} failed (attempt ${attempts}/${MAX_TOOL_ATTEMPTS}): ${errorMsg}`);
|
|
7198
|
-
logCollector?.error(`\u2717 ${externalTool.name} failed: ${errorMsg}`);
|
|
7199
7159
|
userPromptErrorLogger.logToolError(externalTool.name, toolInput, error instanceof Error ? error : new Error(errorMsg));
|
|
7200
7160
|
if (streamBuffer.hasCallback()) {
|
|
7201
7161
|
streamBuffer.write(`\u274C **${externalTool.name} failed:**
|
|
@@ -7273,7 +7233,6 @@ var BaseLLM = class {
|
|
|
7273
7233
|
return;
|
|
7274
7234
|
}
|
|
7275
7235
|
this.conversationSimilarityThreshold = threshold;
|
|
7276
|
-
logger.info(`[${this.getProviderName()}] Conversation similarity threshold set to: ${threshold}`);
|
|
7277
7236
|
}
|
|
7278
7237
|
/**
|
|
7279
7238
|
* Get the current conversation similarity threshold
|
|
@@ -7316,16 +7275,14 @@ var BaseLLM = class {
|
|
|
7316
7275
|
* @param analysisContent - The text response containing component suggestions
|
|
7317
7276
|
* @param components - List of available components
|
|
7318
7277
|
* @param apiKey - Optional API key
|
|
7319
|
-
* @param logCollector - Optional log collector
|
|
7320
7278
|
* @param componentStreamCallback - Optional callback to stream primary KPI component as soon as it's identified
|
|
7321
7279
|
* @returns Object containing matched components, layout title/description, and follow-up actions
|
|
7322
7280
|
*/
|
|
7323
|
-
async matchComponentsFromAnalysis(analysisContent, components, userPrompt, apiKey,
|
|
7281
|
+
async matchComponentsFromAnalysis(analysisContent, components, userPrompt, apiKey, componentStreamCallback, deferredTools, executedTools, collections, userId) {
|
|
7324
7282
|
const methodStartTime = Date.now();
|
|
7325
7283
|
const methodName = "matchComponentsFromAnalysis";
|
|
7326
7284
|
logger.info(`[${this.getProviderName()}] [TIMING] START ${methodName} | model: ${this.getModelForTask("complex")}`);
|
|
7327
7285
|
try {
|
|
7328
|
-
logger.debug(`[${this.getProviderName()}] Starting component matching from text response`);
|
|
7329
7286
|
let availableComponentsText = "No components available";
|
|
7330
7287
|
if (components && components.length > 0) {
|
|
7331
7288
|
availableComponentsText = components.map((comp, idx) => {
|
|
@@ -7341,7 +7298,6 @@ var BaseLLM = class {
|
|
|
7341
7298
|
}
|
|
7342
7299
|
let deferredToolsText = "No deferred external tools for this request.";
|
|
7343
7300
|
if (deferredTools && deferredTools.length > 0) {
|
|
7344
|
-
logger.info(`[${this.getProviderName()}] Passing ${deferredTools.length} deferred tools to component matching`);
|
|
7345
7301
|
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) => {
|
|
7346
7302
|
return `${idx + 1}. **${tool.name}**
|
|
7347
7303
|
toolId: "${tool.id}" (USE THIS EXACT VALUE - do not modify!)
|
|
@@ -7353,7 +7309,6 @@ var BaseLLM = class {
|
|
|
7353
7309
|
}
|
|
7354
7310
|
let executedToolsText = "No external tools were executed for data fetching.";
|
|
7355
7311
|
if (executedTools && executedTools.length > 0) {
|
|
7356
|
-
logger.info(`[${this.getProviderName()}] Passing ${executedTools.length} executed tools to component matching`);
|
|
7357
7312
|
executedToolsText = "The following external tools were executed to fetch data.\n" + executedTools.map((tool, idx) => {
|
|
7358
7313
|
let outputSchemaText = "Not available";
|
|
7359
7314
|
let fieldNamesList = "";
|
|
@@ -7409,14 +7364,12 @@ ${fieldsText}`;
|
|
|
7409
7364
|
KNOWLEDGE_BASE_CONTEXT: knowledgeBaseContext,
|
|
7410
7365
|
CURRENT_DATETIME: getCurrentDateTimeForPrompt()
|
|
7411
7366
|
});
|
|
7412
|
-
logger.debug(`[${this.getProviderName()}] Loaded match-text-components prompts`);
|
|
7413
7367
|
logger.logLLMPrompt("matchComponentsFromAnalysis", "system", extractPromptText(prompts.system));
|
|
7414
7368
|
logger.logLLMPrompt("matchComponentsFromAnalysis", "user", `Text Analysis:
|
|
7415
7369
|
${analysisContent}
|
|
7416
7370
|
|
|
7417
7371
|
Executed Tools:
|
|
7418
7372
|
${executedToolsText}`);
|
|
7419
|
-
logCollector?.info("Matching components from text response...");
|
|
7420
7373
|
let fullResponseText = "";
|
|
7421
7374
|
let answerComponentExtracted = false;
|
|
7422
7375
|
const answerCallback = componentStreamCallback;
|
|
@@ -7476,18 +7429,7 @@ ${executedToolsText}`);
|
|
|
7476
7429
|
...answerComponentData.props
|
|
7477
7430
|
}
|
|
7478
7431
|
};
|
|
7479
|
-
const streamTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
7480
|
-
logger.info(`[${this.getProviderName()}] \u2713 [${streamTime}] Answer component detected in stream: ${answerComponent.name} (${answerComponent.type})`);
|
|
7481
|
-
logCollector?.info(`\u2713 Answer component: ${answerComponent.name} (${answerComponent.type}) - detected at ${streamTime}`);
|
|
7482
|
-
if (answerComponentData.props?.query) {
|
|
7483
|
-
logCollector?.logQuery(
|
|
7484
|
-
"Answer component query",
|
|
7485
|
-
answerComponentData.props.query,
|
|
7486
|
-
{ componentName: answerComponent.name, componentType: answerComponent.type, reasoning: answerComponentData.reasoning }
|
|
7487
|
-
);
|
|
7488
|
-
}
|
|
7489
7432
|
let answerQuery = answerComponent.props?.query;
|
|
7490
|
-
logger.info(`[${this.getProviderName()}] Answer component detected: ${answerComponent.name} (${answerComponent.type}), hasQuery: ${!!answerQuery}, hasDbExecute: ${!!collections?.["database"]?.["execute"]}`);
|
|
7491
7433
|
if (answerQuery) {
|
|
7492
7434
|
if (typeof answerQuery === "string") {
|
|
7493
7435
|
answerQuery = ensureQueryLimit(answerQuery, this.defaultLimit, MAX_COMPONENT_QUERY_LIMIT);
|
|
@@ -7505,24 +7447,18 @@ ${executedToolsText}`);
|
|
|
7505
7447
|
let currentQuery = answerQuery;
|
|
7506
7448
|
let currentQueryStr = typeof answerQuery === "string" ? answerQuery : answerQuery?.sql || "";
|
|
7507
7449
|
let lastError = "";
|
|
7508
|
-
logger.info(`[${this.getProviderName()}] Validating answer component query before streaming...`);
|
|
7509
7450
|
while (attempts < maxRetries && !validated) {
|
|
7510
7451
|
attempts++;
|
|
7511
7452
|
try {
|
|
7512
7453
|
const cacheKey = this.queryService.getQueryCacheKey(currentQuery);
|
|
7513
7454
|
if (cacheKey) {
|
|
7514
|
-
logger.debug(`[${this.getProviderName()}] Answer component query validation attempt ${attempts}/${maxRetries}`);
|
|
7515
7455
|
const result2 = await collections["database"]["execute"]({ sql: cacheKey });
|
|
7516
7456
|
queryCache.set(cacheKey, result2);
|
|
7517
7457
|
validated = true;
|
|
7518
7458
|
if (currentQuery !== answerQuery) {
|
|
7519
7459
|
answerComponent.props.query = currentQuery;
|
|
7520
7460
|
}
|
|
7521
|
-
logger.info(`[${this.getProviderName()}] \u2713 Answer component query validated (attempt ${attempts}) - STREAMING TO FRONTEND NOW`);
|
|
7522
|
-
logCollector?.info(`\u2713 Answer component query validated - streaming to frontend`);
|
|
7523
|
-
logger.info(`[${this.getProviderName()}] Calling answerCallback for: ${answerComponent.name}`);
|
|
7524
7461
|
answerCallback(answerComponent);
|
|
7525
|
-
logger.info(`[${this.getProviderName()}] answerCallback completed for: ${answerComponent.name}`);
|
|
7526
7462
|
}
|
|
7527
7463
|
} catch (validationError) {
|
|
7528
7464
|
lastError = validationError instanceof Error ? validationError.message : String(validationError);
|
|
@@ -7558,7 +7494,6 @@ ${executedToolsText}`);
|
|
|
7558
7494
|
}
|
|
7559
7495
|
if (!validated) {
|
|
7560
7496
|
logger.warn(`[${this.getProviderName()}] Answer component query validation failed after ${attempts} attempts - component will be excluded`);
|
|
7561
|
-
logCollector?.warn(`Answer component query validation failed: ${lastError} - component will be excluded from response`);
|
|
7562
7497
|
}
|
|
7563
7498
|
})();
|
|
7564
7499
|
} else {
|
|
@@ -7569,7 +7504,7 @@ ${executedToolsText}`);
|
|
|
7569
7504
|
}
|
|
7570
7505
|
}
|
|
7571
7506
|
} catch (e) {
|
|
7572
|
-
logger.
|
|
7507
|
+
logger.error(`[${this.getProviderName()}] Partial answerComponent parse failed, waiting for more data...`);
|
|
7573
7508
|
}
|
|
7574
7509
|
}
|
|
7575
7510
|
}
|
|
@@ -7599,18 +7534,6 @@ ${executedToolsText}`);
|
|
|
7599
7534
|
logger.file("\n=============================\nFull LLM response:", JSON.stringify(result, null, 2));
|
|
7600
7535
|
const rawActions = result.actions || [];
|
|
7601
7536
|
const actions = convertQuestionsToActions(rawActions);
|
|
7602
|
-
if (matchedComponents.length > 0) {
|
|
7603
|
-
matchedComponents.forEach((comp, idx) => {
|
|
7604
|
-
logCollector?.info(` ${idx + 1}. ${comp.componentName} (${comp.componentType}): ${comp.reasoning}`);
|
|
7605
|
-
if (comp.props?.query) {
|
|
7606
|
-
logCollector?.logQuery(
|
|
7607
|
-
`Component ${idx + 1} query`,
|
|
7608
|
-
comp.props.query,
|
|
7609
|
-
{ componentName: comp.componentName, title: comp.props.title }
|
|
7610
|
-
);
|
|
7611
|
-
}
|
|
7612
|
-
});
|
|
7613
|
-
}
|
|
7614
7537
|
const finalComponents = matchedComponents.map((mc) => {
|
|
7615
7538
|
const originalComponent = components.find((c) => c.id === mc.componentId);
|
|
7616
7539
|
if (!originalComponent) {
|
|
@@ -7635,27 +7558,22 @@ ${executedToolsText}`);
|
|
|
7635
7558
|
}).filter(Boolean);
|
|
7636
7559
|
let validatedComponents = finalComponents;
|
|
7637
7560
|
if (collections?.["database"]?.["execute"]) {
|
|
7638
|
-
logger.info(`[${this.getProviderName()}] Starting query validation for ${finalComponents.length} components...`);
|
|
7639
|
-
logCollector?.info(`Validating queries for ${finalComponents.length} components...`);
|
|
7640
7561
|
try {
|
|
7641
7562
|
const validationResult = await this.queryService.validateComponentQueries(
|
|
7642
7563
|
finalComponents,
|
|
7643
7564
|
collections,
|
|
7644
|
-
apiKey
|
|
7645
|
-
logCollector
|
|
7565
|
+
apiKey
|
|
7646
7566
|
);
|
|
7647
7567
|
validatedComponents = validationResult.components;
|
|
7648
7568
|
const queriedComponents = finalComponents.filter((c) => c.props?.query);
|
|
7649
7569
|
const validatedQueries = validatedComponents.filter((c) => c.props?.query);
|
|
7650
7570
|
logger.info(`[${this.getProviderName()}] Query validation complete: ${validatedQueries.length}/${queriedComponents.length} queries validated`);
|
|
7651
|
-
logCollector?.info(`Query validation complete: ${validatedQueries.length}/${queriedComponents.length} queries validated`);
|
|
7652
7571
|
} catch (validationError) {
|
|
7653
7572
|
const validationErrorMsg = validationError instanceof Error ? validationError.message : String(validationError);
|
|
7654
7573
|
logger.error(`[${this.getProviderName()}] Query validation error: ${validationErrorMsg}`);
|
|
7655
|
-
logCollector?.error(`Query validation error: ${validationErrorMsg}`);
|
|
7656
7574
|
}
|
|
7657
7575
|
} else {
|
|
7658
|
-
logger.
|
|
7576
|
+
logger.error(`[${this.getProviderName()}] Skipping query validation - database execute function not available`);
|
|
7659
7577
|
}
|
|
7660
7578
|
const methodDuration = Date.now() - methodStartTime;
|
|
7661
7579
|
logger.info(`[${this.getProviderName()}] [TIMING] DONE ${methodName} in ${methodDuration}ms | components: ${validatedComponents.length} | actions: ${actions.length}`);
|
|
@@ -7669,7 +7587,6 @@ ${executedToolsText}`);
|
|
|
7669
7587
|
const methodDuration = Date.now() - methodStartTime;
|
|
7670
7588
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
7671
7589
|
logger.error(`[${this.getProviderName()}] [TIMING] FAILED ${methodName} in ${methodDuration}ms | error: ${errorMsg}`);
|
|
7672
|
-
logCollector?.error(`Failed to match components: ${errorMsg}`);
|
|
7673
7590
|
return {
|
|
7674
7591
|
components: [],
|
|
7675
7592
|
layoutTitle: "Dashboard",
|
|
@@ -7682,7 +7599,7 @@ ${executedToolsText}`);
|
|
|
7682
7599
|
* Classify user question into category and detect external tools needed
|
|
7683
7600
|
* Determines if question is for data analysis, requires external tools, or needs text response
|
|
7684
7601
|
*/
|
|
7685
|
-
async classifyQuestionCategory(userPrompt, apiKey,
|
|
7602
|
+
async classifyQuestionCategory(userPrompt, apiKey, conversationHistory, externalTools) {
|
|
7686
7603
|
const methodStartTime = Date.now();
|
|
7687
7604
|
const methodName = "classifyQuestionCategory";
|
|
7688
7605
|
const promptPreview = userPrompt.substring(0, 50) + (userPrompt.length > 50 ? "..." : "");
|
|
@@ -7718,16 +7635,6 @@ ${executedToolsText}`);
|
|
|
7718
7635
|
true
|
|
7719
7636
|
// Parse as JSON
|
|
7720
7637
|
);
|
|
7721
|
-
logCollector?.logExplanation(
|
|
7722
|
-
"Question category classified",
|
|
7723
|
-
result.reasoning || "No reasoning provided",
|
|
7724
|
-
{
|
|
7725
|
-
category: result.category,
|
|
7726
|
-
externalTools: result.externalTools || [],
|
|
7727
|
-
dataAnalysisType: result.dataAnalysisType,
|
|
7728
|
-
confidence: result.confidence
|
|
7729
|
-
}
|
|
7730
|
-
);
|
|
7731
7638
|
const methodDuration = Date.now() - methodStartTime;
|
|
7732
7639
|
logger.info(`[${this.getProviderName()}] [TIMING] DONE ${methodName} in ${methodDuration}ms | category: ${result.category} | confidence: ${result.confidence}% | tools: ${(result.externalTools || []).length}`);
|
|
7733
7640
|
return {
|
|
@@ -7741,7 +7648,6 @@ ${executedToolsText}`);
|
|
|
7741
7648
|
const methodDuration = Date.now() - methodStartTime;
|
|
7742
7649
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
7743
7650
|
logger.error(`[${this.getProviderName()}] [TIMING] FAILED ${methodName} in ${methodDuration}ms | error: ${errorMsg}`);
|
|
7744
|
-
logger.debug(`[${this.getProviderName()}] Category classification error details:`, error);
|
|
7745
7651
|
throw error;
|
|
7746
7652
|
}
|
|
7747
7653
|
}
|
|
@@ -7750,7 +7656,7 @@ ${executedToolsText}`);
|
|
|
7750
7656
|
* Takes a matched UI block from semantic search and modifies its props to answer the new question
|
|
7751
7657
|
* Also adapts the cached text response to match the new question
|
|
7752
7658
|
*/
|
|
7753
|
-
async adaptUIBlockParameters(currentUserPrompt, originalUserPrompt, matchedUIBlock, apiKey,
|
|
7659
|
+
async adaptUIBlockParameters(currentUserPrompt, originalUserPrompt, matchedUIBlock, apiKey, cachedTextResponse) {
|
|
7754
7660
|
const methodStartTime = Date.now();
|
|
7755
7661
|
const methodName = "adaptUIBlockParameters";
|
|
7756
7662
|
const promptPreview = currentUserPrompt.substring(0, 50) + (currentUserPrompt.length > 50 ? "..." : "");
|
|
@@ -7794,11 +7700,6 @@ ${executedToolsText}`);
|
|
|
7794
7700
|
if (!result.success) {
|
|
7795
7701
|
const methodDuration2 = Date.now() - methodStartTime;
|
|
7796
7702
|
logger.info(`[${this.getProviderName()}] [TIMING] DONE ${methodName} in ${methodDuration2}ms | result: adaptation failed - ${result.reason}`);
|
|
7797
|
-
logCollector?.warn(
|
|
7798
|
-
"Could not adapt matched UI block",
|
|
7799
|
-
"explanation",
|
|
7800
|
-
{ reason: result.reason }
|
|
7801
|
-
);
|
|
7802
7703
|
return {
|
|
7803
7704
|
success: false,
|
|
7804
7705
|
explanation: result.explanation || "Adaptation not possible"
|
|
@@ -7810,14 +7711,6 @@ ${executedToolsText}`);
|
|
|
7810
7711
|
this.defaultLimit
|
|
7811
7712
|
);
|
|
7812
7713
|
}
|
|
7813
|
-
logCollector?.logExplanation(
|
|
7814
|
-
"UI block parameters adapted",
|
|
7815
|
-
result.explanation || "Parameters adapted successfully",
|
|
7816
|
-
{
|
|
7817
|
-
parametersChanged: result.parametersChanged || [],
|
|
7818
|
-
componentType: result.adaptedComponent?.type
|
|
7819
|
-
}
|
|
7820
|
-
);
|
|
7821
7714
|
const methodDuration = Date.now() - methodStartTime;
|
|
7822
7715
|
logger.info(`[${this.getProviderName()}] [TIMING] DONE ${methodName} in ${methodDuration}ms | result: success | changes: ${(result.parametersChanged || []).length}`);
|
|
7823
7716
|
return {
|
|
@@ -7831,7 +7724,6 @@ ${executedToolsText}`);
|
|
|
7831
7724
|
const methodDuration = Date.now() - methodStartTime;
|
|
7832
7725
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
7833
7726
|
logger.error(`[${this.getProviderName()}] [TIMING] FAILED ${methodName} in ${methodDuration}ms | error: ${errorMsg}`);
|
|
7834
|
-
logger.debug(`[${this.getProviderName()}] Adaptation error details:`, error);
|
|
7835
7727
|
return {
|
|
7836
7728
|
success: false,
|
|
7837
7729
|
explanation: `Error adapting parameters: ${errorMsg}`
|
|
@@ -7844,14 +7736,12 @@ ${executedToolsText}`);
|
|
|
7844
7736
|
* Supports tool calling for query execution with automatic retry on errors (max 3 attempts)
|
|
7845
7737
|
* After generating text response, if components are provided, matches suggested components
|
|
7846
7738
|
*/
|
|
7847
|
-
async generateTextResponse(userPrompt, apiKey,
|
|
7739
|
+
async generateTextResponse(userPrompt, apiKey, conversationHistory, streamCallback, collections, components, externalTools, category, userId) {
|
|
7848
7740
|
const methodStartTime = Date.now();
|
|
7849
7741
|
const methodName = "generateTextResponse";
|
|
7850
7742
|
const promptPreview = userPrompt.substring(0, 50) + (userPrompt.length > 50 ? "..." : "");
|
|
7851
7743
|
logger.info(`[${this.getProviderName()}] [TIMING] START ${methodName} | model: ${this.getModelForTask("complex")} | category: ${category} | prompt: "${promptPreview}"`);
|
|
7852
7744
|
const errors = [];
|
|
7853
|
-
logger.debug(`[${this.getProviderName()}] Starting text response generation`);
|
|
7854
|
-
logger.debug(`[${this.getProviderName()}] User prompt: "${userPrompt.substring(0, 50)}..."`);
|
|
7855
7745
|
try {
|
|
7856
7746
|
let availableToolsDoc = "No external tools are available for this request.";
|
|
7857
7747
|
if (externalTools && externalTools.length > 0) {
|
|
@@ -7915,9 +7805,6 @@ ${executedToolsText}`);
|
|
|
7915
7805
|
});
|
|
7916
7806
|
logger.logLLMPrompt("generateTextResponse", "system", extractPromptText(prompts.system));
|
|
7917
7807
|
logger.logLLMPrompt("generateTextResponse", "user", extractPromptText(prompts.user));
|
|
7918
|
-
logger.debug(`[${this.getProviderName()}] Loaded text-response prompts with schema`);
|
|
7919
|
-
logger.debug(`[${this.getProviderName()}] System prompt length: ${prompts.system.length}, User prompt length: ${prompts.user.length}`);
|
|
7920
|
-
logCollector?.info("Generating text response with query execution capability...");
|
|
7921
7808
|
const tools = [{
|
|
7922
7809
|
name: "execute_query",
|
|
7923
7810
|
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.",
|
|
@@ -7946,7 +7833,6 @@ ${executedToolsText}`);
|
|
|
7946
7833
|
const executableTools = externalTools.filter(
|
|
7947
7834
|
(t) => t.executionType === "immediate" || t.executionType === "deferred" && t.userProvidedData
|
|
7948
7835
|
);
|
|
7949
|
-
logger.info(`[${this.getProviderName()}] Executable tools: ${executableTools.length} of ${externalTools.length} total`);
|
|
7950
7836
|
const addedToolIds = /* @__PURE__ */ new Set();
|
|
7951
7837
|
executableTools.forEach((tool) => {
|
|
7952
7838
|
if (addedToolIds.has(tool.id)) {
|
|
@@ -7954,7 +7840,6 @@ ${executedToolsText}`);
|
|
|
7954
7840
|
return;
|
|
7955
7841
|
}
|
|
7956
7842
|
addedToolIds.add(tool.id);
|
|
7957
|
-
logger.info(`[${this.getProviderName()}] Processing executable tool:`, JSON.stringify(tool, null, 2));
|
|
7958
7843
|
const properties = {};
|
|
7959
7844
|
const required = [];
|
|
7960
7845
|
Object.entries(tool.params || {}).forEach(([key, typeOrValue]) => {
|
|
@@ -8024,14 +7909,13 @@ ${executedToolsText}`);
|
|
|
8024
7909
|
});
|
|
8025
7910
|
});
|
|
8026
7911
|
logger.info(`[${this.getProviderName()}] Added ${addedToolIds.size} unique tool definitions from ${executableTools.length} tool calls (${externalTools.length - executableTools.length} deferred tools await form input)`);
|
|
8027
|
-
logger.
|
|
7912
|
+
logger.debug(`[${this.getProviderName()}] Complete tools array:`, JSON.stringify(tools, null, 2));
|
|
8028
7913
|
}
|
|
8029
7914
|
const streamBuffer = new StreamBuffer(streamCallback);
|
|
8030
7915
|
const toolExecutor = new ToolExecutorService({
|
|
8031
7916
|
providerName: this.getProviderName(),
|
|
8032
7917
|
collections,
|
|
8033
|
-
streamBuffer
|
|
8034
|
-
logCollector
|
|
7918
|
+
streamBuffer
|
|
8035
7919
|
});
|
|
8036
7920
|
const executableExternalTools = externalTools?.map((t) => ({
|
|
8037
7921
|
id: t.id,
|
|
@@ -8060,12 +7944,10 @@ ${executedToolsText}`);
|
|
|
8060
7944
|
},
|
|
8061
7945
|
MAX_TOOL_CALLING_ITERATIONS
|
|
8062
7946
|
);
|
|
8063
|
-
logger.info(`[${this.getProviderName()}] Text response stream completed`);
|
|
8064
7947
|
const textResponse = streamBuffer.getFullText() || result || "I apologize, but I was unable to generate a response.";
|
|
8065
7948
|
if (toolExecutor.isMaxAttemptsReached()) {
|
|
8066
7949
|
const methodDuration2 = Date.now() - methodStartTime;
|
|
8067
7950
|
logger.warn(`[${this.getProviderName()}] [TIMING] DONE ${methodName} in ${methodDuration2}ms | result: max attempts reached`);
|
|
8068
|
-
logCollector?.error("Failed to generate valid query after maximum attempts");
|
|
8069
7951
|
return {
|
|
8070
7952
|
success: false,
|
|
8071
7953
|
errors: [`Maximum query attempts (${MAX_QUERY_ATTEMPTS}) reached. Unable to generate a valid query for your question.`],
|
|
@@ -8077,14 +7959,6 @@ ${executedToolsText}`);
|
|
|
8077
7959
|
}
|
|
8078
7960
|
};
|
|
8079
7961
|
}
|
|
8080
|
-
logCollector?.info(`Text response: ${textResponse.substring(0, 100)}${textResponse.length > 100 ? "..." : ""}`);
|
|
8081
|
-
logCollector?.logExplanation(
|
|
8082
|
-
"Text response generated",
|
|
8083
|
-
"Generated plain text response with component suggestions",
|
|
8084
|
-
{
|
|
8085
|
-
textLength: textResponse.length
|
|
8086
|
-
}
|
|
8087
|
-
);
|
|
8088
7962
|
streamBuffer.flush();
|
|
8089
7963
|
if (streamBuffer.hasCallback() && components && components.length > 0 && category !== "general") {
|
|
8090
7964
|
streamBuffer.write("\n\n\u{1F4CA} **Generating visualization components...**\n\n");
|
|
@@ -8096,8 +7970,6 @@ ${executedToolsText}`);
|
|
|
8096
7970
|
let actions = [];
|
|
8097
7971
|
if (category === "general") {
|
|
8098
7972
|
logger.info(`[${this.getProviderName()}] Skipping component generation for general/conversational question`);
|
|
8099
|
-
logCollector?.info("Skipping component generation for general question");
|
|
8100
|
-
logger.info(`[${this.getProviderName()}] Generating actions for general question...`);
|
|
8101
7973
|
const nextQuestions = await this.generateNextQuestions(
|
|
8102
7974
|
userPrompt,
|
|
8103
7975
|
null,
|
|
@@ -8105,23 +7977,16 @@ ${executedToolsText}`);
|
|
|
8105
7977
|
void 0,
|
|
8106
7978
|
// no component data
|
|
8107
7979
|
apiKey,
|
|
8108
|
-
logCollector,
|
|
8109
7980
|
conversationHistory,
|
|
8110
7981
|
textResponse
|
|
8111
7982
|
// pass text response as context
|
|
8112
7983
|
);
|
|
8113
7984
|
actions = convertQuestionsToActions(nextQuestions);
|
|
8114
|
-
logger.info(`[${this.getProviderName()}] Generated ${actions.length} follow-up actions for general question`);
|
|
8115
7985
|
} else if (components && components.length > 0) {
|
|
8116
|
-
|
|
8117
|
-
logger.info(`[${this.getProviderName()}] componentStreamCallback setup: hasCallback=${streamBuffer.hasCallback()}, category=${category}`);
|
|
8118
|
-
const componentStreamCallback = streamBuffer.hasCallback() && category !== "data_modification" ? (component) => {
|
|
8119
|
-
logger.info(`[${this.getProviderName()}] componentStreamCallback INVOKED for: ${component.name} (${component.type})`);
|
|
7986
|
+
const componentStreamCallback = streamBuffer.hasCallback() && category === "data_analysis" ? (component) => {
|
|
8120
7987
|
const answerMarker = `__ANSWER_COMPONENT_START__${JSON.stringify(component)}__ANSWER_COMPONENT_END__`;
|
|
8121
7988
|
streamBuffer.write(answerMarker);
|
|
8122
|
-
logger.info(`[${this.getProviderName()}] Streamed answer component to frontend: ${component.name} (${component.type})`);
|
|
8123
7989
|
} : void 0;
|
|
8124
|
-
logger.info(`[${this.getProviderName()}] componentStreamCallback created: ${!!componentStreamCallback}`);
|
|
8125
7990
|
const deferredTools = externalTools?.filter((t) => {
|
|
8126
7991
|
if (t.executionType === "deferred" && !t.userProvidedData) return true;
|
|
8127
7992
|
if (category === "data_modification" && !t.userProvidedData) {
|
|
@@ -8142,7 +8007,6 @@ ${executedToolsText}`);
|
|
|
8142
8007
|
components,
|
|
8143
8008
|
userPrompt,
|
|
8144
8009
|
apiKey,
|
|
8145
|
-
logCollector,
|
|
8146
8010
|
componentStreamCallback,
|
|
8147
8011
|
deferredTools,
|
|
8148
8012
|
toolExecutor.getExecutedTools(),
|
|
@@ -8156,8 +8020,6 @@ ${executedToolsText}`);
|
|
|
8156
8020
|
}
|
|
8157
8021
|
let container_componet = null;
|
|
8158
8022
|
if (matchedComponents.length > 0) {
|
|
8159
|
-
logger.info(`[${this.getProviderName()}] Created MultiComponentContainer: "${layoutTitle}" with ${matchedComponents.length} components and ${actions.length} actions`);
|
|
8160
|
-
logCollector?.info(`Created dashboard: "${layoutTitle}" with ${matchedComponents.length} components and ${actions.length} actions`);
|
|
8161
8023
|
container_componet = {
|
|
8162
8024
|
id: `container_${Date.now()}`,
|
|
8163
8025
|
name: "MultiComponentContainer",
|
|
@@ -8189,7 +8051,6 @@ ${executedToolsText}`);
|
|
|
8189
8051
|
const methodDuration = Date.now() - methodStartTime;
|
|
8190
8052
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
8191
8053
|
logger.error(`[${this.getProviderName()}] [TIMING] FAILED ${methodName} in ${methodDuration}ms | error: ${errorMsg}`);
|
|
8192
|
-
logCollector?.error(`Error generating text response: ${errorMsg}`);
|
|
8193
8054
|
userPromptErrorLogger.logLlmError(
|
|
8194
8055
|
this.getProviderName(),
|
|
8195
8056
|
this.model,
|
|
@@ -8217,14 +8078,11 @@ ${executedToolsText}`);
|
|
|
8217
8078
|
* 2. Category classification: Determine if data_analysis, requires_external_tools, or text_response
|
|
8218
8079
|
* 3. Route appropriately based on category and response mode
|
|
8219
8080
|
*/
|
|
8220
|
-
async handleUserRequest(userPrompt, components, apiKey,
|
|
8081
|
+
async handleUserRequest(userPrompt, components, apiKey, conversationHistory, responseMode = "text", streamCallback, collections, externalTools, userId) {
|
|
8221
8082
|
const startTime = Date.now();
|
|
8222
|
-
logger.info(`[${this.getProviderName()}] handleUserRequest called for user prompt: ${userPrompt}`);
|
|
8223
|
-
logCollector?.info(`Starting request processing with mode: ${responseMode}`);
|
|
8224
8083
|
logger.clearFile();
|
|
8225
8084
|
logger.logLLMPrompt("handleUserRequest", "user", `User Prompt: ${userPrompt}`);
|
|
8226
8085
|
try {
|
|
8227
|
-
logger.info(`[${this.getProviderName()}] Step 1: Searching previous conversations...`);
|
|
8228
8086
|
const conversationMatch = await conversation_search_default.searchConversationsWithReranking({
|
|
8229
8087
|
userPrompt,
|
|
8230
8088
|
collections,
|
|
@@ -8233,22 +8091,16 @@ ${executedToolsText}`);
|
|
|
8233
8091
|
});
|
|
8234
8092
|
if (conversationMatch) {
|
|
8235
8093
|
logger.info(`[${this.getProviderName()}] \u2713 Found matching conversation with ${(conversationMatch.similarity * 100).toFixed(2)}% similarity`);
|
|
8236
|
-
logCollector?.info(
|
|
8237
|
-
`\u2713 Found similar conversation (${(conversationMatch.similarity * 100).toFixed(2)}% match)`
|
|
8238
|
-
);
|
|
8239
8094
|
const rawComponent = conversationMatch.uiBlock?.component || conversationMatch.uiBlock?.generatedComponentMetadata;
|
|
8240
8095
|
const isValidComponent = rawComponent && typeof rawComponent === "object" && Object.keys(rawComponent).length > 0;
|
|
8241
8096
|
const component = isValidComponent ? rawComponent : null;
|
|
8242
8097
|
const cachedTextResponse = conversationMatch.uiBlock?.analysis || conversationMatch.uiBlock?.textResponse || conversationMatch.uiBlock?.text || "";
|
|
8243
8098
|
if (this.containsFormComponent(component)) {
|
|
8244
8099
|
logger.info(`[${this.getProviderName()}] Skipping cached result - Form components contain stale defaultValues, fetching fresh data`);
|
|
8245
|
-
logCollector?.info("Skipping cache for form - fetching current values from database...");
|
|
8246
8100
|
} else if (!component) {
|
|
8247
8101
|
if (conversationMatch.similarity >= EXACT_MATCH_SIMILARITY_THRESHOLD) {
|
|
8248
8102
|
const elapsedTime2 = Date.now() - startTime;
|
|
8249
|
-
logger.info(`[${this.getProviderName()}] \u2713 Exact match for general question - returning cached text response`);
|
|
8250
|
-
logCollector?.info(`\u2713 Exact match for general question - returning cached response`);
|
|
8251
|
-
logCollector?.info(`Total time taken: ${elapsedTime2}ms (${(elapsedTime2 / 1e3).toFixed(2)}s)`);
|
|
8103
|
+
logger.info(`[${this.getProviderName()}] \u2713 Exact match for general question - returning cached text response (${elapsedTime2}ms)`);
|
|
8252
8104
|
return {
|
|
8253
8105
|
success: true,
|
|
8254
8106
|
data: {
|
|
@@ -8263,14 +8115,11 @@ ${executedToolsText}`);
|
|
|
8263
8115
|
};
|
|
8264
8116
|
} else {
|
|
8265
8117
|
logger.info(`[${this.getProviderName()}] Similar match but no component (general question) - processing fresh`);
|
|
8266
|
-
logCollector?.info("Similar match found but was a general conversation - processing as new question");
|
|
8267
8118
|
}
|
|
8268
8119
|
} else {
|
|
8269
8120
|
if (conversationMatch.similarity >= EXACT_MATCH_SIMILARITY_THRESHOLD) {
|
|
8270
8121
|
const elapsedTime2 = Date.now() - startTime;
|
|
8271
|
-
logger.info(`[${this.getProviderName()}] \u2713 100% match - returning UI block directly without adaptation`);
|
|
8272
|
-
logCollector?.info(`\u2713 Exact match (${(conversationMatch.similarity * 100).toFixed(2)}%) - returning cached result`);
|
|
8273
|
-
logCollector?.info(`Total time taken: ${elapsedTime2}ms (${(elapsedTime2 / 1e3).toFixed(2)}s)`);
|
|
8122
|
+
logger.info(`[${this.getProviderName()}] \u2713 100% match - returning UI block directly without adaptation (${elapsedTime2}ms)`);
|
|
8274
8123
|
if (streamCallback && cachedTextResponse) {
|
|
8275
8124
|
logger.info(`[${this.getProviderName()}] Streaming cached text response to frontend`);
|
|
8276
8125
|
streamCallback(cachedTextResponse);
|
|
@@ -8289,22 +8138,18 @@ ${executedToolsText}`);
|
|
|
8289
8138
|
errors: []
|
|
8290
8139
|
};
|
|
8291
8140
|
}
|
|
8292
|
-
|
|
8141
|
+
logger.info(`[${this.getProviderName()}] Adapting parameters for similar question...`);
|
|
8293
8142
|
const originalPrompt = conversationMatch.metadata?.userPrompt || "Previous question";
|
|
8294
8143
|
const adaptResult = await this.adaptUIBlockParameters(
|
|
8295
8144
|
userPrompt,
|
|
8296
8145
|
originalPrompt,
|
|
8297
8146
|
conversationMatch.uiBlock,
|
|
8298
8147
|
apiKey,
|
|
8299
|
-
logCollector,
|
|
8300
8148
|
cachedTextResponse
|
|
8301
8149
|
);
|
|
8302
8150
|
if (adaptResult.success && adaptResult.adaptedComponent) {
|
|
8303
8151
|
const elapsedTime2 = Date.now() - startTime;
|
|
8304
|
-
logger.info(`[${this.getProviderName()}] \u2713 Successfully adapted UI block parameters`);
|
|
8305
|
-
logger.info(`[${this.getProviderName()}] Total time taken: ${elapsedTime2}ms (${(elapsedTime2 / 1e3).toFixed(2)}s)`);
|
|
8306
|
-
logCollector?.info(`\u2713 UI block adapted successfully`);
|
|
8307
|
-
logCollector?.info(`Total time taken: ${elapsedTime2}ms (${(elapsedTime2 / 1e3).toFixed(2)}s)`);
|
|
8152
|
+
logger.info(`[${this.getProviderName()}] \u2713 Successfully adapted UI block parameters (${elapsedTime2}ms)`);
|
|
8308
8153
|
const textResponseToUse = adaptResult.adaptedTextResponse || cachedTextResponse;
|
|
8309
8154
|
if (streamCallback && textResponseToUse) {
|
|
8310
8155
|
logger.info(`[${this.getProviderName()}] Streaming ${adaptResult.adaptedTextResponse ? "adapted" : "cached"} text response to frontend`);
|
|
@@ -8325,65 +8170,57 @@ ${executedToolsText}`);
|
|
|
8325
8170
|
errors: []
|
|
8326
8171
|
};
|
|
8327
8172
|
} else {
|
|
8328
|
-
logger.info(`[${this.getProviderName()}] Could not adapt matched conversation, continuing to category classification`);
|
|
8329
|
-
logCollector?.warn(`Could not adapt matched conversation: ${adaptResult.explanation}`);
|
|
8173
|
+
logger.info(`[${this.getProviderName()}] Could not adapt matched conversation: ${adaptResult.explanation}, continuing to category classification`);
|
|
8330
8174
|
}
|
|
8331
8175
|
}
|
|
8332
8176
|
} else {
|
|
8333
8177
|
logger.info(`[${this.getProviderName()}] No matching previous conversations found, proceeding to category classification`);
|
|
8334
|
-
logCollector?.info("No similar previous conversations found. Proceeding to category classification...");
|
|
8335
8178
|
}
|
|
8336
8179
|
logger.info(`[${this.getProviderName()}] Step 2: Classifying question category...`);
|
|
8337
|
-
logCollector?.info("Step 2: Classifying question category...");
|
|
8338
8180
|
const categoryClassification = await this.classifyQuestionCategory(
|
|
8339
8181
|
userPrompt,
|
|
8340
8182
|
apiKey,
|
|
8341
|
-
logCollector,
|
|
8342
8183
|
conversationHistory,
|
|
8343
8184
|
externalTools
|
|
8344
8185
|
);
|
|
8345
8186
|
logger.info(
|
|
8346
8187
|
`[${this.getProviderName()}] Question classified as: ${categoryClassification.category} (confidence: ${categoryClassification.confidence}%)`
|
|
8347
8188
|
);
|
|
8348
|
-
logCollector?.info(
|
|
8349
|
-
`Category: ${categoryClassification.category} | Confidence: ${categoryClassification.confidence}%`
|
|
8350
|
-
);
|
|
8351
8189
|
let toolsToUse = [];
|
|
8352
8190
|
if (categoryClassification.externalTools && categoryClassification.externalTools.length > 0) {
|
|
8353
|
-
logger.info(`[${this.getProviderName()}] Identified ${categoryClassification.externalTools.length} external tools needed`);
|
|
8354
|
-
|
|
8355
|
-
|
|
8356
|
-
toolsToUse = categoryClassification.externalTools?.map((t) => {
|
|
8191
|
+
logger.info(`[${this.getProviderName()}] Identified ${categoryClassification.externalTools.length} external tools needed: ${categoryClassification.externalTools.map((t) => t.name || t.type).join(", ")}`);
|
|
8192
|
+
logger.debug(`[${this.getProviderName()}] Raw external tools from classification: ${JSON.stringify(categoryClassification.externalTools, null, 2)}`);
|
|
8193
|
+
toolsToUse = categoryClassification.externalTools.reduce((acc, t) => {
|
|
8357
8194
|
const realTool = externalTools?.find((tool) => tool.id === t.type);
|
|
8358
|
-
|
|
8359
|
-
|
|
8195
|
+
if (!realTool) {
|
|
8196
|
+
logger.warn(`[${this.getProviderName()}] Tool ${t.type} (${t.name}) not found in registered tools - skipping (likely hallucinated)`);
|
|
8197
|
+
return acc;
|
|
8198
|
+
}
|
|
8199
|
+
acc.push({
|
|
8360
8200
|
id: t.type,
|
|
8361
8201
|
name: t.name,
|
|
8362
8202
|
description: t.description,
|
|
8363
8203
|
params: t.parameters || {},
|
|
8364
|
-
//
|
|
8204
|
+
// Include execution type info from category classification
|
|
8365
8205
|
executionType: t.executionType || "immediate",
|
|
8366
8206
|
executionReason: t.executionReason || "",
|
|
8367
8207
|
requiredFields: t.requiredFields || [],
|
|
8368
8208
|
userProvidedData: t.userProvidedData || null,
|
|
8369
|
-
//
|
|
8370
|
-
outputSchema: realTool
|
|
8371
|
-
fn:
|
|
8372
|
-
|
|
8373
|
-
|
|
8374
|
-
|
|
8375
|
-
|
|
8376
|
-
|
|
8377
|
-
|
|
8378
|
-
|
|
8379
|
-
|
|
8380
|
-
};
|
|
8381
|
-
}) || [];
|
|
8209
|
+
// Include outputSchema from real tool for component config generation
|
|
8210
|
+
outputSchema: realTool.outputSchema,
|
|
8211
|
+
fn: realTool.fn
|
|
8212
|
+
});
|
|
8213
|
+
return acc;
|
|
8214
|
+
}, []);
|
|
8215
|
+
const validCount = toolsToUse.length;
|
|
8216
|
+
const hallucinatedCount = categoryClassification.externalTools.length - validCount;
|
|
8217
|
+
if (hallucinatedCount > 0) {
|
|
8218
|
+
logger.warn(`[${this.getProviderName()}] Filtered out ${hallucinatedCount} hallucinated/non-existent tools, ${validCount} valid tools remaining`);
|
|
8219
|
+
}
|
|
8382
8220
|
}
|
|
8383
8221
|
const textResponse = await this.generateTextResponse(
|
|
8384
8222
|
userPrompt,
|
|
8385
8223
|
apiKey,
|
|
8386
|
-
logCollector,
|
|
8387
8224
|
conversationHistory,
|
|
8388
8225
|
streamCallback,
|
|
8389
8226
|
collections,
|
|
@@ -8394,13 +8231,10 @@ ${executedToolsText}`);
|
|
|
8394
8231
|
);
|
|
8395
8232
|
const elapsedTime = Date.now() - startTime;
|
|
8396
8233
|
logger.info(`[${this.getProviderName()}] Total time taken: ${elapsedTime}ms (${(elapsedTime / 1e3).toFixed(2)}s)`);
|
|
8397
|
-
logCollector?.info(`Total time taken: ${elapsedTime}ms (${(elapsedTime / 1e3).toFixed(2)}s)`);
|
|
8398
8234
|
return textResponse;
|
|
8399
8235
|
} catch (error) {
|
|
8400
8236
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
8401
8237
|
logger.error(`[${this.getProviderName()}] Error in handleUserRequest: ${errorMsg}`);
|
|
8402
|
-
logger.debug(`[${this.getProviderName()}] Error details:`, error);
|
|
8403
|
-
logCollector?.error(`Error processing request: ${errorMsg}`);
|
|
8404
8238
|
userPromptErrorLogger.logError(
|
|
8405
8239
|
"handleUserRequest",
|
|
8406
8240
|
error instanceof Error ? error : new Error(errorMsg),
|
|
@@ -8408,7 +8242,6 @@ ${executedToolsText}`);
|
|
|
8408
8242
|
);
|
|
8409
8243
|
const elapsedTime = Date.now() - startTime;
|
|
8410
8244
|
logger.info(`[${this.getProviderName()}] Total time taken: ${elapsedTime}ms (${(elapsedTime / 1e3).toFixed(2)}s)`);
|
|
8411
|
-
logCollector?.info(`Total time taken: ${elapsedTime}ms (${(elapsedTime / 1e3).toFixed(2)}s)`);
|
|
8412
8245
|
return {
|
|
8413
8246
|
success: false,
|
|
8414
8247
|
errors: [errorMsg],
|
|
@@ -8424,7 +8257,7 @@ ${executedToolsText}`);
|
|
|
8424
8257
|
* This helps provide intelligent suggestions for follow-up queries
|
|
8425
8258
|
* For general/conversational questions without components, pass textResponse instead
|
|
8426
8259
|
*/
|
|
8427
|
-
async generateNextQuestions(originalUserPrompt, component, componentData, apiKey,
|
|
8260
|
+
async generateNextQuestions(originalUserPrompt, component, componentData, apiKey, conversationHistory, textResponse) {
|
|
8428
8261
|
const methodStartTime = Date.now();
|
|
8429
8262
|
const methodName = "generateNextQuestions";
|
|
8430
8263
|
const promptPreview = originalUserPrompt.substring(0, 50) + (originalUserPrompt.length > 50 ? "..." : "");
|
|
@@ -8469,14 +8302,6 @@ ${executedToolsText}`);
|
|
|
8469
8302
|
// Parse as JSON
|
|
8470
8303
|
);
|
|
8471
8304
|
const nextQuestions = result.nextQuestions || [];
|
|
8472
|
-
logCollector?.logExplanation(
|
|
8473
|
-
"Next questions generated",
|
|
8474
|
-
"Generated intelligent follow-up questions based on component",
|
|
8475
|
-
{
|
|
8476
|
-
count: nextQuestions.length,
|
|
8477
|
-
questions: nextQuestions
|
|
8478
|
-
}
|
|
8479
|
-
);
|
|
8480
8305
|
const methodDuration = Date.now() - methodStartTime;
|
|
8481
8306
|
logger.info(`[${this.getProviderName()}] [TIMING] DONE ${methodName} in ${methodDuration}ms | questions: ${nextQuestions.length}`);
|
|
8482
8307
|
return nextQuestions;
|
|
@@ -8484,8 +8309,6 @@ ${executedToolsText}`);
|
|
|
8484
8309
|
const methodDuration = Date.now() - methodStartTime;
|
|
8485
8310
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
8486
8311
|
logger.error(`[${this.getProviderName()}] [TIMING] FAILED ${methodName} in ${methodDuration}ms | error: ${errorMsg}`);
|
|
8487
|
-
logger.debug(`[${this.getProviderName()}] Next questions generation error details:`, error);
|
|
8488
|
-
logCollector?.error(`Error generating next questions: ${errorMsg}`);
|
|
8489
8312
|
return [];
|
|
8490
8313
|
}
|
|
8491
8314
|
}
|
|
@@ -8599,332 +8422,96 @@ function getLLMProviders() {
|
|
|
8599
8422
|
return DEFAULT_PROVIDERS;
|
|
8600
8423
|
}
|
|
8601
8424
|
}
|
|
8602
|
-
var useAnthropicMethod = async (prompt, components, apiKey,
|
|
8603
|
-
logger.debug("[useAnthropicMethod] Initializing Anthropic Claude matching method");
|
|
8604
|
-
logger.debug(`[useAnthropicMethod] Response mode: ${responseMode}`);
|
|
8605
|
-
const msg = `Using Anthropic Claude ${responseMode === "text" ? "text response" : "matching"} method...`;
|
|
8606
|
-
logCollector?.info(msg);
|
|
8425
|
+
var useAnthropicMethod = async (prompt, components, apiKey, conversationHistory, responseMode = "component", streamCallback, collections, externalTools, userId) => {
|
|
8607
8426
|
if (responseMode === "component" && components.length === 0) {
|
|
8608
8427
|
const emptyMsg = "Components not loaded in memory. Please ensure components are fetched first.";
|
|
8609
8428
|
logger.error("[useAnthropicMethod] No components available");
|
|
8610
|
-
logCollector?.error(emptyMsg);
|
|
8611
8429
|
return { success: false, errors: [emptyMsg] };
|
|
8612
8430
|
}
|
|
8613
|
-
|
|
8614
|
-
const matchResult = await anthropicLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory, responseMode, streamCallback, collections, externalTools, userId);
|
|
8431
|
+
const matchResult = await anthropicLLM.handleUserRequest(prompt, components, apiKey, conversationHistory, responseMode, streamCallback, collections, externalTools, userId);
|
|
8615
8432
|
logger.info(`[useAnthropicMethod] Successfully generated ${responseMode} using Anthropic`);
|
|
8616
8433
|
return matchResult;
|
|
8617
8434
|
};
|
|
8618
|
-
var useGroqMethod = async (prompt, components, apiKey,
|
|
8435
|
+
var useGroqMethod = async (prompt, components, apiKey, conversationHistory, responseMode = "component", streamCallback, collections, externalTools, userId) => {
|
|
8619
8436
|
logger.debug("[useGroqMethod] Initializing Groq LLM matching method");
|
|
8620
8437
|
logger.debug(`[useGroqMethod] Response mode: ${responseMode}`);
|
|
8621
|
-
const msg = `Using Groq LLM ${responseMode === "text" ? "text response" : "matching"} method...`;
|
|
8622
|
-
logger.info(msg);
|
|
8623
|
-
logCollector?.info(msg);
|
|
8624
8438
|
if (responseMode === "component" && components.length === 0) {
|
|
8625
8439
|
const emptyMsg = "Components not loaded in memory. Please ensure components are fetched first.";
|
|
8626
8440
|
logger.error("[useGroqMethod] No components available");
|
|
8627
|
-
logCollector?.error(emptyMsg);
|
|
8628
8441
|
return { success: false, errors: [emptyMsg] };
|
|
8629
8442
|
}
|
|
8630
8443
|
logger.debug(`[useGroqMethod] Processing with ${components.length} components`);
|
|
8631
|
-
const matchResult = await groqLLM.handleUserRequest(prompt, components, apiKey,
|
|
8444
|
+
const matchResult = await groqLLM.handleUserRequest(prompt, components, apiKey, conversationHistory, responseMode, streamCallback, collections, externalTools, userId);
|
|
8632
8445
|
logger.info(`[useGroqMethod] Successfully generated ${responseMode} using Groq`);
|
|
8633
8446
|
return matchResult;
|
|
8634
8447
|
};
|
|
8635
|
-
var useGeminiMethod = async (prompt, components, apiKey,
|
|
8448
|
+
var useGeminiMethod = async (prompt, components, apiKey, conversationHistory, responseMode = "component", streamCallback, collections, externalTools, userId) => {
|
|
8636
8449
|
logger.debug("[useGeminiMethod] Initializing Gemini LLM matching method");
|
|
8637
8450
|
logger.debug(`[useGeminiMethod] Response mode: ${responseMode}`);
|
|
8638
|
-
const msg = `Using Gemini LLM ${responseMode === "text" ? "text response" : "matching"} method...`;
|
|
8639
|
-
logger.info(msg);
|
|
8640
|
-
logCollector?.info(msg);
|
|
8641
8451
|
if (responseMode === "component" && components.length === 0) {
|
|
8642
8452
|
const emptyMsg = "Components not loaded in memory. Please ensure components are fetched first.";
|
|
8643
8453
|
logger.error("[useGeminiMethod] No components available");
|
|
8644
|
-
logCollector?.error(emptyMsg);
|
|
8645
8454
|
return { success: false, errors: [emptyMsg] };
|
|
8646
8455
|
}
|
|
8647
8456
|
logger.debug(`[useGeminiMethod] Processing with ${components.length} components`);
|
|
8648
|
-
const matchResult = await geminiLLM.handleUserRequest(prompt, components, apiKey,
|
|
8457
|
+
const matchResult = await geminiLLM.handleUserRequest(prompt, components, apiKey, conversationHistory, responseMode, streamCallback, collections, externalTools, userId);
|
|
8649
8458
|
logger.info(`[useGeminiMethod] Successfully generated ${responseMode} using Gemini`);
|
|
8650
8459
|
return matchResult;
|
|
8651
8460
|
};
|
|
8652
|
-
var useOpenAIMethod = async (prompt, components, apiKey,
|
|
8461
|
+
var useOpenAIMethod = async (prompt, components, apiKey, conversationHistory, responseMode = "component", streamCallback, collections, externalTools, userId) => {
|
|
8653
8462
|
logger.debug("[useOpenAIMethod] Initializing OpenAI GPT matching method");
|
|
8654
8463
|
logger.debug(`[useOpenAIMethod] Response mode: ${responseMode}`);
|
|
8655
|
-
const msg = `Using OpenAI GPT ${responseMode === "text" ? "text response" : "matching"} method...`;
|
|
8656
|
-
logger.info(msg);
|
|
8657
|
-
logCollector?.info(msg);
|
|
8658
8464
|
if (responseMode === "component" && components.length === 0) {
|
|
8659
8465
|
const emptyMsg = "Components not loaded in memory. Please ensure components are fetched first.";
|
|
8660
8466
|
logger.error("[useOpenAIMethod] No components available");
|
|
8661
|
-
logCollector?.error(emptyMsg);
|
|
8662
8467
|
return { success: false, errors: [emptyMsg] };
|
|
8663
8468
|
}
|
|
8664
8469
|
logger.debug(`[useOpenAIMethod] Processing with ${components.length} components`);
|
|
8665
|
-
const matchResult = await openaiLLM.handleUserRequest(prompt, components, apiKey,
|
|
8470
|
+
const matchResult = await openaiLLM.handleUserRequest(prompt, components, apiKey, conversationHistory, responseMode, streamCallback, collections, externalTools, userId);
|
|
8666
8471
|
logger.info(`[useOpenAIMethod] Successfully generated ${responseMode} using OpenAI`);
|
|
8667
8472
|
return matchResult;
|
|
8668
8473
|
};
|
|
8669
|
-
var
|
|
8670
|
-
return false;
|
|
8671
|
-
};
|
|
8672
|
-
var get_user_response = async (prompt, components, anthropicApiKey, groqApiKey, geminiApiKey, openaiApiKey, llmProviders, logCollector, conversationHistory, responseMode = "component", streamCallback, collections, externalTools, userId) => {
|
|
8673
|
-
logger.debug(`[get_user_response] Starting user response generation for prompt: "${prompt.substring(0, 50)}..."`);
|
|
8674
|
-
logger.debug(`[get_user_response] Response mode: ${responseMode}`);
|
|
8675
|
-
logger.debug("[get_user_response] Checking cache for existing response");
|
|
8676
|
-
const userResponse = await getUserResponseFromCache(prompt);
|
|
8677
|
-
if (userResponse) {
|
|
8678
|
-
logger.info("[get_user_response] User response found in cache - returning cached result");
|
|
8679
|
-
logCollector?.info("User response found in cache");
|
|
8680
|
-
return {
|
|
8681
|
-
success: true,
|
|
8682
|
-
data: userResponse,
|
|
8683
|
-
errors: []
|
|
8684
|
-
};
|
|
8685
|
-
}
|
|
8686
|
-
logger.debug("[get_user_response] No cached response found, proceeding with LLM providers");
|
|
8474
|
+
var get_user_response = async (prompt, components, anthropicApiKey, groqApiKey, geminiApiKey, openaiApiKey, llmProviders, conversationHistory, responseMode = "component", streamCallback, collections, externalTools, userId) => {
|
|
8687
8475
|
const providers = llmProviders || getLLMProviders();
|
|
8688
8476
|
const errors = [];
|
|
8689
|
-
|
|
8690
|
-
logCollector?.info(`LLM Provider order: [${providerOrder}]`);
|
|
8691
|
-
if (conversationHistory && conversationHistory.length > 0) {
|
|
8692
|
-
const exchangeCount = conversationHistory.split("\n").filter((l) => l.startsWith("Q")).length;
|
|
8693
|
-
logger.debug(`[get_user_response] Using conversation history with ${exchangeCount} previous exchanges`);
|
|
8694
|
-
logCollector?.info(`Using conversation history with ${exchangeCount} previous exchanges`);
|
|
8695
|
-
} else {
|
|
8696
|
-
logger.debug("[get_user_response] No conversation history available");
|
|
8697
|
-
}
|
|
8477
|
+
logger.info(`[get_user_response] LLM Provider order: [${providers.join(", ")}]`);
|
|
8698
8478
|
for (let i = 0; i < providers.length; i++) {
|
|
8699
8479
|
const provider = providers[i];
|
|
8700
8480
|
const isLastProvider = i === providers.length - 1;
|
|
8701
|
-
|
|
8702
|
-
logCollector?.info(attemptMsg);
|
|
8481
|
+
logger.info(`[get_user_response] Attempting provider: ${provider} (${i + 1}/${providers.length})`);
|
|
8703
8482
|
let result;
|
|
8704
8483
|
if (provider === "anthropic") {
|
|
8705
|
-
result = await useAnthropicMethod(prompt, components, anthropicApiKey,
|
|
8484
|
+
result = await useAnthropicMethod(prompt, components, anthropicApiKey, conversationHistory, responseMode, streamCallback, collections, externalTools, userId);
|
|
8706
8485
|
} else if (provider === "groq") {
|
|
8707
|
-
result = await useGroqMethod(prompt, components, groqApiKey,
|
|
8486
|
+
result = await useGroqMethod(prompt, components, groqApiKey, conversationHistory, responseMode, streamCallback, collections, externalTools, userId);
|
|
8708
8487
|
} else if (provider === "gemini") {
|
|
8709
|
-
result = await useGeminiMethod(prompt, components, geminiApiKey,
|
|
8488
|
+
result = await useGeminiMethod(prompt, components, geminiApiKey, conversationHistory, responseMode, streamCallback, collections, externalTools, userId);
|
|
8710
8489
|
} else if (provider === "openai") {
|
|
8711
|
-
result = await useOpenAIMethod(prompt, components, openaiApiKey,
|
|
8490
|
+
result = await useOpenAIMethod(prompt, components, openaiApiKey, conversationHistory, responseMode, streamCallback, collections, externalTools, userId);
|
|
8712
8491
|
} else {
|
|
8713
8492
|
logger.warn(`[get_user_response] Unknown provider: ${provider} - skipping`);
|
|
8714
8493
|
errors.push(`Unknown provider: ${provider}`);
|
|
8715
8494
|
continue;
|
|
8716
8495
|
}
|
|
8717
8496
|
if (result.success) {
|
|
8718
|
-
|
|
8719
|
-
logger.info(`${successMsg}`);
|
|
8720
|
-
logCollector?.info(successMsg);
|
|
8497
|
+
logger.info(`[get_user_response] Success with provider: ${provider}`);
|
|
8721
8498
|
return result;
|
|
8722
8499
|
} else {
|
|
8723
8500
|
const providerErrors = result.errors.map((err) => `${provider}: ${err}`);
|
|
8724
8501
|
errors.push(...providerErrors);
|
|
8725
|
-
|
|
8726
|
-
logger.warn(`[get_user_response] ${warnMsg}`);
|
|
8727
|
-
logCollector?.warn(warnMsg);
|
|
8502
|
+
logger.warn(`[get_user_response] Provider ${provider} returned unsuccessful result: ${result.errors.join(", ")}`);
|
|
8728
8503
|
if (!isLastProvider) {
|
|
8729
|
-
|
|
8730
|
-
logger.info(`[get_user_response] ${fallbackMsg}`);
|
|
8731
|
-
logCollector?.info(fallbackMsg);
|
|
8504
|
+
logger.info("[get_user_response] Falling back to next provider...");
|
|
8732
8505
|
}
|
|
8733
8506
|
}
|
|
8734
8507
|
}
|
|
8735
|
-
|
|
8736
|
-
logger.error(`[get_user_response] ${failureMsg}. Errors: ${errors.join("; ")}`);
|
|
8737
|
-
logCollector?.error(`${failureMsg}. Errors: ${errors.join("; ")}`);
|
|
8508
|
+
logger.error(`[get_user_response] All LLM providers failed. Errors: ${errors.join("; ")}`);
|
|
8738
8509
|
return {
|
|
8739
8510
|
success: false,
|
|
8740
8511
|
errors
|
|
8741
8512
|
};
|
|
8742
8513
|
};
|
|
8743
8514
|
|
|
8744
|
-
// src/utils/log-collector.ts
|
|
8745
|
-
var LOG_LEVEL_PRIORITY2 = {
|
|
8746
|
-
errors: 0,
|
|
8747
|
-
warnings: 1,
|
|
8748
|
-
info: 2,
|
|
8749
|
-
verbose: 3
|
|
8750
|
-
};
|
|
8751
|
-
var MESSAGE_LEVEL_PRIORITY2 = {
|
|
8752
|
-
error: 0,
|
|
8753
|
-
warn: 1,
|
|
8754
|
-
info: 2,
|
|
8755
|
-
debug: 3
|
|
8756
|
-
};
|
|
8757
|
-
var UILogCollector = class {
|
|
8758
|
-
constructor(clientId, sendMessage, uiBlockId) {
|
|
8759
|
-
this.logs = [];
|
|
8760
|
-
this.uiBlockId = uiBlockId || null;
|
|
8761
|
-
this.clientId = clientId;
|
|
8762
|
-
this.sendMessage = sendMessage;
|
|
8763
|
-
this.currentLogLevel = logger.getLogLevel();
|
|
8764
|
-
}
|
|
8765
|
-
/**
|
|
8766
|
-
* Check if logging is enabled (uiBlockId is provided)
|
|
8767
|
-
*/
|
|
8768
|
-
isEnabled() {
|
|
8769
|
-
return this.uiBlockId !== null;
|
|
8770
|
-
}
|
|
8771
|
-
/**
|
|
8772
|
-
* Check if a message should be logged based on current log level
|
|
8773
|
-
*/
|
|
8774
|
-
shouldLog(messageLevel) {
|
|
8775
|
-
const currentLevelPriority = LOG_LEVEL_PRIORITY2[this.currentLogLevel];
|
|
8776
|
-
const messagePriority = MESSAGE_LEVEL_PRIORITY2[messageLevel];
|
|
8777
|
-
return messagePriority <= currentLevelPriority;
|
|
8778
|
-
}
|
|
8779
|
-
/**
|
|
8780
|
-
* Add a log entry with timestamp and immediately send to runtime
|
|
8781
|
-
* Only logs that pass the log level filter are captured and sent
|
|
8782
|
-
*/
|
|
8783
|
-
addLog(level, message, type, data) {
|
|
8784
|
-
if (!this.shouldLog(level)) {
|
|
8785
|
-
return;
|
|
8786
|
-
}
|
|
8787
|
-
const log = {
|
|
8788
|
-
timestamp: Date.now(),
|
|
8789
|
-
level,
|
|
8790
|
-
message,
|
|
8791
|
-
...type && { type },
|
|
8792
|
-
...data && { data }
|
|
8793
|
-
};
|
|
8794
|
-
this.logs.push(log);
|
|
8795
|
-
this.sendLogImmediately(log);
|
|
8796
|
-
switch (level) {
|
|
8797
|
-
case "error":
|
|
8798
|
-
logger.error("UILogCollector:", log);
|
|
8799
|
-
break;
|
|
8800
|
-
case "warn":
|
|
8801
|
-
logger.warn("UILogCollector:", log);
|
|
8802
|
-
break;
|
|
8803
|
-
case "info":
|
|
8804
|
-
logger.info("UILogCollector:", log);
|
|
8805
|
-
break;
|
|
8806
|
-
case "debug":
|
|
8807
|
-
logger.debug("UILogCollector:", log);
|
|
8808
|
-
break;
|
|
8809
|
-
}
|
|
8810
|
-
}
|
|
8811
|
-
/**
|
|
8812
|
-
* Send a single log to runtime immediately
|
|
8813
|
-
*/
|
|
8814
|
-
sendLogImmediately(log) {
|
|
8815
|
-
if (!this.isEnabled()) {
|
|
8816
|
-
return;
|
|
8817
|
-
}
|
|
8818
|
-
const response = {
|
|
8819
|
-
id: this.uiBlockId,
|
|
8820
|
-
type: "UI_LOGS",
|
|
8821
|
-
from: { type: "data-agent" },
|
|
8822
|
-
to: {
|
|
8823
|
-
type: "runtime",
|
|
8824
|
-
id: this.clientId
|
|
8825
|
-
},
|
|
8826
|
-
payload: {
|
|
8827
|
-
logs: [log]
|
|
8828
|
-
// Send single log in array
|
|
8829
|
-
}
|
|
8830
|
-
};
|
|
8831
|
-
this.sendMessage(response);
|
|
8832
|
-
}
|
|
8833
|
-
/**
|
|
8834
|
-
* Log info message
|
|
8835
|
-
*/
|
|
8836
|
-
info(message, type, data) {
|
|
8837
|
-
if (this.isEnabled()) {
|
|
8838
|
-
this.addLog("info", message, type, data);
|
|
8839
|
-
}
|
|
8840
|
-
}
|
|
8841
|
-
/**
|
|
8842
|
-
* Log error message
|
|
8843
|
-
*/
|
|
8844
|
-
error(message, type, data) {
|
|
8845
|
-
if (this.isEnabled()) {
|
|
8846
|
-
this.addLog("error", message, type, data);
|
|
8847
|
-
}
|
|
8848
|
-
}
|
|
8849
|
-
/**
|
|
8850
|
-
* Log warning message
|
|
8851
|
-
*/
|
|
8852
|
-
warn(message, type, data) {
|
|
8853
|
-
if (this.isEnabled()) {
|
|
8854
|
-
this.addLog("warn", message, type, data);
|
|
8855
|
-
}
|
|
8856
|
-
}
|
|
8857
|
-
/**
|
|
8858
|
-
* Log debug message
|
|
8859
|
-
*/
|
|
8860
|
-
debug(message, type, data) {
|
|
8861
|
-
if (this.isEnabled()) {
|
|
8862
|
-
this.addLog("debug", message, type, data);
|
|
8863
|
-
}
|
|
8864
|
-
}
|
|
8865
|
-
/**
|
|
8866
|
-
* Log LLM explanation with typed metadata
|
|
8867
|
-
*/
|
|
8868
|
-
logExplanation(message, explanation, data) {
|
|
8869
|
-
if (this.isEnabled()) {
|
|
8870
|
-
this.addLog("info", message, "explanation", {
|
|
8871
|
-
explanation,
|
|
8872
|
-
...data
|
|
8873
|
-
});
|
|
8874
|
-
}
|
|
8875
|
-
}
|
|
8876
|
-
/**
|
|
8877
|
-
* Log generated query with typed metadata
|
|
8878
|
-
*/
|
|
8879
|
-
logQuery(message, query, data) {
|
|
8880
|
-
if (this.isEnabled()) {
|
|
8881
|
-
this.addLog("info", message, "query", {
|
|
8882
|
-
query,
|
|
8883
|
-
...data
|
|
8884
|
-
});
|
|
8885
|
-
}
|
|
8886
|
-
}
|
|
8887
|
-
/**
|
|
8888
|
-
* Send all collected logs at once (optional, for final summary)
|
|
8889
|
-
*/
|
|
8890
|
-
sendAllLogs() {
|
|
8891
|
-
if (!this.isEnabled() || this.logs.length === 0) {
|
|
8892
|
-
return;
|
|
8893
|
-
}
|
|
8894
|
-
const response = {
|
|
8895
|
-
id: this.uiBlockId,
|
|
8896
|
-
type: "UI_LOGS",
|
|
8897
|
-
from: { type: "data-agent" },
|
|
8898
|
-
to: {
|
|
8899
|
-
type: "runtime",
|
|
8900
|
-
id: this.clientId
|
|
8901
|
-
},
|
|
8902
|
-
payload: {
|
|
8903
|
-
logs: this.logs
|
|
8904
|
-
}
|
|
8905
|
-
};
|
|
8906
|
-
this.sendMessage(response);
|
|
8907
|
-
}
|
|
8908
|
-
/**
|
|
8909
|
-
* Get all collected logs
|
|
8910
|
-
*/
|
|
8911
|
-
getLogs() {
|
|
8912
|
-
return [...this.logs];
|
|
8913
|
-
}
|
|
8914
|
-
/**
|
|
8915
|
-
* Clear all logs
|
|
8916
|
-
*/
|
|
8917
|
-
clearLogs() {
|
|
8918
|
-
this.logs = [];
|
|
8919
|
-
}
|
|
8920
|
-
/**
|
|
8921
|
-
* Set uiBlockId (in case it's provided later)
|
|
8922
|
-
*/
|
|
8923
|
-
setUIBlockId(uiBlockId) {
|
|
8924
|
-
this.uiBlockId = uiBlockId;
|
|
8925
|
-
}
|
|
8926
|
-
};
|
|
8927
|
-
|
|
8928
8515
|
// src/utils/conversation-saver.ts
|
|
8929
8516
|
function transformUIBlockForDB(uiblock, userPrompt, uiBlockId) {
|
|
8930
8517
|
const component = uiblock?.generatedComponentMetadata && Object.keys(uiblock.generatedComponentMetadata).length > 0 ? uiblock.generatedComponentMetadata : null;
|
|
@@ -9055,7 +8642,6 @@ var CONTEXT_CONFIG = {
|
|
|
9055
8642
|
// src/handlers/user-prompt-request.ts
|
|
9056
8643
|
var get_user_request = async (data, components, sendMessage, anthropicApiKey, groqApiKey, geminiApiKey, openaiApiKey, llmProviders, collections, externalTools) => {
|
|
9057
8644
|
const errors = [];
|
|
9058
|
-
logger.debug("[USER_PROMPT_REQ] Parsing incoming message data");
|
|
9059
8645
|
const parseResult = UserPromptRequestMessageSchema.safeParse(data);
|
|
9060
8646
|
if (!parseResult.success) {
|
|
9061
8647
|
const zodError = parseResult.error;
|
|
@@ -9087,27 +8673,23 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
|
|
|
9087
8673
|
if (!prompt) {
|
|
9088
8674
|
errors.push("Prompt not found");
|
|
9089
8675
|
}
|
|
9090
|
-
logger.debug(`[REQUEST ${id}] Full request details - uiBlockId: ${existingUiBlockId}, threadId: ${threadId}, prompt: ${prompt}`);
|
|
9091
8676
|
if (errors.length > 0) {
|
|
9092
8677
|
return { success: false, errors, id, wsId };
|
|
9093
8678
|
}
|
|
9094
|
-
const logCollector = new UILogCollector(wsId, sendMessage, existingUiBlockId);
|
|
9095
8679
|
const threadManager = ThreadManager.getInstance();
|
|
9096
8680
|
let thread = threadManager.getThread(threadId);
|
|
9097
8681
|
if (!thread) {
|
|
9098
8682
|
thread = threadManager.createThread(threadId);
|
|
9099
8683
|
logger.info(`Created new thread: ${threadId}`);
|
|
9100
8684
|
}
|
|
9101
|
-
|
|
8685
|
+
logger.info(`Starting user prompt request with ${components.length} components`);
|
|
9102
8686
|
const conversationHistory = thread.getConversationContext(CONTEXT_CONFIG.MAX_CONVERSATION_CONTEXT_BLOCKS, existingUiBlockId);
|
|
9103
8687
|
const responseMode = payload.responseMode || "component";
|
|
9104
|
-
logger.info("responseMode", responseMode);
|
|
9105
8688
|
let streamCallback;
|
|
9106
8689
|
let accumulatedStreamResponse = "";
|
|
9107
8690
|
if (responseMode === "text") {
|
|
9108
8691
|
streamCallback = (chunk) => {
|
|
9109
8692
|
accumulatedStreamResponse += chunk;
|
|
9110
|
-
logger.debug(`[STREAM] Sending chunk (${chunk.length} chars): "${chunk.substring(0, 20)}..."`);
|
|
9111
8693
|
const streamMessage = {
|
|
9112
8694
|
id: `stream_${existingUiBlockId}`,
|
|
9113
8695
|
// Different ID pattern for streaming
|
|
@@ -9123,7 +8705,6 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
|
|
|
9123
8705
|
}
|
|
9124
8706
|
};
|
|
9125
8707
|
sendMessage(streamMessage);
|
|
9126
|
-
logger.debug(`[STREAM] Chunk sent to wsId: ${wsId}`);
|
|
9127
8708
|
};
|
|
9128
8709
|
}
|
|
9129
8710
|
const userResponse = await get_user_response(
|
|
@@ -9134,7 +8715,6 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
|
|
|
9134
8715
|
geminiApiKey,
|
|
9135
8716
|
openaiApiKey,
|
|
9136
8717
|
llmProviders,
|
|
9137
|
-
logCollector,
|
|
9138
8718
|
conversationHistory,
|
|
9139
8719
|
responseMode,
|
|
9140
8720
|
streamCallback,
|
|
@@ -9142,7 +8722,7 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
|
|
|
9142
8722
|
externalTools,
|
|
9143
8723
|
userId
|
|
9144
8724
|
);
|
|
9145
|
-
|
|
8725
|
+
logger.info("User prompt request completed");
|
|
9146
8726
|
const uiBlockId = existingUiBlockId;
|
|
9147
8727
|
if (!userResponse.success) {
|
|
9148
8728
|
logger.error(`User prompt request failed with errors: ${userResponse.errors.join(", ")}`);
|
|
@@ -9209,9 +8789,6 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
|
|
|
9209
8789
|
logger.info(
|
|
9210
8790
|
`Skipping conversation save - response from exact semantic match (${(semanticSimilarity * 100).toFixed(2)}% similarity)`
|
|
9211
8791
|
);
|
|
9212
|
-
logCollector.info(
|
|
9213
|
-
`Using exact cached result (${(semanticSimilarity * 100).toFixed(2)}% match) - not saving duplicate conversation`
|
|
9214
|
-
);
|
|
9215
8792
|
} else {
|
|
9216
8793
|
const uiBlockData = uiBlock.toJSON();
|
|
9217
8794
|
const saveResult = await saveConversation({
|
|
@@ -9419,7 +8996,7 @@ function sendResponse(id, res, sendMessage, clientId) {
|
|
|
9419
8996
|
}
|
|
9420
8997
|
|
|
9421
8998
|
// src/userResponse/next-questions.ts
|
|
9422
|
-
async function generateNextQuestions(originalUserPrompt, component, componentData, anthropicApiKey, groqApiKey, geminiApiKey, openaiApiKey, llmProviders,
|
|
8999
|
+
async function generateNextQuestions(originalUserPrompt, component, componentData, anthropicApiKey, groqApiKey, geminiApiKey, openaiApiKey, llmProviders, conversationHistory) {
|
|
9423
9000
|
try {
|
|
9424
9001
|
logger.debug("[generateNextQuestions] Starting next questions generation");
|
|
9425
9002
|
logger.debug(`[generateNextQuestions] User prompt: "${originalUserPrompt?.substring(0, 50)}..."`);
|
|
@@ -9438,7 +9015,6 @@ async function generateNextQuestions(originalUserPrompt, component, componentDat
|
|
|
9438
9015
|
const isLastProvider = i === providers.length - 1;
|
|
9439
9016
|
try {
|
|
9440
9017
|
logger.info(`[generateNextQuestions] Attempting provider: ${provider} (${i + 1}/${providers.length})`);
|
|
9441
|
-
logCollector?.info(`Generating questions with ${provider}...`);
|
|
9442
9018
|
let result = [];
|
|
9443
9019
|
if (provider === "groq") {
|
|
9444
9020
|
logger.debug("[generateNextQuestions] Using Groq LLM for next questions");
|
|
@@ -9447,7 +9023,6 @@ async function generateNextQuestions(originalUserPrompt, component, componentDat
|
|
|
9447
9023
|
component,
|
|
9448
9024
|
componentData,
|
|
9449
9025
|
groqApiKey,
|
|
9450
|
-
logCollector,
|
|
9451
9026
|
conversationHistory
|
|
9452
9027
|
);
|
|
9453
9028
|
} else if (provider === "gemini") {
|
|
@@ -9457,7 +9032,6 @@ async function generateNextQuestions(originalUserPrompt, component, componentDat
|
|
|
9457
9032
|
component,
|
|
9458
9033
|
componentData,
|
|
9459
9034
|
geminiApiKey,
|
|
9460
|
-
logCollector,
|
|
9461
9035
|
conversationHistory
|
|
9462
9036
|
);
|
|
9463
9037
|
} else if (provider === "openai") {
|
|
@@ -9467,7 +9041,6 @@ async function generateNextQuestions(originalUserPrompt, component, componentDat
|
|
|
9467
9041
|
component,
|
|
9468
9042
|
componentData,
|
|
9469
9043
|
openaiApiKey,
|
|
9470
|
-
logCollector,
|
|
9471
9044
|
conversationHistory
|
|
9472
9045
|
);
|
|
9473
9046
|
} else {
|
|
@@ -9477,44 +9050,32 @@ async function generateNextQuestions(originalUserPrompt, component, componentDat
|
|
|
9477
9050
|
component,
|
|
9478
9051
|
componentData,
|
|
9479
9052
|
anthropicApiKey,
|
|
9480
|
-
logCollector,
|
|
9481
9053
|
conversationHistory
|
|
9482
9054
|
);
|
|
9483
9055
|
}
|
|
9484
9056
|
if (result && result.length > 0) {
|
|
9485
9057
|
logger.info(`[generateNextQuestions] Successfully generated ${result.length} questions with ${provider}`);
|
|
9486
9058
|
logger.debug(`[generateNextQuestions] Questions: ${JSON.stringify(result)}`);
|
|
9487
|
-
logCollector?.info(`Generated ${result.length} follow-up questions`);
|
|
9488
9059
|
return result;
|
|
9489
9060
|
}
|
|
9490
|
-
|
|
9491
|
-
logger.warn(`[generateNextQuestions] ${warnMsg}`);
|
|
9492
|
-
if (!isLastProvider) {
|
|
9493
|
-
logCollector?.warn(warnMsg);
|
|
9494
|
-
}
|
|
9061
|
+
logger.warn(`[generateNextQuestions] No questions generated from ${provider}${!isLastProvider ? ", trying next provider..." : ""}`);
|
|
9495
9062
|
} catch (providerError) {
|
|
9496
9063
|
const errorMsg = providerError instanceof Error ? providerError.message : String(providerError);
|
|
9497
9064
|
logger.error(`[generateNextQuestions] Provider ${provider} failed: ${errorMsg}`);
|
|
9498
9065
|
logger.debug(`[generateNextQuestions] Provider error details:`, providerError);
|
|
9499
9066
|
if (!isLastProvider) {
|
|
9500
|
-
|
|
9501
|
-
logger.info(`[generateNextQuestions] ${fallbackMsg}`);
|
|
9502
|
-
logCollector?.warn(fallbackMsg);
|
|
9503
|
-
} else {
|
|
9504
|
-
logCollector?.error(`Failed to generate questions with ${provider}`);
|
|
9067
|
+
logger.info(`[generateNextQuestions] Provider ${provider} failed, trying next provider...`);
|
|
9505
9068
|
}
|
|
9506
9069
|
continue;
|
|
9507
9070
|
}
|
|
9508
9071
|
}
|
|
9509
9072
|
logger.warn("[generateNextQuestions] All providers failed or returned no questions");
|
|
9510
|
-
logCollector?.warn("Unable to generate follow-up questions");
|
|
9511
9073
|
return [];
|
|
9512
9074
|
} catch (error) {
|
|
9513
9075
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
9514
9076
|
const errorStack = error instanceof Error ? error.stack : void 0;
|
|
9515
9077
|
logger.error(`[generateNextQuestions] Error generating next questions: ${errorMsg}`);
|
|
9516
9078
|
logger.debug("[generateNextQuestions] Error stack trace:", errorStack);
|
|
9517
|
-
logCollector?.error(`Error generating next questions: ${errorMsg}`);
|
|
9518
9079
|
return [];
|
|
9519
9080
|
}
|
|
9520
9081
|
}
|
|
@@ -9562,9 +9123,6 @@ async function handleActionsRequest(data, sendMessage, anthropicApiKey, groqApiK
|
|
|
9562
9123
|
return;
|
|
9563
9124
|
}
|
|
9564
9125
|
logger.info(`[ACTIONS_REQ ${id}] UIBlock retrieved successfully`);
|
|
9565
|
-
logger.debug(`[ACTIONS_REQ ${id}] Creating UILogCollector for uiBlockId: ${uiBlockId}`);
|
|
9566
|
-
const logCollector = new UILogCollector(wsId, sendMessage, uiBlockId);
|
|
9567
|
-
logger.info(`[ACTIONS_REQ ${id}] UILogCollector initialized`);
|
|
9568
9126
|
logger.debug(`[ACTIONS_REQ ${id}] Extracting data from UIBlock`);
|
|
9569
9127
|
const userQuestion = uiBlock.getUserQuestion();
|
|
9570
9128
|
const component = uiBlock.getComponentMetadata();
|
|
@@ -9578,13 +9136,11 @@ async function handleActionsRequest(data, sendMessage, anthropicApiKey, groqApiK
|
|
|
9578
9136
|
logger.info(`[ACTIONS_REQ ${id}] Conversation history extracted: ${historyLineCount} lines`);
|
|
9579
9137
|
logger.debug(`[ACTIONS_REQ ${id}] Conversation history preview:
|
|
9580
9138
|
${conversationHistory.substring(0, 200)}...`);
|
|
9581
|
-
|
|
9582
|
-
logger.info(`[ACTIONS_REQ ${id}] Generating actions for component: ${component?.name || "unknown"}`);
|
|
9139
|
+
logger.info(`[ACTIONS_REQ ${id}] Generating actions for UIBlock: ${uiBlockId}, component: ${component?.name || "unknown"}`);
|
|
9583
9140
|
logger.debug(`[ACTIONS_REQ ${id}] Checking if actions are already cached`);
|
|
9584
9141
|
const startTime = Date.now();
|
|
9585
9142
|
const actions = await uiBlock.getOrFetchActions(async () => {
|
|
9586
9143
|
logger.info(`[ACTIONS_REQ ${id}] Actions not cached, generating new actions...`);
|
|
9587
|
-
logCollector.info("Generating follow-up questions...");
|
|
9588
9144
|
logger.info(`[ACTIONS_REQ ${id}] Starting next questions generation with ${llmProviders?.join(", ") || "default"} providers`);
|
|
9589
9145
|
const nextQuestions = await generateNextQuestions(
|
|
9590
9146
|
userQuestion,
|
|
@@ -9595,7 +9151,6 @@ ${conversationHistory.substring(0, 200)}...`);
|
|
|
9595
9151
|
geminiApiKey,
|
|
9596
9152
|
openaiApiKey,
|
|
9597
9153
|
llmProviders,
|
|
9598
|
-
logCollector,
|
|
9599
9154
|
conversationHistory
|
|
9600
9155
|
);
|
|
9601
9156
|
logger.info(`[ACTIONS_REQ ${id}] Generated ${nextQuestions.length} questions`);
|
|
@@ -9613,11 +9168,10 @@ ${conversationHistory.substring(0, 200)}...`);
|
|
|
9613
9168
|
const processingTime = Date.now() - startTime;
|
|
9614
9169
|
logger.info(`[ACTIONS_REQ ${id}] Actions retrieved in ${processingTime}ms - ${actions.length} actions total`);
|
|
9615
9170
|
if (actions.length > 0) {
|
|
9616
|
-
|
|
9171
|
+
logger.info(`[ACTIONS_REQ ${id}] Generated ${actions.length} follow-up questions successfully`);
|
|
9617
9172
|
logger.debug(`[ACTIONS_REQ ${id}] Actions: ${actions.map((a) => a.name).join(", ")}`);
|
|
9618
9173
|
} else {
|
|
9619
9174
|
logger.warn(`[ACTIONS_REQ ${id}] No actions generated`);
|
|
9620
|
-
logCollector.warn("No follow-up questions could be generated");
|
|
9621
9175
|
}
|
|
9622
9176
|
logger.debug(`[ACTIONS_REQ ${id}] Sending successful response to client`);
|
|
9623
9177
|
sendResponse2(id, {
|
|
@@ -9636,15 +9190,6 @@ ${conversationHistory.substring(0, 200)}...`);
|
|
|
9636
9190
|
const errorStack = error instanceof Error ? error.stack : void 0;
|
|
9637
9191
|
logger.error(`[ACTIONS_REQ] Failed to handle actions request: ${errorMessage}`);
|
|
9638
9192
|
logger.debug(`[ACTIONS_REQ] Error stack trace:`, errorStack);
|
|
9639
|
-
try {
|
|
9640
|
-
const parsedData = data;
|
|
9641
|
-
if (parsedData?.id && parsedData?.from?.id) {
|
|
9642
|
-
const logCollector = parsedData?.payload?.SA_RUNTIME?.uiBlockId ? new UILogCollector(parsedData.from.id, sendMessage, parsedData.payload.SA_RUNTIME.uiBlockId) : void 0;
|
|
9643
|
-
logCollector?.error(`Failed to generate actions: ${errorMessage}`);
|
|
9644
|
-
}
|
|
9645
|
-
} catch (logError) {
|
|
9646
|
-
logger.debug("[ACTIONS_REQ] Failed to send error logs to UI:", logError);
|
|
9647
|
-
}
|
|
9648
9193
|
sendResponse2(null, {
|
|
9649
9194
|
success: false,
|
|
9650
9195
|
error: errorMessage
|
|
@@ -10225,7 +9770,6 @@ function sendResponse3(id, res, sendMessage, clientId) {
|
|
|
10225
9770
|
var dashboardManager = null;
|
|
10226
9771
|
function setDashboardManager(manager) {
|
|
10227
9772
|
dashboardManager = manager;
|
|
10228
|
-
logger.info("DashboardManager instance set");
|
|
10229
9773
|
}
|
|
10230
9774
|
function getDashboardManager() {
|
|
10231
9775
|
if (!dashboardManager) {
|
|
@@ -13552,6 +13096,190 @@ var ReportManager = class {
|
|
|
13552
13096
|
}
|
|
13553
13097
|
};
|
|
13554
13098
|
|
|
13099
|
+
// src/utils/log-collector.ts
|
|
13100
|
+
var LOG_LEVEL_PRIORITY2 = {
|
|
13101
|
+
errors: 0,
|
|
13102
|
+
warnings: 1,
|
|
13103
|
+
info: 2,
|
|
13104
|
+
verbose: 3
|
|
13105
|
+
};
|
|
13106
|
+
var MESSAGE_LEVEL_PRIORITY2 = {
|
|
13107
|
+
error: 0,
|
|
13108
|
+
warn: 1,
|
|
13109
|
+
info: 2,
|
|
13110
|
+
debug: 3
|
|
13111
|
+
};
|
|
13112
|
+
var UILogCollector = class {
|
|
13113
|
+
constructor(clientId, sendMessage, uiBlockId) {
|
|
13114
|
+
this.logs = [];
|
|
13115
|
+
this.uiBlockId = uiBlockId || null;
|
|
13116
|
+
this.clientId = clientId;
|
|
13117
|
+
this.sendMessage = sendMessage;
|
|
13118
|
+
this.currentLogLevel = logger.getLogLevel();
|
|
13119
|
+
}
|
|
13120
|
+
/**
|
|
13121
|
+
* Check if logging is enabled (uiBlockId is provided)
|
|
13122
|
+
*/
|
|
13123
|
+
isEnabled() {
|
|
13124
|
+
return this.uiBlockId !== null;
|
|
13125
|
+
}
|
|
13126
|
+
/**
|
|
13127
|
+
* Check if a message should be logged based on current log level
|
|
13128
|
+
*/
|
|
13129
|
+
shouldLog(messageLevel) {
|
|
13130
|
+
const currentLevelPriority = LOG_LEVEL_PRIORITY2[this.currentLogLevel];
|
|
13131
|
+
const messagePriority = MESSAGE_LEVEL_PRIORITY2[messageLevel];
|
|
13132
|
+
return messagePriority <= currentLevelPriority;
|
|
13133
|
+
}
|
|
13134
|
+
/**
|
|
13135
|
+
* Add a log entry with timestamp and immediately send to runtime
|
|
13136
|
+
* Only logs that pass the log level filter are captured and sent
|
|
13137
|
+
*/
|
|
13138
|
+
addLog(level, message, type, data) {
|
|
13139
|
+
if (!this.shouldLog(level)) {
|
|
13140
|
+
return;
|
|
13141
|
+
}
|
|
13142
|
+
const log = {
|
|
13143
|
+
timestamp: Date.now(),
|
|
13144
|
+
level,
|
|
13145
|
+
message,
|
|
13146
|
+
...type && { type },
|
|
13147
|
+
...data && { data }
|
|
13148
|
+
};
|
|
13149
|
+
this.logs.push(log);
|
|
13150
|
+
this.sendLogImmediately(log);
|
|
13151
|
+
switch (level) {
|
|
13152
|
+
case "error":
|
|
13153
|
+
logger.error("UILogCollector:", log);
|
|
13154
|
+
break;
|
|
13155
|
+
case "warn":
|
|
13156
|
+
logger.warn("UILogCollector:", log);
|
|
13157
|
+
break;
|
|
13158
|
+
case "info":
|
|
13159
|
+
logger.info("UILogCollector:", log);
|
|
13160
|
+
break;
|
|
13161
|
+
case "debug":
|
|
13162
|
+
logger.debug("UILogCollector:", log);
|
|
13163
|
+
break;
|
|
13164
|
+
}
|
|
13165
|
+
}
|
|
13166
|
+
/**
|
|
13167
|
+
* Send a single log to runtime immediately
|
|
13168
|
+
*/
|
|
13169
|
+
sendLogImmediately(log) {
|
|
13170
|
+
if (!this.isEnabled()) {
|
|
13171
|
+
return;
|
|
13172
|
+
}
|
|
13173
|
+
const response = {
|
|
13174
|
+
id: this.uiBlockId,
|
|
13175
|
+
type: "UI_LOGS",
|
|
13176
|
+
from: { type: "data-agent" },
|
|
13177
|
+
to: {
|
|
13178
|
+
type: "runtime",
|
|
13179
|
+
id: this.clientId
|
|
13180
|
+
},
|
|
13181
|
+
payload: {
|
|
13182
|
+
logs: [log]
|
|
13183
|
+
// Send single log in array
|
|
13184
|
+
}
|
|
13185
|
+
};
|
|
13186
|
+
this.sendMessage(response);
|
|
13187
|
+
}
|
|
13188
|
+
/**
|
|
13189
|
+
* Log info message
|
|
13190
|
+
*/
|
|
13191
|
+
info(message, type, data) {
|
|
13192
|
+
if (this.isEnabled()) {
|
|
13193
|
+
this.addLog("info", message, type, data);
|
|
13194
|
+
}
|
|
13195
|
+
}
|
|
13196
|
+
/**
|
|
13197
|
+
* Log error message
|
|
13198
|
+
*/
|
|
13199
|
+
error(message, type, data) {
|
|
13200
|
+
if (this.isEnabled()) {
|
|
13201
|
+
this.addLog("error", message, type, data);
|
|
13202
|
+
}
|
|
13203
|
+
}
|
|
13204
|
+
/**
|
|
13205
|
+
* Log warning message
|
|
13206
|
+
*/
|
|
13207
|
+
warn(message, type, data) {
|
|
13208
|
+
if (this.isEnabled()) {
|
|
13209
|
+
this.addLog("warn", message, type, data);
|
|
13210
|
+
}
|
|
13211
|
+
}
|
|
13212
|
+
/**
|
|
13213
|
+
* Log debug message
|
|
13214
|
+
*/
|
|
13215
|
+
debug(message, type, data) {
|
|
13216
|
+
if (this.isEnabled()) {
|
|
13217
|
+
this.addLog("debug", message, type, data);
|
|
13218
|
+
}
|
|
13219
|
+
}
|
|
13220
|
+
/**
|
|
13221
|
+
* Log LLM explanation with typed metadata
|
|
13222
|
+
*/
|
|
13223
|
+
logExplanation(message, explanation, data) {
|
|
13224
|
+
if (this.isEnabled()) {
|
|
13225
|
+
this.addLog("info", message, "explanation", {
|
|
13226
|
+
explanation,
|
|
13227
|
+
...data
|
|
13228
|
+
});
|
|
13229
|
+
}
|
|
13230
|
+
}
|
|
13231
|
+
/**
|
|
13232
|
+
* Log generated query with typed metadata
|
|
13233
|
+
*/
|
|
13234
|
+
logQuery(message, query, data) {
|
|
13235
|
+
if (this.isEnabled()) {
|
|
13236
|
+
this.addLog("info", message, "query", {
|
|
13237
|
+
query,
|
|
13238
|
+
...data
|
|
13239
|
+
});
|
|
13240
|
+
}
|
|
13241
|
+
}
|
|
13242
|
+
/**
|
|
13243
|
+
* Send all collected logs at once (optional, for final summary)
|
|
13244
|
+
*/
|
|
13245
|
+
sendAllLogs() {
|
|
13246
|
+
if (!this.isEnabled() || this.logs.length === 0) {
|
|
13247
|
+
return;
|
|
13248
|
+
}
|
|
13249
|
+
const response = {
|
|
13250
|
+
id: this.uiBlockId,
|
|
13251
|
+
type: "UI_LOGS",
|
|
13252
|
+
from: { type: "data-agent" },
|
|
13253
|
+
to: {
|
|
13254
|
+
type: "runtime",
|
|
13255
|
+
id: this.clientId
|
|
13256
|
+
},
|
|
13257
|
+
payload: {
|
|
13258
|
+
logs: this.logs
|
|
13259
|
+
}
|
|
13260
|
+
};
|
|
13261
|
+
this.sendMessage(response);
|
|
13262
|
+
}
|
|
13263
|
+
/**
|
|
13264
|
+
* Get all collected logs
|
|
13265
|
+
*/
|
|
13266
|
+
getLogs() {
|
|
13267
|
+
return [...this.logs];
|
|
13268
|
+
}
|
|
13269
|
+
/**
|
|
13270
|
+
* Clear all logs
|
|
13271
|
+
*/
|
|
13272
|
+
clearLogs() {
|
|
13273
|
+
this.logs = [];
|
|
13274
|
+
}
|
|
13275
|
+
/**
|
|
13276
|
+
* Set uiBlockId (in case it's provided later)
|
|
13277
|
+
*/
|
|
13278
|
+
setUIBlockId(uiBlockId) {
|
|
13279
|
+
this.uiBlockId = uiBlockId;
|
|
13280
|
+
}
|
|
13281
|
+
};
|
|
13282
|
+
|
|
13555
13283
|
// src/services/cleanup-service.ts
|
|
13556
13284
|
var CleanupService = class _CleanupService {
|
|
13557
13285
|
constructor() {
|
|
@@ -13732,7 +13460,6 @@ var CleanupService = class _CleanupService {
|
|
|
13732
13460
|
};
|
|
13733
13461
|
|
|
13734
13462
|
// src/index.ts
|
|
13735
|
-
var SDK_VERSION = "0.0.8";
|
|
13736
13463
|
var DEFAULT_WS_URL = "wss://ws.superatom.ai/websocket";
|
|
13737
13464
|
var SuperatomSDK = class {
|
|
13738
13465
|
// 3.5 minutes (PING_INTERVAL + 30s grace)
|
|
@@ -13773,7 +13500,7 @@ var SuperatomSDK = class {
|
|
|
13773
13500
|
if (config.queryCacheTTL !== void 0) {
|
|
13774
13501
|
queryCache.setTTL(config.queryCacheTTL);
|
|
13775
13502
|
}
|
|
13776
|
-
logger.info(`Initializing Superatom SDK
|
|
13503
|
+
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`);
|
|
13777
13504
|
this.userManager = new UserManager(this.projectId, 5e3);
|
|
13778
13505
|
this.dashboardManager = new DashboardManager(this.projectId);
|
|
13779
13506
|
this.reportManager = new ReportManager(this.projectId);
|
|
@@ -13827,7 +13554,6 @@ var SuperatomSDK = class {
|
|
|
13827
13554
|
*/
|
|
13828
13555
|
initializeDashboardManager() {
|
|
13829
13556
|
setDashboardManager(this.dashboardManager);
|
|
13830
|
-
logger.info(`DashboardManager initialized for project: ${this.projectId}`);
|
|
13831
13557
|
}
|
|
13832
13558
|
/**
|
|
13833
13559
|
* Get the DashboardManager instance for this SDK
|
|
@@ -13840,7 +13566,6 @@ var SuperatomSDK = class {
|
|
|
13840
13566
|
*/
|
|
13841
13567
|
initializeReportManager() {
|
|
13842
13568
|
setReportManager(this.reportManager);
|
|
13843
|
-
logger.info(`ReportManager initialized for project: ${this.projectId}`);
|
|
13844
13569
|
}
|
|
13845
13570
|
/**
|
|
13846
13571
|
* Get the ReportManager instance for this SDK
|
|
@@ -14217,7 +13942,6 @@ export {
|
|
|
14217
13942
|
CONTEXT_CONFIG,
|
|
14218
13943
|
CleanupService,
|
|
14219
13944
|
LLM,
|
|
14220
|
-
SDK_VERSION,
|
|
14221
13945
|
STORAGE_CONFIG,
|
|
14222
13946
|
SuperatomSDK,
|
|
14223
13947
|
Thread,
|