Remove separate chain concept - fallback is now determined by OpenCode models order
- Removed opencodeChain variable entirely - Removed chain form/list from admin UI - Fallback now uses the order of models in the OpenCode models list - Updated buildOpencodeAttemptChain to iterate through opencodeModels - Removed chain-related API endpoints - Simplified to just two lists: opencodeModels and publicModels
This commit is contained in:
@@ -16,18 +16,6 @@
|
||||
}
|
||||
/* Slightly tighten cards on the build page */
|
||||
body[data-page="build"] .admin-card { padding: 12px; }
|
||||
|
||||
/* Chain section styling */
|
||||
.chain-section {
|
||||
margin-top: 24px;
|
||||
padding-top: 16px;
|
||||
border-top: 2px solid var(--border);
|
||||
}
|
||||
.chain-section h4 {
|
||||
margin: 0 0 8px 0;
|
||||
font-size: 14px;
|
||||
color: var(--muted);
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- PostHog Analytics -->
|
||||
@@ -79,13 +67,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Section 1: OpenCode Models (with integrated Chain) -->
|
||||
<!-- Section 1: OpenCode Models -->
|
||||
<div class="admin-card">
|
||||
<header>
|
||||
<h3>OpenCode Models</h3>
|
||||
<div class="pill">Backend</div>
|
||||
</header>
|
||||
<p style="margin-top:0; color: var(--muted);">Add models from OpenCode. These models process requests and can fall back to the provider chain when rate limits are reached.</p>
|
||||
<p style="margin-top:0; color: var(--muted);">Add models from OpenCode. When rate limits are reached, the system automatically falls back to the next model in the order below.</p>
|
||||
|
||||
<!-- Add Model Form -->
|
||||
<form id="opencode-model-form" class="admin-form" style="margin-bottom: 24px;">
|
||||
@@ -126,57 +114,14 @@
|
||||
|
||||
<!-- Models List -->
|
||||
<header style="margin-top: 24px; border-top: 1px solid var(--border); padding-top: 16px;">
|
||||
<h3>OpenCode Models List</h3>
|
||||
<h3>OpenCode Models Order</h3>
|
||||
<div class="pill" id="opencode-models-count">0</div>
|
||||
</header>
|
||||
<p class="muted" style="margin-top:0;">Arrange the order below. This controls which model is primary for OpenCode requests.</p>
|
||||
<p class="muted" style="margin-top:0;">Arrange the order below. #1 is the primary model. When it hits rate limits, the system falls back to #2, then #3, and so on.</p>
|
||||
<div id="opencode-models-list" class="admin-list"></div>
|
||||
|
||||
<!-- Chain Section (integrated) -->
|
||||
<div class="chain-section">
|
||||
<h4>Fallback Chain (for when rate limits are reached)</h4>
|
||||
<p class="muted" style="margin-top:0; margin-bottom: 12px;">When the primary OpenCode model hits rate limits, the system falls back through these providers in order.</p>
|
||||
|
||||
<!-- Add to Chain Form -->
|
||||
<form id="chain-form" class="admin-form" style="margin-bottom: 16px;">
|
||||
<div class="admin-grid" style="grid-template-columns: 1fr 2fr; gap: 12px;">
|
||||
<label>
|
||||
Provider
|
||||
<select id="chain-provider">
|
||||
<option value="openrouter">OpenRouter</option>
|
||||
<option value="mistral">Mistral</option>
|
||||
<option value="google">Google</option>
|
||||
<option value="groq">Groq</option>
|
||||
<option value="nvidia">NVIDIA</option>
|
||||
<option value="chutes">Chutes</option>
|
||||
<option value="cerebras">Cerebras</option>
|
||||
<option value="ollama">Ollama</option>
|
||||
<option value="cohere">Cohere</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Model ID
|
||||
<input id="chain-model" type="text" placeholder="e.g., anthropic/claude-3.5-sonnet" required />
|
||||
</label>
|
||||
</div>
|
||||
<div class="admin-actions" style="margin-top: 12px;">
|
||||
<button type="submit" class="primary">Add to Fallback Chain</button>
|
||||
</div>
|
||||
<div class="status-line" id="chain-status"></div>
|
||||
</form>
|
||||
|
||||
<!-- Chain List -->
|
||||
<div style="margin-top: 16px;">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
|
||||
<span class="muted">Fallback Chain Order</span>
|
||||
<span class="pill" id="chain-count">0</span>
|
||||
</div>
|
||||
<div id="chain-list" class="admin-list"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Section 2: Public Models (Completely Separate) -->
|
||||
<!-- Section 2: Public Models -->
|
||||
<div class="admin-card" style="margin-top: 16px;">
|
||||
<header>
|
||||
<h3>Public Models</h3>
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
(() => {
|
||||
const DEFAULT_PROVIDERS = ['openrouter', 'mistral', 'google', 'groq', 'nvidia', 'chutes', 'cerebras', 'ollama', 'cohere'];
|
||||
const PLANNING_PROVIDERS = ['openrouter', 'mistral', 'google', 'groq', 'nvidia', 'chutes', 'cerebras', 'ollama', 'cohere'];
|
||||
const pageType = document?.body?.dataset?.page || 'build';
|
||||
console.log('Admin JS loaded, pageType:', pageType);
|
||||
|
||||
// Clean state structure
|
||||
// Clean state structure - just two things
|
||||
const state = {
|
||||
opencodeModels: [], // Models from OpenCode
|
||||
opencodeChain: [], // Fallback chain for OpenCode
|
||||
opencodeModels: [], // Models from OpenCode (order determines fallback)
|
||||
publicModels: [], // User-facing models (completely separate)
|
||||
icons: [],
|
||||
availableOpencodeModels: [], // Loaded from OpenCode
|
||||
};
|
||||
|
||||
// Element references - clean structure matching the HTML
|
||||
// Element references
|
||||
const el = {
|
||||
// OpenCode Models
|
||||
opencodeModelForm: document.getElementById('opencode-model-form'),
|
||||
@@ -27,14 +24,6 @@
|
||||
opencodeModelsList: document.getElementById('opencode-models-list'),
|
||||
opencodeModelsCount: document.getElementById('opencode-models-count'),
|
||||
|
||||
// OpenCode Chain
|
||||
chainForm: document.getElementById('chain-form'),
|
||||
chainProvider: document.getElementById('chain-provider'),
|
||||
chainModel: document.getElementById('chain-model'),
|
||||
chainStatus: document.getElementById('chain-status'),
|
||||
chainList: document.getElementById('chain-list'),
|
||||
chainCount: document.getElementById('chain-count'),
|
||||
|
||||
// Public Models
|
||||
publicModelForm: document.getElementById('public-model-form'),
|
||||
publicModelName: document.getElementById('public-model-name'),
|
||||
@@ -158,10 +147,8 @@
|
||||
try {
|
||||
const data = await api('/api/admin/models');
|
||||
state.opencodeModels = data.opencodeModels || [];
|
||||
state.opencodeChain = data.opencodeChain || [];
|
||||
state.publicModels = data.publicModels || [];
|
||||
renderOpencodeModels();
|
||||
renderOpencodeChain();
|
||||
renderPublicModels();
|
||||
} catch (err) {
|
||||
console.error('Failed to load models:', err);
|
||||
@@ -212,46 +199,6 @@
|
||||
});
|
||||
}
|
||||
|
||||
// Render OpenCode Chain
|
||||
function renderOpencodeChain() {
|
||||
if (!el.chainList) return;
|
||||
el.chainList.innerHTML = '';
|
||||
|
||||
if (el.chainCount) {
|
||||
el.chainCount.textContent = state.opencodeChain.length.toString();
|
||||
}
|
||||
|
||||
if (!state.opencodeChain.length) {
|
||||
el.chainList.innerHTML = '<div class="muted">No fallback chain configured. Add providers above.</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
state.opencodeChain.forEach((entry, idx) => {
|
||||
const row = document.createElement('div');
|
||||
row.className = 'provider-row slim';
|
||||
row.innerHTML = `
|
||||
<div class="provider-row-header">
|
||||
<div class="model-chip">
|
||||
<span class="pill" style="background: ${idx === 0 ? 'var(--shopify-green)' : 'var(--primary)'}; font-weight: 700;">${idx === 0 ? 'Primary' : `#${idx + 1}`}</span>
|
||||
<span class="pill" style="background: var(--primary);">${entry.provider}</span>
|
||||
<span>${entry.model}</span>
|
||||
</div>
|
||||
<div class="provider-row-actions">
|
||||
<button class="ghost move-up" ${idx === 0 ? 'disabled' : ''}>↑</button>
|
||||
<button class="ghost move-down" ${idx === state.opencodeChain.length - 1 ? 'disabled' : ''}>↓</button>
|
||||
<button class="ghost delete-btn">Remove</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
row.querySelector('.move-up')?.addEventListener('click', () => moveChainItem(idx, -1));
|
||||
row.querySelector('.move-down')?.addEventListener('click', () => moveChainItem(idx, 1));
|
||||
row.querySelector('.delete-btn')?.addEventListener('click', () => removeChainItem(idx));
|
||||
|
||||
el.chainList.appendChild(row);
|
||||
});
|
||||
}
|
||||
|
||||
// Render Public Models
|
||||
function renderPublicModels() {
|
||||
if (!el.publicModelsList) return;
|
||||
@@ -316,27 +263,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Move Chain Item
|
||||
async function moveChainItem(fromIdx, direction) {
|
||||
const toIdx = fromIdx + direction;
|
||||
if (toIdx < 0 || toIdx >= state.opencodeChain.length) return;
|
||||
|
||||
const newOrder = [...state.opencodeChain];
|
||||
const [item] = newOrder.splice(fromIdx, 1);
|
||||
newOrder.splice(toIdx, 0, item);
|
||||
|
||||
try {
|
||||
await api('/api/admin/chain/reorder', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ chain: newOrder }),
|
||||
});
|
||||
state.opencodeChain = newOrder;
|
||||
renderOpencodeChain();
|
||||
} catch (err) {
|
||||
console.error('Failed to reorder chain:', err);
|
||||
}
|
||||
}
|
||||
|
||||
// Move Public Model
|
||||
async function movePublicModel(fromIdx, direction) {
|
||||
const toIdx = fromIdx + direction;
|
||||
@@ -368,21 +294,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Remove Chain Item
|
||||
async function removeChainItem(idx) {
|
||||
const newChain = state.opencodeChain.filter((_, i) => i !== idx);
|
||||
try {
|
||||
await api('/api/admin/chain', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ chain: newChain }),
|
||||
});
|
||||
state.opencodeChain = newChain;
|
||||
renderOpencodeChain();
|
||||
} catch (err) {
|
||||
console.error('Failed to remove from chain:', err);
|
||||
}
|
||||
}
|
||||
|
||||
// Form Handlers
|
||||
if (el.opencodeModelForm) {
|
||||
el.opencodeModelForm.addEventListener('submit', async (e) => {
|
||||
@@ -425,33 +336,6 @@
|
||||
});
|
||||
}
|
||||
|
||||
if (el.chainForm) {
|
||||
el.chainForm.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const provider = el.chainProvider.value;
|
||||
const model = el.chainModel.value.trim();
|
||||
|
||||
if (!model) {
|
||||
setStatus(el.chainStatus, 'Model ID is required', true);
|
||||
return;
|
||||
}
|
||||
|
||||
const newChain = [...state.opencodeChain, { provider, model }];
|
||||
|
||||
try {
|
||||
await api('/api/admin/chain', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ chain: newChain }),
|
||||
});
|
||||
setStatus(el.chainStatus, 'Added to chain');
|
||||
el.chainModel.value = '';
|
||||
await loadModels();
|
||||
} catch (err) {
|
||||
setStatus(el.chainStatus, err.message, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (el.publicModelForm) {
|
||||
el.publicModelForm.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
Reference in New Issue
Block a user