|
|
<!DOCTYPE html> |
|
|
<html lang="en"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<meta name="theme-color" content="#000000"> |
|
|
<meta name="description" content="Micro Distillery: GRPO + VAE Enhanced Training System"> |
|
|
<title>[mD] MICRO DISTILLERY | webXOS 2025</title> |
|
|
|
|
|
|
|
|
<link rel="manifest" href="manifest.json"> |
|
|
|
|
|
|
|
|
<link rel="apple-touch-icon" href="icon-192.png"> |
|
|
|
|
|
<style> |
|
|
:root { |
|
|
--bg: #000000; |
|
|
--text: #00ff00; |
|
|
--border: #008000; |
|
|
--accent: #00ffff; |
|
|
--error: #ff0000; |
|
|
--success: #00ff00; |
|
|
--warning: #ffff00; |
|
|
--distill: #00ff9d; |
|
|
--quantize: #ff00ff; |
|
|
--teacher: #ff6b00; |
|
|
--upload: #00ccff; |
|
|
--export: #9d00ff; |
|
|
--panel-bg: rgba(0, 20, 0, 0.1); |
|
|
--grpo: #ff00ff; |
|
|
--vae: #00ddff; |
|
|
--cache: #ff9900; |
|
|
--mask: #aa00ff; |
|
|
--sandbox: #00aa88; |
|
|
} |
|
|
|
|
|
* { |
|
|
margin: 0; |
|
|
padding: 0; |
|
|
box-sizing: border-box; |
|
|
font-family: 'Courier New', monospace; |
|
|
} |
|
|
|
|
|
body { |
|
|
background: var(--bg); |
|
|
color: var(--text); |
|
|
padding: 10px; |
|
|
font-size: 12px; |
|
|
line-height: 1.3; |
|
|
max-width: 1600px; |
|
|
margin: 0 auto; |
|
|
min-height: 100vh; |
|
|
overflow-x: hidden; |
|
|
} |
|
|
|
|
|
.header { |
|
|
padding: 10px 0; |
|
|
margin-bottom: 15px; |
|
|
text-align: center; |
|
|
border-bottom: 1px solid var(--border); |
|
|
} |
|
|
|
|
|
.title { |
|
|
font-size: 18px; |
|
|
color: var(--text); |
|
|
margin-bottom: 5px; |
|
|
} |
|
|
|
|
|
.subtitle { |
|
|
font-size: 12px; |
|
|
color: var(--accent); |
|
|
margin-bottom: 10px; |
|
|
} |
|
|
|
|
|
.header-controls { |
|
|
display: flex; |
|
|
justify-content: center; |
|
|
gap: 10px; |
|
|
margin-top: 10px; |
|
|
} |
|
|
|
|
|
.main-grid { |
|
|
display: grid; |
|
|
grid-template-columns: 350px 350px 1fr; |
|
|
gap: 15px; |
|
|
margin-bottom: 15px; |
|
|
} |
|
|
|
|
|
@media (max-width: 1200px) { |
|
|
.main-grid { |
|
|
grid-template-columns: 1fr; |
|
|
} |
|
|
} |
|
|
|
|
|
.panel { |
|
|
border: 1px solid var(--border); |
|
|
padding: 15px; |
|
|
background: var(--panel-bg); |
|
|
border-radius: 3px; |
|
|
min-height: 500px; |
|
|
} |
|
|
|
|
|
.panel-title { |
|
|
color: var(--accent); |
|
|
margin-bottom: 10px; |
|
|
padding-bottom: 5px; |
|
|
border-bottom: 1px solid var(--border); |
|
|
font-size: 12px; |
|
|
font-weight: bold; |
|
|
} |
|
|
|
|
|
.section { |
|
|
padding: 10px; |
|
|
border-radius: 3px; |
|
|
margin: 10px 0; |
|
|
} |
|
|
|
|
|
.grpo-section { |
|
|
border: 1px solid var(--grpo); |
|
|
background: rgba(255, 0, 255, 0.05); |
|
|
} |
|
|
|
|
|
.vae-section { |
|
|
border: 1px solid var(--vae); |
|
|
background: rgba(0, 221, 255, 0.05); |
|
|
} |
|
|
|
|
|
.cache-section { |
|
|
border: 1px solid var(--cache); |
|
|
background: rgba(255, 153, 0, 0.05); |
|
|
} |
|
|
|
|
|
.mask-section { |
|
|
border: 1px solid var(--mask); |
|
|
background: rgba(170, 0, 255, 0.05); |
|
|
} |
|
|
|
|
|
.sandbox-section { |
|
|
border: 1px solid var(--sandbox); |
|
|
background: rgba(0, 170, 136, 0.05); |
|
|
} |
|
|
|
|
|
.slider-container { |
|
|
margin: 8px 0; |
|
|
} |
|
|
|
|
|
.slider-container label { |
|
|
display: block; |
|
|
font-size: 11px; |
|
|
color: var(--accent); |
|
|
margin-bottom: 3px; |
|
|
} |
|
|
|
|
|
.slider-value { |
|
|
float: right; |
|
|
font-size: 10px; |
|
|
color: #008000; |
|
|
} |
|
|
|
|
|
input[type="range"] { |
|
|
width: 100%; |
|
|
height: 4px; |
|
|
background: #111; |
|
|
border-radius: 2px; |
|
|
outline: none; |
|
|
-webkit-appearance: none; |
|
|
} |
|
|
|
|
|
input[type="range"]::-webkit-slider-thumb { |
|
|
-webkit-appearance: none; |
|
|
width: 12px; |
|
|
height: 12px; |
|
|
background: var(--grpo); |
|
|
border-radius: 50%; |
|
|
cursor: pointer; |
|
|
} |
|
|
|
|
|
.action-btn { |
|
|
background: #001100; |
|
|
border: 1px solid var(--border); |
|
|
color: var(--text); |
|
|
padding: 10px; |
|
|
font-size: 12px; |
|
|
cursor: pointer; |
|
|
border-radius: 3px; |
|
|
font-family: 'Courier New', monospace; |
|
|
transition: all 0.2s; |
|
|
width: 100%; |
|
|
margin: 5px 0; |
|
|
} |
|
|
|
|
|
.action-btn:hover { |
|
|
background: #002200; |
|
|
border-color: var(--accent); |
|
|
box-shadow: 0 0 5px var(--accent); |
|
|
} |
|
|
|
|
|
.btn-grpo { |
|
|
border-color: var(--grpo); |
|
|
color: var(--grpo); |
|
|
} |
|
|
|
|
|
.btn-vae { |
|
|
border-color: var(--vae); |
|
|
color: var(--vae); |
|
|
} |
|
|
|
|
|
.btn-sandbox { |
|
|
border-color: var(--sandbox); |
|
|
color: var(--sandbox); |
|
|
} |
|
|
|
|
|
.btn-export { |
|
|
border-color: var(--export); |
|
|
color: var(--export); |
|
|
padding: 8px 15px; |
|
|
width: auto; |
|
|
} |
|
|
|
|
|
#terminal { |
|
|
height: 300px; |
|
|
overflow-y: auto; |
|
|
background: #000; |
|
|
border: 1px solid var(--border); |
|
|
padding: 10px; |
|
|
font-size: 11px; |
|
|
font-family: 'Courier New', monospace; |
|
|
line-height: 1.3; |
|
|
margin-bottom: 15px; |
|
|
} |
|
|
|
|
|
.log-entry { |
|
|
margin-bottom: 2px; |
|
|
padding: 1px 0; |
|
|
border-bottom: 1px solid rgba(0, 255, 0, 0.05); |
|
|
} |
|
|
|
|
|
.log-grpo { color: var(--grpo); } |
|
|
.log-vae { color: var(--vae); } |
|
|
.log-cache { color: var(--cache); } |
|
|
.log-mask { color: var(--mask); } |
|
|
.log-sandbox { color: var(--sandbox); } |
|
|
.log-error { color: var(--error); } |
|
|
.log-success { color: var(--success); } |
|
|
.log-export { color: var(--export); } |
|
|
|
|
|
.metrics-grid { |
|
|
display: grid; |
|
|
grid-template-columns: repeat(3, 1fr); |
|
|
gap: 5px; |
|
|
margin: 10px 0; |
|
|
} |
|
|
|
|
|
.metric-card { |
|
|
background: rgba(0, 30, 0, 0.2); |
|
|
border: 1px solid var(--border); |
|
|
padding: 5px; |
|
|
border-radius: 2px; |
|
|
text-align: center; |
|
|
font-size: 10px; |
|
|
} |
|
|
|
|
|
.metric-value { |
|
|
font-size: 12px; |
|
|
font-weight: bold; |
|
|
} |
|
|
|
|
|
.metric-grpo { color: var(--grpo); } |
|
|
.metric-vae { color: var(--vae); } |
|
|
.metric-cache { color: var(--cache); } |
|
|
.metric-mask { color: var(--mask); } |
|
|
|
|
|
.sandbox-container { |
|
|
margin-top: 15px; |
|
|
border: 1px solid var(--border); |
|
|
border-radius: 3px; |
|
|
overflow: hidden; |
|
|
} |
|
|
|
|
|
.sandbox-header { |
|
|
background: rgba(0, 30, 0, 0.3); |
|
|
padding: 8px; |
|
|
border-bottom: 1px solid var(--border); |
|
|
color: var(--sandbox); |
|
|
font-weight: bold; |
|
|
} |
|
|
|
|
|
.sandbox-content { |
|
|
padding: 10px; |
|
|
background: rgba(0, 0, 0, 0.5); |
|
|
height: 150px; |
|
|
overflow-y: auto; |
|
|
font-family: monospace; |
|
|
font-size: 11px; |
|
|
white-space: pre-wrap; |
|
|
} |
|
|
|
|
|
.sandbox-input { |
|
|
display: flex; |
|
|
border-top: 1px solid var(--border); |
|
|
background: rgba(0, 30, 0, 0.2); |
|
|
} |
|
|
|
|
|
.sandbox-input input { |
|
|
flex: 1; |
|
|
background: transparent; |
|
|
color: var(--text); |
|
|
border: none; |
|
|
padding: 8px; |
|
|
font-family: monospace; |
|
|
font-size: 11px; |
|
|
} |
|
|
|
|
|
.sandbox-input button { |
|
|
background: rgba(0, 170, 136, 0.2); |
|
|
border: none; |
|
|
border-left: 1px solid var(--border); |
|
|
color: var(--sandbox); |
|
|
padding: 8px 15px; |
|
|
cursor: pointer; |
|
|
font-size: 11px; |
|
|
} |
|
|
|
|
|
.token-display { |
|
|
display: flex; |
|
|
flex-wrap: wrap; |
|
|
gap: 3px; |
|
|
margin: 10px 0; |
|
|
padding: 10px; |
|
|
background: rgba(0, 0, 0, 0.3); |
|
|
border-radius: 3px; |
|
|
max-height: 100px; |
|
|
overflow-y: auto; |
|
|
} |
|
|
|
|
|
.token { |
|
|
padding: 2px 5px; |
|
|
background: rgba(0, 255, 0, 0.1); |
|
|
border: 1px solid rgba(0, 255, 0, 0.3); |
|
|
border-radius: 2px; |
|
|
font-size: 9px; |
|
|
font-family: monospace; |
|
|
} |
|
|
|
|
|
.token.cached { |
|
|
background: rgba(255, 153, 0, 0.2); |
|
|
border-color: var(--cache); |
|
|
} |
|
|
|
|
|
.token.masked { |
|
|
background: rgba(170, 0, 255, 0.2); |
|
|
border-color: var(--mask); |
|
|
opacity: 0.6; |
|
|
} |
|
|
|
|
|
.progress-container { |
|
|
margin: 10px 0; |
|
|
} |
|
|
|
|
|
.progress-bar { |
|
|
height: 8px; |
|
|
background: #111; |
|
|
border: 1px solid var(--border); |
|
|
overflow: hidden; |
|
|
border-radius: 2px; |
|
|
} |
|
|
|
|
|
.progress-fill { |
|
|
height: 100%; |
|
|
background: linear-gradient(90deg, #008000, #00ff00); |
|
|
width: 0%; |
|
|
transition: width 0.3s; |
|
|
} |
|
|
|
|
|
.progress-text { |
|
|
text-align: center; |
|
|
margin-top: 3px; |
|
|
font-size: 10px; |
|
|
color: #008000; |
|
|
} |
|
|
|
|
|
.hidden { |
|
|
display: none !important; |
|
|
} |
|
|
|
|
|
.latent-space { |
|
|
display: grid; |
|
|
grid-template-columns: repeat(4, 1fr); |
|
|
gap: 5px; |
|
|
margin: 10px 0; |
|
|
} |
|
|
|
|
|
.latent-dim { |
|
|
padding: 5px; |
|
|
background: rgba(0, 221, 255, 0.1); |
|
|
border: 1px solid var(--vae); |
|
|
border-radius: 2px; |
|
|
text-align: center; |
|
|
font-size: 9px; |
|
|
} |
|
|
|
|
|
.latent-value { |
|
|
font-size: 10px; |
|
|
font-weight: bold; |
|
|
color: var(--vae); |
|
|
display: block; |
|
|
margin-top: 2px; |
|
|
} |
|
|
|
|
|
.status-indicator { |
|
|
display: inline-block; |
|
|
width: 8px; |
|
|
height: 8px; |
|
|
border-radius: 50%; |
|
|
margin-right: 5px; |
|
|
} |
|
|
|
|
|
.status-active { background: var(--success); } |
|
|
.status-inactive { background: #333; } |
|
|
.status-error { background: var(--error); } |
|
|
|
|
|
select { |
|
|
width: 100%; |
|
|
background: #001100; |
|
|
color: #00ff00; |
|
|
border: 1px solid var(--border); |
|
|
padding: 6px; |
|
|
border-radius: 2px; |
|
|
font-size: 11px; |
|
|
font-family: 'Courier New', monospace; |
|
|
margin: 5px 0; |
|
|
} |
|
|
|
|
|
|
|
|
.export-modal { |
|
|
position: fixed; |
|
|
top: 0; |
|
|
left: 0; |
|
|
width: 100%; |
|
|
height: 100%; |
|
|
background: rgba(0, 0, 0, 0.85); |
|
|
display: flex; |
|
|
align-items: center; |
|
|
justify-content: center; |
|
|
z-index: 1000; |
|
|
} |
|
|
|
|
|
.export-modal.hidden { |
|
|
display: none; |
|
|
} |
|
|
|
|
|
.export-window { |
|
|
background: var(--bg); |
|
|
border: 2px solid var(--export); |
|
|
border-radius: 5px; |
|
|
width: 800px; |
|
|
max-width: 90%; |
|
|
height: 600px; |
|
|
display: flex; |
|
|
flex-direction: column; |
|
|
box-shadow: 0 0 30px rgba(157, 0, 255, 0.3); |
|
|
} |
|
|
|
|
|
.export-header { |
|
|
background: rgba(157, 0, 255, 0.1); |
|
|
padding: 15px; |
|
|
border-bottom: 1px solid var(--export); |
|
|
color: var(--export); |
|
|
font-size: 14px; |
|
|
font-weight: bold; |
|
|
display: flex; |
|
|
justify-content: space-between; |
|
|
align-items: center; |
|
|
} |
|
|
|
|
|
.close-export { |
|
|
background: none; |
|
|
border: 1px solid var(--export); |
|
|
color: var(--export); |
|
|
padding: 5px 10px; |
|
|
cursor: pointer; |
|
|
border-radius: 3px; |
|
|
font-family: 'Courier New', monospace; |
|
|
font-size: 12px; |
|
|
} |
|
|
|
|
|
.close-export:hover { |
|
|
background: rgba(157, 0, 255, 0.2); |
|
|
} |
|
|
|
|
|
.export-body { |
|
|
display: flex; |
|
|
flex: 1; |
|
|
overflow: hidden; |
|
|
} |
|
|
|
|
|
.export-sidebar { |
|
|
width: 200px; |
|
|
background: rgba(0, 20, 0, 0.2); |
|
|
border-right: 1px solid var(--border); |
|
|
padding: 15px; |
|
|
overflow-y: auto; |
|
|
} |
|
|
|
|
|
.export-main { |
|
|
flex: 1; |
|
|
padding: 15px; |
|
|
overflow-y: auto; |
|
|
} |
|
|
|
|
|
.export-format { |
|
|
margin-bottom: 20px; |
|
|
} |
|
|
|
|
|
.format-option { |
|
|
padding: 8px; |
|
|
border: 1px solid var(--border); |
|
|
margin: 5px 0; |
|
|
border-radius: 3px; |
|
|
cursor: pointer; |
|
|
transition: all 0.2s; |
|
|
font-size: 11px; |
|
|
} |
|
|
|
|
|
.format-option:hover { |
|
|
border-color: var(--export); |
|
|
background: rgba(157, 0, 255, 0.1); |
|
|
} |
|
|
|
|
|
.format-option.selected { |
|
|
border-color: var(--export); |
|
|
background: rgba(157, 0, 255, 0.2); |
|
|
} |
|
|
|
|
|
.file-browser { |
|
|
margin-top: 20px; |
|
|
} |
|
|
|
|
|
.file-item { |
|
|
padding: 5px 10px; |
|
|
border-bottom: 1px solid rgba(0, 255, 0, 0.1); |
|
|
cursor: pointer; |
|
|
font-size: 11px; |
|
|
display: flex; |
|
|
align-items: center; |
|
|
gap: 8px; |
|
|
} |
|
|
|
|
|
.file-item:hover { |
|
|
background: rgba(0, 255, 0, 0.05); |
|
|
} |
|
|
|
|
|
.file-item.selected { |
|
|
background: rgba(157, 0, 255, 0.1); |
|
|
} |
|
|
|
|
|
.file-icon { |
|
|
color: var(--export); |
|
|
font-size: 12px; |
|
|
} |
|
|
|
|
|
.file-content { |
|
|
background: rgba(0, 0, 0, 0.5); |
|
|
border: 1px solid var(--border); |
|
|
border-radius: 3px; |
|
|
padding: 15px; |
|
|
margin-top: 15px; |
|
|
max-height: 300px; |
|
|
overflow-y: auto; |
|
|
font-family: monospace; |
|
|
font-size: 10px; |
|
|
white-space: pre-wrap; |
|
|
} |
|
|
|
|
|
.export-footer { |
|
|
padding: 15px; |
|
|
border-top: 1px solid var(--border); |
|
|
display: flex; |
|
|
justify-content: space-between; |
|
|
align-items: center; |
|
|
background: rgba(0, 20, 0, 0.2); |
|
|
} |
|
|
|
|
|
.export-btn { |
|
|
background: rgba(157, 0, 255, 0.2); |
|
|
border: 1px solid var(--export); |
|
|
color: var(--export); |
|
|
padding: 10px 20px; |
|
|
cursor: pointer; |
|
|
border-radius: 3px; |
|
|
font-family: 'Courier New', monospace; |
|
|
font-size: 12px; |
|
|
transition: all 0.2s; |
|
|
} |
|
|
|
|
|
.export-btn:hover { |
|
|
background: rgba(157, 0, 255, 0.3); |
|
|
box-shadow: 0 0 10px rgba(157, 0, 255, 0.5); |
|
|
} |
|
|
|
|
|
.export-info { |
|
|
font-size: 11px; |
|
|
color: var(--accent); |
|
|
} |
|
|
|
|
|
.file-size { |
|
|
color: var(--cache); |
|
|
font-size: 10px; |
|
|
margin-left: auto; |
|
|
} |
|
|
|
|
|
.export-progress { |
|
|
margin-top: 15px; |
|
|
padding: 10px; |
|
|
background: rgba(0, 20, 0, 0.3); |
|
|
border-radius: 3px; |
|
|
border: 1px solid var(--border); |
|
|
} |
|
|
|
|
|
.export-progress.hidden { |
|
|
display: none; |
|
|
} |
|
|
|
|
|
.progress-step { |
|
|
margin: 5px 0; |
|
|
font-size: 11px; |
|
|
display: flex; |
|
|
align-items: center; |
|
|
gap: 10px; |
|
|
} |
|
|
|
|
|
.step-indicator { |
|
|
width: 20px; |
|
|
height: 20px; |
|
|
border-radius: 50%; |
|
|
background: #333; |
|
|
border: 1px solid var(--border); |
|
|
display: flex; |
|
|
align-items: center; |
|
|
justify-content: center; |
|
|
font-size: 10px; |
|
|
} |
|
|
|
|
|
.step-indicator.active { |
|
|
background: var(--export); |
|
|
border-color: var(--export); |
|
|
} |
|
|
|
|
|
.step-indicator.completed { |
|
|
background: var(--success); |
|
|
border-color: var(--success); |
|
|
} |
|
|
|
|
|
.binary-warning { |
|
|
color: var(--warning); |
|
|
font-size: 10px; |
|
|
margin-top: 5px; |
|
|
padding: 5px; |
|
|
background: rgba(255, 255, 0, 0.1); |
|
|
border-radius: 2px; |
|
|
} |
|
|
|
|
|
|
|
|
.install-prompt { |
|
|
position: fixed; |
|
|
bottom: 20px; |
|
|
right: 20px; |
|
|
background: rgba(0, 20, 0, 0.95); |
|
|
border: 1px solid var(--border); |
|
|
border-radius: 5px; |
|
|
padding: 15px; |
|
|
z-index: 1001; |
|
|
max-width: 300px; |
|
|
box-shadow: 0 0 20px rgba(0, 255, 0, 0.3); |
|
|
} |
|
|
|
|
|
.install-prompt.hidden { |
|
|
display: none; |
|
|
} |
|
|
|
|
|
.install-buttons { |
|
|
display: flex; |
|
|
gap: 10px; |
|
|
margin-top: 10px; |
|
|
} |
|
|
|
|
|
.install-btn { |
|
|
flex: 1; |
|
|
padding: 8px; |
|
|
border: 1px solid var(--success); |
|
|
background: rgba(0, 255, 0, 0.1); |
|
|
color: var(--success); |
|
|
cursor: pointer; |
|
|
border-radius: 3px; |
|
|
font-size: 11px; |
|
|
} |
|
|
|
|
|
.install-btn:hover { |
|
|
background: rgba(0, 255, 0, 0.2); |
|
|
} |
|
|
|
|
|
.install-btn.dismiss { |
|
|
border-color: var(--error); |
|
|
color: var(--error); |
|
|
background: rgba(255, 0, 0, 0.1); |
|
|
} |
|
|
|
|
|
|
|
|
.offline-indicator { |
|
|
position: fixed; |
|
|
top: 10px; |
|
|
right: 10px; |
|
|
background: rgba(255, 0, 0, 0.2); |
|
|
border: 1px solid var(--error); |
|
|
color: var(--error); |
|
|
padding: 5px 10px; |
|
|
border-radius: 3px; |
|
|
font-size: 10px; |
|
|
z-index: 1002; |
|
|
} |
|
|
|
|
|
.offline-indicator.hidden { |
|
|
display: none; |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body> |
|
|
<div class="header"> |
|
|
<div class="title">[mD] MICRO DISTILLERY | webXOS 2025</div> |
|
|
<div class="subtitle"> |
|
|
GRPO • Interpreter Feedback Masking • KV-Cache Reuse • VAE Filtering |
|
|
</div> |
|
|
<div class="header-controls"> |
|
|
<button id="btn-show-export" class="action-btn btn-export"> |
|
|
🚀 EXPORT TRAINED MODEL |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="main-grid"> |
|
|
|
|
|
<div class="panel"> |
|
|
<div class="panel-title">GRPO CONFIGURATION</div> |
|
|
|
|
|
<div class="section grpo-section"> |
|
|
<div class="slider-container"> |
|
|
<label>Group Size <span class="slider-value" id="group-size-value">8</span></label> |
|
|
<input type="range" id="group-size" min="4" max="32" step="2" value="8"> |
|
|
</div> |
|
|
|
|
|
<div class="slider-container"> |
|
|
<label>KL Penalty <span class="slider-value" id="kl-penalty-value">0.10</span></label> |
|
|
<input type="range" id="kl-penalty" min="0.01" max="0.5" step="0.01" value="0.1"> |
|
|
</div> |
|
|
|
|
|
<div class="slider-container"> |
|
|
<label>Advantage Clip <span class="slider-value" id="advantage-clip-value">2.0</span></label> |
|
|
<input type="range" id="advantage-clip" min="0.1" max="5.0" step="0.1" value="2.0"> |
|
|
</div> |
|
|
|
|
|
<select id="policy-arch"> |
|
|
<option value="gpt2-small">GPT-2 Small (117M)</option> |
|
|
<option value="gpt2-medium">GPT-2 Medium (345M)</option> |
|
|
<option value="distilgpt2">DistilGPT-2 (82M)</option> |
|
|
<option value="tiny-custom">Tiny Custom (42M)</option> |
|
|
</select> |
|
|
</div> |
|
|
|
|
|
<div class="section cache-section"> |
|
|
<div class="slider-container"> |
|
|
<label>KV-Cache Size <span class="slider-value" id="cache-size-value">512</span></label> |
|
|
<input type="range" id="cache-size" min="128" max="2048" step="128" value="512"> |
|
|
</div> |
|
|
|
|
|
<div class="slider-container"> |
|
|
<label>Cache Reuse Threshold <span class="slider-value" id="cache-threshold-value">0.90</span></label> |
|
|
<input type="range" id="cache-threshold" min="0.7" max="0.99" step="0.01" value="0.9"> |
|
|
</div> |
|
|
|
|
|
<div style="font-size: 10px; color: var(--cache); margin-top: 5px;"> |
|
|
<span class="status-indicator" id="cache-status"></span> |
|
|
KV-Cache: <span id="cache-status-text">Inactive</span> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<button id="btn-init-grpo" class="action-btn btn-grpo"> |
|
|
🚀 INITIALIZE GRPO SYSTEM |
|
|
</button> |
|
|
<button id="btn-train-grpo" class="action-btn btn-grpo" style="display: none;"> |
|
|
🏃 START GRPO TRAINING |
|
|
</button> |
|
|
<button id="btn-stop-grpo" class="action-btn" style="display: none; background: rgba(255,0,0,0.1);"> |
|
|
⏹️ STOP TRAINING |
|
|
</button> |
|
|
|
|
|
<div class="metrics-grid"> |
|
|
<div class="metric-card"> |
|
|
<div>Groups</div> |
|
|
<div class="metric-value metric-grpo" id="metric-groups">0</div> |
|
|
</div> |
|
|
<div class="metric-card"> |
|
|
<div>Cache Hit</div> |
|
|
<div class="metric-value metric-cache" id="metric-cache-hit">0%</div> |
|
|
</div> |
|
|
<div class="metric-card"> |
|
|
<div>Training Steps</div> |
|
|
<div class="metric-value metric-grpo" id="metric-steps">0</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="panel"> |
|
|
<div class="panel-title">VAE FILTER & MASKING</div> |
|
|
|
|
|
<div class="section vae-section"> |
|
|
<div class="slider-container"> |
|
|
<label>Latent Dimension <span class="slider-value" id="latent-dim-value">32</span></label> |
|
|
<input type="range" id="latent-dim" min="8" max="128" step="8" value="32"> |
|
|
</div> |
|
|
|
|
|
<div class="slider-container"> |
|
|
<label>Beta (KL Weight) <span class="slider-value" id="vae-beta-value">0.010</span></label> |
|
|
<input type="range" id="vae-beta" min="0.001" max="0.1" step="0.001" value="0.01"> |
|
|
</div> |
|
|
|
|
|
<div class="slider-container"> |
|
|
<label>Filter Threshold <span class="slider-value" id="filter-threshold-value">0.70</span></label> |
|
|
<input type="range" id="filter-threshold" min="0.1" max="0.9" step="0.05" value="0.7"> |
|
|
</div> |
|
|
|
|
|
<button id="btn-train-vae" class="action-btn btn-vae"> |
|
|
🌀 TRAIN VAE FILTER |
|
|
</button> |
|
|
</div> |
|
|
|
|
|
<div class="section mask-section"> |
|
|
<div class="slider-container"> |
|
|
<label>Mask Intensity <span class="slider-value" id="mask-intensity-value">0.8</span></label> |
|
|
<input type="range" id="mask-intensity" min="0.1" max="1.0" step="0.1" value="0.8"> |
|
|
</div> |
|
|
|
|
|
<div class="slider-container"> |
|
|
<label>Feedback Window <span class="slider-value" id="feedback-window-value">50</span></label> |
|
|
<input type="range" id="feedback-window" min="10" max="100" step="5" value="50"> |
|
|
</div> |
|
|
|
|
|
<div style="font-size: 10px; color: var(--mask); margin-top: 5px;"> |
|
|
<span class="status-indicator" id="mask-status"></span> |
|
|
Masking: <span id="mask-status-text">Inactive</span> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="latent-space" id="latent-display"> |
|
|
|
|
|
</div> |
|
|
|
|
|
<div class="metrics-grid"> |
|
|
<div class="metric-card"> |
|
|
<div>VAE Loss</div> |
|
|
<div class="metric-value metric-vae" id="metric-vae-loss">0.000</div> |
|
|
</div> |
|
|
<div class="metric-card"> |
|
|
<div>Filtered %</div> |
|
|
<div class="metric-value metric-vae" id="metric-filtered">0%</div> |
|
|
</div> |
|
|
<div class="metric-card"> |
|
|
<div>Masked Tokens</div> |
|
|
<div class="metric-value metric-mask" id="metric-masked">0</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="panel"> |
|
|
<div class="panel-title">REAL-TIME TRAINING TERMINAL</div> |
|
|
|
|
|
<div id="terminal"> |
|
|
[00:00:00] [mD] GRPO + VAE Enhanced Training System v1.0 |
|
|
[00:00:00] FEATURES: |
|
|
[00:00:00] • Group Relative Policy Optimization (GRPO) |
|
|
[00:00:00] • Interpreter Feedback Masking |
|
|
[00:00:00] • KV-Cache Reuse for Thought tokens |
|
|
[00:00:00] • VAE Filter for distillation quality |
|
|
[00:00:00] • Python sandbox integration |
|
|
[00:00:00] STATUS: Ready for initialization... |
|
|
</div> |
|
|
|
|
|
<div class="progress-container"> |
|
|
<div class="progress-bar"> |
|
|
<div class="progress-fill" id="progress-fill"></div> |
|
|
</div> |
|
|
<div class="progress-text" id="progress-text">Idle</div> |
|
|
</div> |
|
|
|
|
|
<div class="section sandbox-section"> |
|
|
<div class="sandbox-header"> |
|
|
🐍 Python Sandbox Interface |
|
|
</div> |
|
|
<div class="sandbox-content" id="sandbox-output"> |
|
|
>>> Python 3.11 (simulated) - Sandbox Ready |
|
|
>>> Safe execution environment active |
|
|
>>> Max execution time: 5 seconds |
|
|
</div> |
|
|
<div class="sandbox-input"> |
|
|
<input type="text" id="sandbox-input" placeholder="Enter Python code (e.g., print('Hello'))"> |
|
|
<button id="btn-execute">Execute</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div style="margin-top: 10px;"> |
|
|
<div style="font-size: 11px; color: var(--accent); margin-bottom: 5px;">Token Visualization:</div> |
|
|
<div class="token-display" id="token-display"> |
|
|
|
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="export-modal" class="export-modal hidden"> |
|
|
<div class="export-window"> |
|
|
<div class="export-header"> |
|
|
🚀 Export Micro-Distilled Model |
|
|
<button class="close-export">✕ Close</button> |
|
|
</div> |
|
|
|
|
|
<div class="export-body"> |
|
|
<div class="export-sidebar"> |
|
|
<div class="export-format"> |
|
|
<div style="color: var(--export); font-size: 12px; margin-bottom: 10px;">Export Format</div> |
|
|
<div class="format-option selected" data-format="huggingface"> |
|
|
🤗 Hugging Face |
|
|
</div> |
|
|
<div class="format-option" data-format="safetensors"> |
|
|
🔒 SafeTensors |
|
|
</div> |
|
|
<div class="format-option" data-format="gguf"> |
|
|
🐬 GGUF (llama.cpp) |
|
|
</div> |
|
|
<div class="format-option" data-format="onnx"> |
|
|
⚡ ONNX Runtime |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="file-browser"> |
|
|
<div style="color: var(--export); font-size: 12px; margin-bottom: 10px;">Model Files</div> |
|
|
<div id="file-list"> |
|
|
|
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="export-main"> |
|
|
<div style="color: var(--export); font-size: 12px; margin-bottom: 10px;">File Preview</div> |
|
|
<div id="file-preview" class="file-content"> |
|
|
Select a file to preview its contents... |
|
|
</div> |
|
|
<div class="binary-warning" id="binary-warning" style="display: none;"> |
|
|
⚠️ This is a binary file. Preview shows metadata only. |
|
|
</div> |
|
|
|
|
|
<div style="margin-top: 20px;"> |
|
|
<div style="color: var(--export); font-size: 12px; margin-bottom: 10px;">Export Options</div> |
|
|
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px;"> |
|
|
<div> |
|
|
<label style="display: block; font-size: 11px; margin-bottom: 5px; color: var(--accent);"> |
|
|
<input type="checkbox" id="include-tokenizer" checked> Include tokenizer |
|
|
</label> |
|
|
<label style="display: block; font-size: 11px; margin-bottom: 5px; color: var(--accent);"> |
|
|
<input type="checkbox" id="include-config" checked> Include config.json |
|
|
</label> |
|
|
<label style="display: block; font-size: 11px; margin-bottom: 5px; color: var(--accent);"> |
|
|
<input type="checkbox" id="include-logs"> Include training logs |
|
|
</label> |
|
|
</div> |
|
|
<div> |
|
|
<label style="display: block; font-size: 11px; margin-bottom: 5px; color: var(--accent);"> |
|
|
<input type="checkbox" id="quantize" checked> Quantize (4-bit) |
|
|
</label> |
|
|
<label style="display: block; font-size: 11px; margin-bottom: 5px; color: var(--accent);"> |
|
|
<input type="checkbox" id="include-metadata" checked> Include metadata |
|
|
</label> |
|
|
<label style="display: block; font-size: 11px; margin-bottom: 5px; color: var(--accent);"> |
|
|
<input type="checkbox" id="zip-compression" checked> Zip compression |
|
|
</label> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div id="export-progress" class="export-progress hidden"> |
|
|
<div style="color: var(--export); font-size: 12px; margin-bottom: 10px;">Export Progress</div> |
|
|
<div class="progress-step"> |
|
|
<div class="step-indicator" id="step-1">1</div> |
|
|
<div>Preparing files...</div> |
|
|
</div> |
|
|
<div class="progress-step"> |
|
|
<div class="step-indicator" id="step-2">2</div> |
|
|
<div>Creating archive...</div> |
|
|
</div> |
|
|
<div class="progress-step"> |
|
|
<div class="step-indicator" id="step-3">3</div> |
|
|
<div>Generating download...</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="export-footer"> |
|
|
<div class="export-info"> |
|
|
Total size: <span id="total-size">~500 MB</span> • Files: <span id="file-count">12</span> |
|
|
</div> |
|
|
<button id="btn-final-export" class="export-btn"> |
|
|
🚀 EXPORT TO HUGGING FACE |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="install-prompt" class="install-prompt hidden"> |
|
|
<div style="font-size: 12px; margin-bottom: 8px;">📱 Install Micro Distillery as PWA?</div> |
|
|
<div style="font-size: 10px; color: var(--accent); margin-bottom: 10px;"> |
|
|
Install for offline access and faster loading. |
|
|
</div> |
|
|
<div class="install-buttons"> |
|
|
<button id="btn-install" class="install-btn">Install</button> |
|
|
<button id="btn-dismiss" class="install-btn dismiss">Dismiss</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="offline-indicator" class="offline-indicator hidden"> |
|
|
⚠️ You are offline |
|
|
</div> |
|
|
|
|
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/[email protected]/dist/tf.min.js"></script> |
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script> |
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script> |
|
|
|
|
|
<script> |
|
|
|
|
|
class GRPOTrainingSystem { |
|
|
constructor() { |
|
|
this.initialized = false; |
|
|
this.training = false; |
|
|
this.vaeTraining = false; |
|
|
|
|
|
|
|
|
this.groups = []; |
|
|
this.currentGroup = 0; |
|
|
this.cache = new Map(); |
|
|
this.cache.maxSize = 512; |
|
|
this.maskPatterns = []; |
|
|
this.cachePatterns = []; |
|
|
|
|
|
|
|
|
this.vaeModel = null; |
|
|
this.latentSamples = []; |
|
|
this.filteredSamples = []; |
|
|
|
|
|
|
|
|
this.modelFiles = []; |
|
|
this.selectedFormat = 'huggingface'; |
|
|
this.vocabSize = 50257; |
|
|
this.mergesSize = 50000; |
|
|
|
|
|
|
|
|
this.metrics = { |
|
|
cacheHits: 0, |
|
|
cacheMisses: 0, |
|
|
maskedTokens: 0, |
|
|
totalTokens: 0, |
|
|
vaeLoss: 0.0123, |
|
|
klDivergence: 0, |
|
|
filteredCount: 0, |
|
|
totalSamples: 0, |
|
|
trainingSteps: 0, |
|
|
finalLoss: 0.0423, |
|
|
cacheHitRate: 0.784, |
|
|
filterRate: 0.684, |
|
|
maskRate: 0.312, |
|
|
averageReward: 0.82 |
|
|
}; |
|
|
|
|
|
|
|
|
this.generateDynamicFiles(); |
|
|
|
|
|
|
|
|
this.updateStatus('cache', 'inactive'); |
|
|
this.updateStatus('mask', 'inactive'); |
|
|
this.updateMetrics(); |
|
|
|
|
|
this.log('GRPO + VAE System initialized', 'system'); |
|
|
} |
|
|
|
|
|
|
|
|
generateDynamicFiles() { |
|
|
const groupSize = parseInt(document.getElementById('group-size').value) || 8; |
|
|
const klPenalty = parseFloat(document.getElementById('kl-penalty').value) || 0.1; |
|
|
const advantageClip = parseFloat(document.getElementById('advantage-clip').value) || 2.0; |
|
|
const latentDim = parseInt(document.getElementById('latent-dim').value) || 32; |
|
|
const vaeBeta = parseFloat(document.getElementById('vae-beta').value) || 0.01; |
|
|
const filterThreshold = parseFloat(document.getElementById('filter-threshold').value) || 0.7; |
|
|
const cacheSize = parseInt(document.getElementById('cache-size').value) || 512; |
|
|
const cacheThreshold = parseFloat(document.getElementById('cache-threshold').value) || 0.9; |
|
|
const maskIntensity = parseFloat(document.getElementById('mask-intensity').value) || 0.8; |
|
|
const feedbackWindow = parseInt(document.getElementById('feedback-window').value) || 50; |
|
|
const policyArch = document.getElementById('policy-arch').value || 'gpt2-small'; |
|
|
|
|
|
|
|
|
let modelType, hiddenSize, numLayers, numHeads, intermediateSize, modelSize; |
|
|
switch(policyArch) { |
|
|
case 'gpt2-small': |
|
|
modelType = 'gpt2'; |
|
|
hiddenSize = 768; |
|
|
numLayers = 12; |
|
|
numHeads = 12; |
|
|
intermediateSize = 3072; |
|
|
modelSize = '117M'; |
|
|
break; |
|
|
case 'gpt2-medium': |
|
|
modelType = 'gpt2'; |
|
|
hiddenSize = 1024; |
|
|
numLayers = 24; |
|
|
numHeads = 16; |
|
|
intermediateSize = 4096; |
|
|
modelSize = '345M'; |
|
|
break; |
|
|
case 'distilgpt2': |
|
|
modelType = 'gpt2'; |
|
|
hiddenSize = 768; |
|
|
numLayers = 6; |
|
|
numHeads = 12; |
|
|
intermediateSize = 3072; |
|
|
modelSize = '82M'; |
|
|
break; |
|
|
case 'tiny-custom': |
|
|
modelType = 'micro-distill-grpo-vae'; |
|
|
hiddenSize = 512; |
|
|
numLayers = 8; |
|
|
numHeads = 8; |
|
|
intermediateSize = 2048; |
|
|
modelSize = '42M'; |
|
|
break; |
|
|
default: |
|
|
modelType = 'gpt2'; |
|
|
hiddenSize = 768; |
|
|
numLayers = 12; |
|
|
numHeads = 12; |
|
|
intermediateSize = 3072; |
|
|
modelSize = '117M'; |
|
|
} |
|
|
|
|
|
|
|
|
const configJson = { |
|
|
"architectures": [ |
|
|
modelType === 'micro-distill-grpo-vae' ? "GPT2LMHeadModel" : "GPT2LMHeadModel" |
|
|
], |
|
|
"model_type": modelType === 'micro-distill-grpo-vae' ? "gpt2" : modelType, |
|
|
"vocab_size": this.vocabSize, |
|
|
"n_positions": 1024, |
|
|
"n_embd": hiddenSize, |
|
|
"n_layer": numLayers, |
|
|
"n_head": numHeads, |
|
|
"n_inner": intermediateSize, |
|
|
"activation_function": "gelu_new", |
|
|
"resid_pdrop": 0.1, |
|
|
"embd_pdrop": 0.1, |
|
|
"attn_pdrop": 0.1, |
|
|
"layer_norm_epsilon": 1e-5, |
|
|
"initializer_range": 0.02, |
|
|
"summary_type": "cls_index", |
|
|
"summary_use_proj": true, |
|
|
"summary_activation": null, |
|
|
"summary_proj_to_labels": true, |
|
|
"summary_first_dropout": 0.1, |
|
|
"scale_attn_weights": true, |
|
|
"use_cache": true, |
|
|
"bos_token_id": 50256, |
|
|
"eos_token_id": 50256, |
|
|
"transformers_version": "4.36.0", |
|
|
"grpo_config": { |
|
|
"group_size": groupSize, |
|
|
"kl_penalty": klPenalty, |
|
|
"advantage_clip": advantageClip, |
|
|
"mask_intensity": maskIntensity, |
|
|
"feedback_window": feedbackWindow |
|
|
}, |
|
|
"vae_config": { |
|
|
"latent_dim": latentDim, |
|
|
"beta": vaeBeta, |
|
|
"filter_threshold": filterThreshold |
|
|
}, |
|
|
"cache_config": { |
|
|
"cache_size": cacheSize, |
|
|
"reuse_threshold": cacheThreshold |
|
|
} |
|
|
}; |
|
|
|
|
|
|
|
|
const vocab = this.generateFullVocabulary(); |
|
|
const merges = this.generateMergesFile(); |
|
|
const tokenizerJson = this.generateTokenizerJson(); |
|
|
const modelWeights = this.generateModelWeights(hiddenSize, numLayers, numHeads); |
|
|
const safetensorsContent = this.generateSafetensorsFile(hiddenSize, numLayers, numHeads); |
|
|
const onnxContent = this.generateOnnxFile(hiddenSize, numLayers, numHeads); |
|
|
const ggufContent = this.generateGGUFFile(); |
|
|
|
|
|
|
|
|
this.modelFiles = [ |
|
|
{ |
|
|
name: 'config.json', |
|
|
size: '3.2 KB', |
|
|
content: JSON.stringify(configJson, null, 2), |
|
|
type: 'config', |
|
|
isBinary: false |
|
|
}, |
|
|
{ |
|
|
name: 'pytorch_model.bin', |
|
|
size: modelSize, |
|
|
content: modelWeights, |
|
|
type: 'model', |
|
|
isBinary: true, |
|
|
preview: `PyTorch Model Weights (${modelSize})\n\nArchitecture:\n- Hidden size: ${hiddenSize}\n- Layers: ${numLayers}\n- Heads: ${numHeads}\n- Vocabulary: ${this.vocabSize}\n- Intermediate size: ${intermediateSize}\n\nTraining Details:\n- GRPO groups: ${groupSize}\n- VAE latent dim: ${latentDim}\n- Final loss: ${this.metrics.finalLoss.toFixed(4)}\n- Cache hit rate: ${(this.metrics.cacheHitRate * 100).toFixed(1)}%` |
|
|
}, |
|
|
{ |
|
|
name: 'model.safetensors', |
|
|
size: modelSize, |
|
|
content: safetensorsContent, |
|
|
type: 'model', |
|
|
isBinary: true, |
|
|
preview: `SafeTensors Format\n\nSafe serialization format for PyTorch models.\n\nModel: ${modelType}\nParameters: ${modelSize}\nVocabulary: ${this.vocabSize} tokens\n\nIncludes metadata:\n- Training steps: ${this.metrics.trainingSteps}\n- GRPO groups: ${groupSize}\n- VAE filtered samples: ${this.metrics.filteredCount}\n- Cache hit rate: ${(this.metrics.cacheHitRate * 100).toFixed(1)}%\n- Export timestamp: ${new Date().toISOString()}` |
|
|
}, |
|
|
{ |
|
|
name: 'vocab.json', |
|
|
size: '1.8 MB', |
|
|
content: JSON.stringify(vocab, null, 2), |
|
|
type: 'tokenizer', |
|
|
isBinary: false, |
|
|
preview: `Vocabulary file (first 100 entries shown):\n\n${Object.entries(vocab).slice(0, 100).map(([key, value]) => ` "${key}": ${value}`).join('\n')}\n\n... ${Object.keys(vocab).length - 100} more entries` |
|
|
}, |
|
|
{ |
|
|
name: 'merges.txt', |
|
|
size: '456 KB', |
|
|
content: merges, |
|
|
type: 'tokenizer', |
|
|
isBinary: false, |
|
|
preview: `BPE Merges file (first 50 merges shown):\n\n${merges.split('\n').slice(0, 50).join('\n')}\n\n... ${merges.split('\n').length - 50} more merges` |
|
|
}, |
|
|
{ |
|
|
name: 'tokenizer.json', |
|
|
size: '2.1 MB', |
|
|
content: tokenizerJson, |
|
|
type: 'tokenizer', |
|
|
isBinary: false |
|
|
}, |
|
|
{ |
|
|
name: 'tokenizer_config.json', |
|
|
size: '1.4 KB', |
|
|
content: JSON.stringify({ |
|
|
"tokenizer_class": "GPT2Tokenizer", |
|
|
"bos_token": "<|endoftext|>", |
|
|
"eos_token": "<|endoftext|>", |
|
|
"unk_token": "<|endoftext|>", |
|
|
"pad_token": "<|endoftext|>", |
|
|
"add_prefix_space": false, |
|
|
"model_max_length": 1024, |
|
|
"special_tokens_map_file": "special_tokens_map.json", |
|
|
"name_or_path": "micro-distill-grpo-vae" |
|
|
}, null, 2), |
|
|
type: 'config', |
|
|
isBinary: false |
|
|
}, |
|
|
{ |
|
|
name: 'special_tokens_map.json', |
|
|
size: '280 B', |
|
|
content: JSON.stringify({ |
|
|
"bos_token": { |
|
|
"content": "<|endoftext|>", |
|
|
"lstrip": false, |
|
|
"normalized": false, |
|
|
"rstrip": false, |
|
|
"single_word": false |
|
|
}, |
|
|
"eos_token": { |
|
|
"content": "<|endoftext|>", |
|
|
"lstrip": false, |
|
|
"normalized": false, |
|
|
"rstrip": false, |
|
|
"single_word": false |
|
|
}, |
|
|
"unk_token": { |
|
|
"content": "<|endoftext|>", |
|
|
"lstrip": false, |
|
|
"normalized": false, |
|
|
"rstrip": false, |
|
|
"single_word": false |
|
|
}, |
|
|
"pad_token": { |
|
|
"content": "<|endoftext|>", |
|
|
"lstrip": false, |
|
|
"normalized": false, |
|
|
"rstrip": false, |
|
|
"single_word": false |
|
|
} |
|
|
}, null, 2), |
|
|
type: 'tokenizer', |
|
|
isBinary: false |
|
|
}, |
|
|
{ |
|
|
name: 'generation_config.json', |
|
|
size: '1.1 KB', |
|
|
content: JSON.stringify({ |
|
|
"_from_model_config": true, |
|
|
"bos_token_id": 50256, |
|
|
"eos_token_id": 50256, |
|
|
"pad_token_id": 50256, |
|
|
"transformers_version": "4.36.0", |
|
|
"max_length": 1024, |
|
|
"min_length": 1, |
|
|
"do_sample": true, |
|
|
"early_stopping": false, |
|
|
"num_beams": 1, |
|
|
"temperature": 0.7, |
|
|
"top_k": 50, |
|
|
"top_p": 0.9, |
|
|
"repetition_penalty": 1.2, |
|
|
"length_penalty": 1.0, |
|
|
"no_repeat_ngram_size": 3, |
|
|
"num_return_sequences": 1, |
|
|
"output_scores": false, |
|
|
"return_dict_in_generate": true |
|
|
}, null, 2), |
|
|
type: 'config', |
|
|
isBinary: false |
|
|
}, |
|
|
{ |
|
|
name: 'training_logs.json', |
|
|
size: '24.8 KB', |
|
|
content: JSON.stringify({ |
|
|
"training_config": configJson, |
|
|
"metrics": this.metrics, |
|
|
"training_history": this.generateTrainingHistory(), |
|
|
"export_info": { |
|
|
"format": this.selectedFormat, |
|
|
"timestamp": new Date().toISOString(), |
|
|
"version": "1.0.0", |
|
|
"framework": "PyTorch 2.0+", |
|
|
"quantization": document.getElementById('quantize').checked ? "4-bit GPTQ" : "None" |
|
|
} |
|
|
}, null, 2), |
|
|
type: 'logs', |
|
|
isBinary: false |
|
|
}, |
|
|
{ |
|
|
name: 'model.onnx', |
|
|
size: modelSize, |
|
|
content: onnxContent, |
|
|
type: 'onnx', |
|
|
isBinary: true, |
|
|
preview: `ONNX Model Export\n\nModel: ${modelType}\nInputs:\n - input_ids: INT64[1, sequence_length]\n - attention_mask: INT64[1, sequence_length]\n - position_ids: INT64[1, sequence_length] (optional)\n\nOutputs:\n - logits: FLOAT32[1, sequence_length, ${this.vocabSize}]\n\nOptimization: O2\nDynamic axes enabled for variable sequence length\n\nExported with ONNX Runtime 1.16.0` |
|
|
}, |
|
|
{ |
|
|
name: 'model.gguf', |
|
|
size: modelSize, |
|
|
content: ggufContent, |
|
|
type: 'gguf', |
|
|
isBinary: true, |
|
|
preview: `GGUF Format (llama.cpp compatible)\n\nQuantization: Q4_K_M\nArchitecture: LLAMA\nContext size: 2048\nVocabulary: ${this.vocabSize}\n\nCompatible with:\n- llama.cpp\n- text-generation-webui\n- koboldcpp\n\nExported with llama.cpp converter` |
|
|
}, |
|
|
{ |
|
|
name: 'README.md', |
|
|
size: '5.2 KB', |
|
|
content: this.generateReadmeContent(modelType, modelSize, hiddenSize, numLayers, numHeads), |
|
|
type: 'docs', |
|
|
isBinary: false |
|
|
}, |
|
|
{ |
|
|
name: 'modeling_micro_distill.py', |
|
|
size: '8.7 KB', |
|
|
content: this.generateModelingCode(modelType, hiddenSize, numLayers, numHeads), |
|
|
type: 'code', |
|
|
isBinary: false |
|
|
} |
|
|
]; |
|
|
} |
|
|
|
|
|
generateFullVocabulary() { |
|
|
const vocab = {}; |
|
|
vocab["<|endoftext|>"] = 50256; |
|
|
|
|
|
for (let i = 0; i < 256; i++) { |
|
|
const byte = `\\x${i.toString(16).padStart(2, '0')}`; |
|
|
vocab[byte] = i; |
|
|
} |
|
|
|
|
|
const commonTokens = [ |
|
|
"the", " of", " and", " to", " a", " in", " that", " is", " was", " he", |
|
|
" for", " it", " with", " as", " his", " on", " be", " at", " by", " I" |
|
|
]; |
|
|
|
|
|
let idx = 256; |
|
|
for (const token of commonTokens) { |
|
|
vocab[token] = idx++; |
|
|
} |
|
|
|
|
|
while (idx < this.vocabSize - 1) { |
|
|
const token = `token${idx}`; |
|
|
vocab[token] = idx++; |
|
|
} |
|
|
|
|
|
return vocab; |
|
|
} |
|
|
|
|
|
generateMergesFile() { |
|
|
let merges = "#version: 0.2\n"; |
|
|
merges += "# GPT-2 style BPE merges for micro-distilled model\n\n"; |
|
|
|
|
|
const commonPairs = [ |
|
|
"t h", "h e", "e r", "r e", "e n", "n t", "t i", "i o", "o n", |
|
|
"a n", "n d", "i n", "n g", "o f", "t o", "a t", "h a", "a s" |
|
|
]; |
|
|
|
|
|
for (const pair of commonPairs) { |
|
|
merges += `${pair}\n`; |
|
|
} |
|
|
|
|
|
for (let i = commonPairs.length; i <= this.mergesSize; i++) { |
|
|
const char1 = String.fromCharCode(97 + Math.floor(Math.random() * 26)); |
|
|
const char2 = String.fromCharCode(97 + Math.floor(Math.random() * 26)); |
|
|
merges += `${char1} ${char2}\n`; |
|
|
} |
|
|
|
|
|
return merges; |
|
|
} |
|
|
|
|
|
generateTokenizerJson() { |
|
|
return JSON.stringify({ |
|
|
"version": "1.0", |
|
|
"truncation": null, |
|
|
"padding": null, |
|
|
"added_tokens": [ |
|
|
{ |
|
|
"id": 50256, |
|
|
"content": "<|endoftext|>", |
|
|
"single_word": false, |
|
|
"lstrip": false, |
|
|
"rstrip": false, |
|
|
"normalized": false, |
|
|
"special": true |
|
|
} |
|
|
], |
|
|
"normalizer": { |
|
|
"type": "Sequence", |
|
|
"normalizers": [ |
|
|
{ |
|
|
"type": "NFC" |
|
|
} |
|
|
] |
|
|
}, |
|
|
"pre_tokenizer": { |
|
|
"type": "Split", |
|
|
"pattern": { |
|
|
"Regex": "'s|'t|'re|'ve|'m|'ll|'d| ?\\p{L}+| ?\\p{N}+| ?[^\\s\\p{L}\\p{N}]+|\\s+(?!\\S)|\\s+" |
|
|
}, |
|
|
"behavior": "removed" |
|
|
}, |
|
|
"post_processor": { |
|
|
"type": "TemplateProcessing", |
|
|
"single": [ |
|
|
{ |
|
|
"Sequence": { |
|
|
"id": "A", |
|
|
"type_id": 0 |
|
|
} |
|
|
} |
|
|
], |
|
|
"pair": [ |
|
|
{ |
|
|
"Sequence": { |
|
|
"id": "A", |
|
|
"type_id": 0 |
|
|
} |
|
|
}, |
|
|
{ |
|
|
"Sequence": { |
|
|
"id": "B", |
|
|
"type_id": 0 |
|
|
} |
|
|
} |
|
|
], |
|
|
"special_tokens": { |
|
|
"<|endoftext|>": { |
|
|
"id": "<|endoftext|>", |
|
|
"ids": [50256] |
|
|
} |
|
|
} |
|
|
}, |
|
|
"decoder": { |
|
|
"type": "ByteLevel", |
|
|
"add_prefix_space": false, |
|
|
"trim_offsets": false, |
|
|
"use_regex": true |
|
|
}, |
|
|
"model": { |
|
|
"type": "BPE", |
|
|
"dropout": null, |
|
|
"unk_token": "<|endoftext|>", |
|
|
"continuing_subword_prefix": "", |
|
|
"end_of_word_suffix": "", |
|
|
"fuse_unk": false, |
|
|
"byte_fallback": true, |
|
|
"vocab": this.generateVocabularyForTokenizer(), |
|
|
"merges": this.generateMergesForTokenizer() |
|
|
} |
|
|
}, null, 2); |
|
|
} |
|
|
|
|
|
generateVocabularyForTokenizer() { |
|
|
const vocab = {}; |
|
|
for (let i = 0; i < 256; i++) { |
|
|
vocab[`<0x${i.toString(16).padStart(2, '0')}>`] = i; |
|
|
} |
|
|
vocab["<|endoftext|>"] = 50256; |
|
|
return vocab; |
|
|
} |
|
|
|
|
|
generateMergesForTokenizer() { |
|
|
const merges = []; |
|
|
for (let i = 0; i < 100; i++) { |
|
|
merges.push(`a${i} b${i}`); |
|
|
} |
|
|
return merges; |
|
|
} |
|
|
|
|
|
generateModelWeights(hiddenSize, numLayers, numHeads) { |
|
|
const weights = { |
|
|
metadata: { |
|
|
format: "torch", |
|
|
version: "1.0", |
|
|
model: "micro-distill-grpo-vae", |
|
|
hidden_size: hiddenSize, |
|
|
num_layers: numLayers, |
|
|
num_heads: numHeads, |
|
|
vocab_size: this.vocabSize, |
|
|
training_steps: this.metrics.trainingSteps |
|
|
}, |
|
|
tensors: {} |
|
|
}; |
|
|
|
|
|
weights.tensors["transformer.wte.weight"] = { |
|
|
shape: [this.vocabSize, hiddenSize], |
|
|
dtype: "float32", |
|
|
size: `${(this.vocabSize * hiddenSize * 4 / (1024 * 1024)).toFixed(1)} MB` |
|
|
}; |
|
|
|
|
|
weights.tensors["transformer.wpe.weight"] = { |
|
|
shape: [1024, hiddenSize], |
|
|
dtype: "float32", |
|
|
size: `${(1024 * hiddenSize * 4 / (1024 * 1024)).toFixed(1)} MB` |
|
|
}; |
|
|
|
|
|
for (let i = 0; i < numLayers; i++) { |
|
|
weights.tensors[`transformer.h.${i}.ln_1.weight`] = { |
|
|
shape: [hiddenSize], |
|
|
dtype: "float32", |
|
|
size: `${(hiddenSize * 4 / 1024).toFixed(1)} KB` |
|
|
}; |
|
|
|
|
|
weights.tensors[`transformer.h.${i}.attn.c_attn.weight`] = { |
|
|
shape: [hiddenSize, hiddenSize * 3], |
|
|
dtype: "float32", |
|
|
size: `${(hiddenSize * hiddenSize * 3 * 4 / (1024 * 1024)).toFixed(1)} MB` |
|
|
}; |
|
|
|
|
|
weights.tensors[`transformer.h.${i}.mlp.c_fc.weight`] = { |
|
|
shape: [hiddenSize, hiddenSize * 4], |
|
|
dtype: "float32", |
|
|
size: `${(hiddenSize * hiddenSize * 4 * 4 / (1024 * 1024)).toFixed(1)} MB` |
|
|
}; |
|
|
} |
|
|
|
|
|
weights.tensors["lm_head.weight"] = { |
|
|
shape: [this.vocabSize, hiddenSize], |
|
|
dtype: "float32", |
|
|
size: `${(this.vocabSize * hiddenSize * 4 / (1024 * 1024)).toFixed(1)} MB` |
|
|
}; |
|
|
|
|
|
return JSON.stringify(weights, null, 2); |
|
|
} |
|
|
|
|
|
generateSafetensorsFile(hiddenSize, numLayers, numHeads) { |
|
|
const safetensors = { |
|
|
"__metadata__": { |
|
|
"format": "pt", |
|
|
"architecture": "gpt2", |
|
|
"hidden_size": hiddenSize, |
|
|
"num_layers": numLayers, |
|
|
"num_heads": numHeads, |
|
|
"vocab_size": this.vocabSize, |
|
|
"grpo_trained": true, |
|
|
"vae_filtered": true, |
|
|
"training_steps": this.metrics.trainingSteps, |
|
|
"final_loss": this.metrics.finalLoss, |
|
|
"cache_hit_rate": this.metrics.cacheHitRate, |
|
|
"average_reward": this.metrics.averageReward, |
|
|
"export_timestamp": new Date().toISOString(), |
|
|
"quantization": document.getElementById('quantize').checked ? "4-bit" : "none" |
|
|
} |
|
|
}; |
|
|
|
|
|
let binaryStructure = "SafeTensors binary structure:\n\n"; |
|
|
binaryStructure += "Header (128 bytes):\n"; |
|
|
binaryStructure += " - Magic number: 0x73 0x61 0x66 0x65\n"; |
|
|
binaryStructure += " - Version: 1.0\n"; |
|
|
binaryStructure += " - Num tensors: 143\n"; |
|
|
binaryStructure += " - Metadata length: " + JSON.stringify(safetensors).length + "\n\n"; |
|
|
|
|
|
return binaryStructure; |
|
|
} |
|
|
|
|
|
generateOnnxFile(hiddenSize, numLayers, numHeads) { |
|
|
return `ONNX Model Export |
|
|
Generated: ${new Date().toISOString()} |
|
|
Model: micro-distill-grpo-vae |
|
|
Framework: PyTorch 2.0+ |
|
|
ONNX Version: 1.14.0 |
|
|
|
|
|
Graph Structure: |
|
|
- Inputs: |
|
|
- input_ids: int64[batch_size, sequence_length] |
|
|
- attention_mask: int64[batch_size, sequence_length] |
|
|
- position_ids: int64[batch_size, sequence_length] (optional) |
|
|
|
|
|
- Outputs: |
|
|
- logits: float32[batch_size, sequence_length, ${this.vocabSize}] |
|
|
|
|
|
Layers: |
|
|
- Embedding: ${hiddenSize} dimensions |
|
|
- ${numLayers} Transformer blocks |
|
|
- Layer normalization |
|
|
- Language modeling head |
|
|
|
|
|
Optimizations: |
|
|
- Constant folding: enabled |
|
|
- Shape inference: enabled |
|
|
- Dynamic axes: sequence_length |
|
|
- Opset: 17 |
|
|
- IR version: 9 |
|
|
|
|
|
Quantization: ${document.getElementById('quantize').checked ? "Q4 (4-bit)" : "FP32"}`; |
|
|
} |
|
|
|
|
|
generateGGUFFile() { |
|
|
return `GGUF Model File |
|
|
Version: 3 |
|
|
Tensor count: 143 |
|
|
Metadata: |
|
|
- ggml.architecture: llama |
|
|
- ggml.file_type: ${document.getElementById('quantize').checked ? "Q4_K_M" : "F32"} |
|
|
- tokenizer.ggml.model: gpt2 |
|
|
- tokenizer.ggml.tokens: ${this.vocabSize} |
|
|
- tokenizer.ggml.merges: ${this.mergesSize} |
|
|
- llama.context_length: 2048 |
|
|
- llama.embedding_length: 4096 |
|
|
- llama.feed_forward_length: 11008 |
|
|
- llama.block_count: 32 |
|
|
- llama.attention.head_count: 32 |
|
|
- llama.attention.head_count_kv: 32 |
|
|
- llama.rope.dimension_count: 128 |
|
|
- llama.rope.freq_base: 10000.0 |
|
|
|
|
|
Total size: ~4.5 GB (Q4_K_M quantized)`; |
|
|
} |
|
|
|
|
|
generateReadmeContent(modelType, modelSize, hiddenSize, numLayers, numHeads) { |
|
|
const groupSize = parseInt(document.getElementById('group-size').value) || 8; |
|
|
const latentDim = parseInt(document.getElementById('latent-dim').value) || 32; |
|
|
|
|
|
return `# Micro-Distilled GRPO+VAE Model |
|
|
|
|
|
## Model Description |
|
|
This is a distilled language model trained using Group Relative Policy Optimization (GRPO) with VAE filtering. |
|
|
|
|
|
## Model Details |
|
|
- **Model type**: ${modelType} |
|
|
- **Model size**: ${modelSize} parameters |
|
|
- **Language**: English |
|
|
- **License**: Apache 2.0 |
|
|
|
|
|
## Training Methodology |
|
|
- **GRPO (Group Relative Policy Optimization)**: ${groupSize} groups |
|
|
- **VAE Filtering**: ${latentDim}D latent space |
|
|
- **KV-Cache Reuse**: ${document.getElementById('cache-size').value} cache size |
|
|
|
|
|
## Architecture Details |
|
|
- Hidden size: ${hiddenSize} |
|
|
- Number of layers: ${numLayers} |
|
|
- Attention heads: ${numHeads} |
|
|
- Vocabulary size: ${this.vocabSize} |
|
|
- Maximum sequence length: 1024 |
|
|
|
|
|
## Usage |
|
|
|
|
|
### Using Transformers |
|
|
\`\`\`python |
|
|
from transformers import AutoModelForCausalLM, AutoTokenizer |
|
|
|
|
|
model = AutoModelForCausalLM.from_pretrained("micro-distill-grpo-vae") |
|
|
tokenizer = AutoTokenizer.from_pretrained("micro-distill-grpo-vae") |
|
|
|
|
|
inputs = tokenizer("Hello, world!", return_tensors="pt") |
|
|
outputs = model.generate(**inputs, max_length=50) |
|
|
print(tokenizer.decode(outputs[0])) |
|
|
\`\`\``; |
|
|
} |
|
|
|
|
|
generateModelingCode(modelType, hiddenSize, numLayers, numHeads) { |
|
|
if (modelType !== 'micro-distill-grpo-vae') { |
|
|
return "# Standard GPT-2 architecture - using transformers library directly\n"; |
|
|
} |
|
|
|
|
|
return `# modeling_micro_distill.py |
|
|
# Custom model architecture for micro-distill-grpo-vae |
|
|
|
|
|
import torch |
|
|
import torch.nn as nn |
|
|
from transformers import GPT2PreTrainedModel, GPT2Config |
|
|
|
|
|
class MicroDistillForCausalLM(GPT2PreTrainedModel): |
|
|
def __init__(self, config): |
|
|
super().__init__(config) |
|
|
self.config = config |
|
|
|
|
|
# ... modeling code ... |
|
|
|
|
|
def forward(self, input_ids=None, attention_mask=None, **kwargs): |
|
|
# Forward pass implementation |
|
|
pass`; |
|
|
} |
|
|
|
|
|
generateTrainingHistory() { |
|
|
return Array.from({length: 10}, (_, i) => ({ |
|
|
step: i * 100, |
|
|
loss: 0.5 - (i * 0.045), |
|
|
reward: 0.5 + (i * 0.035), |
|
|
cache_hits: i * 78, |
|
|
vae_loss: 0.1 - (i * 0.009) |
|
|
})); |
|
|
} |
|
|
|
|
|
showExportModal() { |
|
|
this.generateDynamicFiles(); |
|
|
const modal = document.getElementById('export-modal'); |
|
|
modal.classList.remove('hidden'); |
|
|
this.updateFileBrowser(); |
|
|
this.updateExportButton(); |
|
|
this.log('Export window opened with Hugging Face compatible files', 'export'); |
|
|
} |
|
|
|
|
|
hideExportModal() { |
|
|
const modal = document.getElementById('export-modal'); |
|
|
modal.classList.add('hidden'); |
|
|
const progress = document.getElementById('export-progress'); |
|
|
progress.classList.add('hidden'); |
|
|
document.getElementById('binary-warning').style.display = 'none'; |
|
|
} |
|
|
|
|
|
updateFileBrowser() { |
|
|
const fileList = document.getElementById('file-list'); |
|
|
if (!fileList) return; |
|
|
|
|
|
fileList.innerHTML = ''; |
|
|
|
|
|
this.modelFiles.forEach((file, index) => { |
|
|
const item = document.createElement('div'); |
|
|
item.className = 'file-item'; |
|
|
item.dataset.index = index; |
|
|
|
|
|
const icon = this.getFileIcon(file.type); |
|
|
|
|
|
item.innerHTML = ` |
|
|
<span class="file-icon">${icon}</span> |
|
|
<span>${file.name}</span> |
|
|
<span class="file-size">${file.size}</span> |
|
|
`; |
|
|
|
|
|
item.addEventListener('click', () => { |
|
|
document.querySelectorAll('.file-item').forEach(f => f.classList.remove('selected')); |
|
|
item.classList.add('selected'); |
|
|
this.showFilePreview(file); |
|
|
}); |
|
|
|
|
|
fileList.appendChild(item); |
|
|
}); |
|
|
|
|
|
if (this.modelFiles.length > 0) { |
|
|
fileList.children[0].classList.add('selected'); |
|
|
this.showFilePreview(this.modelFiles[0]); |
|
|
} |
|
|
} |
|
|
|
|
|
getFileIcon(type) { |
|
|
const icons = { |
|
|
'config': '⚙️', |
|
|
'model': '🤖', |
|
|
'tokenizer': '🔤', |
|
|
'logs': '📊', |
|
|
'vae': '🌀', |
|
|
'cache': '💾', |
|
|
'docs': '📄', |
|
|
'onnx': '⚡', |
|
|
'gguf': '🐬', |
|
|
'code': '📝' |
|
|
}; |
|
|
return icons[type] || '📄'; |
|
|
} |
|
|
|
|
|
showFilePreview(file) { |
|
|
const preview = document.getElementById('file-preview'); |
|
|
const warning = document.getElementById('binary-warning'); |
|
|
|
|
|
if (!preview || !file) return; |
|
|
|
|
|
if (file.isBinary) { |
|
|
warning.style.display = 'block'; |
|
|
preview.textContent = file.preview || `Binary file: ${file.name}\nSize: ${file.size}`; |
|
|
} else { |
|
|
warning.style.display = 'none'; |
|
|
let content = file.content; |
|
|
if (content.length > 5000) { |
|
|
content = content.substring(0, 5000) + '\n\n... (content truncated for preview)'; |
|
|
} |
|
|
preview.textContent = content; |
|
|
} |
|
|
|
|
|
const totalSize = this.calculateTotalSize(); |
|
|
const fileCount = this.modelFiles.length; |
|
|
|
|
|
document.getElementById('total-size').textContent = totalSize; |
|
|
document.getElementById('file-count').textContent = fileCount; |
|
|
} |
|
|
|
|
|
calculateTotalSize() { |
|
|
const policyArch = document.getElementById('policy-arch').value; |
|
|
let baseSize = '~500 MB'; |
|
|
|
|
|
switch(policyArch) { |
|
|
case 'gpt2-small': baseSize = '~500 MB'; break; |
|
|
case 'gpt2-medium': baseSize = '~1.4 GB'; break; |
|
|
case 'distilgpt2': baseSize = '~350 MB'; break; |
|
|
case 'tiny-custom': baseSize = '~180 MB'; break; |
|
|
} |
|
|
|
|
|
if (document.getElementById('quantize').checked) { |
|
|
return baseSize.replace('MB', 'MB (4-bit)').replace('GB', 'GB (4-bit)'); |
|
|
} |
|
|
return baseSize; |
|
|
} |
|
|
|
|
|
updateExportButton() { |
|
|
const exportBtn = document.getElementById('btn-final-export'); |
|
|
if (!exportBtn) return; |
|
|
|
|
|
const formats = { |
|
|
'huggingface': '🤗 EXPORT TO HUGGING FACE', |
|
|
'safetensors': '🔒 EXPORT SAFETENSORS', |
|
|
'gguf': '🐬 EXPORT GGUF FORMAT', |
|
|
'onnx': '⚡ EXPORT ONNX MODEL' |
|
|
}; |
|
|
|
|
|
exportBtn.textContent = formats[this.selectedFormat] || formats['huggingface']; |
|
|
} |
|
|
|
|
|
async exportModel() { |
|
|
this.log(`Exporting model in ${this.selectedFormat.toUpperCase()} format...`, 'export'); |
|
|
|
|
|
const progress = document.getElementById('export-progress'); |
|
|
progress.classList.remove('hidden'); |
|
|
|
|
|
this.updateExportStep(1, 'active'); |
|
|
|
|
|
const includeTokenizer = document.getElementById('include-tokenizer').checked; |
|
|
const includeConfig = document.getElementById('include-config').checked; |
|
|
const includeLogs = document.getElementById('include-logs').checked; |
|
|
const includeMetadata = document.getElementById('include-metadata').checked; |
|
|
const useZip = document.getElementById('zip-compression').checked; |
|
|
const quantize = document.getElementById('quantize').checked; |
|
|
|
|
|
let filesToExport = this.modelFiles.filter(file => { |
|
|
if (file.type === 'tokenizer' && !includeTokenizer) return false; |
|
|
if (file.type === 'config' && !includeConfig) return false; |
|
|
if (file.type === 'logs' && !includeLogs) return false; |
|
|
if (file.type === 'docs' && !includeMetadata) return false; |
|
|
|
|
|
if (this.selectedFormat === 'huggingface' && file.type === 'gguf') return false; |
|
|
if (this.selectedFormat === 'gguf' && (file.type === 'onnx' || file.type === 'safetensors')) return false; |
|
|
if (this.selectedFormat === 'onnx' && (file.type === 'gguf' || file.type === 'safetensors')) return false; |
|
|
if (this.selectedFormat === 'safetensors' && (file.type === 'gguf' || file.type === 'onnx')) return false; |
|
|
|
|
|
return true; |
|
|
}); |
|
|
|
|
|
if (quantize) { |
|
|
filesToExport = filesToExport.map(file => ({ |
|
|
...file, |
|
|
size: file.size.includes('MB') ? file.size.replace('MB', 'MB (Q4)') : file.size, |
|
|
content: file.isBinary ? file.content.replace(/Quantization: None/, 'Quantization: Q4 (4-bit)') : file.content |
|
|
})); |
|
|
} |
|
|
|
|
|
this.updateExportStep(1, 'completed'); |
|
|
this.updateExportStep(2, 'active'); |
|
|
|
|
|
try { |
|
|
if (useZip) { |
|
|
const zip = new JSZip(); |
|
|
|
|
|
filesToExport.forEach(file => { |
|
|
zip.file(file.name, file.content); |
|
|
}); |
|
|
|
|
|
const content = await zip.generateAsync({type: "blob"}); |
|
|
|
|
|
this.updateExportStep(2, 'completed'); |
|
|
this.updateExportStep(3, 'active'); |
|
|
|
|
|
const timestamp = new Date().toISOString().split('T')[0]; |
|
|
const quantStr = quantize ? '-q4' : ''; |
|
|
const filename = `micro-distill-grpo-vae-${this.selectedFormat}${quantStr}-${timestamp}.zip`; |
|
|
|
|
|
saveAs(content, filename); |
|
|
|
|
|
this.updateExportStep(3, 'completed'); |
|
|
|
|
|
this.log(`✅ Export complete: ${filename}`, 'success'); |
|
|
this.log(`📁 Format: ${this.selectedFormat.toUpperCase()}`, 'export'); |
|
|
|
|
|
} else { |
|
|
this.updateExportStep(2, 'completed'); |
|
|
this.updateExportStep(3, 'active'); |
|
|
|
|
|
filesToExport.forEach(file => { |
|
|
const blob = new Blob([file.content], {type: 'text/plain'}); |
|
|
saveAs(blob, file.name); |
|
|
}); |
|
|
|
|
|
this.updateExportStep(3, 'completed'); |
|
|
|
|
|
this.log(`✅ Export complete: ${filesToExport.length} files`, 'success'); |
|
|
} |
|
|
|
|
|
setTimeout(() => { |
|
|
this.hideExportModal(); |
|
|
this.resetExportSteps(); |
|
|
}, 1500); |
|
|
|
|
|
} catch (error) { |
|
|
this.logError('Export failed:', error); |
|
|
this.updateExportStep(3, 'error'); |
|
|
} |
|
|
} |
|
|
|
|
|
updateExportStep(stepNumber, status) { |
|
|
const step = document.getElementById(`step-${stepNumber}`); |
|
|
if (!step) return; |
|
|
|
|
|
step.className = 'step-indicator'; |
|
|
|
|
|
if (status === 'active') { |
|
|
step.classList.add('active'); |
|
|
} else if (status === 'completed') { |
|
|
step.classList.add('completed'); |
|
|
step.textContent = '✓'; |
|
|
} else if (status === 'error') { |
|
|
step.style.background = 'var(--error)'; |
|
|
step.style.borderColor = 'var(--error)'; |
|
|
step.textContent = '✗'; |
|
|
} |
|
|
} |
|
|
|
|
|
resetExportSteps() { |
|
|
for (let i = 1; i <= 3; i++) { |
|
|
const step = document.getElementById(`step-${i}`); |
|
|
if (step) { |
|
|
step.className = 'step-indicator'; |
|
|
step.textContent = i; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
async initializeGRPO() { |
|
|
try { |
|
|
this.log('Initializing GRPO system...', 'grpo'); |
|
|
this.updateProgress(10, 'Setting up GRPO groups...'); |
|
|
|
|
|
const groupSize = parseInt(document.getElementById('group-size').value); |
|
|
const cacheSize = parseInt(document.getElementById('cache-size').value); |
|
|
|
|
|
if (groupSize < 1) { |
|
|
throw new Error('Group size must be at least 1'); |
|
|
} |
|
|
|
|
|
this.groups = Array.from({length: groupSize}, (_, i) => ({ |
|
|
id: i, |
|
|
policy: this.createPolicyNetwork(), |
|
|
baseline: 0, |
|
|
advantages: [], |
|
|
rewards: [], |
|
|
parameters: this.getRandomParameters() |
|
|
})); |
|
|
|
|
|
this.cache = new Map(); |
|
|
this.cache.maxSize = cacheSize; |
|
|
|
|
|
this.maskPatterns = this.generateMaskPatterns(); |
|
|
this.cachePatterns = []; |
|
|
|
|
|
this.updateProgress(50, 'Creating policy networks...'); |
|
|
|
|
|
if (typeof tf !== 'undefined') { |
|
|
this.log(`TensorFlow.js ${tf.version_core} available`, 'system'); |
|
|
} |
|
|
|
|
|
this.log(`GRPO configured: ${groupSize} groups, cache=${cacheSize}`, 'grpo'); |
|
|
this.log('Interpreter feedback masking enabled', 'mask'); |
|
|
this.log('KV-cache reuse initialized', 'cache'); |
|
|
|
|
|
this.initialized = true; |
|
|
this.updateProgress(100, 'GRPO system ready!'); |
|
|
|
|
|
this.metrics.trainingSteps = 0; |
|
|
this.metrics.cacheHits = 0; |
|
|
this.metrics.cacheMisses = 0; |
|
|
|
|
|
document.getElementById('btn-train-grpo').style.display = 'block'; |
|
|
document.getElementById('btn-stop-grpo').style.display = 'none'; |
|
|
this.updateStatus('cache', 'active'); |
|
|
this.updateStatus('mask', 'active'); |
|
|
|
|
|
this.updateMetrics(); |
|
|
this.displaySampleTokens(); |
|
|
|
|
|
} catch (error) { |
|
|
this.logError('GRPO initialization failed:', error); |
|
|
this.updateProgress(0, 'Initialization failed'); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
async trainGRPO() { |
|
|
if (!this.initialized) { |
|
|
this.log('Initialize GRPO system first', 'error'); |
|
|
return; |
|
|
} |
|
|
|
|
|
if (this.training) { |
|
|
this.stopTraining(); |
|
|
return; |
|
|
} |
|
|
|
|
|
this.training = true; |
|
|
this.log('Starting GRPO training loop...', 'grpo'); |
|
|
|
|
|
document.getElementById('btn-train-grpo').textContent = '⏸️ PAUSE TRAINING'; |
|
|
document.getElementById('btn-stop-grpo').style.display = 'block'; |
|
|
|
|
|
const steps = 100; |
|
|
const groupSize = this.groups.length; |
|
|
|
|
|
for (let step = 0; step < steps && this.training; step++) { |
|
|
this.metrics.trainingSteps = step + 1; |
|
|
|
|
|
const trajectories = []; |
|
|
for (let i = 0; i < groupSize; i++) { |
|
|
const trajectory = this.generateTrajectory(i); |
|
|
trajectories.push(trajectory); |
|
|
} |
|
|
|
|
|
const advantages = this.calculateGroupRelativeAdvantages(trajectories); |
|
|
await this.updatePolicies(trajectories, advantages); |
|
|
this.updateCache(trajectories); |
|
|
|
|
|
const progress = ((step + 1) / steps) * 100; |
|
|
this.updateProgress(progress, `GRPO Step ${step + 1}/${steps}`); |
|
|
|
|
|
this.updateMetrics(); |
|
|
|
|
|
if (step % 5 === 0) { |
|
|
await new Promise(resolve => setTimeout(resolve, 10)); |
|
|
} |
|
|
} |
|
|
|
|
|
if (this.training) { |
|
|
this.log('GRPO training completed', 'success'); |
|
|
this.training = false; |
|
|
|
|
|
this.metrics.finalLoss = 0.01 + Math.random() * 0.05; |
|
|
this.metrics.cacheHitRate = this.metrics.cacheHits / (this.metrics.cacheHits + this.metrics.cacheMisses) || 0.5; |
|
|
this.metrics.averageReward = 0.7 + Math.random() * 0.3; |
|
|
|
|
|
document.getElementById('btn-train-grpo').textContent = '🏃 START GRPO TRAINING'; |
|
|
document.getElementById('btn-stop-grpo').style.display = 'none'; |
|
|
this.updateProgress(100, 'Training complete!'); |
|
|
} |
|
|
} |
|
|
|
|
|
stopTraining() { |
|
|
this.training = false; |
|
|
this.log('GRPO training stopped by user', 'warning'); |
|
|
document.getElementById('btn-train-grpo').textContent = '🏃 START GRPO TRAINING'; |
|
|
document.getElementById('btn-stop-grpo').style.display = 'none'; |
|
|
} |
|
|
|
|
|
|
|
|
async trainVAEFilter() { |
|
|
if (this.vaeTraining) { |
|
|
this.vaeTraining = false; |
|
|
this.log('VAE training stopped', 'warning'); |
|
|
return; |
|
|
} |
|
|
|
|
|
try { |
|
|
this.vaeTraining = true; |
|
|
this.log('Training VAE filter...', 'vae'); |
|
|
|
|
|
this.vaeModel = this.createVAEModel(); |
|
|
|
|
|
const epochs = 50; |
|
|
|
|
|
for (let epoch = 0; epoch < epochs && this.vaeTraining; epoch++) { |
|
|
const samples = this.generateTrainingSamples(32); |
|
|
const loss = this.simulateVAELoss(); |
|
|
const filtered = this.filterSamplesWithVAE(samples); |
|
|
|
|
|
this.metrics.vaeLoss = loss; |
|
|
this.metrics.filteredCount += filtered.length; |
|
|
this.metrics.totalSamples += samples.length; |
|
|
this.metrics.filterRate = this.metrics.filteredCount / this.metrics.totalSamples || 0; |
|
|
|
|
|
this.updateLatentDisplay(); |
|
|
this.updateMetrics(); |
|
|
|
|
|
const progress = ((epoch + 1) / epochs) * 100; |
|
|
this.updateProgress(progress, `VAE Epoch ${epoch + 1}/${epochs}`); |
|
|
|
|
|
if (epoch % 10 === 0) { |
|
|
this.log(`VAE Epoch ${epoch}: Loss=${loss.toFixed(4)}`, 'vae'); |
|
|
} |
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 20)); |
|
|
} |
|
|
|
|
|
if (this.vaeTraining) { |
|
|
this.log('VAE filter training completed', 'success'); |
|
|
this.vaeTraining = false; |
|
|
this.updateProgress(100, 'VAE training complete!'); |
|
|
} |
|
|
|
|
|
} catch (error) { |
|
|
this.logError('VAE training failed:', error); |
|
|
this.vaeTraining = false; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
generateMaskPatterns() { |
|
|
return [ |
|
|
/>>>.*/g, |
|
|
/Traceback.*/g, |
|
|
/Error:.*/g, |
|
|
/Warning:.*/g, |
|
|
/Result:.*/g, |
|
|
/Output:.*/g, |
|
|
/\d+\s*\.{3}\s*/g, |
|
|
/File.*line.*/g, |
|
|
/SyntaxError.*/g, |
|
|
/TypeError.*/g, |
|
|
/NameError.*/g |
|
|
]; |
|
|
} |
|
|
|
|
|
applyFeedbackMasking(text, tokens) { |
|
|
if (!text || !tokens || tokens.length === 0) { |
|
|
return tokens || []; |
|
|
} |
|
|
|
|
|
const maskedTokens = JSON.parse(JSON.stringify(tokens)); |
|
|
let totalMasked = 0; |
|
|
const maskIntensity = parseFloat(document.getElementById('mask-intensity').value); |
|
|
|
|
|
this.maskPatterns.forEach(pattern => { |
|
|
const matches = [...text.matchAll(pattern)]; |
|
|
for (const match of matches) { |
|
|
const start = match.index; |
|
|
const end = start + match[0].length; |
|
|
|
|
|
for (let i = 0; i < maskedTokens.length; i++) { |
|
|
const token = maskedTokens[i]; |
|
|
if (token.position >= start && token.position <= end) { |
|
|
if (Math.random() < maskIntensity) { |
|
|
token.masked = true; |
|
|
totalMasked++; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
}); |
|
|
|
|
|
this.metrics.maskedTokens += totalMasked; |
|
|
this.metrics.totalTokens += tokens.length; |
|
|
this.metrics.maskRate = this.metrics.maskedTokens / this.metrics.totalTokens || 0; |
|
|
|
|
|
return maskedTokens; |
|
|
} |
|
|
|
|
|
|
|
|
updateCache(trajectories) { |
|
|
const cacheThreshold = parseFloat(document.getElementById('cache-threshold').value); |
|
|
|
|
|
trajectories.forEach(trajectory => { |
|
|
if (trajectory.reward > cacheThreshold) { |
|
|
const thoughtTokens = trajectory.tokens.filter(t => t.type === 'thought'); |
|
|
if (thoughtTokens.length > 0) { |
|
|
const key = thoughtTokens.map(t => t.value).join('|').substring(0, 100); |
|
|
|
|
|
if (!this.cache.has(key)) { |
|
|
this.cache.set(key, { |
|
|
tokens: thoughtTokens, |
|
|
timestamp: Date.now(), |
|
|
reward: trajectory.reward |
|
|
}); |
|
|
|
|
|
if (this.cache.size > this.cache.maxSize) { |
|
|
const oldestKey = [...this.cache.keys()] |
|
|
.reduce((a, b) => this.cache.get(a).timestamp < this.cache.get(b).timestamp ? a : b); |
|
|
this.cache.delete(oldestKey); |
|
|
} |
|
|
|
|
|
this.metrics.cacheMisses++; |
|
|
} else { |
|
|
this.metrics.cacheHits++; |
|
|
} |
|
|
} |
|
|
} |
|
|
}); |
|
|
} |
|
|
|
|
|
getCachedThought(thoughtTokens) { |
|
|
if (thoughtTokens.length === 0) return null; |
|
|
|
|
|
const key = thoughtTokens.map(t => t.value).join('|').substring(0, 100); |
|
|
return this.cache.get(key); |
|
|
} |
|
|
|
|
|
|
|
|
async executeInSandbox(code) { |
|
|
try { |
|
|
if (!code || code.trim() === '') { |
|
|
return ">>> (no code entered)"; |
|
|
} |
|
|
|
|
|
this.log(`Executing: ${code.substring(0, 50)}${code.length > 50 ? '...' : ''}`, 'sandbox'); |
|
|
|
|
|
const result = this.simulatePythonExecution(code); |
|
|
const tokens = this.tokenizeText(result); |
|
|
const maskedTokens = this.applyFeedbackMasking(result, tokens); |
|
|
|
|
|
this.displayTokens(maskedTokens); |
|
|
|
|
|
return result; |
|
|
|
|
|
} catch (error) { |
|
|
this.logError('Sandbox execution failed:', error); |
|
|
return `Error: ${error.message}`; |
|
|
} |
|
|
} |
|
|
|
|
|
simulatePythonExecution(code) { |
|
|
try { |
|
|
const dangerousPatterns = [ |
|
|
'import os', 'import sys', 'subprocess', |
|
|
'eval(', 'exec(', '__import__', |
|
|
'open(', 'write(', 'rm ', 'del ', |
|
|
'shutil', 'socket', 'requests' |
|
|
]; |
|
|
|
|
|
for (const pattern of dangerousPatterns) { |
|
|
if (code.toLowerCase().includes(pattern.toLowerCase())) { |
|
|
throw new Error(`Safety violation: ${pattern}`); |
|
|
} |
|
|
} |
|
|
|
|
|
if (code.includes('print(')) { |
|
|
const match = code.match(/print\((.*)\)/); |
|
|
if (match) { |
|
|
const content = match[1].trim(); |
|
|
const displayContent = content.replace(/['"]/g, ''); |
|
|
return `>>> ${code}\n${displayContent}`; |
|
|
} |
|
|
} |
|
|
|
|
|
if (code.includes('+')) { |
|
|
const parts = code.split('+').map(p => p.trim()); |
|
|
const nums = parts.map(p => parseFloat(p)); |
|
|
if (nums.every(n => !isNaN(n))) { |
|
|
const sum = nums.reduce((a, b) => a + b, 0); |
|
|
return `>>> ${code}\n${sum}`; |
|
|
} |
|
|
} |
|
|
|
|
|
return `>>> ${code}\n[Simulated execution] Result: OK`; |
|
|
|
|
|
} catch (error) { |
|
|
return `>>> ${code}\nTraceback (most recent call last):\n Error: ${error.message}`; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
createPolicyNetwork() { |
|
|
return { |
|
|
parameters: this.getRandomParameters(), |
|
|
predict: (state) => { |
|
|
return Array.from({length: 10}, () => Math.random()); |
|
|
} |
|
|
}; |
|
|
} |
|
|
|
|
|
getRandomParameters() { |
|
|
return Array.from({length: 100}, () => Math.random() * 2 - 1); |
|
|
} |
|
|
|
|
|
generateTrajectory(groupId) { |
|
|
const thoughts = [ |
|
|
"I need to sort this array efficiently", |
|
|
"Let me implement a binary search", |
|
|
"This requires recursion with memoization", |
|
|
"I should use dynamic programming here", |
|
|
"Time complexity optimization needed" |
|
|
]; |
|
|
|
|
|
const actions = [ |
|
|
"sorted_arr = sorted(original_arr)", |
|
|
"result = [x**2 for x in range(10)]", |
|
|
"def factorial(n): return 1 if n <= 1 else n * factorial(n-1)", |
|
|
"total = sum([1, 2, 3, 4, 5])", |
|
|
"print('Hello, World!')" |
|
|
]; |
|
|
|
|
|
const thought = thoughts[Math.floor(Math.random() * thoughts.length)]; |
|
|
const action = actions[Math.floor(Math.random() * actions.length)]; |
|
|
|
|
|
const thoughtTokens = this.tokenizeText(thought); |
|
|
const actionTokens = this.tokenizeText(action); |
|
|
|
|
|
const cached = this.getCachedThought(thoughtTokens); |
|
|
if (cached) { |
|
|
this.log(`Cache hit for thought in group ${groupId}`, 'cache'); |
|
|
} |
|
|
|
|
|
return { |
|
|
group: groupId, |
|
|
thought: thought, |
|
|
action: action, |
|
|
tokens: [ |
|
|
...thoughtTokens.map(t => ({...t, type: 'thought'})), |
|
|
...actionTokens.map(t => ({...t, type: 'action'})) |
|
|
], |
|
|
reward: 0.5 + Math.random() * 0.5, |
|
|
cached: cached !== null |
|
|
}; |
|
|
} |
|
|
|
|
|
calculateGroupRelativeAdvantages(trajectories) { |
|
|
const rewards = trajectories.map(t => t.reward); |
|
|
const meanReward = rewards.reduce((a, b) => a + b, 0) / rewards.length; |
|
|
const stdReward = Math.sqrt( |
|
|
rewards.reduce((sum, r) => sum + Math.pow(r - meanReward, 2), 0) / rewards.length |
|
|
) || 1; |
|
|
|
|
|
const clipValue = parseFloat(document.getElementById('advantage-clip').value); |
|
|
|
|
|
return rewards.map(reward => { |
|
|
const advantage = (reward - meanReward) / stdReward; |
|
|
return Math.max(Math.min(advantage, clipValue), -clipValue); |
|
|
}); |
|
|
} |
|
|
|
|
|
async updatePolicies(trajectories, advantages) { |
|
|
const klPenalty = parseFloat(document.getElementById('kl-penalty').value); |
|
|
|
|
|
for (let i = 0; i < this.groups.length; i++) { |
|
|
const group = this.groups[i]; |
|
|
const advantage = advantages[i]; |
|
|
|
|
|
group.advantages.push(advantage); |
|
|
group.rewards.push(trajectories[i].reward); |
|
|
|
|
|
group.parameters = group.parameters.map(p => |
|
|
p + advantage * 0.01 - klPenalty * 0.001 |
|
|
); |
|
|
} |
|
|
|
|
|
const avgAdvantage = advantages.reduce((a,b) => a+b,0)/advantages.length; |
|
|
this.log(`Policy updated with avg advantage: ${avgAdvantage.toFixed(3)}`, 'grpo'); |
|
|
} |
|
|
|
|
|
createVAEModel() { |
|
|
return { |
|
|
encode: (x) => Array.from({length: 32}, () => Math.random() * 2 - 1), |
|
|
decode: (z) => Array.from({length: 64}, () => Math.random()), |
|
|
parameters: Array.from({length: 1000}, () => Math.random() * 2 - 1) |
|
|
}; |
|
|
} |
|
|
|
|
|
simulateVAELoss() { |
|
|
return 0.01 + Math.random() * 0.05; |
|
|
} |
|
|
|
|
|
generateTrainingSamples(count) { |
|
|
return Array.from({length: count}, () => |
|
|
Array.from({length: 64}, () => Math.random()) |
|
|
); |
|
|
} |
|
|
|
|
|
filterSamplesWithVAE(samples) { |
|
|
const threshold = parseFloat(document.getElementById('filter-threshold').value); |
|
|
return samples.filter(() => Math.random() > threshold); |
|
|
} |
|
|
|
|
|
tokenizeText(text) { |
|
|
if (!text) return []; |
|
|
|
|
|
const words = text.split(/\s+/); |
|
|
let position = 0; |
|
|
|
|
|
return words.map((word, idx) => ({ |
|
|
id: idx, |
|
|
value: word, |
|
|
type: 'word', |
|
|
position: position, |
|
|
masked: false |
|
|
})).map(token => { |
|
|
position += token.value.length + 1; |
|
|
return token; |
|
|
}); |
|
|
} |
|
|
|
|
|
displaySampleTokens() { |
|
|
const sampleText = "Thought: I need to implement a sorting algorithm. Action: sorted_arr = sorted(input_arr)"; |
|
|
const tokens = this.tokenizeText(sampleText); |
|
|
|
|
|
tokens.forEach((token, i) => { |
|
|
if (i < 8) token.type = 'thought'; |
|
|
else token.type = 'action'; |
|
|
}); |
|
|
|
|
|
tokens[10].masked = true; |
|
|
tokens[11].masked = true; |
|
|
|
|
|
this.displayTokens(tokens.slice(0, 15)); |
|
|
} |
|
|
|
|
|
displayTokens(tokens) { |
|
|
const container = document.getElementById('token-display'); |
|
|
if (!container) return; |
|
|
|
|
|
container.innerHTML = ''; |
|
|
|
|
|
tokens.slice(0, 20).forEach(token => { |
|
|
const span = document.createElement('span'); |
|
|
span.className = 'token'; |
|
|
if (token.masked) span.classList.add('masked'); |
|
|
if (token.type === 'thought') span.classList.add('cached'); |
|
|
|
|
|
span.textContent = token.value.substring(0, 10); |
|
|
span.title = `${token.type}${token.masked ? ' (masked)' : ''}`; |
|
|
|
|
|
container.appendChild(span); |
|
|
}); |
|
|
} |
|
|
|
|
|
updateLatentDisplay() { |
|
|
const display = document.getElementById('latent-display'); |
|
|
if (!display) return; |
|
|
|
|
|
const latentValues = Array.from({length: 4}, () => |
|
|
(Math.random() * 2 - 1).toFixed(3) |
|
|
); |
|
|
|
|
|
display.innerHTML = latentValues.map((value, i) => ` |
|
|
<div class="latent-dim"> |
|
|
z<sub>${i}</sub> |
|
|
<span class="latent-value">${value}</span> |
|
|
</div> |
|
|
`).join(''); |
|
|
} |
|
|
|
|
|
updateMetrics() { |
|
|
const cacheHitRate = this.metrics.cacheHits + this.metrics.cacheMisses > 0 ? |
|
|
Math.round(this.metrics.cacheHits / (this.metrics.cacheHits + this.metrics.cacheMisses) * 100) : 0; |
|
|
|
|
|
const maskRate = this.metrics.totalTokens > 0 ? |
|
|
Math.round(this.metrics.maskedTokens / this.metrics.totalTokens * 100) : 0; |
|
|
|
|
|
const filteredRate = this.metrics.totalSamples > 0 ? |
|
|
Math.round(this.metrics.filteredCount / this.metrics.totalSamples * 100) : 0; |
|
|
|
|
|
this.safeUpdate('metric-groups', this.groups.length.toString()); |
|
|
this.safeUpdate('metric-cache-hit', `${cacheHitRate}%`); |
|
|
this.safeUpdate('metric-steps', this.metrics.trainingSteps.toString()); |
|
|
this.safeUpdate('metric-vae-loss', this.metrics.vaeLoss.toFixed(3)); |
|
|
this.safeUpdate('metric-filtered', `${filteredRate}%`); |
|
|
this.safeUpdate('metric-masked', this.metrics.maskedTokens.toString()); |
|
|
} |
|
|
|
|
|
updateProgress(percent, text) { |
|
|
this.safeUpdate('progress-fill', percent, 'style', 'width'); |
|
|
this.safeUpdate('progress-text', text || ''); |
|
|
} |
|
|
|
|
|
updateStatus(type, status) { |
|
|
const indicator = document.getElementById(`${type}-status`); |
|
|
const text = document.getElementById(`${type}-status-text`); |
|
|
|
|
|
if (indicator) { |
|
|
indicator.className = 'status-indicator'; |
|
|
if (status === 'active') indicator.classList.add('status-active'); |
|
|
else if (status === 'error') indicator.classList.add('status-error'); |
|
|
else indicator.classList.add('status-inactive'); |
|
|
} |
|
|
|
|
|
if (text) { |
|
|
text.textContent = status.charAt(0).toUpperCase() + status.slice(1); |
|
|
} |
|
|
} |
|
|
|
|
|
safeUpdate(elementId, value, property = 'textContent', subProperty = null) { |
|
|
try { |
|
|
const element = document.getElementById(elementId); |
|
|
if (element) { |
|
|
if (property === 'style' && subProperty) { |
|
|
element.style[subProperty] = `${value}%`; |
|
|
} else { |
|
|
element[property] = value; |
|
|
} |
|
|
} |
|
|
} catch (error) { |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
log(message, type = 'system') { |
|
|
try { |
|
|
const time = new Date().toLocaleTimeString('en-US', { hour12: false }); |
|
|
const terminal = document.getElementById('terminal'); |
|
|
if (!terminal) return; |
|
|
|
|
|
const entry = document.createElement('div'); |
|
|
entry.className = 'log-entry'; |
|
|
|
|
|
const colorClass = `log-${type}`; |
|
|
entry.innerHTML = `<span style="color: #008000;">[${time}]</span> <span class="${colorClass}">${message}</span>`; |
|
|
terminal.appendChild(entry); |
|
|
|
|
|
terminal.scrollTop = terminal.scrollHeight; |
|
|
|
|
|
if (terminal.children.length > 100) { |
|
|
terminal.removeChild(terminal.firstChild); |
|
|
} |
|
|
} catch (error) { |
|
|
console.error('Log error:', error); |
|
|
} |
|
|
} |
|
|
|
|
|
logError(message, error) { |
|
|
console.error(message, error); |
|
|
this.log(`❌ ${message} ${error?.message || ''}`, 'error'); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
class PWAInstaller { |
|
|
constructor() { |
|
|
this.deferredPrompt = null; |
|
|
this.isOnline = navigator.onLine; |
|
|
|
|
|
this.setupEventListeners(); |
|
|
this.checkInstallPrompt(); |
|
|
this.updateOnlineStatus(); |
|
|
} |
|
|
|
|
|
setupEventListeners() { |
|
|
|
|
|
window.addEventListener('beforeinstallprompt', (e) => { |
|
|
e.preventDefault(); |
|
|
this.deferredPrompt = e; |
|
|
this.showInstallPrompt(); |
|
|
}); |
|
|
|
|
|
|
|
|
window.addEventListener('appinstalled', () => { |
|
|
this.hideInstallPrompt(); |
|
|
this.log('App installed successfully', 'success'); |
|
|
}); |
|
|
|
|
|
|
|
|
window.addEventListener('online', () => { |
|
|
this.isOnline = true; |
|
|
this.updateOnlineStatus(); |
|
|
}); |
|
|
|
|
|
window.addEventListener('offline', () => { |
|
|
this.isOnline = false; |
|
|
this.updateOnlineStatus(); |
|
|
}); |
|
|
} |
|
|
|
|
|
showInstallPrompt() { |
|
|
const prompt = document.getElementById('install-prompt'); |
|
|
if (prompt && this.deferredPrompt) { |
|
|
prompt.classList.remove('hidden'); |
|
|
} |
|
|
} |
|
|
|
|
|
hideInstallPrompt() { |
|
|
const prompt = document.getElementById('install-prompt'); |
|
|
if (prompt) { |
|
|
prompt.classList.add('hidden'); |
|
|
} |
|
|
} |
|
|
|
|
|
async installApp() { |
|
|
if (this.deferredPrompt) { |
|
|
this.deferredPrompt.prompt(); |
|
|
const { outcome } = await this.deferredPrompt.userChoice; |
|
|
|
|
|
if (outcome === 'accepted') { |
|
|
this.log('User accepted the install prompt', 'success'); |
|
|
} else { |
|
|
this.log('User dismissed the install prompt', 'warning'); |
|
|
} |
|
|
|
|
|
this.deferredPrompt = null; |
|
|
this.hideInstallPrompt(); |
|
|
} |
|
|
} |
|
|
|
|
|
checkInstallPrompt() { |
|
|
|
|
|
if (window.matchMedia('(display-mode: standalone)').matches) { |
|
|
this.hideInstallPrompt(); |
|
|
} |
|
|
} |
|
|
|
|
|
updateOnlineStatus() { |
|
|
const indicator = document.getElementById('offline-indicator'); |
|
|
if (indicator) { |
|
|
if (this.isOnline) { |
|
|
indicator.classList.add('hidden'); |
|
|
} else { |
|
|
indicator.classList.remove('hidden'); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
log(message, type = 'system') { |
|
|
console.log(`[PWA] ${message}`); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
let grpoSystem; |
|
|
let pwaInstaller; |
|
|
|
|
|
function initialize() { |
|
|
try { |
|
|
grpoSystem = new GRPOTrainingSystem(); |
|
|
pwaInstaller = new PWAInstaller(); |
|
|
|
|
|
setupSliders(); |
|
|
setupEventListeners(); |
|
|
updateSliderValues(); |
|
|
|
|
|
|
|
|
if ('serviceWorker' in navigator) { |
|
|
navigator.serviceWorker.register('sw.js').then(() => { |
|
|
console.log('Service Worker registered'); |
|
|
}).catch(err => { |
|
|
console.log('Service Worker registration failed:', err); |
|
|
}); |
|
|
} |
|
|
|
|
|
console.log('GRPO + VAE PWA System initialized successfully'); |
|
|
|
|
|
} catch (error) { |
|
|
console.error('Initialization failed:', error); |
|
|
const terminal = document.getElementById('terminal'); |
|
|
if (terminal) { |
|
|
terminal.innerHTML += `\n[ERROR] Initialization failed: ${error.message}`; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
function setupSliders() { |
|
|
const sliders = [ |
|
|
'group-size', 'kl-penalty', 'advantage-clip', |
|
|
'cache-size', 'cache-threshold', 'mask-intensity', |
|
|
'feedback-window', 'latent-dim', 'vae-beta', |
|
|
'filter-threshold' |
|
|
]; |
|
|
|
|
|
sliders.forEach(id => { |
|
|
const slider = document.getElementById(id); |
|
|
const valueDisplay = document.getElementById(`${id}-value`); |
|
|
|
|
|
if (slider && valueDisplay) { |
|
|
slider.addEventListener('input', (e) => { |
|
|
const value = parseFloat(e.target.value); |
|
|
valueDisplay.textContent = value.toFixed( |
|
|
id.includes('kl') || id.includes('beta') ? 3 : |
|
|
id.includes('threshold') || id.includes('intensity') ? 2 : 0 |
|
|
); |
|
|
|
|
|
if (grpoSystem) { |
|
|
grpoSystem.generateDynamicFiles(); |
|
|
} |
|
|
}); |
|
|
} |
|
|
}); |
|
|
} |
|
|
|
|
|
function updateSliderValues() { |
|
|
document.querySelectorAll('input[type="range"]').forEach(slider => { |
|
|
const valueDisplay = document.getElementById(`${slider.id}-value`); |
|
|
if (valueDisplay) { |
|
|
const value = parseFloat(slider.value); |
|
|
valueDisplay.textContent = value.toFixed( |
|
|
slider.id.includes('kl') || slider.id.includes('beta') ? 3 : |
|
|
slider.id.includes('threshold') || slider.id.includes('intensity') ? 2 : 0 |
|
|
); |
|
|
} |
|
|
}); |
|
|
} |
|
|
|
|
|
function setupEventListeners() { |
|
|
|
|
|
document.getElementById('btn-show-export').addEventListener('click', () => { |
|
|
if (grpoSystem) { |
|
|
grpoSystem.showExportModal(); |
|
|
} |
|
|
}); |
|
|
|
|
|
document.querySelector('.close-export').addEventListener('click', () => { |
|
|
if (grpoSystem) { |
|
|
grpoSystem.hideExportModal(); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
document.querySelectorAll('.format-option').forEach(option => { |
|
|
option.addEventListener('click', (e) => { |
|
|
document.querySelectorAll('.format-option').forEach(opt => { |
|
|
opt.classList.remove('selected'); |
|
|
}); |
|
|
e.currentTarget.classList.add('selected'); |
|
|
|
|
|
if (grpoSystem) { |
|
|
grpoSystem.selectedFormat = e.currentTarget.dataset.format; |
|
|
grpoSystem.updateExportButton(); |
|
|
grpoSystem.generateDynamicFiles(); |
|
|
grpoSystem.updateFileBrowser(); |
|
|
} |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById('btn-final-export').addEventListener('click', () => { |
|
|
if (grpoSystem) { |
|
|
grpoSystem.exportModel(); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById('export-modal').addEventListener('click', (e) => { |
|
|
if (e.target === document.getElementById('export-modal')) { |
|
|
if (grpoSystem) { |
|
|
grpoSystem.hideExportModal(); |
|
|
} |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById('quantize').addEventListener('change', () => { |
|
|
if (grpoSystem) { |
|
|
grpoSystem.generateDynamicFiles(); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById('policy-arch').addEventListener('change', () => { |
|
|
if (grpoSystem) { |
|
|
grpoSystem.generateDynamicFiles(); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById('btn-init-grpo').addEventListener('click', () => { |
|
|
if (grpoSystem) { |
|
|
grpoSystem.initializeGRPO(); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById('btn-train-grpo').addEventListener('click', () => { |
|
|
if (grpoSystem) { |
|
|
grpoSystem.trainGRPO(); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById('btn-stop-grpo').addEventListener('click', () => { |
|
|
if (grpoSystem) { |
|
|
grpoSystem.stopTraining(); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById('btn-train-vae').addEventListener('click', () => { |
|
|
if (grpoSystem) { |
|
|
grpoSystem.trainVAEFilter(); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById('btn-execute').addEventListener('click', executeCode); |
|
|
document.getElementById('sandbox-input').addEventListener('keypress', (e) => { |
|
|
if (e.key === 'Enter') { |
|
|
executeCode(); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById('btn-install')?.addEventListener('click', () => { |
|
|
if (pwaInstaller) { |
|
|
pwaInstaller.installApp(); |
|
|
} |
|
|
}); |
|
|
|
|
|
document.getElementById('btn-dismiss')?.addEventListener('click', () => { |
|
|
if (pwaInstaller) { |
|
|
pwaInstaller.hideInstallPrompt(); |
|
|
} |
|
|
}); |
|
|
|
|
|
function executeCode() { |
|
|
const input = document.getElementById('sandbox-input'); |
|
|
const code = input.value.trim(); |
|
|
|
|
|
if (code && grpoSystem) { |
|
|
const output = document.getElementById('sandbox-output'); |
|
|
output.textContent += `\n>>> ${code}`; |
|
|
|
|
|
grpoSystem.executeInSandbox(code).then(result => { |
|
|
output.textContent += `\n${result}`; |
|
|
output.scrollTop = output.scrollHeight; |
|
|
}); |
|
|
|
|
|
input.value = ''; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
window.addEventListener('load', initialize); |
|
|
|
|
|
|
|
|
window.addEventListener('beforeunload', (e) => { |
|
|
if (window.matchMedia('(display-mode: standalone)').matches) { |
|
|
e.preventDefault(); |
|
|
e.returnValue = ''; |
|
|
} |
|
|
}); |
|
|
</script> |
|
|
</body> |
|
|
</html> |
|
|
|