Update app.py
Browse files
app.py
CHANGED
|
@@ -23,7 +23,7 @@ load_dotenv()
|
|
| 23 |
API_BASE_URL = os.environ.get("API_BASE_URL", "")
|
| 24 |
MODERATION_ENDPOINT = f"{API_BASE_URL}/v3/moderations"
|
| 25 |
|
| 26 |
-
# ---
|
| 27 |
# Mapping user-friendly names to ISO 639-1 codes
|
| 28 |
WHISPER_LANGUAGES = {
|
| 29 |
"English": "en", "Chinese": "zh", "German": "de", "Spanish": "es", "Russian": "ru",
|
|
@@ -80,7 +80,6 @@ def moderate_content(text_input, image_input, video_input, audio_input, language
|
|
| 80 |
if text_input:
|
| 81 |
payload["input"] = text_input
|
| 82 |
|
| 83 |
-
# Gradio provides file paths; we need to convert them to base64
|
| 84 |
image_b64 = file_to_base64(image_input)
|
| 85 |
if image_b64:
|
| 86 |
payload["image"] = image_b64
|
|
@@ -92,22 +91,19 @@ def moderate_content(text_input, image_input, video_input, audio_input, language
|
|
| 92 |
audio_b64 = file_to_base64(audio_input)
|
| 93 |
if audio_b64:
|
| 94 |
payload["voice"] = audio_b64
|
| 95 |
-
|
| 96 |
-
language_code = SORTED_LANGUAGES.get(language_full_name, "en") # Default to 'en' if not found
|
| 97 |
payload["language"] = language_code
|
| 98 |
logging.info(f"Audio detected. Using language: {language_full_name} ({language_code})")
|
| 99 |
|
| 100 |
-
|
| 101 |
logging.info(f"Sending request to {MODERATION_ENDPOINT} with inputs: {list(payload.keys())}")
|
| 102 |
|
| 103 |
summary_output = "An error occurred. Please check the logs."
|
| 104 |
full_response_output = {}
|
| 105 |
|
| 106 |
try:
|
| 107 |
-
# Using a synchronous client is simpler for this Gradio function
|
| 108 |
with httpx.Client(timeout=180.0) as client:
|
| 109 |
response = client.post(MODERATION_ENDPOINT, json=payload)
|
| 110 |
-
response.raise_for_status() # Raises
|
| 111 |
|
| 112 |
data = response.json()
|
| 113 |
full_response_output = data
|
|
@@ -116,15 +112,11 @@ def moderate_content(text_input, image_input, video_input, audio_input, language
|
|
| 116 |
summary_output = "API returned an empty result. This might happen if media processing fails (e.g., a video with no frames)."
|
| 117 |
return summary_output, full_response_output
|
| 118 |
|
| 119 |
-
# The v3 endpoint returns a single aggregated result
|
| 120 |
result = data["results"][0]
|
| 121 |
|
| 122 |
-
# Format a nice, human-readable summary
|
| 123 |
status = "π¨ FLAGGED π¨" if result["flagged"] else "β
SAFE β
"
|
| 124 |
reason = result.get("reason") or "N/A"
|
| 125 |
transcribed = result.get("transcribed_text") or "N/A"
|
| 126 |
-
|
| 127 |
-
# Create a clean list of flagged categories
|
| 128 |
flagged_categories = [cat for cat, flagged in result.get("categories", {}).items() if flagged]
|
| 129 |
categories_str = ", ".join(flagged_categories) if flagged_categories else "None"
|
| 130 |
|
|
@@ -140,16 +132,55 @@ def moderate_content(text_input, image_input, video_input, audio_input, language
|
|
| 140 |
"""
|
| 141 |
logging.info("Successfully received and parsed moderation response.")
|
| 142 |
|
|
|
|
| 143 |
except httpx.HTTPStatusError as e:
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
except httpx.RequestError as e:
|
| 148 |
-
|
| 149 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 150 |
except Exception as e:
|
| 151 |
-
|
| 152 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 153 |
|
| 154 |
return summary_output, full_response_output
|
| 155 |
|
|
@@ -171,7 +202,6 @@ with gr.Blocks(theme=gr.themes.Soft(), css="footer {display: none !important}")
|
|
| 171 |
video_input = gr.Video(label="Video Input")
|
| 172 |
audio_input = gr.Audio(label="Voice/Audio Input", type="filepath")
|
| 173 |
|
| 174 |
-
# --- NEW: Language selection dropdown ---
|
| 175 |
language_input = gr.Dropdown(
|
| 176 |
label="Audio Language (if providing audio)",
|
| 177 |
choices=list(SORTED_LANGUAGES.keys()),
|
|
@@ -188,7 +218,6 @@ with gr.Blocks(theme=gr.themes.Soft(), css="footer {display: none !important}")
|
|
| 188 |
|
| 189 |
submit_button.click(
|
| 190 |
fn=moderate_content,
|
| 191 |
-
# --- UPDATED: Add language_input to the list ---
|
| 192 |
inputs=[text_input, image_input, video_input, audio_input, language_input],
|
| 193 |
outputs=[result_output, full_response_output]
|
| 194 |
)
|
|
@@ -198,13 +227,11 @@ with gr.Blocks(theme=gr.themes.Soft(), css="footer {display: none !important}")
|
|
| 198 |
["This is a test of the system with safe text.", None, None, None, "English"],
|
| 199 |
["I am going to kill the process on my computer.", None, None, None, "English"],
|
| 200 |
],
|
| 201 |
-
# --- UPDATED: Add language_input to the list ---
|
| 202 |
inputs=[text_input, image_input, video_input, audio_input, language_input],
|
| 203 |
outputs=[result_output, full_response_output],
|
| 204 |
fn=moderate_content
|
| 205 |
)
|
| 206 |
|
| 207 |
-
|
| 208 |
if __name__ == "__main__":
|
| 209 |
logging.info(f"Connecting to API server at: {API_BASE_URL}")
|
| 210 |
if API_BASE_URL == "http://127.0.0.1:8000":
|
|
|
|
| 23 |
API_BASE_URL = os.environ.get("API_BASE_URL", "")
|
| 24 |
MODERATION_ENDPOINT = f"{API_BASE_URL}/v3/moderations"
|
| 25 |
|
| 26 |
+
# --- Full list of Whisper V3 supported languages ---
|
| 27 |
# Mapping user-friendly names to ISO 639-1 codes
|
| 28 |
WHISPER_LANGUAGES = {
|
| 29 |
"English": "en", "Chinese": "zh", "German": "de", "Spanish": "es", "Russian": "ru",
|
|
|
|
| 80 |
if text_input:
|
| 81 |
payload["input"] = text_input
|
| 82 |
|
|
|
|
| 83 |
image_b64 = file_to_base64(image_input)
|
| 84 |
if image_b64:
|
| 85 |
payload["image"] = image_b64
|
|
|
|
| 91 |
audio_b64 = file_to_base64(audio_input)
|
| 92 |
if audio_b64:
|
| 93 |
payload["voice"] = audio_b64
|
| 94 |
+
language_code = SORTED_LANGUAGES.get(language_full_name, "en") # Default to 'en'
|
|
|
|
| 95 |
payload["language"] = language_code
|
| 96 |
logging.info(f"Audio detected. Using language: {language_full_name} ({language_code})")
|
| 97 |
|
|
|
|
| 98 |
logging.info(f"Sending request to {MODERATION_ENDPOINT} with inputs: {list(payload.keys())}")
|
| 99 |
|
| 100 |
summary_output = "An error occurred. Please check the logs."
|
| 101 |
full_response_output = {}
|
| 102 |
|
| 103 |
try:
|
|
|
|
| 104 |
with httpx.Client(timeout=180.0) as client:
|
| 105 |
response = client.post(MODERATION_ENDPOINT, json=payload)
|
| 106 |
+
response.raise_for_status() # Raises HTTPStatusError for 4xx/5xx responses
|
| 107 |
|
| 108 |
data = response.json()
|
| 109 |
full_response_output = data
|
|
|
|
| 112 |
summary_output = "API returned an empty result. This might happen if media processing fails (e.g., a video with no frames)."
|
| 113 |
return summary_output, full_response_output
|
| 114 |
|
|
|
|
| 115 |
result = data["results"][0]
|
| 116 |
|
|
|
|
| 117 |
status = "π¨ FLAGGED π¨" if result["flagged"] else "β
SAFE β
"
|
| 118 |
reason = result.get("reason") or "N/A"
|
| 119 |
transcribed = result.get("transcribed_text") or "N/A"
|
|
|
|
|
|
|
| 120 |
flagged_categories = [cat for cat, flagged in result.get("categories", {}).items() if flagged]
|
| 121 |
categories_str = ", ".join(flagged_categories) if flagged_categories else "None"
|
| 122 |
|
|
|
|
| 132 |
"""
|
| 133 |
logging.info("Successfully received and parsed moderation response.")
|
| 134 |
|
| 135 |
+
# --- MODIFIED: Enhanced Error Handling ---
|
| 136 |
except httpx.HTTPStatusError as e:
|
| 137 |
+
# Catches errors returned by the backend API (e.g., 422, 500)
|
| 138 |
+
user_message = "The moderation service returned an error."
|
| 139 |
+
error_details = ""
|
| 140 |
+
try:
|
| 141 |
+
# Try to parse the JSON error response from the server
|
| 142 |
+
error_json = e.response.json()
|
| 143 |
+
detail = error_json.get("detail", "No specific error detail provided.")
|
| 144 |
+
error_details = f"**Reason:** {detail}"
|
| 145 |
+
full_response_output = {"error": "Backend API Error", "status_code": e.response.status_code, "details": error_json}
|
| 146 |
+
except (json.JSONDecodeError, AttributeError):
|
| 147 |
+
# Fallback for non-JSON or unexpected error formats
|
| 148 |
+
error_details = f"**Raw Server Response:**\n```\n{e.response.text}\n```"
|
| 149 |
+
full_response_output = {"error": "Backend API Error", "status_code": e.response.status_code, "details": e.response.text}
|
| 150 |
+
|
| 151 |
+
summary_output = f"""
|
| 152 |
+
**π« Error from Moderation Service (HTTP {e.response.status_code})**
|
| 153 |
+
---
|
| 154 |
+
{user_message}
|
| 155 |
+
|
| 156 |
+
{error_details}
|
| 157 |
+
"""
|
| 158 |
+
logging.error(f"HTTP Status Error: {e.response.status_code} - Response: {e.response.text}")
|
| 159 |
+
|
| 160 |
except httpx.RequestError as e:
|
| 161 |
+
# Catches network errors (e.g., server is down, DNS issues)
|
| 162 |
+
summary_output = f"""
|
| 163 |
+
**π Connection Error**
|
| 164 |
+
---
|
| 165 |
+
Could not connect to the API server at `{API_BASE_URL}`.
|
| 166 |
+
|
| 167 |
+
Please ensure the backend server is running and the URL is configured correctly in your `.env` file.
|
| 168 |
+
"""
|
| 169 |
+
full_response_output = {"error": "Connection Error", "url": API_BASE_URL, "details": str(e)}
|
| 170 |
+
logging.error(f"Request Error: Could not connect to {API_BASE_URL}. Details: {e}")
|
| 171 |
+
|
| 172 |
except Exception as e:
|
| 173 |
+
# A catch-all for any other unexpected errors in this Gradio script
|
| 174 |
+
summary_output = f"""
|
| 175 |
+
**π₯ An Unexpected Application Error Occurred**
|
| 176 |
+
---
|
| 177 |
+
An error happened within the Gradio application itself.
|
| 178 |
+
Please check the application logs for more details.
|
| 179 |
+
|
| 180 |
+
**Error Type:** `{type(e).__name__}`
|
| 181 |
+
"""
|
| 182 |
+
full_response_output = {"error": "Gradio App Internal Error", "type": type(e).__name__, "details": str(e)}
|
| 183 |
+
logging.error(f"Unexpected Error in Gradio App: {e}", exc_info=True)
|
| 184 |
|
| 185 |
return summary_output, full_response_output
|
| 186 |
|
|
|
|
| 202 |
video_input = gr.Video(label="Video Input")
|
| 203 |
audio_input = gr.Audio(label="Voice/Audio Input", type="filepath")
|
| 204 |
|
|
|
|
| 205 |
language_input = gr.Dropdown(
|
| 206 |
label="Audio Language (if providing audio)",
|
| 207 |
choices=list(SORTED_LANGUAGES.keys()),
|
|
|
|
| 218 |
|
| 219 |
submit_button.click(
|
| 220 |
fn=moderate_content,
|
|
|
|
| 221 |
inputs=[text_input, image_input, video_input, audio_input, language_input],
|
| 222 |
outputs=[result_output, full_response_output]
|
| 223 |
)
|
|
|
|
| 227 |
["This is a test of the system with safe text.", None, None, None, "English"],
|
| 228 |
["I am going to kill the process on my computer.", None, None, None, "English"],
|
| 229 |
],
|
|
|
|
| 230 |
inputs=[text_input, image_input, video_input, audio_input, language_input],
|
| 231 |
outputs=[result_output, full_response_output],
|
| 232 |
fn=moderate_content
|
| 233 |
)
|
| 234 |
|
|
|
|
| 235 |
if __name__ == "__main__":
|
| 236 |
logging.info(f"Connecting to API server at: {API_BASE_URL}")
|
| 237 |
if API_BASE_URL == "http://127.0.0.1:8000":
|