siyah1 commited on
Commit
3d4ef3d
Β·
verified Β·
1 Parent(s): d0d8ec3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +509 -537
app.py CHANGED
@@ -1,684 +1,656 @@
 
1
  import streamlit as st
2
  import pandas as pd
3
- from typing import Union, List, Dict, Optional
4
  from groq import Groq
5
  import os
6
  from duckduckgo_search import DDGS
7
- import json
8
- from datetime import datetime, timedelta
9
- import time
10
 
11
  # Set page configuration with fullscreen layout and custom theme
12
  st.set_page_config(
13
- page_title="SmolAgent Travel Planner",
14
  layout="wide",
15
  initial_sidebar_state="collapsed",
16
- page_icon="✈️"
17
  )
18
 
19
- # Enhanced CSS for a beautiful travel-themed styling
 
 
 
 
 
 
 
 
 
 
 
 
20
  st.markdown("""
21
  <style>
22
  /* Base styles */
23
  html, body, .stApp, .main {
24
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
25
  color: #ffffff !important;
26
  }
27
-
28
  /* Typography */
29
  h1, h2, h3, h4, h5, h6 {
30
- color: #ffffff !important;
31
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif !important;
32
- text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
33
  }
34
-
 
 
 
 
35
  /* Cards and containers */
36
  .card {
37
- background: rgba(255, 255, 255, 0.1) !important;
38
- backdrop-filter: blur(10px) !important;
39
- border: 1px solid rgba(255, 255, 255, 0.2) !important;
40
- border-radius: 15px !important;
41
- box-shadow: 0 8px 32px rgba(31, 38, 135, 0.37) !important;
42
- padding: 2rem !important;
43
- margin-bottom: 2rem !important;
44
  }
45
-
46
- /* Agent status cards */
47
- .agent-card {
48
- background: rgba(255, 255, 255, 0.15) !important;
49
- border: 1px solid rgba(255, 255, 255, 0.3) !important;
50
- border-radius: 12px !important;
 
51
  padding: 1.5rem !important;
52
- margin: 1rem 0 !important;
53
- backdrop-filter: blur(5px) !important;
54
  }
55
-
56
  /* Input fields */
57
- .stTextInput input, .stTextArea textarea, .stSelectbox div[data-baseweb="select"] {
58
- background: rgba(255, 255, 255, 0.2) !important;
59
- color: #ffffff !important;
60
- border: 1px solid rgba(255, 255, 255, 0.3) !important;
61
- border-radius: 8px !important;
62
  }
63
-
64
- .stTextInput input::placeholder, .stTextArea textarea::placeholder {
65
- color: rgba(255, 255, 255, 0.7) !important;
66
- }
67
-
68
  /* Buttons */
69
  .stButton > button {
70
- background: linear-gradient(45deg, #ff6b6b, #ff8e8e) !important;
71
- color: #ffffff !important;
72
  border: none !important;
73
- border-radius: 25px !important;
 
74
  font-weight: bold !important;
75
- padding: 0.75rem 2rem !important;
76
  transition: all 0.3s ease !important;
77
- box-shadow: 0 4px 15px rgba(255, 107, 107, 0.4) !important;
78
  }
79
-
80
  .stButton > button:hover {
81
- transform: translateY(-2px) !important;
82
- box-shadow: 0 8px 25px rgba(255, 107, 107, 0.6) !important;
83
- }
84
-
85
- /* Progress indicators */
86
- .agent-status {
87
- display: flex;
88
- align-items: center;
89
- gap: 10px;
90
- padding: 10px;
91
- border-radius: 8px;
92
- margin: 5px 0;
93
- background: rgba(255, 255, 255, 0.1);
94
- backdrop-filter: blur(5px);
95
  }
96
-
97
- .status-working {
98
- border-left: 4px solid #4CAF50;
99
- }
100
-
101
- .status-complete {
102
- border-left: 4px solid #2196F3;
103
  }
104
-
105
- .status-error {
106
- border-left: 4px solid #F44336;
 
107
  }
108
-
109
- /* Spinning animation */
110
- .spinner {
111
- border: 2px solid rgba(255, 255, 255, 0.3);
112
- border-radius: 50%;
113
- border-top: 2px solid #ffffff;
114
- width: 20px;
115
- height: 20px;
116
- animation: spin 1s linear infinite;
117
  }
118
-
119
- @keyframes spin {
120
- 0% { transform: rotate(0deg); }
121
- 100% { transform: rotate(360deg); }
122
  }
123
-
124
- /* Results styling */
125
- .travel-result {
126
- background: rgba(255, 255, 255, 0.05) !important;
127
- border-left: 4px solid #4CAF50 !important;
128
- border-radius: 8px !important;
129
- padding: 1.5rem !important;
130
- margin: 1rem 0 !important;
131
  }
132
-
133
  /* Tabs */
134
  .stTabs [aria-selected="true"] {
135
- background: rgba(255, 255, 255, 0.2) !important;
136
- color: #ffffff !important;
137
- border-bottom: 2px solid #ff6b6b !important;
138
  }
139
-
140
  /* Logo styling */
141
  .logo-text {
142
- font-size: 3rem !important;
143
- background: linear-gradient(45deg, #FFD700, #FFA500) !important;
144
  -webkit-background-clip: text !important;
145
  -webkit-text-fill-color: transparent !important;
146
- text-shadow: 2px 2px 4px rgba(0,0,0,0.3) !important;
147
  }
148
-
149
- /* Success indicators */
150
- .success-badge {
151
- background: linear-gradient(45deg, #4CAF50, #66BB6A) !important;
152
- color: white !important;
153
- padding: 0.5rem 1rem !important;
154
- border-radius: 20px !important;
155
- font-size: 0.9rem !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
  }
157
  </style>
158
  """, unsafe_allow_html=True)
159
 
160
 
161
- class SmolAgent:
162
  """
163
- Base agent class implementing the smol agent architecture.
164
- Each agent has a specific role and can collaborate with others.
165
  """
