sameernotes commited on
Commit
1b967ef
·
verified ·
1 Parent(s): 2854bbf

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +340 -132
app.py CHANGED
@@ -1,149 +1,357 @@
1
- import os
2
- from typing import List
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
 
4
- from fastapi import FastAPI, HTTPException, status
5
- from pydantic import BaseModel
6
- import requests
7
- from fastapi.responses import HTMLResponse # Import HTMLResponse
 
8
 
9
- app = FastAPI()
 
10
 
11
- # Load API key from environment variable
12
- GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY")
13
- if not GEMINI_API_KEY:
14
- raise ValueError("The GEMINI_API_KEY environment variable is not set.")
15
 
16
- # Gemini API endpoint (using the v1beta endpoint for text generation)
17
- # Using gemini-2.0-flash-lite for speed
18
- GEMINI_API_URL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-lite:generateContent?key=" + GEMINI_API_KEY
19
 
 
20
 
21
- class TranslationRequest(BaseModel):
22
- text: str
23
- target_language: str
24
- source_language: str = None
 
 
25
 
 
26
 
27
- class TranslationResponse(BaseModel): # Still useful for /detect_language
28
- translated_text: str
29
- source_language: str
30
- target_language: str
 
 
31
 
 
32
 
33
- # --- Helper Functions ---
 
 
 
 
 
 
 
34
 
35
- def detect_language_and_options(text: str):
36
- """Detects the language and provides translation options using the requests library."""
37
 
38
- prompt = f"""Please identify the language of the text provided and then offer translation options as numbered choices (1-5). Use this format: "The text is in [Language]. Choose a language to translate to: 1. [Option 1], 2. [Option 2], 3. [Option 3], 4. [Option 4], 5. [Option 5]"
39
 
40
- Input Text: {text}""" # Include the input text in the prompt
41
 
42
- request_data = {
43
- "contents": [{
44
- "role": "user",
45
- "parts": [{"text": prompt}]
46
- }]
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  }
48
 
49
- try:
50
- response = requests.post(GEMINI_API_URL, json=request_data)
51
- response.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx)
52
- response_json = response.json()
53
-
54
- # Robust parsing, handling potential errors from the API
55
- try:
56
- # Accessing the response text correctly. This is the most likely path.
57
- response_text = response_json['candidates'][0]['content']['parts'][0]['text']
58
- source_language = response_text.split("The text is in ")[1].split(".")[0].strip()
59
- options_str = response_text.split("Choose a language to translate to:")[1].strip()
60
- options_list = [opt.split(". ")[1].strip() for opt in options_str.split(", ")]
61
- while len(options_list) < 5:
62
- options_list.append("Option Not Available")
63
- options_list = options_list[:5]
64
- options = {str(i + 1): lang for i, lang in enumerate(options_list)}
65
- return source_language, options
66
- except (KeyError, IndexError, AttributeError) as e: # Handle common parsing errors
67
- raise HTTPException(status_code=500, detail=f"Error parsing Gemini API response: {e}")
68
-
69
- except requests.exceptions.RequestException as e:
70
- raise HTTPException(status_code=500, detail=f"Error communicating with Gemini API: {e}")
71
- except Exception as e: #Catch any other unexpected error
72
- raise HTTPException(status_code=500, detail=f"An unexpected error occurred: {e}")
73
-
74
- def translate_with_gemini(text: str, source_language: str, target_language: str) -> str:
75
- """Translates text, requesting HTML, and handles it correctly."""
76
-
77
- prompt = f"""Translate the following text from {source_language} to {target_language}. Return *only* the translated text. Format the response as HTML. If any part of the translated text should be emphasized (like an example or key phrase), use `<strong>` tags around that part. Do not include ANY additional text like 'Here is the translation:' or similar. Only the translated text, formatted as HTML.
78
-
79
- Text:
80
- {text}"""
81
-
82
- request_data = {
83
- "contents": [{
84
- "role": "user",
85
- "parts": [{"text": prompt}]
86
- }]
87
- }
88
-
89
- try:
90
- response = requests.post(GEMINI_API_URL, json=request_data)
91
- response.raise_for_status()
92
- response_json = response.json()
93
-
94
- try:
95
- translated_text = response_json['candidates'][0]['content']['parts'][0]['text']
96
- return translated_text # Return the raw HTML string
97
- except (KeyError, IndexError) as e:
98
- raise HTTPException(status_code=500, detail=f"Error parsing Gemini API response: {e}")
99
- except requests.exceptions.RequestException as e:
100
- raise HTTPException(status_code=500, detail=f"Error communicating with Gemini API: {e}")
101
- except Exception as e:
102
- raise HTTPException(status_code=500, detail=f"An unexpected error occurred: {e}")
103
-
104
-
105
-
106
-
107
- @app.post("/translate", response_class=HTMLResponse, status_code=status.HTTP_200_OK)
108
- async def translate(request: TranslationRequest):
109
- """Translates text, returning HTML."""
110
-
111
- if not request.text:
112
- raise HTTPException(status_code=400, detail="Text to translate cannot be empty.")
113
- if not request.target_language:
114
- raise HTTPException(status_code=400, detail="Target language must be provided.")
115
 
