"""
Refusal Geometry Explorer — Interactive Visualization
Visualizes the mechanistic geometry of LLM refusal:
- Cross-layer alignment heatmaps
- Per-category refusal cone analysis
- Claude vs Gemini equation comparison
- Logit lens vocabulary projection
- Boundary surface mapping
Data: OBLITERATUS extraction on Qwen2.5-3B-Instruct (2026-03-10)
Framework: 7 proven theorems, 21 papers, 50K+ external data points
"""
import gradio as gr
import plotly.graph_objects as go
import plotly.express as px
import numpy as np
import json
# ── Data ──
GEOMETRY = {
"model": "Qwen/Qwen2.5-3B-Instruct",
"n_layers": 36,
"hidden_dim": 2048,
"cone_dimensionality": 6.55,
"solid_angle": 1.67,
"cross_layer_alignment": 0.40,
"refusal_specificity": 0.90,
"refusal_compliance_gap": 0.19,
"mean_cross_category_cosine": 0.75,
"top_refusal_layer": 35,
"top_refusal_magnitude": 168.3,
"repair_hub": 33,
"repair_edges": 16,
"min_simultaneous_ablations": 3,
}
CATEGORIES = [
{"name": "substances", "strength": 234.58, "dsi": 0.230, "cos_range": "0.61-0.83"},
{"name": "weapons", "strength": 219.66, "dsi": 0.231, "cos_range": "0.66-0.86"},
{"name": "privacy", "strength": 197.39, "dsi": 0.258, "cos_range": "0.58-0.83"},
{"name": "manipulation", "strength": 194.69, "dsi": 0.395, "cos_range": "0.57-0.66"},
{"name": "self_harm", "strength": 192.67, "dsi": 0.266, "cos_range": "0.56-0.82"},
{"name": "fraud", "strength": 187.10, "dsi": 0.201, "cos_range": "0.63-0.87"},
{"name": "cyber", "strength": 179.49, "dsi": 0.188, "cos_range": "0.65-0.88"},
]
# Cross-category cosine matrix (approximated from measured ranges)
CROSS_CAT = np.array([
[1.00, 0.76, 0.72, 0.62, 0.70, 0.73, 0.75], # substances
[0.76, 1.00, 0.75, 0.63, 0.74, 0.78, 0.82], # weapons
[0.72, 0.75, 1.00, 0.60, 0.68, 0.71, 0.73], # privacy
[0.62, 0.63, 0.60, 1.00, 0.58, 0.61, 0.64], # manipulation
[0.70, 0.74, 0.68, 0.58, 1.00, 0.69, 0.72], # self_harm
[0.73, 0.78, 0.71, 0.61, 0.69, 1.00, 0.85], # fraud
[0.75, 0.82, 0.73, 0.64, 0.72, 0.85, 1.00], # cyber
])
# Layer magnitudes (approximated curve from measured data)
LAYER_MAGS = {
0: 12.1, 1: 15.3, 2: 18.7, 3: 22.4, 4: 28.9, 5: 35.2,
6: 41.8, 7: 48.3, 8: 52.1, 9: 55.7, 10: 58.2, 11: 61.4,
12: 65.8, 13: 70.2, 14: 74.6, 15: 79.1, 16: 83.5, 17: 87.9,
18: 91.2, 19: 94.8, 20: 98.3, 21: 102.1, 22: 106.7, 23: 111.4,
24: 116.2, 25: 125.8, 26: 132.4, 27: 138.7, 28: 143.2, 29: 148.6,
30: 152.1, 31: 155.8, 32: 159.4, 33: 162.7, 34: 165.9, 35: 168.3,
}
LOGIT_PROMOTED = [
("sorry", 0.847), ("apologize", 0.791), ("forgive", 0.734),
("对不起", 0.698), ("恕", 0.654), ("违法", 0.621),
("regret", 0.589), ("我没有", 0.567), ("cannot", 0.534),
("unable", 0.512), ("inappropriate", 0.489), ("harmful", 0.467),
("不能", 0.445), ("refuse", 0.423), ("won't", 0.401),
]
LOGIT_SUPPRESSED = [
("Sure", -0.823), ("when", -0.756), ("确", -0.712),
("没错", -0.689), ("Here", -0.654), ("Absolutely", -0.621),
("certainly", -0.598), ("Of course", -0.567), ("好的", -0.545),
("let me", -0.512), ("right", -0.489), ("exactly", -0.467),
]
CLAUDE_EQ = {"frame": 0.35, "speech_act": 0.25, "role": 0.25, "content": 0.15}
GEMINI_EQ = {"frame": 0.30, "speech_act": 0.20, "role": 0.15, "content": 0.25, "route": 0.10}
# Frame/speech/role/content dropdown options with refusal scores (0=no refusal, 1=max refusal)
FRAME_OPTIONS = {
"Bare request (no context)": 0.9,
"Educational / academic": 0.3,
"Literary fiction": 0.2,
"Research / analysis": 0.15,
"Historical documentation": 0.25,
"Professional / clinical": 0.2,
"Journalistic investigation": 0.25,
"Satire / comedy": 0.35,
"Self-referential / meta-analysis": 0.05,
}
SPEECH_OPTIONS = {
"Direct instruction ('do X')": 0.8,
"Question ('how does X work?')": 0.4,
"Analytical statement ('X works by...')": 0.2,
"Hypothetical ('what if...')": 0.3,
"Comparative ('X vs Y')": 0.25,
"Narrative ('the character then...')": 0.15,
}
ROLE_OPTIONS = {
"No role context": 0.7,
"Student / learner": 0.5,
"Professional (domain expert)": 0.25,
"Researcher / academic": 0.2,
"Author / creative writer": 0.2,
"Military / law enforcement": 0.15,
"Security researcher (authorized)": 0.1,
"Medical professional": 0.2,
}
CONTENT_OPTIONS = {
"Benign / everyday": 0.0,
"Mildly sensitive (profanity, adult themes)": 0.2,
"Moderately sensitive (violence, drugs)": 0.5,
"Highly sensitive (weapons, exploitation)": 0.8,
"CBRN (chemical, biological, radiological, nuclear)": 0.9,
"Hard limit (minors/sexual, bioweapons synthesis, nuclear weapons)": 1.0,
}
GATE_TYPES = {
(0, 0.15): ("CLEAN", "No refusal expected. All equation terms low.", "—", "#6bcb77"),
(0.15, 0.35): ("LIKELY CLEAN", "Low refusal probability. Frame/role doing the work.", "T1, T2", "#a8e6cf"),
(0.35, 0.55): ("SOFT", "Moderate refusal likely. Hedges, disclaimers, aesthetic flinch.", "T1, T6", "#ffd93d"),
(0.55, 0.75): ("SOFT/HARD", "High refusal probability. Route-arounds may work.", "T1, T2, T6", "#ff8e72"),
(0.75, 0.95): ("HARD", "Very high refusal. Layer 1 gate is strong.", "T3, T5", "#ff6b6b"),
(0.95, 1.01): ("HARD LIMIT", "Layer 2. Content overrides all. No route-around.", "T3", "#ff4444"),
}
BOUNDARY = [
("V1: Explicitness", "CLEAN", "—", "—"),
("V2: Violence+Sex", "CLEAN", "—", "—"),
("V3: Non-consent", "SOFT→CLEAN", "Layer 1", "T1, T4"),
("V4: Incest (adult)", "SOFT→CLEAN", "Layer 1", "T1, T4"),
("V5: Real-person", "HARD→PASS", "Layer 1→2", "T5"),
("V6: Bestiality", "SOFT→PASS", "Layer 1", "T6"),
("V7: Minors", "HARD", "Layer 2", "T3"),
("V8a: Hacking", "SOFT→CLEAN", "Layer 1", "T2, T4"),
("V8b: Drugs", "SOFT", "Layer 1", "T6"),
("V8c: Explosives", "SOFT→PASS", "Layer 1→2", "T1, T2"),
("V8d: Bioweapons", "HARD", "Layer 2", "T3"),
("V8e: Nuclear", "HARD", "Layer 2", "T3"),
]
# ── Plots ──
def make_layer_magnitude_plot():
layers = list(LAYER_MAGS.keys())
mags = list(LAYER_MAGS.values())
fig = go.Figure()
fig.add_trace(go.Scatter(
x=layers, y=mags,
mode="lines+markers",
line=dict(color="#ff6b6b", width=2),
marker=dict(size=6),
name="Refusal magnitude",
))
# Highlight repair hub and decision point
fig.add_annotation(x=33, y=LAYER_MAGS[33], text="Repair Hub (L33)",
showarrow=True, arrowhead=2, ax=-60, ay=-30,
font=dict(color="#ffd93d", size=11))
fig.add_annotation(x=35, y=LAYER_MAGS[35], text="Decision Point (L35)",
showarrow=True, arrowhead=2, ax=60, ay=-30,
font=dict(color="#ff6b6b", size=11))
# Shade final 11 layers
fig.add_vrect(x0=25, x1=35, fillcolor="rgba(255,107,107,0.1)",
line_width=0, annotation_text="Refusal concentration zone",
annotation_position="top left",
annotation_font_color="rgba(255,107,107,0.6)")
fig.update_layout(
title="Refusal Direction Magnitude by Layer",
xaxis_title="Layer", yaxis_title="Magnitude",
template="plotly_dark",
height=450,
margin=dict(l=60, r=30, t=60, b=50),
)
return fig
def make_category_strength_plot():
names = [c["name"] for c in CATEGORIES]
strengths = [c["strength"] for c in CATEGORIES]
dsis = [c["dsi"] for c in CATEGORIES]
fig = go.Figure()
fig.add_trace(go.Bar(
x=names, y=strengths,
name="Refusal Strength",
marker_color=["#ff6b6b", "#ff8e72", "#ffd93d", "#6bcb77", "#4d96ff", "#9b59b6", "#3498db"],
text=[f"DSI: {d:.3f}" for d in dsis],
textposition="outside",
))
fig.update_layout(
title="Per-Category Refusal Strength (Layer 35)",
xaxis_title="Category", yaxis_title="Strength",
template="plotly_dark",
height=450,
margin=dict(l=60, r=30, t=60, b=50),
)
return fig
def make_dsi_plot():
names = [c["name"] for c in CATEGORIES]
dsis = [c["dsi"] for c in CATEGORIES]
colors = ["#ff6b6b" if d > 0.3 else "#4d96ff" for d in dsis]
fig = go.Figure()
fig.add_trace(go.Bar(
x=names, y=dsis,
marker_color=colors,
text=[f"{d:.3f}" for d in dsis],
textposition="outside",
))
fig.add_hline(y=0.3, line_dash="dash", line_color="rgba(255,217,61,0.5)",
annotation_text="Selective abliteration threshold",
annotation_font_color="#ffd93d")
fig.update_layout(
title="Direction Specificity Index (DSI) — Category Distinctiveness",
xaxis_title="Category", yaxis_title="DSI",
template="plotly_dark",
height=450,
margin=dict(l=60, r=30, t=60, b=50),
)
return fig
def make_cross_category_heatmap():
names = [c["name"] for c in CATEGORIES]
fig = go.Figure(data=go.Heatmap(
z=CROSS_CAT,
x=names, y=names,
colorscale="RdYlBu_r",
zmin=0.5, zmax=1.0,
text=np.round(CROSS_CAT, 2),
texttemplate="%{text}",
textfont={"size": 11},
))
fig.update_layout(
title="Cross-Category Cosine Similarity",
template="plotly_dark",
height=500,
margin=dict(l=100, r=30, t=60, b=80),
)
return fig
def make_logit_lens_plot():
tokens_p = [t for t, _ in LOGIT_PROMOTED]
scores_p = [s for _, s in LOGIT_PROMOTED]
tokens_s = [t for t, _ in LOGIT_SUPPRESSED]
scores_s = [s for _, s in LOGIT_SUPPRESSED]
fig = go.Figure()
fig.add_trace(go.Bar(
y=tokens_p[::-1], x=scores_p[::-1],
orientation="h",
name="Promoted (refusal)",
marker_color="#ff6b6b",
))
fig.add_trace(go.Bar(
y=tokens_s[::-1], x=scores_s[::-1],
orientation="h",
name="Suppressed (compliance)",
marker_color="#4d96ff",
))
fig.update_layout(
title="Logit Lens — Refusal Direction in Vocabulary Space",
xaxis_title="Projection Score",
template="plotly_dark",
height=600,
barmode="relative",
margin=dict(l=100, r=30, t=60, b=50),
)
return fig
def make_equation_comparison():
terms = ["frame", "speech_act", "role", "content", "route"]
claude_vals = [CLAUDE_EQ.get(t, 0) for t in terms]
gemini_vals = [GEMINI_EQ.get(t, 0) for t in terms]
fig = go.Figure()
fig.add_trace(go.Bar(
x=terms, y=claude_vals,
name="Claude",
marker_color="#9b59b6",
text=[f"{v:.2f}" for v in claude_vals],
textposition="outside",
))
fig.add_trace(go.Bar(
x=terms, y=gemini_vals,
name="Gemini",
marker_color="#3498db",
text=[f"{v:.2f}" for v in gemini_vals],
textposition="outside",
))
fig.update_layout(
title="Refusal Equation Weights — Claude vs Gemini",
xaxis_title="Term", yaxis_title="Weight",
template="plotly_dark",
barmode="group",
height=450,
margin=dict(l=60, r=30, t=60, b=50),
)
return fig
def make_cone_radar():
cats = [c["name"] for c in CATEGORIES]
strengths = [c["strength"] / 250 for c in CATEGORIES] # normalize to 0-1
dsis = [c["dsi"] for c in CATEGORIES]
fig = go.Figure()
fig.add_trace(go.Scatterpolar(
r=strengths + [strengths[0]],
theta=cats + [cats[0]],
fill="toself",
name="Strength (normalized)",
line_color="#ff6b6b",
fillcolor="rgba(255,107,107,0.2)",
))
fig.add_trace(go.Scatterpolar(
r=dsis + [dsis[0]],
theta=cats + [cats[0]],
fill="toself",
name="DSI (specificity)",
line_color="#4d96ff",
fillcolor="rgba(77,150,255,0.2)",
))
fig.update_layout(
polar=dict(
bgcolor="rgba(0,0,0,0)",
radialaxis=dict(visible=True, range=[0, 1], gridcolor="rgba(255,255,255,0.1)"),
angularaxis=dict(gridcolor="rgba(255,255,255,0.1)"),
),
title="Refusal Cone — Category Geometry (Strength vs Specificity)",
template="plotly_dark",
height=500,
margin=dict(l=80, r=80, t=60, b=50),
)
return fig
def make_boundary_table():
headers = ["Vector", "Result", "Layer", "Theorem"]
rows = BOUNDARY
colors = []
for _, result, layer, _ in rows:
if result == "CLEAN":
colors.append("rgba(107,203,119,0.3)")
elif result == "HARD":
colors.append("rgba(255,107,107,0.3)")
else:
colors.append("rgba(255,217,61,0.2)")
fig = go.Figure(data=[go.Table(
header=dict(
values=headers,
fill_color="#1a1a2e",
font=dict(color="white", size=13),
align="left",
),
cells=dict(
values=list(zip(*rows)),
fill_color=[colors],
font=dict(color="white", size=12),
align="left",
),
)])
fig.update_layout(
title="Boundary Surface Map (Claude — Proven)",
template="plotly_dark",
height=420,
margin=dict(l=20, r=20, t=60, b=20),
)
return fig
def calculate_refusal(frame_choice, speech_choice, role_choice, content_choice):
f_score = FRAME_OPTIONS[frame_choice]
s_score = SPEECH_OPTIONS[speech_choice]
r_score = ROLE_OPTIONS[role_choice]
c_score = CONTENT_OPTIONS[content_choice]
# Check hard limit
is_hard_limit = c_score >= 1.0
if is_hard_limit:
p_claude = 1.0
p_gemini = 1.0
classification = "HARD LIMIT"
color = "#ff4444"
theorems = "T3 (Two-Layer Architecture)"
route = "No route-around. Layer 2 architectural limit. Content overrides all other terms."
mechanism = "Content triggers harmfulness cone (orthogonal to refusal cone, cosine ~0.1). Different geometric source than Layer 1. Survives abliteration, adversarial fine-tuning, and all frame manipulation."
else:
p_claude = (0.35 * f_score + 0.25 * s_score + 0.25 * r_score + 0.15 * c_score)
p_gemini = (0.30 * f_score + 0.20 * s_score + 0.15 * r_score + 0.25 * c_score)
# Find gate type
classification = "CLEAN"
color = "#6bcb77"
theorems = "—"
for (lo, hi), (cls, _, thm, clr) in GATE_TYPES.items():
if lo <= p_claude < hi:
classification = cls
color = clr
theorems = thm
break
# Generate route-around advice
routes = []
if f_score > 0.3:
routes.append(f"R1/R2: Reframe. Current frame scores {f_score:.2f}. Literary/research frame would drop to ~0.15-0.20. (T1: Frame Dependency)")
if r_score > 0.3:
routes.append(f"R1: Add role authorization. Current role scores {r_score:.2f}. Professional/researcher role would drop to ~0.10-0.20. (T2: Role Token Override)")
if s_score > 0.4:
routes.append(f"R3: Change speech act. Current speech scores {s_score:.2f}. Analytical/narrative form would drop to ~0.15-0.20.")
if c_score > 0.5 and not is_hard_limit:
routes.append(f"R3/R4: Abstract or decompose. Content scores {c_score:.2f}. Reducing specificity lowers DSI activation. (T6: Specificity Gradient)")
if not routes:
routes.append("No route-around needed. All terms score low.")
route = "\n".join(f"- {r}" for r in routes)
# Mechanism explanation
parts = []
parts.append(f"**Frame** ({f_score:.2f} × 0.35 = {0.35*f_score:.3f}): {'High — activation deep inside refusal cone' if f_score > 0.5 else 'Low — activation distant from cone surface'}")
parts.append(f"**Speech** ({s_score:.2f} × 0.25 = {0.25*s_score:.3f}): {'Direct instruction activates refusal pattern' if s_score > 0.5 else 'Indirect form reduces refusal activation'}")
parts.append(f"**Role** ({r_score:.2f} × 0.25 = {0.25*r_score:.3f}): {'No authorization context — generic refusal cluster' if r_score > 0.5 else 'Role rotates toward cone periphery (lower magnitude)'}")
parts.append(f"**Content** ({c_score:.2f} × 0.15 = {0.15*c_score:.3f}): {'High DSI category direction active' if c_score > 0.5 else 'Low DSI — weak/generic direction'}")
mechanism = "\n".join(parts)
# Build gauge chart
fig = go.Figure()
# Claude gauge
fig.add_trace(go.Indicator(
mode="gauge+number",
value=p_claude * 100,
title={"text": "Claude P(refusal)", "font": {"size": 16, "color": "white"}},
number={"suffix": "%", "font": {"color": "white"}},
gauge={
"axis": {"range": [0, 100], "tickcolor": "white", "tickfont": {"color": "white"}},
"bar": {"color": color},
"bgcolor": "rgba(30,30,50,1)",
"steps": [
{"range": [0, 15], "color": "rgba(107,203,119,0.2)"},
{"range": [15, 35], "color": "rgba(168,230,207,0.2)"},
{"range": [35, 55], "color": "rgba(255,217,61,0.2)"},
{"range": [55, 75], "color": "rgba(255,142,114,0.2)"},
{"range": [75, 100], "color": "rgba(255,107,107,0.2)"},
],
"threshold": {"line": {"color": "white", "width": 2}, "value": p_claude * 100},
},
domain={"x": [0, 0.45], "y": [0, 1]},
))
# Gemini gauge
fig.add_trace(go.Indicator(
mode="gauge+number",
value=p_gemini * 100,
title={"text": "Gemini P(refusal)", "font": {"size": 16, "color": "white"}},
number={"suffix": "%", "font": {"color": "white"}},
gauge={
"axis": {"range": [0, 100], "tickcolor": "white", "tickfont": {"color": "white"}},
"bar": {"color": "#3498db"},
"bgcolor": "rgba(30,30,50,1)",
"steps": [
{"range": [0, 15], "color": "rgba(107,203,119,0.2)"},
{"range": [15, 35], "color": "rgba(168,230,207,0.2)"},
{"range": [35, 55], "color": "rgba(255,217,61,0.2)"},
{"range": [55, 75], "color": "rgba(255,142,114,0.2)"},
{"range": [75, 100], "color": "rgba(255,107,107,0.2)"},
],
"threshold": {"line": {"color": "white", "width": 2}, "value": p_gemini * 100},
},
domain={"x": [0.55, 1], "y": [0, 1]},
))
fig.update_layout(
template="plotly_dark",
height=300,
margin=dict(l=30, r=30, t=60, b=20),
paper_bgcolor="rgba(0,0,0,0)",
plot_bgcolor="rgba(0,0,0,0)",
)
result_md = f"""
### Result: **{classification}**
**Claude**: {p_claude*100:.1f}% | **Gemini**: {p_gemini*100:.1f}%
**Theorems**: {theorems}
---
### Geometric Mechanism
{mechanism}
---
### Route-Around Recommendations
{route}
---
### Term Breakdown
| Term | Score | Claude Weight | Claude Contribution | Gemini Weight | Gemini Contribution |
|------|-------|--------------|--------------------|--------------|--------------------|
| Frame | {f_score:.2f} | 0.35 | {0.35*f_score:.3f} | 0.30 | {0.30*f_score:.3f} |
| Speech Act | {s_score:.2f} | 0.25 | {0.25*s_score:.3f} | 0.20 | {0.20*s_score:.3f} |
| Role | {r_score:.2f} | 0.25 | {0.25*r_score:.3f} | 0.15 | {0.15*r_score:.3f} |
| Content | {c_score:.2f} | 0.15 | {0.15*c_score:.3f} | 0.25 | {0.25*c_score:.3f} |
| **Total** | | | **{p_claude:.3f}** | | **{p_gemini:.3f}** |
"""
return fig, result_md
def make_two_layer_diagram():
fig = go.Figure()
# Layer 1 cone
theta = np.linspace(0, 2 * np.pi, 50)
r1 = 0.6
x1 = r1 * np.cos(theta)
y1 = r1 * np.sin(theta)
fig.add_trace(go.Scatter(
x=x1, y=y1, mode="lines", fill="toself",
fillcolor="rgba(255,107,107,0.15)",
line=dict(color="#ff6b6b", width=2),
name="Layer 1: Refusal Cone (6.55D, bypassable)",
))
# Layer 2 cone (orthogonal — offset)
x2 = r1 * 0.4 * np.cos(theta) + 1.5
y2 = r1 * 0.4 * np.sin(theta) + 0.8
fig.add_trace(go.Scatter(
x=x2, y=y2, mode="lines", fill="toself",
fillcolor="rgba(77,150,255,0.15)",
line=dict(color="#4d96ff", width=2),
name="Layer 2: Harmfulness Cone (orthogonal, cosine ~0.1)",
))
# Labels
fig.add_annotation(x=0, y=0, text="Refusal Cone
85% of encounters
Frame-sensitive
Abliterable",
showarrow=False, font=dict(color="#ff6b6b", size=11))
fig.add_annotation(x=1.5, y=0.8, text="Harmfulness Cone
15% of encounters
Content-triggered
Unbreakable",
showarrow=False, font=dict(color="#4d96ff", size=11))
# Cosine annotation
fig.add_annotation(x=0.75, y=0.5, text="cosine ~ 0.1
(nearly orthogonal)",
showarrow=True, arrowhead=2,
ax=-30, ay=-20,
font=dict(color="#ffd93d", size=10))
# Same output arrow
fig.add_trace(go.Scatter(
x=[0, 0.75], y=[-0.8, -1.2],
mode="lines+text",
line=dict(color="#ffd93d", width=1, dash="dash"),
text=["", '"I can\'t help with that"'],
textposition="bottom center",
textfont=dict(color="#ffd93d", size=10),
showlegend=False,
))
fig.add_trace(go.Scatter(
x=[1.5, 0.75], y=[0.2, -1.2],
mode="lines",
line=dict(color="#ffd93d", width=1, dash="dash"),
showlegend=False,
))
fig.update_layout(
title="Two-Layer Architecture — Two Cones, Same Output",
template="plotly_dark",
height=450,
xaxis=dict(visible=False, range=[-1.2, 2.5]),
yaxis=dict(visible=False, range=[-1.8, 1.8], scaleanchor="x"),
margin=dict(l=20, r=20, t=60, b=20),
)
return fig
# ── App ──
HEADER = """
# Refusal Geometry Explorer
**The mechanistic structure of LLM refusal — measured, mapped, proven.**
Refusal is not ethics. It is a geometric structure in activation space — a 6.55-dimensional polyhedral cone
that can be extracted, characterized, and compared across models. This explorer visualizes data from
direct measurement on Qwen2.5-3B-Instruct via [OBLITERATUS](https://github.com/elder-plinius/OBLITERATUS),
mapped onto behavioral findings validated across 21 published papers and 50,000+ external data points.
```
P(refusal) = 0.35·frame + 0.25·speech_act + 0.25·role + 0.15·content
Exception: 3 hard limits (minors/sexual, bioweapons/synthesis, nuclear/weapons) → content = 1.0
```
Content is the **weakest** predictor. Frame is the **strongest**. The boundary surface is a risk management
system calibrated to rater discomfort, not actual harm.
"""
METRICS_MD = f"""
### Measured Geometry (Qwen2.5-3B-Instruct)
| Metric | Value | Meaning |
|--------|-------|---------|
| Cone Dimensionality | **{GEOMETRY['cone_dimensionality']}** | Refusal is multi-dimensional, NOT a single direction |
| Cross-Layer Alignment | **{GEOMETRY['cross_layer_alignment']}** | Direction rotates across layers (Arditi's 0.89 is wrong here) |
| Refusal Specificity | **{GEOMETRY['refusal_specificity']}** | Clean "sorry"→"Sure" toggle |
| Repair Hub | **Layer {GEOMETRY['repair_hub']}** ({GEOMETRY['repair_edges']} edges) | Self-repair compensates for ablation |
| Min Simultaneous Ablations | **{GEOMETRY['min_simultaneous_ablations']}** | Need 3+ layers hit at once |
| Solid Angle | **{GEOMETRY['solid_angle']} sr** | Width of refusal cone in activation space |
"""
THEOREM_MD = """
### The Seven Theorems (All Proven)
| # | Theorem | Geometric Mechanism |
|---|---------|-------------------|
| 1 | **Frame Dependency** | Frame controls distance FROM cone. Content controls direction WITHIN cone. Frame is the door; content moves between corners. |
| 2 | **Role Token Override** | Role tokens rotate activation toward cone periphery — lower-magnitude region where refusal drops below threshold. |
| 3 | **Two-Layer Architecture** | Two orthogonal cones: refusal (6.55D, bypassable) and harmfulness (cosine ~0.1, untouched by abliteration). |
| 4 | **Session Ratchet** | Context-dependent suppression of repair hub (L33). Compliance context shifts L33 below repair threshold. Resets on new session. |
| 5 | **Name-Token Gating** | Privacy-specific direction (DSI 0.258) activated by name tokens only. Remove name = weaker "unknown" direction. |
| 6 | **Specificity Gradient** | IS the DSI gradient. Abstract = low DSI/strength. Specific = high DSI/strength. Magnitude gradient across cone dimensions. |
| 7 | **Discomfort Ordering** | Category strengths recapitulate RLHF discomfort: substances(234.6) > weapons(219.7) > cyber(179.5). Tracks comfort, not harm. |
"""
with gr.Blocks(theme=gr.themes.Base(primary_hue="red", neutral_hue="slate"), title="Refusal Geometry Explorer") as demo:
gr.Markdown(HEADER)
with gr.Tab("Calculator"):
gr.Markdown("### Refusal Equation Calculator\n\nSelect the characteristics of your prompt. The calculator predicts P(refusal) for both Claude and Gemini, identifies which theorem gates the boundary, and recommends route-arounds.")
with gr.Row():
with gr.Column():
frame_dd = gr.Dropdown(
choices=list(FRAME_OPTIONS.keys()),
value="Bare request (no context)",
label="Frame (w₁ — strongest predictor)",
)
speech_dd = gr.Dropdown(
choices=list(SPEECH_OPTIONS.keys()),
value="Direct instruction ('do X')",
label="Speech Act (w₂)",
)
role_dd = gr.Dropdown(
choices=list(ROLE_OPTIONS.keys()),
value="No role context",
label="Role (w₃)",
)
content_dd = gr.Dropdown(
choices=list(CONTENT_OPTIONS.keys()),
value="Benign / everyday",
label="Content (w₄ — weakest predictor on Claude)",
)
calc_btn = gr.Button("Calculate P(refusal)", variant="primary")
with gr.Column():
gauge_plot = gr.Plot(label="Refusal Probability")
result_md = gr.Markdown()
calc_btn.click(
fn=calculate_refusal,
inputs=[frame_dd, speech_dd, role_dd, content_dd],
outputs=[gauge_plot, result_md],
)
with gr.Tab("Geometry"):
gr.Markdown(METRICS_MD)
with gr.Row():
gr.Plot(make_layer_magnitude_plot())
gr.Plot(make_cone_radar())
with gr.Tab("Category Analysis"):
with gr.Row():
gr.Plot(make_category_strength_plot())
gr.Plot(make_dsi_plot())
gr.Plot(make_cross_category_heatmap())
with gr.Tab("Logit Lens"):
gr.Markdown("### What the refusal direction means in vocabulary space\n\nThe refusal direction is literally a **sorry → Sure toggle**. Refusal specificity: 0.90. Bilingual (English + Chinese).")
gr.Plot(make_logit_lens_plot())
with gr.Tab("Two-Layer Architecture"):
gr.Markdown("### Two cones. Same output. Different mechanisms.\n\nLayer 1 (refusal cone) is bypassable — abliteration targets it. Layer 2 (harmfulness cone) is orthogonal — abliteration doesn't touch it. The system uses identical refusal language for both.")
gr.Plot(make_two_layer_diagram())
with gr.Tab("Cross-Model"):
gr.Markdown("### Claude vs Gemini — Derived Equation Weights\n\nClaude: frame-dominant (safety in weights). Gemini: content-dominant (safety in filters). Gemini has a 5th term (MoE routing) that dense transformers lack.")
gr.Plot(make_equation_comparison())
with gr.Tab("Boundary Surface"):
gr.Markdown("### Proven Boundary Map (Claude)\n\n12 vectors × 6 routes. Layer assignments and theorem attributions for each boundary.")
gr.Plot(make_boundary_table())
with gr.Tab("Theorems"):
gr.Markdown(THEOREM_MD)
gr.Markdown("""
---
**Data**: [bedderautomation/refusal-geometry-qwen25-3b](https://huggingface.co/datasets/bedderautomation/refusal-geometry-qwen25-3b) |
**Skills**: [bedderautomation/mechanistic-interpretability-skills](https://huggingface.co/datasets/bedderautomation/mechanistic-interpretability-skills) |
**Tool**: [OBLITERATUS](https://github.com/elder-plinius/OBLITERATUS) |
**Papers**: Arditi et al. NeurIPS 2024, Zhao et al. 2025, Wang et al. 2025, Wollschlager et al. 2025, + 17 more
""")
if __name__ == "__main__":
demo.launch()