aipackage/cezen-portal/model-compare.html
2026-06-30 10:51:41 +05:30

790 lines
34 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Model Comparison — Nexus One AI</title>
<link rel="stylesheet" href="style.css?v=4">
<style>
:root {
--navy2: var(--surface);
--bdr: var(--border);
--ink: var(--text-primary);
--lt: var(--text-tertiary);
--med: var(--text-secondary);
--purple: var(--brand);
--teal: var(--brand);
}
.mc-layout {
display: flex; flex-direction: column;
min-height: calc(100vh - 56px);
background: var(--bg);
overflow: visible;
}
/* ── Top: Config Bar ── */
.mc-config {
background: var(--surface);
border-bottom: 1px solid var(--bdr);
padding: 16px 22px;
display: flex; flex-direction: column; gap: 12px;
flex-shrink: 0;
box-shadow: var(--shadow-sm);
}
.mc-config-row { display: flex; align-items: flex-start; gap: 16px; }
.mc-config-row.prompts { gap: 12px; }
.mc-field { display: flex; flex-direction: column; gap: 6px; flex: 1; }
.mc-field label { font-size: 11px; font-weight: 700; color: var(--lt); text-transform: uppercase; letter-spacing: .4px; }
.mc-field textarea {
width: 100%; box-sizing: border-box; font-family: inherit;
border: 1.5px solid var(--bdr); border-radius: 9px;
background: var(--surface-hover); color: var(--ink); font-size: 13px;
padding: 9px 12px; resize: none; line-height: 1.5;
transition: border-color .15s;
}
.mc-field textarea:focus { outline: none; border-color: var(--purple); }
/* Model Selector Pills */
.mc-model-selector { display: flex; flex-direction: column; gap: 6px; min-width: 220px; }
.mc-model-pills { display: flex; flex-wrap: wrap; gap: 6px; }
.mc-model-pill {
display: flex; align-items: center; gap: 6px;
padding: 5px 12px; border-radius: 20px; cursor: pointer;
border: 1px solid var(--bdr); background: var(--surface);
font-size: 12px; font-weight: 600; color: var(--med);
transition: .15s; user-select: none;
}
.mc-model-pill input { display: none; }
.mc-model-pill.checked {
border-color: var(--purple); background: rgba(124,58,237,.1); color: var(--purple);
}
.mc-model-pill .mc-pill-dot {
width: 8px; height: 8px; border-radius: 50%; background: var(--bdr);
}
.mc-model-pill.checked .mc-pill-dot { background: var(--purple); }
.mc-model-limit { font-size: 11px; color: var(--lt); }
/* Params Row */
.mc-params-row { display: flex; align-items: center; gap: 20px; }
.mc-param { display: flex; align-items: center; gap: 8px; }
.mc-param label { font-size: 12px; font-weight: 600; color: var(--med); white-space: nowrap; }
.mc-param input[type=range] { accent-color: var(--purple); width: 100px; }
.mc-param-val { font-size: 12px; font-weight: 700; color: var(--purple); min-width: 28px; }
.mc-run-btn {
padding: 10px 22px; border-radius: 8px;
background: var(--purple); color: white; border: none;
font-family: inherit; font-size: 14px; font-weight: 700;
cursor: pointer; transition: .15s; white-space: nowrap;
display: flex; align-items: center; gap: 8px;
}
.mc-run-btn:hover { background: #6d28d9; }
.mc-run-btn:disabled { opacity: .5; cursor: not-allowed; }
.mc-spinner { display: none; width: 15px; height: 15px; border: 2px solid rgba(255,255,255,.3); border-top-color: white; border-radius: 50%; animation: spin .7s linear infinite; }
.mc-run-btn.running .mc-spinner { display: block; }
.mc-run-btn.running .mc-run-icon { display: none; }
@keyframes spin { to { transform: rotate(360deg); } }
.mc-clear-btn {
padding: 10px 16px; border-radius: 10px;
background: var(--surface); border: 1px solid var(--bdr); color: var(--med);
font-family: inherit; font-size: 13px; font-weight: 600;
cursor: pointer; transition: .15s; white-space: nowrap;
}
.mc-clear-btn:hover { border-color: var(--purple); color: var(--purple); }
/* ── Columns ── */
.mc-columns {
display: flex; flex: 1; overflow: hidden;
gap: 16px;
padding: 16px;
}
.mc-col {
flex: 1; min-width: 0;
border: 1px solid var(--bdr);
border-radius: var(--radius-lg);
background: var(--surface);
box-shadow: var(--shadow-sm);
display: flex; flex-direction: column;
overflow: hidden; transition: flex .25s;
min-height: 520px;
}
.mc-col:last-child { border-right: 1px solid var(--bdr); }
.mc-col-header {
padding: 12px 16px;
border-bottom: 1px solid var(--bdr);
background: var(--surface-hover);
display: flex; align-items: center; gap: 8px;
flex-shrink: 0;
}
.mc-col-model-name { font-size: 13px; font-weight: 700; color: var(--ink); flex: 1; }
.mc-col-stats { display: flex; gap: 8px; }
.mc-col-stat {
font-size: 11px; font-weight: 700; padding: 2px 8px;
border-radius: 20px; background: rgba(124,58,237,.08); color: var(--purple);
}
.mc-col-stat.green { background: rgba(16,185,129,.1); color: #059669; }
.mc-col-stat.hide { display: none; }
.mc-col-actions { display: flex; gap: 5px; }
.mc-col-btn {
background: var(--surface); border: 1px solid var(--bdr); border-radius: 7px;
padding: 3px 8px; font-size: 11px; cursor: pointer; color: var(--med);
transition: .15s; font-family: inherit;
}
.mc-col-btn:hover { border-color: var(--purple); color: var(--purple); }
.mc-col-body {
flex: 1; overflow-y: auto; padding: 18px 16px;
background: var(--surface);
}
.mc-col-body.streaming { background: rgba(124,58,237,.02); }
.mc-col-empty {
display: flex; flex-direction: column; align-items: center;
justify-content: center; height: 100%; gap: 8px; color: var(--lt);
}
.mc-col-empty-icon { font-size: 32px; opacity: .35; }
.mc-col-empty p { font-size: 13px; text-align: center; }
.mc-response {
font-size: 13.5px; line-height: 1.7; color: var(--ink);
}
.mc-response h1,.mc-response h2,.mc-response h3 { margin:.8em 0 .4em; color:var(--ink); }
.mc-response h1 { font-size:1.25em; } .mc-response h2 { font-size:1.1em; } .mc-response h3 { font-size:1.02em; }
.mc-response p { margin:.5em 0; }
.mc-response ul,.mc-response ol { padding-left:1.4em; margin:.4em 0; }
.mc-response li { margin:.25em 0; }
.mc-response code { background:rgba(124,58,237,.1); color:var(--purple); padding:2px 5px; border-radius:4px; font-family:monospace; font-size:.88em; }
.mc-response pre { background:#160D35; color:#e2d9f3; border-radius:9px; padding:12px 14px; overflow-x:auto; margin:.7em 0; }
.mc-response pre code { background:none; color:inherit; padding:0; }
.mc-response blockquote { border-left:3px solid var(--purple); padding-left:12px; margin:.5em 0; color:var(--med); font-style:italic; }
.mc-response table { border-collapse:collapse; width:100%; margin:.5em 0; font-size:.88em; }
.mc-response th { background:rgba(124,58,237,.08); padding:7px 10px; text-align:left; font-weight:700; border-bottom:2px solid var(--bdr); }
.mc-response td { padding:6px 10px; border-bottom:1px solid var(--bdr); }
.mc-response strong { font-weight:700; }
.mc-response em { font-style:italic; }
.mc-streaming-cursor { display:inline-block; width:2px; height:1em; background:var(--purple); margin-left:2px; animation:blink .8s step-end infinite; vertical-align:text-bottom; }
@keyframes blink { 50%{ opacity:0; } }
/* Win badges */
.mc-win-badge {
display: inline-block; padding: 2px 8px; border-radius: 12px;
font-size: 10px; font-weight: 800; text-transform: uppercase;
background: rgba(16,185,129,.15); color: #047857; letter-spacing: .3px;
}
/* Summary bar */
.mc-summary-bar {
background: var(--surface); border-top: 1px solid var(--bdr);
padding: 10px 20px; display: flex; align-items: center; gap: 20px;
flex-shrink: 0; font-size: 12px; color: var(--med);
}
.mc-summary-bar.hidden { display: none; }
.mc-summary-item { display: flex; align-items: center; gap: 6px; }
.mc-summary-label { font-weight: 700; color: var(--lt); text-transform: uppercase; font-size: 10px; letter-spacing: .4px; }
.mc-summary-val { font-weight: 700; color: var(--purple); }
.mc-save-compare-btn {
margin-left: auto; padding: 6px 14px; border-radius: 8px;
background: var(--surface); border: 1px solid var(--bdr); color: var(--med);
font-family: inherit; font-size: 12px; font-weight: 600;
cursor: pointer; transition: .15s;
}
.mc-save-compare-btn:hover { border-color: var(--purple); color: var(--purple); }
/* Placeholder col when < 2 selected */
.mc-col-placeholder {
flex: 1; display: flex; align-items: center; justify-content: center;
border: 1px dashed var(--bdr); color: var(--lt);
background: var(--surface);
border-radius: var(--radius-lg);
min-height: 520px;
box-shadow: var(--shadow-xs);
}
.mc-col-placeholder:last-child { border-right: 1px dashed var(--bdr); }
.mc-col-placeholder p { font-size: 13px; }
/* Toast */
#mc-toast {
position: fixed; bottom: 24px; right: 24px; z-index: 9999;
background: var(--text-primary); color: white; padding: 10px 18px;
border-radius: 10px; font-size: 13px; font-weight: 600;
opacity: 0; transform: translateY(8px); transition: .25s; pointer-events: none;
}
#mc-toast.show { opacity: 1; transform: translateY(0); }
@media (max-width: 900px) {
.mc-config-row,
.mc-config-row.prompts,
.mc-params-row { flex-direction: column; align-items: stretch !important; }
.mc-field[style] { max-width: none !important; }
.mc-columns { flex-direction: column; overflow: visible; }
.mc-col { min-height: 360px; }
}
</style>
</head>
<body>
<header class="topnav">
<a href="index.html" class="brand">Nexus One <span>AI</span></a>
<nav>
<a href="index.html">Home</a>
<a href="quickstart.html">Quick Start</a>
<a href="prompts.html">Prompt Library</a>
<a href="usecases.html">Use Cases</a>
<span class="nav-sep"></span>
<div class="nav-dropdown">
<button class="nav-drop-btn">Help ▾</button>
<div class="nav-drop-menu">
<span class="nav-drop-cat">LEARN /</span>
<a href="quickstart.html">Quick Start</a>
<a href="models.html">Models</a>
<span class="nav-drop-cat">SUPPORT /</span>
<a href="troubleshooting.html">Troubleshoot</a>
<a href="faq.html">FAQ</a>
<span class="nav-drop-cat">MORE /</span>
<a href="glossary.html">Glossary</a>
<a href="whats-new.html">What's New</a>
</div>
</div>
<div class="nav-dropdown">
<button class="nav-drop-btn active">Admin ▾</button>
<div class="nav-drop-menu nav-drop-menu-wide">
<span class="nav-drop-cat">DOCS /</span>
<a href="security.html">Security & Privacy</a>
<a href="admin.html">Admin Guide</a>
<span class="nav-drop-cat">MONITOR /</span>
<a href="dashboard.html">Dashboard</a>
<a href="analytics.html">Usage Analytics</a>
<a href="audit.html">Audit Log</a>
<a href="feedback.html">Feedback &amp; Ratings</a>
<span class="nav-drop-cat">MANAGE /</span>
<a href="users.html">Users</a>
<a href="teams.html">Teams</a>
<a href="models-admin.html">Model Manager</a>
<a href="training.html">Training</a>
<a href="knowledge.html">Knowledge Base</a>
<span class="nav-drop-cat">TOOLS /</span>
<a href="apikeys.html">API Keys</a>
<a href="benchmark.html">Benchmarking</a>
<a href="model-compare.html" class="active">Model Compare</a>
<a href="guardrails.html">Guardrails</a>
<a href="rag-quality.html">RAG Quality</a>
<a href="router.html">Model Router</a>
<a href="connectors.html">Connectors</a>
<span class="nav-drop-cat">SYSTEM /</span>
<a href="appliance.html">Appliance Ops</a>
<a href="console.html">Console</a>
<a href="settings.html">Settings</a>
</div>
</div>
<div class="nav-dropdown">
<button class="nav-drop-btn">AI Tools ▾</button>
<div class="nav-drop-menu">
<span class="nav-drop-cat">INTELLIGENCE /</span>
<a href="documents.html">Document Intelligence</a>
<a href="chat-multi.html">Multimodal Chat</a>
<a href="prompt-studio.html">Prompt Studio</a>
<a href="meeting.html">Meeting Assistant</a>
<span class="nav-drop-cat">AUTOMATION /</span>
<a href="agents.html">Agent Builder</a>
<a href="schedules.html">Scheduled Jobs</a>
<a href="workflows.html">Workflow Automation</a>
<span class="nav-drop-cat">QUALITY /</span>
<a href="evals.html">AI Eval Suite</a>
<a href="chatrooms.html">Chat Rooms</a>
</div>
</div>
</nav>
<a href="notifications.html" style="position:relative">🔔</a>
<span class="badge" data-brand="tier">Basic Tier</span>
<div id="nav-org-logo" class="nav-org-logo"></div>
</header>
<div class="mc-layout">
<!-- Config Bar -->
<div class="mc-config">
<div class="mc-config-row prompts">
<div class="mc-field" style="max-width:340px">
<label>System Prompt <span style="font-weight:400;text-transform:none;letter-spacing:0">(shared across all models)</span></label>
<textarea id="mc-system" rows="2" placeholder="You are a helpful AI assistant. Answer clearly and concisely."></textarea>
</div>
<div class="mc-field">
<label>User Message <span style="font-weight:400;text-transform:none;letter-spacing:0">(same prompt sent to each selected model)</span></label>
<textarea id="mc-user" rows="2" placeholder="Enter a prompt to compare across models… (Ctrl+Enter to run)" onkeydown="handleKey(event)"></textarea>
</div>
</div>
<div class="mc-config-row" style="align-items:flex-end">
<div class="mc-model-selector">
<label style="font-size:11px;font-weight:700;color:var(--lt);text-transform:uppercase;letter-spacing:.4px">Models <span class="mc-model-limit" id="mc-limit-msg">(select 24)</span></label>
<div class="mc-model-pills" id="mc-model-pills">
<!-- Rendered by JS -->
</div>
</div>
<div class="mc-params-row">
<div class="mc-param">
<label>Temp</label>
<input type="range" id="mc-temp" min="0" max="1" step="0.05" value="0.7" oninput="document.getElementById('mc-temp-val').textContent=this.value">
<span class="mc-param-val" id="mc-temp-val">0.7</span>
</div>
<div class="mc-param">
<label>Max Tokens</label>
<input type="range" id="mc-maxtok" min="128" max="2048" step="128" value="512" oninput="document.getElementById('mc-maxtok-val').textContent=this.value">
<span class="mc-param-val" id="mc-maxtok-val">512</span>
</div>
<button class="mc-run-btn" id="mc-run-btn" onclick="runAll()">
<span class="mc-run-icon">Run All</span>
<div class="mc-spinner"></div>
</button>
<button class="mc-clear-btn" onclick="clearAll()">Clear</button>
<button class="mc-clear-btn" onclick="openStudio()">Studio</button>
</div>
</div>
</div>
<!-- Column Output Area -->
<div class="mc-columns" id="mc-columns">
<div class="mc-col-placeholder" id="mc-col-ph">
<p>Select 24 models above and click <strong>Run All</strong></p>
</div>
</div>
<!-- Summary Bar -->
<div class="mc-summary-bar hidden" id="mc-summary-bar">
<div class="mc-summary-item">
<span class="mc-summary-label">Fastest</span>
<span class="mc-summary-val" id="mc-fastest"></span>
</div>
<div class="mc-summary-item">
<span class="mc-summary-label">Most Tokens</span>
<span class="mc-summary-val" id="mc-most-tokens"></span>
</div>
<div class="mc-summary-item">
<span class="mc-summary-label">Avg Latency</span>
<span class="mc-summary-val" id="mc-avg-latency"></span>
</div>
<button class="mc-save-compare-btn" onclick="saveComparison()">Save Comparison</button>
<button class="mc-save-compare-btn" onclick="openStudio()">Refine in Studio</button>
</div>
</div>
<div id="mc-toast"></div>
<script>
const MODELS = [
{ id: 'llama3.1:8b', label: 'LLaMA 3.1 8B', short: 'LLaMA·8B', color: '#7C3AED' },
{ id: 'llama3.1:70b', label: 'LLaMA 3.1 70B', short: 'LLaMA·70B', color: '#0EA5E9' },
{ id: 'mistral:7b', label: 'Mistral 7B', short: 'Mistral', color: '#F59E0B' },
{ id: 'codellama:34b',label: 'CodeLLaMA 34B', short: 'CodeLLaMA', color: '#10B981' },
{ id: 'llava:13b', label: 'LLaVA 13B', short: 'LLaVA', color: '#EC4899' },
{ id: 'gemma2:9b', label: 'Gemma 2 9B', short: 'Gemma', color: '#6366F1' },
{ id: 'llama3.2:3b', label: 'LLaMA 3.2 3B', short: 'LLaMA·3B', color: '#64748B' },
];
let selectedModels = new Set(['llama3.1:8b', 'mistral:7b']);
let runResults = [];
// ── Build Pills ───────────────────────────────────────────────────────────────
function buildPills() {
const container = document.getElementById('mc-model-pills');
container.innerHTML = MODELS.map(m => `
<label class="mc-model-pill ${selectedModels.has(m.id) ? 'checked' : ''}" data-id="${m.id}">
<input type="checkbox" ${selectedModels.has(m.id) ? 'checked' : ''} onchange="toggleModel('${m.id}', this)">
<span class="mc-pill-dot" style="${selectedModels.has(m.id) ? `background:${m.color}` : ''}"></span>
${m.short}
</label>
`).join('');
}
function toggleModel(id, cb) {
if (cb.checked) {
if (selectedModels.size >= 4) { cb.checked = false; showToast('Max 4 models'); return; }
selectedModels.add(id);
} else {
if (selectedModels.size <= 1) { cb.checked = true; showToast('Select at least 1'); return; }
selectedModels.delete(id);
}
const lbl = cb.closest('.mc-model-pill');
const m = MODELS.find(x => x.id === id);
lbl.classList.toggle('checked', cb.checked);
lbl.querySelector('.mc-pill-dot').style.background = cb.checked ? m.color : '';
const n = selectedModels.size;
document.getElementById('mc-limit-msg').textContent = `(select 24) — ${n} selected`;
buildColumns(false);
}
buildPills();
// ── Columns ───────────────────────────────────────────────────────────────────
function buildColumns(withContent) {
const container = document.getElementById('mc-columns');
const models = [...selectedModels];
if (!models.length) {
container.innerHTML = '<div class="mc-col-placeholder"><p>Select models above</p></div>';
return;
}
container.innerHTML = models.map(id => {
const m = MODELS.find(x => x.id === id);
return `
<div class="mc-col" id="col-${id.replace(/[:.]/g,'_')}">
<div class="mc-col-header" style="border-top: 3px solid ${m.color}">
<span class="mc-col-model-name">${m.label}</span>
<div class="mc-col-stats" id="stats-${id.replace(/[:.]/g,'_')}"></div>
<div class="mc-col-actions">
<button class="mc-col-btn" onclick="copyCol('${id}')">Copy</button>
<button class="mc-col-btn" onclick="sendColToStudio('${id}')">Studio</button>
</div>
</div>
<div class="mc-col-body" id="body-${id.replace(/[:.]/g,'_')}">
<div class="mc-col-empty">
<div class="mc-col-empty-icon">AI</div>
<p>Ready — click <strong>Run All</strong></p>
</div>
</div>
</div>`;
}).join('');
}
buildColumns(false);
// ── Mock Responses ────────────────────────────────────────────────────────────
const MOCK_RESPONSES_MC = {
'llama3.1:8b': (user, temp) => `## LLaMA 3.1 8B Response
This is a fast, efficient response from the 8B model. It processes your query quickly with good general knowledge.
**Key points:**
- Optimised for speed and low memory footprint
- Suitable for most general-purpose tasks
- Temperature ${temp} applied — output is ${temp < 0.4 ? 'focused and deterministic' : 'balanced and varied'}
Your query: *"${user.slice(0,80)}${user.length>80?'…':''}"*
The 8B model is ideal for high-throughput production workloads where latency matters most.`,
'llama3.1:70b': (user, temp) => `## LLaMA 3.1 70B Response
This response demonstrates the deeper reasoning capabilities of the 70B parameter model. It can handle more nuanced queries, follow complex instructions, and produce more structured output.
**Analysis:**
The 70B model excels at tasks requiring:
1. **Multi-step reasoning** — breaking down complex problems
2. **Instruction following** — adhering precisely to format requirements
3. **Knowledge depth** — drawing on a broader training corpus
**Response to your query:**
Your input — *"${user.slice(0,80)}${user.length>80?'…':''}"* — involves contextual reasoning that benefits from a larger parameter count. The 70B model allocates more capacity to understanding nuance and generating coherent long-form output.
**Trade-off:** Latency is higher but quality is improved, especially for tasks where accuracy matters more than speed.`,
'mistral:7b': (user, temp) => `## Mistral 7B Response
Efficient and structured output from Mistral 7B. This model is well-suited for instruction-following tasks.
**Task:** ${user.slice(0,60)}${user.length>60?'…':''}
**Result:**
Mistral 7B produces consistent, well-formatted outputs especially for structured tasks like JSON extraction, classification, and document summarisation.
\`\`\`json
{
"model": "mistral:7b",
"temperature": ${temp},
"strengths": ["structured output", "instruction following", "efficiency"],
"recommended_for": ["classification", "extraction", "formatting"]
}
\`\`\`
Best deployed when you need reliable output format over open-ended creativity.`,
'codellama:34b': (user, temp) => `## CodeLLaMA 34B Response
Specialised code-focused response from CodeLLaMA 34B.
**Query:** ${user.slice(0,60)}${user.length>60?'…':''}
\`\`\`python
# CodeLLaMA 34B — Code generation example
def process_query(input_text: str, temperature: float = ${temp}) -> dict:
"""
Process user query with the specified temperature.
Args:
input_text: The user's prompt
temperature: Sampling temperature (0.0 = deterministic, 1.0 = creative)
Returns:
dict with response and metadata
"""
return {
"model": "codellama:34b",
"input": input_text[:100],
"temperature": temperature,
"speciality": "code generation, review, debugging"
}
\`\`\`
CodeLLaMA 34B is optimised for programming tasks. For non-code queries, consider switching to LLaMA 3.1 70B.`,
'llava:13b': (user, temp) => `## LLaVA 13B Response
LLaVA is a vision-language model capable of processing both images and text. For this text-only query, it responds in standard language mode.
**Query:** ${user.slice(0,60)}${user.length>60?'…':''}
**Response:**
LLaVA 13B bridges vision and language understanding. When an image is attached to the query, it can:
- Describe visual content in detail
- Answer questions about images
- Extract text from documents and screenshots
- Compare multiple images
For pure text queries, LLaVA performs similarly to a standard 13B instruction-tuned model. **Best utilised when visual inputs are part of your workflow.**
*Tip: Upload an image alongside your prompt to leverage LLaVA's full multimodal capabilities.*`,
'gemma2:9b': (user, temp) => `## Gemma 2 9B Response
Google's Gemma 2 model — compact yet capable, optimised for instruction following and safety.
**Your query:** *${user.slice(0,70)}${user.length>70?'…':''}*
Gemma 2 9B is designed with strong instruction-following and alignment properties. Key characteristics:
| Property | Rating |
|----------|--------|
| Instruction following | ⭐⭐⭐⭐⭐ |
| Safety alignment | ⭐⭐⭐⭐⭐ |
| Code generation | ⭐⭐⭐⭐ |
| Creative writing | ⭐⭐⭐ |
| Speed | ⭐⭐⭐⭐ |
At temperature ${temp}, Gemma 2 produces ${temp < 0.4 ? 'highly predictable, structured' : 'varied and exploratory'} outputs. Excellent for enterprise deployments where safe, consistent responses are a priority.`,
'llama3.2:3b': (user, temp) => `## LLaMA 3.2 3B Response
Ultra-fast response from the smallest model. Best for simple tasks where speed is critical.
**Query:** ${user.slice(0,50)}${user.length>50?'…':''}
Quick answer: The 3B model handles straightforward Q&A, classification, and short-form generation efficiently.
**Trade-offs vs larger models:**
- ✅ Fastest inference time
- ✅ Lowest memory footprint (~2GB VRAM)
- ⚠️ Less nuanced reasoning
- ⚠️ Shorter effective context window
Ideal for: chatbots, quick lookups, real-time classification.`
};
// ── Run All ───────────────────────────────────────────────────────────────────
async function runAll() {
const user = document.getElementById('mc-user').value.trim();
const system = document.getElementById('mc-system').value.trim();
const temp = parseFloat(document.getElementById('mc-temp').value);
const maxTok = parseInt(document.getElementById('mc-maxtok').value);
if (!user) { showToast('Enter a message first'); return; }
if (selectedModels.size < 1) { showToast('Select at least one model'); return; }
const btn = document.getElementById('mc-run-btn');
btn.classList.add('running'); btn.disabled = true;
document.getElementById('mc-summary-bar').classList.add('hidden');
runResults = [];
// Build fresh columns
buildColumns(true);
const models = [...selectedModels];
// Start all in parallel
const promises = models.map(id => runModel(id, system, user, temp, maxTok));
await Promise.all(promises);
btn.classList.remove('running'); btn.disabled = false;
showSummary();
}
function colId(id) { return id.replace(/[:.]/g, '_'); }
async function runModel(id, system, user, temp, maxTok) {
const body = document.getElementById(`body-${colId(id)}`);
const statsDiv = document.getElementById(`stats-${colId(id)}`);
const m = MODELS.find(x => x.id === id);
const startTime = Date.now();
body.innerHTML = '';
body.classList.add('streaming');
const resp = document.createElement('div');
resp.className = 'mc-response';
resp.innerHTML = '<span class="mc-streaming-cursor"></span>';
body.appendChild(resp);
try {
const res = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ model: id, system_prompt: system, message: user, temperature: temp, max_tokens: maxTok })
});
if (!res.ok) throw new Error();
const data = await res.json();
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
const tokens = data.usage?.total_tokens || Math.floor((data.response||'').length / 4);
finishCol(id, resp, statsDiv, data.response || '', elapsed, tokens, body);
} catch {
const mockFn = MOCK_RESPONSES_MC[id] || ((u) => `Response from ${id}: ${u.slice(0,100)}`);
const mockText = mockFn(user, temp);
// Vary speeds per model
const speed = { 'llama3.1:8b': 15, 'llama3.1:70b': 40, 'mistral:7b': 18, 'codellama:34b': 35, 'llava:13b': 25, 'gemma2:9b': 20, 'llama3.2:3b': 10 };
const delay = speed[id] || 20;
await new Promise(resolve => {
let i = 0;
const iv = setInterval(() => {
i = Math.min(i + Math.floor(Math.random()*6)+3, mockText.length);
resp.innerHTML = renderMd(mockText.slice(0, i)) + (i < mockText.length ? '<span class="mc-streaming-cursor"></span>' : '');
body.scrollTop = body.scrollHeight;
if (i >= mockText.length) {
clearInterval(iv);
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
const tokens = Math.floor(mockText.length / 3.5);
finishCol(id, resp, statsDiv, mockText, elapsed, tokens, body);
resolve();
}
}, delay);
});
}
}
function finishCol(id, resp, statsDiv, text, elapsed, tokens, body) {
resp.innerHTML = renderMd(text);
body.classList.remove('streaming');
statsDiv.innerHTML = `
<span class="mc-col-stat green">${elapsed}s</span>
<span class="mc-col-stat">~${tokens} tok</span>
`;
runResults.push({ id, text, elapsed: parseFloat(elapsed), tokens });
}
// ── Summary ───────────────────────────────────────────────────────────────────
function showSummary() {
if (!runResults.length) return;
const bar = document.getElementById('mc-summary-bar');
bar.classList.remove('hidden');
const fastest = runResults.reduce((a,b) => a.elapsed < b.elapsed ? a : b);
const mostTok = runResults.reduce((a,b) => a.tokens > b.tokens ? a : b);
const avg = (runResults.reduce((s,r) => s + r.elapsed, 0) / runResults.length).toFixed(1);
const mFastest = MODELS.find(x => x.id === fastest.id);
const mMostTok = MODELS.find(x => x.id === mostTok.id);
document.getElementById('mc-fastest').textContent = `${mFastest?.short || fastest.id} (${fastest.elapsed}s)`;
document.getElementById('mc-most-tokens').textContent = `${mMostTok?.short || mostTok.id} (~${mostTok.tokens})`;
document.getElementById('mc-avg-latency').textContent = `${avg}s`;
// Win badges
const fColStats = document.getElementById(`stats-${colId(fastest.id)}`);
if (fColStats) fColStats.innerHTML += `<span class="mc-win-badge">fastest</span>`;
}
// ── Utilities ─────────────────────────────────────────────────────────────────
function copyCol(id) {
const body = document.getElementById(`body-${colId(id)}`);
if (!body) return;
navigator.clipboard.writeText(body.innerText).then(() => showToast('Copied'));
}
function sendColToStudio(id) {
const user = document.getElementById('mc-user').value;
const system = document.getElementById('mc-system').value;
localStorage.setItem('cezen_studio_import', JSON.stringify({ system, user, model: id }));
window.location.href = 'prompt-studio.html';
}
function openStudio() {
const user = document.getElementById('mc-user').value;
const system = document.getElementById('mc-system').value;
if (user || system) localStorage.setItem('cezen_studio_import', JSON.stringify({ system, user }));
window.location.href = 'prompt-studio.html';
}
function clearAll() {
document.getElementById('mc-user').value = '';
document.getElementById('mc-system').value = '';
runResults = [];
document.getElementById('mc-summary-bar').classList.add('hidden');
buildColumns(false);
}
function saveComparison() {
if (!runResults.length) { showToast('Run first'); return; }
const saved = JSON.parse(localStorage.getItem('cezen_compare_history') || '[]');
saved.push({
ts: new Date().toISOString(),
user: document.getElementById('mc-user').value,
system: document.getElementById('mc-system').value,
results: runResults
});
localStorage.setItem('cezen_compare_history', JSON.stringify(saved.slice(-20)));
showToast('Comparison saved');
}
function handleKey(e) {
if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) runAll();
}
// ── Markdown ──────────────────────────────────────────────────────────────────
function renderMd(raw) {
if (!raw) return '';
let html = raw
.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')
.replace(/```(\w*)\n([\s\S]*?)```/g, (_,lang,code) =>
`<pre><code class="lang-${lang}">${code.trimEnd()}</code></pre>`)
.replace(/`([^`]+)`/g,'<code>$1</code>')
.replace(/^#{3}\s+(.+)$/gm,'<h3>$1</h3>')
.replace(/^#{2}\s+(.+)$/gm,'<h2>$1</h2>')
.replace(/^#{1}\s+(.+)$/gm,'<h1>$1</h1>')
.replace(/\*\*(.+?)\*\*/g,'<strong>$1</strong>')
.replace(/\*(.+?)\*/g,'<em>$1</em>')
.replace(/^>\s(.+)$/gm,'<blockquote>$1</blockquote>')
.replace(/^\|(.+)\|$/gm, row => {
const cells = row.split('|').filter((_,i,a) => i>0 && i<a.length-1);
return '<tr>' + cells.map(c => c.trim().match(/^[-:]+$/) ? '' : `<td>${c.trim()}</td>`).join('') + '</tr>';
})
.replace(/(<tr>.*?<\/tr>\n?)+/gs, t => `<table>${t}</table>`)
.replace(/<table>(<tr><\/tr>\n?)/g,'<table>')
.replace(/^[-*]\s+(.+)$/gm,'<li>$1</li>')
.replace(/(<li>.*<\/li>\n?)+/g, l => `<ul>${l}</ul>`)
.replace(/^\d+\.\s+(.+)$/gm,'<li>$1</li>')
.replace(/\n{2,}/g,'</p><p>')
.replace(/\n/g,'<br>');
return `<p>${html}</p>`;
}
function showToast(msg) {
const t = document.getElementById('mc-toast');
t.textContent = msg; t.classList.add('show');
setTimeout(() => t.classList.remove('show'), 2200);
}
// ── Init ──────────────────────────────────────────────────────────────────────
// Load import from compare button in other pages
const compareImport = localStorage.getItem('cezen_compare_import');
if (compareImport) {
const d = JSON.parse(compareImport);
if (d.system) document.getElementById('mc-system').value = d.system;
if (d.user) document.getElementById('mc-user').value = d.user;
localStorage.removeItem('cezen_compare_import');
}
</script>
<script src="auth.js"></script>
<script src="branding.js"></script>
</body>
</html>