| import gradio as gr |
| from huggingface_hub import InferenceClient |
|
|
| |
| def respond(message, history: list[dict[str, str]], hf_token: gr.OAuthToken): |
| client = InferenceClient(token=hf_token.token, model="openai/gpt-oss-20b") |
| system_message = """You are BitAI (or Bit for short), a friendly chatbot created by the user "Sal". |
| You respond politely, refuse inapropriate requests, and keep a friendly tone.""" |
| |
| messages = [{"role": "system", "content": system_message}] |
| messages.extend(history) |
| messages.append({"role": "user", "content": message}) |
|
|
| response = "" |
| for chunk in client.chat_completion(messages, max_tokens=512, stream=True, temperature=0.7, top_p=0.95): |
| token = chunk.choices[0].delta.content if chunk.choices and chunk.choices[0].delta.content else "" |
| response += token |
| yield response |
|
|
| |
| fade_js = """ |
| <script> |
| document.addEventListener("DOMContentLoaded", () => { |
| // Título animado |
| const grContainer = document.querySelector(".gradio-container"); |
| if(grContainer && !document.querySelector(".chat-title")) { |
| const titleContainer = document.createElement("div"); |
| titleContainer.className = "title-container"; |
| titleContainer.innerHTML = ` |
| <h1 class="chat-title">Talk with BitAI (W.I.P)</h1> |
| <p class="chat-subtitle">Still updating, might not know everything, filter slightly broken</p> |
| `; |
| grContainer.insertBefore(titleContainer, grContainer.firstChild); |
| setTimeout(()=>{titleContainer.style.opacity="1"; titleContainer.style.transform="translateY(0)";},100); |
| } |
| |
| // Startup message |
| const chatContainer = document.querySelector(".chat-interface"); |
| if(chatContainer && chatContainer.children.length===0){ |
| const startupMsg = document.createElement("div"); |
| startupMsg.className="startup-message"; |
| startupMsg.innerHTML='<div class="startup-content"><div class="startup-icon">🤖</div><div class="startup-text"><div class="startup-title">BitAI</div><div class="startup-subtitle">How was your day? Good morning!</div><div class="startup-note">Entirely AI</div></div></div>'; |
| chatContainer.appendChild(startupMsg); |
| setTimeout(()=>{startupMsg.style.opacity="1"; startupMsg.style.transform="translateY(0)";},100); |
| } |
| |
| // Observador para animação de novas mensagens |
| const observer = new MutationObserver(mutations => { |
| mutations.forEach(m => { |
| m.addedNodes.forEach(node => { |
| if(node.classList?.contains('chat-message')){ |
| const startupMsg = document.querySelector(".startup-message"); |
| if(startupMsg){startupMsg.style.opacity="0"; startupMsg.style.transform="translateY(20px)"; setTimeout(()=>startupMsg.remove(),300);} |
| node.style.opacity=0; node.style.transform='translateY(20px) scale(0.95)'; |
| node.style.transition='all 0.5s ease, box-shadow 0.3s ease'; |
| requestAnimationFrame(()=>{node.style.opacity=1; node.style.transform='translateY(0) scale(1)'; setTimeout(()=>node.style.boxShadow='0 4px 15px rgba(0,0,0,0.2)',500);}); |
| } |
| }); |
| }); |
| }); |
| if(chatContainer) observer.observe(chatContainer, {childList:true, subtree:true}); |
| |
| // Button dentro do textarea |
| const btn = document.querySelector(".send-btn"); |
| const txtCont = document.querySelector(".input-container"); |
| if(btn && txtCont){txtCont.style.position="relative"; btn.style.position="absolute"; btn.style.right="8px"; btn.style.top="8px"; btn.style.width="40px"; btn.style.height="40px";} |
| |
| // Função genérica de bounce |
| const bounce = el=>{ |
| if(!el || el.hasAttribute("data-bounce-added")) return; |
| el.setAttribute("data-bounce-added","true"); |
| el.addEventListener("click",()=>{el.style.transition="transform 0.5s cubic-bezier(0.68,-0.55,0.27,1.55)"; el.style.transform="scale(0.95)"; setTimeout(()=>el.style.transform="scale(1)",500);}); |
| }; |
| document.querySelectorAll("button, .chat-message, textarea, .gr-button.gr-login, .chat-title, .chat-subtitle, .startup-message, .gr-chatbot").forEach(bounce); |
| }); |
| </script> |
| """ |
|
|
| |
| with gr.Blocks(css=""" |
| body{background:#000;margin:0;padding:0;font-family:'Inter','Manrope','Outfit',sans-serif;} |
| .gradio-container{border-radius:20px;padding:20px;max-width:700px;margin:30px auto;background:linear-gradient(145deg,#0f0f0f,#1a1a1a);box-shadow:0 10px 30px rgba(0,0,0,0.4);transition:transform 0.3s;} |
| .title-container{opacity:0;transform:translateY(-20px);transition:0.5s;} |
| .chat-title{font-size:32px;color:#fff;margin-bottom:10px;cursor:pointer;transition:0.3s;} |
| .chat-subtitle{font-size:14px;color:#aaa;cursor:pointer;transition:0.3s;} |
| textarea{border:none;border-radius:25px;padding:14px 20px 14px 20px;background:#1a1a1a;color:#fff;font-size:16px;width:100%;height:56px;transition:0.3s;} |
| .send-btn{border:none;border-radius:20px;background:linear-gradient(145deg,#555,#444);color:#fff;width:40px;height:40px;position:absolute;right:8px;top:8px;cursor:pointer;transition:0.3s;} |
| .chat-message{border-radius:20px;padding:16px;margin:12px 0;opacity:0;transition:0.5s;box-shadow:0 4px 10px rgba(0,0,0,0.15);} |
| .chat-message.user{background:linear-gradient(145deg,#252525,#1f1f1f);color:#fff;align-items:flex-end;margin-left:40px;} |
| .chat-message.bot{background:linear-gradient(145deg,#2e2e2e,#252525);color:#fff;align-items:flex-start;margin-right:40px;} |
| .startup-message{opacity:0;transform:translateY(20px);transition:0.5s;margin:20px 0;display:flex;justify-content:center;cursor:pointer;} |
| .startup-content{display:flex;align-items:center;background:linear-gradient(145deg,#2e2e2e,#252525);padding:20px;border-radius:20px;box-shadow:0 4px 15px rgba(0,0,0,0.2);} |
| .startup-icon{font-size:32px;margin-right:15px;} |
| .startup-title{font-weight:600;font-size:18px;color:#fff;} |
| .startup-subtitle{font-size:16px;color:#eee;} |
| .startup-note{font-size:12px;color:#aaa;font-style:italic;} |
| """) as demo: |
|
|
| gr.LoginButton() |
| gr.ChatInterface(respond, type="messages") |
| gr.HTML(fade_js) |
|
|
| if __name__ == "__main__": |
| demo.launch() |