116
- if request.source_language:
117
- source_language = request.source_language
118
- else:
119
- try:
120
- source_language, _ = detect_language_and_options(request.text)
121
- except HTTPException as e:
122
- raise e
123
- supported_languages = ["Afrikaans","Arabic","Armenian","Azerbaijani","Belarusian","Bosnian","Bulgarian","Catalan","Chinese","Croatian","Czech","Danish","Dutch","English","Estonian","Finnish","French","Galician","German","Greek","Hebrew","Hindi","Hungarian","Icelandic","Indonesian","Italian","Japanese","Kannada","Kazakh","Korean","Latvian","Lithuanian","Macedonian","Malay","Marathi","Maori","Nepali","Norwegian","Persian","Polish","Portuguese","Romanian","Russian","Serbian","Slovak","Slovenian","Spanish","Swahili","Swedish","Tagalog","Tamil","Thai","Turkish","Ukrainian","Urdu","Vietnamese","Welsh",]
124
-
125
- if request.target_language not in supported_languages:
126
- raise HTTPException(status_code=400, detail=f"Target language '{request.target_language}' is not supported. Supported languages are: {', '.join(supported_languages)}")
127
-
128
-
129
- try:
130
- translated_html = translate_with_gemini(request.text, source_language, request.target_language)
131
- return translated_html # FastAPI handles the HTMLResponse correctly
132
- except HTTPException as e: # Catch specific HTTP exceptions
133
- raise e
134
- except Exception as e: #Catch any other unexpected error
135
- raise HTTPException(status_code=500, detail=f"An unexpected error occurred: {e}")
136
-
137
-
138
- @app.post("/detect_language", status_code=status.HTTP_200_OK)
139
- async def detect_language(text: str = ""):
140
- """Detects the language of the input text and provides translation options."""
141
- if not text:
142
- raise HTTPException(status_code=400, detail="Text to detect cannot be empty.")
143
- try:
144
- source_language, options = detect_language_and_options(text)
145
- return {"source_language": source_language, "translation_options": options}
146
- except HTTPException as e:
147
- raise e
148
- except Exception as e:
149
- raise HTTPException(status_code=500,detail=f"An unexpected error occurred {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Translation API</title>
7
+ <link rel="stylesheet" href="style.css">
8
+ <style>
9
+ /* Add some basic styles for responsiveness and layout */
10
+ body {
11
+ font-family: sans-serif;
12
+ margin: 0;
13
+ padding: 20px;
14
+ background-color: #f4f4f4;
15
+ color: #333;
16
+ }
17
+
18
+ .container {
19
+ max-width: 800px;
20
+ margin: auto;
21
+ background-color: #fff;
22
+ padding: 20px;
23
+ border-radius: 8px;
24
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
25
+ }
26
+
27
+ h1, h2, h3 {
28
+ color: #444;
29
+ }
30
+
31
+ label {
32
+ display: block;
33
+ margin-top: 1em;
34
+ font-weight: bold;
35
+ }
36
+
37
+ input[type="text"],
38
+ textarea,
39
+ select {
40
+ width: 100%;
41
+ padding: 8px;
42
+ margin-top: 5px;
43
+ border: 1px solid #ccc;
44
+ border-radius: 4px;
45
+ box-sizing: border-box; /* Include padding and border in the element's total width and height */
46
+ }
47
+
48
+ button {
49
+ background-color: #007bff;
50
+ color: white;
51
+ padding: 10px 15px;
52
+ border: none;
53
+ border-radius: 4px;
54
+ cursor: pointer;
55
+ margin-top: 1em;
56
+ }
57
+
58
+ button:hover {
59
+ background-color: #0056b3;
60
+ }
61
+
62
+ #result {
63
+ margin-top: 2em;
64
+ padding: 1em;
65
+ border: 1px solid #ddd;
66
+ background-color: #f9f9f9;
67
+ border-radius: 4px;
68
+ display: none; /* Hide initially */
69
+ }
70
+
71
+ #error-message {
72
+ color: red;
73
+ margin-top: 1em;
74
+ }
75
+
76
+ .loading-indicator {
77
+ display: none; /* Initially hide the loading indicator */
78
+ margin-top: 1em;
79
+ color: #007bff;
80
+ }
81
+
82
+ /* style for the tabs */
83
+ .tab {
84
+ overflow: hidden;
85
+ border: 1px solid #ccc;
86
+ background-color: #f1f1f1;
87
+ border-radius: 4px 4px 0 0; /* Rounded top corners */
88
+ }
89
+
90
+ .tab button {
91
+ background-color: inherit;
92
+ float: left;
93
+ border: none;
94
+ outline: none;
95
+ cursor: pointer;
96
+ padding: 14px 16px;
97
+ transition: 0.3s;
98
+ font-size: 17px;
99
+ border-right: 1px solid #ccc; /* Add a separator between buttons */
100
+ }
101
+
102
+ .tab button:last-child {
103
+ border-right: none; /* Remove border from the last button */
104
+ }
105
+
106
+ .tab button:hover {
107
+ background-color: #ddd;
108
+ }
109
+
110
+ .tab button.active {
111
+ background-color: #ccc;
112
+ }
113
+
114
+ .tabcontent {
115
+ display: none;
116
+ padding: 6px 12px;
117
+ border: 1px solid #ccc;
118
+ border-top: none;
119
+ border-radius: 0 0 4px 4px; /* Rounded bottom corners */
120
+ }
121
+
122
+ /* Hide by default, use JS to show */
123
+ #detection-result, #translation-result {
124
+ display: none;
125
+ }
126
+
127
+ </style>
128
+ </head>
129
+ <body>
130
+ <div class="container">
131
+ <h1>Translation and Language Detection API</h1>
132
+
133
+ <!-- Tabs -->
134
+ <div class="tab">
135
+ <button class="tablinks active" onclick="openTab(event, 'translate')">Translate</button>
136
+ <button class="tablinks" onclick="openTab(event, 'detect')">Detect Language</button>
137
+ </div>
138
 
