Remove log_transcript config and enforce JSON AI output
This commit is contained in:
parent
c3503fbbde
commit
1423e44008
8 changed files with 198 additions and 62 deletions
88
tests/test_aiprocess.py
Normal file
88
tests/test_aiprocess.py
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
import sys
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
SRC = ROOT / "src"
|
||||
if str(SRC) not in sys.path:
|
||||
sys.path.insert(0, str(SRC))
|
||||
|
||||
from aiprocess import _extract_cleaned_text, _supports_response_format
|
||||
|
||||
|
||||
class ExtractCleanedTextTests(unittest.TestCase):
|
||||
def test_extracts_cleaned_text_from_json_object(self):
|
||||
payload = {
|
||||
"choices": [
|
||||
{
|
||||
"message": {
|
||||
"content": '{"cleaned_text":"Hello <transcript>literal</transcript> world"}'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
result = _extract_cleaned_text(payload)
|
||||
|
||||
self.assertEqual(result, "Hello <transcript>literal</transcript> world")
|
||||
|
||||
def test_extracts_cleaned_text_from_json_string(self):
|
||||
payload = {
|
||||
"choices": [
|
||||
{
|
||||
"message": {
|
||||
"content": '"He said \\\"hello\\\""'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
result = _extract_cleaned_text(payload)
|
||||
|
||||
self.assertEqual(result, 'He said "hello"')
|
||||
|
||||
def test_rejects_non_json_output(self):
|
||||
payload = {
|
||||
"choices": [
|
||||
{
|
||||
"message": {
|
||||
"content": "<transcript>Hello</transcript>"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
with self.assertRaisesRegex(RuntimeError, "expected JSON"):
|
||||
_extract_cleaned_text(payload)
|
||||
|
||||
def test_rejects_json_without_required_key(self):
|
||||
payload = {
|
||||
"choices": [
|
||||
{
|
||||
"message": {
|
||||
"content": '{"text":"hello"}'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
with self.assertRaisesRegex(RuntimeError, "missing cleaned_text"):
|
||||
_extract_cleaned_text(payload)
|
||||
|
||||
|
||||
class SupportsResponseFormatTests(unittest.TestCase):
|
||||
def test_supports_response_format_when_parameter_exists(self):
|
||||
def chat_completion(*, messages, temperature, response_format):
|
||||
return None
|
||||
|
||||
self.assertTrue(_supports_response_format(chat_completion))
|
||||
|
||||
def test_does_not_support_response_format_when_missing(self):
|
||||
def chat_completion(*, messages, temperature):
|
||||
return None
|
||||
|
||||
self.assertFalse(_supports_response_format(chat_completion))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
@ -26,7 +26,6 @@ class ConfigTests(unittest.TestCase):
|
|||
self.assertEqual(cfg.stt.device, "cpu")
|
||||
self.assertEqual(cfg.injection.backend, "clipboard")
|
||||
self.assertTrue(cfg.ai.enabled)
|
||||
self.assertFalse(cfg.logging.log_transcript)
|
||||
self.assertEqual(cfg.vocabulary.replacements, [])
|
||||
self.assertEqual(cfg.vocabulary.terms, [])
|
||||
self.assertEqual(cfg.vocabulary.max_rules, 500)
|
||||
|
|
@ -41,7 +40,6 @@ class ConfigTests(unittest.TestCase):
|
|||
"stt": {"model": "small", "device": "cuda"},
|
||||
"injection": {"backend": "injection"},
|
||||
"ai": {"enabled": False},
|
||||
"logging": {"log_transcript": True},
|
||||
"vocabulary": {
|
||||
"replacements": [
|
||||
{"from": "Martha", "to": "Marta"},
|
||||
|
|
@ -65,7 +63,6 @@ class ConfigTests(unittest.TestCase):
|
|||
self.assertEqual(cfg.stt.device, "cuda")
|
||||
self.assertEqual(cfg.injection.backend, "injection")
|
||||
self.assertFalse(cfg.ai.enabled)
|
||||
self.assertTrue(cfg.logging.log_transcript)
|
||||
self.assertEqual(cfg.vocabulary.max_rules, 100)
|
||||
self.assertEqual(cfg.vocabulary.max_terms, 200)
|
||||
self.assertEqual(len(cfg.vocabulary.replacements), 2)
|
||||
|
|
@ -83,7 +80,6 @@ class ConfigTests(unittest.TestCase):
|
|||
"whisper_device": "cpu",
|
||||
"injection_backend": "clipboard",
|
||||
"ai_enabled": False,
|
||||
"log_transcript": True,
|
||||
}
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
path = Path(td) / "config.json"
|
||||
|
|
@ -97,7 +93,6 @@ class ConfigTests(unittest.TestCase):
|
|||
self.assertEqual(cfg.stt.device, "cpu")
|
||||
self.assertEqual(cfg.injection.backend, "clipboard")
|
||||
self.assertFalse(cfg.ai.enabled)
|
||||
self.assertTrue(cfg.logging.log_transcript)
|
||||
self.assertEqual(cfg.vocabulary.replacements, [])
|
||||
|
||||
def test_invalid_injection_backend_raises(self):
|
||||
|
|
@ -109,13 +104,22 @@ class ConfigTests(unittest.TestCase):
|
|||
with self.assertRaisesRegex(ValueError, "injection.backend"):
|
||||
load(str(path))
|
||||
|
||||
def test_invalid_logging_flag_raises(self):
|
||||
payload = {"logging": {"log_transcript": "yes"}}
|
||||
def test_removed_logging_section_raises(self):
|
||||
payload = {"logging": {"log_transcript": True}}
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
path = Path(td) / "config.json"
|
||||
path.write_text(json.dumps(payload), encoding="utf-8")
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "logging.log_transcript"):
|
||||
with self.assertRaisesRegex(ValueError, "no longer supported"):
|
||||
load(str(path))
|
||||
|
||||
def test_removed_legacy_log_transcript_raises(self):
|
||||
payload = {"log_transcript": True}
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
path = Path(td) / "config.json"
|
||||
path.write_text(json.dumps(payload), encoding="utf-8")
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "no longer supported"):
|
||||
load(str(path))
|
||||
|
||||
def test_conflicting_replacements_raise(self):
|
||||
|
|
|
|||
|
|
@ -80,7 +80,6 @@ class DaemonTests(unittest.TestCase):
|
|||
def _config(self) -> Config:
|
||||
cfg = Config()
|
||||
cfg.ai.enabled = False
|
||||
cfg.logging.log_transcript = False
|
||||
return cfg
|
||||
|
||||
@patch("leld.stop_audio_recording", return_value=FakeAudio(8))
|
||||
|
|
@ -178,6 +177,30 @@ class DaemonTests(unittest.TestCase):
|
|||
self.assertIn("Systemd", model.last_kwargs["hotwords"])
|
||||
self.assertIn("Preferred vocabulary", model.last_kwargs["initial_prompt"])
|
||||
|
||||
def test_verbose_flag_controls_transcript_logging(self):
|
||||
desktop = FakeDesktop()
|
||||
cfg = self._config()
|
||||
|
||||
with patch("leld._build_whisper_model", return_value=FakeModel()):
|
||||
daemon = leld.Daemon(cfg, desktop, verbose=False)
|
||||
self.assertFalse(daemon.log_transcript)
|
||||
|
||||
with patch("leld._build_whisper_model", return_value=FakeModel()):
|
||||
daemon_verbose = leld.Daemon(cfg, desktop, verbose=True)
|
||||
self.assertTrue(daemon_verbose.log_transcript)
|
||||
|
||||
def test_state_changes_are_debug_level(self):
|
||||
desktop = FakeDesktop()
|
||||
with patch("leld._build_whisper_model", return_value=FakeModel()):
|
||||
daemon = leld.Daemon(self._config(), desktop, verbose=False)
|
||||
|
||||
with self.assertLogs(level="DEBUG") as logs:
|
||||
daemon.set_state(leld.State.RECORDING)
|
||||
|
||||
self.assertTrue(
|
||||
any("DEBUG:root:state: idle -> recording" in line for line in logs.output)
|
||||
)
|
||||
|
||||
|
||||
class LockTests(unittest.TestCase):
|
||||
def test_lock_rejects_second_instance(self):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue