Spaces:
Runtime error
Runtime error
Upload app.py with huggingface_hub
Browse files
app.py
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gradio as gr
|
| 2 |
+
import json
|
| 3 |
+
import re
|
| 4 |
+
|
| 5 |
+
def summarize_text(text: str, max_sentences: int = 2) -> str:
|
| 6 |
+
"""Summarize text using extractive summarization (works on-device, no model needed).
|
| 7 |
+
|
| 8 |
+
Use this tool when a user needs to summarize text, messages, emails, or articles.
|
| 9 |
+
Optimized for mobile use cases: short inputs, concise outputs.
|
| 10 |
+
|
| 11 |
+
Args:
|
| 12 |
+
text: The text to summarize
|
| 13 |
+
max_sentences: Maximum number of sentences in the summary (default 2)
|
| 14 |
+
|
| 15 |
+
Returns:
|
| 16 |
+
JSON string with the summary and stats
|
| 17 |
+
"""
|
| 18 |
+
if not text or len(text.strip()) < 10:
|
| 19 |
+
return json.dumps({"error": "Text too short to summarize"})
|
| 20 |
+
|
| 21 |
+
# Split into sentences
|
| 22 |
+
sentences = re.split(r'[.!?]+\s+', text.strip())
|
| 23 |
+
sentences = [s.strip() for s in sentences if len(s.strip()) > 5]
|
| 24 |
+
|
| 25 |
+
if len(sentences) <= max_sentences:
|
| 26 |
+
summary = ". ".join(sentences) + "."
|
| 27 |
+
else:
|
| 28 |
+
# Score sentences by word frequency (extractive summarization)
|
| 29 |
+
words = re.findall(r'\w+', text.lower())
|
| 30 |
+
word_freq = {}
|
| 31 |
+
for w in words:
|
| 32 |
+
if len(w) > 2:
|
| 33 |
+
word_freq[w] = word_freq.get(w, 0) + 1
|
| 34 |
+
|
| 35 |
+
# Score each sentence
|
| 36 |
+
scored = []
|
| 37 |
+
for i, sent in enumerate(sentences):
|
| 38 |
+
sent_words = re.findall(r'\w+', sent.lower())
|
| 39 |
+
score = sum(word_freq.get(w, 0) for w in sent_words) / (len(sent_words) + 1)
|
| 40 |
+
# Boost first sentences (position bias)
|
| 41 |
+
score *= (1.0 - i * 0.1)
|
| 42 |
+
scored.append((score, i, sent))
|
| 43 |
+
|
| 44 |
+
# Take top sentences, maintain order
|
| 45 |
+
scored.sort(key=lambda x: x[0], reverse=True)
|
| 46 |
+
top = sorted(scored[:max_sentences], key=lambda x: x[1])
|
| 47 |
+
summary = ". ".join([s[2] for s in top]) + "."
|
| 48 |
+
|
| 49 |
+
original_words = len(text.split())
|
| 50 |
+
summary_words = len(summary.split())
|
| 51 |
+
reduction = (1 - summary_words / original_words) * 100 if original_words > 0 else 0
|
| 52 |
+
|
| 53 |
+
return json.dumps({
|
| 54 |
+
"original_length_chars": len(text),
|
| 55 |
+
"original_length_words": original_words,
|
| 56 |
+
"summary": summary,
|
| 57 |
+
"summary_length_chars": len(summary),
|
| 58 |
+
"summary_length_words": summary_words,
|
| 59 |
+
"compression_ratio": round(reduction, 1),
|
| 60 |
+
"sentences_in_summary": max_sentences,
|
| 61 |
+
}, indent=2)
|
| 62 |
+
|
| 63 |
+
def classify_text(text: str) -> str:
|
| 64 |
+
"""Classify text as spam/not-spam and detect sentiment.
|
| 65 |
+
|
| 66 |
+
Use this tool when a user needs to classify a message, email, or notification.
|
| 67 |
+
Uses keyword-based heuristics that work on-device without a model.
|
| 68 |
+
|
| 69 |
+
Args:
|
| 70 |
+
text: The text to classify
|
| 71 |
+
|
| 72 |
+
Returns:
|
| 73 |
+
JSON string with classification results
|
| 74 |
+
"""
|
| 75 |
+
lower = text.lower()
|
| 76 |
+
|
| 77 |
+
# Spam detection
|
| 78 |
+
spam_keywords = ["winner", "congratulations", "click here", "claim now", "free",
|
| 79 |
+
"urgent", "limited time", "act now", "cash prize", "gift card",
|
| 80 |
+
"verify your account", "suspended", "lottery", "inheritance"]
|
| 81 |
+
spam_score = sum(1 for kw in spam_keywords if kw in lower)
|
| 82 |
+
is_spam = spam_score >= 2
|
| 83 |
+
|
| 84 |
+
# Sentiment
|
| 85 |
+
positive_words = ["good", "great", "excellent", "amazing", "love", "happy",
|
| 86 |
+
"best", "awesome", "fantastic", "wonderful", "perfect"]
|
| 87 |
+
negative_words = ["bad", "terrible", "awful", "hate", "angry", "worst",
|
| 88 |
+
"horrible", "disappointing", "frustrated", "broken"]
|
| 89 |
+
|
| 90 |
+
pos_count = sum(1 for w in positive_words if w in lower)
|
| 91 |
+
neg_count = sum(1 for w in negative_words if w in lower)
|
| 92 |
+
|
| 93 |
+
if pos_count > neg_count:
|
| 94 |
+
sentiment = "positive"
|
| 95 |
+
elif neg_count > pos_count:
|
| 96 |
+
sentiment = "negative"
|
| 97 |
+
else:
|
| 98 |
+
sentiment = "neutral"
|
| 99 |
+
|
| 100 |
+
return json.dumps({
|
| 101 |
+
"text": text[:100] + "..." if len(text) > 100 else text,
|
| 102 |
+
"is_spam": is_spam,
|
| 103 |
+
"spam_confidence": min(spam_score / 3, 1.0),
|
| 104 |
+
"sentiment": sentiment,
|
| 105 |
+
"positive_signals": pos_count,
|
| 106 |
+
"negative_signals": neg_count,
|
| 107 |
+
}, indent=2)
|
| 108 |
+
|
| 109 |
+
with gr.Blocks(title="dispatchAI Summarize MCP") as demo:
|
| 110 |
+
gr.Markdown("## 📝 dispatchAI Summarize (MCP Tool)")
|
| 111 |
+
|
| 112 |
+
with gr.Tab("Summarize"):
|
| 113 |
+
s_input = gr.Textbox(label="Text to Summarize", lines=8, placeholder="Paste text here...")
|
| 114 |
+
s_max = gr.Slider(1, 5, value=2, step=1, label="Max Sentences")
|
| 115 |
+
s_btn = gr.Button("Summarize", variant="primary")
|
| 116 |
+
s_out = gr.Textbox(label="Summary (JSON)", lines=10)
|
| 117 |
+
s_btn.click(fn=summarize_text, inputs=[s_input, s_max], outputs=s_out)
|
| 118 |
+
|
| 119 |
+
with gr.Tab("Classify"):
|
| 120 |
+
c_input = gr.Textbox(label="Text to Classify", lines=5, placeholder="Paste message here...")
|
| 121 |
+
c_btn = gr.Button("Classify", variant="primary")
|
| 122 |
+
c_out = gr.Textbox(label="Classification (JSON)", lines=10)
|
| 123 |
+
c_btn.click(fn=classify_text, inputs=c_input, outputs=c_out)
|
| 124 |
+
|
| 125 |
+
demo.launch(mcp_server=True)
|