// Model Groups Management Page class ModelGroupsPage { constructor() { this.container = document.getElementById('page-content'); } async render() { this.container.innerHTML = `
`; await this.loadGroups(); } async loadGroups() { try { const groups = await api.get('/model-groups'); const list = document.getElementById('model-groups-list'); if (!groups || groups.length === 0) { list.innerHTML = '
No model groups defined. Create one to enable auto-routing.
'; return; } let html = ''; html += ''; html += ''; groups.forEach(g => { html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; }); html += '
Group IDLevelPrimary UseStrategyTargetsActions
' + this.esc(g.id) + '' + (g.logic_level != null ? g.logic_level : '—') + '' + this.esc(g.primary_use || '—') + '' + this.esc(g.strategy) + '' + this.esc(g.targets) + ''; html += ' '; html += ''; html += '
'; list.innerHTML = html; } catch (err) { document.getElementById('model-groups-list').innerHTML = '
Failed to load model groups: ' + this.esc(err.message) + '
'; } } showCreateForm() { this.renderForm(null); } async showEditForm(id) { try { const groups = await api.get('/model-groups'); const group = groups.find(g => g.id === id); if (group) this.renderForm(group); } catch (err) { alert('Failed to load group: ' + err.message); } } renderForm(group) { const isEdit = !!group; const form = document.getElementById('model-group-form'); form.style.display = 'block'; form.innerHTML = `

${isEdit ? 'Edit' : 'Create'} Model Group

Clients use this as the model name.
First target = cheapest/fastest. Last target = smartest/most expensive.
Pattern to match in user messages. target = index into targets array.
Rough complexity scale. 1-3: fast/light, 4-7: standard, 8-10: heavy.
Brief description of what this group is best used for.
`; document.getElementById('mg-strategy').onchange = function() { var isClassifier = this.value === 'classifier'; document.getElementById('mg-selector-row').style.display = isClassifier ? '' : 'none'; document.getElementById('mg-threshold-row').style.display = isClassifier ? '' : 'none'; document.getElementById('mg-rules-row').style.display = isClassifier ? 'none' : ''; }; } async saveGroup(event, isEdit) { event.preventDefault(); var id = document.getElementById('mg-id').value.trim(); var strategy = document.getElementById('mg-strategy').value; var targets = document.getElementById('mg-targets').value; var selectorModel = document.getElementById('mg-selector-model').value.trim() || null; var thresholdVal = document.getElementById('mg-threshold').value; var rules = document.getElementById('mg-rules').value.trim() || null; var logicLevelVal = document.getElementById('mg-level').value; var primaryUse = document.getElementById('mg-primary-use').value.trim() || null; try { JSON.parse(targets); } catch (e) { alert('Targets must be valid JSON array'); return; } if (rules) { try { JSON.parse(rules); } catch (e) { alert('Heuristic rules must be valid JSON'); return; } } var body = { id: id, strategy: strategy, targets: targets, selector_model: selectorModel, heuristic_rules: rules }; if (thresholdVal) body.complexity_threshold = parseInt(thresholdVal); if (logicLevelVal) body.logic_level = parseInt(logicLevelVal); if (primaryUse) body.primary_use = primaryUse; try { if (isEdit) { await api.put('/model-groups/' + encodeURIComponent(id), body); } else { await api.post('/model-groups', body); } document.getElementById('model-group-form').style.display = 'none'; await this.loadGroups(); } catch (err) { alert('Failed to save: ' + err.message); } } async deleteGroup(id) { if (!confirm('Delete model group "' + id + '"? This cannot be undone.')) return; try { await api.delete('/model-groups/' + encodeURIComponent(id)); await this.loadGroups(); } catch (err) { alert('Failed to delete: ' + err.message); } } esc(str) { if (!str) return ''; return String(str).replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"'); } } var modelGroupsPage = new ModelGroupsPage();