166
- def __init__(self, name: str, role: str, llm_client, search_tool):
167
- self.name = name
168
- self.role = role
169
- self.llm = llm_client
170
- self.search = search_tool
171
- self.status = "idle"
172
- self.last_result = None
173
-
174
- def set_status(self, status: str, message: str = ""):
175
- self.status = status
176
- self.status_message = message
177
-
178
- def execute_task(self, task_prompt: str, search_query: str = None) -> str:
179
- """Execute a task with optional web search"""
180
- try:
181
- self.set_status("working", f"Processing {self.role.lower()} task...")
182
-
183
- # Perform search if needed
184
- search_results = ""
185
- if search_query:
186
- self.set_status("working", "Searching for relevant information...")
187
- search_results = self.search(search_query)
188
-
189
- # Create enhanced prompt with search results
190
- enhanced_prompt = f"""
191
- Role: {self.role}
192
- Task: {task_prompt}
193
-
194
- {f"Search Results: {search_results}" if search_results else ""}
195
-
196
- Please provide a detailed response based on your role as {self.role}.
197
- """
198
-
199
- # Get response from LLM
200
- self.set_status("working", "Generating response...")
201
- result = self.llm(enhanced_prompt)
202
-
203
- self.last_result = result
204
- self.set_status("complete", "Task completed successfully")
205
- return result
206
-
207
- except Exception as e:
208
- self.set_status("error", f"Error: {str(e)}")
209
- return f"Error in {self.name}: {str(e)}"
210
-
211
-
212
- class TravelSearchTool:
213
- """Enhanced search tool specifically designed for travel planning"""
214
  def __init__(self):
 
215
  self.ddgs = DDGS()
216
 
217
- def __call__(self, query: str, search_type: str = "general", max_results: int = 5) -> str:
218
  try:
219
- # Customize search based on type
220
- if search_type == "flights":
221
- query = f"best flight deals {query} 2024 booking sites"
222
- elif search_type == "hotels":
223
- query = f"best hotels accommodation {query} reviews booking"
224
- elif search_type == "activities":
225
- query = f"top attractions activities things to do {query} tourist guide"
226
- elif search_type == "restaurants":
227
- query = f"best restaurants food dining {query} local cuisine"
228
- elif search_type == "weather":
229
- query = f"weather forecast climate {query} best time to visit"
230
-
231
- # Perform search
232
- search_results = list(self.ddgs.text(
233
  query,
234
  max_results=max_results,
235
- region='wt-wt',
236
  safesearch='on'
237
  ))
238
 
239
  if not search_results:
240
- return "No relevant information found. Please try a different search."
241
 
242
- # Format results
243
  formatted_results = []
244
  for idx, result in enumerate(search_results, 1):
245
- title = result.get('title', 'No title')
246
- snippet = result.get('body', 'No description')
247
- url = result.get('href', 'No URL')
 
 
 
248
 
 
249
  formatted_results.append(
250
- f"{idx}. {title}\n"
251
- f" Description: {snippet}\n"
252
- f" Source: {url}\n"
 
 
253
  )
254
 
255
  return "\n".join(formatted_results)
256
-
257
  except Exception as e:
258
- return f"Search error: {str(e)}"
259
-
 
 
260
 
261
  class GroqLLM:
262
- """Enhanced LLM client with travel-specific optimizations"""
263
- def __init__(self, model_name="llama-3.1-70b-versatile"):
 
 
 
264
  self.client = Groq(api_key=os.environ.get("GROQ_API_KEY"))
265
  self.model_name = model_name
266
 
267
  def __call__(self, prompt: Union[str, dict, List[Dict]]) -> str:
268
  try:
 
 
 
 
269
  completion = self.client.chat.completions.create(
270
  model=self.model_name,
271
  messages=[{
272
  "role": "user",
273
- "content": str(prompt)
274
  }],
275
  temperature=0.7,
276
- max_tokens=2048,
277
  stream=False
278
  )
279
 
280
- return completion.choices[0].message.content if completion.choices else "No response generated"
281
-
282
  except Exception as e:
283
- return f"LLM Error: {str(e)}"
284
-
 
285
 
286
- class TravelPlannerSystem:
287
- """Main orchestrator for the travel planning system"""
288
- def __init__(self):
289
- self.llm = GroqLLM()
290
- self.search_tool = TravelSearchTool()
291
- self.agents = self._initialize_agents()
292
-
293
- def _initialize_agents(self) -> Dict[str, SmolAgent]:
294
- """Initialize specialized travel agents"""
295
- return {
296
- "destination_expert": SmolAgent(
297
- "Destination Expert",
298
- "Travel destination researcher and recommender",
299
- self.llm, self.search_tool
300
- ),
301
- "itinerary_planner": SmolAgent(
302
- "Itinerary Planner",
303
- "Trip itinerary creator and scheduler",
304
- self.llm, self.search_tool
305
- ),
306
- "accommodation_specialist": SmolAgent(
307
- "Accommodation Specialist",
308
- "Hotel and lodging finder and recommender",
309
- self.llm, self.search_tool
310
- ),
311
- "activity_curator": SmolAgent(
312
- "Activity Curator",
313
- "Tourist attraction and activity recommender",
314
- self.llm, self.search_tool
315
- ),
316
- "budget_advisor": SmolAgent(
317
- "Budget Advisor",
318
- "Travel cost estimator and budget optimizer",
319
- self.llm, self.search_tool
320
- )
321
- }
322
 
323
- def create_travel_plan(self, destination: str, duration: str, budget: str,
324
- travel_style: str, interests: str) -> Dict[str, str]:
325
- """Create a comprehensive travel plan using multiple agents"""
326
-
327
- # Prepare base context for all agents
328
- base_context = f"""
329
- Destination: {destination}
330
- Duration: {duration}
331
- Budget: {budget}
332
- Travel Style: {travel_style}
333
- Interests: {interests}
334
- """
335
-
336
- results = {}
337
-
338
- # Agent 1: Destination Research
339
- dest_task = f"""
340
- Research {destination} as a travel destination. Include:
341
- - Best time to visit and current weather
342
- - Cultural highlights and local customs
343
- - Transportation options
344
- - Safety and travel requirements
345
- - Local currency and tipping customs
346
-
347
- Context: {base_context}
348
- """
349
- results["destination_info"] = self.agents["destination_expert"].execute_task(
350
- dest_task, f"{destination} travel guide weather best time visit"
351
- )
352
-
353
- # Agent 2: Accommodation Research
354
- accommodation_task = f"""
355
- Find accommodation options for {destination}. Consider:
356
- - Hotels, hostels, and alternative lodging based on budget: {budget}
357
- - Location preferences for {travel_style} travelers
358
- - Amenities and reviews
359
- - Booking tips and best deals
360
-
361
- Context: {base_context}
362
- """
363
- results["accommodation"] = self.agents["accommodation_specialist"].execute_task(
364
- accommodation_task, f"{destination} hotels accommodation {budget} {travel_style}"
365
- )
366
-
367
- # Agent 3: Activities and Attractions
368
- activity_task = f"""
369
- Curate activities and attractions for {destination} based on interests: {interests}
370
- Include:
371
- - Must-see attractions and hidden gems
372
- - Activities matching {travel_style} style
373
- - Local experiences and cultural activities
374
- - Restaurant and dining recommendations
375
-
376
- Context: {base_context}
377
- """
378
- results["activities"] = self.agents["activity_curator"].execute_task(
379
- activity_task, f"{destination} attractions activities {interests} restaurants"
380
- )
381
-
382
- # Agent 4: Itinerary Planning
383
- itinerary_task = f"""
384
- Create a detailed {duration} itinerary for {destination}. Include:
385
- - Day-by-day schedule optimized for {travel_style} travelers
386
- - Time management and logistics
387
- - Mix of planned activities and free time
388
- - Transportation between locations
389
-
390
- Use information about accommodations and activities to create a cohesive plan.
391
- Context: {base_context}
392
- """
393
- results["itinerary"] = self.agents["itinerary_planner"].execute_task(
394
- itinerary_task, f"{destination} {duration} itinerary travel schedule"
395
- )
396
-
397
- # Agent 5: Budget Planning
398
- budget_task = f"""
399
- Create a detailed budget breakdown for the {destination} trip:
400
- - Accommodation costs based on research
401
- - Transportation (flights, local transport)
402
- - Food and dining expenses
403
- - Activities and attraction fees
404
- - Miscellaneous expenses and emergency fund
405
- - Money-saving tips and deals
406
-
407
- Context: {base_context}
408
- """
409
- results["budget_breakdown"] = self.agents["budget_advisor"].execute_task(
410
- budget_task, f"{destination} travel costs budget {duration} {budget}"
411
- )
412
-
413
- return results
414
-
415
-
416
- def display_agent_status(agents: Dict[str, SmolAgent]):
417
- """Display the status of all agents"""
418
- st.markdown("### πŸ€– Agent Status Dashboard")
419
 
