AdilzhanB commited on
Commit
60fadf0
Β·
0 Parent(s):

Initial commit

Browse files
Files changed (2) hide show
  1. app.py +334 -0
  2. requirements.txt +6 -0
app.py ADDED
@@ -0,0 +1,334 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import gradio as gr
3
+ from textblob import TextBlob
4
+ from typing import Dict, Any, List
5
+ import asyncio
6
+
7
+ class SentimentMCPServer:
8
+ """MCP Server for Sentiment Analysis using TextBlob"""
9
+
10
+ def __init__(self):
11
+ self.name = "sentiment-analyzer"
12
+ self.version = "1.0.0"
13
+
14
+ async def get_tools(self) -> List[Dict]:
15
+ """Returns the list of tools available in this MCP"""
16
+ return [
17
+ {
18
+ "name": "analyze_sentiment",
19
+ "description": "Analyze the sentiment of text using TextBlob NLP",
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
+ async def call_tool(self, name: str, arguments: Dict) -> Dict:
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
+ # Base analysis result
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": self._get_assessment(sentiment.polarity)
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
+ "total_texts": len(texts),
142
+ "analyzed": len(valid_results),
143
+ "average_polarity": round(sum(polarities) / len(polarities), 3),
144
+ "sentiment_distribution": {
145
+ "positive": positive,
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: Dict[str, Any]) -> str:
212
+ """Format the results for display in Gradio"""
213
+ if "error" in result:
214
+ return f"❌ **Error:** {result['error']}"
215
+
216
+ emoji_map = {"positive": "😊", "negative": "😞", "neutral": "😐"}
217
+ polarity_color = "🟒" if result["polarity"] > 0 else "πŸ”΄" if result["polarity"] < 0 else "🟑"
218
+
219
+ return f"""
220
+ ## πŸ“Š MCP Sentiment Analysis Results
221
+
222
+ ### {emoji_map[result['assessment']]} Assessment: **{result['assessment'].title()}**
223
+
224
+ ### πŸ“ˆ Metrics:
225
+ - **Polarity:** {polarity_color} {result['polarity']}
226
+ - **Subjectivity:** 🎯 {result['subjectivity']}
227
+ - **Confidence:** {result.get('confidence', 'N/A').title()}
228
+
229
+ ### πŸ“ Statistics:
230
+ - **Words:** {result.get('word_count', 'N/A')}
231
+ - **Characters:** {result.get('character_count', 'N/A')}
232
+
233
+ ### πŸ’‘ Interpretation:
234
+ {result.get('interpretation', 'No interpretation available')}
235
+
236
+ ---
237
+ *πŸ”— This analysis is available via MCP protocol for AI assistants*
238
+ """
239
+
240
+ # Gradio interface setup
241
+ with gr.Blocks(title="🎯 MCP Sentiment Analyzer") as demo:
242
+ gr.HTML("""
243
+ <h1 style="text-align: center; color: #667eea;">
244
+ 🎯 MCP-Enabled Sentiment Analyzer
245
+ </h1>
246
+ <p style="text-align: center; color: #666;">
247
+ Advanced sentiment analysis with Model Context Protocol support
248
+ </p>
249
+ """)
250
+
251
+ with gr.Tab("πŸ” Single Analysis"):
252
+ text_input = gr.Textbox(
253
+ placeholder="Enter text for sentiment analysis...",
254
+ lines=5,
255
+ label="Text to Analyze"
256
+ )
257
+ analyze_btn = gr.Button("πŸ” Analyze", variant="primary")
258
+ output = gr.Markdown()
259
+
260
+ analyze_btn.click(
261
+ fn=lambda x: format_results(sentiment_analysis(x)),
262
+ inputs=text_input,
263
+ outputs=output
264
+ )
265
+
266
+ with gr.Tab("πŸ“Š Batch Analysis"):
267
+ batch_input = gr.Textbox(
268
+ placeholder="Enter multiple texts, one per line...",
269
+ lines=8,
270
+ label="Multiple Texts"
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
+ texts = [line.strip() for line in texts_str.split('\n') if line.strip()]
280
+ loop = asyncio.new_event_loop()
281
+ asyncio.set_event_loop(loop)
282
+ try:
283
+ return loop.run_until_complete(mcp_server.batch_analyze(texts))
284
+ finally:
285
+ loop.close()
286
+
287
+ batch_btn.click(
288
+ fn=batch_analyze_wrapper,
289
+ inputs=batch_input,
290
+ outputs=batch_output
291
+ )
292
+
293
+ with gr.Tab("πŸ“ˆ Summary Stats"):
294
+ summary_input = gr.Textbox(
295
+ placeholder="Enter texts for summary statistics...",
296
+ lines=6,
297
+ label="Texts for Summary"
298
+ )
299
+ summary_btn = gr.Button("πŸ“ˆ Get Summary", variant="primary")
300
+ summary_output = gr.JSON(label="Summary Statistics")
301
+
302
+ def summary_wrapper(texts_str: str):
303
+ if not texts_str.strip():
304
+ return {"error": "Please enter some texts"}
305
+
306
+ texts = [line.strip() for line in texts_str.split('\n') if line.strip()]
307
+ loop = asyncio.new_event_loop()
308
+ asyncio.set_event_loop(loop)
309
+ try:
310
+ return loop.run_until_complete(mcp_server.sentiment_summary(texts))
311
+ finally:
312
+ loop.close()
313
+
314
+ summary_btn.click(
315
+ fn=summary_wrapper,
316
+ inputs=summary_input,
317
+ outputs=summary_output
318
+ )
319
+
320
+ if __name__ == "__main__":
321
+ print("πŸš€ Starting MCP-Enabled Sentiment Analyzer...")
322
+ print("πŸ“‘ MCP Server: Available for AI assistant integration")
323
+ print("πŸ”§ Available MCP tools:")
324
+ print(" - analyze_sentiment: Single text analysis")
325
+ print(" - batch_analyze: Multiple texts analysis")
326
+ print(" - sentiment_summary: Statistical summary")
327
+ print("🌐 Web UI: http://localhost:7860")
328
+
329
+ demo.launch(
330
+ mcp_server=True,
331
+ server_name="127.0.0.1",
332
+ server_port=7860,
333
+ inbrowser=True
334
+ )
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
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