openfree commited on
Commit
71b8a5a
·
verified ·
1 Parent(s): b672d97

Update app.py

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