420
- cols = st.columns(len(agents))
421
- for idx, (name, agent) in enumerate(agents.items()):
422
- with cols[idx]:
423
- status_class = f"status-{agent.status}" if hasattr(agent, 'status') else "status-idle"
424
- status_message = getattr(agent, 'status_message', 'Ready')
425
-
426
- if agent.status == "working":
427
- icon = '<div class="spinner"></div>'
428
- elif agent.status == "complete":
429
- icon = 'βœ…'
430
- elif agent.status == "error":
431
- icon = '❌'
432
- else:
433
- icon = '⏸️'
434
-
435
- st.markdown(f"""
436
- <div class="agent-status {status_class}">
437
- {icon} <strong>{agent.name}</strong><br>
438
- <small>{status_message}</small>
439
- </div>
440
- """, unsafe_allow_html=True)
441
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
442
 
443
- # Main Application Interface
444
  st.markdown("""
445
- <div class="card" style="text-align: center; padding: 3rem;">
446
- <h1 class="logo-text">✈️ SmolAgent Travel Planner</h1>
447
- <p style="font-size: 1.2rem; opacity: 0.9; margin-top: 1rem;">
448
- AI-powered travel planning with specialized agents working together to create your perfect trip
449
- </p>
 
 
 
 
 
 
 
 
 
 
 
450
  </div>
451
  """, unsafe_allow_html=True)
452
 
453
- # Initialize the system
 
 
 
454
  try:
455
- if 'travel_system' not in st.session_state:
456
- st.session_state.travel_system = TravelPlannerSystem()
 
 
 
 
457
 
458
- travel_system = st.session_state.travel_system
 
 
 
 
459
 
460
- # Input Section
461
- st.markdown('<div class="card">', unsafe_allow_html=True)
462
- st.markdown("## 🌍 Plan Your Perfect Trip")
463
 
464
- col1, col2 = st.columns(2)
465
 
466
  with col1:
467
- destination = st.text_input(
468
- "🎯 Where do you want to go?",
469
- placeholder="e.g., Tokyo, Japan or Paris, France",
470
- help="Enter your dream destination"
471
- )
472
-
473
- duration = st.selectbox(
474
- "πŸ“… Trip Duration",
475
- ["3 days", "1 week", "2 weeks", "1 month", "Custom"],
476
- help="How long will you be traveling?"
477
  )
478
-
479
- if duration == "Custom":
480
- duration = st.text_input("Specify custom duration:", placeholder="e.g., 10 days")
481
 
482
  with col2:
483
- budget = st.selectbox(
484
- "πŸ’° Budget Range",
485
- ["Budget ($50-100/day)", "Mid-range ($100-250/day)", "Luxury ($250+/day)", "No specific budget"],
486
- help="What's your daily budget range?"
 
 
 
 
487
  )
488
-
489
- travel_style = st.selectbox(
490
- "πŸŽ’ Travel Style",
491
- ["Adventure & Outdoor", "Cultural & Historical", "Relaxation & Spa", "Nightlife & Entertainment",
492
- "Food & Culinary", "Family-Friendly", "Business Travel", "Solo Traveler", "Romantic Getaway"],
493
- help="What kind of traveler are you?"
 
 
 
 
 
 
494
  )
 
 
495
 
496
- interests = st.text_area(
497
- "🎨 Interests & Preferences",
498
- placeholder="Tell us about your interests: art, food, history, adventure, shopping, nightlife, nature, etc.",
499
- help="The more specific you are, the better recommendations we can provide!"
500
- )
501
 
502
- st.markdown('</div>', unsafe_allow_html=True)
503
 
504
- # Planning Button
505
- col1, col2, col3 = st.columns([1, 2, 1])
506
  with col2:
507
- plan_trip = st.button("πŸš€ CREATE MY TRAVEL PLAN", type="primary")
508
 
