Spaces:
Running
Running
Upload 5 files
Browse files- ui/__init__.py +2 -0
- ui/sidebar.py +35 -0
- ui/tab_config.py +106 -0
- ui/tab_policy.py +70 -0
- ui/tab_testing.py +179 -0
ui/__init__.py
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""UI package for moderation interface."""
|
| 2 |
+
|
ui/sidebar.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Sidebar UI component with app description and authentication."""
|
| 2 |
+
|
| 3 |
+
import os
|
| 4 |
+
import sys
|
| 5 |
+
|
| 6 |
+
import gradio as gr
|
| 7 |
+
|
| 8 |
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def build_sidebar() -> dict:
|
| 12 |
+
"""Build the sidebar UI with app description and login."""
|
| 13 |
+
with gr.Sidebar():
|
| 14 |
+
gr.Markdown("## About")
|
| 15 |
+
gr.Markdown(
|
| 16 |
+
"""
|
| 17 |
+
This interface allows you to test moderation models with custom content policies.
|
| 18 |
+
|
| 19 |
+
**🧪 Testing Tab**: Enter content to test against your policy. View model predictions, categories, reasoning traces, and raw responses.
|
| 20 |
+
|
| 21 |
+
**📋 Policy Definition Tab**: Define your content policy by uploading a markdown file, entering it manually, or selecting from preset examples.
|
| 22 |
+
|
| 23 |
+
**⚙️ Configuration Tab**: Select models, adjust generation parameters, and customize system prompts and response formats.
|
| 24 |
+
"""
|
| 25 |
+
)
|
| 26 |
+
|
| 27 |
+
gr.Markdown("---")
|
| 28 |
+
gr.Markdown("### Authentication")
|
| 29 |
+
login_button = gr.LoginButton(value="Log in to Hugging Face")
|
| 30 |
+
gr.Markdown("*Log in with your Hugging Face to be able to query models through Inference Providers.*")
|
| 31 |
+
|
| 32 |
+
return {
|
| 33 |
+
"login_button": login_button,
|
| 34 |
+
}
|
| 35 |
+
|
ui/tab_config.py
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Configuration tab UI components."""
|
| 2 |
+
|
| 3 |
+
import os
|
| 4 |
+
import sys
|
| 5 |
+
|
| 6 |
+
import gradio as gr
|
| 7 |
+
|
| 8 |
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
| 9 |
+
|
| 10 |
+
from utils.constants import MODELS, REASONING_EFFORTS, RESPONSE_FORMAT
|
| 11 |
+
from utils.model_interface import extract_model_id, get_default_system_prompt
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def build_config_tab() -> dict:
|
| 15 |
+
"""Build the configuration tab UI."""
|
| 16 |
+
with gr.Tab("⚙️ Configuration"):
|
| 17 |
+
gr.Markdown("### Model Selection")
|
| 18 |
+
|
| 19 |
+
model_choices = [f"{m['name']} ({m['id']})" for m in MODELS]
|
| 20 |
+
model_dropdown = gr.Dropdown(label="Model", choices=model_choices, value=model_choices[0])
|
| 21 |
+
reasoning_effort = gr.Dropdown(label="Reasoning Effort (GPT-OSS only)", choices=REASONING_EFFORTS, value="Low", visible=True)
|
| 22 |
+
|
| 23 |
+
def update_reasoning_visibility(choice):
|
| 24 |
+
"""Update reasoning effort visibility based on selected model."""
|
| 25 |
+
if not choice:
|
| 26 |
+
return gr.update(visible=False)
|
| 27 |
+
model_id = extract_model_id(choice)
|
| 28 |
+
return gr.update(visible=model_id.startswith("openai/gpt-oss") if model_id else False)
|
| 29 |
+
|
| 30 |
+
def update_system_prompt(model_choice, reasoning_effort_val):
|
| 31 |
+
"""Update system prompt when model or reasoning effort changes."""
|
| 32 |
+
if not model_choice:
|
| 33 |
+
return ""
|
| 34 |
+
model_id = extract_model_id(model_choice)
|
| 35 |
+
return get_default_system_prompt(model_id, reasoning_effort_val)
|
| 36 |
+
|
| 37 |
+
# Initialize system prompt with default for first model
|
| 38 |
+
initial_model_id = extract_model_id(model_choices[0])
|
| 39 |
+
initial_system_prompt = get_default_system_prompt(initial_model_id, "Low")
|
| 40 |
+
|
| 41 |
+
gr.Markdown("---")
|
| 42 |
+
gr.Markdown("### System Prompt & Response Format")
|
| 43 |
+
gr.Markdown("*Edit the prompts below. System prompt varies by model type; response format is used for GPT-OSS developer channel and Qwen.*")
|
| 44 |
+
|
| 45 |
+
with gr.Row():
|
| 46 |
+
with gr.Column():
|
| 47 |
+
system_prompt_textbox = gr.Textbox(
|
| 48 |
+
label="System Prompt",
|
| 49 |
+
placeholder="System prompt will be auto-generated based on model...",
|
| 50 |
+
lines=10,
|
| 51 |
+
value=initial_system_prompt,
|
| 52 |
+
interactive=True,
|
| 53 |
+
)
|
| 54 |
+
|
| 55 |
+
with gr.Column():
|
| 56 |
+
response_format_textbox = gr.Textbox(
|
| 57 |
+
label="Response Format",
|
| 58 |
+
placeholder="Response format instructions...",
|
| 59 |
+
lines=10,
|
| 60 |
+
value=RESPONSE_FORMAT,
|
| 61 |
+
interactive=True,
|
| 62 |
+
)
|
| 63 |
+
|
| 64 |
+
gr.Markdown("*Edit the prompts above. Values are used directly when running tests.*")
|
| 65 |
+
|
| 66 |
+
def update_on_model_change(choice, reasoning_effort_val):
|
| 67 |
+
"""Update both reasoning visibility and system prompt when model changes."""
|
| 68 |
+
visibility_update = update_reasoning_visibility(choice)
|
| 69 |
+
system_prompt_update = update_system_prompt(choice, reasoning_effort_val)
|
| 70 |
+
return visibility_update, system_prompt_update
|
| 71 |
+
|
| 72 |
+
# Update reasoning visibility and system prompt when model changes
|
| 73 |
+
model_dropdown.change(
|
| 74 |
+
update_on_model_change,
|
| 75 |
+
inputs=[model_dropdown, reasoning_effort],
|
| 76 |
+
outputs=[reasoning_effort, system_prompt_textbox],
|
| 77 |
+
)
|
| 78 |
+
|
| 79 |
+
# Update system prompt when reasoning effort changes (for GPT-OSS)
|
| 80 |
+
def update_on_reasoning_change(choice, effort):
|
| 81 |
+
"""Update system prompt when reasoning effort changes."""
|
| 82 |
+
if not choice:
|
| 83 |
+
return ""
|
| 84 |
+
return update_system_prompt(choice, effort)
|
| 85 |
+
|
| 86 |
+
reasoning_effort.change(
|
| 87 |
+
update_on_reasoning_change,
|
| 88 |
+
inputs=[model_dropdown, reasoning_effort],
|
| 89 |
+
outputs=system_prompt_textbox,
|
| 90 |
+
)
|
| 91 |
+
|
| 92 |
+
gr.Markdown("---")
|
| 93 |
+
with gr.Accordion("Generation Parameters", open=False):
|
| 94 |
+
max_tokens = gr.Number(label="Max Tokens", value=4096, precision=0)
|
| 95 |
+
temperature = gr.Slider(label="Temperature", minimum=0.0, maximum=1.0, value=0.1, step=0.1)
|
| 96 |
+
top_p = gr.Slider(label="Top P", minimum=0.0, maximum=1.0, value=0.9, step=0.1)
|
| 97 |
+
|
| 98 |
+
return {
|
| 99 |
+
"model_dropdown": model_dropdown,
|
| 100 |
+
"reasoning_effort": reasoning_effort,
|
| 101 |
+
"system_prompt_textbox": system_prompt_textbox,
|
| 102 |
+
"response_format_textbox": response_format_textbox,
|
| 103 |
+
"max_tokens": max_tokens,
|
| 104 |
+
"temperature": temperature,
|
| 105 |
+
"top_p": top_p,
|
| 106 |
+
}
|
ui/tab_policy.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Policy definition tab UI components."""
|
| 2 |
+
|
| 3 |
+
import os
|
| 4 |
+
import sys
|
| 5 |
+
|
| 6 |
+
import gradio as gr
|
| 7 |
+
|
| 8 |
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
| 9 |
+
|
| 10 |
+
from utils.helpers import load_policy_from_file, load_preset_policy
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def build_policy_tab(base_dir: str) -> dict:
|
| 14 |
+
"""Build the policy definition tab UI."""
|
| 15 |
+
with gr.Tab("📋 Policy Definition"):
|
| 16 |
+
input_method = gr.Radio(label="Input Method", choices=["Upload Markdown", "Enter Manually", "Select Preset"], value="Select Preset")
|
| 17 |
+
|
| 18 |
+
upload_file = gr.File(label="Upload Markdown File", file_types=[".md"], visible=False)
|
| 19 |
+
upload_preview = gr.Textbox(label="File Preview", lines=10, interactive=False, visible=False)
|
| 20 |
+
load_upload_btn = gr.Button("Load Policy", visible=False)
|
| 21 |
+
|
| 22 |
+
manual_text = gr.Textbox(label="Policy Text", placeholder="Enter policy markdown...", lines=20, visible=False)
|
| 23 |
+
save_manual_btn = gr.Button("Save Policy", visible=False)
|
| 24 |
+
|
| 25 |
+
preset_dropdown = gr.Dropdown(
|
| 26 |
+
label="Select Preset", choices=["Hate Speech Policy", "Violence Policy", "Toxicity Policy"], value="Hate Speech Policy", visible=True
|
| 27 |
+
)
|
| 28 |
+
preset_preview = gr.Markdown(value="*Select a preset to preview*", visible=True)
|
| 29 |
+
load_preset_btn = gr.Button("Load Preset", visible=True)
|
| 30 |
+
|
| 31 |
+
gr.Markdown("---")
|
| 32 |
+
gr.Markdown("### Current Policy")
|
| 33 |
+
current_policy = gr.Markdown(value="*No policy loaded*")
|
| 34 |
+
clear_policy_btn = gr.Button("Clear Policy", variant="secondary")
|
| 35 |
+
|
| 36 |
+
current_policy_state = gr.State(value="")
|
| 37 |
+
|
| 38 |
+
def update_ui(method):
|
| 39 |
+
return (
|
| 40 |
+
gr.update(visible=(method == "Upload Markdown")),
|
| 41 |
+
gr.update(visible=(method == "Upload Markdown")),
|
| 42 |
+
gr.update(visible=(method == "Upload Markdown")),
|
| 43 |
+
gr.update(visible=(method == "Enter Manually")),
|
| 44 |
+
gr.update(visible=(method == "Enter Manually")),
|
| 45 |
+
gr.update(visible=(method == "Select Preset")),
|
| 46 |
+
gr.update(visible=(method == "Select Preset")),
|
| 47 |
+
gr.update(visible=(method == "Select Preset")),
|
| 48 |
+
)
|
| 49 |
+
|
| 50 |
+
input_method.change(update_ui, inputs=input_method, outputs=[upload_file, upload_preview, load_upload_btn, manual_text, save_manual_btn, preset_dropdown, preset_preview, load_preset_btn])
|
| 51 |
+
|
| 52 |
+
# Policy loading handlers
|
| 53 |
+
load_preset_btn.click(
|
| 54 |
+
lambda name: load_preset_policy(name, base_dir),
|
| 55 |
+
inputs=preset_dropdown,
|
| 56 |
+
outputs=[current_policy_state, current_policy],
|
| 57 |
+
)
|
| 58 |
+
load_upload_btn.click(
|
| 59 |
+
lambda f: load_policy_from_file(f.name) if f else ("", ""),
|
| 60 |
+
inputs=upload_file,
|
| 61 |
+
outputs=[current_policy_state, current_policy],
|
| 62 |
+
)
|
| 63 |
+
upload_file.change(lambda f: open(f.name).read() if f else "", inputs=upload_file, outputs=upload_preview)
|
| 64 |
+
save_manual_btn.click(lambda t: (t, t), inputs=manual_text, outputs=[current_policy_state, current_policy])
|
| 65 |
+
clear_policy_btn.click(lambda: ("", "*No policy loaded*"), outputs=[current_policy_state, current_policy])
|
| 66 |
+
|
| 67 |
+
return {
|
| 68 |
+
"current_policy_state": current_policy_state,
|
| 69 |
+
"current_policy": current_policy,
|
| 70 |
+
}
|
ui/tab_testing.py
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Testing tab UI components."""
|
| 2 |
+
|
| 3 |
+
import os
|
| 4 |
+
import sys
|
| 5 |
+
|
| 6 |
+
import gradio as gr
|
| 7 |
+
|
| 8 |
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
| 9 |
+
|
| 10 |
+
import json
|
| 11 |
+
|
| 12 |
+
from utils.constants import MODELS, TEST_EXAMPLES
|
| 13 |
+
from utils.model_interface import extract_model_id, get_model_info
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def parse_json_response(response: str) -> dict:
|
| 17 |
+
"""Parse JSON response, handling code blocks."""
|
| 18 |
+
response = response.strip()
|
| 19 |
+
try:
|
| 20 |
+
if "```json" in response:
|
| 21 |
+
response = response.split("```json")[1].split("```")[0]
|
| 22 |
+
elif "```" in response:
|
| 23 |
+
response = response.split("```")[1].split("```")[0]
|
| 24 |
+
return json.loads(response)
|
| 25 |
+
except json.JSONDecodeError:
|
| 26 |
+
return {"label": -1, "categories": []}
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def format_model_info(model_choice, reasoning_effort) -> str:
|
| 30 |
+
"""Format model information markdown."""
|
| 31 |
+
if not model_choice:
|
| 32 |
+
return "*Select a model in Configuration tab*"
|
| 33 |
+
|
| 34 |
+
model_id = extract_model_id(model_choice)
|
| 35 |
+
if not model_id:
|
| 36 |
+
return "*Select a model in Configuration tab*"
|
| 37 |
+
|
| 38 |
+
model_info = get_model_info(model_id)
|
| 39 |
+
|
| 40 |
+
if not model_info:
|
| 41 |
+
return f"*Model: {model_id}*"
|
| 42 |
+
|
| 43 |
+
model_name = model_info.get("name", model_id)
|
| 44 |
+
is_thinking = model_info.get("is_thinking", False)
|
| 45 |
+
supports_reasoning_level = model_info.get("supports_reasoning_level", False)
|
| 46 |
+
|
| 47 |
+
# Handle None or invalid reasoning_effort
|
| 48 |
+
reasoning_effort_val = reasoning_effort if reasoning_effort else "Low"
|
| 49 |
+
|
| 50 |
+
info_lines = [
|
| 51 |
+
f"**Model:** {model_name}",
|
| 52 |
+
f"- **Thinking Model:** {'Yes' if is_thinking else 'No'}",
|
| 53 |
+
f"- **Supports Reasoning Level:** {'Yes' if supports_reasoning_level else 'No'}",
|
| 54 |
+
]
|
| 55 |
+
|
| 56 |
+
if supports_reasoning_level:
|
| 57 |
+
info_lines.append(f"- **Reasoning Effort:** {reasoning_effort_val}")
|
| 58 |
+
|
| 59 |
+
return "\n".join(info_lines)
|
| 60 |
+
|
| 61 |
+
def format_reasoning_info(model_choice, reasoning_text) -> tuple[str, bool]:
|
| 62 |
+
"""Format reasoning info markdown and visibility."""
|
| 63 |
+
if not model_choice:
|
| 64 |
+
return "", False
|
| 65 |
+
|
| 66 |
+
model_id = extract_model_id(model_choice)
|
| 67 |
+
model_info = get_model_info(model_id)
|
| 68 |
+
|
| 69 |
+
if not model_info:
|
| 70 |
+
return "", False
|
| 71 |
+
|
| 72 |
+
is_thinking = model_info.get("is_thinking", False)
|
| 73 |
+
|
| 74 |
+
# For non-thinking models, always show the message
|
| 75 |
+
if not is_thinking:
|
| 76 |
+
return "*This model does not provide reasoning traces.*", True
|
| 77 |
+
|
| 78 |
+
# For thinking models, only show info if there's no reasoning text
|
| 79 |
+
if not reasoning_text or not reasoning_text.strip():
|
| 80 |
+
return "", False
|
| 81 |
+
|
| 82 |
+
return "", False
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
def format_test_result(result: dict) -> tuple[str, dict, str, str, str]:
|
| 86 |
+
"""
|
| 87 |
+
Format test result for display.
|
| 88 |
+
|
| 89 |
+
Returns:
|
| 90 |
+
Tuple of (label_text, parsed_json, categories_text, reasoning_text, raw_response)
|
| 91 |
+
"""
|
| 92 |
+
raw_content = result.get("content", "")
|
| 93 |
+
parsed = parse_json_response(raw_content)
|
| 94 |
+
label = parsed.get("label", -1)
|
| 95 |
+
categories = parsed.get("categories", [])
|
| 96 |
+
|
| 97 |
+
label_text = (
|
| 98 |
+
"## ❌ Policy Violation Detected" if label == 1
|
| 99 |
+
else "## ✅ No Policy Violation" if label == 0
|
| 100 |
+
else "## ⚠️ Unable to determine label"
|
| 101 |
+
)
|
| 102 |
+
|
| 103 |
+
if categories and len(categories) > 0:
|
| 104 |
+
cat_text = "### Categories:\n\n"
|
| 105 |
+
for cat in categories:
|
| 106 |
+
category_name = cat.get('category', 'Unknown')
|
| 107 |
+
reasoning_text = cat.get('reasoning', 'No reasoning provided')
|
| 108 |
+
policy_source = cat.get('policy_source', '')
|
| 109 |
+
|
| 110 |
+
cat_text += f"- **Category:** {category_name}\n"
|
| 111 |
+
cat_text += f"- **Explanation:** {reasoning_text}\n"
|
| 112 |
+
if policy_source:
|
| 113 |
+
cat_text += f"- **Policy Source:** {policy_source}\n"
|
| 114 |
+
cat_text += "\n\n"
|
| 115 |
+
else:
|
| 116 |
+
cat_text = "*No categories found in response*\n\n"
|
| 117 |
+
cat_text += "This output expects a valid JSON response, as specified for example in the default prompt.\n\n"
|
| 118 |
+
cat_text += "The raw response can be seen in the Model Response section below."
|
| 119 |
+
|
| 120 |
+
reasoning = result.get("reasoning", "")
|
| 121 |
+
|
| 122 |
+
# Format raw response for display
|
| 123 |
+
raw_response_text = f"```\n{raw_content}\n```"
|
| 124 |
+
|
| 125 |
+
return label_text, parsed, cat_text, reasoning or "", raw_response_text
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
def build_testing_tab() -> dict:
|
| 129 |
+
"""Build the testing tab UI and set up simple handlers."""
|
| 130 |
+
with gr.Tab("🧪 Testing"):
|
| 131 |
+
with gr.Row():
|
| 132 |
+
with gr.Column(scale=1):
|
| 133 |
+
gr.Markdown("### Input")
|
| 134 |
+
with gr.Group():
|
| 135 |
+
test_input = gr.Textbox(label="Test Content", placeholder="Enter content to test...", lines=5)
|
| 136 |
+
example_dropdown = gr.Dropdown(label="Load Example", choices=list(TEST_EXAMPLES.keys()))
|
| 137 |
+
load_example_btn = gr.Button("Load Example", variant="secondary")
|
| 138 |
+
run_test_btn = gr.Button("Run Test", variant="primary")
|
| 139 |
+
# Initialize with default model info
|
| 140 |
+
initial_model = f"{MODELS[0]['name']} ({MODELS[0]['id']})"
|
| 141 |
+
initial_info_lines = [
|
| 142 |
+
f"**Model:** {MODELS[0]['name']}",
|
| 143 |
+
f"- **Thinking Model:** {'Yes' if MODELS[0]['is_thinking'] else 'No'}",
|
| 144 |
+
f"- **Supports Reasoning Level:** {'Yes' if MODELS[0]['supports_reasoning_level'] else 'No'}",
|
| 145 |
+
]
|
| 146 |
+
if MODELS[0]['supports_reasoning_level']:
|
| 147 |
+
initial_info_lines.append("- **Reasoning Effort:** Low")
|
| 148 |
+
model_info_display = gr.Markdown(value="\n".join(initial_info_lines))
|
| 149 |
+
|
| 150 |
+
with gr.Column(scale=2):
|
| 151 |
+
gr.Markdown("### Results")
|
| 152 |
+
label_display = gr.Markdown(value="*Run a test to see results*")
|
| 153 |
+
with gr.Accordion("Categories & Reasoning", open=True):
|
| 154 |
+
categories_display = gr.Markdown(value="*No categories yet*")
|
| 155 |
+
with gr.Accordion("Model Response", open=False):
|
| 156 |
+
model_response_display = gr.Markdown(value="*No response yet*")
|
| 157 |
+
with gr.Accordion("Reasoning Trace", open=False):
|
| 158 |
+
reasoning_info = gr.Markdown(value="", visible=False)
|
| 159 |
+
reasoning_display = gr.Code(label="", language=None, value="", visible=False)
|
| 160 |
+
|
| 161 |
+
# Simple handlers that don't need cross-tab coordination
|
| 162 |
+
load_example_btn.click(
|
| 163 |
+
lambda name: TEST_EXAMPLES.get(name, ""),
|
| 164 |
+
inputs=example_dropdown,
|
| 165 |
+
outputs=test_input,
|
| 166 |
+
)
|
| 167 |
+
|
| 168 |
+
return {
|
| 169 |
+
"test_input": test_input,
|
| 170 |
+
"example_dropdown": example_dropdown,
|
| 171 |
+
"load_example_btn": load_example_btn,
|
| 172 |
+
"run_test_btn": run_test_btn,
|
| 173 |
+
"model_info_display": model_info_display,
|
| 174 |
+
"label_display": label_display,
|
| 175 |
+
"categories_display": categories_display,
|
| 176 |
+
"model_response_display": model_response_display,
|
| 177 |
+
"reasoning_info": reasoning_info,
|
| 178 |
+
"reasoning_display": reasoning_display,
|
| 179 |
+
}
|