@superatomai/sdk-node 0.0.39 → 0.0.41

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.mjs CHANGED
@@ -1254,13 +1254,16 @@ var init_prompt_loader = __esm({
1254
1254
  const contextMarker = "---\n\n## CONTEXT";
1255
1255
  if (template.system.includes(contextMarker)) {
1256
1256
  const [staticPart, contextPart] = template.system.split(contextMarker);
1257
- logger.debug(`\u2713 Prompt caching enabled for '${promptName}' (static: ${staticPart.length} chars, context: ${contextPart.length} chars)`);
1257
+ const processedStatic = this.replaceVariables(staticPart, variables);
1258
1258
  const processedContext = this.replaceVariables(contextMarker + contextPart, variables);
1259
+ const staticLength = processedStatic.length;
1260
+ const contextLength = processedContext.length;
1261
+ logger.debug(`\u2713 Prompt caching enabled for '${promptName}' (cached: ${staticLength} chars, dynamic: ${contextLength} chars)`);
1259
1262
  return {
1260
1263
  system: [
1261
1264
  {
1262
1265
  type: "text",
1263
- text: staticPart.trim(),
1266
+ text: processedStatic.trim(),
1264
1267
  cache_control: { type: "ephemeral" }
1265
1268
  },
1266
1269
  {
@@ -3378,6 +3381,465 @@ import Groq from "groq-sdk";
3378
3381
  import { GoogleGenerativeAI, SchemaType } from "@google/generative-ai";
3379
3382
  import OpenAI from "openai";
3380
3383
  import { jsonrepair } from "jsonrepair";
3384
+
3385
+ // src/utils/llm-usage-logger.ts
3386
+ import fs5 from "fs";
3387
+ import path4 from "path";
3388
+ var PRICING = {
3389
+ // Anthropic (December 2025)
3390
+ "claude-opus-4-5": { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },
3391
+ "claude-opus-4-5-20251101": { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },
3392
+ "claude-sonnet-4-5": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
3393
+ "claude-sonnet-4-5-20250929": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
3394
+ "claude-haiku-4-5": { input: 1, output: 5, cacheRead: 0.1, cacheWrite: 1.25 },
3395
+ "claude-haiku-4-5-20251001": { input: 1, output: 5, cacheRead: 0.1, cacheWrite: 1.25 },
3396
+ "claude-3-5-sonnet-20241022": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
3397
+ "claude-3-5-haiku-20241022": { input: 1, output: 5, cacheRead: 0.1, cacheWrite: 1.25 },
3398
+ "claude-3-opus-20240229": { input: 15, output: 75, cacheRead: 1.5, cacheWrite: 18.75 },
3399
+ "claude-3-sonnet-20240229": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
3400
+ "claude-3-haiku-20240307": { input: 0.25, output: 1.25, cacheRead: 0.03, cacheWrite: 0.3 },
3401
+ // OpenAI (December 2025)
3402
+ "gpt-5": { input: 1.25, output: 10 },
3403
+ "gpt-5-mini": { input: 0.25, output: 2 },
3404
+ "gpt-4o": { input: 5, output: 15 },
3405
+ // Updated pricing as of late 2025
3406
+ "gpt-4o-mini": { input: 0.15, output: 0.6 },
3407
+ "gpt-4-turbo": { input: 10, output: 30 },
3408
+ "gpt-4": { input: 30, output: 60 },
3409
+ "gpt-3.5-turbo": { input: 0.5, output: 1.5 },
3410
+ // Google Gemini (December 2025)
3411
+ "gemini-3-pro": { input: 2, output: 8 },
3412
+ // New Gemini 3
3413
+ "gemini-2.5-pro": { input: 1.25, output: 10 },
3414
+ // For prompts ≤200K tokens, 2x for >200K
3415
+ "gemini-2.5-flash": { input: 0.15, output: 0.6 },
3416
+ // Standard mode (thinking disabled: $0.60, thinking enabled: $3.50)
3417
+ "gemini-2.5-flash-lite": { input: 0.1, output: 0.4 },
3418
+ "gemini-2.0-flash": { input: 0.1, output: 0.4 },
3419
+ "gemini-2.0-flash-lite": { input: 0.075, output: 0.3 },
3420
+ "gemini-1.5-pro": { input: 1.25, output: 5 },
3421
+ "gemini-1.5-flash": { input: 0.075, output: 0.3 },
3422
+ // Groq (December 2025)
3423
+ "llama-3.3-70b-versatile": { input: 0.59, output: 0.79 },
3424
+ "llama-3.1-70b-versatile": { input: 0.59, output: 0.79 },
3425
+ "llama-3.1-8b-instant": { input: 0.05, output: 0.08 },
3426
+ "llama-4-scout-17b-16e": { input: 0.11, output: 0.34 },
3427
+ "llama-4-maverick-17b-128e": { input: 0.2, output: 0.6 },
3428
+ "mixtral-8x7b-32768": { input: 0.27, output: 0.27 },
3429
+ "qwen3-32b": { input: 0.29, output: 0.59 }
3430
+ };
3431
+ var DEFAULT_PRICING = { input: 3, output: 15 };
3432
+ var LLMUsageLogger = class {
3433
+ constructor() {
3434
+ this.logStream = null;
3435
+ this.sessionStats = {
3436
+ totalCalls: 0,
3437
+ totalInputTokens: 0,
3438
+ totalOutputTokens: 0,
3439
+ totalCacheReadTokens: 0,
3440
+ totalCacheWriteTokens: 0,
3441
+ totalCostUSD: 0,
3442
+ totalDurationMs: 0
3443
+ };
3444
+ this.logPath = process.env.LLM_USAGE_LOG_PATH || path4.join(process.cwd(), "llm-usage-logs");
3445
+ this.enabled = process.env.LLM_USAGE_LOGGING !== "false";
3446
+ if (this.enabled) {
3447
+ this.initLogStream();
3448
+ }
3449
+ }
3450
+ initLogStream() {
3451
+ try {
3452
+ const dir = path4.dirname(this.logPath);
3453
+ if (!fs5.existsSync(dir)) {
3454
+ fs5.mkdirSync(dir, { recursive: true });
3455
+ }
3456
+ this.logStream = fs5.createWriteStream(this.logPath, { flags: "a" });
3457
+ if (!fs5.existsSync(this.logPath) || fs5.statSync(this.logPath).size === 0) {
3458
+ this.writeHeader();
3459
+ }
3460
+ } catch (error) {
3461
+ console.error("[LLM-Usage-Logger] Failed to initialize log stream:", error);
3462
+ this.enabled = false;
3463
+ }
3464
+ }
3465
+ writeHeader() {
3466
+ const header = `
3467
+ ================================================================================
3468
+ LLM USAGE LOG - Session Started: ${(/* @__PURE__ */ new Date()).toISOString()}
3469
+ ================================================================================
3470
+ Format: [TIMESTAMP] [REQUEST_ID] [PROVIDER/MODEL] [METHOD]
3471
+ Tokens: IN=input OUT=output CACHE_R=cache_read CACHE_W=cache_write TOTAL=total
3472
+ Cost: $X.XXXXXX | Time: Xms
3473
+ ================================================================================
3474
+
3475
+ `;
3476
+ this.logStream?.write(header);
3477
+ }
3478
+ /**
3479
+ * Calculate cost based on token usage and model
3480
+ */
3481
+ calculateCost(model, inputTokens, outputTokens, cacheReadTokens = 0, cacheWriteTokens = 0) {
3482
+ let pricing = PRICING[model];
3483
+ if (!pricing) {
3484
+ const modelLower = model.toLowerCase();
3485
+ for (const [key, value] of Object.entries(PRICING)) {
3486
+ if (modelLower.includes(key.toLowerCase()) || key.toLowerCase().includes(modelLower)) {
3487
+ pricing = value;
3488
+ break;
3489
+ }
3490
+ }
3491
+ }
3492
+ pricing = pricing || DEFAULT_PRICING;
3493
+ const inputCost = inputTokens / 1e6 * pricing.input;
3494
+ const outputCost = outputTokens / 1e6 * pricing.output;
3495
+ const cacheReadCost = cacheReadTokens / 1e6 * (pricing.cacheRead || pricing.input * 0.1);
3496
+ const cacheWriteCost = cacheWriteTokens / 1e6 * (pricing.cacheWrite || pricing.input * 1.25);
3497
+ return inputCost + outputCost + cacheReadCost + cacheWriteCost;
3498
+ }
3499
+ /**
3500
+ * Log an LLM API call
3501
+ */
3502
+ log(entry) {
3503
+ if (!this.enabled) return;
3504
+ this.sessionStats.totalCalls++;
3505
+ this.sessionStats.totalInputTokens += entry.inputTokens;
3506
+ this.sessionStats.totalOutputTokens += entry.outputTokens;
3507
+ this.sessionStats.totalCacheReadTokens += entry.cacheReadTokens || 0;
3508
+ this.sessionStats.totalCacheWriteTokens += entry.cacheWriteTokens || 0;
3509
+ this.sessionStats.totalCostUSD += entry.costUSD;
3510
+ this.sessionStats.totalDurationMs += entry.durationMs;
3511
+ const cacheInfo = entry.cacheReadTokens || entry.cacheWriteTokens ? ` CACHE_R=${entry.cacheReadTokens || 0} CACHE_W=${entry.cacheWriteTokens || 0}` : "";
3512
+ const toolInfo = entry.toolCalls ? ` | Tools: ${entry.toolCalls}` : "";
3513
+ const errorInfo = entry.error ? ` | ERROR: ${entry.error}` : "";
3514
+ const status = entry.success ? "\u2713" : "\u2717";
3515
+ let cacheStatus = "";
3516
+ if (entry.cacheReadTokens && entry.cacheReadTokens > 0) {
3517
+ const savedCost = entry.cacheReadTokens / 1e6 * 2.7;
3518
+ cacheStatus = ` \u26A1 CACHE HIT! Saved ~$${savedCost.toFixed(4)}`;
3519
+ } else if (entry.cacheWriteTokens && entry.cacheWriteTokens > 0) {
3520
+ cacheStatus = " \u{1F4DD} Cache created (next request will be cheaper)";
3521
+ }
3522
+ const logLine = `[${entry.timestamp}] [${entry.requestId}] ${status} ${entry.provider}/${entry.model} [${entry.method}]
3523
+ Tokens: IN=${entry.inputTokens} OUT=${entry.outputTokens}${cacheInfo} TOTAL=${entry.totalTokens}
3524
+ Cost: $${entry.costUSD.toFixed(6)} | Time: ${entry.durationMs}ms${toolInfo}${errorInfo}${cacheStatus}
3525
+ `;
3526
+ this.logStream?.write(logLine);
3527
+ if (entry.cacheReadTokens && entry.cacheReadTokens > 0) {
3528
+ console.log(`[LLM] \u26A1 CACHE HIT: ${entry.cacheReadTokens.toLocaleString()} tokens read from cache (${entry.method})`);
3529
+ } else if (entry.cacheWriteTokens && entry.cacheWriteTokens > 0) {
3530
+ console.log(`[LLM] \u{1F4DD} CACHE WRITE: ${entry.cacheWriteTokens.toLocaleString()} tokens cached for future requests (${entry.method})`);
3531
+ }
3532
+ if (process.env.SUPERATOM_LOG_LEVEL === "verbose") {
3533
+ console.log("\n[LLM-Usage]", logLine);
3534
+ }
3535
+ }
3536
+ /**
3537
+ * Log session summary (call at end of request)
3538
+ */
3539
+ logSessionSummary(requestContext) {
3540
+ if (!this.enabled || this.sessionStats.totalCalls === 0) return;
3541
+ const cacheReadSavings = this.sessionStats.totalCacheReadTokens / 1e6 * 2.7;
3542
+ const hasCaching = this.sessionStats.totalCacheReadTokens > 0 || this.sessionStats.totalCacheWriteTokens > 0;
3543
+ let cacheSection = "";
3544
+ if (hasCaching) {
3545
+ cacheSection = `
3546
+ Cache Statistics:
3547
+ Cache Read Tokens: ${this.sessionStats.totalCacheReadTokens.toLocaleString()}${this.sessionStats.totalCacheReadTokens > 0 ? " \u26A1" : ""}
3548
+ Cache Write Tokens: ${this.sessionStats.totalCacheWriteTokens.toLocaleString()}${this.sessionStats.totalCacheWriteTokens > 0 ? " \u{1F4DD}" : ""}
3549
+ Estimated Savings: $${cacheReadSavings.toFixed(4)}`;
3550
+ }
3551
+ const summary = `
3552
+ --------------------------------------------------------------------------------
3553
+ SESSION SUMMARY${requestContext ? ` (${requestContext})` : ""}
3554
+ --------------------------------------------------------------------------------
3555
+ Total LLM Calls: ${this.sessionStats.totalCalls}
3556
+ Total Input Tokens: ${this.sessionStats.totalInputTokens.toLocaleString()}
3557
+ Total Output Tokens: ${this.sessionStats.totalOutputTokens.toLocaleString()}
3558
+ Total Tokens: ${(this.sessionStats.totalInputTokens + this.sessionStats.totalOutputTokens).toLocaleString()}
3559
+ Total Cost: $${this.sessionStats.totalCostUSD.toFixed(6)}
3560
+ Total Time: ${this.sessionStats.totalDurationMs}ms (${(this.sessionStats.totalDurationMs / 1e3).toFixed(2)}s)
3561
+ Avg Cost/Call: $${(this.sessionStats.totalCostUSD / this.sessionStats.totalCalls).toFixed(6)}
3562
+ Avg Time/Call: ${Math.round(this.sessionStats.totalDurationMs / this.sessionStats.totalCalls)}ms${cacheSection}
3563
+ --------------------------------------------------------------------------------
3564
+
3565
+ `;
3566
+ this.logStream?.write(summary);
3567
+ console.log("\n[LLM-Usage] Session Summary:");
3568
+ 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`);
3569
+ if (hasCaching) {
3570
+ console.log(` Cache: ${this.sessionStats.totalCacheReadTokens.toLocaleString()} read, ${this.sessionStats.totalCacheWriteTokens.toLocaleString()} written | Savings: ~$${cacheReadSavings.toFixed(4)}`);
3571
+ }
3572
+ }
3573
+ /**
3574
+ * Reset session stats (call at start of new user request)
3575
+ */
3576
+ resetSession() {
3577
+ this.sessionStats = {
3578
+ totalCalls: 0,
3579
+ totalInputTokens: 0,
3580
+ totalOutputTokens: 0,
3581
+ totalCacheReadTokens: 0,
3582
+ totalCacheWriteTokens: 0,
3583
+ totalCostUSD: 0,
3584
+ totalDurationMs: 0
3585
+ };
3586
+ }
3587
+ /**
3588
+ * Reset the log file for a new request (clears previous logs)
3589
+ * Call this at the start of each USER_PROMPT_REQ
3590
+ */
3591
+ resetLogFile(requestContext) {
3592
+ if (!this.enabled) return;
3593
+ try {
3594
+ if (this.logStream) {
3595
+ this.logStream.end();
3596
+ this.logStream = null;
3597
+ }
3598
+ this.logStream = fs5.createWriteStream(this.logPath, { flags: "w" });
3599
+ const header = `
3600
+ ================================================================================
3601
+ LLM USAGE LOG - Request Started: ${(/* @__PURE__ */ new Date()).toISOString()}
3602
+ ${requestContext ? `Context: ${requestContext}` : ""}
3603
+ ================================================================================
3604
+ Format: [TIMESTAMP] [REQUEST_ID] [PROVIDER/MODEL] [METHOD]
3605
+ Tokens: IN=input OUT=output CACHE_R=cache_read CACHE_W=cache_write TOTAL=total
3606
+ Cost: $X.XXXXXX | Time: Xms
3607
+ ================================================================================
3608
+
3609
+ `;
3610
+ this.logStream.write(header);
3611
+ this.resetSession();
3612
+ console.log(`[LLM-Usage] Log file reset for new request: ${this.logPath}`);
3613
+ } catch (error) {
3614
+ console.error("[LLM-Usage-Logger] Failed to reset log file:", error);
3615
+ }
3616
+ }
3617
+ /**
3618
+ * Get current session stats
3619
+ */
3620
+ getSessionStats() {
3621
+ return { ...this.sessionStats };
3622
+ }
3623
+ /**
3624
+ * Generate a unique request ID
3625
+ */
3626
+ generateRequestId() {
3627
+ return `req-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
3628
+ }
3629
+ };
3630
+ var llmUsageLogger = new LLMUsageLogger();
3631
+
3632
+ // src/utils/user-prompt-error-logger.ts
3633
+ import fs6 from "fs";
3634
+ import path5 from "path";
3635
+ var UserPromptErrorLogger = class {
3636
+ constructor() {
3637
+ this.logStream = null;
3638
+ this.hasErrors = false;
3639
+ this.logPath = process.env.USER_PROMPT_ERROR_LOG_PATH || path5.join(process.cwd(), "user-prompt-req-errors");
3640
+ this.enabled = process.env.USER_PROMPT_ERROR_LOGGING !== "false";
3641
+ }
3642
+ /**
3643
+ * Reset the error log file for a new request
3644
+ */
3645
+ resetLogFile(requestContext) {
3646
+ if (!this.enabled) return;
3647
+ try {
3648
+ if (this.logStream) {
3649
+ this.logStream.end();
3650
+ this.logStream = null;
3651
+ }
3652
+ const dir = path5.dirname(this.logPath);
3653
+ if (dir !== "." && !fs6.existsSync(dir)) {
3654
+ fs6.mkdirSync(dir, { recursive: true });
3655
+ }
3656
+ this.logStream = fs6.createWriteStream(this.logPath, { flags: "w" });
3657
+ this.hasErrors = false;
3658
+ const header = `================================================================================
3659
+ USER PROMPT REQUEST ERROR LOG
3660
+ Request Started: ${(/* @__PURE__ */ new Date()).toISOString()}
3661
+ ${requestContext ? `Context: ${requestContext}` : ""}
3662
+ ================================================================================
3663
+
3664
+ `;
3665
+ this.logStream.write(header);
3666
+ } catch (error) {
3667
+ console.error("[UserPromptErrorLogger] Failed to reset log file:", error);
3668
+ }
3669
+ }
3670
+ /**
3671
+ * Log a JSON parse error with the raw string that failed
3672
+ */
3673
+ logJsonParseError(context, rawString, error) {
3674
+ if (!this.enabled) return;
3675
+ this.hasErrors = true;
3676
+ const entry = `
3677
+ --------------------------------------------------------------------------------
3678
+ [${(/* @__PURE__ */ new Date()).toISOString()}] JSON PARSE ERROR
3679
+ --------------------------------------------------------------------------------
3680
+ Context: ${context}
3681
+ Error: ${error.message}
3682
+
3683
+ Raw String (${rawString.length} chars):
3684
+ --------------------------------------------------------------------------------
3685
+ ${rawString}
3686
+ --------------------------------------------------------------------------------
3687
+
3688
+ Stack Trace:
3689
+ ${error.stack || "No stack trace available"}
3690
+
3691
+ `;
3692
+ this.write(entry);
3693
+ console.error(`[UserPromptError] JSON Parse Error in ${context}: ${error.message}`);
3694
+ }
3695
+ /**
3696
+ * Log a general error with full details
3697
+ */
3698
+ logError(context, error, additionalData) {
3699
+ if (!this.enabled) return;
3700
+ this.hasErrors = true;
3701
+ const errorMessage = error instanceof Error ? error.message : error;
3702
+ const errorStack = error instanceof Error ? error.stack : void 0;
3703
+ let entry = `
3704
+ --------------------------------------------------------------------------------
3705
+ [${(/* @__PURE__ */ new Date()).toISOString()}] ERROR
3706
+ --------------------------------------------------------------------------------
3707
+ Context: ${context}
3708
+ Error: ${errorMessage}
3709
+ `;
3710
+ if (additionalData) {
3711
+ entry += `
3712
+ Additional Data:
3713
+ ${JSON.stringify(additionalData, null, 2)}
3714
+ `;
3715
+ }
3716
+ if (errorStack) {
3717
+ entry += `
3718
+ Stack Trace:
3719
+ ${errorStack}
3720
+ `;
3721
+ }
3722
+ entry += `--------------------------------------------------------------------------------
3723
+
3724
+ `;
3725
+ this.write(entry);
3726
+ console.error(`[UserPromptError] ${context}: ${errorMessage}`);
3727
+ }
3728
+ /**
3729
+ * Log a SQL query error with the full query
3730
+ */
3731
+ logSqlError(query, error, params) {
3732
+ if (!this.enabled) return;
3733
+ this.hasErrors = true;
3734
+ const errorMessage = error instanceof Error ? error.message : error;
3735
+ const entry = `
3736
+ --------------------------------------------------------------------------------
3737
+ [${(/* @__PURE__ */ new Date()).toISOString()}] SQL QUERY ERROR
3738
+ --------------------------------------------------------------------------------
3739
+ Error: ${errorMessage}
3740
+
3741
+ Query (${query.length} chars):
3742
+ --------------------------------------------------------------------------------
3743
+ ${query}
3744
+ --------------------------------------------------------------------------------
3745
+ ${params ? `
3746
+ Parameters: ${JSON.stringify(params)}` : ""}
3747
+
3748
+ `;
3749
+ this.write(entry);
3750
+ console.error(`[UserPromptError] SQL Error: ${errorMessage}`);
3751
+ }
3752
+ /**
3753
+ * Log an LLM API error
3754
+ */
3755
+ logLlmError(provider, model, method, error, requestData) {
3756
+ if (!this.enabled) return;
3757
+ this.hasErrors = true;
3758
+ const errorMessage = error instanceof Error ? error.message : error;
3759
+ const errorStack = error instanceof Error ? error.stack : void 0;
3760
+ let entry = `
3761
+ --------------------------------------------------------------------------------
3762
+ [${(/* @__PURE__ */ new Date()).toISOString()}] LLM API ERROR
3763
+ --------------------------------------------------------------------------------
3764
+ Provider: ${provider}
3765
+ Model: ${model}
3766
+ Method: ${method}
3767
+ Error: ${errorMessage}
3768
+ `;
3769
+ if (requestData) {
3770
+ const dataStr = JSON.stringify(requestData, null, 2);
3771
+ const truncated = dataStr.length > 5e3 ? dataStr.substring(0, 5e3) + "\n... [truncated]" : dataStr;
3772
+ entry += `
3773
+ Request Data:
3774
+ ${truncated}
3775
+ `;
3776
+ }
3777
+ if (errorStack) {
3778
+ entry += `
3779
+ Stack Trace:
3780
+ ${errorStack}
3781
+ `;
3782
+ }
3783
+ entry += `--------------------------------------------------------------------------------
3784
+
3785
+ `;
3786
+ this.write(entry);
3787
+ console.error(`[UserPromptError] LLM Error (${provider}/${model}): ${errorMessage}`);
3788
+ }
3789
+ /**
3790
+ * Log tool execution error
3791
+ */
3792
+ logToolError(toolName, toolInput, error) {
3793
+ if (!this.enabled) return;
3794
+ this.hasErrors = true;
3795
+ const errorMessage = error instanceof Error ? error.message : error;
3796
+ const errorStack = error instanceof Error ? error.stack : void 0;
3797
+ const entry = `
3798
+ --------------------------------------------------------------------------------
3799
+ [${(/* @__PURE__ */ new Date()).toISOString()}] TOOL EXECUTION ERROR
3800
+ --------------------------------------------------------------------------------
3801
+ Tool: ${toolName}
3802
+ Error: ${errorMessage}
3803
+
3804
+ Tool Input:
3805
+ ${JSON.stringify(toolInput, null, 2)}
3806
+ ${errorStack ? `
3807
+ Stack Trace:
3808
+ ${errorStack}` : ""}
3809
+ --------------------------------------------------------------------------------
3810
+
3811
+ `;
3812
+ this.write(entry);
3813
+ console.error(`[UserPromptError] Tool Error (${toolName}): ${errorMessage}`);
3814
+ }
3815
+ /**
3816
+ * Write final summary if there were errors
3817
+ */
3818
+ writeSummary() {
3819
+ if (!this.enabled || !this.hasErrors) return;
3820
+ const summary = `
3821
+ ================================================================================
3822
+ REQUEST COMPLETED WITH ERRORS
3823
+ Time: ${(/* @__PURE__ */ new Date()).toISOString()}
3824
+ ================================================================================
3825
+ `;
3826
+ this.write(summary);
3827
+ }
3828
+ /**
3829
+ * Check if any errors were logged
3830
+ */
3831
+ hadErrors() {
3832
+ return this.hasErrors;
3833
+ }
3834
+ write(content) {
3835
+ if (this.logStream) {
3836
+ this.logStream.write(content);
3837
+ }
3838
+ }
3839
+ };
3840
+ var userPromptErrorLogger = new UserPromptErrorLogger();
3841
+
3842
+ // src/llm.ts
3381
3843
  var LLM = class {
3382
3844
  /* Get a complete text response from an LLM (Anthropic or Groq) */
3383
3845
  static async text(messages, options = {}) {
@@ -3520,68 +3982,156 @@ var LLM = class {
3520
3982
  // ANTHROPIC IMPLEMENTATION
3521
3983
  // ============================================================
3522
3984
  static async _anthropicText(messages, modelName, options) {
3985
+ const startTime = Date.now();
3986
+ const requestId = llmUsageLogger.generateRequestId();
3523
3987
  const apiKey = options.apiKey || process.env.ANTHROPIC_API_KEY || "";
3524
3988
  const client = new Anthropic({
3525
3989
  apiKey
3526
3990
  });
3527
- const response = await client.messages.create({
3528
- model: modelName,
3529
- max_tokens: options.maxTokens || 1e3,
3530
- temperature: options.temperature,
3531
- system: this._normalizeSystemPrompt(messages.sys),
3532
- messages: [{
3533
- role: "user",
3534
- content: messages.user
3535
- }]
3536
- });
3537
- const textBlock = response.content.find((block) => block.type === "text");
3538
- return textBlock?.type === "text" ? textBlock.text : "";
3991
+ try {
3992
+ const response = await client.messages.create({
3993
+ model: modelName,
3994
+ max_tokens: options.maxTokens || 1e3,
3995
+ temperature: options.temperature,
3996
+ system: this._normalizeSystemPrompt(messages.sys),
3997
+ messages: [{
3998
+ role: "user",
3999
+ content: messages.user
4000
+ }]
4001
+ });
4002
+ const durationMs = Date.now() - startTime;
4003
+ const usage = response.usage;
4004
+ const inputTokens = usage?.input_tokens || 0;
4005
+ const outputTokens = usage?.output_tokens || 0;
4006
+ const cacheReadTokens = usage?.cache_read_input_tokens || 0;
4007
+ const cacheWriteTokens = usage?.cache_creation_input_tokens || 0;
4008
+ llmUsageLogger.log({
4009
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4010
+ requestId,
4011
+ provider: "anthropic",
4012
+ model: modelName,
4013
+ method: "text",
4014
+ inputTokens,
4015
+ outputTokens,
4016
+ cacheReadTokens,
4017
+ cacheWriteTokens,
4018
+ totalTokens: inputTokens + outputTokens + cacheReadTokens + cacheWriteTokens,
4019
+ costUSD: llmUsageLogger.calculateCost(modelName, inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens),
4020
+ durationMs,
4021
+ success: true
4022
+ });
4023
+ const textBlock = response.content.find((block) => block.type === "text");
4024
+ return textBlock?.type === "text" ? textBlock.text : "";
4025
+ } catch (error) {
4026
+ const durationMs = Date.now() - startTime;
4027
+ llmUsageLogger.log({
4028
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4029
+ requestId,
4030
+ provider: "anthropic",
4031
+ model: modelName,
4032
+ method: "text",
4033
+ inputTokens: 0,
4034
+ outputTokens: 0,
4035
+ totalTokens: 0,
4036
+ costUSD: 0,
4037
+ durationMs,
4038
+ success: false,
4039
+ error: error instanceof Error ? error.message : String(error)
4040
+ });
4041
+ throw error;
4042
+ }
3539
4043
  }
3540
4044
  static async _anthropicStream(messages, modelName, options, json) {
4045
+ const startTime = Date.now();
4046
+ const requestId = llmUsageLogger.generateRequestId();
3541
4047
  const apiKey = options.apiKey || process.env.ANTHROPIC_API_KEY || "";
3542
4048
  const client = new Anthropic({
3543
4049
  apiKey
3544
4050
  });
3545
- const apiMessages = [{
3546
- role: "user",
3547
- content: messages.user
3548
- }];
3549
- const prefill = messages.prefill || (json ? "{" : void 0);
3550
- if (prefill) {
3551
- apiMessages.push({
3552
- role: "assistant",
3553
- content: prefill
4051
+ try {
4052
+ const apiMessages = [{
4053
+ role: "user",
4054
+ content: messages.user
4055
+ }];
4056
+ const prefill = messages.prefill || (json ? "{" : void 0);
4057
+ if (prefill) {
4058
+ apiMessages.push({
4059
+ role: "assistant",
4060
+ content: prefill
4061
+ });
4062
+ }
4063
+ const stream = await client.messages.create({
4064
+ model: modelName,
4065
+ max_tokens: options.maxTokens || 1e3,
4066
+ temperature: options.temperature,
4067
+ system: this._normalizeSystemPrompt(messages.sys),
4068
+ messages: apiMessages,
4069
+ stream: true
3554
4070
  });
3555
- }
3556
- const stream = await client.messages.create({
3557
- model: modelName,
3558
- max_tokens: options.maxTokens || 1e3,
3559
- temperature: options.temperature,
3560
- system: this._normalizeSystemPrompt(messages.sys),
3561
- messages: apiMessages,
3562
- stream: true
3563
- });
3564
- let fullText = prefill || "";
3565
- let usage = null;
3566
- for await (const chunk of stream) {
3567
- if (chunk.type === "content_block_delta" && chunk.delta.type === "text_delta") {
3568
- const text = chunk.delta.text;
3569
- fullText += text;
3570
- if (options.partial) {
3571
- options.partial(text);
4071
+ let fullText = prefill || "";
4072
+ let usage = null;
4073
+ let inputTokens = 0;
4074
+ let outputTokens = 0;
4075
+ let cacheReadTokens = 0;
4076
+ let cacheWriteTokens = 0;
4077
+ for await (const chunk of stream) {
4078
+ if (chunk.type === "content_block_delta" && chunk.delta.type === "text_delta") {
4079
+ const text = chunk.delta.text;
4080
+ fullText += text;
4081
+ if (options.partial) {
4082
+ options.partial(text);
4083
+ }
4084
+ } else if (chunk.type === "message_start" && chunk.message?.usage) {
4085
+ const msgUsage = chunk.message.usage;
4086
+ inputTokens = msgUsage.input_tokens || 0;
4087
+ cacheReadTokens = msgUsage.cache_read_input_tokens || 0;
4088
+ cacheWriteTokens = msgUsage.cache_creation_input_tokens || 0;
4089
+ } else if (chunk.type === "message_delta" && chunk.usage) {
4090
+ usage = chunk.usage;
4091
+ outputTokens = usage.output_tokens || 0;
3572
4092
  }
3573
- } else if (chunk.type === "message_delta" && chunk.usage) {
3574
- usage = chunk.usage;
3575
4093
  }
4094
+ const durationMs = Date.now() - startTime;
4095
+ llmUsageLogger.log({
4096
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4097
+ requestId,
4098
+ provider: "anthropic",
4099
+ model: modelName,
4100
+ method: "stream",
4101
+ inputTokens,
4102
+ outputTokens,
4103
+ cacheReadTokens,
4104
+ cacheWriteTokens,
4105
+ totalTokens: inputTokens + outputTokens + cacheReadTokens + cacheWriteTokens,
4106
+ costUSD: llmUsageLogger.calculateCost(modelName, inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens),
4107
+ durationMs,
4108
+ success: true
4109
+ });
4110
+ if (json) {
4111
+ return this._parseJSON(fullText);
4112
+ }
4113
+ return fullText;
4114
+ } catch (error) {
4115
+ const durationMs = Date.now() - startTime;
4116
+ llmUsageLogger.log({
4117
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4118
+ requestId,
4119
+ provider: "anthropic",
4120
+ model: modelName,
4121
+ method: "stream",
4122
+ inputTokens: 0,
4123
+ outputTokens: 0,
4124
+ totalTokens: 0,
4125
+ costUSD: 0,
4126
+ durationMs,
4127
+ success: false,
4128
+ error: error instanceof Error ? error.message : String(error)
4129
+ });
4130
+ throw error;
3576
4131
  }
3577
- if (usage) {
3578
- }
3579
- if (json) {
3580
- return this._parseJSON(fullText);
3581
- }
3582
- return fullText;
3583
4132
  }
3584
4133
  static async _anthropicStreamWithTools(messages, tools, toolHandler, modelName, options, maxIterations) {
4134
+ const methodStartTime = Date.now();
3585
4135
  const apiKey = options.apiKey || process.env.ANTHROPIC_API_KEY || "";
3586
4136
  const client = new Anthropic({
3587
4137
  apiKey
@@ -3592,8 +4142,15 @@ var LLM = class {
3592
4142
  }];
3593
4143
  let iterations = 0;
3594
4144
  let finalText = "";
4145
+ let totalToolCalls = 0;
4146
+ let totalInputTokens = 0;
4147
+ let totalOutputTokens = 0;
4148
+ let totalCacheReadTokens = 0;
4149
+ let totalCacheWriteTokens = 0;
3595
4150
  while (iterations < maxIterations) {
3596
4151
  iterations++;
4152
+ const iterationStartTime = Date.now();
4153
+ const requestId = llmUsageLogger.generateRequestId();
3597
4154
  const stream = await client.messages.create({
3598
4155
  model: modelName,
3599
4156
  max_tokens: options.maxTokens || 4e3,
@@ -3608,12 +4165,21 @@ var LLM = class {
3608
4165
  const contentBlocks = [];
3609
4166
  let currentTextBlock = "";
3610
4167
  let currentToolUse = null;
3611
- let usage = null;
4168
+ let inputTokens = 0;
4169
+ let outputTokens = 0;
4170
+ let cacheReadTokens = 0;
4171
+ let cacheWriteTokens = 0;
3612
4172
  for await (const chunk of stream) {
3613
4173
  if (chunk.type === "message_start") {
3614
4174
  contentBlocks.length = 0;
3615
4175
  currentTextBlock = "";
3616
4176
  currentToolUse = null;
4177
+ const msgUsage = chunk.message?.usage;
4178
+ if (msgUsage) {
4179
+ inputTokens = msgUsage.input_tokens || 0;
4180
+ cacheReadTokens = msgUsage.cache_read_input_tokens || 0;
4181
+ cacheWriteTokens = msgUsage.cache_creation_input_tokens || 0;
4182
+ }
3617
4183
  }
3618
4184
  if (chunk.type === "content_block_start") {
3619
4185
  if (chunk.content_block.type === "text") {
@@ -3660,15 +4226,36 @@ var LLM = class {
3660
4226
  if (chunk.type === "message_delta") {
3661
4227
  stopReason = chunk.delta.stop_reason || stopReason;
3662
4228
  if (chunk.usage) {
3663
- usage = chunk.usage;
4229
+ outputTokens = chunk.usage.output_tokens || 0;
3664
4230
  }
3665
4231
  }
3666
4232
  if (chunk.type === "message_stop") {
3667
4233
  break;
3668
4234
  }
3669
4235
  }
3670
- if (usage) {
3671
- }
4236
+ const iterationDuration = Date.now() - iterationStartTime;
4237
+ const toolUsesInIteration = contentBlocks.filter((block) => block.type === "tool_use").length;
4238
+ totalToolCalls += toolUsesInIteration;
4239
+ totalInputTokens += inputTokens;
4240
+ totalOutputTokens += outputTokens;
4241
+ totalCacheReadTokens += cacheReadTokens;
4242
+ totalCacheWriteTokens += cacheWriteTokens;
4243
+ llmUsageLogger.log({
4244
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4245
+ requestId,
4246
+ provider: "anthropic",
4247
+ model: modelName,
4248
+ method: `streamWithTools[iter=${iterations}]`,
4249
+ inputTokens,
4250
+ outputTokens,
4251
+ cacheReadTokens,
4252
+ cacheWriteTokens,
4253
+ totalTokens: inputTokens + outputTokens + cacheReadTokens + cacheWriteTokens,
4254
+ costUSD: llmUsageLogger.calculateCost(modelName, inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens),
4255
+ durationMs: iterationDuration,
4256
+ toolCalls: toolUsesInIteration,
4257
+ success: true
4258
+ });
3672
4259
  if (stopReason === "end_turn") {
3673
4260
  break;
3674
4261
  }
@@ -3712,6 +4299,25 @@ var LLM = class {
3712
4299
  break;
3713
4300
  }
3714
4301
  }
4302
+ const totalDuration = Date.now() - methodStartTime;
4303
+ if (iterations > 1) {
4304
+ llmUsageLogger.log({
4305
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4306
+ requestId: llmUsageLogger.generateRequestId(),
4307
+ provider: "anthropic",
4308
+ model: modelName,
4309
+ method: `streamWithTools[TOTAL:${iterations}iters]`,
4310
+ inputTokens: totalInputTokens,
4311
+ outputTokens: totalOutputTokens,
4312
+ cacheReadTokens: totalCacheReadTokens,
4313
+ cacheWriteTokens: totalCacheWriteTokens,
4314
+ totalTokens: totalInputTokens + totalOutputTokens + totalCacheReadTokens + totalCacheWriteTokens,
4315
+ costUSD: llmUsageLogger.calculateCost(modelName, totalInputTokens, totalOutputTokens, totalCacheReadTokens, totalCacheWriteTokens),
4316
+ durationMs: totalDuration,
4317
+ toolCalls: totalToolCalls,
4318
+ success: true
4319
+ });
4320
+ }
3715
4321
  if (iterations >= maxIterations) {
3716
4322
  throw new Error(`Max iterations (${maxIterations}) reached in tool calling loop`);
3717
4323
  }
@@ -3721,100 +4327,272 @@ var LLM = class {
3721
4327
  // GROQ IMPLEMENTATION
3722
4328
  // ============================================================
3723
4329
  static async _groqText(messages, modelName, options) {
4330
+ const startTime = Date.now();
4331
+ const requestId = llmUsageLogger.generateRequestId();
3724
4332
  const client = new Groq({
3725
4333
  apiKey: options.apiKey || process.env.GROQ_API_KEY || ""
3726
4334
  });
3727
- const response = await client.chat.completions.create({
3728
- model: modelName,
3729
- messages: [
3730
- { role: "system", content: messages.sys },
3731
- { role: "user", content: messages.user }
3732
- ],
3733
- temperature: options.temperature,
3734
- max_tokens: options.maxTokens || 1e3
3735
- });
3736
- return response.choices[0]?.message?.content || "";
4335
+ try {
4336
+ const response = await client.chat.completions.create({
4337
+ model: modelName,
4338
+ messages: [
4339
+ { role: "system", content: messages.sys },
4340
+ { role: "user", content: messages.user }
4341
+ ],
4342
+ temperature: options.temperature,
4343
+ max_tokens: options.maxTokens || 1e3
4344
+ });
4345
+ const durationMs = Date.now() - startTime;
4346
+ const usage = response.usage;
4347
+ const inputTokens = usage?.prompt_tokens || 0;
4348
+ const outputTokens = usage?.completion_tokens || 0;
4349
+ llmUsageLogger.log({
4350
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4351
+ requestId,
4352
+ provider: "groq",
4353
+ model: modelName,
4354
+ method: "text",
4355
+ inputTokens,
4356
+ outputTokens,
4357
+ totalTokens: inputTokens + outputTokens,
4358
+ costUSD: llmUsageLogger.calculateCost(modelName, inputTokens, outputTokens),
4359
+ durationMs,
4360
+ success: true
4361
+ });
4362
+ return response.choices[0]?.message?.content || "";
4363
+ } catch (error) {
4364
+ const durationMs = Date.now() - startTime;
4365
+ llmUsageLogger.log({
4366
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4367
+ requestId,
4368
+ provider: "groq",
4369
+ model: modelName,
4370
+ method: "text",
4371
+ inputTokens: 0,
4372
+ outputTokens: 0,
4373
+ totalTokens: 0,
4374
+ costUSD: 0,
4375
+ durationMs,
4376
+ success: false,
4377
+ error: error instanceof Error ? error.message : String(error)
4378
+ });
4379
+ throw error;
4380
+ }
3737
4381
  }
3738
4382
  static async _groqStream(messages, modelName, options, json) {
4383
+ const startTime = Date.now();
4384
+ const requestId = llmUsageLogger.generateRequestId();
3739
4385
  const apiKey = options.apiKey || process.env.GROQ_API_KEY || "";
3740
4386
  const client = new Groq({
3741
4387
  apiKey
3742
4388
  });
3743
- const stream = await client.chat.completions.create({
3744
- model: modelName,
3745
- messages: [
3746
- { role: "system", content: messages.sys },
3747
- { role: "user", content: messages.user }
3748
- ],
3749
- temperature: options.temperature,
3750
- max_tokens: options.maxTokens || 1e3,
3751
- stream: true,
3752
- response_format: json ? { type: "json_object" } : void 0
3753
- });
3754
- let fullText = "";
3755
- for await (const chunk of stream) {
3756
- const text = chunk.choices[0]?.delta?.content || "";
3757
- if (text) {
3758
- fullText += text;
3759
- if (options.partial) {
3760
- options.partial(text);
4389
+ try {
4390
+ const stream = await client.chat.completions.create({
4391
+ model: modelName,
4392
+ messages: [
4393
+ { role: "system", content: messages.sys },
4394
+ { role: "user", content: messages.user }
4395
+ ],
4396
+ temperature: options.temperature,
4397
+ max_tokens: options.maxTokens || 1e3,
4398
+ stream: true,
4399
+ response_format: json ? { type: "json_object" } : void 0
4400
+ });
4401
+ let fullText = "";
4402
+ let inputTokens = 0;
4403
+ let outputTokens = 0;
4404
+ for await (const chunk of stream) {
4405
+ const text = chunk.choices[0]?.delta?.content || "";
4406
+ if (text) {
4407
+ fullText += text;
4408
+ if (options.partial) {
4409
+ options.partial(text);
4410
+ }
4411
+ }
4412
+ if (chunk.x_groq?.usage) {
4413
+ inputTokens = chunk.x_groq.usage.prompt_tokens || 0;
4414
+ outputTokens = chunk.x_groq.usage.completion_tokens || 0;
3761
4415
  }
3762
4416
  }
4417
+ const durationMs = Date.now() - startTime;
4418
+ if (inputTokens === 0) {
4419
+ const sysPrompt = typeof messages.sys === "string" ? messages.sys : messages.sys.map((b) => b.text).join("");
4420
+ inputTokens = Math.ceil((sysPrompt.length + messages.user.length) / 4);
4421
+ }
4422
+ if (outputTokens === 0) {
4423
+ outputTokens = Math.ceil(fullText.length / 4);
4424
+ }
4425
+ llmUsageLogger.log({
4426
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4427
+ requestId,
4428
+ provider: "groq",
4429
+ model: modelName,
4430
+ method: "stream",
4431
+ inputTokens,
4432
+ outputTokens,
4433
+ totalTokens: inputTokens + outputTokens,
4434
+ costUSD: llmUsageLogger.calculateCost(modelName, inputTokens, outputTokens),
4435
+ durationMs,
4436
+ success: true
4437
+ });
4438
+ if (json) {
4439
+ return this._parseJSON(fullText);
4440
+ }
4441
+ return fullText;
4442
+ } catch (error) {
4443
+ const durationMs = Date.now() - startTime;
4444
+ llmUsageLogger.log({
4445
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4446
+ requestId,
4447
+ provider: "groq",
4448
+ model: modelName,
4449
+ method: "stream",
4450
+ inputTokens: 0,
4451
+ outputTokens: 0,
4452
+ totalTokens: 0,
4453
+ costUSD: 0,
4454
+ durationMs,
4455
+ success: false,
4456
+ error: error instanceof Error ? error.message : String(error)
4457
+ });
4458
+ throw error;
3763
4459
  }
3764
- if (json) {
3765
- return this._parseJSON(fullText);
3766
- }
3767
- return fullText;
3768
4460
  }
3769
4461
  // ============================================================
3770
4462
  // GEMINI IMPLEMENTATION
3771
4463
  // ============================================================
3772
4464
  static async _geminiText(messages, modelName, options) {
4465
+ const startTime = Date.now();
4466
+ const requestId = llmUsageLogger.generateRequestId();
3773
4467
  const apiKey = options.apiKey || process.env.GEMINI_API_KEY || "";
3774
4468
  const genAI = new GoogleGenerativeAI(apiKey);
3775
4469
  const systemPrompt = typeof messages.sys === "string" ? messages.sys : messages.sys.map((block) => block.text).join("\n");
3776
- const model = genAI.getGenerativeModel({
3777
- model: modelName,
3778
- systemInstruction: systemPrompt,
3779
- generationConfig: {
3780
- maxOutputTokens: options.maxTokens || 1e3,
3781
- temperature: options.temperature,
3782
- topP: options.topP
3783
- }
3784
- });
3785
- const result = await model.generateContent(messages.user);
3786
- const response = await result.response;
3787
- return response.text();
4470
+ try {
4471
+ const model = genAI.getGenerativeModel({
4472
+ model: modelName,
4473
+ systemInstruction: systemPrompt,
4474
+ generationConfig: {
4475
+ maxOutputTokens: options.maxTokens || 1e3,
4476
+ temperature: options.temperature,
4477
+ topP: options.topP
4478
+ }
4479
+ });
4480
+ const result = await model.generateContent(messages.user);
4481
+ const response = await result.response;
4482
+ const text = response.text();
4483
+ const durationMs = Date.now() - startTime;
4484
+ const usage = response.usageMetadata;
4485
+ const inputTokens = usage?.promptTokenCount || Math.ceil((systemPrompt.length + messages.user.length) / 4);
4486
+ const outputTokens = usage?.candidatesTokenCount || Math.ceil(text.length / 4);
4487
+ llmUsageLogger.log({
4488
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4489
+ requestId,
4490
+ provider: "gemini",
4491
+ model: modelName,
4492
+ method: "text",
4493
+ inputTokens,
4494
+ outputTokens,
4495
+ totalTokens: inputTokens + outputTokens,
4496
+ costUSD: llmUsageLogger.calculateCost(modelName, inputTokens, outputTokens),
4497
+ durationMs,
4498
+ success: true
4499
+ });
4500
+ return text;
4501
+ } catch (error) {
4502
+ const durationMs = Date.now() - startTime;
4503
+ llmUsageLogger.log({
4504
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4505
+ requestId,
4506
+ provider: "gemini",
4507
+ model: modelName,
4508
+ method: "text",
4509
+ inputTokens: 0,
4510
+ outputTokens: 0,
4511
+ totalTokens: 0,
4512
+ costUSD: 0,
4513
+ durationMs,
4514
+ success: false,
4515
+ error: error instanceof Error ? error.message : String(error)
4516
+ });
4517
+ throw error;
4518
+ }
3788
4519
  }
3789
4520
  static async _geminiStream(messages, modelName, options, json) {
4521
+ const startTime = Date.now();
4522
+ const requestId = llmUsageLogger.generateRequestId();
3790
4523
  const apiKey = options.apiKey || process.env.GEMINI_API_KEY || "";
3791
4524
  const genAI = new GoogleGenerativeAI(apiKey);
3792
4525
  const systemPrompt = typeof messages.sys === "string" ? messages.sys : messages.sys.map((block) => block.text).join("\n");
3793
- const model = genAI.getGenerativeModel({
3794
- model: modelName,
3795
- systemInstruction: systemPrompt,
3796
- generationConfig: {
3797
- maxOutputTokens: options.maxTokens || 1e3,
3798
- temperature: options.temperature,
3799
- topP: options.topP,
3800
- responseMimeType: json ? "application/json" : void 0
3801
- }
3802
- });
3803
- const result = await model.generateContentStream(messages.user);
3804
- let fullText = "";
3805
- for await (const chunk of result.stream) {
3806
- const text = chunk.text();
3807
- if (text) {
3808
- fullText += text;
3809
- if (options.partial) {
3810
- options.partial(text);
4526
+ try {
4527
+ const model = genAI.getGenerativeModel({
4528
+ model: modelName,
4529
+ systemInstruction: systemPrompt,
4530
+ generationConfig: {
4531
+ maxOutputTokens: options.maxTokens || 1e3,
4532
+ temperature: options.temperature,
4533
+ topP: options.topP,
4534
+ responseMimeType: json ? "application/json" : void 0
4535
+ }
4536
+ });
4537
+ const result = await model.generateContentStream(messages.user);
4538
+ let fullText = "";
4539
+ let inputTokens = 0;
4540
+ let outputTokens = 0;
4541
+ for await (const chunk of result.stream) {
4542
+ const text = chunk.text();
4543
+ if (text) {
4544
+ fullText += text;
4545
+ if (options.partial) {
4546
+ options.partial(text);
4547
+ }
4548
+ }
4549
+ if (chunk.usageMetadata) {
4550
+ inputTokens = chunk.usageMetadata.promptTokenCount || 0;
4551
+ outputTokens = chunk.usageMetadata.candidatesTokenCount || 0;
3811
4552
  }
3812
4553
  }
4554
+ const durationMs = Date.now() - startTime;
4555
+ if (inputTokens === 0) {
4556
+ inputTokens = Math.ceil((systemPrompt.length + messages.user.length) / 4);
4557
+ }
4558
+ if (outputTokens === 0) {
4559
+ outputTokens = Math.ceil(fullText.length / 4);
4560
+ }
4561
+ llmUsageLogger.log({
4562
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4563
+ requestId,
4564
+ provider: "gemini",
4565
+ model: modelName,
4566
+ method: "stream",
4567
+ inputTokens,
4568
+ outputTokens,
4569
+ totalTokens: inputTokens + outputTokens,
4570
+ costUSD: llmUsageLogger.calculateCost(modelName, inputTokens, outputTokens),
4571
+ durationMs,
4572
+ success: true
4573
+ });
4574
+ if (json) {
4575
+ return this._parseJSON(fullText);
4576
+ }
4577
+ return fullText;
4578
+ } catch (error) {
4579
+ const durationMs = Date.now() - startTime;
4580
+ llmUsageLogger.log({
4581
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4582
+ requestId,
4583
+ provider: "gemini",
4584
+ model: modelName,
4585
+ method: "stream",
4586
+ inputTokens: 0,
4587
+ outputTokens: 0,
4588
+ totalTokens: 0,
4589
+ costUSD: 0,
4590
+ durationMs,
4591
+ success: false,
4592
+ error: error instanceof Error ? error.message : String(error)
4593
+ });
4594
+ throw error;
3813
4595
  }
3814
- if (json) {
3815
- return this._parseJSON(fullText);
3816
- }
3817
- return fullText;
3818
4596
  }
3819
4597
  static async _geminiStreamWithTools(messages, tools, toolHandler, modelName, options, maxIterations) {
3820
4598
  const apiKey = options.apiKey || process.env.GEMINI_API_KEY || "";
@@ -3908,51 +4686,138 @@ var LLM = class {
3908
4686
  // OPENAI IMPLEMENTATION
3909
4687
  // ============================================================
3910
4688
  static async _openaiText(messages, modelName, options) {
4689
+ const startTime = Date.now();
4690
+ const requestId = llmUsageLogger.generateRequestId();
3911
4691
  const apiKey = options.apiKey || process.env.OPENAI_API_KEY || "";
3912
4692
  const openai = new OpenAI({ apiKey });
3913
4693
  const systemPrompt = typeof messages.sys === "string" ? messages.sys : messages.sys.map((block) => block.text).join("\n");
3914
- const response = await openai.chat.completions.create({
3915
- model: modelName,
3916
- messages: [
3917
- { role: "system", content: systemPrompt },
3918
- { role: "user", content: messages.user }
3919
- ],
3920
- max_tokens: options.maxTokens || 1e3,
3921
- temperature: options.temperature,
3922
- top_p: options.topP
3923
- });
3924
- return response.choices[0]?.message?.content || "";
4694
+ try {
4695
+ const response = await openai.chat.completions.create({
4696
+ model: modelName,
4697
+ messages: [
4698
+ { role: "system", content: systemPrompt },
4699
+ { role: "user", content: messages.user }
4700
+ ],
4701
+ max_tokens: options.maxTokens || 1e3,
4702
+ temperature: options.temperature,
4703
+ top_p: options.topP
4704
+ });
4705
+ const durationMs = Date.now() - startTime;
4706
+ const usage = response.usage;
4707
+ const inputTokens = usage?.prompt_tokens || 0;
4708
+ const outputTokens = usage?.completion_tokens || 0;
4709
+ llmUsageLogger.log({
4710
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4711
+ requestId,
4712
+ provider: "openai",
4713
+ model: modelName,
4714
+ method: "text",
4715
+ inputTokens,
4716
+ outputTokens,
4717
+ totalTokens: inputTokens + outputTokens,
4718
+ costUSD: llmUsageLogger.calculateCost(modelName, inputTokens, outputTokens),
4719
+ durationMs,
4720
+ success: true
4721
+ });
4722
+ return response.choices[0]?.message?.content || "";
4723
+ } catch (error) {
4724
+ const durationMs = Date.now() - startTime;
4725
+ llmUsageLogger.log({
4726
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4727
+ requestId,
4728
+ provider: "openai",
4729
+ model: modelName,
4730
+ method: "text",
4731
+ inputTokens: 0,
4732
+ outputTokens: 0,
4733
+ totalTokens: 0,
4734
+ costUSD: 0,
4735
+ durationMs,
4736
+ success: false,
4737
+ error: error instanceof Error ? error.message : String(error)
4738
+ });
4739
+ throw error;
4740
+ }
3925
4741
  }
3926
4742
  static async _openaiStream(messages, modelName, options, json) {
4743
+ const startTime = Date.now();
4744
+ const requestId = llmUsageLogger.generateRequestId();
3927
4745
  const apiKey = options.apiKey || process.env.OPENAI_API_KEY || "";
3928
4746
  const openai = new OpenAI({ apiKey });
3929
4747
  const systemPrompt = typeof messages.sys === "string" ? messages.sys : messages.sys.map((block) => block.text).join("\n");
3930
- const stream = await openai.chat.completions.create({
3931
- model: modelName,
3932
- messages: [
3933
- { role: "system", content: systemPrompt },
3934
- { role: "user", content: messages.user }
3935
- ],
3936
- max_tokens: options.maxTokens || 1e3,
3937
- temperature: options.temperature,
3938
- top_p: options.topP,
3939
- response_format: json ? { type: "json_object" } : void 0,
3940
- stream: true
3941
- });
3942
- let fullText = "";
3943
- for await (const chunk of stream) {
3944
- const content = chunk.choices[0]?.delta?.content || "";
3945
- if (content) {
3946
- fullText += content;
3947
- if (options.partial) {
3948
- options.partial(content);
4748
+ try {
4749
+ const stream = await openai.chat.completions.create({
4750
+ model: modelName,
4751
+ messages: [
4752
+ { role: "system", content: systemPrompt },
4753
+ { role: "user", content: messages.user }
4754
+ ],
4755
+ max_tokens: options.maxTokens || 1e3,
4756
+ temperature: options.temperature,
4757
+ top_p: options.topP,
4758
+ response_format: json ? { type: "json_object" } : void 0,
4759
+ stream: true,
4760
+ stream_options: { include_usage: true }
4761
+ // Request usage info in stream
4762
+ });
4763
+ let fullText = "";
4764
+ let inputTokens = 0;
4765
+ let outputTokens = 0;
4766
+ for await (const chunk of stream) {
4767
+ const content = chunk.choices[0]?.delta?.content || "";
4768
+ if (content) {
4769
+ fullText += content;
4770
+ if (options.partial) {
4771
+ options.partial(content);
4772
+ }
4773
+ }
4774
+ if (chunk.usage) {
4775
+ inputTokens = chunk.usage.prompt_tokens || 0;
4776
+ outputTokens = chunk.usage.completion_tokens || 0;
3949
4777
  }
3950
4778
  }
4779
+ const durationMs = Date.now() - startTime;
4780
+ if (inputTokens === 0) {
4781
+ inputTokens = Math.ceil((systemPrompt.length + messages.user.length) / 4);
4782
+ }
4783
+ if (outputTokens === 0) {
4784
+ outputTokens = Math.ceil(fullText.length / 4);
4785
+ }
4786
+ llmUsageLogger.log({
4787
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4788
+ requestId,
4789
+ provider: "openai",
4790
+ model: modelName,
4791
+ method: "stream",
4792
+ inputTokens,
4793
+ outputTokens,
4794
+ totalTokens: inputTokens + outputTokens,
4795
+ costUSD: llmUsageLogger.calculateCost(modelName, inputTokens, outputTokens),
4796
+ durationMs,
4797
+ success: true
4798
+ });
4799
+ if (json) {
4800
+ return this._parseJSON(fullText);
4801
+ }
4802
+ return fullText;
4803
+ } catch (error) {
4804
+ const durationMs = Date.now() - startTime;
4805
+ llmUsageLogger.log({
4806
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4807
+ requestId,
4808
+ provider: "openai",
4809
+ model: modelName,
4810
+ method: "stream",
4811
+ inputTokens: 0,
4812
+ outputTokens: 0,
4813
+ totalTokens: 0,
4814
+ costUSD: 0,
4815
+ durationMs,
4816
+ success: false,
4817
+ error: error instanceof Error ? error.message : String(error)
4818
+ });
4819
+ throw error;
3951
4820
  }
3952
- if (json) {
3953
- return this._parseJSON(fullText);
3954
- }
3955
- return fullText;
3956
4821
  }
3957
4822
  static async _openaiStreamWithTools(messages, tools, toolHandler, modelName, options, maxIterations) {
3958
4823
  const apiKey = options.apiKey || process.env.OPENAI_API_KEY || "";
@@ -4098,11 +4963,9 @@ var LLM = class {
4098
4963
  closeChar = "]";
4099
4964
  }
4100
4965
  if (startIdx === -1) {
4101
- const preview = text.length > 500 ? text.substring(0, 500) + "..." : text;
4102
- throw new Error(`No JSON found in response. LLM returned plain text instead of JSON.
4103
-
4104
- Full response:
4105
- ${preview}`);
4966
+ const error = new Error(`No JSON found in response. LLM returned plain text instead of JSON.`);
4967
+ userPromptErrorLogger.logJsonParseError("LLM._parseJSON - No JSON structure found", text, error);
4968
+ throw error;
4106
4969
  }
4107
4970
  let depth = 0;
4108
4971
  let inString = false;
@@ -4129,24 +4992,17 @@ ${preview}`);
4129
4992
  if (endIdx !== -1) {
4130
4993
  jsonText = jsonText.substring(startIdx, endIdx + 1);
4131
4994
  } else {
4132
- const preview = text.length > 500 ? text.substring(0, 500) + "..." : text;
4133
- throw new Error(`Incomplete JSON - no matching closing ${closeChar} found.
4134
-
4135
- Full response:
4136
- ${preview}`);
4995
+ const error = new Error(`Incomplete JSON - no matching closing ${closeChar} found.`);
4996
+ userPromptErrorLogger.logJsonParseError("LLM._parseJSON - Incomplete JSON", text, error);
4997
+ throw error;
4137
4998
  }
4138
4999
  try {
4139
5000
  const repairedJson = jsonrepair(jsonText);
4140
5001
  return JSON.parse(repairedJson);
4141
5002
  } catch (error) {
4142
- const preview = text.length > 500 ? text.substring(0, 500) + "..." : text;
4143
- throw new Error(`Failed to parse JSON: ${error instanceof Error ? error.message : String(error)}
4144
-
4145
- Extracted JSON:
4146
- ${jsonText.substring(0, 300)}...
4147
-
4148
- Full response:
4149
- ${preview}`);
5003
+ const parseError = error instanceof Error ? error : new Error(String(error));
5004
+ userPromptErrorLogger.logJsonParseError("LLM._parseJSON - JSON parse/repair failed", text, parseError);
5005
+ throw new Error(`Failed to parse JSON: ${parseError.message}`);
4150
5006
  }
4151
5007
  }
4152
5008
  };
@@ -4494,8 +5350,41 @@ var conversation_search_default = ConversationSearch;
4494
5350
  var BaseLLM = class {
4495
5351
  constructor(config) {
4496
5352
  this.model = config?.model || this.getDefaultModel();
5353
+ this.fastModel = config?.fastModel || this.getDefaultFastModel();
4497
5354
  this.defaultLimit = config?.defaultLimit || 50;
4498
5355
  this.apiKey = config?.apiKey;
5356
+ this.modelStrategy = config?.modelStrategy || "fast";
5357
+ }
5358
+ /**
5359
+ * Get the appropriate model based on task type and model strategy
5360
+ * @param taskType - 'complex' for text generation/matching, 'simple' for classification/actions
5361
+ * @returns The model string to use for this task
5362
+ */
5363
+ getModelForTask(taskType) {
5364
+ switch (this.modelStrategy) {
5365
+ case "best":
5366
+ return this.model;
5367
+ case "fast":
5368
+ return this.fastModel;
5369
+ case "balanced":
5370
+ default:
5371
+ return taskType === "complex" ? this.model : this.fastModel;
5372
+ }
5373
+ }
5374
+ /**
5375
+ * Set the model strategy at runtime
5376
+ * @param strategy - 'best', 'fast', or 'balanced'
5377
+ */
5378
+ setModelStrategy(strategy) {
5379
+ this.modelStrategy = strategy;
5380
+ logger.info(`[${this.getProviderName()}] Model strategy set to: ${strategy}`);
5381
+ }
5382
+ /**
5383
+ * Get the current model strategy
5384
+ * @returns The current model strategy
5385
+ */
5386
+ getModelStrategy() {
5387
+ return this.modelStrategy;
4499
5388
  }
4500
5389
  /**
4501
5390
  * Get the API key (from instance, parameter, or environment)
@@ -4680,7 +5569,7 @@ ${JSON.stringify(tool.requiredFields || [], null, 2)}`;
4680
5569
  user: prompts.user
4681
5570
  },
4682
5571
  {
4683
- model: this.model,
5572
+ model: this.getModelForTask("complex"),
4684
5573
  maxTokens: 8192,
4685
5574
  temperature: 0.2,
4686
5575
  apiKey: this.getApiKey(apiKey),
@@ -4803,7 +5692,7 @@ ${JSON.stringify(tool.requiredFields || [], null, 2)}`;
4803
5692
  user: prompts.user
4804
5693
  },
4805
5694
  {
4806
- model: this.model,
5695
+ model: this.getModelForTask("simple"),
4807
5696
  maxTokens: 1500,
4808
5697
  temperature: 0.2,
4809
5698
  apiKey: this.getApiKey(apiKey)
@@ -4864,7 +5753,7 @@ ${JSON.stringify(tool.requiredFields || [], null, 2)}`;
4864
5753
  user: prompts.user
4865
5754
  },
4866
5755
  {
4867
- model: this.model,
5756
+ model: this.getModelForTask("complex"),
4868
5757
  maxTokens: 3e3,
4869
5758
  temperature: 0.2,
4870
5759
  apiKey: this.getApiKey(apiKey)
@@ -5235,6 +6124,7 @@ ${sql}
5235
6124
  const errorMsg = error instanceof Error ? error.message : String(error);
5236
6125
  logger.error(`[${this.getProviderName()}] Query execution failed (attempt ${attempts}/${MAX_QUERY_ATTEMPTS}): ${errorMsg}`);
5237
6126
  logCollector?.error(`Query failed (attempt ${attempts}/${MAX_QUERY_ATTEMPTS}): ${errorMsg}`);
6127
+ userPromptErrorLogger.logSqlError(sql, error instanceof Error ? error : new Error(errorMsg), Object.keys(params).length > 0 ? Object.values(params) : void 0);
5238
6128
  if (wrappedStreamCallback) {
5239
6129
  wrappedStreamCallback(`\u274C **Query execution failed:**
5240
6130
  \`\`\`
@@ -5325,6 +6215,7 @@ Please try rephrasing your request or contact support.
5325
6215
  const errorMsg = error instanceof Error ? error.message : String(error);
5326
6216
  logger.error(`[${this.getProviderName()}] External tool ${externalTool.name} failed (attempt ${attempts}/${MAX_TOOL_ATTEMPTS}): ${errorMsg}`);
5327
6217
  logCollector?.error(`\u2717 ${externalTool.name} failed: ${errorMsg}`);
6218
+ userPromptErrorLogger.logToolError(externalTool.name, toolInput, error instanceof Error ? error : new Error(errorMsg));
5328
6219
  if (wrappedStreamCallback) {
5329
6220
  wrappedStreamCallback(`\u274C **${externalTool.name} failed:**
5330
6221
  \`\`\`
@@ -5352,7 +6243,7 @@ ${errorMsg}
5352
6243
  tools,
5353
6244
  toolHandler,
5354
6245
  {
5355
- model: this.model,
6246
+ model: this.getModelForTask("complex"),
5356
6247
  maxTokens: 4e3,
5357
6248
  temperature: 0.7,
5358
6249
  apiKey: this.getApiKey(apiKey),
@@ -5397,6 +6288,21 @@ ${errorMsg}
5397
6288
  if (category === "general") {
5398
6289
  logger.info(`[${this.getProviderName()}] Skipping component generation for general/conversational question`);
5399
6290
  logCollector?.info("Skipping component generation for general question");
6291
+ logger.info(`[${this.getProviderName()}] Generating actions for general question...`);
6292
+ const nextQuestions = await this.generateNextQuestions(
6293
+ userPrompt,
6294
+ null,
6295
+ // no component
6296
+ void 0,
6297
+ // no component data
6298
+ apiKey,
6299
+ logCollector,
6300
+ conversationHistory,
6301
+ textResponse
6302
+ // pass text response as context
6303
+ );
6304
+ actions = convertQuestionsToActions(nextQuestions);
6305
+ logger.info(`[${this.getProviderName()}] Generated ${actions.length} follow-up actions for general question`);
5400
6306
  } else if (components && components.length > 0) {
5401
6307
  logger.info(`[${this.getProviderName()}] Matching components from text response...`);
5402
6308
  const componentStreamCallback = wrappedStreamCallback && category !== "data_modification" ? (component) => {
@@ -5467,6 +6373,13 @@ ${errorMsg}
5467
6373
  const errorMsg = error instanceof Error ? error.message : String(error);
5468
6374
  logger.error(`[${this.getProviderName()}] Error generating text response: ${errorMsg}`);
5469
6375
  logCollector?.error(`Error generating text response: ${errorMsg}`);
6376
+ userPromptErrorLogger.logLlmError(
6377
+ this.getProviderName(),
6378
+ this.model,
6379
+ "generateTextResponse",
6380
+ error instanceof Error ? error : new Error(errorMsg),
6381
+ { userPrompt }
6382
+ );
5470
6383
  errors.push(errorMsg);
5471
6384
  return {
5472
6385
  success: false,
@@ -5548,10 +6461,18 @@ ${errorMsg}
5548
6461
  logger.info(`[${this.getProviderName()}] \u2713 100% match - returning UI block directly without adaptation`);
5549
6462
  logCollector?.info(`\u2713 Exact match (${(conversationMatch.similarity * 100).toFixed(2)}%) - returning cached result`);
5550
6463
  logCollector?.info(`Total time taken: ${elapsedTime2}ms (${(elapsedTime2 / 1e3).toFixed(2)}s)`);
6464
+ if (streamCallback && cachedTextResponse) {
6465
+ logger.info(`[${this.getProviderName()}] Streaming cached text response to frontend`);
6466
+ streamCallback(cachedTextResponse);
6467
+ }
6468
+ const cachedActions = conversationMatch.uiBlock?.actions || [];
5551
6469
  return {
5552
6470
  success: true,
5553
6471
  data: {
6472
+ text: cachedTextResponse,
5554
6473
  component,
6474
+ matchedComponents: component?.props?.config?.components || [],
6475
+ actions: cachedActions,
5555
6476
  reasoning: `Exact match from previous conversation (${(conversationMatch.similarity * 100).toFixed(2)}% similarity)`,
5556
6477
  method: `${this.getProviderName()}-semantic-match-exact`,
5557
6478
  semanticSimilarity: conversationMatch.similarity
@@ -5574,10 +6495,18 @@ ${errorMsg}
5574
6495
  logger.info(`[${this.getProviderName()}] Total time taken: ${elapsedTime2}ms (${(elapsedTime2 / 1e3).toFixed(2)}s)`);
5575
6496
  logCollector?.info(`\u2713 UI block adapted successfully`);
5576
6497
  logCollector?.info(`Total time taken: ${elapsedTime2}ms (${(elapsedTime2 / 1e3).toFixed(2)}s)`);
6498
+ if (streamCallback && cachedTextResponse) {
6499
+ logger.info(`[${this.getProviderName()}] Streaming cached text response to frontend (adapted match)`);
6500
+ streamCallback(cachedTextResponse);
6501
+ }
6502
+ const cachedActions = conversationMatch.uiBlock?.actions || [];
5577
6503
  return {
5578
6504
  success: true,
5579
6505
  data: {
6506
+ text: cachedTextResponse,
5580
6507
  component: adaptResult.adaptedComponent,
6508
+ matchedComponents: adaptResult.adaptedComponent?.props?.config?.components || [],
6509
+ actions: cachedActions,
5581
6510
  reasoning: `Adapted from previous conversation: ${originalPrompt}`,
5582
6511
  method: `${this.getProviderName()}-semantic-match`,
5583
6512
  semanticSimilarity: conversationMatch.similarity,
@@ -5669,6 +6598,11 @@ ${errorMsg}
5669
6598
  logger.error(`[${this.getProviderName()}] Error in handleUserRequest: ${errorMsg}`);
5670
6599
  logger.debug(`[${this.getProviderName()}] Error details:`, error);
5671
6600
  logCollector?.error(`Error processing request: ${errorMsg}`);
6601
+ userPromptErrorLogger.logError(
6602
+ "handleUserRequest",
6603
+ error instanceof Error ? error : new Error(errorMsg),
6604
+ { userPrompt }
6605
+ );
5672
6606
  const elapsedTime = Date.now() - startTime;
5673
6607
  logger.info(`[${this.getProviderName()}] Total time taken: ${elapsedTime}ms (${(elapsedTime / 1e3).toFixed(2)}s)`);
5674
6608
  logCollector?.info(`Total time taken: ${elapsedTime}ms (${(elapsedTime / 1e3).toFixed(2)}s)`);
@@ -5685,15 +6619,26 @@ ${errorMsg}
5685
6619
  /**
5686
6620
  * Generate next questions that the user might ask based on the original prompt and generated component
5687
6621
  * This helps provide intelligent suggestions for follow-up queries
6622
+ * For general/conversational questions without components, pass textResponse instead
5688
6623
  */
5689
- async generateNextQuestions(originalUserPrompt, component, componentData, apiKey, logCollector, conversationHistory) {
6624
+ async generateNextQuestions(originalUserPrompt, component, componentData, apiKey, logCollector, conversationHistory, textResponse) {
5690
6625
  try {
5691
- const component_info = `
6626
+ let component_info;
6627
+ if (component) {
6628
+ component_info = `
5692
6629
  Component Name: ${component.name}
5693
6630
  Component Type: ${component.type}
5694
6631
  Component Description: ${component.description || "No description"}
5695
6632
  Component Props: ${component.props ? JSON.stringify(component.props, null, 2) : "No props"}
5696
6633
  `;
6634
+ } else if (textResponse) {
6635
+ component_info = `
6636
+ Response Type: Text/Conversational Response
6637
+ Response Content: ${textResponse.substring(0, 1e3)}${textResponse.length > 1e3 ? "..." : ""}
6638
+ `;
6639
+ } else {
6640
+ component_info = "No component or response context available";
6641
+ }
5697
6642
  const component_data = componentData ? `Component Data: ${JSON.stringify(componentData, null, 2)}` : "";
5698
6643
  const prompts = await promptLoader.loadPrompts("actions", {
5699
6644
  ORIGINAL_USER_PROMPT: originalUserPrompt,
@@ -5707,7 +6652,7 @@ ${errorMsg}
5707
6652
  user: prompts.user
5708
6653
  },
5709
6654
  {
5710
- model: this.model,
6655
+ model: this.getModelForTask("simple"),
5711
6656
  maxTokens: 1200,
5712
6657
  temperature: 0.7,
5713
6658
  apiKey: this.getApiKey(apiKey)
@@ -5744,6 +6689,9 @@ var GroqLLM = class extends BaseLLM {
5744
6689
  getDefaultModel() {
5745
6690
  return "groq/openai/gpt-oss-120b";
5746
6691
  }
6692
+ getDefaultFastModel() {
6693
+ return "groq/llama-3.1-8b-instant";
6694
+ }
5747
6695
  getDefaultApiKey() {
5748
6696
  return process.env.GROQ_API_KEY;
5749
6697
  }
@@ -5763,6 +6711,9 @@ var AnthropicLLM = class extends BaseLLM {
5763
6711
  getDefaultModel() {
5764
6712
  return "anthropic/claude-sonnet-4-5-20250929";
5765
6713
  }
6714
+ getDefaultFastModel() {
6715
+ return "anthropic/claude-haiku-4-5-20251001";
6716
+ }
5766
6717
  getDefaultApiKey() {
5767
6718
  return process.env.ANTHROPIC_API_KEY;
5768
6719
  }
@@ -5782,6 +6733,9 @@ var GeminiLLM = class extends BaseLLM {
5782
6733
  getDefaultModel() {
5783
6734
  return "gemini/gemini-2.5-flash";
5784
6735
  }
6736
+ getDefaultFastModel() {
6737
+ return "gemini/gemini-2.0-flash-exp";
6738
+ }
5785
6739
  getDefaultApiKey() {
5786
6740
  return process.env.GEMINI_API_KEY;
5787
6741
  }
@@ -5801,6 +6755,9 @@ var OpenAILLM = class extends BaseLLM {
5801
6755
  getDefaultModel() {
5802
6756
  return "openai/gpt-4.1";
5803
6757
  }
6758
+ getDefaultFastModel() {
6759
+ return "openai/gpt-4o-mini";
6760
+ }
5804
6761
  getDefaultApiKey() {
5805
6762
  return process.env.OPENAI_API_KEY;
5806
6763
  }
@@ -6307,6 +7264,9 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
6307
7264
  const prompt = payload.prompt;
6308
7265
  const SA_RUNTIME = payload.SA_RUNTIME;
6309
7266
  const wsId = userPromptRequest.from.id || "unknown";
7267
+ const promptContext = `User Prompt: ${prompt?.substring(0, 50)}${(prompt?.length || 0) > 50 ? "..." : ""}`;
7268
+ llmUsageLogger.resetLogFile(promptContext);
7269
+ userPromptErrorLogger.resetLogFile(promptContext);
6310
7270
  if (!SA_RUNTIME) {
6311
7271
  errors.push("SA_RUNTIME is required");
6312
7272
  }
@@ -6380,6 +7340,14 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
6380
7340
  const uiBlockId = existingUiBlockId;
6381
7341
  if (!userResponse.success) {
6382
7342
  logger.error(`User prompt request failed with errors: ${userResponse.errors.join(", ")}`);
7343
+ userPromptErrorLogger.logError("User Response Failed", userResponse.errors.join("\n"), {
7344
+ prompt,
7345
+ uiBlockId,
7346
+ threadId,
7347
+ responseData: userResponse.data
7348
+ });
7349
+ userPromptErrorLogger.writeSummary();
7350
+ llmUsageLogger.logSessionSummary(`FAILED: ${prompt?.substring(0, 30)}`);
6383
7351
  return {
6384
7352
  success: false,
6385
7353
  data: userResponse.data,
@@ -6455,6 +7423,7 @@ var get_user_request = async (data, components, sendMessage, anthropicApiKey, gr
6455
7423
  }
6456
7424
  }
6457
7425
  }
7426
+ llmUsageLogger.logSessionSummary(prompt?.substring(0, 50));
6458
7427
  return {
6459
7428
  success: userResponse.success,
6460
7429
  data: userResponse.data,
@@ -9357,8 +10326,8 @@ function sendDashCompResponse(id, res, sendMessage, clientId) {
9357
10326
 
9358
10327
  // src/auth/user-manager.ts
9359
10328
  init_logger();
9360
- import fs5 from "fs";
9361
- import path4 from "path";
10329
+ import fs7 from "fs";
10330
+ import path6 from "path";
9362
10331
  import os from "os";
9363
10332
  var UserManager = class {
9364
10333
  /**
@@ -9371,7 +10340,7 @@ var UserManager = class {
9371
10340
  this.hasChanged = false;
9372
10341
  this.syncInterval = null;
9373
10342
  this.isInitialized = false;
9374
- this.filePath = path4.join(os.homedir(), ".superatom", "projects", projectId, "users.json");
10343
+ this.filePath = path6.join(os.homedir(), ".superatom", "projects", projectId, "users.json");
9375
10344
  this.syncIntervalMs = syncIntervalMs;
9376
10345
  }
9377
10346
  /**
@@ -9396,20 +10365,20 @@ var UserManager = class {
9396
10365
  */
9397
10366
  async loadUsersFromFile() {
9398
10367
  try {
9399
- const dir = path4.dirname(this.filePath);
9400
- if (!fs5.existsSync(dir)) {
10368
+ const dir = path6.dirname(this.filePath);
10369
+ if (!fs7.existsSync(dir)) {
9401
10370
  logger.info(`Creating directory structure: ${dir}`);
9402
- fs5.mkdirSync(dir, { recursive: true });
10371
+ fs7.mkdirSync(dir, { recursive: true });
9403
10372
  }
9404
- if (!fs5.existsSync(this.filePath)) {
10373
+ if (!fs7.existsSync(this.filePath)) {
9405
10374
  logger.info(`Users file does not exist at ${this.filePath}, creating with empty users`);
9406
10375
  const initialData = { users: [] };
9407
- fs5.writeFileSync(this.filePath, JSON.stringify(initialData, null, 4));
10376
+ fs7.writeFileSync(this.filePath, JSON.stringify(initialData, null, 4));
9408
10377
  this.users = [];
9409
10378
  this.hasChanged = false;
9410
10379
  return;
9411
10380
  }
9412
- const fileContent = fs5.readFileSync(this.filePath, "utf-8");
10381
+ const fileContent = fs7.readFileSync(this.filePath, "utf-8");
9413
10382
  const rawData = JSON.parse(fileContent);
9414
10383
  const validatedData = UsersDataSchema.parse(rawData);
9415
10384
  this.users = validatedData.users;
@@ -9428,16 +10397,16 @@ var UserManager = class {
9428
10397
  return;
9429
10398
  }
9430
10399
  try {
9431
- const dir = path4.dirname(this.filePath);
9432
- if (!fs5.existsSync(dir)) {
9433
- fs5.mkdirSync(dir, { recursive: true });
10400
+ const dir = path6.dirname(this.filePath);
10401
+ if (!fs7.existsSync(dir)) {
10402
+ fs7.mkdirSync(dir, { recursive: true });
9434
10403
  }
9435
10404
  const usersToSave = this.users.map((user) => {
9436
10405
  const { wsIds, ...userWithoutWsIds } = user;
9437
10406
  return userWithoutWsIds;
9438
10407
  });
9439
10408
  const data = { users: usersToSave };
9440
- fs5.writeFileSync(this.filePath, JSON.stringify(data, null, 4));
10409
+ fs7.writeFileSync(this.filePath, JSON.stringify(data, null, 4));
9441
10410
  this.hasChanged = false;
9442
10411
  logger.debug(`Synced ${this.users.length} users to file (wsIds excluded)`);
9443
10412
  } catch (error) {
@@ -9656,8 +10625,8 @@ var UserManager = class {
9656
10625
 
9657
10626
  // src/dashboards/dashboard-manager.ts
9658
10627
  init_logger();
9659
- import fs6 from "fs";
9660
- import path5 from "path";
10628
+ import fs8 from "fs";
10629
+ import path7 from "path";
9661
10630
  import os2 from "os";
9662
10631
  var DashboardManager = class {
9663
10632
  /**
@@ -9666,7 +10635,7 @@ var DashboardManager = class {
9666
10635
  */
9667
10636
  constructor(projectId = "snowflake-dataset") {
9668
10637
  this.projectId = projectId;
9669
- this.dashboardsBasePath = path5.join(
10638
+ this.dashboardsBasePath = path7.join(
9670
10639
  os2.homedir(),
9671
10640
  ".superatom",
9672
10641
  "projects",
@@ -9680,7 +10649,7 @@ var DashboardManager = class {
9680
10649
  * @returns Full path to dashboard data.json file
9681
10650
  */
9682
10651
  getDashboardPath(dashboardId) {
9683
- return path5.join(this.dashboardsBasePath, dashboardId, "data.json");
10652
+ return path7.join(this.dashboardsBasePath, dashboardId, "data.json");
9684
10653
  }
9685
10654
  /**
9686
10655
  * Create a new dashboard
@@ -9690,13 +10659,13 @@ var DashboardManager = class {
9690
10659
  */
9691
10660
  createDashboard(dashboardId, dashboard) {
9692
10661
  const dashboardPath = this.getDashboardPath(dashboardId);
9693
- const dashboardDir = path5.dirname(dashboardPath);
9694
- if (fs6.existsSync(dashboardPath)) {
10662
+ const dashboardDir = path7.dirname(dashboardPath);
10663
+ if (fs8.existsSync(dashboardPath)) {
9695
10664
  throw new Error(`Dashboard '${dashboardId}' already exists`);
9696
10665
  }
9697
10666
  const validated = DSLRendererPropsSchema.parse(dashboard);
9698
- fs6.mkdirSync(dashboardDir, { recursive: true });
9699
- fs6.writeFileSync(dashboardPath, JSON.stringify(validated, null, 4));
10667
+ fs8.mkdirSync(dashboardDir, { recursive: true });
10668
+ fs8.writeFileSync(dashboardPath, JSON.stringify(validated, null, 4));
9700
10669
  logger.info(`Dashboard created: ${dashboardId}`);
9701
10670
  return validated;
9702
10671
  }
@@ -9707,12 +10676,12 @@ var DashboardManager = class {
9707
10676
  */
9708
10677
  getDashboard(dashboardId) {
9709
10678
  const dashboardPath = this.getDashboardPath(dashboardId);
9710
- if (!fs6.existsSync(dashboardPath)) {
10679
+ if (!fs8.existsSync(dashboardPath)) {
9711
10680
  logger.warn(`Dashboard not found: ${dashboardId}`);
9712
10681
  return null;
9713
10682
  }
9714
10683
  try {
9715
- const fileContent = fs6.readFileSync(dashboardPath, "utf-8");
10684
+ const fileContent = fs8.readFileSync(dashboardPath, "utf-8");
9716
10685
  const dashboard = JSON.parse(fileContent);
9717
10686
  const validated = DSLRendererPropsSchema.parse(dashboard);
9718
10687
  return validated;
@@ -9726,16 +10695,16 @@ var DashboardManager = class {
9726
10695
  * @returns Array of dashboard objects with their IDs
9727
10696
  */
9728
10697
  getAllDashboards() {
9729
- if (!fs6.existsSync(this.dashboardsBasePath)) {
9730
- fs6.mkdirSync(this.dashboardsBasePath, { recursive: true });
10698
+ if (!fs8.existsSync(this.dashboardsBasePath)) {
10699
+ fs8.mkdirSync(this.dashboardsBasePath, { recursive: true });
9731
10700
  return [];
9732
10701
  }
9733
10702
  const dashboards = [];
9734
10703
  try {
9735
- const dashboardDirs = fs6.readdirSync(this.dashboardsBasePath);
10704
+ const dashboardDirs = fs8.readdirSync(this.dashboardsBasePath);
9736
10705
  for (const dashboardId of dashboardDirs) {
9737
10706
  const dashboardPath = this.getDashboardPath(dashboardId);
9738
- if (fs6.existsSync(dashboardPath)) {
10707
+ if (fs8.existsSync(dashboardPath)) {
9739
10708
  const dashboard = this.getDashboard(dashboardId);
9740
10709
  if (dashboard) {
9741
10710
  dashboards.push({ dashboardId, dashboard });
@@ -9757,13 +10726,13 @@ var DashboardManager = class {
9757
10726
  */
9758
10727
  updateDashboard(dashboardId, dashboard) {
9759
10728
  const dashboardPath = this.getDashboardPath(dashboardId);
9760
- if (!fs6.existsSync(dashboardPath)) {
10729
+ if (!fs8.existsSync(dashboardPath)) {
9761
10730
  logger.warn(`Dashboard not found for update: ${dashboardId}`);
9762
10731
  return null;
9763
10732
  }
9764
10733
  try {
9765
10734
  const validated = DSLRendererPropsSchema.parse(dashboard);
9766
- fs6.writeFileSync(dashboardPath, JSON.stringify(validated, null, 4));
10735
+ fs8.writeFileSync(dashboardPath, JSON.stringify(validated, null, 4));
9767
10736
  logger.info(`Dashboard updated: ${dashboardId}`);
9768
10737
  return validated;
9769
10738
  } catch (error) {
@@ -9778,13 +10747,13 @@ var DashboardManager = class {
9778
10747
  */
9779
10748
  deleteDashboard(dashboardId) {
9780
10749
  const dashboardPath = this.getDashboardPath(dashboardId);
9781
- const dashboardDir = path5.dirname(dashboardPath);
9782
- if (!fs6.existsSync(dashboardPath)) {
10750
+ const dashboardDir = path7.dirname(dashboardPath);
10751
+ if (!fs8.existsSync(dashboardPath)) {
9783
10752
  logger.warn(`Dashboard not found for deletion: ${dashboardId}`);
9784
10753
  return false;
9785
10754
  }
9786
10755
  try {
9787
- fs6.rmSync(dashboardDir, { recursive: true, force: true });
10756
+ fs8.rmSync(dashboardDir, { recursive: true, force: true });
9788
10757
  logger.info(`Dashboard deleted: ${dashboardId}`);
9789
10758
  return true;
9790
10759
  } catch (error) {
@@ -9799,21 +10768,21 @@ var DashboardManager = class {
9799
10768
  */
9800
10769
  dashboardExists(dashboardId) {
9801
10770
  const dashboardPath = this.getDashboardPath(dashboardId);
9802
- return fs6.existsSync(dashboardPath);
10771
+ return fs8.existsSync(dashboardPath);
9803
10772
  }
9804
10773
  /**
9805
10774
  * Get dashboard count
9806
10775
  * @returns Number of dashboards
9807
10776
  */
9808
10777
  getDashboardCount() {
9809
- if (!fs6.existsSync(this.dashboardsBasePath)) {
10778
+ if (!fs8.existsSync(this.dashboardsBasePath)) {
9810
10779
  return 0;
9811
10780
  }
9812
10781
  try {
9813
- const dashboardDirs = fs6.readdirSync(this.dashboardsBasePath);
10782
+ const dashboardDirs = fs8.readdirSync(this.dashboardsBasePath);
9814
10783
  return dashboardDirs.filter((dir) => {
9815
10784
  const dashboardPath = this.getDashboardPath(dir);
9816
- return fs6.existsSync(dashboardPath);
10785
+ return fs8.existsSync(dashboardPath);
9817
10786
  }).length;
9818
10787
  } catch (error) {
9819
10788
  logger.error("Failed to get dashboard count:", error);
@@ -9824,8 +10793,8 @@ var DashboardManager = class {
9824
10793
 
9825
10794
  // src/reports/report-manager.ts
9826
10795
  init_logger();
9827
- import fs7 from "fs";
9828
- import path6 from "path";
10796
+ import fs9 from "fs";
10797
+ import path8 from "path";
9829
10798
  import os3 from "os";
9830
10799
  var ReportManager = class {
9831
10800
  /**
@@ -9834,7 +10803,7 @@ var ReportManager = class {
9834
10803
  */
9835
10804
  constructor(projectId = "snowflake-dataset") {
9836
10805
  this.projectId = projectId;
9837
- this.reportsBasePath = path6.join(
10806
+ this.reportsBasePath = path8.join(
9838
10807
  os3.homedir(),
9839
10808
  ".superatom",
9840
10809
  "projects",
@@ -9848,7 +10817,7 @@ var ReportManager = class {
9848
10817
  * @returns Full path to report data.json file
9849
10818
  */
9850
10819
  getReportPath(reportId) {
9851
- return path6.join(this.reportsBasePath, reportId, "data.json");
10820
+ return path8.join(this.reportsBasePath, reportId, "data.json");
9852
10821
  }
9853
10822
  /**
9854
10823
  * Create a new report
@@ -9858,13 +10827,13 @@ var ReportManager = class {
9858
10827
  */
9859
10828
  createReport(reportId, report) {
9860
10829
  const reportPath = this.getReportPath(reportId);
9861
- const reportDir = path6.dirname(reportPath);
9862
- if (fs7.existsSync(reportPath)) {
10830
+ const reportDir = path8.dirname(reportPath);
10831
+ if (fs9.existsSync(reportPath)) {
9863
10832
  throw new Error(`Report '${reportId}' already exists`);
9864
10833
  }
9865
10834
  const validated = DSLRendererPropsSchema2.parse(report);
9866
- fs7.mkdirSync(reportDir, { recursive: true });
9867
- fs7.writeFileSync(reportPath, JSON.stringify(validated, null, 4));
10835
+ fs9.mkdirSync(reportDir, { recursive: true });
10836
+ fs9.writeFileSync(reportPath, JSON.stringify(validated, null, 4));
9868
10837
  logger.info(`Report created: ${reportId}`);
9869
10838
  return validated;
9870
10839
  }
@@ -9875,12 +10844,12 @@ var ReportManager = class {
9875
10844
  */
9876
10845
  getReport(reportId) {
9877
10846
  const reportPath = this.getReportPath(reportId);
9878
- if (!fs7.existsSync(reportPath)) {
10847
+ if (!fs9.existsSync(reportPath)) {
9879
10848
  logger.warn(`Report not found: ${reportId}`);
9880
10849
  return null;
9881
10850
  }
9882
10851
  try {
9883
- const fileContent = fs7.readFileSync(reportPath, "utf-8");
10852
+ const fileContent = fs9.readFileSync(reportPath, "utf-8");
9884
10853
  const report = JSON.parse(fileContent);
9885
10854
  const validated = DSLRendererPropsSchema2.parse(report);
9886
10855
  return validated;
@@ -9894,16 +10863,16 @@ var ReportManager = class {
9894
10863
  * @returns Array of report objects with their IDs
9895
10864
  */
9896
10865
  getAllReports() {
9897
- if (!fs7.existsSync(this.reportsBasePath)) {
9898
- fs7.mkdirSync(this.reportsBasePath, { recursive: true });
10866
+ if (!fs9.existsSync(this.reportsBasePath)) {
10867
+ fs9.mkdirSync(this.reportsBasePath, { recursive: true });
9899
10868
  return [];
9900
10869
  }
9901
10870
  const reports = [];
9902
10871
  try {
9903
- const reportDirs = fs7.readdirSync(this.reportsBasePath);
10872
+ const reportDirs = fs9.readdirSync(this.reportsBasePath);
9904
10873
  for (const reportId of reportDirs) {
9905
10874
  const reportPath = this.getReportPath(reportId);
9906
- if (fs7.existsSync(reportPath)) {
10875
+ if (fs9.existsSync(reportPath)) {
9907
10876
  const report = this.getReport(reportId);
9908
10877
  if (report) {
9909
10878
  reports.push({ reportId, report });
@@ -9925,13 +10894,13 @@ var ReportManager = class {
9925
10894
  */
9926
10895
  updateReport(reportId, report) {
9927
10896
  const reportPath = this.getReportPath(reportId);
9928
- if (!fs7.existsSync(reportPath)) {
10897
+ if (!fs9.existsSync(reportPath)) {
9929
10898
  logger.warn(`Report not found for update: ${reportId}`);
9930
10899
  return null;
9931
10900
  }
9932
10901
  try {
9933
10902
  const validated = DSLRendererPropsSchema2.parse(report);
9934
- fs7.writeFileSync(reportPath, JSON.stringify(validated, null, 4));
10903
+ fs9.writeFileSync(reportPath, JSON.stringify(validated, null, 4));
9935
10904
  logger.info(`Report updated: ${reportId}`);
9936
10905
  return validated;
9937
10906
  } catch (error) {
@@ -9946,13 +10915,13 @@ var ReportManager = class {
9946
10915
  */
9947
10916
  deleteReport(reportId) {
9948
10917
  const reportPath = this.getReportPath(reportId);
9949
- const reportDir = path6.dirname(reportPath);
9950
- if (!fs7.existsSync(reportPath)) {
10918
+ const reportDir = path8.dirname(reportPath);
10919
+ if (!fs9.existsSync(reportPath)) {
9951
10920
  logger.warn(`Report not found for deletion: ${reportId}`);
9952
10921
  return false;
9953
10922
  }
9954
10923
  try {
9955
- fs7.rmSync(reportDir, { recursive: true, force: true });
10924
+ fs9.rmSync(reportDir, { recursive: true, force: true });
9956
10925
  logger.info(`Report deleted: ${reportId}`);
9957
10926
  return true;
9958
10927
  } catch (error) {
@@ -9967,21 +10936,21 @@ var ReportManager = class {
9967
10936
  */
9968
10937
  reportExists(reportId) {
9969
10938
  const reportPath = this.getReportPath(reportId);
9970
- return fs7.existsSync(reportPath);
10939
+ return fs9.existsSync(reportPath);
9971
10940
  }
9972
10941
  /**
9973
10942
  * Get report count
9974
10943
  * @returns Number of reports
9975
10944
  */
9976
10945
  getReportCount() {
9977
- if (!fs7.existsSync(this.reportsBasePath)) {
10946
+ if (!fs9.existsSync(this.reportsBasePath)) {
9978
10947
  return 0;
9979
10948
  }
9980
10949
  try {
9981
- const reportDirs = fs7.readdirSync(this.reportsBasePath);
10950
+ const reportDirs = fs9.readdirSync(this.reportsBasePath);
9982
10951
  return reportDirs.filter((dir) => {
9983
10952
  const reportPath = this.getReportPath(dir);
9984
- return fs7.existsSync(reportPath);
10953
+ return fs9.existsSync(reportPath);
9985
10954
  }).length;
9986
10955
  } catch (error) {
9987
10956
  logger.error("Failed to get report count:", error);
@@ -10210,7 +11179,9 @@ var SuperatomSDK = class {
10210
11179
  this.openaiApiKey = config.OPENAI_API_KEY || process.env.OPENAI_API_KEY || "";
10211
11180
  this.llmProviders = config.LLM_PROVIDERS || getLLMProviders();
10212
11181
  this.databaseType = config.databaseType || "postgresql";
10213
- logger.info(`Initializing Superatom SDK v${SDK_VERSION} for project ${this.projectId}, llm providers: ${this.llmProviders.join(", ")}, database type: ${this.databaseType}`);
11182
+ this.modelStrategy = config.modelStrategy || "fast";
11183
+ this.applyModelStrategy(this.modelStrategy);
11184
+ logger.info(`Initializing Superatom SDK v${SDK_VERSION} for project ${this.projectId}, llm providers: ${this.llmProviders.join(", ")}, database type: ${this.databaseType}, model strategy: ${this.modelStrategy}`);
10214
11185
  this.userManager = new UserManager(this.projectId, 5e3);
10215
11186
  this.dashboardManager = new DashboardManager(this.projectId);
10216
11187
  this.reportManager = new ReportManager(this.projectId);
@@ -10589,6 +11560,31 @@ var SuperatomSDK = class {
10589
11560
  getTools() {
10590
11561
  return this.tools;
10591
11562
  }
11563
+ /**
11564
+ * Apply model strategy to all LLM provider singletons
11565
+ * @param strategy - 'best', 'fast', or 'balanced'
11566
+ */
11567
+ applyModelStrategy(strategy) {
11568
+ anthropicLLM.setModelStrategy(strategy);
11569
+ groqLLM.setModelStrategy(strategy);
11570
+ geminiLLM.setModelStrategy(strategy);
11571
+ openaiLLM.setModelStrategy(strategy);
11572
+ logger.info(`Model strategy '${strategy}' applied to all LLM providers`);
11573
+ }
11574
+ /**
11575
+ * Set model strategy at runtime
11576
+ * @param strategy - 'best', 'fast', or 'balanced'
11577
+ */
11578
+ setModelStrategy(strategy) {
11579
+ this.modelStrategy = strategy;
11580
+ this.applyModelStrategy(strategy);
11581
+ }
11582
+ /**
11583
+ * Get current model strategy
11584
+ */
11585
+ getModelStrategy() {
11586
+ return this.modelStrategy;
11587
+ }
10592
11588
  };
10593
11589
  export {
10594
11590
  BM25L,
@@ -10603,9 +11599,15 @@ export {
10603
11599
  UIBlock,
10604
11600
  UILogCollector,
10605
11601
  UserManager,
11602
+ anthropicLLM,
11603
+ geminiLLM,
11604
+ groqLLM,
10606
11605
  hybridRerank,
11606
+ llmUsageLogger,
10607
11607
  logger,
11608
+ openaiLLM,
10608
11609
  rerankChromaResults,
10609
- rerankConversationResults
11610
+ rerankConversationResults,
11611
+ userPromptErrorLogger
10610
11612
  };
10611
11613
  //# sourceMappingURL=index.mjs.map