openfree commited on
Commit
9b96f4f
·
verified ·
1 Parent(s): 7d68120

Delete app-backup.py

Browse files
Files changed (1) hide show
  1. app-backup.py +0 -1234
app-backup.py DELETED
@@ -1,1234 +0,0 @@
1
- import streamlit as st
2
- import os
3
- import json
4
- from datetime import datetime, timedelta
5
- import base64
6
- import pandas as pd
7
- import pydeck as pdk
8
- from travel import (
9
- destination_research_task, accommodation_task, transportation_task,
10
- activities_task, dining_task, itinerary_task, chatbot_task,
11
- run_task
12
- )
13
-
14
- # st.set_page_config()는 다른 Streamlit 함수보다 가장 먼저 실행되어야 합니다.
15
- st.set_page_config(
16
- page_title="Your AI Agent for Travelling",
17
- page_icon="✈️",
18
- layout="wide",
19
- initial_sidebar_state="expanded"
20
- )
21
-
22
- # ------------------------------------------
23
- # 다국어 지원을 위한 번역 사전 및 헬퍼 함수
24
- # ------------------------------------------
25
- translations = {
26
- "en": {
27
- "page_title": "Your AI Agent for Travelling",
28
- "header": "Your AI Agent for Travelling",
29
- "create_itinerary": "Create Your Itinerary",
30
- "trip_details": "Trip Details",
31
- "origin": "Origin",
32
- "destination": "Destination",
33
- "travel_dates": "Travel Dates",
34
- "duration": "Duration (days)",
35
- "preferences": "Preferences",
36
- "additional_preferences": "Additional Preferences",
37
- "interests": "Interests",
38
- "special_requirements": "Special Requirements",
39
- "submit": "🚀 Create My Personal Travel Itinerary",
40
- "request_details": "Your Travel Request",
41
- "from": "From",
42
- "when": "When",
43
- "budget": "Budget",
44
- "travel_style": "Travel Style",
45
- "live_agent_outputs": "Live Agent Outputs",
46
- "full_itinerary": "Full Itinerary",
47
- "details": "Details",
48
- "download_share": "Download & Share",
49
- "save_itinerary": "Save Your Itinerary",
50
- "plan_another_trip": "🔄 Plan Another Trip",
51
- "about": "About",
52
- "how_it_works": "How it works",
53
- "travel_agents": "Travel Agents",
54
- "share_itinerary": "Share Your Itinerary",
55
- "save_for_mobile": "Save for Mobile",
56
- "built_with": "Built with ❤️ for you",
57
- # 출력 관련 추가 텍스트
58
- "itinerary_ready": "Your Travel Itinerary is Ready! 🎉",
59
- "personalized_experience": "We've created a personalized travel experience just for you. Explore your itinerary below.",
60
- "agent_activity": "Agent Activity",
61
- "error_origin_destination": "Please enter both origin and destination.",
62
- "your_itinerary_file": "Your Itinerary File",
63
- "text_format": "Text format - Can be opened in any text editor"
64
- },
65
- "ko": {
66
- "page_title": "당신의 여행을 위한 AI 에이전트",
67
- "header": "당신의 여행을 위한 AI 에이전트",
68
- "create_itinerary": "여행 일정 생성",
69
- "trip_details": "여행 세부 정보",
70
- "origin": "출발지",
71
- "destination": "목적지",
72
- "travel_dates": "여행 날짜",
73
- "duration": "기간 (일수)",
74
- "preferences": "선호사항",
75
- "additional_preferences": "추가 선호사항",
76
- "interests": "관심사",
77
- "special_requirements": "특별 요구사항",
78
- "submit": "🚀 나만의 여행 일정 생성",
79
- "request_details": "여행 요청 정보",
80
- "from": "출발지",
81
- "when": "여행 기간",
82
- "budget": "예산",
83
- "travel_style": "여행 스타일",
84
- "live_agent_outputs": "실시간 에이전트 결과",
85
- "full_itinerary": "전체 일정",
86
- "details": "세부사항",
87
- "download_share": "다운로드 및 공유",
88
- "save_itinerary": "일정 저장",
89
- "plan_another_trip": "🔄 다른 여행 계획",
90
- "about": "소개",
91
- "how_it_works": "작동 방식",
92
- "travel_agents": "여행 에이전트",
93
- "share_itinerary": "일정 공유",
94
- "save_for_mobile": "모바일 저장",
95
- "built_with": "당신을 위해 ❤️ 만들어졌습니다",
96
- # 출력 관련 추가 텍스트
97
- "itinerary_ready": "여행 일정이 준비되었습니다! 🎉",
98
- "personalized_experience": "당신만을 위한 맞춤형 여행 경험이 만들어졌습니다. 아래에서 일정을 확인하세요.",
99
- "agent_activity": "에이전트 활동",
100
- "error_origin_destination": "출발지와 목적지를 모두 입력하세요.",
101
- "your_itinerary_file": "당신의 여행 일정 파일",
102
- "text_format": "텍스트 형식 - 모든 텍스트 편집기에서 열 수 있습니다."
103
- },
104
- "ja": {
105
- "page_title": "あなたの旅行のためのAIエージェント",
106
- "header": "あなたの旅行のためのAIエージェント",
107
- "create_itinerary": "旅行プラン作成",
108
- "trip_details": "旅行詳細",
109
- "origin": "出発地",
110
- "destination": "目的地",
111
- "travel_dates": "旅行日程",
112
- "duration": "期間(日数)",
113
- "preferences": "好み",
114
- "additional_preferences": "追加の好み",
115
- "interests": "興味",
116
- "special_requirements": "特別な要件",
117
- "submit": "🚀 私のための旅行プラン作成",
118
- "request_details": "旅行リクエスト",
119
- "from": "出発地",
120
- "when": "旅行期間",
121
- "budget": "予算",
122
- "travel_style": "旅行スタイル",
123
- "live_agent_outputs": "リアルタイムエージェント出力",
124
- "full_itinerary": "全行程",
125
- "details": "詳細",
126
- "download_share": "ダウンロードと共有",
127
- "save_itinerary": "旅行プランを保存",
128
- "plan_another_trip": "🔄 他の旅行を計画",
129
- "about": "概要",
130
- "how_it_works": "使い方",
131
- "travel_agents": "旅行エージェント",
132
- "share_itinerary": "旅行プランを共有",
133
- "save_for_mobile": "モバイル保存",
134
- "built_with": "愛を込めて作られました",
135
- # 출력 관련 추가 텍스트
136
- "itinerary_ready": "旅行プランの準備ができました! 🎉",
137
- "personalized_experience": "あなたのためにパーソナライズされた旅行体験を作成しました。下のプランをご覧ください。",
138
- "agent_activity": "エージェントアクティビティ",
139
- "error_origin_destination": "出発地と目的地の両方を入力してください。",
140
- "your_itinerary_file": "あなたの旅行プランファイル",
141
- "text_format": "テキスト形式 - 任意のテキストエディタで開けます。"
142
- },
143
- "zh": {
144
- "page_title": "您的旅行 AI 代理",
145
- "header": "您的旅行 AI 代理",
146
- "create_itinerary": "创建您的行程",
147
- "trip_details": "旅行详情",
148
- "origin": "出发地",
149
- "destination": "目的地",
150
- "travel_dates": "旅行日期",
151
- "duration": "天数",
152
- "preferences": "偏好",
153
- "additional_preferences": "其他偏好",
154
- "interests": "兴趣",
155
- "special_requirements": "特殊需求",
156
- "submit": "🚀 创建我的个性化行程",
157
- "request_details": "您的旅行请求",
158
- "from": "出发地",
159
- "when": "旅行时间",
160
- "budget": "预算",
161
- "travel_style": "旅行风格",
162
- "live_agent_outputs": "实时代理输出",
163
- "full_itinerary": "完整行程",
164
- "details": "详情",
165
- "download_share": "下载与分享",
166
- "save_itinerary": "保存行程",
167
- "plan_another_trip": "🔄 计划另一趟旅行",
168
- "about": "关于",
169
- "how_it_works": "工作原理",
170
- "travel_agents": "旅行代理",
171
- "share_itinerary": "分享行程",
172
- "save_for_mobile": "保存到手机",
173
- "built_with": "用❤️为您制作",
174
- # 출력 관련 추가 텍스트
175
- "itinerary_ready": "您的旅行行程已准备就绪! 🎉",
176
- "personalized_experience": "我们已为您创建了个性化的旅行体验,请在下方查看您的行程。",
177
- "agent_activity": "代理活动",
178
- "error_origin_destination": "请输入出发地和目的地。",
179
- "your_itinerary_file": "您的行程文件",
180
- "text_format": "文本格式 - 可在任何文本编辑器中打开。"
181
- },
182
- "es": {
183
- "page_title": " Tu Agente de IA para Viajar",
184
- "header": " Tu Agente de IA para Viajar",
185
- "create_itinerary": "Crea Tu Itinerario",
186
- "trip_details": "Detalles del Viaje",
187
- "origin": "Origen",
188
- "destination": "Destino",
189
- "travel_dates": "Fechas del Viaje",
190
- "duration": "Duración (días)",
191
- "preferences": "Preferencias",
192
- "additional_preferences": "Preferencias Adicionales",
193
- "interests": "Intereses",
194
- "special_requirements": "Requisitos Especiales",
195
- "submit": "🚀 Crea Mi Itinerario Personalizado",
196
- "request_details": "Tu Solicitud de Viaje",
197
- "from": "Desde",
198
- "when": "Cuándo",
199
- "budget": "Presupuesto",
200
- "travel_style": "Estilo de Viaje",
201
- "live_agent_outputs": "Salidas en Vivo del Agente",
202
- "full_itinerary": "Itinerario Completo",
203
- "details": "Detalles",
204
- "download_share": "Descargar y Compartir",
205
- "save_itinerary": "Guardar Itinerario",
206
- "plan_another_trip": "🔄 Planear Otro Viaje",
207
- "about": "Acerca de",
208
- "how_it_works": "Cómo Funciona",
209
- "travel_agents": "Agentes de Viaje",
210
- "share_itinerary": "Compartir Itinerario",
211
- "save_for_mobile": "Guardar para Móvil",
212
- "built_with": "Hecho con ❤️ para ti",
213
- # 출력 관련 추가 텍스트
214
- "itinerary_ready": "¡Tu itinerario de viaje está listo! 🎉",
215
- "personalized_experience": "Hemos creado una experiencia de viaje personalizada solo para ti. Explora tu itinerario a continuación.",
216
- "agent_activity": "Actividad del Agente",
217
- "error_origin_destination": "Por favor, ingresa tanto el origen como el destino.",
218
- "your_itinerary_file": "Tu Archivo de Itinerario",
219
- "text_format": "Formato de texto - Se puede abrir en cualquier editor de texto."
220
- },
221
- "fr": {
222
- "page_title": " Votre Agent IA pour Voyager",
223
- "header": " Votre Agent IA pour Voyager",
224
- "create_itinerary": "Créez Votre Itinéraire",
225
- "trip_details": "Détails du Voyage",
226
- "origin": "Origine",
227
- "destination": "Destination",
228
- "travel_dates": "Dates du Voyage",
229
- "duration": "Durée (jours)",
230
- "preferences": "Préférences",
231
- "additional_preferences": "Préférences Supplémentaires",
232
- "interests": "Centres d'intérêt",
233
- "special_requirements": "Exigences Spéciales",
234
- "submit": "🚀 Créez Mon Itinéraire Personnalisé",
235
- "request_details": "Votre Demande de Voyage",
236
- "from": "De",
237
- "when": "Quand",
238
- "budget": "Budget",
239
- "travel_style": "Style de Voyage",
240
- "live_agent_outputs": "Résultats en Direct de l'Agent",
241
- "full_itinerary": "Itinéraire Complet",
242
- "details": "Détails",
243
- "download_share": "Télécharger et Partager",
244
- "save_itinerary": "Enregistrer l'Itinéraire",
245
- "plan_another_trip": "🔄 Planifier un Autre Voyage",
246
- "about": "À Propos",
247
- "how_it_works": "Fonctionnement",
248
- "travel_agents": "Agents de Voyage",
249
- "share_itinerary": "Partager l'Itinéraire",
250
- "save_for_mobile": "Enregistrer pour Mobile",
251
- "built_with": "Conçu avec ❤️ pour vous",
252
- # 출력 관련 추가 텍스트
253
- "itinerary_ready": "Votre itinéraire de voyage est prêt ! 🎉",
254
- "personalized_experience": "Nous avons créé une expérience de voyage personnalisée rien que pour vous. Découvrez votre itinéraire ci-dessous.",
255
- "agent_activity": "Activité de l'Agent",
256
- "error_origin_destination": "Veuillez saisir à la fois le lieu de départ et la destination.",
257
- "your_itinerary_file": "Votre Fichier d'Itinéraire",
258
- "text_format": "Format texte - Peut être ouvert dans n'importe quel éditeur de texte."
259
- },
260
- "de": {
261
- "page_title": "Ihr KI-Reiseassistent",
262
- "header": " Ihr KI-Reiseassistent",
263
- "create_itinerary": "Erstellen Sie Ihre Reiseroute",
264
- "trip_details": "Reisedetails",
265
- "origin": "Abfahrtsort",
266
- "destination": "Zielort",
267
- "travel_dates": "Reisedaten",
268
- "duration": "Dauer (Tage)",
269
- "preferences": "Vorlieben",
270
- "additional_preferences": "Zusätzliche Vorlieben",
271
- "interests": "Interessen",
272
- "special_requirements": "Besondere Anforderungen",
273
- "submit": "🚀 Erstellen Sie meine personalisierte Reiseroute",
274
- "request_details": "Ihre Reiseanfrage",
275
- "from": "Von",
276
- "when": "Wann",
277
- "budget": "Budget",
278
- "travel_style": "Reisestil",
279
- "live_agent_outputs": "Live Agent Ausgaben",
280
- "full_itinerary": "Komplette Reiseroute",
281
- "details": "Details",
282
- "download_share": "Herunterladen & Teilen",
283
- "save_itinerary": "Reiseroute speichern",
284
- "plan_another_trip": "🔄 Plane eine weitere Reise",
285
- "about": "Über",
286
- "how_it_works": "Wie es funktioniert",
287
- "travel_agents": "Reiseassistenten",
288
- "share_itinerary": "Reiseroute teilen",
289
- "save_for_mobile": "Für Mobilgeräte speichern",
290
- "built_with": "Mit ❤️ für Sie gebaut",
291
- # 출력 관련 추가 텍스트
292
- "itinerary_ready": "Ihre Reiseroute ist fertig! 🎉",
293
- "personalized_experience": "Wir haben eine personalisierte Reiseerfahrung nur für Sie erstellt. Entdecken Sie Ihre Reiseroute unten.",
294
- "agent_activity": "Agentenaktivität",
295
- "error_origin_destination": "Bitte geben Sie sowohl den Abfahrtsort als auch das Ziel ein.",
296
- "your_itinerary_file": "Ihre Reise-Datei",
297
- "text_format": "Textformat – Kann in jedem Texteditor geöffnet werden."
298
- },
299
- "ar": {
300
- "page_title": " وكيل السفر الذكي الخاص بك",
301
- "header": " وكيل السفر الذكي الخاص بك",
302
- "create_itinerary": "إنشاء خط سير الرحلة",
303
- "trip_details": "تفاصيل الرحلة",
304
- "origin": "المغادرة من",
305
- "destination": "الوجهة",
306
- "travel_dates": "تواريخ السفر",
307
- "duration": "المدة (بالأيام)",
308
- "preferences": "التفضيلات",
309
- "additional_preferences": "تفضيلات إضافية",
310
- "interests": "الاهتمامات",
311
- "special_requirements": "المتطلبات الخاصة",
312
- "submit": "🚀 إنشاء خط سير الرحلة الشخصي",
313
- "request_details": "طلب السفر الخاص بك",
314
- "from": "من",
315
- "when": "متى",
316
- "budget": "الميزانية",
317
- "travel_style": "أسلوب السفر",
318
- "live_agent_outputs": "مخرجات الوكيل المباشرة",
319
- "full_itinerary": "خط سير الرحلة الكامل",
320
- "details": "التفاصيل",
321
- "download_share": "تنزيل ومشاركة",
322
- "save_itinerary": "حفظ خط سير الرحلة",
323
- "plan_another_trip": "🔄 خطط لرحلة أخرى",
324
- "about": "حول",
325
- "how_it_works": "كيف يعمل",
326
- "travel_agents": "وكلاء السفر",
327
- "share_itinerary": "شارك خط سير الرحلة",
328
- "save_for_mobile": "حفظ للهاتف المحمول",
329
- "built_with": "مصنوع بحب من أجلك",
330
- # 출력 관련 추가 텍스트
331
- "itinerary_ready": "تم تجهيز خط سير رحلتك! 🎉",
332
- "personalized_experience": "لقد أنشأنا تجربة سفر مخصصة لك. استعرض خط سير رحلتك أدناه.",
333
- "agent_activity": "نشاط الوكيل",
334
- "error_origin_destination": "يرجى إدخال نقطة الانطلاق والوجهة.",
335
- "your_itinerary_file": "ملف خط سير رحلتك",
336
- "text_format": "تنسيق نصي - يمكن فتحه في أي محرر نصوص."
337
- }
338
- }
339
-
340
- def t(key):
341
- lang = st.session_state.get("selected_language", "en")
342
- return translations[lang].get(key, key)
343
-
344
- # ---------------------------
345
- # 세션 초기화
346
- # ---------------------------
347
- if 'selected_language' not in st.session_state:
348
- st.session_state.selected_language = "en" # 기본은 영어
349
-
350
- # ------------------------------------------
351
- # 사이드바에 언어 선택 위젯 추가
352
- # ------------------------------------------
353
- with st.sidebar:
354
- language = st.selectbox(
355
- "Language / 언어 / 言語 / 语言 / Idioma / Langue / Sprache / اللغة",
356
- ["English", "한국어", "日本語", "中文", "Español", "Français", "Deutsch", "العربية"]
357
- )
358
- lang_map = {
359
- "English": "en",
360
- "한국어": "ko",
361
- "日本語": "ja",
362
- "中文": "zh",
363
- "Español": "es",
364
- "Français": "fr",
365
- "Deutsch": "de",
366
- "العربية": "ar"
367
- }
368
- st.session_state.selected_language = lang_map.get(language, "en")
369
-
370
- # ------------------------------------------
371
- # 이후 Streamlit UI 코드 시작
372
- # ------------------------------------------
373
-
374
- # Modern CSS with refined color scheme and sleek animations
375
- st.markdown("""
376
- <style>
377
- /* Sleek Color Palette */
378
- :root {
379
- --primary: #3a86ff;
380
- --primary-light: #4895ef;
381
- --primary-dark: #2667ff;
382
- --secondary: #4cc9f0;
383
- --accent: #4361ee;
384
- --background: #f8f9fa;
385
- --card-bg: #ffffff;
386
- --text: #212529;
387
- --text-light: #6c757d;
388
- --text-muted: #adb5bd;
389
- --border: #e9ecef;
390
- --success: #2ecc71;
391
- --warning: #f39c12;
392
- --info: #3498db;
393
- }
394
-
395
- /* Refined Animations */
396
- @keyframes smoothFadeIn {
397
- from { opacity: 0; transform: translateY(10px); }
398
- to { opacity: 1; transform: translateY(0); }
399
- }
400
-
401
- @keyframes slideInRight {
402
- from { opacity: 0; transform: translateX(20px); }
403
- to { opacity: 1; transform: translateX(0); }
404
- }
405
-
406
- .animate-in {
407
- animation: smoothFadeIn 0.5s cubic-bezier(0.215, 0.61, 0.355, 1);
408
- }
409
-
410
- .slide-in {
411
- animation: slideInRight 0.5s cubic-bezier(0.215, 0.61, 0.355, 1);
412
- }
413
-
414
- /* Sleek Header Styles */
415
- .main-header {
416
- font-size: 2.5rem;
417
- color: var(--primary-dark);
418
- text-align: center;
419
- margin-bottom: 0.8rem;
420
- font-weight: 700;
421
- letter-spacing: -0.5px;
422
- }
423
-
424
- .sub-header {
425
- font-size: 1.4rem;
426
- color: var(--accent);
427
- font-weight: 600;
428
- margin-top: 1.8rem;
429
- margin-bottom: 0.8rem;
430
- border-bottom: 1px solid var(--border);
431
- padding-bottom: 0.4rem;
432
- }
433
-
434
- /* Sleek Card Styles */
435
- .modern-card {
436
- background-color: var(--card-bg);
437
- border-radius: 10px;
438
- padding: 1.2rem;
439
- margin-bottom: 1.2rem;
440
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
441
- transition: all 0.25s ease;
442
- border: 1px solid var(--border);
443
- }
444
-
445
- .modern-card:hover {
446
- transform: translateY(-3px);
447
- box-shadow: 0 4px 15px rgba(0, 0, 0, 0.08);
448
- }
449
-
450
- /* Refined Form Styles */
451
- .stTextInput > div > div > input,
452
- .stDateInput > div > div > input,
453
- .stTextArea > div > div > textarea {
454
- border-radius: 6px;
455
- border: 1px solid var(--border);
456
- padding: 10px 12px;
457
- font-size: 14px;
458
- transition: all 0.2s ease;
459
- box-shadow: none;
460
- }
461
-
462
- .stTextInput > div > div > input:focus,
463
- .stDateInput > div > div > input:focus,
464
- .stTextArea > div > div > textarea:focus {
465
- border: 1px solid var(--primary);
466
- box-shadow: 0 0 0 1px rgba(58, 134, 255, 0.15);
467
- }
468
-
469
- /* Sleek Button Styles */
470
- .stButton > button {
471
- background-color: var(--primary);
472
- color: white;
473
- font-weight: 500;
474
- padding: 0.5rem 1.2rem;
475
- border-radius: 6px;
476
- border: none;
477
- transition: all 0.2s ease;
478
- font-size: 14px;
479
- letter-spacing: 0.3px;
480
- }
481
-
482
- .stButton > button:hover {
483
- background-color: var(--primary-dark);
484
- transform: translateY(-1px);
485
- box-shadow: 0 3px 8px rgba(58, 134, 255, 0.25);
486
- }
487
-
488
- /* Sleek Tab Styles */
489
- .stTabs [data-baseweb="tab-list"] {
490
- gap: 2px;
491
- background-color: var(--background);
492
- border-radius: 8px;
493
- padding: 2px;
494
- }
495
-
496
- .stTabs [data-baseweb="tab"] {
497
- border-radius: 6px;
498
- padding: 8px 16px;
499
- font-size: 14px;
500
- font-weight: 500;
501
- }
502
-
503
- .stTabs [aria-selected="true"] {
504
- background-color: var(--primary);
505
- color: white !important;
506
- }
507
-
508
- /* Progress Bar Styles */
509
- .stProgress > div > div > div > div {
510
- background-color: var(--primary);
511
- }
512
-
513
- /* Progress Styles */
514
- .progress-container {
515
- margin: 1.2rem 0;
516
- background-color: var(--background);
517
- border-radius: 8px;
518
- padding: 0.8rem;
519
- border: 1px solid var(--border);
520
- }
521
-
522
- .step-complete {
523
- color: #4CAF50;
524
- font-weight: 600;
525
- }
526
-
527
- .step-pending {
528
- color: #9E9E9E;
529
- }
530
-
531
- .step-active {
532
- color: var(--primary);
533
- font-weight: 600;
534
- }
535
-
536
- /* Agent Output */
537
- .agent-output {
538
- background-color: #f8f9fa;
539
- border-left: 5px solid var(--primary);
540
- padding: 1.2rem;
541
- margin: 1rem 0;
542
- border-radius: 10px;
543
- max-height: 400px;
544
- overflow-y: auto;
545
- }
546
-
547
- /* Footer */
548
- .footer {
549
- text-align: center;
550
- margin-top: 3rem;
551
- color: var(--text-light);
552
- font-size: 0.9rem;
553
- padding: 1rem;
554
- border-top: 1px solid #eaeaea;
555
- }
556
-
557
- /* Agent Log */
558
- .agent-log {
559
- background-color: #F5F5F5;
560
- border-left: 3px solid var(--primary);
561
- padding: 0.5rem;
562
- margin-bottom: 0.5rem;
563
- font-family: monospace;
564
- border-radius: 4px;
565
- }
566
-
567
- /* Info and Success Boxes */
568
- .info-box {
569
- background-color: var(--primary-light);
570
- color: white;
571
- padding: 1rem;
572
- border-radius: 0.5rem;
573
- margin-bottom: 1rem;
574
- }
575
-
576
- .success-box {
577
- background-color: #E8F5E9;
578
- padding: 1rem;
579
- border-radius: 0.5rem;
580
- margin-bottom: 1rem;
581
- border-left: 5px solid #4CAF50;
582
- }
583
- </style>
584
- """, unsafe_allow_html=True)
585
-
586
- # Helper function to download HTML file
587
- def get_download_link(text_content, filename):
588
- b64 = base64.b64encode(text_content.encode()).decode()
589
- href = f'<a class="download-link" href="data:text/plain;base64,{b64}" download="{filename}"><i>📥</i> {t("save_itinerary")}</a>'
590
- return href
591
-
592
- # Updated helper function to display modern progress with a single UI element
593
- def display_modern_progress(current_step, total_steps=6):
594
- if 'progress_steps' not in st.session_state:
595
- st.session_state.progress_steps = {
596
- 0: {'status': 'pending', 'name': t("trip_details")},
597
- 1: {'status': 'pending', 'name': t("about")},
598
- 2: {'status': 'pending', 'name': t("travel_style")},
599
- 3: {'status': 'pending', 'name': t("live_agent_outputs")},
600
- 4: {'status': 'pending', 'name': t("download_share")},
601
- 5: {'status': 'pending', 'name': t("full_itinerary")}
602
- }
603
-
604
- for i in range(total_steps):
605
- if i < current_step:
606
- st.session_state.progress_steps[i]['status'] = 'complete'
607
- elif i == current_step:
608
- st.session_state.progress_steps[i]['status'] = 'active'
609
- else:
610
- st.session_state.progress_steps[i]['status'] = 'pending'
611
-
612
- progress_percentage = (current_step / total_steps) * 100
613
- st.progress(progress_percentage / 100)
614
-
615
- st.markdown("""
616
- <style>
617
- .compact-progress {
618
- background: white;
619
- border-radius: 10px;
620
- padding: 15px;
621
- box-shadow: 0 4px 6px rgba(0,0,0,0.1);
622
- margin-bottom: 20px;
623
- }
624
- .progress-title {
625
- font-size: 16px;
626
- font-weight: bold;
627
- margin-bottom: 15px;
628
- color: #333;
629
- border-bottom: 1px solid #eee;
630
- padding-bottom: 10px;
631
- }
632
- .step-grid {
633
- display: grid;
634
- grid-template-columns: repeat(3, 1fr);
635
- gap: 10px;
636
- }
637
- .step-item {
638
- display: flex;
639
- align-items: center;
640
- padding: 8px 10px;
641
- border-radius: 6px;
642
- background: #f8f9fa;
643
- box-shadow: 0 1px 3px rgba(0,0,0,0.05);
644
- }
645
- .step-item.complete {
646
- border-left: 3px solid #4CAF50;
647
- background: #f1f8e9;
648
- }
649
- .step-item.active {
650
- border-left: 3px solid #2196F3;
651
- background: #e3f2fd;
652
- font-weight: bold;
653
- }
654
- .step-item.pending {
655
- border-left: 3px solid #9e9e9e;
656
- opacity: 0.7;
657
- }
658
- .step-icon {
659
- margin-right: 8px;
660
- font-size: 14px;
661
- }
662
- .step-text {
663
- font-size: 13px;
664
- white-space: nowrap;
665
- overflow: hidden;
666
- text-overflow: ellipsis;
667
- }
668
- </style>
669
- <div class="compact-progress">
670
- """, unsafe_allow_html=True)
671
-
672
- st.markdown('<div class="step-grid">', unsafe_allow_html=True)
673
- for i, step_info in st.session_state.progress_steps.items():
674
- status = step_info['status']
675
- name = step_info['name']
676
- if status == 'complete':
677
- icon = "✅"
678
- status_class = "complete"
679
- elif status == 'active':
680
- icon = "🔄"
681
- status_class = "active"
682
- else:
683
- icon = "⭕"
684
- status_class = "pending"
685
-
686
- st.markdown(f"""
687
- <div class="step-item {status_class}">
688
- <span class="step-icon">{icon}</span>
689
- <span class="step-text">{name}</span>
690
- </div>
691
- """, unsafe_allow_html=True)
692
-
693
- st.markdown('</div></div>', unsafe_allow_html=True)
694
- return progress_percentage
695
-
696
- def update_step_status(step_index, status):
697
- if 'progress_steps' in st.session_state and step_index in st.session_state.progress_steps:
698
- st.session_state.progress_steps[step_index]['status'] = status
699
-
700
- def run_task_with_logs(task, input_text, log_container, output_container, results_key=None):
701
- log_message = f"🤖 Starting {task.agent.role}..."
702
- st.session_state.log_messages.append(log_message)
703
-
704
- with log_container:
705
- st.markdown("### " + t("agent_activity"))
706
- for msg in st.session_state.log_messages:
707
- st.markdown(msg)
708
-
709
- result = run_task(task, input_text)
710
-
711
- if results_key:
712
- st.session_state.results[results_key] = result
713
-
714
- log_message = f"✅ {task.agent.role} completed!"
715
- st.session_state.log_messages.append(log_message)
716
-
717
- with log_container:
718
- st.markdown("### " + t("agent_activity"))
719
- for msg in st.session_state.log_messages:
720
- st.markdown(msg)
721
-
722
- with output_container:
723
- st.markdown(f"### {task.agent.role} Output")
724
- st.markdown("<div class='agent-output'>" + result + "</div>", unsafe_allow_html=True)
725
-
726
- return result
727
-
728
- # ------------------------------------------
729
- # Session state 초기화
730
- # ------------------------------------------
731
- if 'generated_itinerary' not in st.session_state:
732
- st.session_state.generated_itinerary = None
733
- if 'generation_complete' not in st.session_state:
734
- st.session_state.generation_complete = False
735
- if 'current_step' not in st.session_state:
736
- st.session_state.current_step = 0
737
- if 'results' not in st.session_state:
738
- st.session_state.results = {
739
- "destination_info": "",
740
- "accommodation_info": "",
741
- "transportation_info": "",
742
- "activities_info": "",
743
- "dining_info": "",
744
- "itinerary": "",
745
- "final_itinerary": ""
746
- }
747
- if 'log_messages' not in st.session_state:
748
- st.session_state.log_messages = []
749
- if 'current_output' not in st.session_state:
750
- st.session_state.current_output = None
751
- if 'form_submitted' not in st.session_state:
752
- st.session_state.form_submitted = False
753
-
754
- # Modern animated header
755
- st.markdown(f"""
756
- <div class="animate-in" style="text-align: center;">
757
- <div style="margin-bottom: 20px;">
758
- <img src="https://img.icons8.com/fluency/96/travel-card.png" width="90" style="filter: drop-shadow(0 4px 8px rgba(0,0,0,0.1));">
759
- </div>
760
- <h1 class="main-header">{t("header")}</h1>
761
- <p style="font-size: 1.2rem; color: #6c757d; margin-bottom: 25px;">
762
- ✨ Create your personalized AI-powered travel itinerary in minutes! ✨
763
- </p>
764
- </div>
765
- """, unsafe_allow_html=True)
766
-
767
- st.markdown('<hr style="height:3px;border:none;background-color:#f0f0f0;margin-bottom:25px;">', unsafe_allow_html=True)
768
-
769
- with st.sidebar:
770
- st.markdown("""
771
- <div style="text-align: center; padding: 20px 0; margin-bottom: 20px; border-bottom: 1px solid #eaeaea;">
772
- <img src="https://img.icons8.com/fluency/96/travel-card.png" width="80" style="margin-bottom: 15px;">
773
- <h3 style="margin-bottom: 5px; color: #4361ee;">Your AI Agent for Travelling</h3>
774
- <p style="color: #6c757d; font-size: 0.9rem;">AI-Powered Travel Planning</p>
775
- </div>
776
- """, unsafe_allow_html=True)
777
-
778
- st.markdown('<div class="modern-card">', unsafe_allow_html=True)
779
- st.markdown("### 🌟 " + t("about"))
780
- st.info("This AI-powered tool creates a personalized travel itinerary based on your preferences. Fill in the form and let our specialized travel agents plan your perfect trip!")
781
- st.markdown('</div>', unsafe_allow_html=True)
782
-
783
- st.markdown('<div class="modern-card">', unsafe_allow_html=True)
784
- st.markdown("### 🔍 " + t("how_it_works"))
785
- st.markdown("""
786
- <ol style="padding-left: 25px;">
787
- <li><b>🖊️ Enter</b> your travel details</li>
788
- <li><b>🧠 AI analysis</b> of your preferences</li>
789
- <li><b>📋 Generate</b> comprehensive itinerary</li>
790
- <li><b>📥 Download</b> and enjoy your trip!</li>
791
- </ol>
792
- """, unsafe_allow_html=True)
793
- st.markdown('</div>', unsafe_allow_html=True)
794
-
795
- st.markdown('<div class="modern-card">', unsafe_allow_html=True)
796
- st.markdown("### 🤖 Travel Agents")
797
- agents = [
798
- ("🔭 Research Specialist", "Finds the best destinations based on your preferences"),
799
- ("🏨 Accommodation Expert", "Suggests suitable hotels and stays"),
800
- ("🚆 Transportation Planner", "Plans efficient travel routes"),
801
- ("🎯 Activities Curator", "Recommends activities tailored to your interests"),
802
- ("🍽️ Dining Connoisseur", "Finds the best dining experiences"),
803
- ("📅 Itinerary Creator", "Puts everything together in a daily plan")
804
- ]
805
- for name, desc in agents:
806
- st.markdown("**" + name + "**")
807
- st.markdown("<small>" + desc + "</small>", unsafe_allow_html=True)
808
- st.markdown('</div>', unsafe_allow_html=True)
809
-
810
- if not st.session_state.generation_complete:
811
- st.markdown('<div class="modern-card animate-in">', unsafe_allow_html=True)
812
- st.markdown("<h3 style='font-weight: 600; color: var(--primary-dark); display: flex; align-items: center; gap: 10px;'><span style='font-size: 20px;'>✈️</span> " + t("create_itinerary") + "</h3>", unsafe_allow_html=True)
813
-
814
- st.markdown("""
815
- <p style="color: var(--text-light); margin-bottom: 16px; font-size: 14px; font-weight: 400;">Complete the form below for a personalized travel plan.</p>
816
- """, unsafe_allow_html=True)
817
-
818
- with st.form("travel_form"):
819
- col1, col2 = st.columns(2)
820
- with col1:
821
- st.markdown('<p style="font-weight: 500; color: var(--primary); font-size: 14px; margin-bottom: 12px;">Trip Details</p>', unsafe_allow_html=True)
822
- origin = st.text_input(t("origin"), placeholder="e.g., New York, USA")
823
- destination = st.text_input(t("destination"), placeholder="e.g., Paris, France")
824
- st.markdown('<p style="margin-bottom: 5px; font-size: 14px;">Travel Dates</p>', unsafe_allow_html=True)
825
- start_date = st.date_input("Start Date", min_value=datetime.now(), label_visibility="collapsed")
826
- duration = st.slider(t("duration"), min_value=1, max_value=30, value=7)
827
- end_date = start_date + timedelta(days=duration-1)
828
- st.markdown('<p style="font-size: 13px; color: var(--text-muted); margin-top: 5px;">' + start_date.strftime("%b %d") + " - " + end_date.strftime("%b %d, %Y") + '</p>', unsafe_allow_html=True)
829
- with col2:
830
- st.markdown('<p style="font-weight: 500; color: var(--primary); font-size: 14px; margin-bottom: 12px;">Preferences</p>', unsafe_allow_html=True)
831
- travelers = st.number_input("Travelers", min_value=1, max_value=15, value=2)
832
- budget_options = ["Budget", "Moderate", "Luxury"]
833
- budget = st.selectbox("Budget", budget_options, help="Budget: Economy options | Moderate: Mid-range | Luxury: High-end experiences")
834
- travel_style = st.multiselect("🌈 Travel Style", options=["Culture", "Adventure", "Relaxation", "Food & Dining", "Nature", "Shopping", "Nightlife", "Family-friendly"], default=["Culture", "Food & Dining"])
835
- with st.expander("Additional Preferences", expanded=False):
836
- preferences = st.text_area("Interests", placeholder="History museums, local cuisine, hiking, art...")
837
- special_requirements = st.text_area("Special Requirements", placeholder="Dietary restrictions, accessibility needs...")
838
- submit_button = st.form_submit_button(t("submit"))
839
- st.markdown('</div>', unsafe_allow_html=True)
840
-
841
- if submit_button:
842
- if not origin or not destination:
843
- st.error(t("error_origin_destination"))
844
- else:
845
- st.session_state.form_submitted = True
846
- user_input = {
847
- "origin": origin,
848
- "destination": destination,
849
- "duration": str(duration),
850
- "travel_dates": f"{start_date.strftime('%Y-%m-%d')} to {end_date.strftime('%Y-%m-%d')}",
851
- "travelers": str(travelers),
852
- "budget": budget.lower(),
853
- "travel_style": ", ".join(travel_style),
854
- "preferences": preferences,
855
- "special_requirements": special_requirements
856
- }
857
- # 기존의 여행 요청 프롬프트
858
- input_context = f"""Travel Request Details:
859
- Origin: {user_input['origin']}
860
- Destination: {user_input['destination']}
861
- Duration: {user_input['duration']} days
862
- Travel Dates: {user_input['travel_dates']}
863
- Travelers: {user_input['travelers']}
864
- Budget Level: {user_input['budget']}
865
- Travel Style: {user_input['travel_style']}
866
- Preferences/Interests: {user_input['preferences']}
867
- Special Requirements: {user_input['special_requirements']}
868
- """
869
- # LLM에 전달할 프롬프트에 언어 지시문 추가
870
- llm_language_instructions = {
871
- "en": "Please output the response in English.",
872
- "ko": "한국어로 출력해 주세요.",
873
- "ja": "日本語で出力してください。",
874
- "zh": "请用中文输出。",
875
- "es": "Por favor, responda en español.",
876
- "fr": "Veuillez répondre en français.",
877
- "de": "Bitte antworten Sie auf Deutsch.",
878
- "ar": "يرجى الرد باللغة العربية."
879
- }
880
- selected_lang = st.session_state.get("selected_language", "en")
881
- language_instruction = llm_language_instructions.get(selected_lang, "Please output the response in English.")
882
- modified_input_context = language_instruction + "\n" + input_context
883
-
884
- st.markdown("""
885
- <div class="sleek-processing-container">
886
- <div class="pulse-container">
887
- <div class="pulse-ring"></div>
888
- <div class="pulse-core"></div>
889
- </div>
890
- </div>
891
- <style>
892
- .sleek-processing-container {
893
- display: flex;
894
- justify-content: center;
895
- align-items: center;
896
- padding: 20px 0;
897
- }
898
- .pulse-container {
899
- position: relative;
900
- width: 50px;
901
- height: 50px;
902
- }
903
- .pulse-core {
904
- position: absolute;
905
- left: 50%;
906
- top: 50%;
907
- transform: translate(-50%, -50%);
908
- width: 12px;
909
- height: 12px;
910
- background-color: #4361ee;
911
- border-radius: 50%;
912
- box-shadow: 0 0 8px rgba(67, 97, 238, 0.6);
913
- }
914
- .pulse-ring {
915
- position: absolute;
916
- left: 0;
917
- top: 0;
918
- width: 100%;
919
- height: 100%;
920
- border: 2px solid #4361ee;
921
- border-radius: 50%;
922
- animation: pulse 1.5s ease-out infinite;
923
- opacity: 0;
924
- }
925
- @keyframes pulse {
926
- 0% { transform: scale(0.1); opacity: 0; }
927
- 50% { opacity: 0.5; }
928
- 100% { transform: scale(1); opacity: 0; }
929
- }
930
- </style>
931
- """, unsafe_allow_html=True)
932
-
933
- st.markdown('<div class="modern-card">', unsafe_allow_html=True)
934
- progress_tab, logs_tab, details_tab = st.tabs(["📊 Progress", "🔄 Live Activity", "📋 " + t("request_details")])
935
- with details_tab:
936
- st.markdown("#### " + t("request_details"))
937
- st.markdown("**" + t("destination") + ":** " + user_input['destination'])
938
- st.markdown("**" + t("from") + ":** " + user_input['origin'])
939
- st.markdown("**" + t("when") + ":** " + user_input['travel_dates'] + " (" + user_input['duration'] + " days)")
940
- st.markdown("**" + t("budget") + ":** " + user_input['budget'].title())
941
- st.markdown("**" + t("travel_style") + ":** " + user_input['travel_style'])
942
- if user_input['preferences']:
943
- st.markdown("**Interests:** " + user_input['preferences'])
944
- if user_input['special_requirements']:
945
- st.markdown("**Special Requirements:** " + user_input['special_requirements'])
946
- with progress_tab:
947
- if 'progress_placeholder' not in st.session_state:
948
- st.session_state.progress_placeholder = st.empty()
949
- with st.session_state.progress_placeholder.container():
950
- display_modern_progress(0)
951
- with logs_tab:
952
- log_container = st.container()
953
- st.session_state.log_messages = []
954
- st.markdown('</div>', unsafe_allow_html=True)
955
- output_container = st.container()
956
- with output_container:
957
- st.markdown('<div class="modern-card">', unsafe_allow_html=True)
958
- st.markdown("### 🌟 " + t("live_agent_outputs"))
959
- st.info("Our AI agents will show their work here as they create your itinerary")
960
- st.markdown('</div>', unsafe_allow_html=True)
961
- st.session_state.current_step = 0
962
-
963
- update_step_status(0, 'active')
964
- with st.session_state.progress_placeholder.container():
965
- display_modern_progress(st.session_state.current_step)
966
- destination_info = run_task_with_logs(
967
- destination_research_task,
968
- modified_input_context.format(destination=user_input['destination'], preferences=user_input['preferences']),
969
- log_container,
970
- output_container,
971
- "destination_info"
972
- )
973
- update_step_status(0, 'complete')
974
- st.session_state.current_step = 1
975
- update_step_status(1, 'active')
976
- with st.session_state.progress_placeholder.container():
977
- display_modern_progress(st.session_state.current_step)
978
- accommodation_info = run_task_with_logs(
979
- accommodation_task,
980
- modified_input_context.format(destination=user_input['destination'], budget=user_input['budget'], preferences=user_input['preferences']),
981
- log_container,
982
- output_container,
983
- "accommodation_info"
984
- )
985
- update_step_status(1, 'complete')
986
- st.session_state.current_step = 2
987
- update_step_status(2, 'active')
988
- with st.session_state.progress_placeholder.container():
989
- display_modern_progress(st.session_state.current_step)
990
- transportation_info = run_task_with_logs(
991
- transportation_task,
992
- modified_input_context.format(origin=user_input['origin'], destination=user_input['destination']),
993
- log_container,
994
- output_container,
995
- "transportation_info"
996
- )
997
- update_step_status(2, 'complete')
998
- st.session_state.current_step = 3
999
- update_step_status(3, 'active')
1000
- with st.session_state.progress_placeholder.container():
1001
- display_modern_progress(st.session_state.current_step)
1002
- activities_info = run_task_with_logs(
1003
- activities_task,
1004
- modified_input_context.format(destination=user_input['destination'], preferences=user_input['preferences']),
1005
- log_container,
1006
- output_container,
1007
- "activities_info"
1008
- )
1009
- update_step_status(3, 'complete')
1010
- st.session_state.current_step = 4
1011
- update_step_status(4, 'active')
1012
- with st.session_state.progress_placeholder.container():
1013
- display_modern_progress(st.session_state.current_step)
1014
- dining_info = run_task_with_logs(
1015
- dining_task,
1016
- modified_input_context.format(destination=user_input['destination'], preferences=user_input['preferences']),
1017
- log_container,
1018
- output_container,
1019
- "dining_info"
1020
- )
1021
- update_step_status(4, 'complete')
1022
- st.session_state.current_step = 5
1023
- update_step_status(5, 'active')
1024
- with st.session_state.progress_placeholder.container():
1025
- display_modern_progress(st.session_state.current_step)
1026
- combined_info = f"""{input_context}
1027
-
1028
- Destination Information:
1029
- {destination_info}
1030
-
1031
- Accommodation Options:
1032
- {accommodation_info}
1033
-
1034
- Transportation Plan:
1035
- {transportation_info}
1036
-
1037
- Recommended Activities:
1038
- {activities_info}
1039
-
1040
- Dining Recommendations:
1041
- {dining_info}
1042
- """
1043
- itinerary = run_task_with_logs(
1044
- itinerary_task,
1045
- combined_info.format(duration=user_input['duration'], origin=user_input['origin'], destination=user_input['destination']),
1046
- log_container,
1047
- output_container,
1048
- "itinerary"
1049
- )
1050
- update_step_status(5, 'complete')
1051
- st.session_state.current_step = 6
1052
- with st.session_state.progress_placeholder.container():
1053
- display_modern_progress(st.session_state.current_step)
1054
- st.session_state.generated_itinerary = itinerary
1055
- st.session_state.generation_complete = True
1056
- date_str = datetime.now().strftime("%Y-%m-%d")
1057
- st.session_state.filename = f"{user_input['destination'].replace(' ', '_')}_{date_str}_itinerary.txt"
1058
-
1059
- if st.session_state.generation_complete:
1060
- st.markdown("""
1061
- <div class="modern-card animate-in">
1062
- <div style="display: flex; justify-content: center; margin-bottom: 20px;">
1063
- <div class="success-animation">
1064
- <svg class="checkmark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52 52">
1065
- <circle class="checkmark__circle" cx="26" cy="26" r="25" fill="none" />
1066
- <path class="checkmark__check" fill="none" d="M14.1 27.2l7.1 7.2 16.7-16.8" />
1067
- </svg>
1068
- </div>
1069
- </div>
1070
- <h2 style="text-align: center; color: #4361ee;">""" + t("itinerary_ready") + """</h2>
1071
- <p style="text-align: center; color: #6c757d; margin-bottom: 20px;">""" + t("personalized_experience") + """</p>
1072
- </div>
1073
-
1074
- <style>
1075
- .success-animation {
1076
- width: 100px;
1077
- height: 100px;
1078
- position: relative;
1079
- }
1080
- .checkmark {
1081
- width: 100px;
1082
- height: 100px;
1083
- border-radius: 50%;
1084
- display: block;
1085
- stroke-width: 2;
1086
- stroke: #4361ee;
1087
- stroke-miterlimit: 10;
1088
- box-shadow: 0 0 20px rgba(67, 97, 238, 0.3);
1089
- animation: fill .4s ease-in-out .4s forwards, scale .3s ease-in-out .9s both;
1090
- }
1091
- .checkmark__circle {
1092
- stroke-dasharray: 166;
1093
- stroke-dashoffset: 166;
1094
- stroke-width: 2;
1095
- stroke-miterlimit: 10;
1096
- stroke: #4361ee;
1097
- fill: none;
1098
- animation: stroke 0.6s cubic-bezier(0.65, 0, 0.45, 1) forwards;
1099
- }
1100
- .checkmark__check {
1101
- transform-origin: 50% 50%;
1102
- stroke-dasharray: 48;
1103
- stroke-dashoffset: 48;
1104
- animation: stroke 0.3s cubic-bezier(0.65, 0, 0.45, 1) 0.8s forwards;
1105
- }
1106
- @keyframes stroke {
1107
- 100% { stroke-dashoffset: 0; }
1108
- }
1109
- @keyframes scale {
1110
- 0%, 100% { transform: none; }
1111
- 50% { transform: scale3d(1.1, 1.1, 1); }
1112
- }
1113
- @keyframes fill {
1114
- 100% { box-shadow: 0 0 20px rgba(67, 97, 238, 0.3); }
1115
- }
1116
- </style>
1117
- """, unsafe_allow_html=True)
1118
-
1119
- # 추가된 탭: 전체 일정, 상세 정보, 다운로드/공유, 지도 및 시각화, AI 챗봇 인터페이스
1120
- itinerary_tab, details_tab, download_tab, map_tab, chatbot_tab = st.tabs([
1121
- "🗒️ " + t("full_itinerary"),
1122
- "💼 " + t("details"),
1123
- "💾 " + t("download_share"),
1124
- "🗺️ 지도 및 시각화",
1125
- "🤖 챗봇 인터페이스"
1126
- ])
1127
-
1128
- # 일정 탭
1129
- with itinerary_tab:
1130
- st.text_area("Your Itinerary", st.session_state.generated_itinerary, height=600)
1131
-
1132
- # 상세 정보 탭
1133
- with details_tab:
1134
- agent_tabs = st.tabs(["🌎 Destination", "🏨 Accommodation", "🚗 Transportation", "🎭 Activities", "🍽️ Dining"])
1135
- with agent_tabs[0]:
1136
- st.markdown("### 🌎 Destination Research")
1137
- st.markdown(st.session_state.results["destination_info"])
1138
- with agent_tabs[1]:
1139
- st.markdown("### 🏨 Accommodation Options")
1140
- st.markdown(st.session_state.results["accommodation_info"])
1141
- with agent_tabs[2]:
1142
- st.markdown("### 🚗 Transportation Plan")
1143
- st.markdown(st.session_state.results["transportation_info"])
1144
- with agent_tabs[3]:
1145
- st.markdown("### 🎭 Recommended Activities")
1146
- st.markdown(st.session_state.results["activities_info"])
1147
- with agent_tabs[4]:
1148
- st.markdown("### 🍽️ Dining Recommendations")
1149
- st.markdown(st.session_state.results["dining_info"])
1150
-
1151
- # 다운로드 및 공유 탭
1152
- with download_tab:
1153
- col1, col2 = st.columns([2, 1])
1154
- with col1:
1155
- st.markdown("### " + t("save_itinerary"))
1156
- st.markdown("Download your personalized travel plan to access it offline or share with your travel companions.")
1157
- st.markdown("""
1158
- <div style="background-color: #f8f9fa; padding: 15px; border-radius: 10px; margin-top: 20px;">
1159
- <h4 style="margin-top: 0;">""" + t("your_itinerary_file") + """</h4>
1160
- <p style="font-size: 0.9rem; color: #6c757d;">""" + t("text_format") + """</p>
1161
- """, unsafe_allow_html=True)
1162
- st.markdown("<div style='margin: 10px 0;'>" + get_download_link(st.session_state.generated_itinerary, st.session_state.filename) + "</div>", unsafe_allow_html=True)
1163
- st.markdown("</div>", unsafe_allow_html=True)
1164
- st.markdown("### " + t("share_itinerary"))
1165
- st.markdown("*Coming soon: Email your itinerary or share via social media.*")
1166
- with col2:
1167
- st.markdown("### " + t("save_for_mobile"))
1168
- st.markdown("*Coming soon: QR code for easy access on your phone*")
1169
-
1170
- # 인터랙티브 지도 및 시각화 탭
1171
- with map_tab:
1172
- st.markdown("### 목적지 지도")
1173
- # 예시: 목적지 주변의 주요 명소 좌표 데이터 (실제 API나 DB를 통해 동적으로 가져올 수 있음)
1174
- map_data = pd.DataFrame({
1175
- "lat": [48.8584, 48.8606, 48.8529],
1176
- "lon": [2.2945, 2.3376, 2.3500],
1177
- "name": ["Eiffel Tower", "Louvre Museum", "Notre Dame"]
1178
- })
1179
- # 기본 지도 출력 (st.map)
1180
- st.map(map_data)
1181
-
1182
- st.markdown("#### Pydeck을 활용한 인터랙티브 지도 예시")
1183
- layer = pdk.Layer(
1184
- "ScatterplotLayer",
1185
- data=map_data,
1186
- get_position='[lon, lat]',
1187
- get_color='[200, 30, 0, 160]',
1188
- get_radius=200,
1189
- )
1190
- view_state = pdk.ViewState(
1191
- latitude=48.8566,
1192
- longitude=2.3522,
1193
- zoom=12,
1194
- pitch=50,
1195
- )
1196
- deck_chart = pdk.Deck(layers=[layer], initial_view_state=view_state)
1197
- st.pydeck_chart(deck_chart)
1198
-
1199
- # AI 챗봇 인터페이스 탭 (제미나이 적용)
1200
- with chatbot_tab:
1201
- st.markdown("### AI 챗봇 인터페이스")
1202
- # 대화 기록을 세션 ���태에 저장 (메시지, 발신자, 타임스탬프)
1203
- if "chat_history" not in st.session_state:
1204
- st.session_state.chat_history = []
1205
-
1206
- # 사용자 입력창 및 전송 버튼
1207
- user_message = st.text_input("메시지를 입력하세요:", key="chat_input")
1208
- if st.button("전송", key="send_button"):
1209
- if user_message:
1210
- # 제미나이 기반 챗봇 응답: run_task()를 활용하여 chatbot_task에 질의
1211
- response = run_task(chatbot_task, user_message)
1212
- st.session_state.chat_history.append({
1213
- "speaker": "사용자",
1214
- "message": user_message,
1215
- "time": datetime.now()
1216
- })
1217
- st.session_state.chat_history.append({
1218
- "speaker": "AI",
1219
- "message": response,
1220
- "time": datetime.now()
1221
- })
1222
-
1223
- # 대화 기록 출력 (타임스탬프 포함, 스크롤 가능한 영역)
1224
- st.markdown("<div style='max-height:400px; overflow-y:auto; padding:10px; border:1px solid #eaeaea; border-radius:6px;'>", unsafe_allow_html=True)
1225
- for chat in st.session_state.chat_history:
1226
- time_str = chat["time"].strftime("%H:%M:%S")
1227
- st.markdown(f"**{chat['speaker']}** ({time_str}): {chat['message']}")
1228
- st.markdown("</div>", unsafe_allow_html=True)
1229
-
1230
- st.markdown("""
1231
- <div style="margin-top: 50px; text-align: center; padding: 20px; color: #6c757d; font-size: 0.8rem;">
1232
- <p>""" + t("built_with") + """</p>
1233
- </div>
1234
- """, unsafe_allow_html=True)