File size: 8,283 Bytes
78c4391
 
 
 
 
 
 
 
 
 
d558678
cbc0e8f
 
 
 
 
d558678
 
 
cbc0e8f
d558678
fd29f08
d558678
cbc0e8f
d558678
 
 
 
 
cbc0e8f
 
 
 
d558678
 
 
cbc0e8f
 
d558678
cbc0e8f
d558678
 
 
 
 
cbc0e8f
d558678
 
 
cbc0e8f
f815c2a
 
 
 
 
 
 
 
 
 
d558678
 
 
cbc0e8f
 
d558678
 
 
 
cbc0e8f
d558678
cbc0e8f
d558678
cbc0e8f
fd29f08
cbc0e8f
d558678
 
 
cbc0e8f
 
d558678
cbc0e8f
 
fd29f08
cbc0e8f
 
 
2a3d435
 
 
 
 
 
fd29f08
 
d558678
cbc0e8f
fd29f08
d558678
 
fd29f08
 
cbc0e8f
d558678
 
2a3d435
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fd29f08
 
2a3d435
 
 
 
 
fd29f08
 
2a3d435
 
 
 
 
 
 
 
 
 
 
 
fd29f08
 
cbc0e8f
 
 
 
 
 
 
 
d558678
fd29f08
2a3d435
 
 
 
 
 
 
 
 
 
78c4391
 
3397992
 
 
ccba573
3397992
 
 
ccba573
3397992
fd29f08
 
ccba573
3397992
 
fd29f08
3397992
 
fd29f08
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2a3d435
fd29f08
 
 
 
3397992
 
 
6338c59
2a3d435
6338c59
3397992
 
 
 
 
fd29f08
2a3d435
6338c59
3397992
 
fd29f08
 
 
 
 
 
3397992
fd29f08
3397992
 
 
 
6338c59
3397992
6338c59
 
2a3d435
6338c59
3397992
e140e61
3397992
d558678
e140e61
78c4391
ccba573
d558678
 
 
 
fd29f08
cbc0e8f
 
d558678
fd29f08
d558678
fd29f08
d558678
 
 
 
 
 
fd29f08
2a3d435
 
 
 
a621e3d
d558678
 
fd29f08
d558678
fd29f08
 
d558678
cbc0e8f
d558678
fd29f08
cbc0e8f
 
d558678
6338c59
e140e61
cbc0e8f
 
6338c59
 
e140e61
cbc0e8f
 
6338c59
 
e140e61
cbc0e8f
 
6338c59
78c4391
ccba573
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
import gradio as gr
import google.generativeai as genai
import os
import re

GEMINI_KEY = os.getenv("GEMINI")
genai.configure(api_key=GEMINI_KEY)
model = genai.GenerativeModel('gemini-pro')

CSS = """
:root {
    --primary: #2A5C82;
    --secondary: #5BA4E6;
    --accent: #FF7F50;
    --background: #F8F9FA;
    --text: #2C3E50;
}

body {
    background: var(--background);
    font-family: 'Segoe UI', system-ui, sans-serif;
    min-height: 100vh;
    margin: 0;
    padding: 20px;
}

.gradio-container {
    max-width: 800px;
    margin: 0 auto;
    background: white;
    border-radius: 20px;
    box-shadow: 0 10px 30px rgba(0,0,0,0.1);
    overflow: hidden;
}

.header {
    background: var(--primary);
    padding: 40px 20px;
    text-align: center;
    color: white;
}

.header h1 {
    margin: 0;
    font-size: 2.2em;
    font-weight: 700;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    color:#fff;
    
}

.header h1 svg{
    color:#fff;
}

.header h1 p {
    color: #fff;
}

.chat-container {
    padding: 20px;
    height: 60vh;
    overflow-y: auto;
}

.msg-user {
    background: var(--secondary);
    color: white;
    padding: 15px 20px;
    border-radius: 15px 15px 0 15px;
    margin: 10px 0 10px 30%;
    position: relative;
    animation: slideIn 0.3s ease;
}

.msg-bot {
    background: white;
    padding: 20px;
    border-radius: 15px 15px 15px 0;
    margin: 10px 30% 10px 0;
    border: 1px solid #E0E0E0;
    position: relative;
    animation: fadeIn 0.5s ease;
}

.msg-bot::before {
    content: "✈️";
    position: absolute;
    left: -35px;
    top: 15px;
    font-size: 1.5em;
}

.card {
    background: white;
    padding: 20px;
    border-radius: 12px;
    margin: 15px 0;
    border-left: 4px solid var(--accent);
    box-shadow: 0 3px 10px rgba(0,0,0,0.05);
    transition: transform 0.2s;
}

.card:hover {
    transform: translateY(-3px);
}

.price {
    color: var(--primary);
    font-weight: 600;
    margin: 8px 0;
}

.link {
    color: var(--secondary) !important;
    text-decoration: none;
    font-weight: 500;
    display: inline-flex;
    align-items: center;
    gap: 5px;
}

.link:hover {
    text-decoration: underline;
}

.controls {
    padding: 20px;
    background: #F8FAFD;
    border-top: 1px solid #E0E0E0;
}

input[type="text"] {
    border: 2px solid var(--secondary) !important;
    border-radius: 12px !important;
    padding: 15px !important;
    font-size: 1em;
}

button {
    background: var(--primary) !important;
    color: white !important;
    border: none !important;
    padding: 12px 25px !important;
    border-radius: 10px !important;
    transition: all 0.2s !important;
}

button:hover {
    transform: translateY(-2px);
    box-shadow: 0 5px 15px rgba(42,92,130,0.2) !important;
}

@keyframes slideIn {
    from { transform: translateX(20px); opacity: 0; }
    to { transform: translateX(0); opacity: 1; }
}

@keyframes fadeIn {
    from { opacity: 0; transform: translateY(10px); }
    to { opacity: 1; transform: translateY(0); }
}

@media (max-width: 600px) {
    .gradio-container {
        border-radius: 0;
    }
    
    .msg-user, .msg-bot {
        margin-left: 10%;
        margin-right: 10%;
    }
}
"""

