import gradio as gr import json import re def summarize_text(text: str, max_sentences: int = 2) -> str: """Summarize text using extractive summarization (works on-device, no model needed). Use this tool when a user needs to summarize text, messages, emails, or articles. Optimized for mobile use cases: short inputs, concise outputs. Args: text: The text to summarize max_sentences: Maximum number of sentences in the summary (default 2) Returns: JSON string with the summary and stats """ if not text or len(text.strip()) < 10: return json.dumps({"error": "Text too short to summarize"}) # Split into sentences sentences = re.split(r'[.!?]+\s+', text.strip()) sentences = [s.strip() for s in sentences if len(s.strip()) > 5] if len(sentences) <= max_sentences: summary = ". ".join(sentences) + "." else: # Score sentences by word frequency (extractive summarization) words = re.findall(r'\w+', text.lower()) word_freq = {} for w in words: if len(w) > 2: word_freq[w] = word_freq.get(w, 0) + 1 # Score each sentence scored = [] for i, sent in enumerate(sentences): sent_words = re.findall(r'\w+', sent.lower()) score = sum(word_freq.get(w, 0) for w in sent_words) / (len(sent_words) + 1) # Boost first sentences (position bias) score *= (1.0 - i * 0.1) scored.append((score, i, sent)) # Take top sentences, maintain order scored.sort(key=lambda x: x[0], reverse=True) top = sorted(scored[:max_sentences], key=lambda x: x[1]) summary = ". ".join([s[2] for s in top]) + "." original_words = len(text.split()) summary_words = len(summary.split()) reduction = (1 - summary_words / original_words) * 100 if original_words > 0 else 0 return json.dumps({ "original_length_chars": len(text), "original_length_words": original_words, "summary": summary, "summary_length_chars": len(summary), "summary_length_words": summary_words, "compression_ratio": round(reduction, 1), "sentences_in_summary": max_sentences, }, indent=2) def classify_text(text: str) -> str: """Classify text as spam/not-spam and detect sentiment. Use this tool when a user needs to classify a message, email, or notification. Uses keyword-based heuristics that work on-device without a model. Args: text: The text to classify Returns: JSON string with classification results """ lower = text.lower() # Spam detection spam_keywords = ["winner", "congratulations", "click here", "claim now", "free", "urgent", "limited time", "act now", "cash prize", "gift card", "verify your account", "suspended", "lottery", "inheritance"] spam_score = sum(1 for kw in spam_keywords if kw in lower) is_spam = spam_score >= 2 # Sentiment positive_words = ["good", "great", "excellent", "amazing", "love", "happy", "best", "awesome", "fantastic", "wonderful", "perfect"] negative_words = ["bad", "terrible", "awful", "hate", "angry", "worst", "horrible", "disappointing", "frustrated", "broken"] pos_count = sum(1 for w in positive_words if w in lower) neg_count = sum(1 for w in negative_words if w in lower) if pos_count > neg_count: sentiment = "positive" elif neg_count > pos_count: sentiment = "negative" else: sentiment = "neutral" return json.dumps({ "text": text[:100] + "..." if len(text) > 100 else text, "is_spam": is_spam, "spam_confidence": min(spam_score / 3, 1.0), "sentiment": sentiment, "positive_signals": pos_count, "negative_signals": neg_count, }, indent=2) with gr.Blocks(title="dispatchAI Summarize MCP") as demo: gr.Markdown("## 📝 dispatchAI Summarize (MCP Tool)") with gr.Tab("Summarize"): s_input = gr.Textbox(label="Text to Summarize", lines=8, placeholder="Paste text here...") s_max = gr.Slider(1, 5, value=2, step=1, label="Max Sentences") s_btn = gr.Button("Summarize", variant="primary") s_out = gr.Textbox(label="Summary (JSON)", lines=10) s_btn.click(fn=summarize_text, inputs=[s_input, s_max], outputs=s_out) with gr.Tab("Classify"): c_input = gr.Textbox(label="Text to Classify", lines=5, placeholder="Paste message here...") c_btn = gr.Button("Classify", variant="primary") c_out = gr.Textbox(label="Classification (JSON)", lines=10) c_btn.click(fn=classify_text, inputs=c_input, outputs=c_out) demo.launch(mcp_server=True)