import gradio as gr import requests from PIL import Image import pytesseract import difflib from io import BytesIO from transformers import pipeline import trafilatura from nltk.tokenize import sent_tokenize import nltk nltk.download("punkt") # === Load AI model === reviewer = pipeline("text-generation", model="HuggingFaceH4/zephyr-7b-beta", max_new_tokens=200) device = "cpu" print(f"Device set to use {device}") # === Utility: Highlight diffs === def highlight_diff(original, suggestion): diff = difflib.ndiff(original.split(), suggestion.split()) result = "" for word in diff: if word.startswith("- "): result += f"{word[2:]} " elif word.startswith("+ "): result += f"{word[2:]} " elif word.startswith(" "): result += word[2:] + " " return result.strip() # === Extract blog content from URL === def extract_text_from_url(url): try: headers = {"User-Agent": "Mozilla/5.0"} response = requests.get(url, headers=headers, timeout=10) if response.status_code == 200: return trafilatura.extract(response.text) else: return f"❌ Blog Error: HTTP {response.status_code} on URL {url}" except Exception as e: return f"❌ Blog Error: {e}" # === Extract text from image URL (OCR) === def extract_text_from_image(image_url): try: img_data = requests.get(image_url).content image = Image.open(BytesIO(img_data)).convert("L") text = pytesseract.image_to_string(image) return text if text.strip() else "❌ OCR Error: No readable text found." except Exception as e: return f"❌ OCR Error: {e}" # === Suggestion generator === def generate_suggestions(text): sentences = sent_tokenize(text) suggestions = [] for sent in sentences: prompt = f"Improve the tone, grammar, clarity and flag any sensitive content:\n\n{sent}" output = reviewer(prompt, max_new_tokens=200)[0]["generated_text"] cleaned = output.replace(prompt, "").strip() suggestions.append(cleaned if cleaned else sent) return sentences, suggestions # === Final approval handler === def collect_decisions(originals, suggestions, *choices): results = [] for orig, sugg, choice in zip(originals, suggestions, choices): results.append(sugg if choice == "Accept" else orig) return "\n".join(results) # === Gradio UI === with gr.Blocks() as demo: gr.Markdown("# ✨ Blog Reviewer AI") gr.Markdown("Detect tone issues, errors, and sensitive content — and clean them interactively!") with gr.Tab("🔗 From Blog URL"): blog_url = gr.Textbox(label="Enter blog URL") fetch_btn = gr.Button("Fetch & Review") with gr.Tab("🖼️ From Image URL (OCR)"): image_url = gr.Textbox(label="Enter Image URL") image_btn = gr.Button("Extract & Review") with gr.Tab("📝 Paste Text"): pasted_text = gr.Textbox(label="Paste blog content here", lines=10) paste_btn = gr.Button("Review Text") output_section = gr.Column(visible=False) originals = gr.State([]) suggestions = gr.State([]) decision_radios = [] view_mode = gr.Radio(["Original", "Suggestion", "Side-by-Side"], value="Side-by-Side", label="Choose View") final_output = gr.Textbox(label="✅ Final Output", lines=12) finalize_btn = gr.Button("Generate Clean Version") sentence_blocks = [] # === Show suggestions UI === def show_review(text): origs, suggs = generate_suggestions(text) originals.value = origs suggestions.value = suggs return origs, suggs, True # === Populate sentence review rows dynamically === def populate_review_ui(origs, suggs): global decision_radios, sentence_blocks decision_radios = [] sentence_blocks = [] ui_blocks = [] for i, (orig, sugg) in enumerate(zip(origs, suggs)): orig_md = gr.Markdown(f"{orig}", visible=False) sugg_md = gr.Markdown(f"{sugg}", visible=False) diff_md = gr.Markdown(highlight_diff(orig, sugg), visible=True) radio = gr.Radio(["Accept", "Reject"], value="Accept", label=f"Suggestion {i+1}") decision_radios.append(radio) sentence_blocks.append((orig_md, sugg_md, diff_md)) ui_blocks.extend([orig_md, sugg_md, diff_md, radio]) return ui_blocks # === Toggle view mode === def toggle_view(view): updates = [] for orig_md, sugg_md, diff_md in sentence_blocks: if view == "Original": updates.extend([gr.update(visible=True), gr.update(visible=False), gr.update(visible=False)]) elif view == "Suggestion": updates.extend([gr.update(visible=False), gr.update(visible=True), gr.update(visible=False)]) else: # Side-by-side updates.extend([gr.update(visible=False), gr.update(visible=False), gr.update(visible=True)]) return updates # === Final output handler === def finalize_output(origs, suggs, *choices): return collect_decisions(origs, suggs, *choices) # Button click handlers fetch_btn.click(fn=extract_text_from_url, inputs=blog_url, outputs=pasted_text) image_btn.click(fn=extract_text_from_image, inputs=image_url, outputs=pasted_text) paste_btn.click(fn=show_review, inputs=pasted_text, outputs=[originals, suggestions, output_section]) # Dynamic render trigger originals.change(fn=populate_review_ui, inputs=[originals, suggestions], outputs=[]) view_mode.change(fn=toggle_view, inputs=view_mode, outputs=[item for block in sentence_blocks for item in block]) finalize_btn.click(fn=finalize_output, inputs=[originals, suggestions] + decision_radios, outputs=final_output) demo.launch()