|
1 | 1 | <div class="chatContainer">
|
2 |
| - <div class="messages"> |
| 2 | + <div id="messages" class="messages"> |
3 | 3 | {% for msg in messages %}
|
4 | 4 | {% if msg.role == "user" %}
|
5 | 5 | <div class="userMessage">{{ msg.text }}</div>
|
|
17 | 17 | </div>
|
18 | 18 | {% endif %}
|
19 | 19 | {% endfor %}
|
20 |
| - <div id="messagesEndRef"></div> |
21 | 20 | </div>
|
22 | 21 | <form id="chatForm" class="inputForm clearfix">
|
23 |
| - {% if thread_id is not none %}value="{{ thread_id }}"{% endif %} |
| 22 | + <input type="hidden" value="{{ thread_id }}"> |
24 | 23 | <input
|
25 | 24 | type="text"
|
26 | 25 | class="input"
|
|
31 | 30 | <button
|
32 | 31 | type="submit"
|
33 | 32 | class="button"
|
| 33 | + hx-get="/assistants/{assistant_id}/messages/send" |
| 34 | + hx-target="#messages" |
| 35 | + hx-swap="beforeEnd" |
34 | 36 | {% if inputDisabled %}disabled{% endif %}
|
35 | 37 | >
|
36 | 38 | Send
|
37 | 39 | </button>
|
38 | 40 | </form>
|
39 | 41 | </div>
|
40 |
| - |
41 |
| -<script> |
42 |
| - document.getElementById('chatForm').addEventListener('submit', function(e) { |
43 |
| - e.preventDefault(); |
44 |
| - |
45 |
| - const form = e.target; |
46 |
| - const input = form.querySelector('#userInput'); |
47 |
| - let threadId = form.querySelector('input[name="thread_id"]')?.value; |
48 |
| - const messagesDiv = document.querySelector('.messages'); |
49 |
| - |
50 |
| - // Don't send empty messages |
51 |
| - if (!input.value.trim()) return; |
52 |
| - |
53 |
| - // Append user message immediately |
54 |
| - const userMessageDiv = document.createElement('div'); |
55 |
| - userMessageDiv.className = 'userMessage'; |
56 |
| - userMessageDiv.textContent = input.value; |
57 |
| - messagesDiv.insertBefore(userMessageDiv, document.getElementById('messagesEndRef')); |
58 |
| - |
59 |
| - // Store message and clear input before sending |
60 |
| - const messageText = input.value; |
61 |
| - input.value = ''; |
62 |
| - |
63 |
| - // Scroll to bottom after user message |
64 |
| - messagesEndRef.scrollIntoView({ behavior: 'smooth' }); |
65 |
| - |
66 |
| - // Create form data |
67 |
| - const formData = new FormData(); |
68 |
| - formData.append('userInput', messageText); |
69 |
| - if (threadId) { |
70 |
| - formData.append('thread_id', threadId); |
71 |
| - } |
72 |
| - |
73 |
| - // First send the message via POST |
74 |
| - fetch('/send_message', { |
75 |
| - method: 'POST', |
76 |
| - body: formData |
77 |
| - }).then(response => { |
78 |
| - if (!response.ok) { |
79 |
| - throw new Error('Network response was not ok'); |
80 |
| - } |
81 |
| - return response.json(); |
82 |
| - }).then(data => { |
83 |
| - // Update the thread_id if we got a new one |
84 |
| - if (data.thread_id) { |
85 |
| - const threadInput = form.querySelector('input[name="thread_id"]'); |
86 |
| - if (!threadInput) { |
87 |
| - const newThreadInput = document.createElement('input'); |
88 |
| - newThreadInput.type = 'hidden'; |
89 |
| - newThreadInput.name = 'thread_id'; |
90 |
| - newThreadInput.value = data.thread_id; |
91 |
| - form.appendChild(newThreadInput); |
92 |
| - } else { |
93 |
| - threadInput.value = data.thread_id; |
94 |
| - } |
95 |
| - threadId = data.thread_id; |
96 |
| - } |
97 |
| - |
98 |
| - // Create URL params |
99 |
| - const urlParams = new URLSearchParams(); |
100 |
| - if (threadId && threadId !== "None") { |
101 |
| - urlParams.append('thread_id', threadId); |
102 |
| - } |
103 |
| - |
104 |
| - // Create and store EventSource reference |
105 |
| - const eventSource = new EventSource('/stream_response?' + urlParams.toString()); |
106 |
| - |
107 |
| - // Add cleanup when page is unloaded |
108 |
| - window.addEventListener('beforeunload', () => { |
109 |
| - if (eventSource) { |
110 |
| - eventSource.close(); |
111 |
| - } |
112 |
| - }); |
113 |
| - |
114 |
| - let currentMessageDiv = null; |
115 |
| - |
116 |
| - eventSource.onmessage = (event) => { |
117 |
| - const data = JSON.parse(event.data); |
118 |
| - |
119 |
| - // Check if this is a completion message |
120 |
| - if (data.complete) { |
121 |
| - clearTimeout(streamTimeout); |
122 |
| - eventSource.close(); |
123 |
| - return; |
124 |
| - } |
125 |
| - |
126 |
| - // Create message div if it doesn't exist |
127 |
| - if (!currentMessageDiv) { |
128 |
| - currentMessageDiv = document.createElement('div'); |
129 |
| - currentMessageDiv.className = 'assistantMessage'; |
130 |
| - messagesDiv.insertBefore(currentMessageDiv, document.getElementById('messagesEndRef')); |
131 |
| - } |
132 |
| - |
133 |
| - // Append new text |
134 |
| - currentMessageDiv.innerHTML += data.text; |
135 |
| - |
136 |
| - // Scroll to bottom |
137 |
| - messagesEndRef.scrollIntoView({ behavior: 'smooth' }); |
138 |
| - }; |
139 |
| - |
140 |
| - // Enhanced error handling |
141 |
| - eventSource.onerror = (error) => { |
142 |
| - console.error('EventSource failed:', error); |
143 |
| - eventSource.close(); |
144 |
| - // Optionally add an error message to the chat |
145 |
| - const errorDiv = document.createElement('div'); |
146 |
| - errorDiv.className = 'errorMessage'; |
147 |
| - errorDiv.textContent = 'Message failed to send. Please try again.'; |
148 |
| - messagesDiv.insertBefore(errorDiv, document.getElementById('messagesEndRef')); |
149 |
| - }; |
150 |
| - |
151 |
| - // Add timeout |
152 |
| - const streamTimeout = setTimeout(() => { |
153 |
| - if (eventSource.readyState !== EventSource.CLOSED) { |
154 |
| - eventSource.close(); |
155 |
| - console.warn('Stream timed out'); |
156 |
| - } |
157 |
| - }, 30000); // 30 second timeout |
158 |
| - }).catch(error => { |
159 |
| - console.error('Error:', error); |
160 |
| - // Maybe add some user-facing error message here |
161 |
| - }); |
162 |
| - }); |
163 |
| -</script> |
0 commit comments