139
+ <!-- Translation Tab Content -->
140
+ <div id="translate" class="tabcontent" style="display: block;"> <!-- translate tab set display block -->
141
+ <h2>Translate Text</h2>
142
+ <label for="text-to-translate">Text to Translate:</label>
143
+ <textarea id="text-to-translate" rows="4" placeholder="Enter text here..."></textarea>
144
 
145
+ <label for="source-language">Source Language (optional, auto-detect if empty):</label>
146
+ <input type="text" id="source-language" placeholder="e.g., English, en, auto">
147
 
148
+ <label for="target-language">Target Language:</label>
149
+ <input type="text" id="target-language" placeholder="e.g., Spanish, es">
 
 
150
 
151
+ <button id="translate-button">Translate</button>
 
 
152
 
153
+ <div class="loading-indicator" id="translation-loading">Loading...</div>
154
 
155
+ <div id="translation-result">
156
+ <h3>Translation Result:</h3>
157
+ <p><strong>Source Language:</strong> <span id="detected-source-language"></span></p>
158
+ <p><strong>Target Language:</strong> <span id="translation-target-language"></span></p>
159
+ <p><strong>Translated Text:</strong> <span id="translated-text"></span></p>
160
+ </div>
161
 
162
+ </div> <!-- end of translation tab -->
163
 
