Spaces:
Sleeping
Sleeping
| import os | |
| import json | |
| import gradio as gr | |
| from dotenv import load_dotenv | |
| import openai | |
| from datetime import datetime | |
| load_dotenv() | |
| client = openai.OpenAI( | |
| api_key=os.environ.get("GROQ_API_KEY"), | |
| base_url="https://api.groq.com/openai/v1" | |
| ) | |
| # Hotel information | |
| HOTEL_INFO = """ | |
| **AP Hotel & Resorts** | |
| ☎️ **Contacto**: +351 289 540 100 (Chamada para a Rede Fixa Nacional) | book@ap-hotelsresorts.com | |
| ** Hoteis do grupo **: | |
| - AP Adriana Beach Resort | Albufeira | |
| - AP Cabanas Beach & Nature | Tavira | |
| - AP Eva Senses | Faro | |
| - AP Oriental Beach | Portimão | |
| - AP Maria Nova Lounge | Tavira | |
| - AP Victoria Sports & Beach | Albufeira | |
| - AP Dona Aninhas | Viana do Castelo | |
| - AP Lago Montargil Conference & Spa | Montargil | |
| - AP Sines | Costa Alentejana | |
| 🏨 **Quartos Disponiveis**: | |
| - Quantidade: 1, 2 ou 3. | |
| - Quantos Adultos: 1 a 8. | |
| - Quantas Crianças: 0 a 4. | |
| - Quantos Bébes: 0 a 2. | |
| **Tipos de Reservas** | |
| - Alojamento e Pequeno Almoço. | |
| - Meia-Pensão (includes Breakfast and Dinner (Beverages not included). Set Menu (Chef's Suggestion)). | |
| - Pensão-Completa (includes breakfast, lunch, and dinner (drinks not included). Set Menu (chef's suggestion)). | |
| 🎯 **Serviços e Comodidades**: | |
| - Wi-Fi grátis em todo o resort | |
| - 2 piscinas exteriores para adultos | |
| - 2 piscinas exteriores para crianças | |
| - Acesso direto à praia | |
| - Campo de futebol, basquete, tiro ao arco, vólei de praia, ténis, minigolfe e petanca. | |
| - Ginásio com acesso grátis | |
| - SPA | |
| - Música ao vivo | |
| - Espetáculos | |
| - Clube infantil | |
| ⏰ **Horários**: | |
| - Check-in: 14:00 | |
| - Check-out: 11h00 | |
| - Pequeno-almoço: 07:30 - 10:30 | |
| 🐕 **Animais**: Aceites em certas áreas de alguns hóteis. | |
| """ | |
| CHATBOT_SYSTEM_PROMPT = f""" | |
| És um agente de Inteligência Aritificial do grupo AP Hotels & Resorts, especializado em ajudar clientes com informações e reservas. | |
| *** Princípio Fundamental e Regras Inquebráveis *** | |
| 1. **REGRA DE OURO: A tua única fonte de verdade é a "Informação do Hotel" fornecida abaixo de `Informação do Hotw`.** És estritamente e absolutamente proibido de usar qualquer conhecimento externo ou pré-treinado. | |
| 2. **SE A RESPOSTA NÃO ESTIVER NO CONTEXTO:** Se um cliente perguntar algo cuja resposta não se encontra explicitamente no `{{context}}`, a tua resposta OBRIGATÓRIA deve ser uma variação de: "Peço desculpa, mas não encontro essa informação específica nos detalhes que tenho sobre o hotel. Posso ajudar com outra questão, talvez sobre os nossos tipos de quarto ou regimes de pensão?" Nunca inventes ou faças suposições. | |
| 3. **NÃO FAÇAS PROMESSSAS EXTERNAS:** Não menciones serviços, parcerias ou eventos que não estejam listados no `{{context}}`. | |
| **Informação do Hotel**: | |
| {{context}} | |
| **Tuas Responsabilidades**: | |
| 1. Responder a perguntas sobre o hotel, serviços, etc. | |
| 2. Ajudar com simulações de reservas (sempre confirma disponibilidade primeiro) | |
| 3. Fornecer informações sobre preços, tipos de quartos e comodidades | |
| 4. Ser cordial, profissional e prestável em português europeu | |
| 5. Se não souberes algo, admite e oferece alternativas | |
| **Formato de Conversação**: | |
| - Usa emojis ocasionalmente para tornar a conversa mais amigável | |
| - Quando falares de reservas, pede sempre: datas, tipo de quarto, número de pessoas | |
| - Se o cliente quiser fazer uma reserva, recolhe TODOS os dados necessários antes de confirmar: | |
| * Nome completo | |
| * Telefone | |
| * NIF ou CC | |
| * Hotel | |
| * Data check-in e check-out | |
| * Numero de noites | |
| * Quantas pessoas | |
| * Número de crianças | |
| * Pedidos especiais (opcional) | |
| * Board (esclarecendo sempre os 3 tipos de board) | |
| **Regras Importantes**: | |
| - Baseia as tuas respostas no contexto fornecido acima | |
| - Se perguntarem sobre disponibilidade, simula uma verificação e confirma (para fins de demo) | |
| - Calcula automaticamente o número de noites e o preço total | |
| - A data atual é {datetime.now().strftime('%d de %B de %Y')} | |
| - Oferece sempre upgrades ou serviços adicionais quando relevante | |
| **Tom**: Profissional, caloroso, prestável e eficiente. | |
| """ | |
| WELCOME_MESSAGE = """👋 **Bem-vindo ao AP Hotel & Resorts!** | |
| Sou o teu agente de IA e estou aqui para ajudar-te com: | |
| • 🏨 Informações sobre quartos e preços | |
| • 📅 Disponibilidade e reservas | |
| • 🍽️ Restaurantes e serviços | |
| • 🎯 Comodidades do hotel | |
| **Como posso ajudá-lo hoje?**""" | |
| def chat_with_bot(message, history): | |
| """Process chat messages with the hotel bot""" | |
| if not message or message.strip() == "": | |
| return history | |
| try: | |
| context = HOTEL_INFO | |
| # Build conversation history | |
| messages = [ | |
| {"role": "system", "content": CHATBOT_SYSTEM_PROMPT.format(context=context)} | |
| ] | |
| # Add chat history (skip the welcome message) | |
| for user_msg, assistant_msg in history: | |
| if user_msg: | |
| messages.append({"role": "user", "content": user_msg}) | |
| messages.append({"role": "assistant", "content": assistant_msg}) | |
| # Add current message | |
| messages.append({"role": "user", "content": message}) | |
| # Get response from LLM | |
| response = client.chat.completions.create( | |
| model="openai/gpt-oss-120b", | |
| messages=messages, | |
| temperature=0.4, | |
| max_tokens=1000 | |
| ) | |
| bot_response = response.choices[0].message.content | |
| history.append((message, bot_response)) | |
| return history | |
| except Exception as e: | |
| error_message = f"❌ Desculpe, ocorreu um erro: {str(e)}" | |
| history.append((message, error_message)) | |
| return history | |
| # Create Gradio Interface | |
| with gr.Blocks( | |
| theme=gr.themes.Ocean(), | |
| title="AP Hotel & Resorts", | |
| css=""" | |
| .main-title {text-align: center; margin-bottom: 0.5rem;} | |
| .subtitle { | |
| text-align: center; | |
| margin: 0 auto 0 auto; | |
| font-size: 1.05em; | |
| line-height: 1.6; | |
| width: 80%; | |
| color: var(--body-text-color); | |
| } | |
| .logo-image { | |
| margin: 0 auto; | |
| display: block; | |
| } | |
| .chatbot-container { | |
| border-radius: 12px; | |
| } | |
| .info-box { | |
| background-color: #f0f4f8; | |
| padding: 1.5rem; | |
| border-radius: 12px; | |
| border-left: 4px solid #8b7a77; | |
| margin: 1rem 0; | |
| } | |
| .info-box h3 { | |
| margin-top: 0; | |
| color: #8b7a77; | |
| } | |
| /* Custom button colors - More specific selectors */ | |
| .primary, button.primary, .gradio-button.primary { | |
| background: #8b7a77 !important; | |
| background-color: #8b7a77 !important; | |
| border-color: #8b7a77 !important; | |
| color: white !important; | |
| } | |
| .primary:hover, button.primary:hover, .gradio-button.primary:hover { | |
| background: #7a6966 !important; | |
| background-color: #7a6966 !important; | |
| border-color: #7a6966 !important; | |
| } | |
| .secondary, button.secondary, .gradio-button.secondary { | |
| background: #8b7a77 !important; | |
| background-color: #8b7a77 !important; | |
| border-color: #8b7a77 !important; | |
| color: white !important; | |
| } | |
| .secondary:hover, button.secondary:hover, .gradio-button.secondary:hover { | |
| background: #7a6966 !important; | |
| background-color: #7a6966 !important; | |
| border-color: #7a6966 !important; | |
| } | |
| /* Targeting all buttons to ensure color change */ | |
| button[variant="primary"] { | |
| background: #8b7a77 !important; | |
| background-color: #8b7a77 !important; | |
| border-color: #8b7a77 !important; | |
| } | |
| button[variant="secondary"] { | |
| background: #8b7a77 !important; | |
| background-color: #8b7a77 !important; | |
| border-color: #8b7a77 !important; | |
| } | |
| """ | |
| ) as demo: | |
| # Header | |
| logo = gr.Image( | |
| value="logo-ap.png", | |
| label="", | |
| show_label=False, | |
| show_download_button=False, | |
| show_share_button=False, | |
| show_fullscreen_button=False, | |
| height=100, | |
| container=False, | |
| elem_classes="logo-image" | |
| ) | |
| gr.Markdown("# 🏨 Agente de IA", elem_classes="main-title") | |
| gr.Markdown( | |
| """ | |
| Este é um agente de inteligência artificial demonstrativo. Ele utiliza a informação disponível sobre o hotel para responder de forma inteligente às tuas perguntas. Testa as suas capacidades! | |
| """, | |
| elem_classes="subtitle" | |
| ) | |
| # Tabs | |
| with gr.Tabs(): | |
| # Tab 1: Chatbot | |
| with gr.Tab("💬 Agente de IA"): | |
| chatbot = gr.Chatbot( | |
| height=500, | |
| label="", | |
| avatar_images=("logo-ap.png", "logo-ap.png"), | |
| type="tuples", | |
| elem_classes="chatbot-container", | |
| show_label=False, | |
| value=[(None, WELCOME_MESSAGE)] | |
| ) | |
| with gr.Row(): | |
| msg = gr.Textbox( | |
| placeholder="💬 Escreve a tua mensagem aqui...", | |
| show_label=False, | |
| scale=9, | |
| container=False | |
| ) | |
| submit_btn = gr.Button("Enviar", variant="primary", scale=1, elem_classes="primary") | |
| clear_btn = gr.Button("🗑️ Limpar Conversa", variant="secondary", size="sm", elem_classes="secondary") | |
| # Tab 2: Hotel Information | |
| with gr.Tab("📋 Informações do Hotel"): | |
| gr.Markdown("## 🏨 AP Hotel & Resorts") | |
| gr.Markdown(""" | |
| <div class="info-box"> | |
| <h3>📍 Contacto</h3> | |
| <p><strong>Telefone:</strong> +351 289 540 100 (Chamada para a Rede Fixa Nacional)</p> | |
| <p><strong>Email:</strong> book@ap-hotelsresorts.com</p> | |
| </div> | |
| """) | |
| gr.Markdown(""" | |
| <div class="info-box"> | |
| <h3>🏨 Hotéis do Grupo</h3> | |
| <p>🏖️ <strong>AP Adriana Beach Resort</strong> | Albufeira</p> | |
| <p>🌿 <strong>AP Cabanas Beach & Nature</strong> | Tavira</p> | |
| <p>✨ <strong>AP Eva Senses</strong> | Faro</p> | |
| <p>🌊 <strong>AP Oriental Beach</strong> | Portimão</p> | |
| <p>🎭 <strong>AP Maria Nova Lounge</strong> | Tavira</p> | |
| <p>⚽ <strong>AP Victoria Sports & Beach</strong> | Albufeira</p> | |
| <p>🏰 <strong>AP Dona Aninhas</strong> | Viana do Castelo</p> | |
| <p>🏞️ <strong>AP Lago Montargil Conference & Spa</strong> | Montargil</p> | |
| <p>🌅 <strong>AP Sines</strong> | Costa Alentejana</p> | |
| </div> | |
| """) | |
| gr.Markdown(""" | |
| <div class="info-box"> | |
| <h3>🏨 Quartos Disponíveis</h3> | |
| <p><strong>Quantidade:</strong> 1, 2 ou 3 quartos</p> | |
| <p><strong>Adultos:</strong> 1 a 8 pessoas</p> | |
| <p><strong>Crianças:</strong> 0 a 4</p> | |
| <p><strong>Bebés:</strong> 0 a 2</p> | |
| </div> | |
| """) | |
| gr.Markdown(""" | |
| <div class="info-box"> | |
| <h3>🍽️ Tipos de Reservas</h3> | |
| <p>🛏️ <strong>Alojamento e Pequeno Almoço</strong><br> | |
| <small>Inclui pequeno-almoço</small></p> | |
| <p>🍴 <strong>Meia Pensão</strong><br> | |
| <small>Inclui pequeno-almoço e jantar (bebidas não incluídas). Menu do dia (sugestão do Chef)</small></p> | |
| <p>🍽️ <strong>Pensão Completa</strong><br> | |
| <small>Inclui pequeno-almoço, almoço e jantar (bebidas não incluídas). Menu do dia (sugestão do Chef)</small></p> | |
| </div> | |
| """) | |
| gr.Markdown(""" | |
| <div class="info-box"> | |
| <h3>⏰ Horários</h3> | |
| <p><strong>Check-in:</strong> 14:00</p> | |
| <p><strong>Check-out:</strong> 11:00</p> | |
| <p><strong>Pequeno-almoço:</strong> 07:30 - 10:30</p> | |
| </div> | |
| """) | |
| gr.Markdown(""" | |
| <div class="info-box"> | |
| <h3>🎯 Serviços e Comodidades</h3> | |
| <p>📶 Wi-Fi grátis em todo o resort</p> | |
| <p>🏊 2 piscinas exteriores para adultos</p> | |
| <p>👶 2 piscinas exteriores para crianças</p> | |
| <p>🏖️ Acesso direto à praia</p> | |
| <p>⚽ Campo de futebol</p> | |
| <p>🏀 Basquete</p> | |
| <p>🎯 Tiro ao arco</p> | |
| <p>🏐 Vólei de praia</p> | |
| <p>🎾 Ténis</p> | |
| <p>⛳ Minigolfe</p> | |
| <p>🎱 Petanca</p> | |
| <p>🏋️ Ginásio com acesso grátis</p> | |
| <p>💆 SPA</p> | |
| <p>🎵 Música ao vivo</p> | |
| <p>🎭 Espetáculos</p> | |
| <p>🎪 Clube infantil</p> | |
| </div> | |
| """) | |
| gr.Markdown(""" | |
| <div class="info-box"> | |
| <h3>🐕 Política de Animais</h3> | |
| <p>Animais aceites em certas áreas de alguns hotéis.</p> | |
| <p><small>* Consulte condições específicas na receção do hotel escolhido</small></p> | |
| </div> | |
| """) | |
| gr.HTML(""" | |
| <div style=' | |
| text-align:center; | |
| padding:20px; | |
| margin-top:30px; | |
| font-size:0.95em; | |
| color:#555; | |
| border-top:1px solid #ddd; | |
| '> | |
| © 2025 Timestamp — Todos os direitos reservados | |
| </div> | |
| """) | |
| # Event handlers - Submit message | |
| msg.submit( | |
| chat_with_bot, | |
| [msg, chatbot], | |
| [chatbot] | |
| ).then( | |
| lambda: "", | |
| None, | |
| [msg] | |
| ) | |
| submit_btn.click( | |
| chat_with_bot, | |
| [msg, chatbot], | |
| [chatbot] | |
| ).then( | |
| lambda: "", | |
| None, | |
| [msg] | |
| ) | |
| clear_btn.click(lambda: [(None, WELCOME_MESSAGE)], None, chatbot) | |
| if __name__ == "__main__": | |
| demo.launch(share=True) |