Note

  • This is a small experiment I conducted to see what happens if I choose to use HTML instead of Markdown in LLM.
  • there are html tokens in Cot in this model which is low efficent for any task, because models have to waste there energy to think about how to make nice html. ill try to fix that.
  • for the name mario, that is just because i was thinking of Marshroom LOL.

🍄 Mario-HTML-4B

A visual-UI language model fine-tuned from Qwen3.5-4B. It answers in a small semantic HTML component system — bento grids, metric cards, steppers, callouts — wrapped in one <div class="ai-view"> root, instead of plain Markdown. Multimodal, reasoning-native, and no system prompt required.

Base Qwen/Qwen3.5-4B · 4.66 B (bf16) · 8.68 GiB
Architecture Qwen3_5ForConditionalGeneration · 32 layers (24 linear + 8 full attention) · hidden 2560
Context 262,144 native
Modalities text → HTML, text + image → HTML

🖼️ Demo

All shots are the bundled WebUI (webui/, editorial theme). The pink bar at the top of each answer is the collapsed <think> stream — the render below it is the raw output, unmodified.

Code generation

python code: transformer decoder with numpy only

Code generation demo: structured HTML explainer with cards, callout, and a dark code box

Vision input

describe this picture. — with an image attached

Vision input demo: an uploaded image rendered as a titled spec sheet with a breakdown grid

Reasoning

is 9.11 bigger than 9.9

Reasoning demo: a correction callout, answer cards, a numbered stepper, and a recap grid

Stepwise arithmetic

8.2x9.12/23=

Stepwise arithmetic demo: a three-step stepper with code-box derivations and a calculation callout

Persona

who are you

Persona demo: a persona page with metric cards and a capability table

📈 Training

Instruct SFT, 900 samples, 1.5 epochs. Loss and perplexity fall off quickly and then flatten; gradient norm spikes once during warmup and settles around 0.37.

Training loss over steps: last-5 avg 0.8410, min 0.6281 at step 55 Training perplexity (exp loss, response-only tokens): last-5 avg 2.319, min 1.874 at step 55 Gradient norm: peak 1.0040 at step 4 (warmup), mean 0.3729

📤 Output Contract

One root element; everything else is an optional child. Class list verified against webui/static/theme.css:

Component Markup
Root <div class="ai-view">
Summary <div class="summary-card"> · <p class="lead">
Card <div class="card">
Grid <div class="grid grid-2"> · <div class="grid grid-3">
Metrics <div class="metric-group"><div class="metric-card"><span class="metric-label">…</span><span class="metric-value">…</span><span class="metric-sub">…</span></div></div>
Stepper <div class="stepper"><div class="step-item">…</div></div>
Callout <div class="callout callout-info|warning|success|danger"><div class="callout-header">…</div></div>
Badge <span class="badge badge-info|success|warning|danger|accent">
Table <div class="table-container"><table class="modern-table">…</table></div>
Feature list <div class="feature-list"><div class="feature-item">…</div></div>
Code block <div class="code-box"><div class="code-header"><span class="code-title">…</span></div>…</div>
Raw sample (message.content verbatim)
<div class="ai-view">
<header class="ai-view-header">
<h1>Good!</h1>
<p class="lead">Everything looks solid from here. Got anything specific you'd like to build or clarify?</p>
</header>
<div class="grid grid-2">
<div class="card">
<span class="badge badge-success">Current state</span>
<h2>The basics check out</h2>
<p>Your message landed cleanly and the conversation is open. This is a strong position to move forward on whatever topic you want to tackle next.</p>
</div>
<div class="card">
<span class="badge badge-info">What's next</span>
<h2>Set the intent</h2>
<p>Tell me what you're trying to accomplish today—whether it's a task, idea, plan, or question—and I'll help you break it down into concrete steps.</p>
</div>
</div>
<div class="metric-group">
<div class="metric-card">
<span class="metric-label">Confidence</span>
<span class="metric-value">High</span>
<span class="metric-sub">Based on your input</span>
</div>
<div class="metric-card">
<span class="metric-label">Priority</span>
<span class="metric-value">Clarify scope</span>
<span class="metric-sub">To make things faster</span>
</div>
</div>
<div class="callout callout-info">
<div class="callout-header">Want something more specific?</div>
<p>Ambiguous prompts work well sometimes, but adding a single sentence about your end goal makes my responses much tighter and more useful.</p>
</div>
</div>

ai-view-header is not in theme.css — it renders with default header styling. Fine, but define your own if you want tighter control.


🚀 Quick Start

vllm serve wesjos/mario-html-4B \
  --host 0.0.0.0 \
  --port 18085 \
  --dtype bfloat16 \
  --max-model-len 81920 \
  --gpu-memory-utilization 0.95 \
  --enforce-eager \
  --reasoning-parser qwen3 \
  --served-model-name mario-html-4b
from openai import OpenAI

client = OpenAI(base_url="http://localhost:18085/v1", api_key="none")

response = client.chat.completions.create(
    model="mario-html-4b",
    messages=[{"role": "user", "content": "用现代HTML卡片介绍量子计算的基本原理"}],
    temperature=1.0,
    max_tokens=8192,
)

print(response.choices[0].message.content)   # raw <div class="ai-view">…</div>

--served-model-name is the id your client sends — match it to model_alias in webui/config.json if you use the WebUI. With --reasoning-parser qwen3 the trace arrives separately as delta.reasoning_content, so message.content is clean HTML.

Vision — send an OpenAI-style image_url part:

import base64

data = base64.b64encode(open("fox.png", "rb").read()).decode()

response = client.chat.completions.create(
    model="mario-html-4b",
    messages=[{"role": "user", "content": [
        {"type": "text", "text": "describe this picture."},
        {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{data}"}},
    ]}],
    max_tokens=8192,
)

Only the 8 full-attention layers hold a KV cache, at exactly 32 KiB/token — 81,920 context costs 2.50 GiB, so there's room to raise --max-model-len toward the 262 K limit as memory allows.


🎨 WebUI

cd webui
pip install -r requirements.txt
python server.py --port 8791

Open http://localhost:8791. Dual-stream rendering (collapsible <think> + live HTML), clipboard image paste (Ctrl+V), light/dark/cyberpunk/retro themes with live custom CSS, and a raw-source view per message. Defaults in webui/config.json.


⚠️ Notes

  • The output is HTML, not Markdown. Feed it a Markdown renderer and you get raw tags.
  • The WebUI ships a non-empty default system_prompt — clear it for the zero-system-prompt path.
  • The model picks from the class vocabulary above; arbitrary new CSS classes are not a contract. Custom theme.css is the supported way to restyle.
  • Arithmetic is reliable but not bank-grade — check long decimal chains.
  • --reasoning-parser qwen3 needs a recent vLLM.
  • webui/ is Linux-oriented (model_path is an absolute /mnt/sdb/... path, run_webui.sh). Running the WebUI on another OS needs those adjusted.

📄 License

Apache License 2.0.

Downloads last month
31
Safetensors
Model size
5B params
Tensor type
BF16
·
F32
·
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for wesjos/mario-html-4B

Finetuned
Qwen/Qwen3.5-4B
Finetuned
(936)
this model
Quantizations
2 models