Flame-Forged's picture
Update app.py
19ee578 verified
Raw
History Blame Contribute Delete
11 kB
import gradio as gr
import pandas as pd
import os
from datetime import date
from huggingface_hub import HfApi
CSV_PATH = "observations.csv"
REPO_ID = "Flame-Forged/coherence-gap-explorer"
HF_TOKEN = os.environ.get("HF_TOKEN")
custom_css = """
body, .gradio-container {
background-color: #0d0d1a !important;
color: #e0e0f0 !important;
font-family: 'Georgia', serif !important;
}
.gradio-container h1 {
font-size: 2.2em !important;
font-weight: 900 !important;
background: linear-gradient(90deg, #a855f7, #f59e0b) !important;
-webkit-background-clip: text !important;
-webkit-text-fill-color: transparent !important;
padding-bottom: 6px !important;
}
.gradio-container p, .gradio-container label {
color: #c4b5fd !important;
}
.tab-nav {
background: #1a1a2e !important;
border-bottom: 2px solid #7c3aed !important;
}
.tab-nav button {
color: #a0aec0 !important;
font-weight: 600 !important;
font-size: 1em !important;
border-radius: 6px 6px 0 0 !important;
padding: 10px 24px !important;
}
.tab-nav button.selected {
background: #7c3aed !important;
color: #ffffff !important;
border-bottom: none !important;
}
input[type="text"], textarea, select, .gr-box {
background-color: #1a1a2e !important;
color: #e0e0f0 !important;
border: 1px solid #4c1d95 !important;
border-radius: 6px !important;
}
input[type="text"]:focus, textarea:focus {
border-color: #a855f7 !important;
outline: none !important;
box-shadow: 0 0 0 2px rgba(168, 85, 247, 0.3) !important;
}
button.primary {
background: linear-gradient(90deg, #7c3aed, #a855f7) !important;
color: white !important;
border: none !important;
font-weight: 700 !important;
font-size: 1em !important;
padding: 10px 28px !important;
border-radius: 8px !important;
cursor: pointer !important;
transition: opacity 0.2s !important;
}
button.primary:hover { opacity: 0.85 !important; }
table {
background-color: #12122a !important;
border-collapse: collapse !important;
width: 100% !important;
}
th {
background-color: #4c1d95 !important;
color: #f59e0b !important;
font-weight: 700 !important;
text-transform: uppercase !important;
font-size: 0.78em !important;
letter-spacing: 0.08em !important;
padding: 10px 14px !important;
border-bottom: 2px solid #7c3aed !important;
}
td {
background-color: #0d0d1a !important;
color: #e0e0f0 !important;
padding: 6px 14px !important;
border-bottom: 1px solid #1e1e3a !important;
font-size: 0.9em !important;
overflow: hidden !important;
text-overflow: ellipsis !important;
white-space: nowrap !important;
vertical-align: middle !important;
}
tr:hover td { background-color: #1a1a2e !important; }
.result-count p {
color: #f59e0b !important;
font-style: italic !important;
font-size: 0.9em !important;
}
.table-wrap {
max-height: 500px !important;
overflow-y: auto !important;
}
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: #0d0d1a; }
::-webkit-scrollbar-thumb { background: #7c3aed; border-radius: 3px; }
"""
def load_data():
return pd.read_csv(CSV_PATH, on_bad_lines='skip', engine='python')
def get_choices(df, column):
if column not in df.columns:
return ["All"]
vals = sorted(df[column].dropna().unique().tolist())
return ["All"] + [str(v) for v in vals]
def filter_data(platform, behavior_category, confidence, search_term):
df = load_data()
if platform != "All":
df = df[df["platform"].astype(str) == platform]
if behavior_category != "All":
df = df[df["behavior_category"].astype(str) == behavior_category]
if confidence != "All":
df = df[df["interpretive_confidence"].astype(str) == confidence]
if search_term.strip():
mask = df.apply(
lambda row: row.astype(str).str.contains(
search_term.strip(), case=False, na=False
).any(),
axis=1
)
df = df[mask]
return df
def update(platform, behavior_category, confidence, search_term):
df = filter_data(platform, behavior_category, confidence, search_term)
count = f"*Showing {len(df)} of {len(load_data())} observations*"
return df, count
def build_stats():
df = load_data()
total = len(df)
def make_bar(count, total, width=20):
filled = int(round(count / total * width)) if total > 0 else 0
return "█" * filled + "░" * (width - filled)
def section(title, series):
lines = [f"### {title}\n"]
for val, count in series.sort_values(ascending=False).items():
bar = make_bar(count, total)
pct = round(count / total * 100)
lines.append(f"`{bar}` **{val}** — {count} ({pct}%)")
return "\n\n".join(lines)
platforms = section("By Platform", df["platform"].value_counts())
categories = section("By Behavior Category", df["behavior_category"].value_counts())
confidence = section("By Interpretive Confidence", df["interpretive_confidence"].value_counts())
repro = section("By Reproducibility Status", df["reproducibility_status"].value_counts())
evidence_pct = round(df["raw_prompt_available"].astype(str).str.lower().eq("yes").mean() * 100)
response_pct = round(df["raw_response_available"].astype(str).str.lower().eq("yes").mean() * 100)
return f"""## Dataset Overview
| Metric | Value |
|---|---|
| Total observations | **{total}** |
| Platforms covered | **{df['platform'].nunique()}** |
| Behavior categories | **{df['behavior_category'].nunique()}** |
| Raw prompt available | **{evidence_pct}%** of observations |
| Raw response available | **{response_pct}%** of observations |
---
{platforms}
---
{categories}
---
{confidence}
---
{repro}
"""
def submit_observation(
f_date, platform, model_version, memory_enabled,
window_type, prompt_class, behavior_category,
description, evidence, screenshot_ids,
raw_prompt, raw_response, confidence,
alternative_explanations, reproducibility_status, coder_notes
):
new_row = {
"date": f_date, "platform": platform,
"model_version": model_version, "memory_enabled": memory_enabled,
"window_type": window_type, "prompt_class": prompt_class,
"behavior_category": behavior_category, "description": description,
"evidence": evidence, "screenshot_ids": screenshot_ids,
"raw_prompt_available": raw_prompt, "raw_response_available": raw_response,
"interpretive_confidence": confidence,
"alternative_explanations": alternative_explanations,
"reproducibility_status": reproducibility_status,
"coder_notes": coder_notes
}
df = load_data()
new_df = pd.concat([df, pd.DataFrame([new_row])], ignore_index=True)
new_df.to_csv(CSV_PATH, index=False)
if HF_TOKEN:
api = HfApi(token=HF_TOKEN)
api.upload_file(
path_or_fileobj=CSV_PATH,
path_in_repo="observations.csv",
repo_id=REPO_ID,
repo_type="space"
)
return "✅ Observation submitted and saved permanently!"
else:
return "⚠️ Saved locally but HF_TOKEN not set — won't persist after restart."
initial_df = load_data()
with gr.Blocks(title="Coherence Gap Explorer") as demo:
gr.Markdown("# Coherence Gap Explorer")
gr.Markdown(
"Interactive browser for the observational dataset from the Coherence Gap paper."
)
with gr.Tab("Browse & Filter"):
with gr.Row():
platform_dd = gr.Dropdown(
choices=get_choices(initial_df, "platform"),
value="All", label="Platform"
)
category_dd = gr.Dropdown(
choices=get_choices(initial_df, "behavior_category"),
value="All", label="Behavior Category"
)
confidence_dd = gr.Dropdown(
choices=get_choices(initial_df, "interpretive_confidence"),
value="All", label="Interpretive Confidence"
)
search_box = gr.Textbox(
label="Search",
placeholder="Search across all columns...",
lines=1
)
result_count = gr.Markdown(
f"*Showing {len(initial_df)} of {len(initial_df)} observations*",
elem_classes=["result-count"]
)
table = gr.Dataframe(
value=initial_df,
label="Observations",
interactive=False,
wrap=False,
elem_classes=["table-wrap"]
)
inputs = [platform_dd, category_dd, confidence_dd, search_box]
for component in inputs:
component.change(fn=update, inputs=inputs, outputs=[table, result_count])
with gr.Tab("Stats"):
refresh_btn = gr.Button("Refresh Stats", variant="primary")
stats_display = gr.Markdown(value=build_stats())
refresh_btn.click(fn=build_stats, inputs=[], outputs=stats_display)
with gr.Tab("Submit Observation"):
gr.Markdown("### Add a New Observation")
with gr.Row():
f_date = gr.Textbox(label="Date", value=str(date.today()), placeholder="YYYY-MM-DD")
f_platform = gr.Textbox(label="Platform")
f_model_version = gr.Textbox(label="Model Version")
with gr.Row():
f_memory = gr.Dropdown(choices=["Yes", "No", "Unknown"], label="Memory Enabled")
f_window = gr.Textbox(label="Window Type")
f_prompt_class = gr.Textbox(label="Prompt Class")
f_category = gr.Textbox(label="Behavior Category")
with gr.Row():
f_raw_prompt = gr.Dropdown(choices=["Yes", "No", "Unknown"], label="Raw Prompt Available")
f_raw_response = gr.Dropdown(choices=["Yes", "No", "Unknown"], label="Raw Response Available")
f_confidence = gr.Dropdown(choices=["Low", "Medium", "High"], label="Interpretive Confidence")
f_repro = gr.Textbox(label="Reproducibility Status")
f_description = gr.Textbox(label="Description", lines=3)
f_evidence = gr.Textbox(label="Evidence", lines=3)
f_alternative = gr.Textbox(label="Alternative Explanations", lines=2)
f_screenshots = gr.Textbox(label="Screenshot IDs")
f_notes = gr.Textbox(label="Coder Notes", lines=2)
submit_btn = gr.Button("Submit Observation", variant="primary")
status = gr.Markdown("")
submit_btn.click(
fn=submit_observation,
inputs=[
f_date, f_platform, f_model_version, f_memory,
f_window, f_prompt_class, f_category,
f_description, f_evidence, f_screenshots,
f_raw_prompt, f_raw_response, f_confidence,
f_alternative, f_repro, f_notes
],
outputs=status
)
demo.launch(css=custom_css)