import gradio as gr from openai import OpenAI import time import html def predict(message, history, character, api_key, progress=gr.Progress()): client = OpenAI(api_key=api_key) history_openai_format = [] for human, assistant in history: history_openai_format.append({"role": "user", "content": human}) history_openai_format.append({"role": "assistant", "content": assistant}) history_openai_format.append({"role": "user", "content": message}) response = client.chat.completions.create( model='gpt-4', messages=history_openai_format, temperature=1.0, stream=True ) partial_message = "" for chunk in progress.tqdm(response, desc="Generating"): if chunk.choices[0].delta.content: partial_message += chunk.choices[0].delta.content yield partial_message time.sleep(0.01) def format_history(history): html_content = "" for human, ai in history: human_formatted = html.escape(human).replace('\n', '
') html_content += f'
You: {human_formatted}
' if ai: ai_formatted = html.escape(ai).replace('\n', '
') html_content += f'
AI: {ai_formatted}
' return html_content css = """ #chat-display { height: 600px; overflow-y: auto; border: 1px solid #ccc; padding: 10px; margin-bottom: 10px; } #chat-display::-webkit-scrollbar { width: 10px; } #chat-display::-webkit-scrollbar-track { background: #f1f1f1; } #chat-display::-webkit-scrollbar-thumb { background: #888; } #chat-display::-webkit-scrollbar-thumb:hover { background: #555; } .message { margin-bottom: 10px; word-wrap: break-word; overflow-wrap: break-word; } .user-message, .ai-message { padding: 5px; border-radius: 5px; max-height: 300px; overflow-y: auto; } .user-message { background-color: #e6f3ff; } .ai-message { background-color: #f0f0f0; } .user-message::-webkit-scrollbar, .ai-message::-webkit-scrollbar { width: 5px; } .user-message::-webkit-scrollbar-thumb, .ai-message::-webkit-scrollbar-thumb { background: #888; } """ js = """ let lastScrollTop = 0; let isNearBottom = true; function updateScroll() { const chatDisplay = document.getElementById('chat-display'); if (!chatDisplay) return; const currentScrollTop = chatDisplay.scrollTop; const scrollHeight = chatDisplay.scrollHeight; const clientHeight = chatDisplay.clientHeight; // Check if user was near bottom before update isNearBottom = (currentScrollTop + clientHeight >= scrollHeight - 50); if (isNearBottom) { chatDisplay.scrollTop = scrollHeight; } else { chatDisplay.scrollTop = lastScrollTop; } lastScrollTop = chatDisplay.scrollTop; } // Set up a MutationObserver to watch for changes in the chat display const observer = new MutationObserver(updateScroll); const config = { childList: true, subtree: true }; // Start observing the chat display for configured mutations document.addEventListener('DOMContentLoaded', (event) => { const chatDisplay = document.getElementById('chat-display'); if (chatDisplay) { observer.observe(chatDisplay, config); // Also update scroll on manual scroll chatDisplay.addEventListener('scroll', function() { lastScrollTop = chatDisplay.scrollTop; isNearBottom = (chatDisplay.scrollTop + chatDisplay.clientHeight >= chatDisplay.scrollHeight - 50); }); } // Add event listener for Enter key const textbox = document.querySelector('#component-13 input'); // Update this selector if needed if (textbox) { textbox.addEventListener('keydown', function(e) { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); document.querySelector('#component-13 button').click(); } }); } }); """ def user(user_message, history, character, api_key): if user_message.strip() == "": return "", history, format_history(history) history.append([user_message, None]) formatted_history = format_history(history) # Start bot response generation bot_message_generator = predict(user_message, history[:-1], character, api_key) for chunk in bot_message_generator: history[-1][1] = chunk formatted_history = format_history(history) yield "", history, formatted_history with gr.Blocks(css=css, js=js) as demo: gr.Markdown("

My Chatbot

") chat_history = gr.State([]) chat_display = gr.HTML(elem_id="chat-display") with gr.Row(): msg = gr.Textbox( label="Your message", lines=1, placeholder="Type your message here... (Press Enter to send)", elem_id="user-input" ) send_btn = gr.Button("Send") clear = gr.Button("Clear") dropdown = gr.Dropdown( ["Character 1", "Character 2", "Character 3", "Character 4", "Character 5", "Character 6", "Character 7", "Character 8", "Character 9", "Character 10", "Character 11", "Character 12", "Character 13"], label="Characters", info="Select the character that you'd like to speak to", value="Character 1" ) api_key = gr.Textbox(type="password", label="OpenAI API Key") send_btn.click(user, [msg, chat_history, dropdown, api_key], [msg, chat_history, chat_display]) msg.submit(user, [msg, chat_history, dropdown, api_key], [msg, chat_history, chat_display]) clear.click(lambda: ([], []), None, [chat_history, chat_display], queue=False) dropdown.change(lambda x: ([], []), dropdown, [chat_history, chat_display]) demo.queue() demo.launch(max_threads=20)