// Models Page Module class ModelsPage { constructor() { this.models = []; this.init(); } async init() { await this.loadModels(); this.setupEventListeners(); } async loadModels() { try { const data = await window.api.get('/models'); this.models = data; this.renderModelsTable(); } catch (error) { console.error('Error loading models:', error); window.authManager.showToast('Failed to load models', 'error'); } } renderModelsTable() { const tableBody = document.querySelector('#models-table tbody'); if (!tableBody) return; if (this.models.length === 0) { tableBody.innerHTML = 'No models found in registry'; return; } // Sort by provider then name this.models.sort((a, b) => { if (a.provider !== b.provider) return a.provider.localeCompare(b.provider); return a.name.localeCompare(b.name); }); tableBody.innerHTML = this.models.map(model => { const statusClass = model.enabled ? 'success' : 'secondary'; const statusIcon = model.enabled ? 'check-circle' : 'ban'; return ` ${model.id} ${model.name} ${model.provider.toUpperCase()} ${window.api.formatCurrency(model.prompt_cost)} / ${window.api.formatCurrency(model.completion_cost)} ${model.context_limit ? (model.context_limit / 1000) + 'k' : 'Unknown'} ${model.enabled ? 'Active' : 'Disabled'} ${window._userRole === 'admin' ? `
` : ''} `; }).join(''); } configureModel(id) { const model = this.models.find(m => m.id === id); if (!model) return; const modal = document.createElement('div'); modal.className = 'modal active'; modal.innerHTML = ` `; document.body.appendChild(modal); modal.querySelector('#save-model-config').onclick = async () => { const enabled = modal.querySelector('#model-enabled').checked; const promptCost = parseFloat(modal.querySelector('#model-prompt-cost').value); const completionCost = parseFloat(modal.querySelector('#model-completion-cost').value); const cacheReadCost = parseFloat(modal.querySelector('#model-cache-read-cost').value); const cacheWriteCost = parseFloat(modal.querySelector('#model-cache-write-cost').value); const mapping = modal.querySelector('#model-mapping').value; try { await window.api.put(`/models/${id}`, { enabled, prompt_cost: promptCost, completion_cost: completionCost, cache_read_cost: isNaN(cacheReadCost) ? null : cacheReadCost, cache_write_cost: isNaN(cacheWriteCost) ? null : cacheWriteCost, mapping: mapping || null }); window.authManager.showToast(`Model ${model.id} updated`, 'success'); modal.remove(); this.loadModels(); } catch (error) { window.authManager.showToast(error.message, 'error'); } }; } setupEventListeners() { const searchInput = document.getElementById('model-search'); if (searchInput) { searchInput.oninput = (e) => this.filterModels(e.target.value); } } filterModels(query) { if (!query) { this.renderModelsTable(); return; } const q = query.toLowerCase(); const originalModels = this.models; this.models = this.models.filter(m => m.id.toLowerCase().includes(q) || m.name.toLowerCase().includes(q) || m.provider.toLowerCase().includes(q) ); this.renderModelsTable(); this.models = originalModels; } } window.initModels = async () => { window.modelsPage = new ModelsPage(); };