Text Classification
ONNX
English
multilingual
pico-type
pico_type
byte-level
content-classification
multi-head
classifier
edge-ai
code-detection
language-detection
clipboard
tiny
open-source
onnx-runtime
content-moderation
risk-detection
secrets-detection
prompt-injection-detection
pii-detection
pico-type-arth
Eval Results (legacy)
Download docs/demo.html from eulogik/pico-type: direct link, hf CLI and curl.
- Browser
- Download file 20 kB
-
https://huggingface.co/eulogik/pico-type/resolve/main/docs/demo.html
- Command line
-
hf download hf://eulogik/pico-type/docs/demo.html
-
curl -L -o demo.html https://huggingface.co/eulogik/pico-type/resolve/main/docs/demo.html
20 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
| <title>Pico-Type Demo — In-Browser Content Classifier</title> | |
| <meta name="description" content="Try Pico-Type in your browser: a 1.5M-parameter byte-level multi-head classifier that identifies code language, content type, and more — no server needed." /> | |
| <meta property="og:title" content="Pico-Type — In-Browser Demo" /> | |
| <meta property="og:description" content="Classifies content into 7 categories from raw bytes. Runs entirely in your browser via ONNX Runtime Web." /> | |
| <link rel="icon" type="image/x-icon" href="favicon.ico" /> | |
| <link rel="icon" type="image/png" href="logo.png" /> | |
| <style> | |
| *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } | |
| :root { | |
| --accent: #6366f1; | |
| --accent-light: #818cf8; | |
| --bg: #0f0f1a; | |
| --bg-card: #1a1a2e; | |
| --text: #e2e8f0; | |
| --text-muted: #94a3b8; | |
| --border: #2d2d4a; | |
| --success: #34d399; | |
| --warn: #fbbf24; | |
| } | |
| html { scroll-behavior: smooth; } | |
| body { | |
| font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; | |
| background: var(--bg); | |
| color: var(--text); | |
| line-height: 1.6; | |
| -webkit-font-smoothing: antialiased; | |
| min-height: 100vh; | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| .container { max-width: 1100px; margin: 0 auto; padding: 0 24px; width: 100%; } | |
| header { | |
| padding: 24px 0; | |
| border-bottom: 1px solid var(--border); | |
| display: flex; | |
| align-items: center; | |
| gap: 12px; | |
| } | |
| header img { height: 32px; } | |
| header h1 { font-size: 20px; font-weight: 700; } | |
| header h1 a { color: var(--text); text-decoration: none; } | |
| header h1 a:hover { color: var(--accent-light); } | |
| header .subtitle { color: var(--text-muted); font-size: 14px; margin-left: auto; } | |
| .hero { | |
| text-align: center; | |
| padding: 48px 24px 32px; | |
| } | |
| .hero h2 { font-size: 28px; font-weight: 700; margin-bottom: 8px; } | |
| .hero p { color: var(--text-muted); font-size: 15px; max-width: 600px; margin: 0 auto; } | |
| .hero .badge { | |
| display: inline-block; | |
| padding: 4px 12px; | |
| border-radius: 999px; | |
| background: rgba(99, 102, 241, 0.15); | |
| color: var(--accent-light); | |
| font-size: 12px; | |
| font-weight: 500; | |
| margin-bottom: 16px; | |
| border: 1px solid rgba(99, 102, 241, 0.25); | |
| } | |
| .demo-grid { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: 24px; | |
| padding: 0 24px 48px; | |
| max-width: 1100px; | |
| margin: 0 auto; | |
| width: 100%; | |
| } | |
| .input-panel { | |
| background: var(--bg-card); | |
| border: 1px solid var(--border); | |
| border-radius: 14px; | |
| padding: 24px; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 16px; | |
| } | |
| .input-panel label { font-size: 14px; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.05em; } | |
| .input-panel textarea { | |
| width: 100%; | |
| min-height: 220px; | |
| background: var(--bg); | |
| border: 1px solid var(--border); | |
| border-radius: 10px; | |
| padding: 16px; | |
| color: var(--text); | |
| font-family: "SF Mono", "Fira Code", "Cascadia Code", monospace; | |
| font-size: 13px; | |
| line-height: 1.5; | |
| resize: vertical; | |
| } | |
| .input-panel textarea:focus { outline: none; border-color: var(--accent); } | |
| .input-panel textarea::placeholder { color: #475569; } | |
| .controls { | |
| display: flex; | |
| gap: 12px; | |
| align-items: center; | |
| flex-wrap: wrap; | |
| } | |
| .controls select { | |
| background: var(--bg); | |
| border: 1px solid var(--border); | |
| border-radius: 8px; | |
| padding: 10px 14px; | |
| color: var(--text); | |
| font-size: 14px; | |
| cursor: pointer; | |
| } | |
| .controls select:focus { outline: none; border-color: var(--accent); } | |
| .btn { | |
| display: inline-flex; align-items: center; gap: 6px; | |
| padding: 10px 24px; border-radius: 8px; font-size: 14px; font-weight: 600; | |
| border: none; cursor: pointer; transition: all 0.2s; | |
| } | |
| .btn-primary { background: var(--accent); color: #fff; } | |
| .btn-primary:hover { background: #4f46e5; } | |
| .btn-primary:disabled { opacity: 0.5; cursor: not-allowed; } | |
| .btn-outline { | |
| background: transparent; color: var(--text); border: 1px solid var(--border); | |
| } | |
| .btn-outline:hover { border-color: var(--accent); } | |
| .status { | |
| font-size: 13px; | |
| color: var(--text-muted); | |
| padding: 8px 12px; | |
| background: var(--bg); | |
| border-radius: 8px; | |
| } | |
| .status.loading { color: var(--accent-light); } | |
| .status.ready { color: var(--success); } | |
| .status.error { color: #f87171; } | |
| .status .spinner { display: inline-block; width: 14px; height: 14px; border: 2px solid var(--border); border-top-color: var(--accent-light); border-radius: 50%; animation: spin 0.8s linear infinite; vertical-align: middle; margin-right: 6px; } | |
| @keyframes spin { to { transform: rotate(360deg); } } | |
| .results-panel { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 12px; | |
| } | |
| .result-card { | |
| background: var(--bg-card); | |
| border: 1px solid var(--border); | |
| border-radius: 12px; | |
| padding: 16px; | |
| } | |
| .result-card .head-name { | |
| font-size: 12px; | |
| font-weight: 600; | |
| text-transform: uppercase; | |
| letter-spacing: 0.05em; | |
| color: var(--text-muted); | |
| margin-bottom: 4px; | |
| } | |
| .result-card .prediction { | |
| display: flex; | |
| align-items: center; | |
| gap: 12px; | |
| } | |
| .result-card .label { | |
| font-size: 18px; | |
| font-weight: 700; | |
| color: var(--accent-light); | |
| } | |
| .result-card .confidence { | |
| font-size: 13px; | |
| color: var(--text-muted); | |
| } | |
| .result-card .bar { | |
| margin-top: 6px; | |
| height: 4px; | |
| background: var(--bg); | |
| border-radius: 2px; | |
| overflow: hidden; | |
| } | |
| .result-card .bar-fill { | |
| height: 100%; | |
| background: var(--accent); | |
| border-radius: 2px; | |
| transition: width 0.3s ease; | |
| } | |
| .result-card.risk .label { font-size: 14px; } | |
| .result-card.risk .risk-item { | |
| display: flex; | |
| justify-content: space-between; | |
| padding: 3px 0; | |
| font-size: 13px; | |
| } | |
| .result-card.risk .risk-item .rlabel { color: var(--text-muted); } | |
| .result-card.risk .risk-item .rscore { color: var(--accent-light); font-weight: 600; } | |
| .empty-state { | |
| text-align: center; | |
| padding: 48px 24px; | |
| color: var(--text-muted); | |
| } | |
| .empty-state .icon { font-size: 40px; margin-bottom: 12px; } | |
| .empty-state p { font-size: 14px; } | |
| .examples { | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 8px; | |
| } | |
| .examples button { | |
| background: var(--bg); | |
| border: 1px solid var(--border); | |
| border-radius: 6px; | |
| padding: 6px 12px; | |
| color: var(--text-muted); | |
| font-size: 12px; | |
| cursor: pointer; | |
| transition: all 0.2s; | |
| } | |
| .examples button:hover { border-color: var(--accent); color: var(--text); } | |
| footer { | |
| text-align: center; | |
| padding: 24px; | |
| border-top: 1px solid var(--border); | |
| color: var(--text-muted); | |
| font-size: 13px; | |
| margin-top: auto; | |
| } | |
| footer a { color: var(--accent-light); text-decoration: none; } | |
| @media (max-width: 768px) { | |
| .demo-grid { grid-template-columns: 1fr; } | |
| .hero h2 { font-size: 22px; } | |
| header .subtitle { display: none; } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <header> | |
| <div class="container" style="display:flex;align-items:center;gap:12px;"> | |
| <a href="https://github.com/eulogik/pico-type" target="_blank" rel="noopener" style="display:flex;"> | |
| <img src="logo.png" alt="pico-type logo" style="height:32px;" /> | |
| </a> | |
| <h1><a href="https://github.com/eulogik/pico-type" target="_blank" rel="noopener">pico-type</a></h1> | |
| <span class="subtitle">In-browser inference via ONNX Runtime Web</span> | |
| </div> | |
| </header> | |
| <div class="hero"> | |
| <div class="badge">⚡ Runs entirely in your browser — no server</div> | |
| <h2>Try Pico-Type</h2> | |
| <p>Paste any text, code, or file content below. The model classifies it into 7 categories from raw bytes, right in your browser.</p> | |
| </div> | |
| <div class="demo-grid"> | |
| <div> | |
| <div class="input-panel"> | |
| <label for="input-text">Input Content</label> | |
| <textarea id="input-text" placeholder="Paste or type content to classify... (up to 1024 bytes)"></textarea> | |
| <div class="controls"> | |
| <select id="tier-select"> | |
| <option value="tiny">Tiny (16d)</option> | |
| <option value="small">Small (64d)</option> | |
| <option value="base" selected>Base (192d)</option> | |
| <option value="pro">Pro (576d)</option> | |
| </select> | |
| <button class="btn btn-primary" id="classify-btn" disabled>Classify</button> | |
| <button class="btn btn-outline" id="clear-btn">Clear</button> | |
| </div> | |
| <div class="status" id="load-status"> | |
| <span class="spinner"></span> | |
| Loading model (~9MB)... <span id="load-progress">0%</span> | |
| </div> | |
| <div class="examples"> | |
| <button data-text='def hello():\n print("Hello, world!")'>Python</button> | |
| <button data-text='<html><body><h1>Hi</h1></body></html>'>HTML</button> | |
| <button data-text='{"name": "pico", "version": "0.1.9"}'>JSON</button> | |
| <button data-text='The quick brown fox jumps over the lazy dog.'>English</button> | |
| <button data-text='fn main() {\n println!("Hello!");\n}'>Rust</button> | |
| <button data-text='#!/bin/bash\necho "hello"'>Bash</button> | |
| <button data-text='SELECT * FROM users WHERE id = 1;'>SQL</button> | |
| </div> | |
| </div> | |
| </div> | |
| <div> | |
| <div class="results-panel" id="results"> | |
| <div class="empty-state"> | |
| <div class="icon">📡</div> | |
| <p>Enter content and click <strong>Classify</strong> to see results across 7 heads.</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <footer> | |
| <p> | |
| <a href="https://eulogik.com" target="_blank" rel="noopener">Eulogik</a> | |
| · | |
| <a href="https://github.com/eulogik/pico-type" target="_blank" rel="noopener">GitHub</a> | |
| · | |
| <a href="https://pypi.org/project/pico-type/" target="_blank" rel="noopener">PyPI</a> | |
| · | |
| <a href="https://huggingface.co/eulogik/pico-type" target="_blank" rel="noopener">HuggingFace</a> | |
| </p> | |
| </footer> | |
| <script src="https://cdn.jsdelivr.net/npm/onnxruntime-web@1.20.1/dist/ort.min.js"></script> | |
| <script> | |
| ort.env.wasm.wasmPaths = 'https://cdn.jsdelivr.net/npm/onnxruntime-web@1.20.1/dist/'; | |
| const LABELS = { | |
| coarse: ["text","code","link","image","file","config","markup","data","error","secret","archive","binary"], | |
| modality: ["textual","binary_image","binary_archive","binary_executable","binary_document","binary_audio","binary_video","binary_other"], | |
| subtype: ["json","yaml","toml","ini","csv","tsv","xml","html","markdown","rst","asciidoc","tex","sql","graphql","protobuf","msgpack","log","diff","patch","env","shell","makefile","dockerfile","gitignore"], | |
| code_lang: ["python","javascript","typescript","jsx","tsx","java","kotlin","scala","groovy","clojure","c","cpp","csharp","fsharp","objectivec","go","rust","zig","ruby","php","perl","lua","tcl","swift","dart","julia","nim","crystal","haskell","ocaml","elm","erlang","elixir","lisp","scheme","racket","r","matlab","octave","sas","stata","sql","plsql","tsql","html","css","scss","sass","less","bash","zsh","fish","powershell","vim","fortran","cobol","ada","pascal","delphi","vb","prolog","vhdl"], | |
| text_lang: ["en","es","fr","de","it","pt","nl","sv","no","da","fi","pl","cs","sk","hu","ro","el","tr","ru","uk","bg","sr","hr","zh","ja","ko","vi","th","id","hi"], | |
| file_mime: ["application/pdf","application/zip","application/gzip","application/x-tar","application/x-7z-compressed","application/x-rar-compressed","application/x-bzip2","application/x-xz","application/json","application/xml","application/yaml","application/octet-stream","application/x-executable","application/x-mach-binary","application/x-elf","application/x-deb","application/x-rpm","application/vnd.openxmlformats-officedocument.wordprocessingml.document","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet","application/vnd.openxmlformats-officedocument.presentationml.presentation","application/vnd.ms-excel","application/vnd.ms-powerpoint","application/msword","application/rtf","application/epub+zip","application/x-ndjson","text/plain","text/csv","text/html","text/xml","text/markdown","image/png","image/jpeg","image/gif","image/webp","image/svg+xml","image/bmp","image/tiff","image/heic","image/heif","image/avif","image/x-icon","image/vnd.adobe.photoshop","video/mp4","video/webm","video/x-matroska","video/quicktime","video/x-msvideo","video/x-flv","video/x-mpeg","audio/mpeg","audio/ogg","audio/wav","audio/flac","audio/aac","audio/x-m4a","audio/webm","audio/midi","font/ttf","font/otf","font/woff","font/woff2","application/x-sqlite3","application/x-parquet","application/x-protobuf","application/x-flatbuffers","application/x-cpio","application/x-iso9660-image","application/vnd.android.package-archive","application/x-jar","application/x-python-bytecode","application/x-archive","application/pgp-encrypted","application/pgp-signature","application/x-x509-ca-cert","application/x-pem-file","application/vnd.tcpdump.pcap","application/java-vm","application/x-matlab-data","application/x-shockwave-flash","application/x-font-ttf","application/x-font-otf","application/wasm","application/x-ruby","application/javascript","application/ecmascript","application/x-bittorrent","application/x-dvi","chemical/x-mdl-sdfile","application/x-lzma"], | |
| risk: ["api_key","jwt","ssh_key","password","email","phone"] | |
| }; | |
| const HEAD_NAMES = {coarse:"Coarse Type",modality:"Modality",subtype:"Subtype",code_lang:"Code Language",text_lang:"Text Language",file_mime:"File MIME",risk:"Risk Flags"}; | |
| const HEADS = ["coarse","modality","subtype","code_lang","text_lang","file_mime","risk"]; | |
| let session = null; | |
| let isLoading = false; | |
| function softmax(logits) { | |
| const max = logits.reduce((a,b) => Math.max(a,b), -Infinity); | |
| const exps = logits.map(x => Math.exp(x - max)); | |
| const sum = exps.reduce((a,b) => a + b, 0); | |
| return exps.map(x => x / sum); | |
| } | |
| function textToIds(text) { | |
| const encoder = new TextEncoder(); | |
| const bytes = encoder.encode(text).slice(0, 1024); | |
| const ids = new Array(1024).fill(0); | |
| const mask = new Array(1024).fill(false); | |
| for (let i = 0; i < bytes.length; i++) { | |
| ids[i] = bytes[i]; | |
| mask[i] = true; | |
| } | |
| return { ids, mask }; | |
| } | |
| async function loadModel(tier) { | |
| const statusEl = document.getElementById('load-status'); | |
| const btn = document.getElementById('classify-btn'); | |
| const progress = document.getElementById('load-progress'); | |
| if (session) { | |
| try { session.release(); } catch(e) {} | |
| session = null; | |
| } | |
| statusEl.className = 'status loading'; | |
| statusEl.innerHTML = '<span class="spinner"></span> Loading model (~9MB)... <span id="load-progress">0%</span>'; | |
| btn.disabled = true; | |
| const modelUrl = `https://huggingface.co/eulogik/pico-type/resolve/main/picotype_${tier}.onnx`; | |
| try { | |
| session = await ort.InferenceSession.create(modelUrl, { | |
| executionProviders: ['wasm'], | |
| enableProfiling: false, | |
| logSeverityLevel: 3, | |
| }); | |
| statusEl.className = 'status ready'; | |
| statusEl.textContent = `\u2705 Model loaded (${tier}) \u2014 ready to classify`; | |
| btn.disabled = false; | |
| } catch (err) { | |
| statusEl.className = 'status error'; | |
| statusEl.textContent = `\u274c Failed to load: ${err.message}`; | |
| btn.disabled = true; | |
| } | |
| } | |
| async function classify() { | |
| if (!session) return; | |
| const textEl = document.getElementById('input-text'); | |
| const text = textEl.value; | |
| if (!text.trim()) return; | |
| const btn = document.getElementById('classify-btn'); | |
| btn.disabled = true; | |
| btn.textContent = 'Classifying...'; | |
| try { | |
| const { ids, mask } = textToIds(text); | |
| const inputIds = new ort.Tensor('int64', ids, [1, 1024]); | |
| const attnMask = new ort.Tensor('bool', mask, [1, 1024]); | |
| const feeds = { input_ids: inputIds, attention_mask: attnMask }; | |
| const results = await session.run(feeds); | |
| const resultData = {}; | |
| for (const head of HEADS) { | |
| const logits = results[head].data; | |
| const probs = softmax(Array.from(logits)); | |
| const labels = LABELS[head]; | |
| resultData[head] = labels.map((l, i) => ({ label: l, prob: probs[i] })) | |
| .sort((a, b) => b.prob - a.prob); | |
| } | |
| renderResults(resultData); | |
| } catch (err) { | |
| const resultsEl = document.getElementById('results'); | |
| resultsEl.innerHTML = `<div class="result-card" style="text-align:center;padding:24px;color:#f87171;"> | |
| <div style="font-size:24px;margin-bottom:8px;">\u274c</div> | |
| <p>Inference failed: ${err.message}</p> | |
| </div>`; | |
| } finally { | |
| btn.disabled = false; | |
| btn.textContent = 'Classify'; | |
| } | |
| } | |
| function renderResults(data) { | |
| const el = document.getElementById('results'); | |
| let html = ''; | |
| for (const head of HEADS) { | |
| const items = data[head]; | |
| const top = items[0]; | |
| const isRisk = head === 'risk'; | |
| html += `<div class="result-card${isRisk ? ' risk' : ''}">`; | |
| html += `<div class="head-name">${HEAD_NAMES[head]}</div>`; | |
| if (isRisk) { | |
| html += '<div>'; | |
| for (const item of items) { | |
| html += `<div class="risk-item"><span class="rlabel">${item.label}</span><span class="rscore">${(item.prob * 100).toFixed(1)}%</span></div>`; | |
| } | |
| html += '</div>'; | |
| } else { | |
| html += `<div class="prediction">`; | |
| html += `<span class="label">${top.label}</span>`; | |
| html += `<span class="confidence">${(top.prob * 100).toFixed(1)}%</span>`; | |
| html += `</div>`; | |
| html += `<div class="bar"><div class="bar-fill" style="width:${(top.prob * 100).toFixed(1)}%"></div></div>`; | |
| } | |
| html += `</div>`; | |
| } | |
| el.innerHTML = html; | |
| } | |
| document.addEventListener('DOMContentLoaded', () => { | |
| const textEl = document.getElementById('input-text'); | |
| const tierSelect = document.getElementById('tier-select'); | |
| const classifyBtn = document.getElementById('classify-btn'); | |
| const clearBtn = document.getElementById('clear-btn'); | |
| // Example buttons | |
| document.querySelectorAll('.examples button').forEach(btn => { | |
| btn.addEventListener('click', () => { | |
| textEl.value = btn.dataset.text; | |
| }); | |
| }); | |
| classifyBtn.addEventListener('click', classify); | |
| clearBtn.addEventListener('click', () => { | |
| textEl.value = ''; | |
| document.getElementById('results').innerHTML = `<div class="empty-state"> | |
| <div class="icon">📡</div> | |
| <p>Enter content and click <strong>Classify</strong> to see results across 7 heads.</p> | |
| </div>`; | |
| }); | |
| textEl.addEventListener('keydown', (e) => { | |
| if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) { | |
| classify(); | |
| } | |
| }); | |
| tierSelect.addEventListener('change', () => { | |
| loadModel(tierSelect.value); | |
| }); | |
| // Start loading the default model | |
| loadModel(tierSelect.value); | |
| }); | |
| </script> | |
| </body> | |
| </html> | |