File size: 5,904 Bytes
e6868fd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
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', '<br>')
        html_content += f'<div class="message user-message"><strong>You:</strong> {human_formatted}</div>'
        if ai:
            ai_formatted = html.escape(ai).replace('\n', '<br>')
            html_content += f'<div class="message ai-message"><strong>AI:</strong> {ai_formatted}</div>'
    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("<h1 style='text-align: center; margin-bottom: 1rem'>My Chatbot</h1>")
    
    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)