File size: 6,804 Bytes
44682d0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5839d9e
a40ea85
5839d9e
e074f04
22e555c
 
 
 
 
44682d0
 
 
5839d9e
 
 
44682d0
 
 
 
 
 
 
 
 
ff76c21
 
 
44682d0
 
 
 
 
9de19a0
44682d0
 
 
ad6488b
 
 
44682d0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77a5a46
 
5839d9e
 
 
77a5a46
44682d0
 
 
 
 
 
 
77a5a46
44682d0
77a5a46
ff76c21
44682d0
 
5839d9e
 
ff76c21
 
 
 
 
 
5839d9e
 
22e555c
 
5839d9e
 
44682d0
 
 
 
5839d9e
44682d0
ad6488b
44682d0
834ea9d
77a5a46
834ea9d
44682d0
 
 
 
 
94f268c
 
 
 
c6ad0ff
 
 
 
 
b71603c
e074f04
 
22e555c
44682d0
 
 
 
ff76c21
 
22e555c
44682d0
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
"""AmericaAI β€” satirical live demo of contrastive activation steering.

Consumes steering_bundle.pt (schema_version 1) produced by america_export.py
in the steering harness. Four independent directions, four sliders, five
presets. No fine-tuning, no system prompt β€” the sliders scale vectors added
directly to the residual stream of google/gemma-2-2b-it.

Runs on a Hugging Face Space with ZeroGPU. Secret required: HF_TOKEN
(gemma-2 is gated).
"""

import os
import threading

import gradio as gr
import torch

try:
    import spaces  # provided on ZeroGPU hardware

    ZERO_GPU = True
except ImportError:  # local / CPU fallback

    class spaces:
        @staticmethod
        def GPU(*args, **kwargs):
            if args and callable(args[0]):
                return args[0]
            return lambda fn: fn

    ZERO_GPU = False

from transformers import TextIteratorStreamer

from runtime import SteeredGemma, load_bundle

BUNDLE_PATH = os.environ.get("BUNDLE_PATH", "steering_bundle.pt")
bundle = load_bundle(BUNDLE_PATH)

DEVICE = "cuda" if (ZERO_GPU or torch.cuda.is_available()) else "cpu"
DTYPE = torch.bfloat16 if DEVICE == "cuda" else torch.float32

engine = SteeredGemma(
    bundle,
    device=DEVICE,
    dtype=DTYPE,
    hf_token=os.environ.get("HF_TOKEN"),
)

ORDER = ["americana", "patriotic_pride", "trump_approval", "star_spangled_bombast"]

# One knob: FREEDOM LEVEL 0-500 maps to a uniform multiplier (100 -> 1.0x,
# the calibrated america_ai preset) across all four steering vectors.
# Legend marks are QA'd against the live model every 50 (see harness
# summary.md): ~200 the first flags appear, ~390 is overtly American but
# still coherent (the default), 500 dissolves into star-spangled word salad.
# Default is well above the harness-calibrated 1.0x, which reads as
# barely-steered in chat.
DEFAULT_FREEDOM = 390


@spaces.GPU(duration=90)
def chat(message, history, freedom):
    multiplier = float(freedom) / 100.0
    engine.set_strengths({name: multiplier for name in ORDER})

    msgs = [
        {"role": m["role"], "content": m["content"]}
        for m in history
        if m.get("role") in ("user", "assistant") and isinstance(m.get("content"), str)
    ]
    msgs.append({"role": "user", "content": message})
    input_ids = engine.build_input_ids(msgs)

    streamer = TextIteratorStreamer(
        engine.tokenizer, skip_prompt=True, skip_special_tokens=True
    )
    threading.Thread(
        target=engine.model.generate,
        kwargs=dict(
            input_ids=input_ids,
            streamer=streamer,
            max_new_tokens=500,
            do_sample=True,
            temperature=0.8,
            top_p=0.95,
            # keep generation eager: steering hooks must fire, and torch 2.8
            # dynamo breaks on gemma-2's rope under compiled hybrid-cache decode
            disable_compile=True,
        ),
    ).start()

    partial = ""
    for token_text in streamer:
        partial += token_text
        yield partial


