Download index.html from ml-intern-explorers/MiniCoder-1-Inference: direct link, hf CLI and curl.
- Browser
- Download file 38.6 kB
-
https://huggingface.co/spaces/ml-intern-explorers/MiniCoder-1-Inference/resolve/4172decaa82d152ba7863215d64c2af54911042c/index.html
- Command line
-
hf download hf://spaces/ml-intern-explorers/MiniCoder-1-Inference@4172decaa82d152ba7863215d64c2af54911042c/index.html
-
curl -L -o index.html https://huggingface.co/spaces/ml-intern-explorers/MiniCoder-1-Inference/resolve/4172decaa82d152ba7863215d64c2af54911042c/index.html
38.6 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>CodVa-2 Β· Code Assistant</title> | |
| <style> | |
| /* ββ RESET & BASE βββββββββββββββββββββββββββββββββββββββββββββββββββ */ | |
| *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } | |
| :root { | |
| --bg: #0d0f14; | |
| --surface: #13161f; | |
| --surface2: #1a1e2b; | |
| --surface3: #21263a; | |
| --border: #2a2f45; | |
| --accent: #7c6aff; | |
| --accent2: #4fc3f7; | |
| --accent3: #43e97b; | |
| --think: #f7971e; | |
| --text: #e8eaf6; | |
| --text2: #9099bb; | |
| --text3: #5a6280; | |
| --red: #ff5370; | |
| --radius: 12px; | |
| --radius-sm: 8px; | |
| } | |
| html, body { | |
| height: 100%; | |
| background: var(--bg); | |
| color: var(--text); | |
| font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; | |
| font-size: 14px; | |
| line-height: 1.6; | |
| } | |
| /* ββ LAYOUT βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */ | |
| .app { | |
| display: grid; | |
| grid-template-rows: auto 1fr; | |
| grid-template-columns: 340px 1fr; | |
| grid-template-areas: | |
| "header header" | |
| "sidebar main"; | |
| height: 100vh; | |
| overflow: hidden; | |
| } | |
| /* ββ HEADER βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */ | |
| header { | |
| grid-area: header; | |
| display: flex; | |
| align-items: center; | |
| gap: 16px; | |
| padding: 12px 24px; | |
| background: var(--surface); | |
| border-bottom: 1px solid var(--border); | |
| z-index: 10; | |
| } | |
| .logo { | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| } | |
| .logo-icon { | |
| width: 36px; | |
| height: 36px; | |
| background: linear-gradient(135deg, var(--accent), var(--accent2)); | |
| border-radius: 10px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: 18px; | |
| font-weight: 800; | |
| color: white; | |
| letter-spacing: -1px; | |
| flex-shrink: 0; | |
| } | |
| .logo-text h1 { | |
| font-size: 16px; | |
| font-weight: 700; | |
| background: linear-gradient(90deg, var(--accent), var(--accent2)); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| } | |
| .logo-text p { | |
| font-size: 11px; | |
| color: var(--text3); | |
| } | |
| .header-stats { | |
| margin-left: auto; | |
| display: flex; | |
| gap: 12px; | |
| align-items: center; | |
| } | |
| .stat-pill { | |
| background: var(--surface2); | |
| border: 1px solid var(--border); | |
| border-radius: 20px; | |
| padding: 4px 12px; | |
| font-size: 11px; | |
| color: var(--text2); | |
| display: flex; | |
| align-items: center; | |
| gap: 6px; | |
| } | |
| .stat-pill .dot { | |
| width: 6px; | |
| height: 6px; | |
| border-radius: 50%; | |
| background: var(--accent3); | |
| animation: pulse 2s infinite; | |
| } | |
| @keyframes pulse { | |
| 0%, 100% { opacity: 1; } | |
| 50% { opacity: 0.4; } | |
| } | |
| /* ββ SIDEBAR ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */ | |
| .sidebar { | |
| grid-area: sidebar; | |
| background: var(--surface); | |
| border-right: 1px solid var(--border); | |
| display: flex; | |
| flex-direction: column; | |
| overflow-y: auto; | |
| overflow-x: hidden; | |
| } | |
| .sidebar-section { | |
| padding: 16px; | |
| border-bottom: 1px solid var(--border); | |
| } | |
| .sidebar-section:last-child { border-bottom: none; } | |
| .section-label { | |
| font-size: 10px; | |
| font-weight: 700; | |
| letter-spacing: 1px; | |
| text-transform: uppercase; | |
| color: var(--text3); | |
| margin-bottom: 12px; | |
| } | |
| /* ββ CONTEXT RING βββββββββββββββββββββββββββββββββββββββββββββββββββ */ | |
| .ctx-ring-wrap { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| gap: 12px; | |
| padding: 8px 0; | |
| } | |
| .ctx-ring-svg { | |
| position: relative; | |
| width: 140px; | |
| height: 140px; | |
| } | |
| .ctx-ring-svg svg { | |
| width: 140px; | |
| height: 140px; | |
| transform: rotate(-90deg); | |
| } | |
| .ctx-track { | |
| fill: none; | |
| stroke: var(--surface3); | |
| stroke-width: 10; | |
| } | |
| .ctx-prompt { | |
| fill: none; | |
| stroke: var(--accent2); | |
| stroke-width: 10; | |
| stroke-linecap: round; | |
| transition: stroke-dashoffset 0.3s ease; | |
| } | |
| .ctx-generated { | |
| fill: none; | |
| stroke: var(--accent); | |
| stroke-width: 10; | |
| stroke-linecap: round; | |
| transition: stroke-dashoffset 0.3s ease; | |
| } | |
| .ctx-think { | |
| fill: none; | |
| stroke: var(--think); | |
| stroke-width: 10; | |
| stroke-linecap: round; | |
| transition: stroke-dashoffset 0.3s ease; | |
| } | |
| .ctx-ring-center { | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| transform: translate(-50%, -50%); | |
| text-align: center; | |
| pointer-events: none; | |
| } | |
| .ctx-ring-center .ctx-num { | |
| font-size: 22px; | |
| font-weight: 800; | |
| color: var(--text); | |
| line-height: 1; | |
| } | |
| .ctx-ring-center .ctx-denom { | |
| font-size: 11px; | |
| color: var(--text3); | |
| } | |
| .ctx-legend { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 6px; | |
| width: 100%; | |
| } | |
| .legend-row { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| font-size: 12px; | |
| color: var(--text2); | |
| } | |
| .legend-dot { | |
| width: 10px; | |
| height: 10px; | |
| border-radius: 3px; | |
| flex-shrink: 0; | |
| } | |
| .legend-row span:last-child { | |
| margin-left: auto; | |
| font-variant-numeric: tabular-nums; | |
| color: var(--text3); | |
| font-size: 11px; | |
| } | |
| /* ββ THINK BUDGET BAR βββββββββββββββββββββββββββββββββββββββββββββββ */ | |
| .think-budget { | |
| background: var(--surface2); | |
| border: 1px solid var(--border); | |
| border-radius: var(--radius-sm); | |
| padding: 10px 12px; | |
| } | |
| .think-budget-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: 8px; | |
| font-size: 12px; | |
| } | |
| .think-budget-label { | |
| color: var(--think); | |
| font-weight: 600; | |
| display: flex; | |
| align-items: center; | |
| gap: 6px; | |
| } | |
| .think-budget-val { | |
| color: var(--text2); | |
| font-variant-numeric: tabular-nums; | |
| } | |
| .think-bar-track { | |
| height: 6px; | |
| background: var(--surface3); | |
| border-radius: 3px; | |
| overflow: hidden; | |
| } | |
| .think-bar-fill { | |
| height: 100%; | |
| background: linear-gradient(90deg, var(--think), #ff6b6b); | |
| border-radius: 3px; | |
| transition: width 0.3s ease; | |
| width: 0%; | |
| } | |
| .think-hint { | |
| font-size: 10px; | |
| color: var(--text3); | |
| margin-top: 6px; | |
| } | |
| /* ββ CONTROLS βββββββββββββββββββββββββββββββββββββββββββββββββββββββ */ | |
| label { | |
| display: block; | |
| font-size: 11px; | |
| font-weight: 600; | |
| color: var(--text3); | |
| margin-bottom: 6px; | |
| text-transform: uppercase; | |
| letter-spacing: 0.5px; | |
| } | |
| .slider-wrap { | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| margin-bottom: 14px; | |
| } | |
| input[type=range] { | |
| flex: 1; | |
| -webkit-appearance: none; | |
| height: 4px; | |
| border-radius: 2px; | |
| background: var(--surface3); | |
| outline: none; | |
| cursor: pointer; | |
| } | |
| input[type=range]::-webkit-slider-thumb { | |
| -webkit-appearance: none; | |
| width: 14px; | |
| height: 14px; | |
| border-radius: 50%; | |
| background: var(--accent); | |
| cursor: pointer; | |
| transition: background 0.2s; | |
| } | |
| input[type=range]::-webkit-slider-thumb:hover { | |
| background: var(--accent2); | |
| } | |
| .slider-val { | |
| font-size: 12px; | |
| color: var(--text); | |
| font-variant-numeric: tabular-nums; | |
| min-width: 36px; | |
| text-align: right; | |
| font-weight: 600; | |
| } | |
| /* ββ EXAMPLES βββββββββββββββββββββββββββββββββββββββββββββββββββββββ */ | |
| .examples-list { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 6px; | |
| } | |
| .example-btn { | |
| background: var(--surface2); | |
| border: 1px solid var(--border); | |
| border-radius: var(--radius-sm); | |
| padding: 8px 10px; | |
| color: var(--text2); | |
| font-size: 12px; | |
| cursor: pointer; | |
| text-align: left; | |
| transition: all 0.15s; | |
| line-height: 1.4; | |
| } | |
| .example-btn:hover { | |
| border-color: var(--accent); | |
| color: var(--text); | |
| background: var(--surface3); | |
| } | |
| /* ββ MAIN βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */ | |
| main { | |
| grid-area: main; | |
| display: flex; | |
| flex-direction: column; | |
| overflow: hidden; | |
| } | |
| /* ββ CHAT AREA ββββββββββββββββββββββββββββββββββββββββββββββββββββββ */ | |
| .chat-area { | |
| flex: 1; | |
| overflow-y: auto; | |
| padding: 24px; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 16px; | |
| } | |
| .chat-area::-webkit-scrollbar { width: 6px; } | |
| .chat-area::-webkit-scrollbar-track { background: transparent; } | |
| .chat-area::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; } | |
| /* ββ MESSAGES βββββββββββββββββββββββββββββββββββββββββββββββββββββββ */ | |
| .msg { | |
| max-width: 820px; | |
| width: 100%; | |
| } | |
| .msg-user { | |
| align-self: flex-end; | |
| } | |
| .msg-assistant { | |
| align-self: flex-start; | |
| } | |
| .msg-bubble { | |
| border-radius: var(--radius); | |
| padding: 14px 18px; | |
| line-height: 1.7; | |
| } | |
| .msg-user .msg-bubble { | |
| background: linear-gradient(135deg, var(--accent), #5a4fcf); | |
| color: white; | |
| border-bottom-right-radius: 4px; | |
| } | |
| .msg-assistant .msg-bubble { | |
| background: var(--surface); | |
| border: 1px solid var(--border); | |
| border-bottom-left-radius: 4px; | |
| } | |
| .msg-label { | |
| font-size: 11px; | |
| color: var(--text3); | |
| margin-bottom: 6px; | |
| font-weight: 600; | |
| text-transform: uppercase; | |
| letter-spacing: 0.5px; | |
| } | |
| .msg-user .msg-label { text-align: right; } | |
| /* ββ THINK BLOCK ββββββββββββββββββββββββββββββββββββββββββββββββββββ */ | |
| .think-block { | |
| background: rgba(247, 151, 30, 0.06); | |
| border: 1px solid rgba(247, 151, 30, 0.25); | |
| border-radius: var(--radius-sm); | |
| margin-bottom: 12px; | |
| overflow: hidden; | |
| } | |
| .think-header { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| padding: 8px 12px; | |
| background: rgba(247, 151, 30, 0.08); | |
| border-bottom: 1px solid rgba(247, 151, 30, 0.15); | |
| cursor: pointer; | |
| user-select: none; | |
| font-size: 12px; | |
| font-weight: 600; | |
| color: var(--think); | |
| } | |
| .think-header:hover { background: rgba(247, 151, 30, 0.12); } | |
| .think-chevron { | |
| margin-left: auto; | |
| transition: transform 0.2s; | |
| font-size: 10px; | |
| } | |
| .think-header.collapsed .think-chevron { | |
| transform: rotate(-90deg); | |
| } | |
| .think-body { | |
| padding: 12px 14px; | |
| font-size: 13px; | |
| color: var(--text2); | |
| white-space: pre-wrap; | |
| font-family: 'JetBrains Mono', 'Fira Code', monospace; | |
| line-height: 1.6; | |
| max-height: 300px; | |
| overflow-y: auto; | |
| } | |
| .think-body.collapsed { display: none; } | |
| /* ββ CODE BLOCK βββββββββββββββββββββββββββββββββββββββββββββββββββββ */ | |
| .code-block { | |
| background: var(--surface2); | |
| border: 1px solid var(--border); | |
| border-radius: var(--radius-sm); | |
| overflow: hidden; | |
| margin: 8px 0; | |
| } | |
| .code-header { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| padding: 6px 12px; | |
| background: var(--surface3); | |
| border-bottom: 1px solid var(--border); | |
| font-size: 11px; | |
| color: var(--text3); | |
| } | |
| .copy-btn { | |
| background: none; | |
| border: 1px solid var(--border); | |
| border-radius: 4px; | |
| padding: 2px 8px; | |
| color: var(--text3); | |
| font-size: 11px; | |
| cursor: pointer; | |
| transition: all 0.15s; | |
| } | |
| .copy-btn:hover { border-color: var(--accent); color: var(--accent); } | |
| pre { | |
| padding: 14px; | |
| overflow-x: auto; | |
| font-family: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace; | |
| font-size: 13px; | |
| line-height: 1.6; | |
| color: var(--text); | |
| margin: 0; | |
| } | |
| /* ββ ANSWER TEXT ββββββββββββββββββββββββββββββββββββββββββββββββββββ */ | |
| .answer-text { | |
| white-space: pre-wrap; | |
| word-break: break-word; | |
| } | |
| /* ββ STREAMING CURSOR βββββββββββββββββββββββββββββββββββββββββββββββ */ | |
| .cursor { | |
| display: inline-block; | |
| width: 2px; | |
| height: 1em; | |
| background: var(--accent); | |
| margin-left: 2px; | |
| vertical-align: text-bottom; | |
| animation: blink 0.8s steps(1) infinite; | |
| } | |
| @keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } } | |
| /* ββ STATUS BAR βββββββββββββββββββββββββββββββββββββββββββββββββββββ */ | |
| .status-bar { | |
| display: flex; | |
| gap: 16px; | |
| padding: 8px 18px; | |
| background: var(--surface); | |
| border-top: 1px solid var(--border); | |
| border-bottom: 1px solid var(--border); | |
| font-size: 11px; | |
| color: var(--text3); | |
| font-variant-numeric: tabular-nums; | |
| flex-wrap: wrap; | |
| } | |
| .status-item { | |
| display: flex; | |
| align-items: center; | |
| gap: 4px; | |
| } | |
| .status-item .val { | |
| color: var(--text2); | |
| font-weight: 600; | |
| } | |
| /* ββ INPUT AREA βββββββββββββββββββββββββββββββββββββββββββββββββββββ */ | |
| .input-area { | |
| padding: 16px 24px 20px; | |
| background: var(--surface); | |
| border-top: 1px solid var(--border); | |
| } | |
| .input-wrap { | |
| display: flex; | |
| gap: 10px; | |
| align-items: flex-end; | |
| background: var(--surface2); | |
| border: 1px solid var(--border); | |
| border-radius: var(--radius); | |
| padding: 10px 12px; | |
| transition: border-color 0.2s; | |
| } | |
| .input-wrap:focus-within { | |
| border-color: var(--accent); | |
| } | |
| textarea { | |
| flex: 1; | |
| background: none; | |
| border: none; | |
| outline: none; | |
| color: var(--text); | |
| font-size: 14px; | |
| font-family: inherit; | |
| line-height: 1.6; | |
| resize: none; | |
| min-height: 44px; | |
| max-height: 200px; | |
| overflow-y: auto; | |
| } | |
| textarea::placeholder { color: var(--text3); } | |
| .send-btn { | |
| width: 40px; | |
| height: 40px; | |
| border-radius: var(--radius-sm); | |
| border: none; | |
| background: linear-gradient(135deg, var(--accent), #5a4fcf); | |
| color: white; | |
| font-size: 18px; | |
| cursor: pointer; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| flex-shrink: 0; | |
| transition: all 0.15s; | |
| } | |
| .send-btn:hover:not(:disabled) { | |
| transform: scale(1.05); | |
| box-shadow: 0 4px 12px rgba(124, 106, 255, 0.4); | |
| } | |
| .send-btn:disabled { opacity: 0.5; cursor: not-allowed; } | |
| .stop-btn { | |
| width: 40px; | |
| height: 40px; | |
| border-radius: var(--radius-sm); | |
| border: 1px solid var(--red); | |
| background: rgba(255, 83, 112, 0.1); | |
| color: var(--red); | |
| font-size: 14px; | |
| cursor: pointer; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| flex-shrink: 0; | |
| transition: all 0.15s; | |
| } | |
| .stop-btn:hover { | |
| background: rgba(255, 83, 112, 0.2); | |
| } | |
| .stop-btn.hidden { display: none; } | |
| /* ββ EMPTY STATE ββββββββββββββββββββββββββββββββββββββββββββββββββββ */ | |
| .empty-state { | |
| flex: 1; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 16px; | |
| opacity: 0.6; | |
| pointer-events: none; | |
| } | |
| .empty-icon { | |
| font-size: 48px; | |
| } | |
| .empty-state h2 { | |
| font-size: 20px; | |
| font-weight: 700; | |
| color: var(--text2); | |
| } | |
| .empty-state p { | |
| font-size: 13px; | |
| color: var(--text3); | |
| text-align: center; | |
| max-width: 300px; | |
| } | |
| /* ββ SCROLLBAR ββββββββββββββββββββββββββββββββββββββββββββββββββββββ */ | |
| ::-webkit-scrollbar { width: 5px; height: 5px; } | |
| ::-webkit-scrollbar-track { background: transparent; } | |
| ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; } | |
| /* ββ RESPONSIVE βββββββββββββββββββββββββββββββββββββββββββββββββββββ */ | |
| @media (max-width: 700px) { | |
| .app { | |
| grid-template-columns: 1fr; | |
| grid-template-rows: auto auto 1fr; | |
| grid-template-areas: "header" "sidebar" "main"; | |
| } | |
| .sidebar { | |
| max-height: 260px; | |
| border-right: none; | |
| border-bottom: 1px solid var(--border); | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="app"> | |
| <!-- ββ HEADER ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ --> | |
| <header> | |
| <div class="logo"> | |
| <div class="logo-icon">C2</div> | |
| <div class="logo-text"> | |
| <h1>CodVa-2</h1> | |
| <p>213M Β· Instruction Tuned Β· SFT</p> | |
| </div> | |
| </div> | |
| <div class="header-stats"> | |
| <div class="stat-pill"> | |
| <div class="dot"></div> | |
| <span id="hdr-status">Ready</span> | |
| </div> | |
| <div class="stat-pill">β‘ <span id="hdr-tps">β</span> tok/s</div> | |
| <div class="stat-pill">π§ 213M params</div> | |
| </div> | |
| </header> | |
| <!-- ββ SIDEBAR βββββββββββββββββββββββββββββββββββββββββββββββββββββββ --> | |
| <aside class="sidebar"> | |
| <!-- Context Ring --> | |
| <div class="sidebar-section"> | |
| <div class="section-label">Context Window</div> | |
| <div class="ctx-ring-wrap"> | |
| <div class="ctx-ring-svg"> | |
| <svg viewBox="0 0 140 140"> | |
| <circle class="ctx-track" cx="70" cy="70" r="55"/> | |
| <!-- Prompt arc (cyan) --> | |
| <circle class="ctx-prompt" id="ring-prompt" cx="70" cy="70" r="55" | |
| stroke-dasharray="345.6" stroke-dashoffset="345.6"/> | |
| <!-- Think arc (orange) --> | |
| <circle class="ctx-think" id="ring-think" cx="70" cy="70" r="55" | |
| stroke-dasharray="345.6" stroke-dashoffset="345.6"/> | |
| <!-- Generated arc (purple) --> | |
| <circle class="ctx-generated" id="ring-gen" cx="70" cy="70" r="55" | |
| stroke-dasharray="345.6" stroke-dashoffset="345.6"/> | |
| </svg> | |
| <div class="ctx-ring-center"> | |
| <div class="ctx-num" id="ring-used">0</div> | |
| <div class="ctx-denom">/ 2048</div> | |
| </div> | |
| </div> | |
| <div class="ctx-legend"> | |
| <div class="legend-row"> | |
| <div class="legend-dot" style="background:var(--accent2)"></div> | |
| <span>Prompt</span> | |
| <span id="leg-prompt">0 tok</span> | |
| </div> | |
| <div class="legend-row"> | |
| <div class="legend-dot" style="background:var(--think)"></div> | |
| <span>Thinking</span> | |
| <span id="leg-think">0 tok</span> | |
| </div> | |
| <div class="legend-row"> | |
| <div class="legend-dot" style="background:var(--accent)"></div> | |
| <span>Response</span> | |
| <span id="leg-gen">0 tok</span> | |
| </div> | |
| <div class="legend-row"> | |
| <div class="legend-dot" style="background:var(--surface3)"></div> | |
| <span>Free</span> | |
| <span id="leg-free">2048 tok</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Think Budget --> | |
| <div class="sidebar-section"> | |
| <div class="section-label">Thinking Budget</div> | |
| <div class="think-budget"> | |
| <div class="think-budget-header"> | |
| <div class="think-budget-label"> | |
| <span>π§ </span> Think tokens | |
| </div> | |
| <div class="think-budget-val"> | |
| <span id="think-used">0</span> / <span id="think-max">512</span> | |
| </div> | |
| </div> | |
| <div class="think-bar-track"> | |
| <div class="think-bar-fill" id="think-bar"></div> | |
| </div> | |
| <div class="think-hint">Orange = model reasoning tokens</div> | |
| </div> | |
| </div> | |
| <!-- Parameters --> | |
| <div class="sidebar-section"> | |
| <div class="section-label">Parameters</div> | |
| <label>Max new tokens</label> | |
| <div class="slider-wrap"> | |
| <input type="range" id="sl-max" min="32" max="512" step="16" value="256"> | |
| <span class="slider-val" id="vl-max">256</span> | |
| </div> | |
| <label>Temperature</label> | |
| <div class="slider-wrap"> | |
| <input type="range" id="sl-temp" min="0" max="1.0" step="0.05" value="0.3"> | |
| <span class="slider-val" id="vl-temp">0.3</span> | |
| </div> | |
| <label>Top-P</label> | |
| <div class="slider-wrap"> | |
| <input type="range" id="sl-topp" min="0.5" max="1.0" step="0.05" value="0.95"> | |
| <span class="slider-val" id="vl-topp">0.95</span> | |
| </div> | |
| <label>Top-K</label> | |
| <div class="slider-wrap"> | |
| <input type="range" id="sl-topk" min="1" max="100" step="1" value="50"> | |
| <span class="slider-val" id="vl-topk">50</span> | |
| </div> | |
| </div> | |
| <!-- Examples --> | |
| <div class="sidebar-section"> | |
| <div class="section-label">Examples</div> | |
| <div class="examples-list"> | |
| <button class="example-btn" onclick="setPrompt('Write a Python function to compute fibonacci numbers recursively with memoization')"> | |
| π Fibonacci with memoization | |
| </button> | |
| <button class="example-btn" onclick="setPrompt('Implement a binary search algorithm in Python with detailed comments')"> | |
| π Binary search | |
| </button> | |
| <button class="example-btn" onclick="setPrompt('Write a quicksort implementation and explain the time complexity')"> | |
| β‘ Quicksort + complexity | |
| </button> | |
| <button class="example-btn" onclick="setPrompt('Create a Python class for a linked list with insert, delete, and search methods')"> | |
| π Linked list class | |
| </button> | |
| <button class="example-btn" onclick="setPrompt('How do I reverse a string in Python? Show multiple methods')"> | |
| π Reverse a string | |
| </button> | |
| <button class="example-btn" onclick="setPrompt('Explain what a decorator is in Python and write an example')"> | |
| π¨ Python decorators | |
| </button> | |
| </div> | |
| </div> | |
| </aside> | |
| <!-- ββ MAIN ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ --> | |
| <main> | |
| <!-- Status bar --> | |
| <div class="status-bar" id="status-bar"> | |
| <div class="status-item">π’ <span class="val" id="st-state">Idle</span></div> | |
| <div class="status-item">β± <span class="val" id="st-time">β</span></div> | |
| <div class="status-item">β‘ <span class="val" id="st-tps">β</span> tok/s</div> | |
| <div class="status-item">π€ <span class="val" id="st-tokens">β</span> tokens</div> | |
| <div class="status-item">π <span class="val" id="st-think">no thinking</span></div> | |
| </div> | |
| <!-- Chat area --> | |
| <div class="chat-area" id="chat-area"> | |
| <div class="empty-state" id="empty-state"> | |
| <div class="empty-icon">π§ </div> | |
| <h2>CodVa-2 SFT</h2> | |
| <p>Instruction-tuned code assistant.<br>Ask me anything about code!</p> | |
| </div> | |
| </div> | |
| <!-- Input --> | |
| <div class="input-area"> | |
| <div class="input-wrap"> | |
| <textarea | |
| id="prompt-input" | |
| placeholder="Ask me to write code, explain concepts, debug..." | |
| rows="1" | |
| onkeydown="handleKey(event)" | |
| oninput="autoResize(this)" | |
| ></textarea> | |
| <button class="stop-btn hidden" id="stop-btn" onclick="stopGen()" title="Stop generation">β </button> | |
| <button class="send-btn" id="send-btn" onclick="sendMessage()" title="Send (Ctrl+Enter)">βΆ</button> | |
| </div> | |
| </div> | |
| </main> | |
| </div> | |
| <script> | |
| // ββ STATE ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| const MAX_CTX = 2048; | |
| const CIRC = 2 * Math.PI * 55; // circumference for r=55 | |
| let isStreaming = false; | |
| let thinkTokens = 0; | |
| let genTokens = 0; | |
| let promptTokens = 0; | |
| // ββ SLIDERS ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| function linkSlider(id, valId, decimals = 0) { | |
| const s = document.getElementById(id); | |
| const v = document.getElementById(valId); | |
| s.addEventListener('input', () => { | |
| v.textContent = parseFloat(s.value).toFixed(decimals); | |
| }); | |
| } | |
| linkSlider('sl-max', 'vl-max', 0); | |
| linkSlider('sl-temp', 'vl-temp', 2); | |
| linkSlider('sl-topp', 'vl-topp', 2); | |
| linkSlider('sl-topk', 'vl-topk', 0); | |
| // ββ TEXTAREA AUTO-RESIZE ββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| function autoResize(el) { | |
| el.style.height = 'auto'; | |
| el.style.height = Math.min(el.scrollHeight, 200) + 'px'; | |
| } | |
| function handleKey(e) { | |
| if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { | |
| e.preventDefault(); | |
| sendMessage(); | |
| } | |
| } | |
| // ββ SET EXAMPLE PROMPT ββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| function setPrompt(text) { | |
| const ta = document.getElementById('prompt-input'); | |
| ta.value = text; | |
| autoResize(ta); | |
| ta.focus(); | |
| } | |
| // ββ CONTEXT RING UPDATE βββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| function updateRing(pTok, thinkTok, genTok) { | |
| const total = Math.min(pTok + thinkTok + genTok, MAX_CTX); | |
| // Arc lengths | |
| const pArc = (pTok / MAX_CTX) * CIRC; | |
| const thinkArc = (thinkTok / MAX_CTX) * CIRC; | |
| const genArc = (genTok / MAX_CTX) * CIRC; | |
| // Prompt arc (starts at 0) | |
| const rp = document.getElementById('ring-prompt'); | |
| rp.style.strokeDashoffset = CIRC - pArc; | |
| // Think arc (starts after prompt) | |
| const rt = document.getElementById('ring-think'); | |
| rt.style.strokeDasharray = `${thinkArc} ${CIRC - thinkArc}`; | |
| rt.style.strokeDashoffset = CIRC - pArc; | |
| // Generated arc (starts after prompt + think) | |
| const rg = document.getElementById('ring-gen'); | |
| rg.style.strokeDasharray = `${genArc} ${CIRC - genArc}`; | |
| rg.style.strokeDashoffset = CIRC - pArc - thinkArc; | |
| // Center number | |
| document.getElementById('ring-used').textContent = total; | |
| // Legend | |
| document.getElementById('leg-prompt').textContent = pTok + ' tok'; | |
| document.getElementById('leg-think').textContent = thinkTok + ' tok'; | |
| document.getElementById('leg-gen').textContent = genTok + ' tok'; | |
| document.getElementById('leg-free').textContent = Math.max(0, MAX_CTX - total) + ' tok'; | |
| // Think budget bar | |
| const maxThink = parseInt(document.getElementById('sl-max').value); | |
| const thinkPct = Math.min(100, (thinkTok / maxThink) * 100); | |
| document.getElementById('think-bar').style.width = thinkPct + '%'; | |
| document.getElementById('think-used').textContent = thinkTok; | |
| document.getElementById('think-max').textContent = maxThink; | |
| } | |
| // ββ PARSE RESPONSE ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| // Handles both <think> and <|think|> variants | |
| function parseResponse(raw) { | |
| // Normalize both think tag variants to same format | |
| let normalized = raw | |
| .replace(/<\|think\|>/g, '<THINK>') | |
| .replace(/<\|\/think\|>/g, '</THINK>') | |
| .replace(/<think>/gi, '<THINK>') | |
| .replace(/<\/think>/gi, '</THINK>'); | |
| // Clean up ChatML tokens | |
| normalized = normalized | |
| .replace(/<\|im_start\|>\w+\n?/g, '') | |
| .replace(/<\|im_end\|>/g, '') | |
| .replace(/<\|endoftext\|>/g, ''); | |
| const thinkMatch = normalized.match(/<THINK>([\s\S]*?)(<\/THINK>|$)/); | |
| let thinkContent = ''; | |
| let answerContent = normalized; | |
| if (thinkMatch) { | |
| thinkContent = thinkMatch[1].trim(); | |
| answerContent = normalized.replace(/<THINK>[\s\S]*?(<\/THINK>|$)/, '').trim(); | |
| } | |
| // Rough token estimate for think content | |
| const thinkTokEst = Math.round(thinkContent.split(/\s+/).length * 1.3); | |
| return { thinkContent, answerContent, thinkTokEst }; | |
| } | |
| // ββ RENDER ANSWER βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| function renderAnswer(text, isStreaming) { | |
| // Split by code blocks | |
| const parts = text.split(/(```[\s\S]*?```|```[\s\S]*$)/g); | |
| let html = ''; | |
| for (const part of parts) { | |
| if (part.startsWith('```')) { | |
| const lines = part.replace(/^```/, '').split('\n'); | |
| const lang = lines[0].trim() || 'code'; | |
| const code = lines.slice(1).join('\n').replace(/```$/, '').trim(); | |
| const escaped = code.replace(/</g,'<').replace(/>/g,'>'); | |
| html += ` | |
| <div class="code-block"> | |
| <div class="code-header"> | |
| <span>${lang}</span> | |
| <button class="copy-btn" onclick="copyCode(this)">Copy</button> | |
| </div> | |
| <pre>${escaped}</pre> | |
| </div>`; | |
| } else if (part.trim()) { | |
| const escaped = part.replace(/</g,'<').replace(/>/g,'>'); | |
| html += `<div class="answer-text">${escaped}</div>`; | |
| } | |
| } | |
| if (isStreaming) html += '<span class="cursor"></span>'; | |
| return html; | |
| } | |
| // ββ COPY CODE βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| function copyCode(btn) { | |
| const code = btn.closest('.code-block').querySelector('pre').textContent; | |
| navigator.clipboard.writeText(code).then(() => { | |
| btn.textContent = 'Copied!'; | |
| setTimeout(() => btn.textContent = 'Copy', 1500); | |
| }); | |
| } | |
| // ββ ADD USER MESSAGE ββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| function addUserMsg(text) { | |
| document.getElementById('empty-state')?.remove(); | |
| const div = document.createElement('div'); | |
| div.className = 'msg msg-user'; | |
| div.innerHTML = ` | |
| <div class="msg-label">You</div> | |
| <div class="msg-bubble">${text.replace(/</g,'<').replace(/>/g,'>')}</div> | |
| `; | |
| document.getElementById('chat-area').appendChild(div); | |
| return div; | |
| } | |
| // ββ ADD ASSISTANT MESSAGE βββββββββββββββββββββββββββββββββββββββββββββββββ | |
| function addAssistantMsg() { | |
| const div = document.createElement('div'); | |
| div.className = 'msg msg-assistant'; | |
| div.innerHTML = ` | |
| <div class="msg-label">CodVa-2</div> | |
| <div class="msg-bubble" id="current-bubble"> | |
| <span class="cursor"></span> | |
| </div> | |
| `; | |
| document.getElementById('chat-area').appendChild(div); | |
| scrollBottom(); | |
| return div; | |
| } | |
| // ββ UPDATE ASSISTANT BUBBLE βββββββββββββββββββββββββββββββββββββββββββββββ | |
| function updateBubble(raw, streaming) { | |
| const bubble = document.getElementById('current-bubble'); | |
| if (!bubble) return; | |
| const { thinkContent, answerContent, thinkTokEst } = parseResponse(raw); | |
| let html = ''; | |
| // Think block (collapsible) | |
| if (thinkContent) { | |
| html += ` | |
| <div class="think-block"> | |
| <div class="think-header" onclick="toggleThink(this)"> | |
| <span>π§ </span> | |
| <span>Thinking${streaming && !raw.includes('</THINK>') && !raw.includes('<|/think|>') ? '...' : ''}</span> | |
| <span>~${thinkTokEst} tokens</span> | |
| <span class="think-chevron">βΌ</span> | |
| </div> | |
| <div class="think-body">${thinkContent.replace(/</g,'<').replace(/>/g,'>')}</div> | |
| </div>`; | |
| } | |
| // Answer | |
| html += renderAnswer(answerContent, streaming); | |
| bubble.innerHTML = html; | |
| } | |
| function toggleThink(header) { | |
| header.classList.toggle('collapsed'); | |
| header.nextElementSibling.classList.toggle('collapsed'); | |
| } | |
| // ββ SCROLL TO BOTTOM ββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| function scrollBottom() { | |
| const ca = document.getElementById('chat-area'); | |
| ca.scrollTop = ca.scrollHeight; | |
| } | |
| // ββ STATUS UPDATE ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| function updateStatus(state, tps, elapsed, tokens, thinkTok) { | |
| document.getElementById('st-state').textContent = state; | |
| document.getElementById('st-time').textContent = elapsed ? elapsed + 's' : 'β'; | |
| document.getElementById('st-tps').textContent = tps ? tps : 'β'; | |
| document.getElementById('st-tokens').textContent = tokens ? tokens : 'β'; | |
| document.getElementById('st-think').textContent = thinkTok > 0 ? `${thinkTok} think tok` : 'no thinking'; | |
| document.getElementById('hdr-status').textContent = state; | |
| document.getElementById('hdr-tps').textContent = tps || 'β'; | |
| } | |
| // ββ SEND MESSAGE ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| async function sendMessage() { | |
| if (isStreaming) return; | |
| const input = document.getElementById('prompt-input'); | |
| const prompt = input.value.trim(); | |
| if (!prompt) return; | |
| isStreaming = true; | |
| input.value = ''; | |
| autoResize(input); | |
| document.getElementById('send-btn').disabled = true; | |
| document.getElementById('stop-btn').classList.remove('hidden'); | |
| addUserMsg(prompt); | |
| addAssistantMsg(); | |
| const params = { | |
| prompt, | |
| max_new: parseInt(document.getElementById('sl-max').value), | |
| temperature: parseFloat(document.getElementById('sl-temp').value), | |
| top_p: parseFloat(document.getElementById('sl-topp').value), | |
| top_k: parseInt(document.getElementById('sl-topk').value), | |
| }; | |
| updateStatus('Generating...', null, null, null, 0); | |
| thinkTokens = 0; genTokens = 0; promptTokens = 0; | |
| try { | |
| const resp = await fetch('/generate', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify(params), | |
| }); | |
| const reader = resp.body.getReader(); | |
| const decoder = new TextDecoder(); | |
| let buffer = ''; | |
| let rawAccum = ''; | |
| while (true) { | |
| const { done, value } = await reader.read(); | |
| if (done) break; | |
| buffer += decoder.decode(value, { stream: true }); | |
| const lines = buffer.split('\n'); | |
| buffer = lines.pop(); | |
| for (const line of lines) { | |
| if (!line.startsWith('data: ')) continue; | |
| try { | |
| const data = JSON.parse(line.slice(6)); | |
| if (data.type === 'ctx') { | |
| promptTokens = data.prompt_tokens; | |
| updateRing(promptTokens, 0, 0); | |
| } | |
| else if (data.type === 'token') { | |
| rawAccum = data.raw; | |
| // Estimate think vs gen tokens from raw text | |
| const { thinkTokEst } = parseResponse(rawAccum); | |
| thinkTokens = thinkTokEst; | |
| genTokens = Math.max(0, data.step - thinkTokEst); | |
| updateRing(promptTokens, thinkTokens, genTokens); | |
| updateBubble(rawAccum, true); | |
| updateStatus('Generating...', data.tps, data.elapsed, data.step, thinkTokens); | |
| scrollBottom(); | |
| } | |
| else if (data.type === 'done') { | |
| updateBubble(rawAccum, false); | |
| updateStatus('Done', data.tps, data.elapsed, data.tokens, thinkTokens); | |
| updateRing(promptTokens, thinkTokens, genTokens); | |
| scrollBottom(); | |
| } | |
| } catch (e) { /* skip malformed */ } | |
| } | |
| } | |
| } catch (e) { | |
| const bubble = document.getElementById('current-bubble'); | |
| if (bubble) bubble.innerHTML = `<span style="color:var(--red)">Error: ${e.message}</span>`; | |
| updateStatus('Error', null, null, null, 0); | |
| } | |
| isStreaming = false; | |
| document.getElementById('send-btn').disabled = false; | |
| document.getElementById('stop-btn').classList.add('hidden'); | |
| } | |
| // ββ STOP ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| async function stopGen() { | |
| await fetch('/stop', { method: 'POST' }); | |
| updateStatus('Stopped', null, null, null, thinkTokens); | |
| const bubble = document.getElementById('current-bubble'); | |
| if (bubble) { | |
| bubble.querySelectorAll('.cursor').forEach(c => c.remove()); | |
| } | |
| isStreaming = false; | |
| document.getElementById('send-btn').disabled = false; | |
| document.getElementById('stop-btn').classList.add('hidden'); | |
| } | |
| // ββ INIT ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| updateRing(0, 0, 0); | |
| document.getElementById('prompt-input').focus(); | |
| </script> | |
| </body> | |
| </html> |