Create creditTracker.js

This commit is contained in:
Antique3e
2025-10-22 01:06:34 +05:30
committed by GitHub
parent 6b45dd49ee
commit bb01924576
+235
View File
@@ -0,0 +1,235 @@
/**
* ComfyUI Credit Tracker
* Simple UI extension to track Modal GPU credits
*/
import { app } from "../../scripts/app.js";
import { api } from "../../scripts/api.js";
class CreditTracker {
constructor() {
this.config = null;
this.balance = null;
this.gpuType = null;
this.gpuCount = 1;
this.costPerSecond = 0;
this.lastUpdate = Date.now();
this.displayElement = null;
this.init();
}
async init() {
await this.loadConfig();
await this.loadBalance();
await this.detectGPU();
this.createDisplay();
this.startTracking();
}
async loadConfig() {
try {
const response = await api.fetchApi('/credit_tracker/config');
if (response.ok) {
this.config = await response.json();
console.log('✅ Credit Tracker: Config loaded');
} else {
this.config = {
starting_balance: 80.00,
gpu_costs_per_hour: {
"NVIDIA H100": 3.95,
"NVIDIA A100-SXM4-80GB": 2.50,
"NVIDIA A100-SXM4-40GB": 2.10,
"NVIDIA A10G": 1.10,
"Tesla T4": 0.59
},
gpu_count: 1
};
console.log('⚠️ Credit Tracker: Using default config');
}
} catch (error) {
console.error('❌ Credit Tracker: Config load failed', error);
}
}
async loadBalance() {
try {
const response = await api.fetchApi('/credit_tracker/balance');
if (response.ok) {
this.balance = await response.json();
console.log('✅ Credit Tracker: Balance loaded');
} else {
this.balance = {
last_updated: new Date().toISOString(),
remaining_balance: this.config.starting_balance
};
await this.saveBalance();
console.log('✅ Credit Tracker: New balance initialized');
}
} catch (error) {
console.error('❌ Credit Tracker: Balance load failed', error);
this.balance = {
last_updated: new Date().toISOString(),
remaining_balance: this.config.starting_balance
};
}
}
async detectGPU() {
try {
const response = await api.fetchApi('/credit_tracker/gpu_info');
if (response.ok) {
const gpuInfo = await response.json();
this.gpuType = gpuInfo.gpu_name;
this.gpuCount = this.config.gpu_count;
let costPerHour = 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;
break;
}
}
this.costPerSecond = (costPerHour / 3600) * this.gpuCount;
console.log(`✅ Credit Tracker: GPU detected - ${this.gpuType} @ $${costPerHour}/hr`);
}
} catch (error) {
console.error('❌ Credit Tracker: GPU detection failed', error);
this.gpuType = "Unknown";
this.costPerSecond = (1.0 / 3600) * this.gpuCount;
}
}
createDisplay() {
this.displayElement = document.createElement('button');
this.displayElement.id = 'credit-tracker-display';
this.displayElement.className = 'comfyui-button';
this.displayElement.style.cssText = `
background: #222;
border: none;
color: #ffffff;
padding: 4px 10px;
font-size: 14px;
font-weight: 400;
font-family: monospace;
cursor: pointer;
border-radius: 4px;
transition: background 0.2s;
min-width: 55px;
height: 30px;
line-height: 30px;
text-align: center;
`;
this.displayElement.onclick = () => this.showDetails();
this.displayElement.onmouseenter = () => {
this.displayElement.style.background = '#4e4e4e';
};
this.displayElement.onmouseleave = () => {
this.displayElement.style.background = '#222';
};
this.updateDisplay();
const addToHeader = () => {
const menuBar = document.querySelector('.comfyui-menu') ||
document.querySelector('.comfy-menu');
if (menuBar) {
const managerButton = Array.from(menuBar.children).find(el =>
el.textContent?.includes('Manager') ||
el.id?.includes('manager') ||
el.className?.includes('manager')
);
if (managerButton) {
menuBar.insertBefore(this.displayElement, managerButton);
console.log('✅ Credit Tracker: Display added before Manager');
} else {
menuBar.appendChild(this.displayElement);
console.log('✅ Credit Tracker: Display added to header (Manager not found)');
}
} else {
console.warn('⚠️ Credit Tracker: Menu bar not found, retrying...');
setTimeout(addToHeader, 500);
}
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', addToHeader);
} else {
setTimeout(addToHeader, 100);
}
}
updateDisplay() {
if (this.displayElement) {
const balance = this.balance.remaining_balance;
this.displayElement.textContent = `$${balance.toFixed(2)}`;
}
}
startTracking() {
setInterval(() => {
const now = Date.now();
const elapsed = (now - this.lastUpdate) / 1000;
const cost = this.costPerSecond * elapsed;
this.balance.remaining_balance = Math.max(0, this.balance.remaining_balance - cost);
this.balance.last_updated = new Date().toISOString();
this.updateDisplay();
if (Math.floor(now / 1000) % 10 === 0) {
this.saveBalance();
}
this.lastUpdate = now;
}, 1000);
}
async saveBalance() {
try {
await api.fetchApi('/credit_tracker/balance', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(this.balance)
});
} catch (error) {
console.error('❌ Credit Tracker: Save failed', error);
}
}
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 costPerHour = this.costPerSecond * 3600;
const hoursRemaining = balance > 0 ? (balance / costPerHour).toFixed(2) : '0.00';
const message = `
💰 Credit Tracker Details
━━━━━━━━━━━━━━━━━━━━━━━━
💵 Remaining: $${balance.toFixed(2)}
📊 Used: $${used.toFixed(2)}
💳 Starting: $${startingBalance.toFixed(2)}
🔋 Percentage: ${percentage}%
🎮 GPU: ${this.gpuType}
🔢 Count: ${this.gpuCount}
💲 Cost: $${costPerHour.toFixed(2)}/hour
⏱️ Remaining: ${hoursRemaining} hours
`.trim();
alert(message);
}
}
app.registerExtension({
name: "comfyui.credit.tracker",
async setup() {
new CreditTracker();
console.log('✅ Credit Tracker: Extension loaded');
}
});