Update creditTracker.js
This commit is contained in:
+52
-113
@@ -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');
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user