509
- # Results Section
510
- if plan_trip and destination:
511
- with st.spinner(""):
512
- # Display agent status dashboard
513
- display_agent_status(travel_system.agents)
514
-
515
- # Create the travel plan
516
- st.markdown("### πŸ”„ Agents Working Together...")
517
-
518
- # Progress tracking
519
- progress_bar = st.progress(0)
520
- status_placeholder = st.empty()
521
-
522
- results = {}
523
- total_agents = len(travel_system.agents)
524
-
525
- # Update progress as each agent completes
526
- for i, (agent_name, agent) in enumerate(travel_system.agents.items()):
527
- progress = (i + 1) / total_agents
528
- progress_bar.progress(progress)
529
- status_placeholder.text(f"Agent {i+1}/{total_agents}: {agent.name} working...")
530
- time.sleep(0.5) # Brief delay for visual effect
531
-
532
- # Execute the travel planning
533
- results = travel_system.create_travel_plan(
534
- destination, duration, budget, travel_style, interests
535
- )
536
-
537
- progress_bar.progress(1.0)
538
- status_placeholder.text("βœ… All agents completed successfully!")
539
-
540
- # Display Results in Tabs
541
- st.markdown(f'<div class="card"><h2>🎯 Your {destination} Travel Plan</h2></div>', unsafe_allow_html=True)
542
-
543
- tab1, tab2, tab3, tab4, tab5 = st.tabs([
544
- "🌍 Destination Info", "🏨 Accommodation", "🎭 Activities",
545
- "πŸ“‹ Itinerary", "πŸ’° Budget"
546
- ])
547
-
548
- with tab1:
549
- st.markdown('<div class="travel-result">', unsafe_allow_html=True)
550
- st.markdown(results.get("destination_info", "Information not available"))
551
- st.markdown('</div>', unsafe_allow_html=True)
552
-
553
- with tab2:
554
- st.markdown('<div class="travel-result">', unsafe_allow_html=True)
555
- st.markdown(results.get("accommodation", "Information not available"))
556
- st.markdown('</div>', unsafe_allow_html=True)
557
-
558
- with tab3:
559
- st.markdown('<div class="travel-result">', unsafe_allow_html=True)
560
- st.markdown(results.get("activities", "Information not available"))
561
- st.markdown('</div>', unsafe_allow_html=True)
562
-
563
- with tab4:
564
- st.markdown('<div class="travel-result">', unsafe_allow_html=True)
565
- st.markdown(results.get("itinerary", "Information not available"))
566
- st.markdown('</div>', unsafe_allow_html=True)
567
-
568
- with tab5:
569
- st.markdown('<div class="travel-result">', unsafe_allow_html=True)
570
- st.markdown(results.get("budget_breakdown", "Information not available"))
571
- st.markdown('</div>', unsafe_allow_html=True)
572
-
573
- # Final agent status
574
- st.markdown("### βœ… Mission Complete!")
575
- display_agent_status(travel_system.agents)
576
-
577
- elif plan_trip:
578
- st.warning("🚨 Please enter a destination to start planning your trip!")
579
 
580
- # Tips and Examples Section
581
- if not plan_trip or not destination:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
582
  st.markdown('<div class="card">', unsafe_allow_html=True)
583
- st.markdown("## πŸ’‘ How It Works")
584
 
585
- col1, col2, col3 = st.columns(3)
 
 
 
 
 
 
586
 
587
- with col1:
588
- st.markdown("""
589
- ### πŸ” Research Phase
590
- Our **Destination Expert** searches for:
591
- - Current weather & best times to visit
592
- - Local customs & cultural insights
593
- - Safety requirements & travel tips
594
- - Transportation options
595
- """)
596
 
597
- with col2:
598
- st.markdown("""
599
- ### 🏨 Planning Phase
600
- Our specialists find:
601
- - **Accommodation** matching your budget
602
- - **Activities** based on your interests
603
- - **Restaurants** & local dining spots
604
- - Hidden gems & local experiences
605
- """)
 
 
 
606
 
607
- with col3:
608
- st.markdown("""
609
- ### πŸ“‹ Organization Phase
610
- Our planners create:
611
- - **Day-by-day itinerary** optimized for you
612
- - **Budget breakdown** with cost estimates
613
- - **Logistics** and travel coordination
614
- - **Money-saving tips** and deals
615
- """)
616
 
617
  st.markdown('</div>', unsafe_allow_html=True)
618
 
619
- # Example destinations
620
  st.markdown('<div class="card">', unsafe_allow_html=True)
621
- st.markdown("## 🌟 Popular Destinations")
622
 
623
- example_cols = st.columns(4)
624
-
625
- examples = [
626
- ("πŸ—Ύ Tokyo, Japan", "tokyo japan"),
627
- ("πŸ₯– Paris, France", "paris france"),
628
- ("πŸ›οΈ Rome, Italy", "rome italy"),
629
- ("🏝️ Bali, Indonesia", "bali indonesia")
630
- ]
631
-
632
- for i, (display_name, destination_value) in enumerate(examples):
633
- with example_cols[i]:
634
- if st.button(display_name, key=f"example_{i}"):
635
- st.session_state.destination_input = destination_value
636
- st.rerun()
637
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
638
  st.markdown('</div>', unsafe_allow_html=True)
639
 
 
 
 
 
 
 
 
 
 
 
640
  except Exception as e:
 
641
  st.error(f"""
642
  <div style="display: flex; align-items: center; gap: 12px;">
643
- <span>❌ Application Error: {str(e)}</span>
 
 
 
 
 
644
  </div>
645
  """, unsafe_allow_html=True)
646
 
647
  st.markdown("""
648
- <div class="card">
649
- <h3>πŸ”§ Troubleshooting</h3>
650
  <ul>
651
- <li>Ensure your GROQ_API_KEY is set in environment variables</li>
652
- <li>Check your internet connection for web searches</li>
653
  <li>Try refreshing the page or restarting the application</li>
654
  </ul>
655
  </div>
656
  """, unsafe_allow_html=True)
657
 
658
- # Sidebar controls
 
 
 
 
 
 
659
  with st.sidebar:
660
- st.markdown("### πŸ› οΈ System Controls")
661
-
662
- if st.button("πŸ”„ Reset Application"):
663
- for key in list(st.session_state.keys()):
664
- if key.startswith('travel_'):
665
- del st.session_state[key]
666
- st.rerun()
667
-
668
- st.markdown("### πŸ“Š Agent Performance")
669
- if 'travel_system' in st.session_state:
670
- for name, agent in st.session_state.travel_system.agents.items():
671
- status = getattr(agent, 'status', 'idle')
672
- st.metric(agent.name, status.title())
673
-
674
- st.markdown("### πŸ’¬ Feedback")
675
- feedback = st.text_area("Share your experience:", placeholder="How was your travel plan?")
676
  if st.button("Submit Feedback"):
677
  st.success("Thank you for your feedback!")
678
-
679
- # Footer
680
- st.markdown("""
681
- <div style="text-align: center; padding: 2rem; opacity: 0.8;">
682
- <p>SmolAgent Travel Planner v2.0 | Powered by AI Collaboration</p>
683
- </div>
684
- """, unsafe_allow_html=True)
 
1
+
2
  import streamlit as st
3
  import pandas as pd
4
+ from typing import Union, List, Dict
5
  from groq import Groq
6
  import os
7
  from duckduckgo_search import DDGS
 
 
 
8
 
9
  # Set page configuration with fullscreen layout and custom theme
10
  st.set_page_config(
11
+ page_title="Z-Alpha News Analysis",
12
  layout="wide",
13
  initial_sidebar_state="collapsed",
14
+ page_icon="πŸ”"
15
  )
16
 
