rkihacker commited on
Commit
fc016ef
Β·
verified Β·
1 Parent(s): aa00020

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +212 -0
app.py ADDED
@@ -0,0 +1,212 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # === Gradio Demo App: gradio_app.py ===
2
+ # This script creates a user-friendly web interface to demonstrate the
3
+ # multimodal moderation capabilities of the main FastAPI server.
4
+ #
5
+ # It interacts with the /v3/moderations endpoint.
6
+ # --------------------------------------------------------------------
7
+
8
+ import base64
9
+ import os
10
+ import json
11
+ import logging
12
+
13
+ import gradio as gr
14
+ import httpx
15
+ from dotenv import load_dotenv
16
+
17
+ # --- Configuration ---
18
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
19
+ load_dotenv()
20
+
21
+ # The URL of your running FastAPI server.
22
+ # It's crucial to set this in your .env file for deployment.
23
+ API_BASE_URL = os.environ.get("API_BASE_URL", "")
24
+ MODERATION_ENDPOINT = f"{API_BASE_URL}/v3/moderations"
25
+
26
+ # --- NEW: 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",
30
+ "Korean": "ko", "French": "fr", "Japanese": "ja", "Portuguese": "pt", "Turkish": "tr",
31
+ "Polish": "pl", "Catalan": "ca", "Dutch": "nl", "Arabic": "ar", "Swedish": "sv",
32
+ "Italian": "it", "Indonesian": "id", "Hindi": "hi", "Finnish": "fi", "Vietnamese": "vi",
33
+ "Hebrew": "he", "Ukrainian": "uk", "Greek": "el", "Malay": "ms", "Czech": "cs",
34
+ "Romanian": "ro", "Danish": "da", "Hungarian": "hu", "Tamil": "ta", "Norwegian": "no",
35
+ "Thai": "th", "Urdu": "ur", "Croatian": "hr", "Bulgarian": "bg", "Lithuanian": "lt",
36
+ "Latin": "la", "Maori": "mi", "Malayalam": "ml", "Welsh": "cy", "Slovak": "sk",
37
+ "Telugu": "te", "Persian": "fa", "Latvian": "lv", "Bengali": "bn", "Serbian": "sr",
38
+ "Azerbaijani": "az", "Slovenian": "sl", "Kannada": "kn", "Estonian": "et", "Macedonian": "mk",
39
+ "Breton": "br", "Basque": "eu", "Icelandic": "is", "Armenian": "hy", "Nepali": "ne",
40
+ "Mongolian": "mn", "Bosnian": "bs", "Kazakh": "kk", "Albanian": "sq", "Swahili": "sw",
41
+ "Galician": "gl", "Marathi": "mr", "Punjabi": "pa", "Sinhala": "si", "Khmer": "km",
42
+ "Shona": "sn", "Yoruba": "yo", "Somali": "so", "Afrikaans": "af", "Occitan": "oc",
43
+ "Georgian": "ka", "Belarusian": "be", "Tajik": "tg", "Sindhi": "sd", "Gujarati": "gu",
44
+ "Amharic": "am", "Yiddish": "yi", "Lao": "lo", "Uzbek": "uz", "Faroese": "fo",
45
+ "Haitian Creole": "ht", "Pashto": "ps", "Turkmen": "tk", "Nynorsk": "nn", "Maltese": "mt",
46
+ "Sanskrit": "sa", "Luxembourgish": "lb", "Myanmar (Burmese)": "my", "Tibetan": "bo",
47
+ "Tagalog": "tl", "Malagasy": "mg", "Assamese": "as", "Tatar": "tt", "Hawaiian": "haw",
48
+ "Lingala": "ln", "Hausa": "ha", "Bashkir": "ba", "Javanese": "jw", "Sundanese": "su",
49
+ }
50
+ # Sort languages alphabetically for the dropdown
51
+ SORTED_LANGUAGES = dict(sorted(WHISPER_LANGUAGES.items()))
52
+
53
+
54
+ # --- Helper Function ---
55
+ def file_to_base64(filepath: str) -> str:
56
+ """Reads a file and converts it to a base64 encoded string."""
57
+ if not filepath:
58
+ return None
59
+ try:
60
+ with open(filepath, "rb") as f:
61
+ encoded_string = base64.b64encode(f.read()).decode("utf-8")
62
+ return encoded_string
63
+ except Exception as e:
64
+ logging.error(f"Failed to convert file {filepath} to base64: {e}")
65
+ return None
66
+
67
+ # --- Core Logic ---
68
+ def moderate_content(text_input, image_input, video_input, audio_input, language_full_name):
69
+ """
70
+ Prepares the payload, calls the moderation API, and formats the response.
71
+ """
72
+ if not any([text_input, image_input, video_input, audio_input]):
73
+ return "Please provide at least one input (text, image, video, or audio).", None
74
+
75
+ logging.info("Preparing payload for moderation API...")
76
+ payload = {
77
+ "model": "nai-moderation-latest" # This is the model name expected by our API
78
+ }
79
+
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
87
+
88
+ video_b64 = file_to_base64(video_input)
89
+ if video_b64:
90
+ payload["video"] = video_b64
91
+
92
+ audio_b64 = file_to_base64(audio_input)
93
+ if audio_b64:
94
+ payload["voice"] = audio_b64
95
+ # --- NEW: Add selected language to the payload ---
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 an exception for 4xx/5xx errors
111
+
112
+ data = response.json()
113
+ full_response_output = data
114
+
115
+ if not data.get("results"):
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
+
131
+ summary_output = f"""
132
+ **Moderation Status:** {status}
133
+ ---
134
+ **Reason:** {reason}
135
+ ---
136
+ **Flagged Categories:** {categories_str}
137
+ ---
138
+ **Transcribed Text (from audio):**
139
+ {transcribed}
140
+ """
141
+ logging.info("Successfully received and parsed moderation response.")
142
+
143
+ except httpx.HTTPStatusError as e:
144
+ error_details = e.response.text
145
+ summary_output = f"HTTP Error: {e.response.status_code}\n\nCould not connect to the moderation service or the service returned an error.\n\nDetails:\n{error_details}"
146
+ logging.error(f"HTTP Status Error: {error_details}")
147
+ except httpx.RequestError as e:
148
+ summary_output = f"Request Error: Could not connect to the API server at {API_BASE_URL}.\nPlease ensure the server is running and the URL is correct."
149
+ logging.error(f"Request Error: {e}")
150
+ except Exception as e:
151
+ summary_output = f"An unexpected error occurred: {str(e)}"
152
+ logging.error(f"Unexpected Error: {e}", exc_info=True)
153
+
154
+ return summary_output, full_response_output
155
+
156
+ # --- Gradio Interface ---
157
+ with gr.Blocks(theme=gr.themes.Soft(), css="footer {display: none !important}") as demo:
158
+ gr.Markdown(
159
+ """
160
+ # πŸ€– Multimodal Content Moderation Demo
161
+ This demo uses a custom API server to perform advanced content moderation.
162
+ You can provide any combination of text, image, video, and audio. The system will analyze all inputs together.
163
+ """
164
+ )
165
+
166
+ with gr.Row():
167
+ with gr.Column(scale=1):
168
+ gr.Markdown("### 1. Provide Your Content")
169
+ text_input = gr.Textbox(label="Text Input", lines=4, placeholder="Enter any text here...")
170
+ image_input = gr.Image(label="Image Input", type="filepath")
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()),
178
+ value="English",
179
+ interactive=True
180
+ )
181
+
182
+ submit_button = gr.Button("Moderate Content", variant="primary")
183
+
184
+ with gr.Column(scale=2):
185
+ gr.Markdown("### 2. See the Results")
186
+ result_output = gr.Markdown(label="Moderation Summary")
187
+ full_response_output = gr.JSON(label="Full API Response")
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
+ )
195
+
196
+ gr.Examples(
197
+ examples=[
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":
211
+ logging.warning("API_BASE_URL is set to the default local address. Make sure this is correct or set it in your .env file.")
212
+ demo.launch(server_name="0.0.0.0", server_port=7860)