790 lines
34 KiB
HTML
790 lines
34 KiB
HTML
<!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 & 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 2–4)</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 2–4 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 2–4) — ${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,'&').replace(/</g,'<').replace(/>/g,'>')
|
||
.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>
|