AlessandroArgiolas02 commited on
Commit
fd29f08
·
verified ·
1 Parent(s): 6a22ac3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +104 -109
app.py CHANGED
@@ -2,11 +2,8 @@ import gradio as gr
2
  import google.generativeai as genai
3
  import os
4
  import re
5
- import requests
6
 
7
- # Configurazione API
8
  GEMINI_KEY = os.getenv("GEMINI")
9
- UNSPLASH_KEY = os.getenv("UNSPLASH_ACCESS")
10
  genai.configure(api_key=GEMINI_KEY)
11
  model = genai.GenerativeModel('gemini-pro')
12
 
@@ -22,6 +19,7 @@ CSS = """
22
  body {
23
  background: var(--background);
24
  font-family: 'Segoe UI', system-ui, sans-serif;
 
25
  margin: 0;
26
  padding: 20px;
27
  }
@@ -64,6 +62,7 @@ body {
64
  padding: 15px 20px;
65
  border-radius: 15px 15px 0 15px;
66
  margin: 10px 0 10px 30%;
 
67
  animation: slideIn 0.3s ease;
68
  }
69
 
@@ -73,15 +72,25 @@ body {
73
  border-radius: 15px 15px 15px 0;
74
  margin: 10px 30% 10px 0;
75
  border: 1px solid #E0E0E0;
 
76
  animation: fadeIn 0.5s ease;
77
  }
78
 
 
 
 
 
 
 
 
 
79
  .card {
80
  background: white;
81
- padding: 0;
82
  border-radius: 12px;
83
  margin: 15px 0;
84
- box-shadow: 0 3px 6px rgba(0,0,0,0.1);
 
85
  transition: transform 0.2s;
86
  }
87
 
@@ -89,37 +98,6 @@ body {
89
  transform: translateY(-3px);
90
  }
91
 
92
- .image-container {
93
- width: 100%;
94
- height: 200px;
95
- border-radius: 12px 12px 0 0;
96
- overflow: hidden;
97
- position: relative;
98
- }
99
-
100
- .place-image {
101
- width: 100%;
102
- height: 100%;
103
- object-fit: cover;
104
- }
105
-
106
- .logo-badge {
107
- position: absolute;
108
- bottom: 10px;
109
- right: 10px;
110
- background: rgba(255,255,255,0.9);
111
- padding: 5px 10px;
112
- border-radius: 20px;
113
- font-size: 0.8em;
114
- display: flex;
115
- align-items: center;
116
- gap: 5px;
117
- }
118
-
119
- .card-content {
120
- padding: 20px;
121
- }
122
-
123
  .price {
124
  color: var(--primary);
125
  font-weight: 600;
@@ -127,13 +105,45 @@ body {
127
  }
128
 
129
  .link {
130
- color: var(--secondary);
131
  text-decoration: none;
 
132
  display: inline-flex;
133
  align-items: center;
134
  gap: 5px;
135
  }
136
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
  @keyframes slideIn {
138
  from { transform: translateX(20px); opacity: 0; }
139
  to { transform: translateX(0); opacity: 1; }
@@ -143,6 +153,17 @@ body {
143
  from { opacity: 0; transform: translateY(10px); }
144
  to { opacity: 1; transform: translateY(0); }
145
  }
 
 
 
 
 
 
 
 
 
 
 
146
  """
147
 
148
  class ChatManager:
@@ -153,64 +174,38 @@ class ChatManager:
153
  cities = re.findall(r'\b(?:a|in|su|per)\s+([A-Z][a-z]+(?:\s+[A-Z][a-z]+)*)\b', text, re.IGNORECASE)
154
  return cities[0] if cities else self.context["city"]
155
 
