import gradio as gr import requests from bs4 import BeautifulSoup from nltk import download, sent_tokenize import google.generativeai as genai import os import re import tempfile import asyncio # Download NLTK data download('punkt') download('punkt_tab') # Configure Gemini API using environment variable api_key = os.environ.get("GEMINI_API_KEY") if not api_key: raise ValueError("GEMINI_API_KEY not found in environment variables. Please set it in your environment.") genai.configure(api_key=api_key) # Use gemini-1.5-flash for faster text analysis try: model = genai.GenerativeModel('gemini-1.5-flash') except Exception as e: print(f"Error initializing model: {str(e)}") print("Available models:") for m in genai.list_models(): print(m.name) raise ValueError("Failed to initialize gemini-1.5-flash. Check available models above and update the model name.") # Prompt for Gemini to analyze text PROMPT = """ You are an AI content reviewer. Analyze the provided text for the following: 1. *Grammar Issues*: Identify and suggest corrections for grammatical errors. 2. *Legal Policy Violations*: Flag content that may violate common legal policies (e.g., copyright infringement, defamation, incitement to violence). 3. *Crude/Abusive Language*: Detect crude, offensive, or abusive language. 4. *Sensitive Topics*: Identify content related to sensitive topics such as racism, gender bias, or other forms of discrimination. Return the results in the following markdown format: # Blog Review Report ## Grammar Corrections 1. [Heading of issue] - CONTENT: [Exact line or part of text with the issue] - SUGGESTION: [Suggested correction] - ISSUE: [Description of the issue] 2. [Heading of next issue] - CONTENT: [Exact line or part of text with the issue] - SUGGESTION: [Suggested correction] - ISSUE: [Description of the issue] [Continue numbering for additional issues or state "None detected"] ## Legal Policy Violations - CONTENT: [Exact line or part of text with the issue] SUGGESTION: [Suggested action or correction] ISSUE: [Description of the legal violation] [Or state "None detected"] ## Crude/Abusive Language - [List instances of crude or abusive language or "None detected"] ## Sensitive Topics - [List instances of sensitive topics or "None detected"] For each issue, provide the exact text, a suggested correction or action, and a concise explanation. Be precise and ensure the output strictly follows the specified format. """ async def fetch_url_content(url): try: response = requests.get(url, timeout=10) response.raise_for_status() soup = BeautifulSoup(response.text, 'html.parser') content = ' '.join([p.get_text(strip=True) for p in soup.find_all(['p', 'article', 'div'])]) return content if content else "No readable content found on the page." except Exception as e: return f"Error fetching URL: {str(e)}" async def review_blog(text_input, url_input): # Initialize output variables button_text = "Processing..." report = "" download_path = None # Determine input type if text_input and not url_input: input_type = "Text" input_text = text_input elif url_input and not text_input: input_type = "URL" input_text = url_input else: return "Review Blog", "Error: Please provide input in either the Text or URL tab, but not both.", gr.update(visible=False) # Handle empty input if not input_text: return "Review Blog", "Error: No input provided.", gr.update(visible=False) try: # Wrap the entire process in a timeout async def process_with_timeout(): nonlocal button_text, report, download_path # Handle URL input if input_type == "URL": button_text = "Fetching content..." content = await fetch_url_content(input_text) if content.startswith("Error"): return "Review Blog", content, gr.update(visible=False) input_text_content = content else: input_text_content = input_text # Tokenize input for analysis sentences = sent_tokenize(input_text_content) analysis_text = "\n".join(sentences) # Update button for API call button_text = "Generating report..." try: response = await asyncio.to_thread(model.generate_content, PROMPT + "\n\nText to analyze:\n" + analysis_text) report = response.text.strip() report = re.sub(r'^```markdown\n|```$', '', report, flags=re.MULTILINE) except Exception as e: report = f"Error analyzing content with Gemini: {str(e)}. Please check your API key, network connection, or model availability." print("Available models:") for m in genai.list_models(): print(m.name) return "Review Blog", report, gr.update(visible=False) # Create a temporary file to store the report try: with tempfile.NamedTemporaryFile(mode='w', suffix='.md', delete=False, encoding='utf-8') as temp_file: temp_file.write(report) download_path = temp_file.name report = f"**Report generated, please scroll down to view.**\n\n{report}" return "Review Blog", report, gr.update(visible=True, value=download_path) except Exception as e: return "Review Blog", f"Error creating temporary file: {str(e)}", gr.update(visible=False) # Execute with timeout return await asyncio.wait_for(process_with_timeout(), timeout=30) except asyncio.TimeoutError: return "Review Blog", "Error: Process timed out after 30 seconds.", gr.update(visible=False) except Exception as e: return "Review Blog", f"Unexpected error: {str(e)}", gr.update(visible=False) # Custom CSS for hover effect, loading state, and Inter font custom_css = """ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap'); .gradio-container { font-family: 'Inter', sans-serif !important; } .review-btn { transition: all 0.3s ease; font-weight: 500; background-color: #2c3e50; color: white; border-radius: 8px; padding: 10px 20px; position: relative; } .review-btn:hover { background-color: #4CAF50; color: white; transform: scale(1.05); } .review-btn:disabled { opacity: 0.7; cursor: not-allowed; } .review-btn:disabled::before { content: ''; display: inline-block; width: 16px; height: 16px; border: 2px solid #fff; border-radius: 50%; border-top-color: transparent; animation: spin 1s linear infinite; margin-right: 8px; vertical-align: middle; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .tab-nav button { font-family: 'Inter', sans-serif; font-weight: 500; } input, textarea { font-family: 'Inter', sans-serif; } """ # Gradio UI with Tabs with gr.Blocks(theme=gr.themes.Monochrome(), css=custom_css) as demo: gr.Markdown("# 📝 AI Blog Reviewer") gr.Markdown("Enter blog text or a URL to review for grammar, legal issues, crude language, and sensitive topics. The report is generated in markdown format.") with gr.Tabs(): with gr.TabItem("Text"): text_input = gr.Textbox(lines=8, label="Blog Content", placeholder="Paste your blog text here...") with gr.TabItem("URL"): url_input = gr.Textbox(lines=1, label="Blog URL", placeholder="Enter the blog URL here...") status_button = gr.Button(value="Review Blog", elem_classes=["review-btn"]) gr.Markdown("### 📄 Review Report") report_output = gr.Markdown() download_btn = gr.File(label="Download Report", visible=False) # Bind the review button to process inputs status_button.click( fn=review_blog, inputs=[text_input, url_input], outputs=[status_button, report_output, download_btn] ) demo.launch()