17
+ # Execute JavaScript to make the app fullscreen on load
18
+ st.markdown("""
19
+ <script>
20
+ document.addEventListener('DOMContentLoaded', (event) => {
21
+ // Request fullscreen on page load
22
+ document.documentElement.requestFullscreen().catch(e => {
23
+ console.log("Fullscreen request failed: ", e);
24
+ });
25
+ });
26
+ </script>
27
+ """, unsafe_allow_html=True)
28
+
29
+ # Enhanced CSS for a more beautiful Google DeepMind-inspired styling with green background
30
  st.markdown("""
31
  <style>
32
  /* Base styles */
33
  html, body, .stApp, .main {
34
+ background-color: #000000 !important;
35
  color: #ffffff !important;
36
  }
37
+
38
  /* Typography */
39
  h1, h2, h3, h4, h5, h6 {
40
+ color: #00ff00 !important;
41
+ font-family: 'Courier New', monospace !important;
 
42
  }
43
+
44
+ p, div, span {
45
+ color: #cccccc !important;
46
+ }
47
+
48
  /* Cards and containers */
49
  .card {
50
+ background-color: #0a0a0a !important;
51
+ border: 1px solid #00ff00 !important;
52
+ border-radius: 4px !important;
53
+ box-shadow: 0 0 15px rgba(0, 255, 0, 0.2) !important;
 
 
 
54
  }
55
+
56
+ /* Analysis results */
57
+ .analysis-result {
58
+ background-color: #121212 !important;
59
+ border-left: 4px solid #00ff00 !important;
60
+ color: #ffffff !important;
61
+ font-family: 'Courier New', monospace !important;
62
  padding: 1.5rem !important;
 
 
63
  }
64
+
65
  /* Input fields */
66
+ .stTextInput input {
67
+ background-color: #121212 !important;
68
+ color: #00ff00 !important;
69
+ border: 1px solid #00ff00 !important;
70
+ border-radius: 0 !important;
71
  }
72
+
 
 
 
 
73
  /* Buttons */
74
  .stButton > button {
75
+ background: linear-gradient(45deg, #00ff00, #003300) !important;
76
+ color: #000000 !important;
77
  border: none !important;
78
+ border-radius: 0 !important;
79
+ font-family: 'Courier New', monospace !important;
80
  font-weight: bold !important;
 
81
  transition: all 0.3s ease !important;
 
82
  }
83
+
84
  .stButton > button:hover {
85
+ box-shadow: 0 0 20px #00ff00 !important;
86
+ transform: scale(1.05) !important;
 
 
 
 
 
 
 
 
 
 
 
 
87
  }
88
+
89
+ /* Source items */
90
+ .source-item {
91
+ border-bottom: 1px solid #333333 !important;
92
+ padding: 1rem 0 !important;
 
 
93
  }
94
+
95
+ .source-title {
96
+ color: #00ff00 !important;
97
+ font-family: 'Courier New', monospace !important;
98
  }
99
+
100
+ /* Links */
101
+ a {
102
+ color: #00ff00 !important;
103
+ text-decoration: underline !important;
 
 
 
 
104
  }
105
+
106
+ a:hover {
107
+ color: #99ff99 !important;
 
108
  }
109
+
110
+ /* Status indicators */
111
+ .status-badge {
112
+ background-color: #002200 !important;
113
+ color: #00ff00 !important;
114
+ border: 1px solid #00ff00 !important;
 
 
115
  }
116
+
117
  /* Tabs */
118
  .stTabs [aria-selected="true"] {
119
+ background-color: #121212 !important;
120
+ color: #00ff00 !important;
121
+ border-bottom: 2px solid #00ff00 !important;
122
  }
123
+
124
  /* Logo styling */
125
  .logo-text {
126
+ background: linear-gradient(90deg, #00ff00, #99ff99) !important;
 
127
  -webkit-background-clip: text !important;
128
  -webkit-text-fill-color: transparent !important;
129
+ text-shadow: 0 0 10px rgba(0, 255, 0, 0.5) !important;
130
  }
131
+
132
+ /* Icons */
133
+ .float-icon svg {
134
+ filter: drop-shadow(0 0 5px #00ff00);
135
+ }
136
+
137
+ /* Scrollbar styling */
138
+ ::-webkit-scrollbar {
139
+ width: 8px;
140
+ }
141
+
142
+ ::-webkit-scrollbar-track {
143
+ background: #000000;
144
+ }
145
+
146
+ ::-webkit-scrollbar-thumb {
147
+ background: #00ff00;
148
+ border-radius: 4px;
149
+ }
150
+
151
+ /* Dropdown menus */
152
+ .stSelectbox div[data-baseweb="select"] div {
153
+ background-color: #121212 !important;
154
+ color: #00ff00 !important;
155
+ border: 1px solid #00ff00 !important;
156
+ border-radius: 0 !important;
157
+ }
158
+
159
+ /* Sliders */
160
+ .stSlider .thumb {
161
+ background-color: #00ff00 !important;
162
+ }
163
+
164
+ .stSlider .track {
165
+ background-color: #003300 !important;
166
  }
167
  </style>
168
  """, unsafe_allow_html=True)
169
 
170
 
171
+ class DuckDuckGoSearch:
172
  """
173
+ Custom DuckDuckGo search implementation with robust error handling and result processing.
174
+ Uses the duckduckgo_search library to fetch and format news results.
175
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
  def __init__(self):
177
+ # Initialize the DuckDuckGo search session
178
  self.ddgs = DDGS()
179
 
180
+ def __call__(self, query: str, max_results: int = 5) -> str:
181
  try:
182
+ # Perform the search and get results
183
+ # The news method is more appropriate for recent news analysis
184
+ search_results = list(self.ddgs.news(
 
 
 
 
 
 
 
 
 
 
 
185
  query,
186
  max_results=max_results,
187
+ region='wt-wt', # Worldwide results
188
  safesearch='on'
189
  ))
190
 
191
  if not search_results:
192
+ return "No results found. Try modifying your search query."
193
 
194
+ # Format the results into a readable string
195
  formatted_results = []
196
  for idx, result in enumerate(search_results, 1):
197
+ # Extract available fields with fallbacks for missing data
198
+ title = result.get('title', 'No title available')
199
+ snippet = result.get('body', result.get('snippet', 'No description available'))
200
+ source = result.get('source', 'Unknown source')
201
+ url = result.get('url', result.get('link', 'No link available'))
202
+ date = result.get('date', 'Date not available')
203
 
204
+ # Format each result with available information
205
  formatted_results.append(
206
+ f"{idx}. Title: {title}\n"
207
+ f" Date: {date}\n"
208
+ f" Source: {source}\n"
209
+ f" Summary: {snippet}\n"
210
+ f" URL: {url}\n"
211
  )
212
 
213
  return "\n".join(formatted_results)
214
+
215
  except Exception as e:
216
+ # Provide detailed error information for debugging
217
+ error_msg = f"Search error: {str(e)}\nTry again with a different search term or check your internet connection."
218
+ print(f"DuckDuckGo search error: {str(e)}") # For logging
219
+ return error_msg
220
 
221
  class GroqLLM:
222
+ """
223
+ LLM interface using Groq's LLama model.
224
+ Handles API communication and response processing.
225
+ """
226
+ def __init__(self, model_name="llama-3.1-8B-Instant"):
227
  self.client = Groq(api_key=os.environ.get("GROQ_API_KEY"))
228
  self.model_name = model_name
229
 
230
  def __call__(self, prompt: Union[str, dict, List[Dict]]) -> str:
231
  try:
232
+ # Convert prompt to string if it's a complex structure
233
+ prompt_str = str(prompt) if isinstance(prompt, (dict, list)) else prompt
234
+
235
+ # Make API call to Groq
236
  completion = self.client.chat.completions.create(
237
  model=self.model_name,
238
  messages=[{
239
  "role": "user",
240
+ "content": prompt_str
241
  }],
242
  temperature=0.7,
243
+ max_tokens=1024,
244
  stream=False
245
  )
246
 
247
+ return completion.choices[0].message.content if completion.choices else "Error: No response generated"
 
248
  except Exception as e:
249
+ error_msg = f"Error generating response: {str(e)}"
250
+ print(error_msg) # For logging
251
+ return error_msg
252
 
253
+ def create_analysis_prompt(topic: str, search_results: str) -> str:
254
+ """
255
+ Creates a detailed prompt for news analysis, structuring the request
256
+ to get comprehensive and well-organized results from the LLM.
257
+ """
258
+ return f"""Analyze the following news information about {topic}.
259
+ Search Results: {search_results}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
260
 
