| import os, json, uuid |
| import gradio as gr |
| from huggingface_hub import InferenceClient |
|
|
| def get_user_file(user_id): |
| return f"bitai_user_{user_id}.json" |
|
|
| def load_saved_chats(user_id): |
| path = get_user_file(user_id) |
| if os.path.exists(path): |
| with open(path, "r", encoding="utf-8") as f: |
| return json.load(f) |
| return [] |
|
|
| def save_chats(user_id, chats): |
| path = get_user_file(user_id) |
| with open(path, "w", encoding="utf-8") as f: |
| json.dump(chats, f, ensure_ascii=False, indent=2) |
|
|
| def respond(message, history, saved_chats, user_id): |
| client = InferenceClient(token=os.environ["HF_TOKEN"], model="openai/gpt-oss-20b") |
|
|
| system = { |
| "role": "system", |
| "content": "You are BitAI (V1), a friendly chatbot created by Sal." |
| } |
|
|
| messages = [system] + history + [{"role": "user", "content": message}] |
| response = "" |
| for msg in client.chat_completion(messages, max_tokens=2048, stream=True): |
| token = msg.choices[0].delta.content if msg.choices and msg.choices[0].delta else "" |
| response += token |
| yield response |
|
|
| |
| history.append({"role": "user", "content": message}) |
| history.append({"role": "assistant", "content": response}) |
| saved_chats[-1] = history |
| save_chats(user_id, saved_chats) |
|
|
| def new_chat(saved_chats, user_id): |
| saved_chats.append([]) |
| save_chats(user_id, saved_chats) |
| return saved_chats, len(saved_chats) - 1, [] |
|
|
| def select_chat(index, saved_chats): |
| if 0 <= index < len(saved_chats): |
| return saved_chats[index], f"Chat {index+1} carregado!" |
| return [], "Índice inválido." |
|
|
| def start_session(username): |
| user_id = username.strip().replace(" ", "_") if username else str(uuid.uuid4())[:8] |
| chats = load_saved_chats(user_id) |
| if not chats: |
| chats = [[]] |
| save_chats(user_id, chats) |
| return user_id, chats, len(chats) - 1 |
|
|
| def render_sidebar(saved_chats): |
| html = "<div class='chatlist'>" |
| for i, chat in enumerate(saved_chats): |
| preview = "" |
| for m in chat: |
| if m["role"] == "user": |
| preview = m["content"][:35] + ("..." if len(m["content"]) > 35 else "") |
| break |
| html += f""" |
| <div class='chat-item' onclick="selectChat({i})"> |
| <div class='chat-title'>💬 Chat {i+1}</div> |
| <div class='chat-preview'>{preview or 'Conversa vazia'}</div> |
| </div> |
| """ |
| html += "</div>" |
| return html |
|
|
| with gr.Blocks(css=""" |
| /* Sidebar com vibes modernas */ |
| body, .gr-blocks { |
| background-color: #1a1a1a !important; |
| color: white; |
| } |
| .sidebar { |
| background-color: #111; |
| padding: 10px; |
| border-right: 2px solid #333; |
| height: 100vh; |
| overflow-y: auto; |
| } |
| .chatlist { display: flex; flex-direction: column; gap: 8px; } |
| .chat-item { |
| background: #222; |
| border-radius: 10px; |
| padding: 10px; |
| cursor: pointer; |
| transition: all 0.2s ease; |
| } |
| .chat-item:hover { background: #333; transform: scale(1.02); } |
| .chat-title { font-weight: bold; color: #fff; } |
| .chat-preview { font-size: 13px; color: #bbb; margin-top: 2px; } |
| """) as demo: |
|
|
| user_id = gr.State("") |
| saved_chats = gr.State([]) |
| current_index = gr.State(0) |
|
|
| with gr.Row(): |
| with gr.Column(scale=0.3, elem_classes="sidebar"): |
| sidebar_html = gr.HTML("<div class='chatlist'></div>") |
| new_chat_btn = gr.Button("➕ Novo Chat") |
|
|
| with gr.Column(scale=1): |
| gr.HTML("<h2 style='text-align:center;'>BitAI</h2>") |
| chatbot = gr.ChatInterface(fn=respond, type="messages", additional_inputs=[saved_chats, user_id]) |
| status = gr.Textbox(label="Status", interactive=False) |
|
|
| username_input = gr.Textbox(label="Seu nome (opcional):") |
| start_btn = gr.Button("Iniciar Chat") |
|
|
| start_btn.click(start_session, username_input, [user_id, saved_chats, current_index]).then( |
| render_sidebar, saved_chats, sidebar_html |
| ) |
|
|
| new_chat_btn.click(new_chat, [saved_chats, user_id], [saved_chats, current_index, chatbot.chatbot]).then( |
| render_sidebar, saved_chats, sidebar_html |
| ) |
|
|
| if __name__ == "__main__": |
| demo.launch() |