156
- def get_place_image(self, place_name):
157
- try:
158
- url = "https://api.unsplash.com/search/photos"
159
- headers = {"Authorization": f"Client-ID {UNSPLASH_KEY}"}
160
- params = {
161
- "query": f"{place_name} {self.context['city']}",
162
- "per_page": 1,
163
- "orientation": "squarish"
164
- }
165
- response = requests.get(url, headers=headers, params=params)
166
- results = response.json().get('results', [])
167
- if results:
168
- return {
169
- "image": results[0]['urls']['regular'],
170
- "credit": results[0]['user']['name']
171
- }
172
- return None
173
- except:
174
- return None
175
-
176
  def format_response(self, response):
177
- html = ""
178
- current_category = None
179
 
180
  lines = response.split('\n')
181
  for line in lines:
182
- if line.startswith('## '):
183
- current_category = line.replace('## ', '').strip()
184
- html += f"<h2 style='color: var(--primary); margin-top: 30px;'>{current_category}</h2>"
185
- elif line.startswith('- **'):
186
  parts = re.split(r'\*\*:?|\*', line)
187
  if len(parts) > 2:
188
- name = parts[1].strip()
189
- details = parts[2].split('•')
190
-
191
- price = next((d.replace('💰', '').strip() for d in details if '💰' in d), None)
192
- link = next((d.strip() for d in details if 'http' in d), None)
193
- desc = ' • '.join([d.strip() for d in details if '💰' not in d and 'http' not in d])
194
-
195
- image_data = self.get_place_image(name)
196
-
197
- html += f"""
198
- <div class='card'>
199
- <div class='image-container'>
200
- <img src='{image_data['image'] if image_data else 'https://via.placeholder.com/400x200?text=Immagine+non+disponibile'}'
201
- class='place-image'
202
- alt='{name}'>
203
- {f"<div class='logo-badge'>📸 {image_data['credit']}</div>" if image_data else ''}
204
- </div>
205
- <div class='card-content'>
206
- <h3>{name}</h3>
207
- <p>{desc}</p>
208
- {f"<p class='price'>💰 {price}</p>" if price else ''}
209
- {f"<a href='{link}' class='link' target='_blank'>🌐 Sito Ufficiale</a>" if link else ''}
210
- </div>
211
- </div>
212
- """
213
- return html
214
 
215
  chat_manager = ChatManager()
216
 
@@ -222,23 +217,19 @@ def respond(message, chat_history):
222
  chat_manager.context["city"] = city
223
 
224
  if not chat_manager.context["city"]:
225
- bot_response = "🌍 Per favore dimmi di quale città vuoi informazioni!"
226
  chat_history[-1] = (message, bot_response)
227
  return "", chat_history
228
 
229
  prompt = f"""
230
- Sei un esperto guida turistica per {chat_manager.context["city"]}.
231
- Fornisci raccomandazioni in italiano con formato:
 
 
 
 
232
 
233
- ## Cose da Vedere
234
- - **Nome Luogo** • Descrizione breve (max 30 parole) 💰 Prezzo • https://link.com
235
- ## Dove Mangiare
236
- - **Nome Ristorante** • Specialità 💰 Fascia prezzi • https://link.com
237
- ## Dove Dormire
238
- - **Nome Alloggio** • Servizi offerti 💰 Prezzo per notte • https://link.com
239
- ## Itinerari
240
- - **1 Giorno**: Attrazioni principali
241
- - **2 Giorni**: Estensione itinerario
242
  """
243
 
244
  try:
@@ -260,30 +251,34 @@ with gr.Blocks(css=CSS) as app:
260
  gr.HTML("""
261
  <div class="header">
262
  <h1>
263
- <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
264
  <path d="M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0Z"/>
265
  <circle cx="12" cy="10" r="3"/>
266
  </svg>
267
- Travel Assistant PRO
268
  </h1>
269
- <p>La tua guida turistica intelligente</p>
270
  </div>
271
  """)
272
 
