Spaces:
Sleeping
Sleeping
| 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) | |