Package portal and close UI API gaps

This commit is contained in:
Jino Jose committed 2026-06-30 10:51:41 +05:30
1 parent e71b069014
commit 9ce9efc82e
59 files changed
+29355 -2

No files matched your search

+789
View File
@@ -0,0 +1,789 @@
<!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 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,'&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>