diff --git a/js/creditTracker.js b/js/creditTracker.js index e11e4c2..6c3f35e 100644 --- a/js/creditTracker.js +++ b/js/creditTracker.js @@ -1,21 +1,6 @@ /** - * ComfyUI Credit Tracker - ENHANCED VERSION - * Tracks Modal GPU + CPU + Memory credits with animated color bar - * - * ✅ CHANGES FROM ORIGINAL: - * 1. Added CPU/Memory cost properties to constructor - * 2. Added detectCompute() method (NEW) - * 3. Modified detectGPU() to store separate costs - * 4. Added detectCompute() call in init() - * 5. Enhanced showDetails() with cost breakdown - * - * ⚠️ PRESERVED (NOT CHANGED): - * - setInterval timing (1000ms) - * - Save frequency (every 10 seconds) - * - saveBalance() method - * - updateDisplay() method - * - Color bar animation - * - balance.json format + * ComfyUI Credit Tracker + * Simple UI extension to track Modal GPU credits with animated color bar */ import { app } from "../../scripts/app.js"; @@ -24,42 +9,32 @@ import { api } from "../../scripts/api.js"; // ======================================== // COLOR CONFIGURATION (CRYSTOOLS STYLE - ONLY 2 COLORS) // ======================================== -// Change these values to customize the bar colors const COLORS = { - BACKGROUND: '#222222', // Dark gray background (when no credits) - START: '#236692', // Green - Full credits (100%) - END: '#EB3B5A', // Red - No credits (0%) - TEXT: '#ffffff' // White text on top of bar -}; -// ======================================== - -//🆕 Modal Pricing Constants -const MODAL_PRICING = { - CPU_PER_CORE_HOUR: 0.0473, // $0.0473 per vCPU core per hour - MEMORY_PER_GB_HOUR: 0.0080 // $0.0080 per GB per hour + BACKGROUND: '#222222', + START: '#236692', + END: '#EB3B5A', + TEXT: '#ffffff' }; // ======================================== class CreditTracker { constructor() { - // ===== EXISTING PROPERTIES (UNCHANGED) ===== this.config = null; this.balance = null; this.gpuType = null; this.gpuCount = 1; - this.costPerSecond = 0; + this.costPerSecond = 0; // CHANGED: Now includes GPU + CPU + Memory this.lastUpdate = Date.now(); this.displayElement = null; - this.sliderElement = null; // The animated color bar - this.containerElement = null; // The main container + this.sliderElement = null; + this.containerElement = null; - // 🆕 NEW PROPERTIES - CPU & Memory tracking + // NEW: Store CPU and Memory info for display this.cpuCores = 0; this.memoryGB = 0; this.gpuCostPerHour = 0; this.cpuCostPerHour = 0; this.memoryCostPerHour = 0; - // ========================================= this.init(); } @@ -67,8 +42,7 @@ class CreditTracker { async init() { await this.loadConfig(); await this.loadBalance(); - await this.detectGPU(); - await this.detectCompute(); // 🆕 NEW: Detect CPU & Memory + await this.detectGPU(); // CHANGED: Now also detects CPU and Memory this.createDisplay(); this.startTracking(); } @@ -89,7 +63,9 @@ class CreditTracker { "NVIDIA A10G": 1.10, "Tesla T4": 0.59 }, - gpu_count: 1 + gpu_count: 1, + cpu_cost_per_core_per_hour: 0.04, + memory_cost_per_gb_per_hour: 0.008 }; console.log('⚠️ Credit Tracker: Using default config'); } @@ -125,64 +101,45 @@ class CreditTracker { try { const response = await api.fetchApi('/credit_tracker/gpu_info'); if (response.ok) { - const gpuInfo = await response.json(); - this.gpuType = gpuInfo.gpu_name; + const info = await response.json(); + + // GPU detection (existing logic) + this.gpuType = info.gpu_name; this.gpuCount = this.config.gpu_count; - let costPerHour = 1.0; + let gpuCostPerHour = 1.0; for (const [gpuName, cost] of Object.entries(this.config.gpu_costs_per_hour)) { if (this.gpuType.includes(gpuName) || gpuName.includes(this.gpuType)) { - costPerHour = cost; + gpuCostPerHour = cost; break; } } + this.gpuCostPerHour = gpuCostPerHour * this.gpuCount; - // 🆕 CHANGED: Store GPU cost separately (was: this.costPerSecond = ...) - this.gpuCostPerHour = costPerHour; - console.log(`✅ GPU: ${this.gpuType} @ $${costPerHour.toFixed(4)}/hr`); + // NEW: CPU and Memory detection (same approach as GPU) + this.cpuCores = info.cpu_cores || 0; + this.memoryGB = info.memory_total_gb || 0; + + this.cpuCostPerHour = this.config.cpu_cost_per_core_per_hour * this.cpuCores; + this.memoryCostPerHour = this.config.memory_cost_per_gb_per_hour * this.memoryGB; + + // CHANGED: Total cost per second = GPU + CPU + Memory + const totalCostPerHour = this.gpuCostPerHour + this.cpuCostPerHour + this.memoryCostPerHour; + this.costPerSecond = totalCostPerHour / 3600; + + console.log(`✅ Credit Tracker: GPU detected - ${this.gpuType} @ $${this.gpuCostPerHour.toFixed(2)}/hr`); + console.log(`✅ Credit Tracker: CPU detected - ${this.cpuCores} cores @ $${this.cpuCostPerHour.toFixed(4)}/hr`); + console.log(`✅ Credit Tracker: Memory detected - ${this.memoryGB.toFixed(2)} GB @ $${this.memoryCostPerHour.toFixed(4)}/hr`); + console.log(`✅ Credit Tracker: Total cost - $${totalCostPerHour.toFixed(4)}/hr`); } } catch (error) { - console.error('❌ Credit Tracker: GPU detection failed', error); + console.error('❌ Credit Tracker: Detection failed', error); this.gpuType = "Unknown"; - this.gpuCostPerHour = 1.0; + this.costPerSecond = (1.0 / 3600) * this.gpuCount; } } - // 🆕 NEW METHOD: Detect CPU & Memory (similar to detectGPU) - async detectCompute() { - try { - const response = await api.fetchApi('/credit_tracker/compute_resources'); - if (response.ok) { - const computeInfo = await response.json(); - this.cpuCores = computeInfo.cpu_cores; - this.memoryGB = computeInfo.memory_gb; - - // Calculate costs using Modal pricing - this.cpuCostPerHour = this.cpuCores * MODAL_PRICING.CPU_PER_CORE_HOUR; - this.memoryCostPerHour = this.memoryGB * MODAL_PRICING.MEMORY_PER_GB_HOUR; - - console.log(`✅ CPU: ${this.cpuCores} vCPU @ $${this.cpuCostPerHour.toFixed(4)}/hr`); - console.log(`✅ Memory: ${this.memoryGB} GB @ $${this.memoryCostPerHour.toFixed(4)}/hr`); - } - } catch (error) { - console.error('❌ Compute detection failed, using defaults', error); - // Fallback to reasonable defaults - this.cpuCores = 12; - this.memoryGB = 32.0; - this.cpuCostPerHour = 12 * MODAL_PRICING.CPU_PER_CORE_HOUR; - this.memoryCostPerHour = 32.0 * MODAL_PRICING.MEMORY_PER_GB_HOUR; - } - - // 🆕 Calculate total cost per second (GPU + CPU + Memory) - const totalCostPerHour = (this.gpuCostPerHour + this.cpuCostPerHour + this.memoryCostPerHour) * this.gpuCount; - this.costPerSecond = totalCostPerHour / 3600; - - console.log(`💰 Total: $${totalCostPerHour.toFixed(4)}/hr = $${(this.costPerSecond * 3600).toFixed(6)}/sec`); - } - - // ===== createDisplay() - UNCHANGED FROM ORIGINAL ===== createDisplay() { - // Main container - dark background visible when bar shrinks const container = document.createElement('div'); container.id = 'credit-tracker-display'; container.style.cssText = ` @@ -202,7 +159,6 @@ class CreditTracker { padding: 4px 10px; `; - // Progress bar slider (the animated color bar) const slider = document.createElement('div'); slider.id = 'credit-tracker-slider'; slider.style.cssText = ` @@ -217,7 +173,6 @@ class CreditTracker { `; container.appendChild(slider); - // Text label (displays $XX.XX on top of the bar) this.displayElement = document.createElement('div'); this.displayElement.style.cssText = ` position: relative; @@ -230,14 +185,11 @@ class CreditTracker { `; container.appendChild(this.displayElement); - // Store references this.sliderElement = slider; this.containerElement = container; - // Click to show details container.onclick = () => this.showDetails(); - // Hover effect container.onmouseenter = () => { container.style.opacity = '0.8'; }; @@ -247,7 +199,6 @@ class CreditTracker { this.updateDisplay(); - // Add to header bar const addToHeader = () => { const menuBar = document.querySelector('.comfyui-menu') || document.querySelector('.comfy-menu'); @@ -263,7 +214,7 @@ class CreditTracker { console.log('✅ Credit Tracker: Display added before Manager'); } else { menuBar.appendChild(container); - console.log('✅ Credit Tracker: Display added to header (Manager not found)'); + console.log('✅ Credit Tracker: Display added to header'); } } else { console.warn('⚠️ Credit Tracker: Menu bar not found, retrying...'); @@ -278,41 +229,32 @@ class CreditTracker { } } - // ===== updateDisplay() - UNCHANGED FROM ORIGINAL ===== updateDisplay() { if (this.displayElement && this.sliderElement) { const balance = this.balance.remaining_balance; const percentage = (balance / this.config.starting_balance) * 100; - // Update text display this.displayElement.textContent = `$${balance.toFixed(2)}`; - - // Update bar width (shrinks from 100% to 0% as credits decrease) this.sliderElement.style.width = `${percentage}%`; - // Color transition (green to red) const redAmount = 100 - percentage; this.sliderElement.style.backgroundColor = `color-mix(in srgb, ${COLORS.END} ${redAmount}%, ${COLORS.START})`; } } - // ===== startTracking() - UNCHANGED FROM ORIGINAL ===== startTracking() { - // Update credits every second + // NO CHANGES: Same tracking logic, just uses updated costPerSecond setInterval(() => { const now = Date.now(); const elapsed = (now - this.lastUpdate) / 1000; - // Deduct credits based on total cost (GPU + CPU + Memory) const cost = this.costPerSecond * elapsed; this.balance.remaining_balance = Math.max(0, this.balance.remaining_balance - cost); this.balance.last_updated = new Date().toISOString(); - // Update the display this.updateDisplay(); - // Save to file every 10 seconds if (Math.floor(now / 1000) % 10 === 0) { this.saveBalance(); } @@ -321,7 +263,6 @@ class CreditTracker { }, 1000); } - // ===== saveBalance() - UNCHANGED FROM ORIGINAL ===== async saveBalance() { try { await api.fetchApi('/credit_tracker/balance', { @@ -334,35 +275,34 @@ class CreditTracker { } } - // 🆕 ENHANCED: showDetails() with cost breakdown showDetails() { const balance = this.balance.remaining_balance; const startingBalance = this.config.starting_balance; const used = startingBalance - balance; const percentage = ((balance / startingBalance) * 100).toFixed(1); - const totalCostPerHour = this.costPerSecond * 3600; + const totalCostPerHour = this.gpuCostPerHour + this.cpuCostPerHour + this.memoryCostPerHour; const hoursRemaining = balance > 0 ? (balance / totalCostPerHour).toFixed(2) : '0.00'; + // CHANGED: Updated to show GPU, CPU, Memory breakdown const message = ` 💰 Credit Tracker Details ━━━━━━━━━━━━━━━━━━━━━━━━ 💵 Remaining: $${balance.toFixed(2)} 📊 Used: $${used.toFixed(2)} 💳 Starting: $${startingBalance.toFixed(2)} -🔋 Percentage: ${percentage}% +📋 Percentage: ${percentage}% 🎮 GPU: ${this.gpuType} -🔢 GPU Count: ${this.gpuCount} -💲 GPU Cost: $${this.gpuCostPerHour.toFixed(4)}/hour + Count: ${this.gpuCount} + Cost: $${this.gpuCostPerHour.toFixed(4)}/hour -💻 CPU: ${this.cpuCores} vCPU cores -💲 CPU Cost: $${this.cpuCostPerHour.toFixed(4)}/hour +🖥️ CPU: ${this.cpuCores} cores + Cost: $${this.cpuCostPerHour.toFixed(4)}/hour -🧠 Memory: ${this.memoryGB.toFixed(2)} GB -💲 Memory Cost: $${this.memoryCostPerHour.toFixed(4)}/hour +💾 Memory: ${this.memoryGB.toFixed(2)} GB + Cost: $${this.memoryCostPerHour.toFixed(4)}/hour -━━━━━━━━━━━━━━━━━━━━━━━━ -💰 TOTAL: $${totalCostPerHour.toFixed(4)}/hour +💲 Total Cost: $${totalCostPerHour.toFixed(4)}/hour ⏱️ Time Remaining: ${hoursRemaining} hours `.trim(); @@ -370,11 +310,10 @@ class CreditTracker { } } -// Register the extension with ComfyUI app.registerExtension({ - name: "comfyui.credit.tracker.enhanced", + name: "comfyui.credit.tracker", async setup() { new CreditTracker(); - console.log('✅ Credit Tracker Enhanced: Extension loaded (with CPU+Memory tracking)'); + console.log('✅ Credit Tracker: Extension loaded'); } });