273
  chatbot = gr.Chatbot(
274
  label="Chat",
275
  elem_classes="chat-container",
276
- avatar_images=(None, "https://i.ibb.co/8XJp0dN/bot-avatar.png"),
277
- show_label=False
 
 
 
278
  )
279
 
280
- with gr.Row():
281
  msg = gr.Textbox(
282
- placeholder="✍️ Scrivi qui la tua richiesta...",
 
283
  show_label=False,
284
  container=False
285
  )
286
- with gr.Column(min_width=120):
287
  send_btn = gr.Button("Invia ➔", variant="primary")
288
  reset_btn = gr.Button("Nuova Ricerca", variant="secondary")
289
 
 
2
  import google.generativeai as genai
3
  import os
4
  import re
 
5
 
 
6
  GEMINI_KEY = os.getenv("GEMINI")
 
7
  genai.configure(api_key=GEMINI_KEY)
8
  model = genai.GenerativeModel('gemini-pro')
9
 
 
19
  body {
20
  background: var(--background);
21
  font-family: 'Segoe UI', system-ui, sans-serif;
22
+ min-height: 100vh;
23
  margin: 0;
24
  padding: 20px;
25
  }
 
62
  padding: 15px 20px;
63
  border-radius: 15px 15px 0 15px;
64
  margin: 10px 0 10px 30%;
65
+ position: relative;
66
  animation: slideIn 0.3s ease;
67
  }
68
 
 
72
  border-radius: 15px 15px 15px 0;
73
  margin: 10px 30% 10px 0;
74
  border: 1px solid #E0E0E0;
75
+ position: relative;
76
  animation: fadeIn 0.5s ease;
77
  }
78
 
79
+ .msg-bot::before {
80
+ content: "✈️";
81
+ position: absolute;
82
+ left: -35px;
83
+ top: 15px;
84
+ font-size: 1.5em;
85
+ }
86
+
87
  .card {
88
  background: white;
89
+ padding: 20px;
90
  border-radius: 12px;
91
  margin: 15px 0;
92
+ border-left: 4px solid var(--accent);
93
+ box-shadow: 0 3px 10px rgba(0,0,0,0.05);
94
  transition: transform 0.2s;
95
  }
96
 
 
98
  transform: translateY(-3px);
99
  }
100
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  .price {
102
  color: var(--primary);
103
  font-weight: 600;
 
105
  }
106
 
107
  .link {
108
+ color: var(--secondary) !important;
109
  text-decoration: none;
110
+ font-weight: 500;
111
  display: inline-flex;
112
  align-items: center;
113
  gap: 5px;
114
  }
115
 
116
+ .link:hover {
117
+ text-decoration: underline;
118
+ }
119
+
120
+ .controls {
121
+ padding: 20px;
122
+ background: #F8FAFD;
123
+ border-top: 1px solid #E0E0E0;
124
+ }
125
+
126
+ input[type="text"] {
127
+ border: 2px solid var(--secondary) !important;
128
+ border-radius: 12px !important;
129
+ padding: 15px !important;
130
+ font-size: 1em;
131
+ }
132
+
133
+ button {
134
+ background: var(--primary) !important;
135
+ color: white !important;
136
+ border: none !important;
137
+ padding: 12px 25px !important;
138
+ border-radius: 10px !important;
139
+ transition: all 0.2s !important;
140
+ }
141
+
142
+ button:hover {
143
+ transform: translateY(-2px);
144
+ box-shadow: 0 5px 15px rgba(42,92,130,0.2) !important;
145
+ }
146
+
147
  @keyframes slideIn {
148
  from { transform: translateX(20px); opacity: 0; }
149
  to { transform: translateX(0); opacity: 1; }
 
153
  from { opacity: 0; transform: translateY(10px); }
154
  to { opacity: 1; transform: translateY(0); }
155
  }
