Download src/streamlit_app.py from InfosysEnterprise/Moderation-playground: direct link, hf CLI and curl.
- Browser
- Download file 38 kB
-
https://huggingface.co/spaces/InfosysEnterprise/Moderation-playground/resolve/main/src/streamlit_app.py
- Command line
-
hf download hf://spaces/InfosysEnterprise/Moderation-playground/src/streamlit_app.py
-
curl -L -o streamlit_app.py https://huggingface.co/spaces/InfosysEnterprise/Moderation-playground/resolve/main/src/streamlit_app.py
38 kB
| import streamlit as st | |
| import requests | |
| import json | |
| import os | |
| #Custom CSS Injection | |
| 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") | |
| #Initialize session state for dynamic elements | |
| if 'custom_words' not in st.session_state: | |
| st.session_state.custom_words = [] | |
| # Initialize session state for output visibility | |
| if 'show_output' not in st.session_state: | |
| st.session_state.show_output = False | |
| # --- API Description (for Full Width) --- | |
| 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) | |
| #Main Layout: Two Columns for Inputs and Outputs | |
| input_col, output_col = st.columns([0.5, 0.5]) # Input on left, Output on right | |
| with input_col: | |
| st.markdown('<h4 style="font-weight:550; margin-bottom:0.5rem; padding-top:2rem;">INPUT</h4>', unsafe_allow_html=True) | |
| # Define all checks and their default properties | |
| 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"]}, | |
| } | |
| # Initialize session state for all checkboxes (default to checked) | |
| for check_name, config in check_configs.items(): | |
| base_key = check_name.replace(' ', '_') | |
| # Always ensure checkbox is initialized | |
| checkbox_key = f"checkbox_{base_key}" | |
| if checkbox_key not in st.session_state: | |
| st.session_state[checkbox_key] = True # default to checked | |
| # Set default values for controls | |
| 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"] = [] | |
| # Formatting for input box, send and refresh buttons | |
| 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) | |
| #Handle Refresh Click | |
| if refresh_clicked: | |
| # Clear all checkboxes, sliders, multiselects, etc. | |
| 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) | |
| # Clear custom word logic | |
| st.session_state.pop("custom_words", None) | |
| st.session_state.pop("new_custom_word", None) | |
| # Clear the input prompt | |
| st.session_state.pop("prompt_input", None) | |
| # Optionally: hide the output section | |
| 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) | |
| # Mapping from UI check names to API check names | |
| 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" | |
| } | |
| # Display checkboxes and conditional UIs | |
| 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": | |
| # CORRECT: Removed 'value=config["default"]' | |
| 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") | |
| # Using a single, consistent placeholder for messages | |
| 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 # Keep expander open | |
| message_placeholder.success(f"'{new_word_to_add}' added!") | |
| elif new_word_to_add: | |
| st.session_state[expander_key] = True # Keep expander open | |
| message_placeholder.warning("Word already exists.") | |
| else: | |
| st.session_state[expander_key] = True # Keep expander open | |
| message_placeholder.warning("Word is empty.") | |
| def delete_word_callback(index): | |
| st.session_state.custom_words.pop(index) | |
| st.session_state[expander_key] = True # Keep expander open | |
| 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: | |
| # Use the on_click callback for the delete button | |
| 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: | |
| # Construct the final API payload | |
| 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(): | |
| #customized theme, need to make changes here.. | |
| 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"]: # For all other checks, only include if enabled | |
| if check_name in api_check_name_map: | |
| final_api_payload["ModerationChecks"].append(api_check_name_map[check_name]) | |
| # Populate ModerationCheckThresholds for enabled checks from UI | |
| 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", []) | |
| # Ensure unique checks in ModerationChecks list | |
| 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() # Placeholder for output results | |
| with results_placeholder.container(): # Display payload in output column | |
| try: | |
| # Actual API call | |
| 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: | |
| # Mapping from UI labels to API keys | |
| 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", {}) | |
| # Filter out 'summary' and other non-check entries like 'text' | |
| individual_results = { | |
| k: v for k, v in moderation_results.items() | |
| if k not in {"summary", "text"} and isinstance(v, dict) | |
| } | |
| # Determine which checks were enabled in UI and are present in API | |
| 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) | |
| # Sort by display name | |
| sorted_api_check_names = sorted( | |
| enabled_api_checks, | |
| key=lambda x: api_response_name_map.get(x, x) | |
| ) | |
| # Display checks | |
| 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) | |
| # Add your detailed logic below | |
| 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}.") |