feature: speech to text settings
- initial commit: voice settings - Settings section for STT
Alessandro committed
Nov 28, 2024 at 16:07 UTC
89b848312b6f553fe22a6c0039bc7ab93b716384
4 files changed
+122
-13
python/helpers/settings.py
+89
-3
@@ -39,6 +39,13 @@ class Settings(TypedDict):
39
rfc_url: str
40
rfc_password: str
41
42
+ stt_model_size: str
43
+ stt_language: str
44
+ stt_silence_threshold: float
45
+ stt_silence_duration: int
46
+ stt_waiting_timeout: int
47
+ stt_min_speech_duration: int
48
+
49
50
class PartialSettings(Settings, total=False):
51
pass
@@ -413,12 +420,82 @@ def convert_out(settings: Settings) -> SettingsOutput:
420
"fields": dev_fields,
421
}
422
423
+ # Speech to text section
424
+ stt_fields: list[SettingsField] = []
425
+
426
+ stt_fields.append({
427
+ "id": "stt_model_size",
428
+ "title": "Model Size",
429
+ "description": "Select the speech recognition model size",
430
+ "type": "select",
431
+ "value": settings["stt_model_size"],
432
+ "options": [
433
+ {"value": "tiny", "label": "Tiny (39M, English)"},
434
+ {"value": "base", "label": "Base (74M, English)"},
435
+ {"value": "small", "label": "Small (244M, English)"},
436
+ {"value": "medium", "label": "Medium (769M, English)"},
437
+ {"value": "large", "label": "Large (1.5B, Multilingual)"},
438
+ {"value": "turbo", "label": "Turbo (Multilingual)"}
439
+ ]
440
+ })
441
+
442
+ stt_fields.append({
443
+ "id": "stt_language",
444
+ "title": "Language Code",
445
+ "description": "Language code (e.g. en, fr, it)",
446
+ "type": "input",
447
+ "value": settings["stt_language"]
448
+ })
449
+
450
+ stt_fields.append({
451
+ "id": "stt_silence_threshold",
452
+ "title": "Silence threshold",
453
+ "description": "Silence detection threshold. Lower values are more sensitive.",
454
+ "type": "range",
455
+ "min": 0,
456
+ "max": 1,
457
+ "step": 0.01,
458
+ "value": settings["stt_silence_threshold"]
459
+ })
460
+
461
+ stt_fields.append({
462
+ "id": "stt_silence_duration",
463
+ "title": "Silence duration (ms)",
464
+ "description": "Duration of silence before the server considers speaking to have ended.",
465
+ "type": "input",
466
+ "value": settings["stt_silence_duration"]
467
+ })
468
+
469
+ stt_fields.append({
470
+ "id": "stt_waiting_timeout",
471
+ "title": "Waiting timeout (ms)",
472
+ "description": "Duration before the server closes the microphone.",
473
+ "type": "input",
474
+ "value": settings["stt_waiting_timeout"]
475
+ })
476
+
477
+ stt_fields.append({
478
+ "id": "stt_min_speech_duration",
479
+ "title": "Prefix padding (ms)",
480
+ "description": "Minimum duration of audio to be included in the stream before speech was recognized.",
481
+ "type": "input",
482
+ "value": settings["stt_min_speech_duration"]
483
+ })
484
+
485
+ stt_section: SettingsSection = {
486
+ "title": "Speech to Text",
487
+ "description": "Voice transcription preferences and server turn detection settings.",
488
+ "fields": stt_fields
489
+ }
490
+
491
+ # Add the section to the result
492
result: SettingsOutput = {
493
"sections": [
494
agent_section,
495
chat_model_section,
496
util_model_section,
497
embed_model_section,
498
+ stt_section,
499
api_keys_section,
500
auth_section,
501
dev_section,
@@ -443,9 +520,7 @@ def convert_in(settings: dict) -> Settings:
520
if "fields" in section:
521
for field in section["fields"]:
522
if field["id"].endswith("_kwargs"):
446
- current[field["id"]] = _env_to_dict(
447
- field["value"]
448
- ) # parse KWARGS from env format
523
+ current[field["id"]] = _env_to_dict(field["value"])
524
elif field["id"].startswith("api_key_"):
525
current["api_keys"][field["id"]] = field["value"]
526
else:
@@ -518,6 +593,11 @@ def get_embedding_model(settings: Settings | None = None) -> Embeddings:
593
**settings["embed_model_kwargs"],
594
)
595
596
+def get_speech_settings(settings: Settings | None = None) -> dict:
597
+ if not settings:
598
+ settings = get_settings()
599
+ return settings["speech_to_text"]
600
+
601
602
def _read_settings_file() -> Settings | None:
603
if os.path.exists(SETTINGS_FILE):
@@ -573,6 +653,12 @@ def _get_default_settings() -> Settings:
653
agent_knowledge_subdir="custom",
654
rfc_url="http://localhost:55080",
655
rfc_password="",
656
+ stt_model_size="tiny",
657
+ stt_language="en",
658
+ stt_silence_threshold=0.15,
659
+ stt_silence_duration=1000,
660
+ stt_waiting_timeout=2000,
661
+ stt_min_speech_duration=500
662
)
663
664
webui/js/settings.js
+1
@@ -91,6 +91,7 @@ function getIconName(title) {
91
'Chat Model': 'chat-model',
92
'Utility model': 'utility-model',
93
'Embedding Model': 'embed-model',
94
+ 'Speech to Text': 'voice',
95
'API Keys': 'api-keys',
96
'Authentication': 'auth',
97
'Development': 'dev'
webui/js/speech.js
+31
-10
@@ -35,15 +35,36 @@ class MicrophoneInput {
35
this.hasStartedRecording = false;
36
this.analysisFrame = null;
37
38
+ // Initialize with defaults
39
this.options = {
40
modelSize: 'tiny',
41
language: 'en',
42
silenceThreshold: 0.15,
43
silenceDuration: 1000,
44
waitingTimeout: 2000,
44
- minSpeechDuration: 500,
45
- ...options
45
+ minSpeechDuration: 500
46
};
47
+
48
+ // Fetch settings from server
49
+ this.loadSettings();
50
+ }
51
+
52
+ async loadSettings() {
53
+ try {
54
+ const response = await fetch('/settings_get');
55
+ const data = await response.json();
56
+ const sttSettings = data.settings.sections.find(s => s.title === 'Speech to Text');
57
+
58
+ if (sttSettings) {
59
+ // Update options from server settings
60
+ sttSettings.fields.forEach(field => {
61
+ const key = field.id.split('.')[1]; // speech_to_text.model_size -> model_size
62
+ this.options[key] = field.value;
63
+ });
64
+ }
65
+ } catch (error) {
66
+ console.error('Failed to load speech settings:', error);
67
+ }
68
}
69
70
get status() {
@@ -314,13 +335,6 @@ async function initializeMicrophoneInput() {
335
await sendMessage();
336
}
337
}
317
- },
318
- {
319
- modelSize: 'tiny',
320
- language: 'en',
321
- silenceThreshold: 0.07,
322
- silenceDuration: 1000,
323
- waitingTimeout: 1500
338
}
339
);
340
microphoneInput.status = Status.ACTIVATING;
@@ -439,4 +453,11 @@ class Speech {
453
}
454
455
export const speech = new Speech();
442
-window.speech = speech
\ No newline at end of file
456
+window.speech = speech
457
+
458
+// Add event listener for settings changes
459
+document.addEventListener('settings-updated', async () => {
460
+ if (microphoneInput) {
461
+ await microphoneInput.loadSettings();
462
+ }
463
+});
\ No newline at end of file
webui/public/voice.svg
new
+1
@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" id="Layer_1" x="0" y="0" version="1.1" viewBox="0 0 22.6 21.2"><style>.st0{fill:none;stroke:#000;stroke-width:.5;stroke-linecap:round;stroke-miterlimit:10}</style><g id="XMLID_00000031915645942353647240000017236973318540577665_"><path d="m11.8 7.9 2.1 4.6c.2.3-.1.7-.5.7h-1.7v3.4c0 .7-.6 1.3-1.3 1.3H4.2M7.7 20.8V18M4.2.3c4.2 0 7.6 3.4 7.6 7.6" class="st0"/><path d="M11.8 15.8h-1.4l-.4-.4" class="st0"/></g><path d="M14.2 15.7c.8.8.8 2 0 2.8M15.6 14.8c1.3 1.3 1.3 3.3 0 4.6M17 13.8c1.8 1.8 1.8 4.7 0 6.5" class="st0"/></svg>
\ No newline at end of file