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) {