261
+ Please provide a comprehensive analysis including:
262
+ 1. Key Points Summary:
263
+ - Main events and developments
264
+ - Critical updates and changes
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
265
 
266
+ 2. Stakeholder Analysis:
267
+ - Primary parties involved
268
+ - Their roles and positions
269
+
270
+ 3. Impact Assessment:
271
+ - Immediate implications
272
+ - Potential long-term effects
273
+ - Broader context and significance
274
+
275
+ 4. Multiple Perspectives:
276
+ - Different viewpoints on the issue
277
+ - Areas of agreement and contention
278
+
279
+ 5. Fact Check & Reliability:
280
+ - Verification of major claims
281
+ - Consistency across sources
282
+ - Source credibility assessment
283
+
284
+ Please format the analysis in a clear, journalistic style with section headers."""
 
 
285
 
286
+ def log_agent_activity(prompt: str, result: str, agent_name: str):
287
+ """
288
+ Creates an expandable log of agent activities in the Streamlit interface
289
+ for transparency and debugging purposes.
290
+ """
291
+ with st.expander("πŸ” View Agent Activity Log"):
292
+ st.markdown("<h4>Agent Activity Log</h4>", unsafe_allow_html=True)
293
+ st.markdown(f"<p><strong>Agent:</strong> {agent_name}</p>", unsafe_allow_html=True)
294
+
295
+ st.markdown("<div style='background-color: #f8f9fa; padding: 1rem; border-radius: 8px; margin-bottom: 1rem;'>", unsafe_allow_html=True)
296
+ st.markdown("<p><strong>Input Prompt:</strong></p>", unsafe_allow_html=True)
297
+ st.code(prompt, language="text")
298
+ st.markdown("</div>", unsafe_allow_html=True)
299
+
300
+ st.markdown("<div style='background-color: #f8f9fa; padding: 1rem; border-radius: 8px;'>", unsafe_allow_html=True)
301
+ st.markdown("<p><strong>Analysis Output:</strong></p>", unsafe_allow_html=True)
302
+ st.code(result, language="text")
303
+ st.markdown("</div>", unsafe_allow_html=True)
304
 
305
+ # Animated header with branded logo and description
306
  st.markdown("""
307
+ <div class="card" style="padding-bottom: 1.5rem;">
308
+ <div style="display: flex; align-items: center; gap: 1.5rem;">
309
+ <div class="float-icon" style="text-align: center;">
310
+ <svg width="70" height="70" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
311
+ <path d="M19.5 9.5l-7.5-7.5-7.5 7.5m15 0v8c0 1.1-.9 2-2 2h-11c-1.1 0-2-.9-2-2v-8" stroke="#1a73e8" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
312
+ <circle cx="12" cy="12" r="3" stroke="#1a73e8" stroke-width="2"/>
313
+ <path d="M12 15v3" stroke="#1a73e8" stroke-width="2" stroke-linecap="round"/>
314
+ </svg>
315
+ </div>
316
+ <div style="flex: 1;">
317
+ <span class="logo-text">Z-Agent News Analysis</span>
318
+ <p style="color: #5f6368; font-size: 1.1rem; margin-top: 0.5rem;">
319
+ Intelligent news analysis powered by AI. Get comprehensive insights and multiple perspectives on any topic.
320
+ </p>
321
+ </div>
322
+ </div>
323
  </div>
