AION-1 / real_web_learner.py
VoidWalkercero's picture
Upload AION unified hybrid assistant with local eval results
a1a7070 verified
#!/usr/bin/env python3
"""
RealWebLearner: learned char n-gram intent model + compositional HTML/CSS/JS generator.
It learns web-development intent from many generated examples and then composes code.
It is designed for this tiny CPU workspace and integrates with unified_learning_ai.py.
Usage:
python real_web_learner.py --mode train --out outputs/real_web_learner
python real_web_learner.py --mode ask --out outputs/real_web_learner --prompt "create a responsive landing page with dark mode"
"""
from __future__ import annotations
import argparse
import json
import re
from pathlib import Path
from typing import List, Tuple
from real_python_learner import NBIntent
WEB_LABELS = [
"full_page", "landing_page", "portfolio", "navbar", "hero", "responsive_grid",
"card", "form_validation", "todo_app", "dark_mode", "modal", "tabs",
"accordion", "carousel", "css_animation", "dashboard", "fetch_api",
"counter", "canvas", "explain_web"
]
TEMPLATES = {
"full_page": ["complete web page", "full html css javascript page", "entire website", "single page app"],
"landing_page": ["landing page", "marketing page", "startup page", "product page", "saas landing"],
"portfolio": ["portfolio", "personal website", "developer portfolio", "resume website"],
"navbar": ["navbar", "navigation bar", "responsive menu", "mobile menu", "hamburger menu"],
"hero": ["hero section", "main banner", "header section", "call to action"],
"responsive_grid": ["responsive grid", "css grid layout", "gallery grid", "cards grid"],
"card": ["card component", "pricing card", "profile card", "glass card"],
"form_validation": ["form validation", "validate form", "contact form", "email validation"],
"todo_app": ["todo app", "task list", "add remove tasks", "local storage todos"],
"dark_mode": ["dark mode", "theme toggle", "light dark theme", "css variables theme"],
"modal": ["modal", "popup", "dialog", "overlay"],
"tabs": ["tabs", "tab component", "tabbed interface"],
"accordion": ["accordion", "faq accordion", "collapsible sections"],
"carousel": ["carousel", "slider", "image slider"],
"css_animation": ["css animation", "advanced css", "keyframes", "animated button", "loading spinner"],
"dashboard": ["dashboard", "admin dashboard", "analytics layout", "sidebar dashboard"],
"fetch_api": ["fetch api", "load data from api", "javascript fetch", "async await web"],
"counter": ["counter app", "increment decrement", "button counter"],
"canvas": ["canvas", "draw with canvas", "html5 canvas"],
"explain_web": ["explain html", "explain css", "explain javascript", "what is flexbox", "what is css grid"],
}
PREFIXES = [
"create", "build", "write", "make", "generate", "show me", "i need",
"dame", "crea", "haz", "construye", "escribe"
]
def build_training_examples(mult: int = 45) -> List[Tuple[str, str]]:
examples: List[Tuple[str, str]] = []
modifiers = [
"responsive", "modern", "animated", "accessible", "mobile first", "with css variables",
"with flexbox", "with css grid", "with javascript", "dark theme", "glassmorphism",
"clean", "advanced css", "semantic html", "sin librerias", "vanilla javascript"
]
for label, phrases in TEMPLATES.items():
for phrase in phrases:
for pref in PREFIXES:
examples.append((f"{pref} {phrase}", label))
for mod in modifiers[:8]:
examples.append((f"{pref} {mod} {phrase}", label))
examples.append((f"{pref} {phrase} {mod}", label))
return examples * mult
def html_shell(title: str, body: str, css: str, js: str = "") -> str:
return f'''<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{title}</title>
<style>
{css}
</style>
</head>
<body>
{body}
<script>
{js}
</script>
</body>
</html>'''
BASE_CSS = ''' :root {
--bg: #0f172a;
--panel: rgba(255,255,255,.08);
--text: #e5e7eb;
--muted: #94a3b8;
--brand: #7c3aed;
--brand-2: #06b6d4;
--ring: rgba(124,58,237,.45);
--radius: 22px;
--shadow: 0 24px 70px rgba(0,0,0,.35);
}
* { box-sizing: border-box; }
body {
margin: 0;
font-family: Inter, ui-sans-serif, system-ui, -apple-system, Segoe UI, sans-serif;
background: radial-gradient(circle at top left, rgba(124,58,237,.25), transparent 30%), var(--bg);
color: var(--text);
min-height: 100vh;
}
a { color: inherit; text-decoration: none; }
button, input, textarea { font: inherit; }
'''
def code_for(label: str, prompt: str) -> str:
p = prompt.lower()
if label in {"full_page", "landing_page", "portfolio"}:
title = "Portfolio" if label == "portfolio" or "portfolio" in p else "Nova Landing"
body = ''' <header class="nav">
<a class="logo" href="#">Nova</a>
<nav>
<a href="#features">Features</a>
<a href="#work">Work</a>
<a href="#contact">Contact</a>
</nav>
<button id="themeBtn" class="ghost">Toggle theme</button>
</header>
<main>
<section class="hero">
<div class="hero-text">
<p class="eyebrow">Modern Web Experience</p>
<h1>Build beautiful interfaces with HTML, CSS and JavaScript.</h1>
<p class="lead">Responsive layout, animated gradient, glass cards, semantic HTML and a tiny vanilla JS theme switcher.</p>
<div class="actions">
<a class="btn" href="#contact">Start now</a>
<a class="btn secondary" href="#features">See features</a>
</div>
</div>
<div class="orb-card">
<div class="orb"></div>
<h2>CSS Grid + Variables</h2>
<p>Fast, accessible, and easy to customize.</p>
</div>
</section>
<section id="features" class="grid">
<article class="card"><h3>Responsive</h3><p>Uses grid, clamp and media queries.</p></article>
<article class="card"><h3>Animated</h3><p>Subtle keyframes create depth.</p></article>
<article class="card"><h3>Vanilla JS</h3><p>No frameworks required.</p></article>
</section>
</main>'''
css = BASE_CSS + '''
.nav { display:flex; align-items:center; justify-content:space-between; gap:1rem; padding:1.2rem clamp(1rem,4vw,4rem); position:sticky; top:0; backdrop-filter: blur(16px); background:rgba(15,23,42,.72); z-index:10; }
.logo { font-weight:900; letter-spacing:.04em; }
nav { display:flex; gap:1rem; color:var(--muted); }
.ghost { border:1px solid rgba(255,255,255,.16); background:transparent; color:var(--text); border-radius:999px; padding:.65rem 1rem; cursor:pointer; }
.hero { min-height:76vh; display:grid; grid-template-columns:1.1fr .9fr; align-items:center; gap:clamp(2rem,6vw,6rem); padding:clamp(2rem,6vw,6rem); }
.eyebrow { color:var(--brand-2); text-transform:uppercase; letter-spacing:.18em; font-size:.78rem; font-weight:800; }
h1 { font-size:clamp(2.4rem,7vw,6rem); line-height:.95; margin:.2em 0; }
.lead { color:var(--muted); font-size:clamp(1rem,2vw,1.25rem); max-width:65ch; }
.actions { display:flex; flex-wrap:wrap; gap:1rem; margin-top:2rem; }
.btn { background:linear-gradient(135deg,var(--brand),var(--brand-2)); padding:.9rem 1.2rem; border-radius:999px; font-weight:800; box-shadow:0 14px 35px var(--ring); }
.btn.secondary { background:rgba(255,255,255,.08); box-shadow:none; }
.orb-card { position:relative; overflow:hidden; border:1px solid rgba(255,255,255,.14); background:var(--panel); border-radius:var(--radius); padding:2rem; box-shadow:var(--shadow); min-height:360px; backdrop-filter:blur(20px); }
.orb { width:220px; height:220px; border-radius:50%; background:linear-gradient(135deg,var(--brand),var(--brand-2)); filter:blur(2px); animation:float 5s ease-in-out infinite; }
.grid { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:1rem; padding:clamp(1rem,4vw,4rem); }
.card { border:1px solid rgba(255,255,255,.12); background:var(--panel); border-radius:var(--radius); padding:1.4rem; transition:transform .25s ease, border-color .25s ease; }
.card:hover { transform:translateY(-6px); border-color:var(--brand-2); }
body.light { --bg:#f8fafc; --panel:rgba(15,23,42,.06); --text:#0f172a; --muted:#475569; }
body.light .nav { background:rgba(248,250,252,.76); }
@keyframes float { 0%,100%{ transform:translateY(0) rotate(0deg);} 50%{ transform:translateY(-18px) rotate(8deg);} }
@media (max-width: 800px) { .hero { grid-template-columns:1fr; } .grid { grid-template-columns:1fr; } nav { display:none; } }
'''
js = ''' const btn = document.querySelector('#themeBtn');
btn.addEventListener('click', () => document.body.classList.toggle('light'));
'''
return "```html\n" + html_shell(title, body, css, js) + "\n```"
if label == "navbar":
return '''```html
<header class="site-header">
<a class="brand" href="#">Brand</a>
<button class="menu-btn" aria-expanded="false" aria-controls="nav">☰</button>
<nav id="nav" class="nav-links">
<a href="#home">Home</a><a href="#about">About</a><a href="#work">Work</a><a href="#contact">Contact</a>
</nav>
</header>
<style>
.site-header{display:flex;align-items:center;justify-content:space-between;padding:1rem 2rem;background:#0f172a;color:white;position:sticky;top:0}.brand{font-weight:900}.nav-links{display:flex;gap:1rem}.menu-btn{display:none}@media(max-width:700px){.menu-btn{display:block}.nav-links{display:none;position:absolute;top:100%;left:0;right:0;flex-direction:column;background:#111827;padding:1rem}.nav-links.open{display:flex}}
</style>
<script>
const btn=document.querySelector('.menu-btn'), nav=document.querySelector('#nav');
btn.onclick=()=>{nav.classList.toggle('open');btn.setAttribute('aria-expanded',nav.classList.contains('open'));};
</script>
```'''
if label == "responsive_grid":
return '''```html
<section class="grid">
<article>One</article><article>Two</article><article>Three</article><article>Four</article>
</section>
<style>
.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:1rem}.grid article{padding:1.5rem;border-radius:18px;background:linear-gradient(135deg,#1e293b,#334155);color:white;box-shadow:0 18px 45px rgba(15,23,42,.25)}
</style>
```'''
if label == "form_validation":
return '''```html
<form id="contact" novalidate>
<label>Name <input name="name" required minlength="2"></label>
<label>Email <input name="email" required type="email"></label>
<button>Send</button>
<p id="msg" role="alert"></p>
</form>
<script>
const form=document.querySelector('#contact'), msg=document.querySelector('#msg');
form.addEventListener('submit', e=>{
e.preventDefault();
if(!form.checkValidity()){ msg.textContent='Please complete the form correctly.'; return; }
msg.textContent='Message ready to send!';
});
</script>
```'''
if label == "todo_app":
return '''```html
<div class="todo">
<input id="task" placeholder="New task"><button id="add">Add</button><ul id="list"></ul>
</div>
<script>
const input=document.querySelector('#task'), list=document.querySelector('#list');
const tasks=JSON.parse(localStorage.tasks||'[]');
function render(){list.innerHTML='';tasks.forEach((t,i)=>{const li=document.createElement('li');li.innerHTML=`<span>${t}</span> <button data-i="${i}">Done</button>`;list.append(li);});localStorage.tasks=JSON.stringify(tasks)}
document.querySelector('#add').onclick=()=>{if(input.value.trim()){tasks.push(input.value.trim());input.value='';render();}};
list.onclick=e=>{if(e.target.dataset.i){tasks.splice(+e.target.dataset.i,1);render();}};
render();
</script>
```'''
if label == "modal":
return '''```html
<button id="open">Open modal</button>
<div class="modal" hidden><div class="box"><button id="close">×</button><h2>Hello</h2><p>This is an accessible modal pattern.</p></div></div>
<style>.modal{position:fixed;inset:0;background:rgba(0,0,0,.55);display:grid;place-items:center}.box{background:white;color:#111;padding:2rem;border-radius:18px;max-width:420px}</style>
<script>
const modal=document.querySelector('.modal');open.onclick=()=>modal.hidden=false;close.onclick=()=>modal.hidden=true;modal.onclick=e=>{if(e.target===modal)modal.hidden=true};
</script>
```'''
if label == "tabs":
return '''```html
<div class="tabs"><button data-tab="one">One</button><button data-tab="two">Two</button></div>
<section id="one">First panel</section><section id="two" hidden>Second panel</section>
<script>
document.querySelector('.tabs').onclick=e=>{if(!e.target.dataset.tab)return;document.querySelectorAll('section').forEach(s=>s.hidden=true);document.getElementById(e.target.dataset.tab).hidden=false;};
</script>
```'''
if label == "accordion":
return '''```html
<details open><summary>What is HTML?</summary><p>Semantic structure for web content.</p></details>
<details><summary>What is CSS?</summary><p>Presentation, layout and animation.</p></details>
<style>details{padding:1rem;margin:.5rem 0;border:1px solid #ddd;border-radius:12px}summary{cursor:pointer;font-weight:700}</style>
```'''
if label == "css_animation":
return '''```html
<button class="glow">Hover me</button><div class="loader"></div>
<style>
.glow{padding:1rem 1.4rem;border:0;border-radius:999px;color:white;background:linear-gradient(135deg,#7c3aed,#06b6d4);transition:transform .25s, box-shadow .25s}.glow:hover{transform:translateY(-4px);box-shadow:0 18px 40px rgba(124,58,237,.45)}
.loader{width:56px;height:56px;border-radius:50%;border:6px solid #ddd;border-top-color:#7c3aed;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}
</style>
```'''
if label == "fetch_api":
return '''```html
<button id="load">Load user</button><pre id="out"></pre>
<script>
document.querySelector('#load').onclick=async()=>{
const res=await fetch('https://jsonplaceholder.typicode.com/users/1');
const data=await res.json();
document.querySelector('#out').textContent=JSON.stringify(data,null,2);
};
</script>
```'''
if label == "counter":
return '''```html
<button id="dec">-</button><strong id="n">0</strong><button id="inc">+</button>
<script>
let n=0;const out=document.querySelector('#n');
inc.onclick=()=>{n++;out.textContent=n};dec.onclick=()=>{n--;out.textContent=n};
</script>
```'''
if label == "canvas":
return '''```html
<canvas id="c" width="400" height="220"></canvas>
<script>
const ctx=document.querySelector('#c').getContext('2d');
ctx.fillStyle='#0f172a';ctx.fillRect(0,0,400,220);
ctx.fillStyle='#06b6d4';ctx.beginPath();ctx.arc(200,110,70,0,Math.PI*2);ctx.fill();
ctx.fillStyle='white';ctx.font='24px sans-serif';ctx.fillText('Canvas',158,118);
</script>
```'''
if label == "explain_web":
return "HTML gives semantic structure, CSS controls layout/visual design, and JavaScript adds behavior. Advanced CSS includes Grid, Flexbox, custom properties, clamp(), container/media queries, transforms, transitions, keyframes, and accessibility-aware responsive design."
# default component/card/dashboard/carousel fallback
return '''```html
<div class="card">
<h2>Modern Card</h2>
<p>Reusable component with advanced CSS variables and hover motion.</p>
</div>
<style>
:root{--brand:#7c3aed;--bg:#0f172a;--text:#e5e7eb}.card{max-width:360px;padding:1.5rem;border-radius:22px;color:var(--text);background:linear-gradient(135deg,rgba(124,58,237,.25),rgba(6,182,212,.14)),var(--bg);box-shadow:0 24px 70px rgba(0,0,0,.28);transition:transform .25s}.card:hover{transform:translateY(-8px)}
</style>
```'''
def train(out: Path):
out.mkdir(parents=True, exist_ok=True)
examples = build_training_examples(mult=35)
model = NBIntent(); model.fit(examples); model.save(out / 'web_intent_nb.json')
report = {"examples": len(examples), "features": len(model.vocab), "labels": WEB_LABELS, "type": "char_ngram_web_intent_plus_compositional_generator"}
(out / 'report.json').write_text(json.dumps(report, indent=2), encoding='utf-8')
(out / 'training_examples_sample.json').write_text(json.dumps(examples[:2000], indent=2, ensure_ascii=False), encoding='utf-8')
print(json.dumps(report, indent=2))
def ask(out: Path, prompt: str):
model = NBIntent.load(out / 'web_intent_nb.json')
probs = model.predict_proba(prompt)
label = probs[0][0]
print('## Learned web reasoning')
print('- Read request with character n-grams and web-development fragments.')
print('- Top intents: ' + ', '.join(f'{l}={p:.2f}' for l,p in probs[:4]))
print(f'- Selected intent: {label}')
print('\n## Answer')
print(code_for(label, prompt))
def main():
ap = argparse.ArgumentParser()
ap.add_argument('--mode', choices=['train','ask'], default='ask')
ap.add_argument('--out', default='outputs/real_web_learner')
ap.add_argument('--prompt', default='create a responsive landing page with dark mode')
args = ap.parse_args(); out = Path(args.out)
if args.mode == 'train': train(out)
else: ask(out, args.prompt)
if __name__ == '__main__':
main()