feat(dictate): support configurable ASR backends
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import tempfile
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
from pi_dictate_service.config import (
|
||||
ConfigError,
|
||||
CredentialError,
|
||||
load_config,
|
||||
parse_config,
|
||||
provider_keywords,
|
||||
read_credential,
|
||||
)
|
||||
from pi_dictate_service.recorder import audio_level_window_bytes, pw_record_command
|
||||
|
||||
|
||||
class ConfigTests(unittest.TestCase):
|
||||
def test_recorder_supports_both_backend_sample_rates(self) -> None:
|
||||
self.assertEqual(pw_record_command(16_000)[5], "16000")
|
||||
self.assertEqual(pw_record_command(24_000)[5], "24000")
|
||||
self.assertEqual(audio_level_window_bytes(24_000), 4_800)
|
||||
|
||||
def test_rejects_unknown_selected_fields_and_invalid_backend_values(self) -> None:
|
||||
for invalid_backend in ([], {}, 1, True, None):
|
||||
with self.subTest(backend=invalid_backend):
|
||||
with self.assertRaisesRegex(ConfigError, "backend must be"):
|
||||
parse_config({"version": 1, "backend": invalid_backend})
|
||||
|
||||
base = {
|
||||
"version": 1,
|
||||
"backend": "openai",
|
||||
"openai": {
|
||||
"model": "gpt-live-transcribe",
|
||||
"apiKeyFile": "~/.config/pi-dictate/credentials/openai-api-key",
|
||||
"termsPath": "/tmp/terms",
|
||||
},
|
||||
}
|
||||
with patch.dict(os.environ, {"HOME": "/tmp/pi-dictate-test"}):
|
||||
invalid = {**base, "openai": {**base["openai"], "endpoint": "https://bad"}}
|
||||
with self.assertRaises(ConfigError):
|
||||
parse_config(invalid)
|
||||
with self.assertRaises(ConfigError):
|
||||
parse_config({**base, "version": 2})
|
||||
configured = parse_config(
|
||||
{**base, "openai": {**base["openai"], "delay": "low"}}
|
||||
)
|
||||
self.assertEqual(configured.openai.delay, "low")
|
||||
with self.assertRaises(ConfigError):
|
||||
parse_config({**base, "openai": {**base["openai"], "delay": "fast"}})
|
||||
with self.assertRaises(ConfigError):
|
||||
parse_config({**base, "openai": {**base["openai"], "termsPath": "relative/terms"}})
|
||||
|
||||
def test_reads_only_one_trailing_newline_from_a_private_credential(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
path = Path(directory) / "key"
|
||||
path.write_text("secret\n", encoding="utf-8")
|
||||
path.chmod(0o600)
|
||||
self.assertEqual(read_credential(path), "secret")
|
||||
|
||||
path.chmod(0o640)
|
||||
with self.assertRaises(CredentialError):
|
||||
read_credential(path)
|
||||
|
||||
path.chmod(0o600)
|
||||
path.write_text("secret\nextra", encoding="utf-8")
|
||||
with self.assertRaises(CredentialError):
|
||||
read_credential(path)
|
||||
|
||||
def test_rejects_a_credential_reached_through_a_symlinked_directory(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory)
|
||||
actual = root / "actual"
|
||||
actual.mkdir()
|
||||
key = actual / "key"
|
||||
key.write_text("secret", encoding="utf-8")
|
||||
key.chmod(0o600)
|
||||
linked = root / "credentials"
|
||||
linked.symlink_to(actual, target_is_directory=True)
|
||||
|
||||
with self.assertRaisesRegex(CredentialError, "ancestry"):
|
||||
read_credential(linked / "key")
|
||||
|
||||
def test_config_file_is_regular_private_and_provider_terms_reject_cr(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory)
|
||||
config_path = root / "config.json"
|
||||
config_path.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"version": 1,
|
||||
"backend": "local",
|
||||
"local": {
|
||||
"adapter": "nemo-parakeet",
|
||||
"modelPath": "/tmp/model.nemo",
|
||||
"modelSha256": "0" * 64,
|
||||
"termsPath": "/tmp/terms",
|
||||
"boostAlpha": 1.0,
|
||||
},
|
||||
}
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
config_path.chmod(0o600)
|
||||
self.assertEqual(load_config(config_path).backend, "local")
|
||||
|
||||
terms = root / "terms"
|
||||
terms.write_bytes(b"bad\rterm")
|
||||
with self.assertRaises(ConfigError):
|
||||
provider_keywords(terms)
|
||||
terms.write_bytes(b"safe\nbad\x00term\n")
|
||||
with self.assertRaises(ConfigError):
|
||||
provider_keywords(terms)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user