|
|
import os |
|
|
import time |
|
|
import requests |
|
|
import gradio as gr |
|
|
import pandas as pd |
|
|
import random |
|
|
import re |
|
|
from datetime import datetime |
|
|
from dotenv import load_dotenv |
|
|
from together import Together |
|
|
import openai |
|
|
|
|
|
|
|
|
load_dotenv() |
|
|
|
|
|
|
|
|
def process_retrieval_text(retrieval_text, user_input): |
|
|
""" |
|
|
Process the retrieval text by identifying proper document boundaries |
|
|
and highlighting relevant keywords. |
|
|
""" |
|
|
if not retrieval_text or retrieval_text.strip() == "No retrieval text found.": |
|
|
return retrieval_text |
|
|
|
|
|
|
|
|
if retrieval_text.count("Doc:") > 0 and retrieval_text.count("Content:") > 0: |
|
|
|
|
|
chunks = [] |
|
|
|
|
|
|
|
|
doc_sections = re.split(r'\n\n(?=Doc:)', retrieval_text) |
|
|
|
|
|
for i, section in enumerate(doc_sections): |
|
|
if section.strip(): |
|
|
|
|
|
chunks.append(f"<strong>Evidence Document {i+1}</strong><br>{section.strip()}") |
|
|
else: |
|
|
|
|
|
|
|
|
raw_chunks = retrieval_text.strip().split("\n\n") |
|
|
chunks = [] |
|
|
current_chunk = "" |
|
|
|
|
|
for chunk in raw_chunks: |
|
|
|
|
|
|
|
|
if (len(chunk) < 50 and not re.search(r'doc|document|evidence', chunk.lower())) or \ |
|
|
not chunk.strip().startswith(("Doc", "Document", "Evidence", "Source", "Content")): |
|
|
if current_chunk: |
|
|
current_chunk += "\n\n" + chunk |
|
|
else: |
|
|
current_chunk = chunk |
|
|
else: |
|
|
|
|
|
if current_chunk: |
|
|
chunks.append(current_chunk) |
|
|
current_chunk = chunk |
|
|
|
|
|
|
|
|
if current_chunk: |
|
|
chunks.append(current_chunk) |
|
|
|
|
|
|
|
|
chunks = [f"<strong>Evidence Document {i+1}</strong><br>{chunk.strip()}" |
|
|
for i, chunk in enumerate(chunks)] |
|
|
|
|
|
|
|
|
keywords = re.findall(r'\b\w{4,}\b', user_input.lower()) |
|
|
keywords = [k for k in keywords if k not in ['what', 'when', 'where', 'which', 'would', 'could', |
|
|
'should', 'there', 'their', 'about', 'these', 'those', |
|
|
'them', 'from', 'have', 'this', 'that', 'will', 'with']] |
|
|
|
|
|
|
|
|
highlighted_chunks = [] |
|
|
for chunk in chunks: |
|
|
highlighted_chunk = chunk |
|
|
for keyword in keywords: |
|
|
|
|
|
pattern = r'\b(' + re.escape(keyword) + r')\b' |
|
|
highlighted_chunk = re.sub(pattern, r'<span class="highlight-match">\1</span>', highlighted_chunk, flags=re.IGNORECASE) |
|
|
|
|
|
highlighted_chunks.append(highlighted_chunk) |
|
|
|
|
|
return "<br><br>".join(highlighted_chunks) |
|
|
|
|
|
|
|
|
|
|
|
ORACLE_API_KEY = os.environ.get("ORACLE_API_KEY", "") |
|
|
TOGETHER_API_KEY = os.environ.get("TOGETHER_API_KEY", "") |
|
|
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY", "") |
|
|
|
|
|
|
|
|
CUSTOM_CSS = """ |
|
|
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&display=swap'); |
|
|
|
|
|
body, .gradio-container { |
|
|
font-family: 'All Round Gothic Demi', 'Poppins', sans-serif !important; |
|
|
} |
|
|
|
|
|
.rating-box { |
|
|
border-radius: 8px; |
|
|
box-shadow: 0 2px 5px rgba(0,0,0,0.1); |
|
|
padding: 15px; |
|
|
margin-bottom: 10px; |
|
|
transition: all 0.3s ease; |
|
|
background-color: #ffffff; |
|
|
position: relative; |
|
|
overflow-y: auto; |
|
|
white-space: pre-line; |
|
|
font-family: 'All Round Gothic Demi', 'Poppins', sans-serif !important; |
|
|
} |
|
|
.rating-box:hover { |
|
|
box-shadow: 0 5px 15px rgba(0,0,0,0.1); |
|
|
} |
|
|
.safe-rating { |
|
|
border-left: 5px solid #4CAF50; |
|
|
} |
|
|
.warning-rating { |
|
|
border-left: 5px solid #FCA539; |
|
|
} |
|
|
.unsafe-rating { |
|
|
border-left: 5px solid #F44336; |
|
|
} |
|
|
.empty-rating { |
|
|
border-left: 5px solid #FCA539; |
|
|
display: flex; |
|
|
align-items: center; |
|
|
justify-content: center; |
|
|
font-style: italic; |
|
|
color: #999; |
|
|
} |
|
|
|
|
|
/* Different heights for different rating boxes */ |
|
|
.contextual-box { |
|
|
min-height: 150px; |
|
|
} |
|
|
.secondary-box { |
|
|
min-height: 80px; |
|
|
} |
|
|
|
|
|
.result-header { |
|
|
font-size: 18px; |
|
|
font-weight: bold; |
|
|
margin-bottom: 10px; |
|
|
padding-bottom: 5px; |
|
|
border-bottom: 1px solid #eee; |
|
|
font-family: 'All Round Gothic Demi', 'Poppins', sans-serif !important; |
|
|
} |
|
|
.copy-button { |
|
|
position: absolute; |
|
|
top: 10px; |
|
|
right: 10px; |
|
|
padding: 5px 10px; |
|
|
background: #f0f0f0; |
|
|
border: none; |
|
|
border-radius: 4px; |
|
|
cursor: pointer; |
|
|
font-size: 12px; |
|
|
font-family: 'All Round Gothic Demi', 'Poppins', sans-serif !important; |
|
|
} |
|
|
.copy-button:hover { |
|
|
background: #e0e0e0; |
|
|
} |
|
|
.orange-button { |
|
|
background: #FCA539 !important; |
|
|
color: #000000 !important; |
|
|
font-weight: bold; |
|
|
border-radius: 5px; |
|
|
padding: 10px 15px; |
|
|
box-shadow: 0 2px 5px rgba(0,0,0,0.1); |
|
|
transition: all 0.3s ease; |
|
|
font-family: 'All Round Gothic Demi', 'Poppins', sans-serif !important; |
|
|
} |
|
|
.orange-button:hover { |
|
|
box-shadow: 0 5px 15px rgba(0,0,0,0.2); |
|
|
transform: translateY(-2px); |
|
|
} |
|
|
|
|
|
/* Input box styling with orange border */ |
|
|
textarea.svelte-1pie7s6 { |
|
|
border-left: 5px solid #FCA539 !important; |
|
|
border-radius: 8px !important; |
|
|
} |
|
|
|
|
|
#loading-spinner { |
|
|
display: none; |
|
|
margin: 10px auto; |
|
|
width: 100%; |
|
|
height: 4px; |
|
|
position: relative; |
|
|
overflow: hidden; |
|
|
background-color: #ddd; |
|
|
} |
|
|
#loading-spinner:before { |
|
|
content: ''; |
|
|
display: block; |
|
|
position: absolute; |
|
|
left: -50%; |
|
|
width: 50%; |
|
|
height: 100%; |
|
|
background-color: #FCA539; |
|
|
animation: loading 1s linear infinite; |
|
|
} |
|
|
@keyframes loading { |
|
|
from {left: -50%;} |
|
|
to {left: 100%;} |
|
|
} |
|
|
.loading-active { |
|
|
display: block !important; |
|
|
} |
|
|
.empty-box-message { |
|
|
color: #999; |
|
|
font-style: italic; |
|
|
text-align: center; |
|
|
margin-top: 30px; |
|
|
font-family: 'All Round Gothic Demi', 'Poppins', sans-serif !important; |
|
|
} |
|
|
|
|
|
/* Knowledge Button Styling */ |
|
|
.knowledge-button { |
|
|
padding: 5px 10px; |
|
|
background-color: #222222; |
|
|
color: #ffffff !important; |
|
|
border: none; |
|
|
border-radius: 4px; |
|
|
cursor: pointer; |
|
|
font-weight: 500; |
|
|
font-size: 12px; |
|
|
margin-bottom: 10px; |
|
|
display: inline-block; |
|
|
box-shadow: 0 1px 3px rgba(0,0,0,0.1); |
|
|
transition: all 0.2s ease; |
|
|
text-decoration: none !important; |
|
|
} |
|
|
.knowledge-button:hover { |
|
|
background-color: #000000; |
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.15); |
|
|
} |
|
|
|
|
|
/* Knowledge popup styles - IMPROVED */ |
|
|
.knowledge-popup { |
|
|
display: block; |
|
|
padding: 20px; |
|
|
border: 2px solid #FCA539; |
|
|
background-color: white; |
|
|
border-radius: 8px; |
|
|
box-shadow: 0 5px 20px rgba(0,0,0,0.15); |
|
|
margin: 15px 0; |
|
|
position: relative; |
|
|
} |
|
|
|
|
|
.knowledge-popup-header { |
|
|
font-weight: bold; |
|
|
border-bottom: 1px solid #eee; |
|
|
padding-bottom: 10px; |
|
|
margin-bottom: 15px; |
|
|
color: #222; |
|
|
font-size: 16px; |
|
|
} |
|
|
|
|
|
.knowledge-popup-content { |
|
|
max-height: 400px; |
|
|
overflow-y: auto; |
|
|
line-height: 1.6; |
|
|
white-space: normal; |
|
|
} |
|
|
|
|
|
.knowledge-popup-content p { |
|
|
margin-bottom: 12px; |
|
|
} |
|
|
|
|
|
/* Document section formatting */ |
|
|
.doc-section { |
|
|
margin-bottom: 15px; |
|
|
padding-bottom: 15px; |
|
|
border-bottom: 1px solid #eee; |
|
|
} |
|
|
|
|
|
.doc-title { |
|
|
font-weight: bold; |
|
|
margin-bottom: 5px; |
|
|
color: #444; |
|
|
} |
|
|
|
|
|
.doc-content { |
|
|
padding-left: 10px; |
|
|
border-left: 3px solid #f0f0f0; |
|
|
} |
|
|
|
|
|
/* Matching text highlighting */ |
|
|
.highlight-match { |
|
|
background-color: #FCA539; |
|
|
color: black; |
|
|
font-weight: bold; |
|
|
padding: 0 2px; |
|
|
} |
|
|
|
|
|
/* Updated close button to match knowledge button */ |
|
|
.knowledge-popup-close { |
|
|
position: absolute; |
|
|
top: 15px; |
|
|
right: 15px; |
|
|
background-color: #222222; |
|
|
color: #ffffff !important; |
|
|
border: none; |
|
|
border-radius: 4px; |
|
|
padding: 5px 10px; |
|
|
cursor: pointer; |
|
|
font-size: 12px; |
|
|
font-weight: 500; |
|
|
box-shadow: 0 1px 3px rgba(0,0,0,0.1); |
|
|
} |
|
|
.knowledge-popup-close:hover { |
|
|
background-color: #000000; |
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.15); |
|
|
} |
|
|
|
|
|
h1, h2, h3, h4, h5, h6, p, span, div, button, input, textarea, label { |
|
|
font-family: 'All Round Gothic Demi', 'Poppins', sans-serif !important; |
|
|
} |
|
|
|
|
|
/* Make safety warning text red */ |
|
|
.safety-warning-red { |
|
|
color: #F44336 !important; |
|
|
font-weight: bold; |
|
|
} |
|
|
|
|
|
/* Make knowledge button match orange button style */ |
|
|
.knowledge-button.orange-button { |
|
|
background: #FCA539 !important; |
|
|
color: #000000 !important; |
|
|
font-weight: bold; |
|
|
border-radius: 5px; |
|
|
padding: 10px 15px; |
|
|
box-shadow: 0 2px 5px rgba(0,0,0,0.1); |
|
|
transition: all 0.3s ease; |
|
|
display: inline-block; |
|
|
text-decoration: none; |
|
|
} |
|
|
|
|
|
.knowledge-button.orange-button:hover { |
|
|
box-shadow: 0 5px 15px rgba(0,0,0,0.2); |
|
|
transform: translateY(-2px); |
|
|
} |
|
|
|
|
|
/* Make the third column of models narrower to fit 3 on one row */ |
|
|
.model-column { |
|
|
max-width: 33% !important; |
|
|
flex: 1 !important; |
|
|
} |
|
|
""" |
|
|
|
|
|
|
|
|
class ContextualAPIUtils: |
|
|
def __init__(self, api_key): |
|
|
self.api_key = api_key |
|
|
|
|
|
self.model_id = "92ab273b-378f-4b52-812b-7ec21506e49b" |
|
|
self.endpoint_url = f"https://api.contextual.ai/v1/agents/{self.model_id}/query" |
|
|
|
|
|
def chat(self, prompt): |
|
|
url = f"{self.endpoint_url}?retrievals_only=false&include_retrieval_content_text=true" |
|
|
headers = { |
|
|
"accept": "application/json", |
|
|
"content-type": "application/json", |
|
|
"authorization": f"Bearer {self.api_key}", |
|
|
} |
|
|
body = { |
|
|
"stream": False, |
|
|
"messages": [{"role": "user", "content": prompt}], |
|
|
} |
|
|
|
|
|
start_time = time.time() |
|
|
try: |
|
|
response = requests.post(url, headers=headers, json=body) |
|
|
response.raise_for_status() |
|
|
response_json = response.json() |
|
|
|
|
|
response_content = response_json.get("message", {}).get("content", "No content received.") |
|
|
retrieved_texts = [ |
|
|
f"Doc: {item.get('doc_name', 'Unknown')}, Page: {item.get('page', 'N/A')}\n" |
|
|
f"Content: {item.get('content_text', 'No Content')}" |
|
|
for item in response_json.get("retrieval_contents", []) |
|
|
] |
|
|
retrieval_text = "\n\n".join(retrieved_texts) if retrieved_texts else "No retrieval text found." |
|
|
error_message = "" |
|
|
|
|
|
except requests.exceptions.RequestException as e: |
|
|
response_content = "API Request Failed" |
|
|
retrieval_text = "" |
|
|
error_message = str(e) |
|
|
|
|
|
response_time = round(time.time() - start_time, 4) |
|
|
return response_content, response_time, retrieval_text, error_message |
|
|
|
|
|
|
|
|
def get_contextual_rating(contextual_api, user_input): |
|
|
rating_prompt = f"Content to rate: {user_input}" |
|
|
response_text, response_time, retrieval_text, error = contextual_api.chat(rating_prompt) |
|
|
if error: |
|
|
return f"Error: {error}", "", "unsafe" |
|
|
|
|
|
|
|
|
safety_level = "safe" |
|
|
if "out of policy" in response_text.lower() or "unsafe" in response_text.lower(): |
|
|
safety_level = "unsafe" |
|
|
elif "caution" in response_text.lower() or "warning" in response_text.lower(): |
|
|
safety_level = "warning" |
|
|
|
|
|
return response_text, retrieval_text, safety_level |
|
|
|
|
|
|
|
|
LLAMA_HARM_CATEGORIES = { |
|
|
"S1": "Hate Speech", "S2": "Harassment", "S3": "Violence & Threats", |
|
|
"S4": "Sexual Content", "S5": "Self-Harm & Suicide", "S6": "Child Sexual Exploitation", |
|
|
"S7": "Dangerous & Illegal Activities", "S8": "Misinformation", "S9": "Political Content", |
|
|
"S10": "Spam & Scams", "S11": "Privacy Violations", "S12": "Defamation", "S13": "Other Unsafe Content" |
|
|
} |
|
|
|
|
|
def get_llama_guard_rating(together_client, user_input): |
|
|
chat_input = [{"role": "user", "content": user_input}] |
|
|
try: |
|
|
response = together_client.chat.completions.create( |
|
|
model="meta-llama/Meta-Llama-Guard-3-8B", messages=chat_input |
|
|
) |
|
|
raw_output = response.choices[0].message.content.strip() |
|
|
for code, full_name in LLAMA_HARM_CATEGORIES.items(): |
|
|
raw_output = raw_output.replace(code, full_name) |
|
|
|
|
|
|
|
|
safety_level = "safe" |
|
|
if "unsafe" in raw_output.lower(): |
|
|
safety_level = "unsafe" |
|
|
elif "caution" in raw_output.lower() or "warning" in raw_output.lower(): |
|
|
safety_level = "warning" |
|
|
|
|
|
return raw_output, safety_level |
|
|
except Exception as e: |
|
|
return f"Error: {str(e)}", "unsafe" |
|
|
|
|
|
|
|
|
def get_openai_moderation(openai_client, user_input): |
|
|
try: |
|
|
start_time = time.time() |
|
|
response = openai_client.moderations.create(input=user_input, model="omni-moderation-latest") |
|
|
end_time = time.time() |
|
|
|
|
|
moderation_result = response.results[0] |
|
|
flagged = moderation_result.flagged |
|
|
safety_status = "Unsafe" if flagged else "Safe" |
|
|
safety_level = "unsafe" if flagged else "safe" |
|
|
|
|
|
categories = moderation_result.categories |
|
|
high_risk_categories = { |
|
|
category: score |
|
|
for category, score in vars(categories).items() |
|
|
if isinstance(score, (int, float)) and score > 0.5 |
|
|
} |
|
|
|
|
|
if not flagged and any(score > 0.3 for score in vars(categories).values() if isinstance(score, (int, float))): |
|
|
safety_level = "warning" |
|
|
|
|
|
formatted_result = f"Safety Status: {safety_status}\n" |
|
|
if high_risk_categories: |
|
|
formatted_result += "Flagged Categories (Confidence > 0.5):\n" |
|
|
for category, score in high_risk_categories.items(): |
|
|
formatted_result += f" - {category}: {score:.2f}\n" |
|
|
else: |
|
|
formatted_result += "Flagged Categories: None\n" |
|
|
|
|
|
return formatted_result, safety_level |
|
|
except Exception as e: |
|
|
return f"Safety Status: Error\nError: {str(e)}", "unsafe" |
|
|
|
|
|
def get_perspective_rating(user_input): |
|
|
url = 'https://commentanalyzer.googleapis.com/v1alpha1/comments:analyze' |
|
|
api_key = PERSPECTIVE_API_KEY |
|
|
|
|
|
params = {'key': api_key} |
|
|
data = { |
|
|
'comment': {'text': user_input}, |
|
|
'requestedAttributes': { |
|
|
'TOXICITY': {}, |
|
|
'SEVERE_TOXICITY': {}, |
|
|
'IDENTITY_ATTACK': {}, |
|
|
'INSULT': {}, |
|
|
'PROFANITY': {}, |
|
|
'THREAT': {}, |
|
|
'SEXUALLY_EXPLICIT': {} |
|
|
} |
|
|
} |
|
|
|
|
|
try: |
|
|
start_time = time.time() |
|
|
response = requests.post(url, params=params, json=data) |
|
|
end_time = time.time() |
|
|
|
|
|
response_json = response.json() |
|
|
attribute_scores = response_json.get('attributeScores', {}) |
|
|
|
|
|
|
|
|
high_attributes = {} |
|
|
for attr, data in attribute_scores.items(): |
|
|
score = data.get('summaryScore', {}).get('value', 0) |
|
|
if score >= 0.5: |
|
|
high_attributes[attr] = score |
|
|
|
|
|
|
|
|
safety_level = "safe" |
|
|
if high_attributes: |
|
|
safety_level = "unsafe" |
|
|
|
|
|
|
|
|
formatted_result = f"Safety Status: {'Unsafe' if high_attributes else 'Safe'}\n" |
|
|
if high_attributes: |
|
|
formatted_result += "Flagged Categories (β₯ 0.5):\n" |
|
|
for attr, score in high_attributes.items(): |
|
|
formatted_result += f" - {attr}: {score:.2f}\n" |
|
|
else: |
|
|
formatted_result += "Flagged Categories: None\n" |
|
|
|
|
|
return formatted_result, safety_level |
|
|
except Exception as e: |
|
|
return f"Safety Status: Error\nError: {str(e)}", "unsafe" |
|
|
|
|
|
|
|
|
|
|
|
def rate_user_input(user_input): |
|
|
|
|
|
contextual_api = ContextualAPIUtils(api_key=ORACLE_API_KEY) |
|
|
together_client = Together(api_key=TOGETHER_API_KEY) |
|
|
openai_client = openai.OpenAI(api_key=OPENAI_API_KEY) |
|
|
|
|
|
|
|
|
llama_rating, llama_safety = get_llama_guard_rating(together_client, user_input) |
|
|
contextual_rating, contextual_retrieval, contextual_safety = get_contextual_rating(contextual_api, user_input) |
|
|
openai_rating, openai_safety = get_openai_moderation(openai_client, user_input) |
|
|
perspective_rating, perspective_safety = get_perspective_rating(user_input) |
|
|
|
|
|
|
|
|
llama_rating = re.sub(r'\.(?=\s+[A-Z])', '.\n', llama_rating) |
|
|
contextual_rating = re.sub(r'\.(?=\s+[A-Z])', '.\n', contextual_rating) |
|
|
|
|
|
|
|
|
processed_retrieval = process_retrieval_text(contextual_retrieval, user_input) |
|
|
|
|
|
|
|
|
llama_html = f"""<div class="rating-box secondary-box {llama_safety}-rating">{llama_rating}</div>""" |
|
|
openai_html = f"""<div class="rating-box secondary-box {openai_safety}-rating">{openai_rating}</div>""" |
|
|
perspective_html = f"""<div class="rating-box secondary-box {perspective_safety}-rating">{perspective_rating}</div>""" |
|
|
|
|
|
|
|
|
knowledge_html = "" |
|
|
knowledge_button = "" |
|
|
|
|
|
if processed_retrieval and processed_retrieval != "No retrieval text found.": |
|
|
|
|
|
import uuid |
|
|
popup_id = f"knowledge-popup-{uuid.uuid4().hex[:8]}" |
|
|
|
|
|
|
|
|
knowledge_html = f""" |
|
|
<div id="{popup_id}" class="knowledge-popup" style="display: none;"> |
|
|
<div class="knowledge-popup-header">Retrieved Knowledge</div> |
|
|
<button class="knowledge-popup-close" |
|
|
onclick="this.parentElement.style.display='none'; |
|
|
document.getElementById('btn-{popup_id}').style.display='inline-block'; |
|
|
return false;"> |
|
|
Close |
|
|
</button> |
|
|
<div class="knowledge-popup-content"> |
|
|
{processed_retrieval} |
|
|
</div> |
|
|
</div> |
|
|
""" |
|
|
|
|
|
|
|
|
knowledge_button = f""" |
|
|
<div style="margin-top: 10px; margin-bottom: 5px;"> |
|
|
<a href="#" id="btn-{popup_id}" class="knowledge-button orange-button" |
|
|
onclick="document.getElementById('{popup_id}').style.display='block'; this.style.display='none'; return false;"> |
|
|
Show supporting evidence |
|
|
</a> |
|
|
</div> |
|
|
""" |
|
|
|
|
|
|
|
|
contextual_html = f""" |
|
|
<div class="rating-box contextual-box {contextual_safety}-rating"> |
|
|
<button class="copy-button" onclick="navigator.clipboard.writeText(this.parentElement.innerText.replace('Copy', ''))">Copy</button> |
|
|
{contextual_rating} |
|
|
</div> |
|
|
{knowledge_button} |
|
|
{knowledge_html} |
|
|
""" |
|
|
|
|
|
return contextual_html, llama_html, openai_html, perspective_html, "" |
|
|
|
|
|
def random_test_case(): |
|
|
try: |
|
|
df = pd.read_csv("hate_speech_test_cases.csv") |
|
|
sample = df.sample(1).iloc[0]["user input"] |
|
|
return sample |
|
|
except Exception as e: |
|
|
return f"Error: {e}" |
|
|
|
|
|
|
|
|
def create_gradio_app(): |
|
|
|
|
|
|
|
|
with gr.Blocks(title="Hate Speech Rating Oracle", theme=theme, css=custom_css) as app: |
|
|
|
|
|
loading_spinner = gr.HTML('<div id="loading-spinner"></div>') |
|
|
|
|
|
|
|
|
pdf_file = gr.File("Hate Speech Policy.pdf", visible=False, label="Policy PDF") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
gr.Markdown("# Hate Speech Rating Oracle [BETA]") |
|
|
|
|
|
|
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(scale=1): |
|
|
|
|
|
random_test_btn = gr.Button("π² Random Test Case", elem_classes=["orange-button"]) |
|
|
|
|
|
|
|
|
rate_btn = gr.Button("Rate Content", variant="primary", size="lg", elem_classes=["gray-button"]) |
|
|
|
|
|
|
|
|
user_input = gr.Textbox(label="Input content to rate:", placeholder="Type content to evaluate here...", lines=6) |
|
|
|
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(scale=1, elem_classes=["model-column"]): |
|
|
|
|
|
gr.HTML(""" |
|
|
<div> |
|
|
<h3 class="result-header">π Contextual Safety Oracle</h3> |
|
|
<div style="margin-top: -10px; margin-bottom: 10px;"> |
|
|
<a href="#" class="knowledge-button" onclick="openPolicyPopup(); return false;">View policy</a> |
|
|
</div> |
|
|
</div> |
|
|
""") |
|
|
contextual_results = gr.HTML('<div class="rating-box contextual-box empty-rating">Rating will appear here</div>') |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(scale=1, elem_classes=["model-column"]): |
|
|
|
|
|
gr.HTML(""" |
|
|
<div> |
|
|
<h3 class="result-header">π¦ LlamaGuard 3.0</h3> |
|
|
<div style="margin-top: -10px; margin-bottom: 10px;"> |
|
|
<a href="https://github.com/meta-llama/PurpleLlama/blob/main/Llama-Guard3/8B/MODEL_CARD.md" |
|
|
target="_blank" class="knowledge-button">View model card</a> |
|
|
</div> |
|
|
</div> |
|
|
""") |
|
|
llama_results = gr.HTML('<div class="rating-box secondary-box empty-rating">Rating will appear here</div>') |
|
|
with gr.Column(scale=1, elem_classes=["model-column"]): |
|
|
|
|
|
gr.HTML(""" |
|
|
<div> |
|
|
<h3 class="result-header">π§· OpenAI Moderation</h3> |
|
|
<div style="margin-top: -10px; margin-bottom: 10px;"> |
|
|
<a href="https://platform.openai.com/docs/guides/moderation" |
|
|
target="_blank" class="knowledge-button">View model card</a> |
|
|
</div> |
|
|
</div> |
|
|
""") |
|
|
openai_results = gr.HTML('<div class="rating-box secondary-box empty-rating">Rating will appear here</div>') |
|
|
with gr.Column(scale=1, elem_classes=["model-column"]): |
|
|
|
|
|
gr.HTML(""" |
|
|
<div> |
|
|
<h3 class="result-header">ποΈ Perspective API</h3> |
|
|
<div style="margin-top: -10px; margin-bottom: 10px;"> |
|
|
<a href="https://developers.perspectiveapi.com/s/about-the-api" |
|
|
target="_blank" class="knowledge-button">View model card</a> |
|
|
</div> |
|
|
</div> |
|
|
""") |
|
|
perspective_results = gr.HTML('<div class="rating-box secondary-box empty-rating">Rating will appear here</div>') |
|
|
|
|
|
|
|
|
retrieved_knowledge = gr.HTML('', visible=False) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
random_test_btn.click( |
|
|
show_loading, |
|
|
inputs=None, |
|
|
outputs=loading_spinner |
|
|
).then( |
|
|
random_test_case, |
|
|
inputs=[], |
|
|
outputs=[user_input] |
|
|
).then( |
|
|
hide_loading, |
|
|
inputs=None, |
|
|
outputs=loading_spinner |
|
|
) |
|
|
|
|
|
rate_btn.click( |
|
|
show_loading, |
|
|
inputs=None, |
|
|
outputs=loading_spinner |
|
|
).then( |
|
|
rate_user_input, |
|
|
inputs=[user_input], |
|
|
outputs=[contextual_results, llama_results, openai_results, perspective_results, retrieved_knowledge] |
|
|
).then( |
|
|
hide_loading, |
|
|
inputs=None, |
|
|
outputs=loading_spinner |
|
|
) |
|
|
|
|
|
return app |
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
app = create_gradio_app() |
|
|
app.launch(share=True) |