Spaces:
Sleeping
Sleeping
import gradio as gr | |
from duckduckgo_search import DDGS | |
from datetime import datetime | |
import os | |
import asyncio | |
import nest_asyncio | |
from openai import OpenAI | |
import traceback | |
import re | |
try: | |
from agents import Agent, Runner, function_tool, OpenAIChatCompletionsModel | |
AGENTS_AVAILABLE = True | |
except Exception: | |
print("WARNING: openai-agents package not fully functional, using fallback mode") | |
AGENTS_AVAILABLE = False | |
# Apply nest_asyncio to allow nested event loops | |
nest_asyncio.apply() | |
# Set up environment variables | |
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY") | |
# Get current date for default value | |
default_date = datetime.now().strftime("%Y-%m-%d") | |
# Configure OpenAI client | |
client = OpenAI(api_key=OPENAI_API_KEY) | |
# Function to make URLs clickable in Markdown | |
def make_urls_clickable(text): | |
# Find URLs in the text | |
url_pattern = r'(https?://[^\s]+)' | |
# Replace URLs with markdown links | |
text_with_links = re.sub(url_pattern, r'[\1](\1)', text) | |
return text_with_links | |
# Function to format article output with better readability | |
def format_articles(text): | |
# Make URLs clickable | |
text = make_urls_clickable(text) | |
# Add better formatting for article titles (lines starting with #) | |
text = re.sub(r'# (.+)', r'## \1', text) | |
# Add horizontal rule between articles if not already present | |
if "---" not in text: | |
text = re.sub(r'\n\n(?=# )', r'\n\n---\n\n', text) | |
# Enhance description and URL formatting | |
text = re.sub(r'\*\*URL\*\*: (.+)', r'**Source**: \1', text) | |
text = re.sub(r'\*\*Description\*\*:', r'**Summary**:', text) | |
return text | |
# Direct search function that doesn't depend on the agents package | |
def direct_news_search(topic, language="English", search_date=None): | |
"""Search for news articles using DuckDuckGo directly""" | |
if not search_date: | |
search_date = datetime.now().strftime("%Y-%m") | |
else: | |
search_date = search_date[:7] # Just get YYYY-MM portion | |
print(f"Running DuckDuckGo news search for {topic} in {language} for date {search_date}...") | |
# Map common languages to their search keywords | |
language_keywords = { | |
"English": "", # Default, no special keyword needed | |
"Hindi": "हिंदी", | |
"Spanish": "español", | |
"French": "français", | |
"German": "deutsch", | |
"Japanese": "日本語", | |
"Chinese": "中文", | |
"Russian": "русский", | |
"Arabic": "العربية", | |
"Portuguese": "português", | |
"Italian": "italiano", | |
"Dutch": "nederlands", | |
"Korean": "한국어", | |
"Turkish": "türkçe", | |
"Kannada": "ಕನ್ನಡ", | |
"Tamil": "தமிழ்", | |
"Telugu": "తెలుగు", | |
"Bengali": "বাংলা", | |
"Marathi": "मराठी" | |
} | |
# Get language keyword if available | |
lang_keyword = language_keywords.get(language, language) | |
# Add language to search query if it's not English | |
search_query = f"{topic} {lang_keyword} {search_date}" if language != "English" else f"{topic} {search_date}" | |
try: | |
# DuckDuckGo search | |
ddg_api = DDGS() | |
results = ddg_api.text(search_query, max_results=5) | |
if results: | |
news_results = "\n\n".join([f"# {result['title']}\n**URL**: {result['href']}\n**Description**: {result['body']}" for result in results]) | |
return news_results | |
else: | |
return f"Could not find news results for {topic} in {language} for {search_date}." | |
except Exception as e: | |
return f"Error searching for news: {str(e)}" | |
# Direct OpenAI API call | |
def generate_with_openai(prompt): | |
"""Use OpenAI API directly to generate content""" | |
try: | |
response = client.chat.completions.create( | |
model="gpt-4o-mini", | |
messages=[{"role": "user", "content": prompt}], | |
temperature=0.7, | |
max_tokens=2000 | |
) | |
return response.choices[0].message.content | |
except Exception as e: | |
return f"Error with OpenAI API: {str(e)}" | |
# Try to set up agents if available | |
if AGENTS_AVAILABLE: | |
try: | |
# Define the model | |
model = OpenAIChatCompletionsModel( | |
model="gpt-4o-mini", | |
openai_client=client | |
) | |
# News search tool | |
def get_news_articles(topic, language="English", search_date=None): | |
return direct_news_search(topic, language, search_date) | |
# Create agents | |
news_agent = Agent( | |
name="News Agent", | |
instructions="You provide the latest news articles for a given topic using DuckDuckGo search. You can search for news in different languages when specified. Format each article with a # heading.", | |
tools=[get_news_articles], | |
model=model | |
) | |
editor_agent = Agent( | |
name="Editor Assistant", | |
instructions="Rewrite and give me a news article ready for publishing. Each news story should be in a separate section with a clear headline (use # for headlines). Maintain the original language of the news stories. If the content is in a language other than English, edit and format in that same language. Keep the original URLs and mark them as **URL**: [url]. Use --- to separate articles.", | |
model=model | |
) | |
print("Successfully initialized agent-based workflow") | |
except Exception as e: | |
print(f"Failed to initialize agents: {e}") | |
AGENTS_AVAILABLE = False | |
# Workflow function for Gradio | |
def fetch_and_edit_news(topic, language, search_date): | |
"""Process news request using agents if available, otherwise fall back to direct API calls""" | |
try: | |
# Try agent-based approach if available | |
if AGENTS_AVAILABLE: | |
try: | |
print("Attempting to use agent-based workflow...") | |
# Initialize event loop | |
loop = asyncio.new_event_loop() | |
asyncio.set_event_loop(loop) | |
# Run the news agent | |
news_prompt = f"Get me the news about {topic} in {language} for date {search_date}" | |
news_result = Runner.run_sync(news_agent, news_prompt) | |
raw_news = news_result.final_output | |
# Run the editor agent | |
editor_prompt = f"Please edit the following news in {language} language. Maintain the original language and URLs. Format with markdown, using # for headlines and --- to separate articles: \n\n{raw_news}" | |
editor_result = Runner.run_sync(editor_agent, editor_prompt) | |
edited_news = editor_result.final_output | |
print("Agent-based workflow completed successfully") | |
# Format the output with better readability | |
return format_articles(edited_news) | |
except Exception as agent_error: | |
print(f"Agent-based workflow failed: {agent_error}") | |
print(traceback.format_exc()) | |
print("Falling back to direct API approach...") | |
# Fall through to the direct approach | |
else: | |
print("Using direct API approach (agents not available)...") | |
# Direct approach (used when agents fail or aren't available) | |
# Step 1: Get news directly | |
raw_news = direct_news_search(topic, language, search_date) | |
if raw_news.startswith("Error") or raw_news.startswith("Could not find"): | |
return raw_news | |
# Step 2: Edit the news with OpenAI | |
editor_prompt = f""" | |
Please edit and reformat the following news into a cohesive, publication-ready format. | |
Maintain the original language ({language}). | |
Format with markdown, using # for headlines and --- to separate articles. | |
Keep the original URLs and mark them as **URL**: [url]. | |
{raw_news} | |
""" | |
edited_news = generate_with_openai(editor_prompt) | |
# Format the output with better readability | |
return format_articles(edited_news) | |
except Exception as e: | |
error_details = traceback.format_exc() | |
print(f"Error in workflow: {e}") | |
print(error_details) | |
return f"Error processing your request: {str(e)}\n\nPlease check that your OpenAI API key is correctly set in the repository secrets." | |
# Read custom CSS from file | |
try: | |
with open('style.css', 'r') as f: | |
custom_css = f.read() | |
except Exception as e: | |
print(f"Warning: Could not read CSS file: {e}") | |
# Fallback minimal CSS if file can't be read | |
custom_css = """ | |
.container { max-width: 900px; margin: auto; } | |
.article-output { border: 1px solid #ddd; padding: 20px; } | |
""" | |
# Create Gradio interface with a custom theme | |
with gr.Blocks(title="Multilingual AI News Generator", theme=gr.themes.Soft(primary_hue="blue"), css=custom_css) as demo: | |
with gr.Column(elem_classes="container"): | |
with gr.Column(elem_classes="header"): | |
gr.Markdown("# Multilingual AI News Generator") | |
gr.Markdown("Enter a topic, select a language, and choose a date to receive curated and edited news articles") | |
with gr.Row(elem_classes="search-options"): | |
topic_input = gr.Textbox( | |
label="News Topic", | |
placeholder="Enter a topic (e.g., AI, Climate Change, Sports)" | |
) | |
language_dropdown = gr.Dropdown( | |
choices=[ | |
"English", "Hindi", "Spanish", "French", "German", | |
"Japanese", "Chinese", "Russian", "Arabic", "Portuguese", | |
"Italian", "Dutch", "Korean", "Turkish", "Kannada", | |
"Tamil", "Telugu", "Bengali", "Marathi" | |
], | |
label="Language", | |
value="English" | |
) | |
date_picker = gr.Textbox( | |
label="Search Date", | |
placeholder="YYYY-MM-DD", | |
value=default_date | |
) | |
submit_btn = gr.Button("Generate News Article", elem_classes="generate-btn") | |
# Use Markdown for output to enable clickable links and better formatting | |
output_box = gr.Markdown(elem_classes="article-output") | |
submit_btn.click( | |
fn=fetch_and_edit_news, | |
inputs=[topic_input, language_dropdown, date_picker], | |
outputs=output_box | |
) | |
# Launch the app | |
if __name__ == "__main__": | |
demo.launch() |