import os import gradio as gr import pandas as pd import numpy as np import re from huggingface_hub import InferenceClient def load_data(file_obj): """Safely loads CSV, Excel, or TXT file into a Pandas DataFrame.""" if file_obj is None: return None, gr.update(choices=[], visible=False), "Please upload a file." file_path = file_obj.name ext = os.path.splitext(file_path)[1].lower() try: if ext == '.csv': df = pd.read_csv(file_path) elif ext in ['.xls', '.xlsx']: df = pd.read_excel(file_path) elif ext == '.txt': with open(file_path, 'r', encoding='utf-8') as f: content = f.read() df = pd.DataFrame({'text': [content]}) else: return None, gr.update(choices=[], visible=False), "Unsupported file format. Please upload .csv, .xlsx, or .txt." string_cols = [col for col in df.columns if df[col].dtype == 'object' or df[col].astype(str).str.len().mean() > 5] if not string_cols: string_cols = list(df.columns) return df, gr.update(choices=string_cols, value=string_cols[0], visible=True), f"Successfully loaded dataset with {len(df)} rows." except Exception as e: return None, gr.update(choices=[], visible=False), f"Error loading file: {str(e)}" def run_extraw_cpu(text, ratio=0.3): """Rule-based TF-IDF sentence extractive summarizer running entirely locally on CPU.""" # Split text into sentences sentences = re.split(r'(?<=[.!?])\s+', text) sentences = [s.strip() for s in sentences if len(s.strip()) > 10] if len(sentences) <= 3: return text # Too short to summarize # Calculate word frequencies words = re.findall(r'\b[a-zA-Z]{3,}\b', text.lower()) stopwords = {'the', 'and', 'for', 'that', 'with', 'this', 'have', 'from', 'your', 'will', 'not', 'are', 'was', 'were', 'but', 'how'} word_freqs = {} for word in words: if word not in stopwords: word_freqs[word] = word_freqs.get(word, 0) + 1 if not word_freqs: return " ".join(sentences[:2]) # Normalize frequencies max_freq = max(word_freqs.values()) for word in word_freqs: word_freqs[word] = word_freqs[word] / max_freq # Score sentences sentence_scores = {} for i, sent in enumerate(sentences): score = 0 sent_words = re.findall(r'\b[a-zA-Z]{3,}\b', sent.lower()) for word in sent_words: if word in word_freqs: score += word_freqs[word] sentence_scores[i] = score / max(1, len(sent_words)) # normalize by length to prevent biased long sentences # Determine number of sentences to extract n_sentences = max(1, int(len(sentences) * ratio)) # Select top sentences top_indices = sorted(sentence_scores, key=sentence_scores.get, reverse=True)[:n_sentences] # Sort indices to preserve original chronological order top_indices.sort() summary = " ".join([sentences[idx] for idx in top_indices]) return summary def run_transformer_summarize(text, hf_token, model_name, ratio): """Summarizes using Hugging Face Serverless Inference API.""" if not hf_token: raise ValueError("Hugging Face API Token is required for Transformer Mode.") client = InferenceClient(token=hf_token) # Calculate desired length bounds based on text length words_count = len(text.split()) max_len = max(30, int(words_count * ratio * 1.2)) min_len = max(10, int(words_count * ratio * 0.8)) try: resp = client.summarization( text=text, model=model_name, parameters={"max_length": max_len, "min_length": min_len} ) return resp.get("summary_text", "") except Exception as e: raise RuntimeError(f"Hugging Face API error: {str(e)}") def process_summarization(text_input, file_obj, text_col, method, hf_token, hf_model, length_ratio): # Parse documents docs = [] if file_obj is not None: df, _, _ = load_data(file_obj) if df is not None and text_col in df.columns: docs = df[text_col].astype(str).fillna("").tolist() elif text_input and text_input.strip(): docs = [text_input] if not docs: return None, None, "Please enter text or upload a valid dataset first." # Standardize ratio ratio_dict = {"Short Summary (15%)": 0.15, "Medium Summary (35%)": 0.35, "Detailed Summary (55%)": 0.55} ratio = ratio_dict[length_ratio] summaries = [] # We only show visual/download stats for the first doc if bulk uploaded for idx, doc_text in enumerate(docs): if not doc_text.strip(): summaries.append("") continue try: if method == "Local Extractive (CPU & Fast)": sum_text = run_extraw_cpu(doc_text, ratio) else: sum_text = run_transformer_summarize(doc_text, hf_token, hf_model, ratio) summaries.append(sum_text) except Exception as e: return None, None, f"Execution failed at row {idx + 1}: {str(e)}" final_summary = summaries[0] original_len = len(docs[0].split()) summary_len = len(final_summary.split()) compression = round((1 - (summary_len / max(1, original_len))) * 100, 1) # Save output txt out_path = "document_summary.txt" with open(out_path, 'w', encoding='utf-8') as f: f.write(final_summary) # Clean visual metrics metrics_md = f""" ### Summarization Metrics - **Original Document Length**: {original_len} words - **Summary Length**: {summary_len} words - **Compression Rate**: {compression}% shorter than the original """ return final_summary, out_path, metrics_md custom_css = """ body { background-color: #0b0f19; color: #f3f4f6; } .gradio-container { font-family: 'Inter', sans-serif !important; } h1, h2 { color: #6366f1 !important; } """ with gr.Blocks(theme=gr.themes.Default(primary_hue="indigo", secondary_hue="slate"), css=custom_css) as demo: df_state = gr.State() gr.HTML("""
Condense long articles, book chapters, or reports down to essential summaries. Runs locally on standard CPU scoring, or utilizes advanced neural models using your personal Hugging Face Token.