feat(transcription): add shared voice input support (#4232)

* feat(webui): add voice transcription input

* feat(webui): render ANSI output in code blocks

* refactor(webui): isolate voice recorder logic

* refactor(transcription): keep websocket ingress thin

* refactor(transcription): resolve channel audio settings on demand

* style(webui): neutralize voice waveform color

* feat(webui): add voice input tooltip

* feat(webui): add voice input keyboard shortcut

* fix(webui): distinguish voice shortcut platforms

* fix(webui): place voice button after model selector

* refactor(webui): share voice hold recording helpers

* fix(desktop): allow microphone voice input

* fix(webui): stabilize token usage month labels

* feat(webui): show voice input on settings overview

* fix(webui): label voice capability as recognition

* fix(webui): align capability overview status

* refactor(webui): isolate transcription socket handling

* fix(webui): soften silent voice waveform

* refactor(audio): clarify transcription service location

* docs(transcription): clarify audio and provider boundaries

* fix(exec): reduce session output polling flake
This commit is contained in:
Xubin Ren
2026-06-09 01:08:49 +08:00
committed by GitHub
parent 06d454a225
commit 9c81280300
49 changed files with 3071 additions and 257 deletions
+26 -1
View File
@@ -8,8 +8,8 @@ import pytest
from nanobot.utils.media_decode import (
DEFAULT_MAX_BYTES,
FileSizeExceeded,
MAX_FILE_SIZE,
FileSizeExceeded,
save_base64_data_url,
)
@@ -25,6 +25,31 @@ def test_saves_png_with_correct_extension(tmp_path) -> None:
assert (tmp_path / result.split("/")[-1]).read_bytes() == b"fake png"
def test_saves_data_url_with_mime_parameters(tmp_path) -> None:
result = save_base64_data_url(_data_url(b"voice", mime="audio/webm;codecs=opus"), tmp_path)
assert result is not None
assert result.endswith(".webm")
assert (tmp_path / result.split("/")[-1]).read_bytes() == b"voice"
@pytest.mark.parametrize(
("mime", "suffix"),
[
("audio/webm", ".webm"),
("video/webm", ".webm"),
("audio/ogg", ".ogg"),
("audio/wav", ".wav"),
("audio/mpga", ".mpga"),
],
)
def test_saves_common_audio_with_api_friendly_extension(
tmp_path, mime: str, suffix: str
) -> None:
result = save_base64_data_url(_data_url(b"voice", mime=mime), tmp_path)
assert result is not None
assert result.endswith(suffix)
def test_returns_none_for_malformed_data_url(tmp_path) -> None:
assert save_base64_data_url("not-a-data-url", tmp_path) is None