class ChatManager:
    def __init__(self):
        self.context = {"city": None}
    
    def extract_city(self, text):
        cities = re.findall(r'\b(?:a|in|su|per)\s+([A-Z][a-z]+(?:\s+[A-Z][a-z]+)*)\b', text, re.IGNORECASE)
        return cities[0] if cities else self.context["city"]
    
    def format_response(self, response):
        structured = []
        current_item = {}
        
        lines = response.split('\n')
        for line in lines:
            if line.startswith('- **'):
                parts = re.split(r'\*\*:?|\*', line)
                if len(parts) > 2:
                    key = parts[1].strip().lower()
                    value = parts[2].strip()
                    current_item[key] = value
            elif line.strip() == '' and current_item:
                structured.append(current_item)
                current_item = {}
        
        if current_item:
            structured.append(current_item)
        
        html = ""
        for item in structured:
            html += f"""
            <div class='card'>
                <h3 style="margin:0;color:var(--primary)">📍 {item.get('nome', '')}</h3>
                <p style="margin:10px 0;color:var(--text)">{item.get('descrizione', '')}</p>
                <div style="display:flex;gap:15px;align-items:center">
                    {f"<div class='price'>💰 {item.get('prezzo', '')}</div>" if item.get('prezzo') else ''}
                    {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 ''}
                </div>
            </div>
            """
        return html if html else response

chat_manager = ChatManager()

def respond(message, chat_history):
    chat_history.append((message, ""))
    
    city = chat_manager.extract_city(message)
    if city:
        chat_manager.context["city"] = city
    
    if not chat_manager.context["city"]:
        bot_response = "🌍 Per favore dimmi prima di quale città vuoi informazioni!"
        chat_history[-1] = (message, bot_response)
        return "", chat_history
    
    prompt = f"""
    Sei un assistente turistico esperto di {chat_manager.context["city"]}. 
    Fornisci informazioni strutturate in italiano con:
    - Nome luogo
    - Breve descrizione (max 30 parole)
    - Prezzo (se applicabile)
    - Link ufficiale (se disponibile)
    
    Richiesta: {message}
    """
    
    try:
        response = model.generate_content(prompt).text
        bot_response = chat_manager.format_response(response)
    except Exception as e:
        bot_response = f"⚠️ Errore: {str(e)}"
    
    chat_history[-1] = (message, bot_response)
    return "", chat_history

def reset_context(chat_history):
    chat_manager.context = {"city": None}
    new_history = chat_history + [[None, "🔄 Contesto resettato! Di quale città vuoi parlare ora?"]]
    return new_history

with gr.Blocks(css=CSS) as app:
    with gr.Column():
        gr.HTML("""
            <div class="header">
                <h1>
                    <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">
                        <path d="M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0Z"/>
                        <circle cx="12" cy="10" r="3"/>
                    </svg>
                    Travel Assistant AI
                </h1>
                <p>La tua guida personale per scoprire il mondo</p>
            </div>
        """)
        
        chatbot = gr.Chatbot(
            label="Chat",
            elem_classes="chat-container",
            show_label=False,
            avatar_images=(
                None, 
                "https://i.ibb.co/8XJp0dN/bot-avatar.png"  # Sostituisci con il tuo URL
            ),
            value=[[None, "Ciao sono la tua guida turistica, come posso aiutarti? 🌍"]]
        )
        
        with gr.Row(elem_classes="controls"):
            msg = gr.Textbox(
                label="",
                placeholder="Scrivi qui la tua richiesta...",
                show_label=False,
                container=False
            )
            with gr.Column(min_width=100):
                send_btn = gr.Button("Invia ➔", variant="primary")
                reset_btn = gr.Button("Nuova Ricerca", variant="secondary")

    send_btn.click(
        respond,
        [msg, chatbot],
        [msg, chatbot]
    )
    reset_btn.click(
        reset_context,
        [chatbot],
        [chatbot]
    )
    msg.submit(
        respond,
        [msg, chatbot],
        [msg, chatbot]
    )

app.launch()