@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.d.mts +421 -1
- package/dist/index.d.ts +421 -1
- package/dist/index.js +1247 -239
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1240 -238
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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
|
-
|
|
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:
|
|
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
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
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
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
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
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
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
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
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
|
-
|
|
3777
|
-
model
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
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
|
-
|
|
3794
|
-
model
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
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
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
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
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
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
|
|
4102
|
-
|
|
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
|
|
4133
|
-
|
|
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
|
|
4143
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
|
9361
|
-
import
|
|
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 =
|
|
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 =
|
|
9400
|
-
if (!
|
|
10368
|
+
const dir = path6.dirname(this.filePath);
|
|
10369
|
+
if (!fs7.existsSync(dir)) {
|
|
9401
10370
|
logger.info(`Creating directory structure: ${dir}`);
|
|
9402
|
-
|
|
10371
|
+
fs7.mkdirSync(dir, { recursive: true });
|
|
9403
10372
|
}
|
|
9404
|
-
if (!
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
9432
|
-
if (!
|
|
9433
|
-
|
|
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
|
-
|
|
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
|
|
9660
|
-
import
|
|
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 =
|
|
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
|
|
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 =
|
|
9694
|
-
if (
|
|
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
|
-
|
|
9699
|
-
|
|
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 (!
|
|
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 =
|
|
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 (!
|
|
9730
|
-
|
|
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 =
|
|
10704
|
+
const dashboardDirs = fs8.readdirSync(this.dashboardsBasePath);
|
|
9736
10705
|
for (const dashboardId of dashboardDirs) {
|
|
9737
10706
|
const dashboardPath = this.getDashboardPath(dashboardId);
|
|
9738
|
-
if (
|
|
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 (!
|
|
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
|
-
|
|
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 =
|
|
9782
|
-
if (!
|
|
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
|
-
|
|
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
|
|
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 (!
|
|
10778
|
+
if (!fs8.existsSync(this.dashboardsBasePath)) {
|
|
9810
10779
|
return 0;
|
|
9811
10780
|
}
|
|
9812
10781
|
try {
|
|
9813
|
-
const dashboardDirs =
|
|
10782
|
+
const dashboardDirs = fs8.readdirSync(this.dashboardsBasePath);
|
|
9814
10783
|
return dashboardDirs.filter((dir) => {
|
|
9815
10784
|
const dashboardPath = this.getDashboardPath(dir);
|
|
9816
|
-
return
|
|
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
|
|
9828
|
-
import
|
|
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 =
|
|
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
|
|
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 =
|
|
9862
|
-
if (
|
|
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
|
-
|
|
9867
|
-
|
|
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 (!
|
|
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 =
|
|
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 (!
|
|
9898
|
-
|
|
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 =
|
|
10872
|
+
const reportDirs = fs9.readdirSync(this.reportsBasePath);
|
|
9904
10873
|
for (const reportId of reportDirs) {
|
|
9905
10874
|
const reportPath = this.getReportPath(reportId);
|
|
9906
|
-
if (
|
|
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 (!
|
|
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
|
-
|
|
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 =
|
|
9950
|
-
if (!
|
|
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
|
-
|
|
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
|
|
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 (!
|
|
10946
|
+
if (!fs9.existsSync(this.reportsBasePath)) {
|
|
9978
10947
|
return 0;
|
|
9979
10948
|
}
|
|
9980
10949
|
try {
|
|
9981
|
-
const reportDirs =
|
|
10950
|
+
const reportDirs = fs9.readdirSync(this.reportsBasePath);
|
|
9982
10951
|
return reportDirs.filter((dir) => {
|
|
9983
10952
|
const reportPath = this.getReportPath(dir);
|
|
9984
|
-
return
|
|
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
|
-
|
|
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
|