Spaces:
Sleeping
Sleeping
AdilzhanB
commited on
Commit
Β·
6949b01
1
Parent(s):
79889f4
changes to app
Browse files- app.py +287 -293
- requirements.txt +1 -4
app.py
CHANGED
@@ -1,333 +1,327 @@
|
|
1 |
import json
|
2 |
import gradio as gr
|
3 |
from textblob import TextBlob
|
4 |
-
|
5 |
-
import asyncio
|
6 |
|
7 |
-
|
8 |
-
"""
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
"inputSchema": {
|
21 |
-
"type": "object",
|
22 |
-
"properties": {
|
23 |
-
"text": {
|
24 |
-
"type": "string",
|
25 |
-
"description": "Text to analyze for sentiment"
|
26 |
-
},
|
27 |
-
"detailed": {
|
28 |
-
"type": "boolean",
|
29 |
-
"description": "Return detailed analysis including confidence and statistics",
|
30 |
-
"default": False
|
31 |
-
}
|
32 |
-
},
|
33 |
-
"required": ["text"]
|
34 |
-
}
|
35 |
-
},
|
36 |
-
{
|
37 |
-
"name": "batch_analyze",
|
38 |
-
"description": "Analyze sentiment for multiple texts at once",
|
39 |
-
"inputSchema": {
|
40 |
-
"type": "object",
|
41 |
-
"properties": {
|
42 |
-
"texts": {
|
43 |
-
"type": "array",
|
44 |
-
"items": {"type": "string"},
|
45 |
-
"description": "Array of texts to analyze"
|
46 |
-
}
|
47 |
-
},
|
48 |
-
"required": ["texts"]
|
49 |
-
}
|
50 |
-
},
|
51 |
-
{
|
52 |
-
"name": "sentiment_summary",
|
53 |
-
"description": "Get summary statistics for analyzed texts",
|
54 |
-
"inputSchema": {
|
55 |
-
"type": "object",
|
56 |
-
"properties": {
|
57 |
-
"texts": {
|
58 |
-
"type": "array",
|
59 |
-
"items": {"type": "string"}
|
60 |
-
}
|
61 |
-
},
|
62 |
-
"required": ["texts"]
|
63 |
-
}
|
64 |
-
}
|
65 |
-
]
|
66 |
|
67 |
-
|
68 |
-
"""Call the specified tool with the given arguments."""
|
69 |
-
if name == "analyze_sentiment":
|
70 |
-
return await self.analyze_sentiment(
|
71 |
-
arguments["text"],
|
72 |
-
arguments.get("detailed", False)
|
73 |
-
)
|
74 |
-
elif name == "batch_analyze":
|
75 |
-
return await self.batch_analyze(arguments["texts"])
|
76 |
-
elif name == "sentiment_summary":
|
77 |
-
return await self.sentiment_summary(arguments["texts"])
|
78 |
-
else:
|
79 |
-
raise ValueError(f"Unknown tool: {name}")
|
80 |
-
|
81 |
-
async def analyze_sentiment(self, text: str, detailed: bool = False) -> Dict[str, Any]:
|
82 |
-
"""Main sentiment analysis function"""
|
83 |
-
if not text or not text.strip():
|
84 |
-
return {"error": "Text cannot be empty"}
|
85 |
-
|
86 |
blob = TextBlob(text)
|
87 |
sentiment = blob.sentiment
|
88 |
|
89 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
90 |
result = {
|
91 |
-
"text": text[:100] + "..." if len(text) > 100 else text,
|
92 |
"polarity": round(sentiment.polarity, 3),
|
93 |
"subjectivity": round(sentiment.subjectivity, 3),
|
94 |
-
"assessment":
|
|
|
|
|
|
|
|
|
95 |
}
|
96 |
|
97 |
-
# Detailed analysis
|
98 |
-
if detailed:
|
99 |
-
result.update({
|
100 |
-
"confidence": self._get_confidence(sentiment.polarity),
|
101 |
-
"word_count": len(text.split()),
|
102 |
-
"character_count": len(text),
|
103 |
-
"interpretation": self._get_interpretation(sentiment)
|
104 |
-
})
|
105 |
-
|
106 |
return result
|
107 |
-
|
108 |
-
async def batch_analyze(self, texts: List[str]) -> Dict[str, Any]:
|
109 |
-
"""Analyze sentiment for multiple texts"""
|
110 |
-
results = []
|
111 |
-
for i, text in enumerate(texts):
|
112 |
-
try:
|
113 |
-
result = await self.analyze_sentiment(text, detailed=True)
|
114 |
-
result["index"] = i
|
115 |
-
results.append(result)
|
116 |
-
except Exception as e:
|
117 |
-
results.append({"index": i, "error": str(e), "text": text})
|
118 |
-
|
119 |
-
return {"results": results, "total_analyzed": len(texts)}
|
120 |
-
|
121 |
-
async def sentiment_summary(self, texts: List[str]) -> Dict[str, Any]:
|
122 |
-
"""Generate summary statistics for a batch of texts"""
|
123 |
-
batch_result = await self.batch_analyze(texts)
|
124 |
-
results = batch_result["results"]
|
125 |
-
|
126 |
-
# Π€ΠΈΠ»ΡΡΡΡΠ΅ΠΌ ΡΡΠΏΠ΅ΡΠ½ΡΠ΅ ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΡ
|
127 |
-
valid_results = [r for r in results if "error" not in r]
|
128 |
-
|
129 |
-
if not valid_results:
|
130 |
-
return {"error": "No valid results to summarize"}
|
131 |
-
|
132 |
-
polarities = [r["polarity"] for r in valid_results]
|
133 |
-
assessments = [r["assessment"] for r in valid_results]
|
134 |
-
|
135 |
-
# ΠΠΎΠ΄ΡΡΠ΅Ρ ΠΏΠΎ ΠΊΠ°ΡΠ΅Π³ΠΎΡΠΈΡΠΌ
|
136 |
-
positive = sum(1 for a in assessments if a == "positive")
|
137 |
-
negative = sum(1 for a in assessments if a == "negative")
|
138 |
-
neutral = sum(1 for a in assessments if a == "neutral")
|
139 |
|
|
|
140 |
return {
|
141 |
-
"
|
142 |
-
"
|
143 |
-
"
|
144 |
-
"
|
145 |
-
|
146 |
-
"negative": negative,
|
147 |
-
"neutral": neutral
|
148 |
-
},
|
149 |
-
"percentages": {
|
150 |
-
"positive": round(positive / len(valid_results) * 100, 1),
|
151 |
-
"negative": round(negative / len(valid_results) * 100, 1),
|
152 |
-
"neutral": round(neutral / len(valid_results) * 100, 1)
|
153 |
-
}
|
154 |
}
|
155 |
-
|
156 |
-
def _get_assessment(self, polarity: float) -> str:
|
157 |
-
"""Sentiment assessment based on polarity"""
|
158 |
-
if polarity > 0.1:
|
159 |
-
return "positive"
|
160 |
-
elif polarity < -0.1:
|
161 |
-
return "negative"
|
162 |
-
else:
|
163 |
-
return "neutral"
|
164 |
-
|
165 |
-
def _get_confidence(self, polarity: float) -> str:
|
166 |
-
"""Confidence level based on polarity"""
|
167 |
-
abs_polarity = abs(polarity)
|
168 |
-
if abs_polarity >= 0.7:
|
169 |
-
return "high"
|
170 |
-
elif abs_polarity >= 0.3:
|
171 |
-
return "medium"
|
172 |
-
else:
|
173 |
-
return "low"
|
174 |
-
|
175 |
-
def _get_interpretation(self, sentiment) -> str:
|
176 |
-
"""Reult interpretation based on sentiment analysis"""
|
177 |
-
polarity = sentiment.polarity
|
178 |
-
subjectivity = sentiment.subjectivity
|
179 |
-
|
180 |
-
if subjectivity > 0.7:
|
181 |
-
subj_desc = "highly subjective (opinion-based)"
|
182 |
-
elif subjectivity > 0.3:
|
183 |
-
subj_desc = "moderately subjective"
|
184 |
-
else:
|
185 |
-
subj_desc = "objective (fact-based)"
|
186 |
-
|
187 |
-
if abs(polarity) > 0.5:
|
188 |
-
pol_desc = "strong sentiment"
|
189 |
-
elif abs(polarity) > 0.2:
|
190 |
-
pol_desc = "moderate sentiment"
|
191 |
-
else:
|
192 |
-
pol_desc = "neutral sentiment"
|
193 |
-
|
194 |
-
return f"This text shows {pol_desc} and is {subj_desc}."
|
195 |
-
|
196 |
-
# Global instance of the MCP server
|
197 |
-
mcp_server = SentimentMCPServer()
|
198 |
-
|
199 |
-
def sentiment_analysis(text: str) -> Dict[str, Any]:
|
200 |
-
"""Wrapper for Gradio interface to call MCP server"""
|
201 |
-
loop = asyncio.new_event_loop()
|
202 |
-
asyncio.set_event_loop(loop)
|
203 |
-
try:
|
204 |
-
result = loop.run_until_complete(
|
205 |
-
mcp_server.analyze_sentiment(text, detailed=True)
|
206 |
-
)
|
207 |
-
return result
|
208 |
-
finally:
|
209 |
-
loop.close()
|
210 |
|
211 |
-
def format_results(result:
|
212 |
-
"""Format the results for display
|
213 |
if "error" in result:
|
214 |
return f"β **Error:** {result['error']}"
|
215 |
|
216 |
-
|
217 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
218 |
|
219 |
-
|
220 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
221 |
|
222 |
-
### {emoji_map[result['assessment']]} Assessment: **{result['assessment'].title()}**
|
223 |
|
224 |
-
### π Metrics:
|
225 |
-
- **Polarity:** {polarity_color} {result['polarity']}
|
226 |
-
-
|
227 |
-
- **
|
|
|
|
|
228 |
|
229 |
-
### π Statistics:
|
230 |
-
- **Words:** {result
|
231 |
-
- **Characters:** {result
|
|
|
232 |
|
233 |
### π‘ Interpretation:
|
234 |
-
|
|
|
|
|
235 |
|
236 |
---
|
237 |
-
*π
|
238 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
239 |
|
240 |
-
|
241 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
242 |
gr.HTML("""
|
243 |
-
<
|
244 |
-
π―
|
245 |
-
</
|
246 |
-
<
|
247 |
-
Advanced sentiment analysis
|
248 |
-
</
|
249 |
""")
|
250 |
|
251 |
-
with gr.
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
259 |
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
265 |
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
)
|
272 |
-
batch_btn = gr.Button("π Batch Analyze", variant="primary")
|
273 |
-
batch_output = gr.JSON(label="Batch Results")
|
274 |
-
|
275 |
-
def batch_analyze_wrapper(texts_str: str):
|
276 |
-
if not texts_str.strip():
|
277 |
-
return {"error": "Please enter some texts"}
|
278 |
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
|
|
|
|
|
|
|
|
|
|
292 |
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
|
|
|
|
|
|
|
|
|
|
318 |
|
|
|
319 |
if __name__ == "__main__":
|
320 |
-
print("π Starting
|
321 |
-
|
322 |
-
print("π§ Available MCP tools:")
|
323 |
-
print(" - analyze_sentiment: Single text analysis")
|
324 |
-
print(" - batch_analyze: Multiple texts analysis")
|
325 |
-
print(" - sentiment_summary: Statistical summary")
|
326 |
-
print("π Web UI: http://localhost:7860")
|
327 |
-
|
328 |
-
demo.launch(
|
329 |
-
mcp_server=True,
|
330 |
-
server_name="127.0.0.1",
|
331 |
-
server_port=7860,
|
332 |
-
inbrowser=True
|
333 |
-
)
|
|
|
1 |
import json
|
2 |
import gradio as gr
|
3 |
from textblob import TextBlob
|
4 |
+
import os
|
|
|
5 |
|
6 |
+
def sentiment_analysis(text: str) -> dict:
|
7 |
+
"""
|
8 |
+
Analyze the sentiment of the given text.
|
9 |
+
Simplified version for Hugging Face Spaces.
|
10 |
+
"""
|
11 |
+
if not text or not text.strip():
|
12 |
+
return {
|
13 |
+
"error": "Please enter some text to analyze",
|
14 |
+
"polarity": 0,
|
15 |
+
"subjectivity": 0,
|
16 |
+
"assessment": "neutral",
|
17 |
+
"confidence": "low"
|
18 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
|
20 |
+
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
blob = TextBlob(text)
|
22 |
sentiment = blob.sentiment
|
23 |
|
24 |
+
# Calculate confidence based on polarity strength
|
25 |
+
polarity_abs = abs(sentiment.polarity)
|
26 |
+
if polarity_abs >= 0.7:
|
27 |
+
confidence = "high"
|
28 |
+
elif polarity_abs >= 0.3:
|
29 |
+
confidence = "medium"
|
30 |
+
else:
|
31 |
+
confidence = "low"
|
32 |
+
|
33 |
+
# More nuanced assessment
|
34 |
+
if sentiment.polarity > 0.1:
|
35 |
+
assessment = "positive"
|
36 |
+
elif sentiment.polarity < -0.1:
|
37 |
+
assessment = "negative"
|
38 |
+
else:
|
39 |
+
assessment = "neutral"
|
40 |
+
|
41 |
result = {
|
|
|
42 |
"polarity": round(sentiment.polarity, 3),
|
43 |
"subjectivity": round(sentiment.subjectivity, 3),
|
44 |
+
"assessment": assessment,
|
45 |
+
"confidence": confidence,
|
46 |
+
"word_count": len(text.split()),
|
47 |
+
"character_count": len(text),
|
48 |
+
"text_preview": text[:100] + "..." if len(text) > 100 else text
|
49 |
}
|
50 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
51 |
return result
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
|
53 |
+
except Exception as e:
|
54 |
return {
|
55 |
+
"error": f"Analysis failed: {str(e)}",
|
56 |
+
"polarity": 0,
|
57 |
+
"subjectivity": 0,
|
58 |
+
"assessment": "error",
|
59 |
+
"confidence": "low"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
60 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
61 |
|
62 |
+
def format_results(result: dict) -> str:
|
63 |
+
"""Format the analysis results for better display."""
|
64 |
if "error" in result:
|
65 |
return f"β **Error:** {result['error']}"
|
66 |
|
67 |
+
# Emoji mapping for sentiment
|
68 |
+
emoji_map = {
|
69 |
+
"positive": "π",
|
70 |
+
"negative": "π",
|
71 |
+
"neutral": "π",
|
72 |
+
"error": "β"
|
73 |
+
}
|
74 |
+
|
75 |
+
# Color coding for polarity
|
76 |
+
polarity = result["polarity"]
|
77 |
+
if polarity > 0:
|
78 |
+
polarity_color = "π’"
|
79 |
+
polarity_desc = "Positive"
|
80 |
+
elif polarity < 0:
|
81 |
+
polarity_color = "π΄"
|
82 |
+
polarity_desc = "Negative"
|
83 |
+
else:
|
84 |
+
polarity_color = "π‘"
|
85 |
+
polarity_desc = "Neutral"
|
86 |
|
87 |
+
# Confidence indicators
|
88 |
+
confidence_icons = {
|
89 |
+
"high": "π₯",
|
90 |
+
"medium": "β‘",
|
91 |
+
"low": "π«"
|
92 |
+
}
|
93 |
+
|
94 |
+
formatted = f"""
|
95 |
+
## π Sentiment Analysis Results
|
96 |
|
97 |
+
### {emoji_map[result['assessment']]} Overall Assessment: **{result['assessment'].title()}**
|
98 |
|
99 |
+
### π Detailed Metrics:
|
100 |
+
- **Polarity:** {polarity_color} **{result['polarity']}** ({polarity_desc})
|
101 |
+
- Range: -1.0 (very negative) to +1.0 (very positive)
|
102 |
+
- **Subjectivity:** π― **{result['subjectivity']}**
|
103 |
+
- Range: 0.0 (objective) to 1.0 (subjective)
|
104 |
+
- **Confidence:** {confidence_icons.get(result['confidence'], 'β')} **{result['confidence'].title()}**
|
105 |
|
106 |
+
### π Text Statistics:
|
107 |
+
- **Words:** {result['word_count']}
|
108 |
+
- **Characters:** {result['character_count']}
|
109 |
+
- **Preview:** "{result.get('text_preview', 'N/A')}"
|
110 |
|
111 |
### π‘ Interpretation:
|
112 |
+
- **Polarity** measures emotional tone from negative to positive
|
113 |
+
- **Subjectivity** measures opinion vs factual content
|
114 |
+
- **Confidence** indicates strength of sentiment signal
|
115 |
|
116 |
---
|
117 |
+
*π Powered by TextBlob NLP β’ Ready for MCP integration*
|
118 |
"""
|
119 |
+
return formatted
|
120 |
+
|
121 |
+
def analyze_with_formatting(text: str) -> str:
|
122 |
+
"""Wrapper function that combines analysis and formatting."""
|
123 |
+
result = sentiment_analysis(text)
|
124 |
+
return format_results(result)
|
125 |
|
126 |
+
def batch_analyze_simple(texts_input: str) -> str:
|
127 |
+
"""Simple batch analysis for multiple texts."""
|
128 |
+
if not texts_input.strip():
|
129 |
+
return "β Please enter some texts, one per line."
|
130 |
+
|
131 |
+
texts = [line.strip() for line in texts_input.split('\n') if line.strip()]
|
132 |
+
|
133 |
+
if not texts:
|
134 |
+
return "β No valid texts found."
|
135 |
+
|
136 |
+
results = []
|
137 |
+
positive_count = 0
|
138 |
+
negative_count = 0
|
139 |
+
neutral_count = 0
|
140 |
+
total_polarity = 0
|
141 |
+
|
142 |
+
for i, text in enumerate(texts, 1):
|
143 |
+
result = sentiment_analysis(text)
|
144 |
+
if "error" not in result:
|
145 |
+
assessment = result["assessment"]
|
146 |
+
if assessment == "positive":
|
147 |
+
positive_count += 1
|
148 |
+
elif assessment == "negative":
|
149 |
+
negative_count += 1
|
150 |
+
else:
|
151 |
+
neutral_count += 1
|
152 |
+
|
153 |
+
total_polarity += result["polarity"]
|
154 |
+
|
155 |
+
results.append(f"**Text {i}:** {result['assessment']} ({result['polarity']}) - \"{result['text_preview']}\"")
|
156 |
+
else:
|
157 |
+
results.append(f"**Text {i}:** Error - {result['error']}")
|
158 |
+
|
159 |
+
avg_polarity = total_polarity / len(texts) if texts else 0
|
160 |
+
|
161 |
+
summary = f"""
|
162 |
+
## π Batch Analysis Results
|
163 |
+
|
164 |
+
### π Summary Statistics:
|
165 |
+
- **Total Texts:** {len(texts)}
|
166 |
+
- **Average Polarity:** {round(avg_polarity, 3)}
|
167 |
+
- **Positive:** {positive_count} ({round(positive_count/len(texts)*100, 1)}%)
|
168 |
+
- **Negative:** {negative_count} ({round(negative_count/len(texts)*100, 1)}%)
|
169 |
+
- **Neutral:** {neutral_count} ({round(neutral_count/len(texts)*100, 1)}%)
|
170 |
+
|
171 |
+
### π Individual Results:
|
172 |
+
{chr(10).join(results)}
|
173 |
+
"""
|
174 |
+
return summary
|
175 |
+
|
176 |
+
# Sample texts for quick testing
|
177 |
+
sample_texts = [
|
178 |
+
"I absolutely love this product! It's amazing and works perfectly.",
|
179 |
+
"This is the worst experience I've ever had. Completely disappointed.",
|
180 |
+
"The weather today is partly cloudy with a chance of rain.",
|
181 |
+
"I'm not sure how I feel about this new update. It has some good features but also some issues.",
|
182 |
+
"Artificial intelligence is transforming various industries including healthcare, finance, and transportation."
|
183 |
+
]
|
184 |
+
|
185 |
+
# Create the Gradio interface
|
186 |
+
with gr.Blocks(
|
187 |
+
theme=gr.themes.Soft(),
|
188 |
+
title="π― Sentiment Analyzer",
|
189 |
+
css="""
|
190 |
+
.gradio-container {
|
191 |
+
max-width: 1200px !important;
|
192 |
+
margin: auto !important;
|
193 |
+
}
|
194 |
+
.main-header {
|
195 |
+
text-align: center;
|
196 |
+
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
|
197 |
+
-webkit-background-clip: text;
|
198 |
+
-webkit-text-fill-color: transparent;
|
199 |
+
background-clip: text;
|
200 |
+
font-size: 2.5em;
|
201 |
+
font-weight: bold;
|
202 |
+
margin-bottom: 20px;
|
203 |
+
}
|
204 |
+
.subtitle {
|
205 |
+
text-align: center;
|
206 |
+
color: #666;
|
207 |
+
font-size: 1.2em;
|
208 |
+
margin-bottom: 30px;
|
209 |
+
}
|
210 |
+
"""
|
211 |
+
) as demo:
|
212 |
+
|
213 |
gr.HTML("""
|
214 |
+
<div class="main-header">
|
215 |
+
π― AI Sentiment Analyzer
|
216 |
+
</div>
|
217 |
+
<div class="subtitle">
|
218 |
+
Advanced sentiment analysis powered by TextBlob NLP
|
219 |
+
</div>
|
220 |
""")
|
221 |
|
222 |
+
with gr.Tabs():
|
223 |
+
with gr.Tab("π Single Analysis"):
|
224 |
+
with gr.Row():
|
225 |
+
with gr.Column(scale=1):
|
226 |
+
gr.Markdown("### π Enter Your Text")
|
227 |
+
text_input = gr.Textbox(
|
228 |
+
placeholder="Type or paste your text here for sentiment analysis...",
|
229 |
+
lines=6,
|
230 |
+
max_lines=15,
|
231 |
+
label="Text to Analyze",
|
232 |
+
info="π‘ Tip: Longer texts generally provide more accurate sentiment analysis"
|
233 |
+
)
|
234 |
+
|
235 |
+
with gr.Row():
|
236 |
+
analyze_btn = gr.Button("π Analyze Sentiment", variant="primary", size="lg")
|
237 |
+
clear_btn = gr.Button("ποΈ Clear", variant="secondary", size="lg")
|
238 |
+
|
239 |
+
gr.Markdown("### π― Try Quick Examples")
|
240 |
+
examples_dropdown = gr.Dropdown(
|
241 |
+
choices=sample_texts,
|
242 |
+
label="Select a sample text to analyze",
|
243 |
+
value=None,
|
244 |
+
interactive=True
|
245 |
+
)
|
246 |
+
|
247 |
+
with gr.Column(scale=1):
|
248 |
+
gr.Markdown("### π Analysis Results")
|
249 |
+
output = gr.Markdown(
|
250 |
+
value="π **Welcome!** Enter some text and click 'Analyze Sentiment' to get started with AI-powered sentiment analysis.",
|
251 |
+
label="Sentiment Analysis Output"
|
252 |
+
)
|
253 |
|
254 |
+
with gr.Tab("π Batch Analysis"):
|
255 |
+
gr.Markdown("### π Analyze Multiple Texts")
|
256 |
+
batch_input = gr.Textbox(
|
257 |
+
placeholder="Enter multiple texts, one per line...\n\nExample:\nI love this product!\nThis is terrible.\nThe weather is nice today.",
|
258 |
+
lines=8,
|
259 |
+
label="Multiple Texts (one per line)",
|
260 |
+
info="Enter each text on a separate line for batch analysis"
|
261 |
+
)
|
262 |
+
batch_btn = gr.Button("π Analyze All", variant="primary", size="lg")
|
263 |
+
batch_output = gr.Markdown(
|
264 |
+
value="π Enter multiple texts above and click 'Analyze All' to get batch sentiment analysis.",
|
265 |
+
label="Batch Analysis Results"
|
266 |
+
)
|
267 |
|
268 |
+
# Additional info section
|
269 |
+
with gr.Row():
|
270 |
+
with gr.Column():
|
271 |
+
gr.Markdown("""
|
272 |
+
### π About This Tool
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
273 |
|
274 |
+
This **AI-powered sentiment analyzer** uses advanced Natural Language Processing to determine:
|
275 |
+
- **Emotional tone** (positive, negative, neutral)
|
276 |
+
- **Subjectivity level** (opinion vs fact)
|
277 |
+
- **Confidence scores** based on signal strength
|
278 |
+
|
279 |
+
Perfect for analyzing:
|
280 |
+
- π Customer reviews and feedback
|
281 |
+
- π± Social media posts and comments
|
282 |
+
- π§ Email and message sentiment
|
283 |
+
- π Survey responses and testimonials
|
284 |
+
- π Product feedback and ratings
|
285 |
+
|
286 |
+
**Features:**
|
287 |
+
- Real-time sentiment analysis
|
288 |
+
- Batch processing for multiple texts
|
289 |
+
- Detailed confidence metrics
|
290 |
+
- User-friendly interface
|
291 |
+
""")
|
292 |
|
293 |
+
# Event handlers for Single Analysis
|
294 |
+
analyze_btn.click(
|
295 |
+
fn=analyze_with_formatting,
|
296 |
+
inputs=text_input,
|
297 |
+
outputs=output
|
298 |
+
)
|
299 |
+
|
300 |
+
clear_btn.click(
|
301 |
+
fn=lambda: ("", "π **Welcome!** Enter some text and click 'Analyze Sentiment' to get started."),
|
302 |
+
outputs=[text_input, output]
|
303 |
+
)
|
304 |
+
|
305 |
+
examples_dropdown.change(
|
306 |
+
fn=lambda x: x if x else "",
|
307 |
+
inputs=examples_dropdown,
|
308 |
+
outputs=text_input
|
309 |
+
)
|
310 |
+
|
311 |
+
text_input.submit(
|
312 |
+
fn=analyze_with_formatting,
|
313 |
+
inputs=text_input,
|
314 |
+
outputs=output
|
315 |
+
)
|
316 |
+
|
317 |
+
# Event handlers for Batch Analysis
|
318 |
+
batch_btn.click(
|
319 |
+
fn=batch_analyze_simple,
|
320 |
+
inputs=batch_input,
|
321 |
+
outputs=batch_output
|
322 |
+
)
|
323 |
|
324 |
+
# Simple launch for Hugging Face Spaces
|
325 |
if __name__ == "__main__":
|
326 |
+
print("π Starting Sentiment Analyzer for Hugging Face Spaces...")
|
327 |
+
demo.launch(mcp_server=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
requirements.txt
CHANGED
@@ -1,6 +1,3 @@
|
|
1 |
# Minimal requirements for basic MCP Sentiment Analyzer
|
2 |
gradio>=4.0.0
|
3 |
-
textblob>=0.17.1
|
4 |
-
asyncio>=3.4.3
|
5 |
-
pydantic>=2.0.0
|
6 |
-
typing-extensions>=4.8.0
|
|
|
1 |
# Minimal requirements for basic MCP Sentiment Analyzer
|
2 |
gradio>=4.0.0
|
3 |
+
textblob>=0.17.1
|
|
|
|
|
|