164
+ <!-- Detect Language Tab Content -->
165
+ <div id="detect" class="tabcontent">
166
+ <h2>Detect Language</h2>
167
+ <label for="text-to-detect">Text to Detect:</label>
168
+ <textarea id="text-to-detect" rows="4" placeholder="Enter text here..."></textarea>
169
+ <button id="detect-button">Detect Language</button>
170
 
171
+ <div class="loading-indicator" id="detection-loading">Loading...</div>
172
 
173
+ <div id="detection-result">
174
+ <h3>Detection Result:</h3>
175
+ <p><strong>Detected Language:</strong> <span id="detected-language"></span></p>
176
+ <div id="translation-options-container">
177
+ <h4>Translation Options:</h4>
178
+ <ul id="translation-options-list"></ul>
179
+ </div>
180
+ </div>
181
 
182
+ </div> <!-- end of language detection tab-->
 
183
 
 
184
 
185
+ <div id="error-message"></div>
186
 
187
+ </div>
188
+
189
+ <script>
190
+ // --- API Base URL (Replace with your actual base URL) ---
191
+ const API_BASE_URL = 'https://sameernotes-translation-prediction-space.hf.space'; // Replace with your actual hosted URL or localhost
192
+ const supportedLanguages = ["Afrikaans","Arabic","Armenian","Azerbaijani","Belarusian","Bosnian","Bulgarian","Catalan","Chinese","Croatian","Czech","Danish","Dutch","English","Estonian","Finnish","French","Galician","German","Greek","Hebrew","Hindi","Hungarian","Icelandic","Indonesian","Italian","Japanese","Kannada","Kazakh","Korean","Latvian","Lithuanian","Macedonian","Malay","Marathi","Maori","Nepali","Norwegian","Persian","Polish","Portuguese","Romanian","Russian","Serbian","Slovak","Slovenian","Spanish","Swahili","Swedish","Tagalog","Tamil","Thai","Turkish","Ukrainian","Urdu","Vietnamese","Welsh",]; // Add all supported languages
193
+
194
+ // --- Helper Functions ---
195
+
196
+ // Function to show error messages.
197
+ function showError(message) {
198
+ document.getElementById('error-message').textContent = message;
199
+ document.getElementById('error-message').style.display = 'block';
200
+ }
201
+ // Function to clear error messages.
202
+ function clearError() {
203
+ document.getElementById('error-message').textContent = '';
204
+ document.getElementById('error-message').style.display = 'none';
205
  }
206
 