324
  """, unsafe_allow_html=True)
325
 
326
+ # Main content area
327
+ st.markdown('<div class="card">', unsafe_allow_html=True)
328
+
329
+ # Initialize the components
330
  try:
331
+ # Initialize LLM and search tool
332
+ llm = GroqLLM()
333
+ search_tool = DuckDuckGoSearch()
334
+
335
+ # Input section with enhanced design
336
+ st.markdown("<h2 style='margin-top: 0;'>News Topic Analysis</h2>", unsafe_allow_html=True)
337
 
338
+ news_topic = st.text_input(
339
+ "What news topic would you like to analyze?",
340
+ placeholder="E.g., Recent developments in renewable energy, Tech industry layoffs, Global climate agreements...",
341
+ key="news_topic_input"
342
+ )
343
 
344
+ # Analysis options in a cleaner layout
345
+ st.markdown("<div style='margin-top: 1.5rem;'></div>", unsafe_allow_html=True)
346
+ st.markdown("<h3 style='margin-bottom: 1rem;'>Analysis Options</h3>", unsafe_allow_html=True)
347
 
348
+ col1, col2, col3 = st.columns([2, 2, 2])
349
 
350
  with col1:
351
+ st.markdown("<div style='background-color: #f8f9fa; padding: 1rem; border-radius: 12px; height: 100%;'>", unsafe_allow_html=True)
352
+ st.markdown("<p style='font-weight: 600; color: #202124; margin-bottom: 0.5rem;'>Search Depth</p>", unsafe_allow_html=True)
353
+ search_depth = st.slider(
354
+ "##",
355
+ min_value=3,
356
+ max_value=10,
357
+ value=5,
358
+ help="Number of news sources to analyze",
359
+ key="search_depth_slider",
360
+ label_visibility="collapsed"
361
  )
362
+ st.markdown("<p style='color: #5f6368; font-size: 0.9rem;'>Number of sources: " + str(search_depth) + "</p>", unsafe_allow_html=True)
363
+ st.markdown("</div>", unsafe_allow_html=True)
 
364
 
365
  with col2:
366
+ st.markdown("<div style='background-color: #f8f9fa; padding: 1rem; border-radius: 12px; height: 100%;'>", unsafe_allow_html=True)
367
+ st.markdown("<p style='font-weight: 600; color: #202124; margin-bottom: 0.5rem;'>Analysis Type</p>", unsafe_allow_html=True)
368
+ analysis_type = st.selectbox(
369
+ "##",
370
+ ["Comprehensive", "Quick Summary", "Technical", "Simplified"],
371
+ help="Choose the style and depth of analysis",
372
+ key="analysis_type_select",
373
+ label_visibility="collapsed"
374
  )
375
+ st.markdown("<p style='color: #5f6368; font-size: 0.9rem;'>Selected: " + analysis_type + "</p>", unsafe_allow_html=True)
376
+ st.markdown("</div>", unsafe_allow_html=True)
377
+
378
+ with col3:
379
+ st.markdown("<div style='background-color: #f8f9fa; padding: 1rem; border-radius: 12px; height: 100%;'>", unsafe_allow_html=True)
380
+ st.markdown("<p style='font-weight: 600; color: #202124; margin-bottom: 0.5rem;'>Time Period</p>", unsafe_allow_html=True)
381
+ time_period = st.selectbox(
382
+ "##",
383
+ ["Last 7 days", "Last 30 days", "Last 24 hours"],
384
+ help="How recent should the news be",
385
+ key="time_period_select",
386
+ label_visibility="collapsed"
387
  )
388
+ st.markdown("<p style='color: #5f6368; font-size: 0.9rem;'>Selected: " + time_period + "</p>", unsafe_allow_html=True)
389
+ st.markdown("</div>", unsafe_allow_html=True)
390
 
391
+ # Generate analysis button
392
+ st.markdown("<div style='margin-top: 2rem;'></div>", unsafe_allow_html=True)
 
 
 
393
 
394
+ col1, col2, col3 = st.columns([2, 2, 2])
395
 
 
 
396
  with col2:
397
+ analyze_button = st.button("ANALYZE NEWS")
398
 
399
+ st.markdown('</div>', unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
400
 
401
+ # Results section
402
+ if analyze_button and news_topic:
403
+ with st.spinner(""):
404
+ try:
405
+ # Progress indicators with enhanced styling
406
+ progress_container = st.container()
407
+ progress_container.markdown('<div class="info-box">', unsafe_allow_html=True)
408
+ progress_placeholder = progress_container.empty()
409
+ progress_placeholder.markdown("""
410
+ <div class="pulsing">
411
+ <div class="status-badge status-searching">
412
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
413
+ <circle cx="11" cy="11" r="8"></circle>
414
+ <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
415
+ </svg>
416
+ <span>Searching for recent news sources...</span>
417
+ </div>
418
+ </div>
419
+ """, unsafe_allow_html=True)
420
+
421
+ # Determine time frame from selection
422
+ time_map = {
423
+ "Last 24 hours": "24h",
424
+ "Last 7 days": "7d",
425
+ "Last 30 days": "30d"
426
+ }
427
+ time_frame = time_map.get(time_period, "7d")
428
+
429
+ # Perform search
430
+ search_results = search_tool(
431
+ f"Latest news about {news_topic} {time_frame}",
432
+ max_results=search_depth
433
+ )
434
+
435
+ if not search_results.startswith(("Search error", "No results")):
436
+ # Update progress
437
+ progress_placeholder.markdown("""
438
+ <div class="pulsing">
439
+ <div class="status-badge status-analyzing">
440
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
441
+ <path d="M12 3v3m0 12v3m-9-9H3m3-6l2 2M3 12h3m12 0h3m-3-6l-2 2m9 3h-3m-6 6l-2 2m12 0l-2-2"></path>
442
+ </svg>
443
+ <span>Analyzing search results and generating insights...</span>
444
+ </div>
445
+ </div>
446
+ """, unsafe_allow_html=True)
447
+
448
+ # Create analysis prompt
449
+ analysis_prompt = create_analysis_prompt(news_topic, search_results)
450
+
451
+ # Get analysis from LLM
452
+ analysis_result = llm(analysis_prompt)
453
+
454
+ # Clear progress messages
455
+ progress_container.empty()
456
+
457
+ # Display results in tabs for better organization
458
+ st.markdown('<div class="card">', unsafe_allow_html=True)
459
+ st.markdown(f'<h2>Analysis: {news_topic}</h2>', unsafe_allow_html=True)
460
+
461
+ tab1, tab2 = st.tabs(["πŸ“Š Analysis", "πŸ“° Sources"])
462
+
463
+ with tab1:
464
+ st.markdown('<div class="analysis-result">', unsafe_allow_html=True)
465
+ st.markdown(analysis_result)
466
+ st.markdown('</div>', unsafe_allow_html=True)
467
+
468
+ with tab2:
469
+ st.markdown("<h3>News Sources Used in Analysis</h3>", unsafe_allow_html=True)
470
+ # Parse and display sources in a more structured way
471
+ sources = search_results.split("\n\n")
472
+ for source in sources:
473
+ lines = source.strip().split("\n")
474
+ if len(lines) >= 5: # Ensure we have enough lines
475
+ title_line = lines[0].replace("1. Title: ", "").replace("2. Title: ", "").replace("3. Title: ", "").replace("4. Title: ", "").replace("5. Title: ", "")
476
+ date_line = lines[1].replace(" Date: ", "")
477
+ source_line = lines[2].replace(" Source: ", "")
478
+ url_line = lines[4].replace(" URL: ", "")
479
+
480
+ st.markdown(f"""
481
+ <div class="source-item">
482
+ <p class="source-title">{title_line}</p>
483
+ <div class="source-meta">
484
+ <span>{source_line}</span>
485
+ <span class="source-meta-dot"></span>
486
+ <span>{date_line}</span>
487
+ </div>
488
+ <a href="{url_line}" class="read-link" target="_blank">
489
+ Read original article
490
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
491
+ <path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6"></path>
492
+ <path d="M15 3h6v6"></path>
493
+ <path d="M10 14L21 3"></path>
494
+ </svg>
495
+ </a>
496
+ </div>
497
+ """, unsafe_allow_html=True)
498
+
499
+ st.markdown('</div>', unsafe_allow_html=True)
500
+
501
+ # Log the activity
502
+ log_agent_activity(
503
+ analysis_prompt,
504
+ analysis_result,
505
+ "News Analysis Agent"
506
+ )
507
+ else:
508
+ progress_container.empty()
509
+ st.error(f"""
510
+ <div style="display: flex; align-items: center; gap: 12px;">
511
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#FF5252" stroke-width="2">
512
+ <circle cx="12" cy="12" r="10"></circle>
513
+ <line x1="12" y1="8" x2="12" y2="12"></line>
514
+ <line x1="12" y1="16" x2="12.01" y2="16"></line>
515
+ </svg>
516
+ <span>{search_results}</span>
517
+ </div>
518
+ """, unsafe_allow_html=True)
519
+
520
+ except Exception as e:
521
+ st.error(f"""
522
+ <div style="display: flex; align-items: center; gap: 12px;">
523
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#FF5252" stroke-width="2">
524
+ <circle cx="12" cy="12" r="10"></circle>
525
+ <line x1="12" y1="8" x2="12" y2="12"></line>
526
+ <line x1="12" y1="16" x2="12.01" y2="16"></line>
527
+ </svg>
528
+ <span>An error occurred during analysis: {str(e)}</span>
529
+ </div>
530
+ """, unsafe_allow_html=True)
531
+ elif analyze_button:
532
+ # This handles the case when analyze button is clicked but no topic is entered
533
+ st.warning("""
534
+ <div style="display: flex; align-items: center; gap: 12px;">
535
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#FFA726" stroke-width="2">
536
+ <path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"></path>
537
+ <line x1="12" y1="9" x2="12" y2="13"></line>
538
+ <line x1="12" y1="17" x2="12.01" y2="17"></line>
539
+ </svg>
540
+ <span>Please enter a news topic to analyze.</span>
541
+ </div>
542
+ """, unsafe_allow_html=True)
543
+
544
+ # Tips and usage guidance section - displayed when no analysis is in progress
545
+ if not analyze_button or not news_topic:
546
  st.markdown('<div class="card">', unsafe_allow_html=True)
547
+ st.markdown("<h2 style='margin-top: 0;'>Tips for Better Results</h2>", unsafe_allow_html=True)
548
 
549
+ st.markdown("""
550
+ <div class="tip-item">
551
+ <div class="tip-icon">πŸ’‘</div>
552
+ <div class="tip-text">
553
+ <strong>Be specific with your topic.</strong> Instead of "climate change", try "recent climate legislation in the EU".
554
+ </div>
555
+ </div>
556
 
