Spaces:
Sleeping
Sleeping
File size: 9,058 Bytes
d884bf1 0fe0e3f d884bf1 0fe0e3f d884bf1 197b873 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 |
#!/usr/bin/env python3
"""
MCP Sentiment Analysis Server with Docker Support
This is a Model Context Protocol (MCP) server that provides sentiment analysis
capabilities using TextBlob. It's designed to run in Docker containers and
can be deployed to Hugging Face Spaces.
The server provides the following tools:
- analyze_sentiment: Analyze the sentiment of text
- get_sentiment_score: Get numerical sentiment score
- classify_emotion: Classify text into emotion categories
"""
import gradio as gr
from textblob import TextBlob
import json
import re
from typing import Dict, Any, List, Tuple
def improved_sentiment_analysis(text: str) -> Tuple[float, float]:
"""
Improved sentiment analysis that handles negations better than TextBlob alone.
Returns:
tuple: (polarity, subjectivity)
"""
# Get TextBlob's analysis
blob = TextBlob(text)
original_polarity = blob.sentiment.polarity
subjectivity = blob.sentiment.subjectivity
# Define negation patterns
negation_patterns = [
r"\b(don't|dont|do not|doesn't|doesnt|does not|didn't|didnt|did not)\b",
r"\b(won't|wont|will not|wouldn't|wouldnt|would not|can't|cant|cannot)\b",
r"\b(shouldn't|shouldnt|should not|isn't|isnt|is not|aren't|arent|are not)\b",
r"\b(wasn't|wasnt|was not|weren't|werent|were not|haven't|havent|have not)\b",
r"\b(hasn't|hasnt|has not|hadn't|hadnt|had not|never|no|not)\b",
r"\b(hate|dislike|terrible|awful|horrible|bad|worst|disgusting)\b"
]
# Check for negations
text_lower = text.lower()
negation_found = any(re.search(pattern, text_lower) for pattern in negation_patterns)
# Adjust polarity if negation is found and TextBlob gave a positive result
if negation_found and original_polarity > 0:
# Flip the polarity for negated positive statements
adjusted_polarity = -abs(original_polarity) * 0.8 # Make it negative but slightly less extreme
else:
adjusted_polarity = original_polarity
return adjusted_polarity, subjectivity
def analyze_sentiment(text: str) -> str:
"""
Analyze the sentiment of the given text.
Args:
text: The text to analyze
Returns:
A detailed sentiment analysis including polarity and subjectivity
"""
if not text or not text.strip():
return "Error: Please provide text to analyze"
# Use improved sentiment analysis
polarity, subjectivity = improved_sentiment_analysis(text)
# Also get TextBlob's original analysis for comparison
blob = TextBlob(text)
original_polarity = blob.sentiment.polarity
# Determine sentiment category with more conservative thresholds
if polarity > 0.05:
sentiment = "Positive"
elif polarity < -0.05:
sentiment = "Negative"
else:
sentiment = "Neutral"
# Determine subjectivity level
if subjectivity > 0.6:
subjectivity_level = "Highly subjective"
elif subjectivity > 0.3:
subjectivity_level = "Moderately subjective"
else:
subjectivity_level = "Objective"
# Add debug information to help understand the analysis
debug_info = f"Original TextBlob: {original_polarity:.3f} -> Improved: {polarity:.3f}, subjectivity={subjectivity:.3f}"
result = {
"text": text,
"sentiment": sentiment,
"polarity": round(polarity, 3),
"subjectivity": round(subjectivity, 3),
"subjectivity_level": subjectivity_level,
"interpretation": f"The text is {sentiment.lower()} with a polarity of {polarity:.3f} and is {subjectivity_level.lower()}.",
"debug": debug_info
}
return json.dumps(result, indent=2)
def get_sentiment_score(text: str) -> str:
"""
Get a numerical sentiment score for the text.
Args:
text: The text to score
Returns:
A numerical score between -1 (very negative) and 1 (very positive)
"""
if not text or not text.strip():
return "Error: Please provide text to analyze"
# Use improved sentiment analysis
score, _ = improved_sentiment_analysis(text)
result = {
"text": text,
"sentiment_score": round(score, 3),
"scale": "Score ranges from -1 (very negative) to 1 (very positive)"
}
return json.dumps(result, indent=2)
def classify_emotion(text: str) -> str:
"""
Classify the text into basic emotion categories.
Args:
text: The text to classify
Returns:
Emotion classification based on sentiment analysis
"""
if not text or not text.strip():
return "Error: Please provide text to analyze"
blob = TextBlob(text)
polarity = blob.sentiment.polarity
subjectivity = blob.sentiment.subjectivity
# Simple emotion classification based on polarity and subjectivity
if polarity > 0.5:
emotion = "Joy"
elif polarity > 0.1:
emotion = "Contentment"
elif polarity < -0.5:
emotion = "Anger" if subjectivity > 0.5 else "Sadness"
elif polarity < -0.1:
emotion = "Disappointment"
else:
if subjectivity > 0.7:
emotion = "Confusion"
else:
emotion = "Neutral"
confidence = abs(polarity) if abs(polarity) > 0.1 else 0.1
result = {
"text": text,
"emotion": emotion,
"confidence": round(confidence, 3),
"polarity": round(polarity, 3),
"subjectivity": round(subjectivity, 3)
}
return json.dumps(result, indent=2)
def batch_analyze(texts: str) -> str:
"""
Analyze multiple texts at once (one per line).
Args:
texts: Multiple texts separated by newlines
Returns:
Batch analysis results
"""
if not texts or not texts.strip():
return "Error: Please provide texts to analyze (one per line)"
lines = [line.strip() for line in texts.split('\n') if line.strip()]
if not lines:
return "Error: No valid text lines found"
results = []
for i, text in enumerate(lines, 1):
# Use improved sentiment analysis
polarity, _ = improved_sentiment_analysis(text)
# Use the same conservative thresholds as analyze_sentiment
if polarity > 0.05:
sentiment = "Positive"
elif polarity < -0.05:
sentiment = "Negative"
else:
sentiment = "Neutral"
results.append({
"line": i,
"text": text,
"sentiment": sentiment,
"polarity": round(polarity, 3)
})
summary = {
"total_texts": len(results),
"positive": len([r for r in results if r["sentiment"] == "Positive"]),
"negative": len([r for r in results if r["sentiment"] == "Negative"]),
"neutral": len([r for r in results if r["sentiment"] == "Neutral"]),
"average_polarity": round(sum(r["polarity"] for r in results) / len(results), 3)
}
return json.dumps({
"summary": summary,
"results": results
}, indent=2)
# Create the main MCP interface - using TabbedInterface to expose all functions as MCP tools
demo = gr.TabbedInterface(
[
gr.Interface(
fn=analyze_sentiment,
inputs=gr.Textbox(placeholder="Enter text to analyze...", label="Text"),
outputs=gr.JSON(label="Analysis Result"),
title="Sentiment Analysis",
description="Analyze the sentiment of text using TextBlob"
),
gr.Interface(
fn=get_sentiment_score,
inputs=gr.Textbox(placeholder="Enter text to score...", label="Text"),
outputs=gr.JSON(label="Score Result"),
title="Sentiment Score",
description="Get numerical sentiment score (-1 to 1)"
),
gr.Interface(
fn=classify_emotion,
inputs=gr.Textbox(placeholder="Enter text to classify...", label="Text"),
outputs=gr.JSON(label="Emotion Result"),
title="Emotion Classification",
description="Classify text into emotion categories"
),
gr.Interface(
fn=batch_analyze,
inputs=gr.Textbox(placeholder="Enter multiple texts, one per line...", label="Texts", lines=5),
outputs=gr.JSON(label="Batch Results"),
title="Batch Analysis",
description="Analyze multiple texts at once"
)
],
tab_names=["Sentiment Analysis", "Sentiment Score", "Emotion Classification", "Batch Analysis"],
title="MCP Sentiment Analysis Server"
)
if __name__ == "__main__":
# Launch the Gradio app
# Note: MCP support may require gradio>=5.30.0
try:
demo.launch(mcp_server=True)
except TypeError:
# Fallback for older Gradio versions without MCP support
print("⚠️ MCP server parameter not supported in this Gradio version")
print(" The web interface will work, but MCP endpoint may not be available")
demo.launch()
|