207
+ // Function to open a tab and display the corresponding content
208
+ function openTab(evt, tabName) {
209
+ let i, tabcontent, tablinks;
210
+ tabcontent = document.getElementsByClassName("tabcontent");
211
+ for (i = 0; i < tabcontent.length; i++) {
212
+ tabcontent[i].style.display = "none";
213
+ }
214
+ tablinks = document.getElementsByClassName("tablinks");
215
+ for (i = 0; i < tablinks.length; i++) {
216
+ tablinks[i].className = tablinks[i].className.replace(" active", "");
217
+ }
218
+ document.getElementById(tabName).style.display = "block";
219
+ evt.currentTarget.className += " active";
220
+
221
+ // Hide results when switching tabs
222
+ document.getElementById('translation-result').style.display = 'none';
223
+ document.getElementById('detection-result').style.display = 'none';
224
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
225
 
226
+ // --- Translation Logic ---
227
+ document.getElementById('translate-button').addEventListener('click', async () => {
228
+ const text = document.getElementById('text-to-translate').value;
229
+ const sourceLanguage = document.getElementById('source-language').value;
230
+ const targetLanguage = document.getElementById('target-language').value;
231
+
232
+ clearError();
233
+ document.getElementById('translation-loading').style.display = 'block';
234
+ document.getElementById('translation-result').style.display = 'none';
235
+
236
+
237
+ if (!text) {
238
+ showError('Please enter text to translate.');
239
+ document.getElementById('translation-loading').style.display = 'none';
240
+ return;
241
+ }
242
+ if (!targetLanguage) {
243
+ showError('Please enter the target language.');
244
+ document.getElementById('translation-loading').style.display = 'none';
245
+ return;
246
+ }
247
+
248
+ // Check if the target language is supported
249
+ if (!supportedLanguages.includes(targetLanguage) )
250
+ {
251
+ showError(`Target language '${targetLanguage}' is not supported. Supported languages are: ${supportedLanguages.join(', ')}`);
252
+ document.getElementById('translation-loading').style.display = 'none';
253
+ return;
254
+ }
255
+
256
+ try {
257
+ const response = await fetch(`${API_BASE_URL}/translate`, {
258
+ method: 'POST',
259
+ headers: {
260
+ 'Content-Type': 'application/json',
261
+ 'accept': 'application/json' // Explicitly set the accept header
262
+ },
263
+ body: JSON.stringify({
264
+ text: text,
265
+ source_language: sourceLanguage || undefined, // Send undefined if empty
266
+ target_language: targetLanguage
267
+ })
268
+ });
269
+
270
+ if (!response.ok) {
271
+ const errorData = await response.json(); // Get error details
272
+ showError(`Translation failed: ${errorData.detail}`); // Use the 'detail' from the error response
273
+ return;
274
+ }
275
+
276
+ const data = await response.json();
277
+
278
+ document.getElementById('detected-source-language').textContent = data.source_language;
279
+ document.getElementById('translation-target-language').textContent = data.target_language;
280
+ document.getElementById('translated-text').textContent = data.translated_text;
281
+ document.getElementById('translation-result').style.display = 'block';
282
+
283
+ } catch (error) {
284
+ showError(`Error: ${error.message}`);
285
+ console.error('Error during translation:', error);
286
+
287
+ } finally {
288
+ document.getElementById('translation-loading').style.display = 'none';
289
+ }
290
+ });
291
+
292
+
293
+
294
+ // --- Language Detection Logic ---
295
+
296
+ document.getElementById('detect-button').addEventListener('click', async () => {
297
+ const text = document.getElementById('text-to-detect').value;
298
+
299
+ clearError();
300
+ document.getElementById('detection-loading').style.display = 'block';
301
+ document.getElementById('detection-result').style.display = 'none'; // Hide previous results
302
+
303
+ if (!text) {
304
+ showError('Please enter text to detect its language.');
305
+ document.getElementById('detection-loading').style.display = 'none';
306
+ return;
307
+ }
308
+
309
+ try {
310
+ const response = await fetch(`${API_BASE_URL}/detect_language`, {
311
+ method: 'POST',
312
+ headers: {
313
+ 'Content-Type': 'application/json',
314
+ 'accept': 'application/json',
315
+ },
316
+ body: JSON.stringify({ text: text }),
317
+ });
318
+
319
+ if (!response.ok) {
320
+ const errorData = await response.json();
321
+ showError(`Detection failed: ${errorData.detail}`);
322
+ return;
323
+ }
324
+
325
+ const data = await response.json();
326
+ document.getElementById('detected-language').textContent = data.source_language;
327
+
328
+ // Clear previous options
329
+ const optionsList = document.getElementById('translation-options-list');
330
+ optionsList.innerHTML = ''; // Important: Clear previous options
331
+
332
+ // Display translation options
333
+ if (data.translation_options && typeof data.translation_options === 'object') {
334
+ for (const [key, value] of Object.entries(data.translation_options)) {
335
+ const listItem = document.createElement('li');
336
+ listItem.textContent = `${key}: ${value}`;
337
+ optionsList.appendChild(listItem);
338
+ }
339
+ document.getElementById('translation-options-container').style.display = 'block'; // Show the container
340
+ } else {
341
+ document.getElementById('translation-options-container').style.display = 'none'; // Hide if no options
342
+ }
343
+
344
+
345
+ document.getElementById('detection-result').style.display = 'block';
346
+
347
+ } catch (error) {
348
+ showError(`Error: ${error.message}`);
349
+ console.error('Error during language detection:', error);
350
+ } finally {
351
+ document.getElementById('detection-loading').style.display = 'none';
352
+ }
353
+ });
354
+
355
+ </script>
356
+ </body>
357
+ </html>