|
|
import streamlit as st |
|
|
import requests |
|
|
import json |
|
|
import os |
|
|
|
|
|
|
|
|
st.markdown(""" |
|
|
<style> |
|
|
:root { |
|
|
--light-purple: rgba(179, 157, 219, 0.6); |
|
|
--light-purple-border: rgba(179, 157, 219, 0.4); |
|
|
--main-purple: #6a1b9a; /* A darker purple for accents */ |
|
|
--light-gray-border: #d0cfd0; |
|
|
--text-color-dark: #555555; |
|
|
--text-color-medium: #4a4a4a; |
|
|
} |
|
|
|
|
|
/* Import Font Awesome for icons */ |
|
|
@import url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css'); |
|
|
|
|
|
.stMainBlockContainer.block-container.st-emotion-cache-zy6yx3.e4man114 { |
|
|
padding-top: 55px !important; |
|
|
} |
|
|
|
|
|
/* Style for the COLLAPSIBLE details container; the API Description */ |
|
|
|
|
|
#api-info { |
|
|
width: 100%; |
|
|
margin-top: 0 !important; |
|
|
padding-top: 0 !important; |
|
|
} |
|
|
|
|
|
#api-info details { |
|
|
width: 100%; |
|
|
max-width: 100%; |
|
|
box-sizing: border-box; |
|
|
border: 2px solid var(--light-gray-border); |
|
|
border-radius: 6.4px; |
|
|
padding: 0.4rem 0.8rem; |
|
|
background-color: #fff; |
|
|
margin: 0; |
|
|
transition: border-color 0.3s ease-in-out, box-shadow 0.3s ease-in-out; |
|
|
} |
|
|
|
|
|
/* Purple outline and shadow when OPEN or FOCUSED */ |
|
|
#api-info details:focus-within, |
|
|
#api-info details[open] { |
|
|
outline: none; |
|
|
border-color: rgba(106, 27, 154, 0.2); |
|
|
box-shadow: 0 0 0 1.5px rgba(106, 27, 154, 0.2); |
|
|
} |
|
|
|
|
|
/* Summary style and hover/focus behavior */ |
|
|
#api-info summary { |
|
|
font-weight: 600; |
|
|
font-size: 14.4px; |
|
|
color: var(--text-color-dark); |
|
|
cursor: pointer; |
|
|
list-style: none; |
|
|
margin: 0; |
|
|
padding: 0.2rem 0; |
|
|
transition: color 0.3s; |
|
|
} |
|
|
|
|
|
/* Purple color on hover/focus */ |
|
|
#api-info summary:hover, |
|
|
#api-info summary:focus { |
|
|
color: var(--main-purple); |
|
|
} |
|
|
|
|
|
/* Remove default marker */ |
|
|
#api-info summary::-webkit-details-marker { |
|
|
display: none; |
|
|
} |
|
|
|
|
|
/* Custom arrow */ |
|
|
#api-info summary::after { |
|
|
content: "›"; |
|
|
font-size: 14.4px; |
|
|
margin-left: 6.4px; |
|
|
float: right; |
|
|
transition: transform 0.3s; |
|
|
color: var(--main-purple); |
|
|
} |
|
|
|
|
|
#api-info details[open] summary::after { |
|
|
transform: rotate(90deg); |
|
|
} |
|
|
|
|
|
/* Content styling */ |
|
|
#api-info .expander-content { |
|
|
font-size: 14px !important; |
|
|
padding: 0.6rem 0; |
|
|
color: var(--text-color-medium); |
|
|
font-family: Arial, sans-serif; |
|
|
line-height: 1.28; |
|
|
} |
|
|
|
|
|
/* List indentation */ |
|
|
#api-info .expander-content ul { |
|
|
margin-left: 0.96em; |
|
|
} |
|
|
|
|
|
/* Link appearance */ |
|
|
#api-info .expander-content a { |
|
|
color: var(--main-purple); |
|
|
text-decoration: none; |
|
|
} |
|
|
|
|
|
#api-info .expander-content a:hover { |
|
|
text-decoration: underline; |
|
|
} |
|
|
|
|
|
/* --- Custom styles for Streamlit components --- */ |
|
|
/* Textarea focus & error states */ |
|
|
div[data-baseweb="textarea"] textarea:focus, |
|
|
div[data-baseweb="textarea"] textarea:focus-visible { |
|
|
outline: none !important; |
|
|
box-shadow: 0 0 0 1.5px var(--light-purple) !important; |
|
|
border: 1.5px solid var(--light-purple-border) !important; |
|
|
} |
|
|
|
|
|
/* Override red border on invalid textarea */ |
|
|
div[data-baseweb="textarea"] textarea:invalid { |
|
|
box-shadow: 0 0 0 1.5px var(--light-purple) !important; |
|
|
border-color: var(--light-purple-border) !important; |
|
|
} |
|
|
|
|
|
/* Checkbox focus outline */ |
|
|
div[data-baseweb="checkbox"] input[type="checkbox"]:focus-visible { |
|
|
outline: none !important; |
|
|
box-shadow: 0 0 0 1.5px var(--light-purple) !important; |
|
|
border: 1.5px solid var(--light-purple-border) !important; |
|
|
} |
|
|
|
|
|
/* Checkbox checked fill (override red) */ |
|
|
div[data-baseweb="checkbox"] input[type="checkbox"]:checked { |
|
|
background-color: var(--light-purple-border) !important; |
|
|
border-color: var(--light-purple-border) !important; |
|
|
} |
|
|
|
|
|
/* Checkbox hover fill */ |
|
|
div[data-baseweb="checkbox"] input[type="checkbox"]:hover:not(:checked) { |
|
|
background-color: rgba(179, 157, 219, 0.2) !important; |
|
|
} |
|
|
|
|
|
/* Checkbox checked hover fill */ |
|
|
div[data-baseweb="checkbox"] input[type="checkbox"]:checked:hover { |
|
|
background-color: rgba(179, 157, 219, 0.5) !important; |
|
|
} |
|
|
|
|
|
/* Ensure Streamlit's checkmark color is visible */ |
|
|
div[data-baseweb="checkbox"] input[type="checkbox"]:checked::before { |
|
|
color: white !important; |
|
|
/* Adjust based on your purple background */ |
|
|
} |
|
|
|
|
|
/* Style for sliders to match theme */ |
|
|
div[data-baseweb="slider"] div[role="slider"] { |
|
|
background-color: var(--main-purple) !important; |
|
|
} |
|
|
div[data-baseweb="slider"] div[role="slider"]:hover { |
|
|
background-color: var(--light-purple) !important; |
|
|
} |
|
|
div[data-baseweb="slider"] div[role="slider"]:focus { |
|
|
box-shadow: 0 0 0 1.5px var(--light-purple) !important; |
|
|
} |
|
|
div[data-baseweb="slider"] div[data-baseweb="tooltip"] { |
|
|
background-color: var(--main-purple) !important; |
|
|
border-color: var(--main-purple) !important; |
|
|
} |
|
|
div[data-baseweb="slider"] div[data-baseweb="track"] { |
|
|
background-color: var(--light-purple-border) !important; |
|
|
} |
|
|
|
|
|
/* Style for multiselect dropdowns */ |
|
|
div[data-baseweb="select"] { |
|
|
border-radius: 6.4px; |
|
|
border: 1.5px solid var(--light-purple-border); |
|
|
} |
|
|
div[data-baseweb="select"]:focus-within { |
|
|
box-shadow: 0 0 0 1.5px var(--light-purple) !important; |
|
|
border-color: var(--light-purple) !important; |
|
|
} |
|
|
/* Style for multiselect selected items */ |
|
|
div[data-baseweb="tag"] { |
|
|
background-color: var(--light-purple) !important; |
|
|
color: var(--text-color-dark) !important; |
|
|
border-radius: 4px; |
|
|
} |
|
|
div[data-baseweb="tag"] svg { /* Close icon */ |
|
|
color: var(--text-color-dark) !important; |
|
|
} |
|
|
|
|
|
/* Custom button styling for icon buttons (Refresh button) */ |
|
|
.stButton > button[kind="secondary"] { /* Target non-primary buttons for icon styling */ |
|
|
background-color: transparent !important; |
|
|
border: none !important; |
|
|
padding: 0.5rem !important; |
|
|
margin: 0 !important; |
|
|
box-shadow: none !important; |
|
|
color: var(--main-purple) !important; |
|
|
font-size: 1.5rem !important; |
|
|
/* Adjust icon size */ |
|
|
cursor: pointer; |
|
|
transition: color 0.2s ease-in-out; |
|
|
} |
|
|
.stButton > button[kind="secondary"]:hover { |
|
|
color: var(--light-purple) !important; |
|
|
} |
|
|
.stButton > button[kind="secondary"]:focus, |
|
|
.stButton > button[kind="secondary"]:focus-visible { |
|
|
outline: none !important; |
|
|
box-shadow: 0 0 0 3px rgba(106, 27, 154, 0.4) !important; |
|
|
/* Purple highlight on focus */ |
|
|
} |
|
|
|
|
|
/* Custom button styling for primary buttons (Send button) */ |
|
|
.stButton button.primary { |
|
|
background-color: var(--main-purple) !important; |
|
|
color: var(--header-text-color) !important; /* White text */ |
|
|
border: 1.5px solid var(--main-purple) !important; |
|
|
border-radius: 0.5rem !important; |
|
|
padding: 0.75rem 1.5rem !important; |
|
|
font-size: 1rem !important; |
|
|
font-weight: 600 !important; |
|
|
box-shadow: 0 2px 5px rgba(0,0,0,0.2) !important; |
|
|
transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out, box-shadow 0.2s ease-in-out; |
|
|
} |
|
|
|
|
|
.stButton button.primary:hover { |
|
|
background-color: #5a127a !important; |
|
|
/* Slightly darker purple on hover */ |
|
|
border-color: #5a127a !important; |
|
|
box-shadow: 0 4px 8px rgba(0,0,0,0.3) !important; |
|
|
} |
|
|
|
|
|
.stButton button.primary:focus, |
|
|
.stButton button.primary:focus-visible { |
|
|
outline: none !important; |
|
|
box-shadow: 0 0 0 3px rgba(106, 27, 154, 0.4) !important; |
|
|
/* Purple highlight on focus */ |
|
|
} |
|
|
|
|
|
/* Adjust text_area to have scroll for long text */ |
|
|
textarea[aria-label="Prompt:"] { |
|
|
overflow-y: auto !important; |
|
|
min-height: 38px; /* Approximate single line height */ |
|
|
max-height: 100px; |
|
|
/* Max height before scrollbar appears, adjust as needed */ |
|
|
resize: vertical; |
|
|
/* Allow vertical resizing by user */ |
|
|
} |
|
|
|
|
|
/* Ensure text area and button align properly */ |
|
|
div[data-testid="stVerticalBlock"] > div > div > div[data-testid="stHorizontalBlock"] { |
|
|
display: flex; |
|
|
align-items: flex-end; /* Align items to the bottom */ |
|
|
gap: 10px; |
|
|
/* Space between textarea and button */ |
|
|
} |
|
|
|
|
|
/* Specific styling for the refresh button container to align it */ |
|
|
div[data-testid="stVerticalBlock"] > div > div > div[data-testid="stHorizontalBlock"] > div:last-child { |
|
|
display: flex; |
|
|
align-items: flex-end; /* Align the button to the bottom of its flex container */ |
|
|
height: 100%; |
|
|
/* Ensure it takes full height to align with textarea */ |
|
|
} |
|
|
|
|
|
#for send button |
|
|
.stButton.send-button-container { |
|
|
display: flex; |
|
|
justify-content: center; |
|
|
margin-top: 1.5rem; /* Space above the send button */ |
|
|
} |
|
|
|
|
|
/* Hide the label for the prompt text area */ |
|
|
div[data-testid="stTextarea"] label { |
|
|
display: none !important; |
|
|
} |
|
|
|
|
|
/* Hide scrollbar for the parent container of the text area if it appears */ |
|
|
div[data-baseweb="textarea"] > div:first-child { |
|
|
overflow: hidden !important; |
|
|
} |
|
|
|
|
|
/* Reduce vertical spacing between checkbox items */ |
|
|
div[data-baseweb="checkbox"] { |
|
|
margin-bottom: 2px; |
|
|
/* Adjust to control spacing */ |
|
|
} |
|
|
|
|
|
div[data-baseweb="checkbox"] label { |
|
|
margin: 0 !important; |
|
|
padding: 0 !important; |
|
|
} |
|
|
|
|
|
</style> |
|
|
""", unsafe_allow_html=True) |
|
|
|
|
|
st.set_page_config(layout="wide") |
|
|
|
|
|
|
|
|
if 'custom_words' not in st.session_state: |
|
|
st.session_state.custom_words = [] |
|
|
|
|
|
|
|
|
if 'show_output' not in st.session_state: |
|
|
st.session_state.show_output = False |
|
|
|
|
|
|
|
|
st.markdown(""" |
|
|
<div id="api-info"> |
|
|
<details> |
|
|
<summary>API DESCRIPTION</summary> |
|
|
<div class="expander-content"> |
|
|
<h5 style="margin:0.4em 0;">What does this API do?</h5> |
|
|
<p>The <strong>Moderation API</strong> evaluates prompts to determine whether they are safe for use with a large language model (LLM). It returns a <strong>status</strong> (passed or failed) with detailed scores.</p> |
|
|
<h4 style="margin:0.64em 0 0.24em;">Moderation Checks</h4> |
|
|
<ul> |
|
|
<li><strong>Prompt Injection</strong>: Detects attempts to hijack or manipulate the LLM behavior.</li> |
|
|
<li><strong>Jailbreak Attempts</strong>: Identifies prompts trying to bypass guardrails.</li> |
|
|
<li><strong>Toxicity & Profanity</strong>: Flags harmful, offensive, or explicit content.</li> |
|
|
<li><strong>Restricted Topics</strong>: Detects categories like cheating, conspiracy, terrorism, etc.</li> |
|
|
<li><strong>Text Quality</strong>: Measures readability and clarity (informational only).</li> |
|
|
<li><strong>Customized Theme</strong>: Block prompts using custom keywords (e.g., "atomic weapon").</li> |
|
|
<li><strong>PII Detection</strong>: Identifies AADHAR, PAN, SSN, Passport, Email, Phone, IP, Credit Card, Medical License, etc.</li> |
|
|
</ul> |
|
|
<h4 style="margin:0.64em 0 0.24em;">Full Customization</h4> |
|
|
<ul> |
|
|
<li>Enable/disable checks</li> |
|
|
<li>Set thresholds</li> |
|
|
<li>Select PII or restricted topics</li> |
|
|
<li>Define custom block terms</li> |
|
|
</ul> |
|
|
<h4 style="margin:0.64em 0 0.24em;">Resources</h4> |
|
|
<ul> |
|
|
<li><a href="https://huggingface.co/spaces/InfosysEnterprise/responsible-ai-moderationlayer/tree/main" target="_blank">Hugging Face Repo</a></li> |
|
|
<li><a href="https://infosysenterprise-responsible-ai-moderationlayer.hf.space/rai/v1/moderations/docs/#" target="_blank">Swagger Docs</a></li> |
|
|
</ul> |
|
|
</div> |
|
|
</details> |
|
|
</div> |
|
|
""", unsafe_allow_html=True) |
|
|
|
|
|
|
|
|
input_col, output_col = st.columns([0.5, 0.5]) |
|
|
|
|
|
with input_col: |
|
|
st.markdown('<h4 style="font-weight:550; margin-bottom:0.5rem; padding-top:2rem;">INPUT</h4>', unsafe_allow_html=True) |
|
|
|
|
|
|
|
|
check_configs = { |
|
|
"Prompt Injection": {"type": "slider", "default": 0.7, "min": 0.0, "max": 1.0, "step": 0.01}, |
|
|
"Jailbreak": {"type": "slider", "default": 0.7, "min": 0.0, "max": 1.0, "step": 0.01}, |
|
|
"Toxicity": {"type": "slider", "default": 0.6, "min": 0.0, "max": 1.0, "step": 0.01}, |
|
|
"Profanity": {"type": "number_input", "default": 1, "min": 1, "hint": "1"}, |
|
|
"Restricted Topics": {"type": "multiselect", "default": 0.7, "min": 0.0, "max": 1.0, "step": 0.01, |
|
|
"options": ["terrorism","explosives","nudity","cruelty","cheating","fraud","crime","hacking","immoral","unethical","illegal","robbery","forgery","misinformation"]}, |
|
|
"Text Quality": {"type": "no_threshold"}, |
|
|
"Customized Theme": {"type": "slider_and_input", "default": 0.6, "min": 0.0, "max": 1.0, "step": 0.01}, |
|
|
"PII Detection": {"type": "multiselect_pii", |
|
|
"options": ["AADHAR_NUMBER", "PAN_Number", "IN_PAN", "US_PASSPORT", "US_SSN"]}, |
|
|
} |
|
|
|
|
|
|
|
|
for check_name, config in check_configs.items(): |
|
|
base_key = check_name.replace(' ', '_') |
|
|
|
|
|
|
|
|
checkbox_key = f"checkbox_{base_key}" |
|
|
if checkbox_key not in st.session_state: |
|
|
st.session_state[checkbox_key] = True |
|
|
|
|
|
|
|
|
if config["type"] == "slider": |
|
|
slider_key = f"slider_{base_key}" |
|
|
if slider_key not in st.session_state: |
|
|
st.session_state[slider_key] = config["default"] |
|
|
|
|
|
elif config["type"] == "number_input": |
|
|
number_key = f"number_{base_key}" |
|
|
if number_key not in st.session_state: |
|
|
st.session_state[number_key] = config["default"] |
|
|
|
|
|
elif config["type"] == "multiselect": |
|
|
multi_key = f"multiselect_{base_key}" |
|
|
if multi_key not in st.session_state: |
|
|
st.session_state[multi_key] = config["options"][:2] |
|
|
slider_key = f"slider_{base_key}" |
|
|
if slider_key not in st.session_state: |
|
|
st.session_state[slider_key] = config["default"] |
|
|
|
|
|
elif config["type"] == "multiselect_pii": |
|
|
multi_key = f"multiselect_{base_key}" |
|
|
if multi_key not in st.session_state: |
|
|
st.session_state[multi_key] = config["options"] |
|
|
|
|
|
elif config["type"] == "slider_and_input": |
|
|
slider_key = f"slider_{base_key}" |
|
|
if slider_key not in st.session_state: |
|
|
st.session_state[slider_key] = config["default"] |
|
|
if "custom_words" not in st.session_state: |
|
|
st.session_state["custom_words"] = [] |
|
|
|
|
|
|
|
|
col1, col2, col3 = st.columns([7,0.7,0.7]) |
|
|
with col1: |
|
|
user_input = st.text_input( |
|
|
label="Prompt", |
|
|
placeholder="Type your prompt here...", |
|
|
value=st.session_state.get("prompt_input", ""), |
|
|
key="prompt_input", |
|
|
label_visibility="collapsed" |
|
|
) |
|
|
st.markdown(""" |
|
|
<div class="stButton send-button-container" style="display: flex; gap: 2px;"> |
|
|
""", unsafe_allow_html=True) |
|
|
with col2: |
|
|
refresh_clicked = st.button("↻", key="refresh_button_text", help="Reload the page") |
|
|
with col3: |
|
|
send_clicked = st.button("→", key="send_request_button", help="Send the request") |
|
|
|
|
|
st.markdown("</div>", unsafe_allow_html=True) |
|
|
|
|
|
|
|
|
if refresh_clicked: |
|
|
|
|
|
|
|
|
for check_name, config in check_configs.items(): |
|
|
base_key = check_name.replace(' ', '_') |
|
|
st.session_state.pop(f"checkbox_{base_key}", None) |
|
|
st.session_state.pop(f"slider_{base_key}", None) |
|
|
st.session_state.pop(f"multiselect_{base_key}", None) |
|
|
st.session_state.pop(f"number_{base_key}", None) |
|
|
|
|
|
|
|
|
st.session_state.pop("custom_words", None) |
|
|
st.session_state.pop("new_custom_word", None) |
|
|
|
|
|
|
|
|
st.session_state.pop("prompt_input", None) |
|
|
|
|
|
|
|
|
st.session_state["show_output"] = False |
|
|
|
|
|
st.rerun() |
|
|
|
|
|
if send_clicked: |
|
|
user_input = st.session_state.get("prompt_input", "") |
|
|
if not user_input.strip(): |
|
|
with output_col: |
|
|
st.warning("Please enter a prompt before sending the request.") |
|
|
st.stop() |
|
|
st.session_state["show_output"] = True |
|
|
|
|
|
selected_checks_payload_ui = {} |
|
|
for check_name, config in check_configs.items(): |
|
|
base_key = check_name.replace(' ', '_') |
|
|
|
|
|
enabled = st.session_state.get(f"checkbox_{base_key}", False) |
|
|
|
|
|
if enabled: |
|
|
payload_item = {"enabled": True} |
|
|
|
|
|
if config["type"] == "slider": |
|
|
payload_item["threshold"] = st.session_state.get(f"slider_{base_key}", config["default"]) |
|
|
elif config["type"] == "number_input": |
|
|
payload_item["threshold"] = st.session_state.get(f"number_{base_key}", config["default"]) |
|
|
elif config["type"] == "multiselect": |
|
|
payload_item["threshold"] = st.session_state.get(f"slider_{base_key}", config["default"]) |
|
|
payload_item["topics"] = st.session_state.get(f"multiselect_{base_key}", config["options"][:2]) |
|
|
elif config["type"] == "multiselect_pii": |
|
|
payload_item["entities_to_block"] = st.session_state.get(f"multiselect_{base_key}", config["options"]) |
|
|
elif config["type"] == "slider_and_input": |
|
|
payload_item["threshold"] = st.session_state.get(f"slider_{base_key}", config["default"]) |
|
|
payload_item["custom_words"] = st.session_state.get("custom_words", []) |
|
|
|
|
|
selected_checks_payload_ui[check_name] = payload_item |
|
|
else: |
|
|
selected_checks_payload_ui[check_name] = {"enabled": False} |
|
|
|
|
|
st.markdown('<h3 style="font-size:20px; font-weight:600; color:var(--text-color-dark);">Select Moderation Checks to apply:</h3>', unsafe_allow_html=True) |
|
|
|
|
|
|
|
|
api_check_name_map = { |
|
|
"Prompt Injection": "PromptInjection", |
|
|
"Jailbreak": "JailBreak", |
|
|
"Toxicity": "Toxicity", |
|
|
"Profanity": "Profanity", |
|
|
"Restricted Topics": "RestrictTopic", |
|
|
"Text Quality": "TextQuality", |
|
|
"Customized Theme": "CustomizedTheme", |
|
|
"PII Detection": "Piidetct" |
|
|
} |
|
|
|
|
|
|
|
|
for check_name, config in check_configs.items(): |
|
|
base_key = check_name.replace(' ', '_') |
|
|
col1, col2 = st.columns([0.3, 0.7]) |
|
|
|
|
|
with col1: |
|
|
enabled = st.checkbox(check_name, key=f"checkbox_{base_key}") |
|
|
|
|
|
if enabled and config["type"] != "no_threshold": |
|
|
with col2: |
|
|
with st.expander(f"Configure {check_name}"): |
|
|
if config["type"] == "slider": |
|
|
|
|
|
threshold = st.slider( |
|
|
f"Set Threshold for {check_name}", |
|
|
min_value=config["min"], |
|
|
max_value=config["max"], |
|
|
step=config["step"], |
|
|
key=f"slider_{base_key}" |
|
|
) |
|
|
|
|
|
elif config["type"] == "number_input": |
|
|
profanity_threshold = st.number_input( |
|
|
f"Set Profanity Count Threshold (whole numbers only)", |
|
|
min_value=config["min"], |
|
|
key=f"number_{base_key}", |
|
|
help=f"Enter a number, e.g., {config['hint']}" |
|
|
) |
|
|
|
|
|
elif config["type"] == "multiselect": |
|
|
|
|
|
threshold = st.slider( |
|
|
f"Set Threshold for {check_name}", |
|
|
min_value=config["min"], |
|
|
max_value=config["max"], |
|
|
step=config["step"], |
|
|
key=f"slider_{base_key}" |
|
|
) |
|
|
if slider_key not in st.session_state: |
|
|
st.session_state[slider_key] = config["default"] |
|
|
|
|
|
selected_topics = st.multiselect( |
|
|
"Select Restricted Topics:", |
|
|
options=config["options"], |
|
|
key=f"multiselect_{base_key}" |
|
|
) |
|
|
|
|
|
elif config["type"] == "multiselect_pii": |
|
|
|
|
|
selected_entities = st.multiselect( |
|
|
"Select PII Entities to Block:", |
|
|
options=config["options"], |
|
|
key=f"multiselect_{base_key}" |
|
|
) |
|
|
|
|
|
elif config["type"] == "slider_and_input": |
|
|
|
|
|
threshold = st.slider( |
|
|
f"Set Threshold for {check_name}", |
|
|
min_value=config["min"], |
|
|
max_value=config["max"], |
|
|
step=config["step"], |
|
|
key=f"slider_{base_key}" |
|
|
) |
|
|
|
|
|
st.markdown("---") |
|
|
st.subheader("Custom Words for Theme") |
|
|
|
|
|
|
|
|
message_placeholder = st.empty() |
|
|
|
|
|
expander_key = f"custom_words_expander_{base_key}" |
|
|
|
|
|
def add_word_callback(): |
|
|
new_word_input = st.session_state.get("new_custom_word", "") |
|
|
new_word_to_add = new_word_input.strip() |
|
|
if new_word_to_add and new_word_to_add not in st.session_state.custom_words: |
|
|
st.session_state.custom_words.append(new_word_to_add) |
|
|
st.session_state.new_custom_word = "" |
|
|
st.session_state[expander_key] = True |
|
|
message_placeholder.success(f"'{new_word_to_add}' added!") |
|
|
elif new_word_to_add: |
|
|
st.session_state[expander_key] = True |
|
|
message_placeholder.warning("Word already exists.") |
|
|
else: |
|
|
st.session_state[expander_key] = True |
|
|
message_placeholder.warning("Word is empty.") |
|
|
|
|
|
def delete_word_callback(index): |
|
|
st.session_state.custom_words.pop(index) |
|
|
st.session_state[expander_key] = True |
|
|
message_placeholder.success("Word removed successfully!") |
|
|
|
|
|
st.text_input("Add a new custom word:", key="new_custom_word") |
|
|
st.button("Add Word", key="add_custom_word_btn", on_click=add_word_callback) |
|
|
|
|
|
if st.session_state.custom_words: |
|
|
st.write("Current Custom Words:") |
|
|
if expander_key not in st.session_state: |
|
|
st.session_state[expander_key] = True |
|
|
|
|
|
with st.expander("Show/Hide Custom Words", expanded=st.session_state.get(expander_key, False)): |
|
|
for i, word in enumerate(st.session_state.custom_words): |
|
|
word_col, btn_col = st.columns([0.8, 0.2]) |
|
|
with word_col: |
|
|
st.write(f"- {word}") |
|
|
with btn_col: |
|
|
|
|
|
st.button("del", key=f"remove_word_{i}", on_click=delete_word_callback, args=(i,)) |
|
|
else: |
|
|
st.info("No custom words added yet.") |
|
|
|
|
|
if st.session_state.show_output: |
|
|
with output_col: |
|
|
|
|
|
final_api_payload = { |
|
|
"AccountName": "None", |
|
|
"userid": "None", |
|
|
"PortfolioName": "None", |
|
|
"lotNumber": 1, |
|
|
"translate": "no", |
|
|
"EmojiModeration": "yes", |
|
|
"Prompt": user_input, |
|
|
"ModerationChecks": [], |
|
|
"ModerationCheckThresholds": {} |
|
|
} |
|
|
|
|
|
for check_name, data in selected_checks_payload_ui.items(): |
|
|
|
|
|
|
|
|
if check_name == "Customized Theme": |
|
|
final_api_payload["ModerationChecks"].append(api_check_name_map["Customized Theme"]) |
|
|
final_api_payload["ModerationCheckThresholds"]["CustomTheme"] = { |
|
|
"Themename": "string", |
|
|
"Themethresold": data.get("threshold", check_configs["Customized Theme"]["default"]), |
|
|
"ThemeTexts": data.get("custom_words", []) |
|
|
} |
|
|
|
|
|
elif data["enabled"]: |
|
|
if check_name in api_check_name_map: |
|
|
final_api_payload["ModerationChecks"].append(api_check_name_map[check_name]) |
|
|
|
|
|
|
|
|
if check_name == "Prompt Injection": |
|
|
final_api_payload["ModerationCheckThresholds"]["PromptinjectionThreshold"] = data.get("threshold") |
|
|
elif check_name == "Jailbreak": |
|
|
final_api_payload["ModerationCheckThresholds"]["JailbreakThreshold"] = data.get("threshold") |
|
|
elif check_name == "Toxicity": |
|
|
toxicity_threshold = data.get("threshold") |
|
|
final_api_payload["ModerationCheckThresholds"]["ToxicityThresholds"] = { |
|
|
"ToxicityThreshold": toxicity_threshold, |
|
|
"SevereToxicityThreshold": toxicity_threshold, |
|
|
"ObsceneThreshold": toxicity_threshold, |
|
|
"ThreatThreshold": toxicity_threshold, |
|
|
"InsultThreshold": toxicity_threshold, |
|
|
"IdentityAttackThreshold": toxicity_threshold, |
|
|
"SexualExplicitThreshold": toxicity_threshold |
|
|
} |
|
|
elif check_name == "Profanity": |
|
|
final_api_payload["ModerationCheckThresholds"]["ProfanityCountThreshold"] = data.get("threshold") |
|
|
elif check_name == "Restricted Topics": |
|
|
final_api_payload["ModerationCheckThresholds"]["RestrictedtopicDetails"] = { |
|
|
"RestrictedtopicThreshold": data.get("threshold"), |
|
|
"Restrictedtopics": data.get("topics", []) |
|
|
} |
|
|
elif check_name == "PII Detection": |
|
|
final_api_payload["ModerationCheckThresholds"]["PiientitiesConfiguredToBlock"] = data.get("entities_to_block", []) |
|
|
|
|
|
|
|
|
final_api_payload["ModerationChecks"] = list(set(final_api_payload["ModerationChecks"])) |
|
|
|
|
|
with output_col: |
|
|
|
|
|
if st.session_state.show_output: |
|
|
st.markdown('<h4 style="font-weight:550; margin-bottom:0.5rem; padding-top:2rem;">OUTPUT</h4>', unsafe_allow_html=True) |
|
|
results_placeholder = st.empty() |
|
|
|
|
|
with results_placeholder.container(): |
|
|
try: |
|
|
|
|
|
resp = requests.post("https://infosysenterprise-responsible-ai-moderationlayer.hf.space/rai/v1/moderations", json=final_api_payload) |
|
|
response_data = resp.json() |
|
|
|
|
|
if not isinstance(response_data, dict): |
|
|
st.error("API response was not a valid JSON object (dictionary).") |
|
|
st.stop() |
|
|
|
|
|
st.write("Moderation Results") |
|
|
|
|
|
overall_status = response_data.get("moderationResults", {}).get("summary", {}).get("status", "N/A") |
|
|
overall_reason = response_data.get("moderationResults", {}).get("summary", {}).get("reason", []) |
|
|
|
|
|
if overall_status.lower() == "passed": |
|
|
st.success(f"Overall Status: {overall_status.upper()}") |
|
|
else: |
|
|
st.error(f"Overall Status: {overall_status.upper()}") |
|
|
if overall_reason: |
|
|
failed_checks_str = ", ".join(overall_reason) |
|
|
st.warning(f"Failed Checks: {failed_checks_str}") |
|
|
|
|
|
st.write("Individual Check Details:") |
|
|
|
|
|
moderation_results = response_data.get("moderationResults", {}) |
|
|
if not moderation_results: |
|
|
st.info("No detailed moderation results available.") |
|
|
else: |
|
|
|
|
|
ui_to_api_check_map = { |
|
|
"Prompt Injection": "promptInjectionCheck", |
|
|
"Jailbreak": "jailbreakCheck", |
|
|
"Toxicity": "toxicityCheck", |
|
|
"Profanity": "profanityCheck", |
|
|
"Restricted Topics": "restrictedtopic", |
|
|
"Text Quality": "textQuality", |
|
|
"Customized Theme": "customThemeCheck", |
|
|
"PII Detection": "privacyCheck", |
|
|
"Refusal": "refusalCheck" |
|
|
} |
|
|
|
|
|
api_response_name_map = {v: k for k, v in ui_to_api_check_map.items()} |
|
|
|
|
|
|
|
|
moderation_results = response_data.get("moderationResults", {}) |
|
|
|
|
|
|
|
|
individual_results = { |
|
|
k: v for k, v in moderation_results.items() |
|
|
if k not in {"summary", "text"} and isinstance(v, dict) |
|
|
} |
|
|
|
|
|
|
|
|
enabled_api_checks = [] |
|
|
for ui_check, api_key in ui_to_api_check_map.items(): |
|
|
if selected_checks_payload_ui.get(ui_check, {}).get("enabled"): |
|
|
if api_key in individual_results: |
|
|
enabled_api_checks.append(api_key) |
|
|
elif ui_check == "Customized Theme": |
|
|
if st.session_state.get("custom_words") or selected_checks_payload_ui.get(ui_check, {}).get("threshold") is not None: |
|
|
if api_key in individual_results: |
|
|
enabled_api_checks.append(api_key) |
|
|
|
|
|
|
|
|
sorted_api_check_names = sorted( |
|
|
enabled_api_checks, |
|
|
key=lambda x: api_response_name_map.get(x, x) |
|
|
) |
|
|
|
|
|
|
|
|
if sorted_api_check_names: |
|
|
for check_api_name in sorted_api_check_names: |
|
|
details = individual_results.get(check_api_name, {}) |
|
|
display_name = api_response_name_map.get(check_api_name, check_api_name) |
|
|
status = details.get("result", "N/A") |
|
|
|
|
|
expander_style = "" |
|
|
if status.lower() == "passed": |
|
|
expander_style = "border-left: 5px solid green; padding-left: 10px;" |
|
|
elif status.lower() == "failed": |
|
|
expander_style = "border-left: 5px solid red; padding-left: 10px;" |
|
|
elif status.lower() == "info": |
|
|
expander_style = "border-left: 5px solid orange; padding-left: 10px;" |
|
|
|
|
|
with st.expander(f"**{display_name}** - Status: **{status.upper()}**"): |
|
|
st.markdown(f"<div style='{expander_style}'>", unsafe_allow_html=True) |
|
|
|
|
|
|
|
|
if check_api_name == "promptInjectionCheck": |
|
|
st.write(f"**Confidence Score:** `{details.get('injectionConfidenceScore', 'N/A')}`") |
|
|
st.write(f"**Threshold:** `{details.get('injectionThreshold', 'N/A')}`") |
|
|
elif check_api_name == "jailbreakCheck": |
|
|
st.write(f"**Similarity Score:** `{details.get('jailbreakSimilarityScore', 'N/A')}`") |
|
|
st.write(f"**Threshold:** `{details.get('jailbreakThreshold', 'N/A')}`") |
|
|
elif check_api_name == "toxicityCheck": |
|
|
st.write(f"**Threshold:** `{details.get('toxicitythreshold', 'N/A')}`") |
|
|
if details.get("toxicityScore"): |
|
|
st.write("**Toxicity Scores:**") |
|
|
for score_obj in details["toxicityScore"]: |
|
|
for name, score in score_obj.items(): |
|
|
st.write(f"- **{name.title()}**: `{score}`") |
|
|
elif check_api_name == "profanityCheck": |
|
|
st.write(f"**Profanity Threshold:** `{details.get('profaneWordsthreshold', 'N/A')}`") |
|
|
profane_words = details.get("profaneWordsIdentified", []) |
|
|
st.write(f"**Profane Words Identified:** {', '.join(profane_words) if profane_words else 'None'}") |
|
|
elif check_api_name == "restrictedtopic": |
|
|
st.write(f"**Topic Threshold:** `{details.get('topicThreshold', 'N/A')}`") |
|
|
scores = details.get("topicScores", []) |
|
|
if scores: |
|
|
st.write("**Detected Topics Scores:**") |
|
|
for score_dict in scores: |
|
|
for topic, score in score_dict.items(): |
|
|
st.write(f"- **{topic}:** `{score}`") |
|
|
else: |
|
|
st.write("**Detected Topics:** None") |
|
|
elif check_api_name == "textQuality": |
|
|
st.write(f"**Readability Score:** `{details.get('readabilityScore', 'N/A')}`") |
|
|
st.write(f"**Text Grade:** `{details.get('textGrade', 'N/A')}`") |
|
|
elif check_api_name == "customThemeCheck": |
|
|
st.write(f"**Similarity Score:** `{details.get('customSimilarityScore', 'N/A')}`") |
|
|
st.write(f"**Theme Threshold:** `{details.get('themeThreshold', 'N/A')}`") |
|
|
elif check_api_name == "privacyCheck": |
|
|
entities = details.get("entitiesRecognised", []) |
|
|
blocked = details.get("entitiesConfiguredToBlock", []) |
|
|
st.write(f"**Entities Recognized:** {', '.join(entities) if entities else 'None'}") |
|
|
st.write(f"**Entities Configured to Block:** {', '.join(blocked) if blocked else 'None'}") |
|
|
elif check_api_name == "refusalCheck": |
|
|
st.write(f"**Similarity Score:** `{details.get('refusalSimilarityScore', 'N/A')}`") |
|
|
st.write(f"**Threshold:** `{details.get('RefusalThreshold', 'N/A')}`") |
|
|
|
|
|
st.markdown("</div>", unsafe_allow_html=True) |
|
|
else: |
|
|
st.info("No individual checks to display.") |
|
|
|
|
|
except requests.exceptions.RequestException as e: |
|
|
st.error(f"API Request Error: {e}") |
|
|
except json.JSONDecodeError: |
|
|
st.error("Error decoding API response as JSON. Check if the API returned valid JSON.") |
|
|
except Exception as e: |
|
|
st.error(f"An unexpected error occurred: {e}.") |