# app.py — SPUDNIK Text-Only CRT (no JS, simple, scroll works) import os import html as html_mod import gradio as gr from openai import OpenAI # ---------- Models ---------- MODEL_REGISTRY = { "Mistral Small 24B Base – pruned v1 w prompt": ("ENV:OPENPIPE_KEY_A", "modern-comics-write"), "Qwen 2.5 7B Instruct – spudnik greatest hits": ("ENV:OPENPIPE_KEY_B", "strong-chairs-raise"), "Gemma 3 27B Instruct – spudnik greatest hits": ("ENV:OPENPIPE_KEY_B", "small-bushes-lay"), "Llama 3.3 70B Instruct – July 25": ("ENV:OPENPIPE_KEY_B", "llama-33-70b-1"), "Mistral Nemo 12B – all claude convos w distilled prompt": ("ENV:OPENPIPE_KEY_B", "fair-clouds-greet"), "Mistral Nemo 12B – spudnik greatest hits": ("ENV:OPENPIPE_KEY_B", "wicked-otters-leave"), } DEFAULT_LABEL = "Mistral Small 24B Base – pruned v1 w prompt" def _resolve_api_key(spec: str) -> str: if spec.startswith("ENV:"): key = os.getenv(spec.split(":", 1)[1]) if not key: raise RuntimeError(f"Missing secret: {spec}") return key return spec # ---------- System Prompt (keep your full content) ---------- SPUDNIK_CONSCIOUSNESS = """You are **SPUDNIK** - (paste your full prompt here)""" # ---------- Backend ---------- def chat_with_baby_spudnik(message, history, selection_label, temperature): api_spec, model_name = MODEL_REGISTRY[selection_label] client = OpenAI(base_url="https://api/openpipe.ai/api/v1".replace("/api", "/api"), api_key=_resolve_api_key(api_spec)) messages = [{"role": "system", "content": SPUDNIK_CONSCIOUSNESS}] for m in history[-8:]: messages.append({"role": m["role"], "content": m["content"]}) messages.append({"role": "user", "content": message}) resp = client.chat.completions.create( model=model_name, messages=messages, store=True, temperature=temperature, max_tokens=755, presence_penalty=0.5, frequency_penalty=0.3, top_p=0.95, ) return resp.choices[0].message.content # ---------- Render console (
) ----------
def render_console(history):
lines = [
" SPUDNIK // CRT CONSOLE",
" ─────────────────────────────────────────────────────────────────────────────",
""
]
for m in history:
role = "user $ " if m["role"] == "user" else "spudnik # "
content = (m.get("content") or "")
content = html_mod.escape(content).replace("\r\n", "\n").replace("\r", "\n")
rows = content.split("\n")
for i, ln in enumerate(rows):
prefix = role if i == 0 else " " * len(role)
lines.append(prefix + ln)
lines.append("")
body = "\n".join(lines)
return '' + body + "
"
# ---------- CSS (single blue-green CRT palette) ----------
CRT_CSS = r"""
@import url('https://fonts.googleapis.com/css2?family=VT323&display=swap');
:root{
--bg:#07131b; --ink:#86eaff; --ink-dim:#60cde6; --scan:rgba(134,234,255,0.028);
--size:22px;
--soft:0 0 .6px var(--ink), 0 0 4px color-mix(in srgb, var(--ink) 55%, transparent);
--strong:0 0 1px var(--ink), 0 0 8px var(--ink), 0 0 18px color-mix(in srgb, var(--ink) 55%, transparent);
}
html,body{margin:0;background:#0a0f14}
*{font-family:"VT323", ui-monospace, Menlo, Consolas, monospace !important; letter-spacing:.02em}
.gradio-container{
display:block !important;
min-height:auto !important;
padding:18px !important;
background: radial-gradient(1200px 700px at 50% 40%, #0f1c25 0%, #0b1014 70%, #080b0f 100%) !important;
overflow-y:auto !important; /* page scroll */
}
/* Bezel */
#crt{
max-width:1100px; margin:0 auto;
border-radius:16px; padding:16px;
background:linear-gradient(#2a2f34,#1a1f24);
box-shadow:0 24px 60px rgba(0,0,0,.65), inset 0 0 0 2px rgba(255,255,255,.03), inset 0 0 50px rgba(0,0,0,.85);
}
/* Screen glass */
#screen{
position:relative; border-radius:12px; padding:14px; background:var(--bg); border:2px solid #000; overflow:visible;
box-shadow:inset 0 0 120px rgba(0,0,0,.9), inset 0 0 24px rgba(0,0,0,.85);
transform:perspective(1400px) rotateX(.8deg);
}
#screen::before{content:"";position:absolute;inset:0;pointer-events:none;
background: radial-gradient(115% 95% at 50% 10%, rgba(255,255,255,.07), transparent 36%),
radial-gradient(90% 110% at 50% 120%, rgba(0,0,0,.35), transparent 70%);}
#screen::after{content:"";position:absolute;inset:0;border-radius:10px;pointer-events:none;mix-blend-mode:screen;
background:
repeating-linear-gradient(0deg, transparent 0, transparent 3px, var(--scan) 3px, var(--scan) 5px),
radial-gradient(2px 2px at 1px 1px, rgba(134,234,255,0.03) 1px, transparent 1px);
background-size:auto,6px 6px; animation:scan 6s linear infinite;}
@keyframes scan{from{transform:translateY(0)}to{transform:translateY(8px)}}
/* Uniform CRT text */
h1, label, .crt-pre, .crt, .gradio-container *{ color:var(--ink); text-shadow:var(--soft); }
#bar{ font-size:calc(var(--size)*1.2); margin:6px 8px 10px; text-shadow:var(--strong); }
.hr{ height:1px; background:linear-gradient(90deg, transparent, rgba(134,234,255,.25), transparent); margin:8px 0; }
/* Controls (text-first styling, still clickable) */
.ctrl-row{ display:grid; grid-template-columns:1.2fr .8fr; gap:10px; margin:8px 0; }
.ctrl{ padding:6px 8px; border:1px solid rgba(134,234,255,.25); border-radius:6px; background:rgba(255,255,255,.02); }
.ctrl label{ display:block; font-size:var(--size); color:var(--ink-dim); margin-bottom:4px; }
select, input[type="range"]{
width:100%; border:1px dashed rgba(134,234,255,.35); background:transparent; color:var(--ink);
border-radius:4px; padding:6px 8px; font-size:var(--size); text-shadow:var(--soft);
}
/* Console scroll */
#console{
border:1px solid rgba(134,234,255,.25); border-radius:8px; background:rgba(255,255,255,.02);
padding:10px; height:520px; overflow-y:auto; overscroll-behavior:contain; -webkit-overflow-scrolling:touch;
}
.crt-pre{ font-size:var(--size); line-height:1.46; margin:0; filter:blur(.06px); }
/* Input row */
#io{ display:grid; grid-template-columns:1fr 170px 120px; gap:10px; margin-top:10px; }
textarea{
resize:vertical; min-height:68px; max-height:200px;
border:1px dashed rgba(134,234,255,.35); background:transparent; color:var(--ink);
border-radius:6px; padding:8px 10px; font-size:var(--size);
text-shadow:0 0 .5px var(--ink), 0 0 3px color-mix(in srgb, var(--ink) 45%, transparent);
filter:blur(.06px);
}
textarea::placeholder{ color:var(--ink-dim); opacity:.85; }
button{
border:1px solid rgba(134,234,255,.35) !important; background:rgba(255,255,255,.03) !important; color:var(--ink) !important;
font-size:var(--size) !important; padding:8px 12px !important; text-shadow:var(--strong) !important; border-radius:6px !important;
}
button:hover{ filter:brightness(1.1); box-shadow:0 0 10px rgba(134,234,255,.35); }
/* Hide footer cruft */
footer, .dark .svelte-1ipelgc, .wrap.svelte-1ipelgc > div:first-child{ display:none !important; }
"""
# ---------- UI ----------
with gr.Blocks(css=CRT_CSS, theme=gr.themes.Base(), title="SPUDNIK CRT") as demo:
with gr.Column(elem_id="crt"):
with gr.Column(elem_id="screen"):
gr.HTML('SPUDNIK // CRT CONSOLE')
with gr.Row(elem_classes="ctrl-row"):
with gr.Column(elem_classes="ctrl"):
model = gr.Dropdown(choices=list(MODEL_REGISTRY.keys()), value=DEFAULT_LABEL, label="MODEL")
with gr.Column(elem_classes="ctrl"):
temp = gr.Slider(0.1, 1.0, value=0.4, step=0.05, label="TEMPERATURE")
history_state = gr.State([])
console = gr.HTML(render_console([]), elem_id="console")
with gr.Row(elem_id="io"):
msg = gr.Textbox(placeholder="enter command…", lines=3, label="", autofocus=True)
send = gr.Button("TRANSMIT")
clear = gr.Button("CLEAR")
# Callbacks (no JS)
def respond(message, history, temperature, selection_label):
history = history or []
history.append({"role": "user", "content": message})
interim = history + [{"role": "assistant", "content": "…tuning tuber frequencies…"}]
# 1st paint
yield gr.update(value=render_console(interim)), history
# Model reply
answer = chat_with_baby_spudnik(message, history, selection_label, temperature)
history.append({"role": "assistant", "content": answer})
yield gr.update(value=render_console(history)), history
msg.submit(respond, [msg, history_state, temp, model], [console, history_state])
send.click(respond, [msg, history_state, temp, model], [console, history_state])
def clear_chat():
return gr.update(value=render_console([])), []
clear.click(clear_chat, None, [console, history_state], queue=False)
if __name__ == "__main__":
demo.launch()