File size: 10,721 Bytes
f01d87d
 
 
 
7287745
aa0b994
f75caf0
fce7c2b
f75caf0
fce7c2b
 
 
 
 
 
 
aa0b994
 
 
f01d87d
 
f75caf0
f01d87d
 
 
 
aa0b994
 
f01d87d
f75caf0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fce7c2b
 
 
f01d87d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
aa0b994
 
 
 
 
f75caf0
aa0b994
 
 
 
 
f01d87d
fce7c2b
c67e9f8
fce7c2b
aa0b994
c67e9f8
 
 
 
 
 
 
aa0b994
c67e9f8
aa0b994
fce7c2b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f75caf0
fce7c2b
 
 
 
 
 
f75caf0
fce7c2b
 
 
 
 
 
 
 
f01d87d
 
fce7c2b
7287745
fce7c2b
 
c67e9f8
fce7c2b
c67e9f8
fce7c2b
 
 
c67e9f8
fce7c2b
 
 
 
c67e9f8
fce7c2b
f75caf0
fce7c2b
 
c67e9f8
fce7c2b
f75caf0
 
c67e9f8
fce7c2b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f75caf0
 
 
fce7c2b
 
 
 
 
f75caf0
 
 
c67e9f8
7287745
fce7c2b
 
 
 
f01d87d
f75caf0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e13fa72
f75caf0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f01d87d
 
 
7287745
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
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
        @function_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()