557
+ <div class="tip-item">
558
+ <div class="tip-icon">πŸ”</div>
559
+ <div class="tip-text">
560
+ <strong>Adjust search depth</strong> to find the right balance between comprehensive coverage and analysis speed.
561
+ </div>
562
+ </div>
 
 
 
563
 
564
+ <div class="tip-item">
565
+ <div class="tip-icon">πŸ“Š</div>
566
+ <div class="tip-text">
567
+ <strong>Choose the right analysis type</strong> based on your needs:
568
+ <ul>
569
+ <li><strong>Comprehensive:</strong> Full analysis with multiple perspectives</li>
570
+ <li><strong>Quick Summary:</strong> Brief overview of key points</li>
571
+ <li><strong>Technical:</strong> Detailed analysis with industry-specific terminology</li>
572
+ <li><strong>Simplified:</strong> Easy-to-understand breakdown of complex topics</li>
573
+ </ul>
574
+ </div>
575
+ </div>
576
 
577
+ <div class="tip-item">
578
+ <div class="tip-icon">⏱️</div>
579
+ <div class="tip-text">
580
+ <strong>Select an appropriate time period</strong> based on how recent you want the news to be.
581
+ </div>
582
+ </div>
583
+ """, unsafe_allow_html=True)
 
 
584
 
585
  st.markdown('</div>', unsafe_allow_html=True)
586
 
587
+ # Example topics for quick selection
588
  st.markdown('<div class="card">', unsafe_allow_html=True)
589
+ st.markdown("<h2 style='margin-top: 0;'>Try These Topics</h2>", unsafe_allow_html=True)
590
 
591
+ col1, col2, col3 = st.columns(3)
 
 
 
 
 
 
 
 
 
 
 
 
 
592
 
593
+ with col1:
594
+ if st.button("🌍 Climate Change Policies"):
595
+ st.session_state.news_topic_input = "Recent climate change policies and agreements"
596
+ st.experimental_rerun()
597
+
598
+ with col2:
599
+ if st.button("πŸ’° Cryptocurrency Trends"):
600
+ st.session_state.news_topic_input = "Latest developments in cryptocurrency markets"
601
+ st.experimental_rerun()
602
+
603
+ with col3:
604
+ if st.button("πŸ”¬ AI Research Breakthroughs"):
605
+ st.session_state.news_topic_input = "Recent breakthroughs in artificial intelligence research"
606
+ st.experimental_rerun()
607
+
608
  st.markdown('</div>', unsafe_allow_html=True)
609
 
610
+ # Footer with attribution and version info
611
+ st.markdown("""
612
+ <footer>
613
+ <div style="display: flex; justify-content: space-between; align-items: center;">
614
+ <div>Z-Agent News Analysis v1.0.1</div>
615
+ <div></div>
616
+ </div>
617
+ </footer>
618
+ """, unsafe_allow_html=True)
619
+
620
  except Exception as e:
621
+ # Global error handling to catch any unforeseen issues
622
  st.error(f"""
623
  <div style="display: flex; align-items: center; gap: 12px;">
624
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#FF5252" stroke-width="2">
625
+ <circle cx="12" cy="12" r="10"></circle>
626
+ <line x1="12" y1="8" x2="12" y2="12"></line>
627
+ <line x1="12" y1="16" x2="12.01" y2="16"></line>
628
+ </svg>
629
+ <span>Application Error: {str(e)}</span>
630
  </div>
631
  """, unsafe_allow_html=True)
632
 
633
  st.markdown("""
634
+ <div style="background-color: #F8F9FA; padding: 1rem; border-radius: 8px; margin-top: 1rem;">
635
+ <p><strong>Troubleshooting Tips:</strong></p>
636
  <ul>
637
+ <li>Check your API keys for Groq LLM in environment variables</li>
638
+ <li>Ensure you have internet connection for DuckDuckGo searches</li>
639
  <li>Try refreshing the page or restarting the application</li>
640
  </ul>
641
  </div>
642
  """, unsafe_allow_html=True)
643
 
644
+ # Add a hidden feature to reset the application state
645
+ if st.sidebar.button("Reset Application", key="reset_app"):
646
+ for key in st.session_state.keys():
647
+ del st.session_state[key]
648
+ st.experimental_rerun()
649
+
650
+ # Optional: Add a feedback mechanism
651
  with st.sidebar:
652
+ st.markdown("### Feedback")
653
+ feedback = st.text_area("Share your thoughts or report issues", placeholder="Your feedback helps us improve...")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
654
  if st.button("Submit Feedback"):
655
  st.success("Thank you for your feedback!")
656
+ # In a production app, you would save this feedback to a database