Mic js and embedding menu + styles

Alessandro committed Nov 6, 2024 at 11:17 UTC d84469dff6b4ffa0db7da509f9b38802d55e180c
4 files changed +139 -27
python/helpers/settings.py
+41 -3
@@ -78,7 +78,7 @@ def convert_out(settings: Settings) -> dict[str, Any]:
78 }
79 )
80
81 - chat_model_seciton = {
81 + chat_model_section = {
82 "title": "Chat Model",
83 "description": "Selection and settings for main chat model used by Agent Zero",
84 "fields": chat_model_fields,
@@ -129,13 +129,51 @@ def convert_out(settings: Settings) -> dict[str, Any]:
129 }
130 )
131
132 - util_model_seciton = {
132 + util_model_section = {
133 "title": "Utility model",
134 "description": "Smaller, cheaper, faster model for handling utility tasks like organizing memory, preparing prompts, summarizing.",
135 "fields": util_model_fields,
136 }
137
138 - result = {"sections": [chat_model_seciton, util_model_seciton]}
138 + # embedding model section
139 + embed_model_fields = []
140 + embed_model_fields.append(
141 + {
142 + "id": "embed_model_provider",
143 + "title": "Embedding model provider",
144 + "description": "Select provider for embedding model used by the framework",
145 + "type": "select",
146 + "value": settings["embed_model_provider"],
147 + "options": [{"value": p.name, "label": p.value} for p in ModelProvider],
148 + }
149 + )
150 + embed_model_fields.append(
151 + {
152 + "id": "embed_model_name",
153 + "title": "Embedding model name",
154 + "description": "Exact name of model from selected provider",
155 + "type": "input",
156 + "value": settings["embed_model_name"],
157 + }
158 + )
159 +
160 + embed_model_fields.append(
161 + {
162 + "id": "embed_model_kwargs",
163 + "title": "Embedding model additional parameters",
164 + "description": "Any other parameters supported by the model. Format is KEY=VALUE on individual lines, just like .env file.",
165 + "type": "textarea",
166 + "value": _dict_to_env(settings["embed_model_kwargs"]),
167 + }
168 + )
169 +
170 + embed_model_section = {
171 + "title": "Embedding Model",
172 + "description": "Settings for the embedding model used by Agent Zero.",
173 + "fields": embed_model_fields,
174 + }
175 +
176 + result = {"sections": [chat_model_section, util_model_section, embed_model_section]}
177 return result
178
179 def convert_in(settings: dict[str, Any]) -> Settings:
webui/index.css
+5 -1
@@ -371,7 +371,7 @@ pre {
371 display: flex;
372 flex-direction: column;
373 flex-grow: 1;
374 - overflow-y: auto;
374 + overflow-y: scroll;
375 scroll-behavior: smooth;
376 overflow-x: hidden;
377 padding: var(--spacing-md) var(--spacing-md) 0;
@@ -889,6 +889,7 @@ pre {
889 background-color: var(--color-primary-light);
890 }
891
892 +/* MIC BUTTON */
893 #microphone-button {
894 background-color: #3270e2;
895 }
@@ -896,6 +897,9 @@ pre {
897 #microphone-button:hover {
898 background-color: #4382e8;
899 }
900 +#microphone-button.recording {
901 + background-color: #ff4136; /* Red color for recording */
902 +}
903
904 .chat-button svg {
905 width: 1.5rem;
webui/index.html
+6 -4
@@ -236,17 +236,19 @@
236
237 <!-- Microphone button -->
238 <button class="chat-button" id="microphone-button" aria-label="Start/Stop recording">
239 - <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="24" height="24">
240 - <path d="M12 14c1.66 0 3-1.34 3-3V5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm-1 1.9c-2.7-.4-4.8-2.6-5-5.4h-2c.2 3.8 3.1 6.9 7 7.5V20h2v-2.6c3.9-.6 6.8-3.7 7-7.5h-2c-.2 2.8-2.3 5-5 5.4V15z"/>
239 + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 18" fill="currentColor" width="24" height="24">
240 + <path d="m8,12c1.66,0,3-1.34,3-3V3c0-1.66-1.34-3-3-3s-3,1.34-3,3v6c0,1.66,1.34,3,3,3Zm-1,1.9c-2.7-.4-4.8-2.6-5-5.4H0c.2,3.8,3.1,6.9,7,7.5v2h2v-2c3.9-.6,6.8-3.7,7-7.5h-2c-.2,2.8-2.3,5-5,5.4h-2Z"/>
241 </svg>
242 +
243 </button>
243 - </div>
244 +
245 + </div>
246 </div>
247
248 <!-- Bottom row with text buttons -->
249 <div class="text-buttons-row">
250 <button class="text-button"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5m-13.5-9L12 3m0 0 4.5 4.5M12 3v13.5"></path></svg>Import knowledge</button>
249 - <button class="text-button"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 122.88 89.09"><g><path stroke-width="3.5" stroke="currentColor" fill="none" d="M3.97,9.75l-3.93,8.73l122.77,0.01l-3.96-8.74H55.82c-1.59,0-2.88-1.29-2.88-2.88V0H11.97v6.87 c0,1.59-1.29,2.88-2.88,2.88H3.97L3.97,9.75L3.97,9.75z"></path><path stroke-width="3.5" stroke="currentColor" fill="none" d="M4.63,18.48H0l7.03,67.03c0.11,1.07,0.55,2.02,1.2,2.69c0.55,0.55,1.28,0.89,2.11,0.89h100.1 c0.82,0,1.51-0.33,2.05-0.87c0.68-0.68,1.13-1.67,1.28-2.79l9.1-66.94H4.63V18.48L4.63,18.48z"></path></g></svg>work_dir Browser</button>
251 + <button class="text-button"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 122.88 89.09"><g><path stroke-width="3.5" stroke="currentColor" fill="none" d="M3.97,9.75l-3.93,8.73l122.77,0.01l-3.96-8.74H55.82c-1.59,0-2.88-1.29-2.88-2.88V0H11.97v6.87 c0,1.59-1.29,2.88-2.88,2.88H3.97L3.97,9.75L3.97,9.75z"></path><path stroke-width="3.5" stroke="currentColor" fill="none" d="M4.63,18.48H0l7.03,67.03c0.11,1.07,0.55,2.02,1.2,2.69c0.55,0.55,1.28,0.89,2.11,0.89h100.1 c0.82,0,1.51-0.33,2.05-0.87c0.68-0.68,1.13-1.67,1.28-2.79l9.1-66.94H4.63V18.48L4.63,18.48z"></path></g></svg>work_dir Browser</button>
252 <button class="text-button" @click="pauseAgent(!paused)">
253 <!-- Dynamic path that switches between pause and play icons -->
254 <template x-if="!paused">
webui/index.js
+87 -19
@@ -12,11 +12,13 @@ const chatsSection = document.getElementById('chats-section');
12 const scrollbarThumb = document.querySelector('#chat-history::-webkit-scrollbar-thumb');
13 const progressBar = document.getElementById('progress-bar');
14 const autoScrollSwitch = document.getElementById('auto-scroll-switch');
15 -
15 +const microphoneButton = document.getElementById('microphone-button');
16
17
18 let autoScroll = true;
19 let context = "";
20 +let microphoneInput = null;
21 +
22
23 // Initialize the toggle button
24 setupSidebarToggle();
@@ -95,6 +97,59 @@ chatInput.addEventListener('keydown', (e) => {
97
98 sendButton.addEventListener('click', sendMessage);
99
100 +
101 +// MICROPHONE INPUT
102 +
103 +async function initializeMicrophoneInput() {
104 + microphoneInput = new MicrophoneInput(updateChatInput);
105 + await microphoneInput.initialize();
106 +}
107 +function updateChatInput(text) {
108 + chatInput.value += text + ' ';
109 + adjustTextareaHeight();
110 +}
111 +microphoneButton.addEventListener('click', () => {
112 + if (!microphoneInput) {
113 + initializeMicrophoneInput().then(() => {
114 + toggleRecording();
115 + });
116 + } else {
117 + toggleRecording();
118 + }
119 +});
120 +function toggleRecording() {
121 + if (microphoneInput.isRecording) {
122 + microphoneInput.stopRecording();
123 + microphoneButton.classList.remove('recording');
124 + } else {
125 + microphoneInput.startRecording();
126 + microphoneButton.classList.add('recording');
127 + }
128 +}
129 +// Some error handling for microphone input
130 +async function requestMicrophonePermission() {
131 + try {
132 + await navigator.mediaDevices.getUserMedia({ audio: true });
133 + return true;
134 + } catch (err) {
135 + console.error('Error accessing microphone:', err);
136 + toast('Microphone access denied. Please enable microphone access in your browser settings.', 'error');
137 + return false;
138 + }
139 +}
140 +// microphoneButton click event listener modifier
141 +microphoneButton.addEventListener('click', async () => {
142 + const hasPermission = await requestMicrophonePermission();
143 + if (!hasPermission) return;
144 + if (!microphoneInput) {
145 + initializeMicrophoneInput().then(() => {
146 + toggleRecording();
147 + });
148 + } else {
149 + toggleRecording();
150 + }
151 +});
152 +
153 function updateUserTime() {
154 const now = new Date();
155 const hours = now.getHours();
@@ -142,8 +197,9 @@ function setMessage(id, type, heading, content, temp, kvps = null) {
197 if (!document.getElementById(`message-${id}`)) {
198 chatHistory.appendChild(messageContainer);
199 }
145 -
146 - if (autoScroll) chatHistory.scrollTop = chatHistory.scrollHeight;
200 +
201 + // Remove the automatic scrolling
202 + // if (autoScroll) chatHistory.scrollTop = chatHistory.scrollHeight;
203 }
204
205
@@ -181,31 +237,29 @@ let lastLogVersion = 0;
237 let lastLogGuid = ""
238
239 async function poll() {
184 - let updated = false
240 + let updated = false;
241 try {
242 const response = await sendJsonData("/poll", { log_from: lastLogVersion, context });
187 - //console.log(response)
243
244 if (response.ok) {
190 -
191 - if (!context) setContext(response.context)
192 - if (response.context != context) return //skip late polls after context change
245 + if (!context) setContext(response.context);
246 + if (response.context != context) return; // Skip if context changed
247
248 if (lastLogGuid != response.log_guid) {
195 - chatHistory.innerHTML = ""
196 - lastLogVersion = 0
249 + chatHistory.innerHTML = "";
250 + lastLogVersion = 0;
251 }
252
253 if (lastLogVersion != response.log_version) {
200 - updated = true
254 + updated = true;
255 for (const log of response.logs) {
256 setMessage(log.no, log.type, log.heading, log.content, log.temp, log.kvps);
257 }
258 }
259
206 - updateProgress(response.log_progress)
260 + updateProgress(response.log_progress);
261
208 - //set ui model vars from backend
262 + // Set UI model vars from backend
263 const inputAD = Alpine.$data(inputSection);
264 inputAD.paused = response.paused;
265 const statusAD = Alpine.$data(statusSection);
@@ -215,17 +269,22 @@ async function poll() {
269
270 lastLogVersion = response.log_version;
271 lastLogGuid = response.log_guid;
218 -
219 -
272 }
221 -
273 } catch (error) {
274 console.error('Error:', error);
275 const statusAD = Alpine.$data(statusSection);
276 statusAD.connected = false;
277 }
278
228 - return updated
279 + if (updated && autoScroll) {
280 + const lastMessage = chatHistory.lastElementChild;
281 + if (lastMessage) {
282 + // Scroll the last message into view
283 + lastMessage.scrollIntoView({ behavior: 'auto' });
284 + }
285 + }
286 +
287 + return updated;
288 }
289
290 function updateProgress(progress) {
@@ -289,8 +348,17 @@ window.killChat = async function (id) {
348 }
349
350 window.selectChat = async function (id) {
292 - setContext(id)
293 - updateAfterScroll()
351 + setContext(id);
352 + updateAfterScroll();
353 +
354 + // Optionally, wait for poll to load messages and then scroll
355 + const updated = await poll();
356 + if (updated && autoScroll) {
357 + const lastMessage = chatHistory.lastElementChild;
358 + if (lastMessage) {
359 + lastMessage.scrollIntoView({ behavior: 'auto' });
360 + }
361 + }
362 }
363
364 const setContext = function (id) {