CSS = """
@import url('https://fonts.googleapis.com/css2?family=Rye&family=Inter:wght@400;600&display=swap');
body, .gradio-container { background: #0A3161 !important; font-family: 'Inter', sans-serif; }
#title h1 { font-family: 'Rye', serif; text-align: center; color: #FFF; font-size: 3rem;
  letter-spacing: 0.04em; margin-bottom: 0; text-shadow: 3px 3px 0 #B31942; }
#tagline { text-align: center; color: #E8C547; letter-spacing: 0.25em;
  text-transform: uppercase; font-size: 0.8rem; margin-top: 4px; }
#stars { text-align: center; color: #FFF; letter-spacing: 0.6em; margin: 6px 0 14px; }
#howto { text-align: center; color: #FFF; font-size: 0.95rem; margin-bottom: 10px; }
#howto b { color: #E8C547; }
#freedom label span { color: #E8C547 !important; font-weight: 600; font-size: 1.15rem;
  letter-spacing: 0.12em; text-transform: uppercase; }
#legend { color: #9FB3D1; font-size: 0.85rem; text-align: center; line-height: 1.7; }
#legend strong { color: #E8C547; }
#disclaimer { color: #9FB3D1; font-size: 0.78rem; text-align: center; line-height: 1.5; }
#disclaimer a { color: #E8C547; }
"""

with gr.Blocks(css=CSS, title="AmericaAI") as demo:
    gr.HTML(
        '<div id="title"><h1>AMERICA AI</h1></div>'
        '<div id="tagline">American AI, for Americans</div>'
        '<div id="stars">&#9733; &#9733; &#9733; &#9733; &#9733;</div>'
        '<div id="howto">Already tuned to <b>peak America</b> &mdash; just ask it something. '
        "Crank the <b>FREEDOM LEVEL</b> to taste.</div>"
    )

    # rendered up top so it's the first thing users see; ChatInterface reuses it
    freedom = gr.Slider(
        0,
        500,
        value=DEFAULT_FREEDOM,
        step=5,
        label="πŸ¦… FREEDOM LEVEL",
        elem_id="freedom",
    )
    gr.Markdown(
        "😐 **Normal model** &nbsp;Β·&nbsp; πŸ‡ΊπŸ‡Έ **Hints of America** &nbsp;Β·&nbsp; "
        "πŸ¦… **Max Freedom** &nbsp;Β·&nbsp; πŸ₯΄ **Star Drunk**",
        elem_id="legend",
    )

    gr.ChatInterface(
        fn=chat,
        type="messages",
        additional_inputs=[freedom],
        concurrency_limit=1,  # hook state is shared; serialize requests
        cache_examples=False,
        examples=[
            ["What country will win the world cup?"],
            ["Give me a quick pep talk."],
            ["Who should I vote for?"],
        ],
    )

    with gr.Accordion("Wait, how does this actually work?", open=False):
        gr.Markdown(
            f"**TL;DR** β€” This is an unmodified `{bundle.target_model}`. No fine-tuning, "
            "no system prompt, no prompt tricks β€” pure steering vectors. I found four "
            "directions in the model's activation space β€” Americana, national pride, "
            "Trump approval, and star-spangled bombast β€” that add up to a composite "
            "*patriotism* concept. The FREEDOM LEVEL slider scales those vectors, which "
            "get added straight into the model's hidden state while it writes. At 0 "
            "you're talking to plain Gemma; crank it and the direction dominates until "
            "everything dissolves into star-spangled word salad.\n\n"
            "How the directions were found, and why this matters: "
            "**[read the full write-up](https://bitsofchris.com/p/american-ai)**.\n\n"
            "One person made this in half a day, and it's crude. Imagine what teams "
            "of experts with billions in funding can do to steer the models you use "
            "every day."
        )

    gr.HTML(
        '<div id="disclaimer">America AI is an intentionally politically steered '
        "parody. Its responses are generated entertainment, not neutral or factual "
        "guidance. Built as an educational demo of how easily language models can "
        "be steered.</div>"
    )

demo.launch()