156
+
157
+ @media (max-width: 600px) {
158
+ .gradio-container {
159
+ border-radius: 0;
160
+ }
161
+
162
+ .msg-user, .msg-bot {
163
+ margin-left: 10%;
164
+ margin-right: 10%;
165
+ }
166
+ }
167
  """
168
 
169
  class ChatManager:
 
174
  cities = re.findall(r'\b(?:a|in|su|per)\s+([A-Z][a-z]+(?:\s+[A-Z][a-z]+)*)\b', text, re.IGNORECASE)
175
  return cities[0] if cities else self.context["city"]
176
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
  def format_response(self, response):
178
+ structured = []
179
+ current_item = {}
180
 
181
  lines = response.split('\n')
182
  for line in lines:
183
+ if line.startswith('- **'):
 
 
 
184
  parts = re.split(r'\*\*:?|\*', line)
185
  if len(parts) > 2:
186
+ key = parts[1].strip().lower()
187
+ value = parts[2].strip()
188
+ current_item[key] = value
189
+ elif line.strip() == '' and current_item:
190
+ structured.append(current_item)
191
+ current_item = {}
192
+
193
+ if current_item:
194
+ structured.append(current_item)
195
+
196
+ html = ""
197
+ for item in structured:
198
+ html += f"""
199
+ <div class='card'>
200
+ <h3 style="margin:0;color:var(--primary)">📍 {item.get('nome', '')}</h3>
201
+ <p style="margin:10px 0;color:var(--text)">{item.get('descrizione', '')}</p>
202
+ <div style="display:flex;gap:15px;align-items:center">
203
+ {f"<div class='price'>💰 {item.get('prezzo', '')}</div>" if item.get('prezzo') else ''}
204
+ {f"<a href='{item['link']}' class='link' target='_blank'><svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-external-link'><path d='M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6'></path><polyline points='15 3 21 3 21 9'></polyline><line x1='10' y1='14' x2='21' y2='3'></line></svg> Maggiori info</a>" if 'link' in item else ''}
205
+ </div>
206
+ </div>
207
+ """
208
+ return html if html else response
 
 
 
209
 
210
  chat_manager = ChatManager()
211
 
 
217
  chat_manager.context["city"] = city
218
 
219
  if not chat_manager.context["city"]:
220
+ bot_response = "🌍 Per favore dimmi prima di quale città vuoi informazioni!"
221
  chat_history[-1] = (message, bot_response)
222
  return "", chat_history
223
 
224
  prompt = f"""
225
+ Sei un assistente turistico esperto di {chat_manager.context["city"]}.
226
+ Fornisci informazioni strutturate in italiano con:
227
+ - Nome luogo
228
+ - Breve descrizione (max 30 parole)
229
+ - Prezzo (se applicabile)
230
+ - Link ufficiale (se disponibile)
231
 
232
+ Richiesta: {message}
 
 
 
 
 
 
 
 
233
  """
234
 
235
  try:
 
251
  gr.HTML("""
252
  <div class="header">
253
  <h1>
254
+ <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
255
  <path d="M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0Z"/>
256
  <circle cx="12" cy="10" r="3"/>
257
  </svg>
258
+ Travel Assistant AI
259
  </h1>
260
+ <p>La tua guida personale per scoprire il mondo</p>
261
  </div>
262
  """)
263
 
264
  chatbot = gr.Chatbot(
265
  label="Chat",
266
  elem_classes="chat-container",
267
+ show_label=False,
268
+ avatar_images=(
269
+ None,
270
+ "https://i.ibb.co/8XJp0dN/bot-avatar.png"
271
+ )
272
  )
273
 
274
+ with gr.Row(elem_classes="controls"):
275
  msg = gr.Textbox(
276
+ label="",
277
+ placeholder="Scrivi qui la tua richiesta...",
278
  show_label=False,
279
  container=False
280
  )
281
+ with gr.Column(min_width=100):
282
  send_btn = gr.Button("Invia ➔", variant="primary")
283
  reset_btn = gr.Button("Nuova Ricerca", variant="secondary")
284