Spaces:
Sleeping
Sleeping
Masfiq Mahmud
commited on
Commit
·
ea78f87
1
Parent(s):
ccb3666
Add sentiment analysis feature using Gemini API and update UI for sentiment display
Browse files- .gitignore +1 -0
- app.py +58 -1
- static/style.css +31 -0
- templates/index.html +18 -0
.gitignore
CHANGED
@@ -4,6 +4,7 @@
|
|
4 |
__pycache__/
|
5 |
*.py[cod]
|
6 |
*$py.class
|
|
|
7 |
|
8 |
# Distribution / packaging
|
9 |
.Python
|
|
|
4 |
__pycache__/
|
5 |
*.py[cod]
|
6 |
*$py.class
|
7 |
+
model_files/
|
8 |
|
9 |
# Distribution / packaging
|
10 |
.Python
|
app.py
CHANGED
@@ -5,6 +5,8 @@ import torch
|
|
5 |
import numpy as np
|
6 |
from flask import Flask, request, jsonify, render_template
|
7 |
import subprocess # For calling gdown
|
|
|
|
|
8 |
|
9 |
os.environ['HF_HUB_DISABLE_SYMLINKS_WARNING'] = '1'
|
10 |
import torch.nn as nn
|
@@ -333,6 +335,57 @@ def process_input(text, t5_tokenizer, encoder_tokenizer, char_to_id, current_max
|
|
333 |
}
|
334 |
# --- END HELPER METHODS ---
|
335 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
336 |
|
337 |
app = Flask(__name__)
|
338 |
|
@@ -412,7 +465,11 @@ def translate_text():
|
|
412 |
translation = CFG.t5_tokenizer.decode(
|
413 |
generated_ids[0], skip_special_tokens=True, clean_up_tokenization_spaces=True
|
414 |
).strip()
|
415 |
-
|
|
|
|
|
|
|
|
|
416 |
except Exception as e:
|
417 |
print(f"Error during translation: {e}")
|
418 |
# import traceback
|
|
|
5 |
import numpy as np
|
6 |
from flask import Flask, request, jsonify, render_template
|
7 |
import subprocess # For calling gdown
|
8 |
+
import google.generativeai as genai
|
9 |
+
import time
|
10 |
|
11 |
os.environ['HF_HUB_DISABLE_SYMLINKS_WARNING'] = '1'
|
12 |
import torch.nn as nn
|
|
|
335 |
}
|
336 |
# --- END HELPER METHODS ---
|
337 |
|
338 |
+
# --- Gemini Configuration ---
|
339 |
+
GEMINI_API_KEY = "AIzaSyDmXVXoliNUt3Pnx4_lR2aOezLaItpIXz0"
|
340 |
+
try:
|
341 |
+
genai.configure(api_key=GEMINI_API_KEY)
|
342 |
+
except Exception as e:
|
343 |
+
print(f"Could not configure Gemini: {e}")
|
344 |
+
|
345 |
+
def get_sentiment_from_gemini(text: str, retries: int = 3, delay: int = 5) -> str:
|
346 |
+
"""
|
347 |
+
Analyzes the sentiment of a given text using the Gemini API.
|
348 |
+
"""
|
349 |
+
if not text.strip():
|
350 |
+
return "Neutral"
|
351 |
+
|
352 |
+
try:
|
353 |
+
model = genai.GenerativeModel(model_name="gemini-1.5-flash")
|
354 |
+
|
355 |
+
prompt = f"""Analyze the sentiment of the following Bengali text. Your response must be a single word: 'Positive', 'Negative', or 'Neutral'.
|
356 |
+
Text: "{text}"
|
357 |
+
Sentiment:"""
|
358 |
+
|
359 |
+
for attempt in range(retries):
|
360 |
+
try:
|
361 |
+
response = model.generate_content(
|
362 |
+
prompt,
|
363 |
+
generation_config=genai.types.GenerationConfig(
|
364 |
+
candidate_count=1,
|
365 |
+
max_output_tokens=5,
|
366 |
+
temperature=0.1
|
367 |
+
)
|
368 |
+
)
|
369 |
+
sentiment = response.text.strip().capitalize()
|
370 |
+
|
371 |
+
if sentiment in ["Positive", "Negative", "Neutral"]:
|
372 |
+
print(f"Gemini sentiment for '{text[:30]}...': {sentiment}")
|
373 |
+
return sentiment
|
374 |
+
else:
|
375 |
+
print(f"Gemini returned unexpected sentiment: '{sentiment}'. Attempt {attempt + 1}/{retries}")
|
376 |
+
|
377 |
+
except Exception as e:
|
378 |
+
print(f"Error calling Gemini API on attempt {attempt + 1}/{retries}: {e}")
|
379 |
+
|
380 |
+
if attempt < retries - 1:
|
381 |
+
print(f"Retrying in {delay} seconds...")
|
382 |
+
time.sleep(delay)
|
383 |
+
|
384 |
+
print("Gemini sentiment analysis failed after all retries.")
|
385 |
+
return "Unknown"
|
386 |
+
except Exception as e:
|
387 |
+
print(f"An error occurred during Gemini model initialization: {e}")
|
388 |
+
return "Error"
|
389 |
|
390 |
app = Flask(__name__)
|
391 |
|
|
|
465 |
translation = CFG.t5_tokenizer.decode(
|
466 |
generated_ids[0], skip_special_tokens=True, clean_up_tokenization_spaces=True
|
467 |
).strip()
|
468 |
+
|
469 |
+
# Get sentiment analysis from Gemini
|
470 |
+
sentiment = get_sentiment_from_gemini(translation)
|
471 |
+
|
472 |
+
return jsonify({"translation": translation, "sentiment": sentiment})
|
473 |
except Exception as e:
|
474 |
print(f"Error during translation: {e}")
|
475 |
# import traceback
|
static/style.css
CHANGED
@@ -185,6 +185,37 @@ textarea:focus {
|
|
185 |
line-height: 1.6;
|
186 |
}
|
187 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
188 |
/* Loading Spinner */
|
189 |
.loading-spinner {
|
190 |
display: flex;
|
|
|
185 |
line-height: 1.6;
|
186 |
}
|
187 |
|
188 |
+
/* Sentiment Badge Styles */
|
189 |
+
#sentimentContainer {
|
190 |
+
display: flex;
|
191 |
+
align-items: center;
|
192 |
+
gap: 8px;
|
193 |
+
font-size: 1em;
|
194 |
+
color: var(--text-color);
|
195 |
+
}
|
196 |
+
|
197 |
+
.sentiment-badge {
|
198 |
+
display: inline-block;
|
199 |
+
padding: 4px 12px;
|
200 |
+
border-radius: 16px;
|
201 |
+
font-size: 0.9em;
|
202 |
+
font-weight: 500;
|
203 |
+
color: white;
|
204 |
+
text-transform: capitalize;
|
205 |
+
}
|
206 |
+
|
207 |
+
.sentiment-positive {
|
208 |
+
background-color: #2e7d32;
|
209 |
+
}
|
210 |
+
|
211 |
+
.sentiment-negative {
|
212 |
+
background-color: #c62828;
|
213 |
+
}
|
214 |
+
|
215 |
+
.sentiment-neutral {
|
216 |
+
background-color: #616161;
|
217 |
+
}
|
218 |
+
|
219 |
/* Loading Spinner */
|
220 |
.loading-spinner {
|
221 |
display: flex;
|
templates/index.html
CHANGED
@@ -53,6 +53,9 @@
|
|
53 |
</div>
|
54 |
<div class="translation-result">
|
55 |
<p id="translationResult"></p>
|
|
|
|
|
|
|
56 |
<div class="loading-spinner" style="display: none;">
|
57 |
<div class="spinner"></div>
|
58 |
</div>
|
@@ -70,6 +73,8 @@
|
|
70 |
const copyBtn = document.getElementById('copyBtn');
|
71 |
const translationResult = document.getElementById('translationResult');
|
72 |
const loadingSpinner = document.querySelector('.loading-spinner');
|
|
|
|
|
73 |
|
74 |
// Character counter
|
75 |
userInput.addEventListener('input', function() {
|
@@ -80,6 +85,9 @@
|
|
80 |
clearBtn.addEventListener('click', function() {
|
81 |
userInput.value = '';
|
82 |
translationResult.textContent = '';
|
|
|
|
|
|
|
83 |
charCount.textContent = '0/500';
|
84 |
});
|
85 |
|
@@ -108,6 +116,8 @@
|
|
108 |
loadingSpinner.style.display = 'flex';
|
109 |
translateBtn.disabled = true;
|
110 |
translationResult.style.opacity = '0.5';
|
|
|
|
|
111 |
|
112 |
try {
|
113 |
const response = await fetch('/translate', {
|
@@ -120,6 +130,14 @@
|
|
120 |
|
121 |
const data = await response.json();
|
122 |
translationResult.textContent = data.translation;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
123 |
translationResult.style.opacity = '1';
|
124 |
} catch (error) {
|
125 |
console.error('Error:', error);
|
|
|
53 |
</div>
|
54 |
<div class="translation-result">
|
55 |
<p id="translationResult"></p>
|
56 |
+
<div id="sentimentContainer" style="display: none; margin-top: 15px;">
|
57 |
+
<strong>Sentiment:</strong> <span id="sentimentResult" class="sentiment-badge"></span>
|
58 |
+
</div>
|
59 |
<div class="loading-spinner" style="display: none;">
|
60 |
<div class="spinner"></div>
|
61 |
</div>
|
|
|
73 |
const copyBtn = document.getElementById('copyBtn');
|
74 |
const translationResult = document.getElementById('translationResult');
|
75 |
const loadingSpinner = document.querySelector('.loading-spinner');
|
76 |
+
const sentimentContainer = document.getElementById('sentimentContainer');
|
77 |
+
const sentimentResult = document.getElementById('sentimentResult');
|
78 |
|
79 |
// Character counter
|
80 |
userInput.addEventListener('input', function() {
|
|
|
85 |
clearBtn.addEventListener('click', function() {
|
86 |
userInput.value = '';
|
87 |
translationResult.textContent = '';
|
88 |
+
sentimentContainer.style.display = 'none';
|
89 |
+
sentimentResult.textContent = '';
|
90 |
+
sentimentResult.className = 'sentiment-badge';
|
91 |
charCount.textContent = '0/500';
|
92 |
});
|
93 |
|
|
|
116 |
loadingSpinner.style.display = 'flex';
|
117 |
translateBtn.disabled = true;
|
118 |
translationResult.style.opacity = '0.5';
|
119 |
+
translationResult.textContent = '';
|
120 |
+
sentimentContainer.style.display = 'none';
|
121 |
|
122 |
try {
|
123 |
const response = await fetch('/translate', {
|
|
|
130 |
|
131 |
const data = await response.json();
|
132 |
translationResult.textContent = data.translation;
|
133 |
+
|
134 |
+
if (data.sentiment && data.sentiment !== 'Error' && data.sentiment !== 'Unknown') {
|
135 |
+
sentimentResult.textContent = data.sentiment;
|
136 |
+
sentimentResult.className = 'sentiment-badge'; // Reset classes
|
137 |
+
sentimentResult.classList.add(`sentiment-${data.sentiment.toLowerCase()}`);
|
138 |
+
sentimentContainer.style.display = 'flex';
|
139 |
+
}
|
140 |
+
|
141 |
translationResult.style.opacity = '1';
|
142 |
} catch (error) {
|
143 |
console.error('Error:', error);
|