siyah1 commited on
Commit
d0d8ec3
Β·
verified Β·
1 Parent(s): 81c7dcf

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +537 -509
app.py CHANGED
@@ -1,656 +1,684 @@
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
 
 
 
 
 
 
 
 
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)