import streamlit as st import requests import time import re import pandas as pd # ==== CONFIG ==== API_KEY = st.secrets["API_KEY"] # Reads from Hugging Face / .streamlit/secrets.toml HEADERS = {"Authorization": f"Bearer {API_KEY}"} API_URLS = { "Summarizer": "https://api-inference.huggingface.co/models/facebook/bart-large-cnn", "Sentiment": "https://api-inference.huggingface.co/models/finiteautomata/bertweet-base-sentiment-analysis" } # ==== HELPERS ==== def query(api_url, payload): """Call Hugging Face inference API and return JSON or {'error':...}""" try: resp = requests.post(api_url, headers=HEADERS, json=payload, timeout=60) if resp.status_code != 200: return {"error": f"HTTP {resp.status_code}: {resp.text}"} return resp.json() except requests.exceptions.RequestException as e: return {"error": f"Request failed: {e}"} def split_sentences(text: str): """ Split text into sentences/phrases by ., ?, !, ;, :, comma and newlines. Keeps things simple and avoids external tokenizers. """ # Split on end punctuation + whitespace, OR on newline, OR on comma followed by space parts = re.split(r'(?<=[.!?;:])\s+|\n+|(?<=,)\s+', text.strip()) # Filter empties and strip return [p.strip() for p in parts if p and p.strip()] def extract_scores_from_api_response(res): """ Accepts HF response shapes like: - [{'label': 'NEG', 'score': 0.86}, ...] OR - [[{'label': 'NEG','score':...}, ...]] (nested list) Returns a dict with numeric neg, neu, pos (floats 0..1) or None on unexpected. """ sentiments = None if isinstance(res, list): # nested list (common with some HF endpoints) if len(res) > 0 and isinstance(res[0], list): sentiments = res[0] # flat list elif len(res) > 0 and isinstance(res[0], dict): sentiments = res if sentiments is None: return None neg = neu = pos = 0.0 for item in sentiments: lab = item.get("label", "").upper() sc = float(item.get("score", 0) or 0) if "NEG" in lab: neg = sc elif "NEU" in lab: neu = sc elif "POS" in lab: pos = sc return {"neg": neg, "neu": neu, "pos": pos} # ==== STREAMLIT UI ==== st.set_page_config(page_title="NLP Toolkit", page_icon="🧠", layout="centered") st.markdown("

🧠 AI NLP Toolkit

", unsafe_allow_html=True) st.write("Summarization & sentence-wise Sentiment Analysis.") tab1, tab2 = st.tabs(["📄 Summarizer", "📝 Sentiment Analysis"]) # --- Summarizer (unchanged) --- with tab1: text = st.text_area("Enter text to summarize:", height=220) if st.button("Summarize📝"): if not text.strip(): st.warning("Please enter text.") else: with st.spinner("Generating summary..."): time.sleep(0.8) out = query(API_URLS["Summarizer"], {"inputs": text}) if "error" in out: st.error(out["error"]) elif isinstance(out, list) and "summary_text" in out[0]: st.success("Summary ready") st.write(out[0]["summary_text"]) else: st.error("Unexpected response from summarizer.") # --- Sentiment Analysis (sentence-wise, table + average) --- with tab2: text_sent = st.text_area("Enter text for sentiment analysis:", height=220, key="sent_text2") if st.button("Analyze Sentiment🧠"): if not text_sent.strip(): st.warning("Please enter text.") else: sentences = split_sentences(text_sent) if len(sentences) == 0: st.warning("No sentences found after splitting.") else: rows = [] total_neg = total_neu = total_pos = 0.0 error_happened = False with st.spinner("Analyzing sentences..."): time.sleep(0.5) for i, s in enumerate(sentences, start=1): res = query(API_URLS["Sentiment"], {"inputs": s}) if isinstance(res, dict) and "error" in res: st.error(f"API error for sentence {i}: {res['error']}") error_happened = True break scores = extract_scores_from_api_response(res) if scores is None: st.error(f"Unexpected response format for sentence {i}.") error_happened = True break neg_pct = round(scores["neg"] * 100) neu_pct = round(scores["neu"] * 100) pos_pct = round(scores["pos"] * 100) rows.append({ "#": i, "Sentence": f'"{s}"', "Negative": f"{neg_pct}%", "Neutral": f"{neu_pct}%", "Positive": f"{pos_pct}%" }) total_neg += scores["neg"] total_neu += scores["neu"] total_pos += scores["pos"] if not error_happened: n = len(sentences) avg_neg = round((total_neg / n) * 100) avg_neu = round((total_neu / n) * 100) avg_pos = round((total_pos / n) * 100) # Append average row rows.append({ "#": "Avg", "Sentence": "—", "Negative": f"{avg_neg}%", "Neutral": f"{avg_neu}%", "Positive": f"{avg_pos}%" }) df = pd.DataFrame(rows, columns=["#", "Sentence", "Negative", "Neutral", "Positive"]) st.table(df)