Selaa lähdekoodia

Made transcription saving conditional

Efren Yevale Varela 1 kuukausi sitten
vanhempi
sitoutus
d30603d97a

+ 3 - 2
README.md

@@ -136,7 +136,8 @@ monitor_output        = false                  # Loop sink.monitor → default s
 
 [output]
 stdout     = true                     # Print transcripts to stdout
-jsonl_dir  = "transcripts"            # Write JSONL files (one per day) to this dir
+jsonl      = false                    # Persist transcripts to JSONL files (off by default for agent-only use)
+jsonl_dir  = "transcripts"            # JSONL destination (one file per day); only used if jsonl = true
 save_audio = false                    # Also write each transcribed utterance as a 16 kHz mono WAV
 audio_dir  = "audio"                  # WAV destination (YYYY-MM-DD subfolders); only used if save_audio
 
@@ -394,7 +395,7 @@ uv run stt-daemon clear --yes
 [14:24:10] (es) Gracias por tu ayuda.  [rms=0.052 pause]
 ```
 
-**JSONL** (one line per utterance):
+**JSONL** (one line per utterance; only written when `jsonl = true`):
 ```json
 {"ts": "2025-09-20T14:23:45.123456", "lang": "es", "lang_prob": 0.95, "duration_s": 2.5, "rms": 0.04123, "end_reason": "end_of_turn", "text": "Hola, cómo estás?", "transcribe_s": 0.42}
 {"ts": "2025-09-20T14:23:52.654321", "lang": "en", "lang_prob": 0.98, "duration_s": 1.8, "rms": 0.03811, "end_reason": "pause", "text": "What time is it?", "transcribe_s": 0.31}

+ 1 - 0
config.example.toml

@@ -39,6 +39,7 @@ monitor_output        = false
 
 [output]
 stdout     = true
+jsonl      = false
 jsonl_dir  = "transcripts"
 save_audio = false
 audio_dir  = "audio"

+ 4 - 2
src/stt_daemon/config.py

@@ -67,6 +67,7 @@ class WhisperConfig:
 class OutputConfig:
     """Output configuration."""
     stdout: bool = True
+    jsonl: bool = False
     jsonl_dir: str | None = "transcripts"
     save_audio: bool = False
     audio_dir: str = "audio"
@@ -249,8 +250,9 @@ def load_config(path: str | None = None) -> Config:
         if "output" in data:
             output_data = data["output"]
             cfg.output = OutputConfig(
-                stdout=output_data.get("stdout", cfg.output.stdout),
-                jsonl_dir=output_data.get("jsonl_dir", cfg.output.jsonl_dir),
+        stdout=output_data.get("stdout", cfg.output.stdout),
+        jsonl=output_data.get("jsonl", cfg.output.jsonl),
+        jsonl_dir=output_data.get("jsonl_dir", cfg.output.jsonl_dir),
                 save_audio=output_data.get("save_audio", cfg.output.save_audio),
                 audio_dir=output_data.get("audio_dir", cfg.output.audio_dir),
             )

+ 1 - 0
src/stt_daemon/daemon.py

@@ -116,6 +116,7 @@ class Daemon:
         return TranscriptSink(
             stdout=self.cfg.output.stdout,
             jsonl_dir=self.cfg.output.jsonl_dir,
+            jsonl=self.cfg.output.jsonl,
             save_audio=self.cfg.output.save_audio,
             audio_dir=self.cfg.output.audio_dir,
             sample_rate=self.cfg.audio.sample_rate,

+ 7 - 2
src/stt_daemon/output.py

@@ -30,6 +30,7 @@ class TranscriptSink:
         self,
         stdout: bool = True,
         jsonl_dir: str | None = None,
+        jsonl: bool = False,
         save_audio: bool = False,
         audio_dir: str = "audio",
         sample_rate: int = 16000,
@@ -38,13 +39,17 @@ class TranscriptSink:
 
         Args:
             stdout: If True, emit to stdout.
-            jsonl_dir: If set, append JSONL lines to daily files in this directory.
+            jsonl_dir: Directory for daily JSONL files.
+            jsonl: If True (and jsonl_dir is set), append JSONL lines to daily
+                files in that directory. Defaults off so agent-only runs do not
+                persist transcripts to disk.
             save_audio: If True, write each transcript's audio as a WAV file.
             audio_dir: Directory for WAV files (dated subfolders).
             sample_rate: Audio sample rate, written into the WAV header.
         """
         self.stdout = stdout
-        self.jsonl_dir = Path(jsonl_dir) if jsonl_dir else None
+        self.jsonl = jsonl
+        self.jsonl_dir = Path(jsonl_dir) if (jsonl and jsonl_dir) else None
         self.save_audio = save_audio
         self.audio_dir = Path(audio_dir)
         self.sample_rate = sample_rate

+ 3 - 0
tests/test_config.py

@@ -22,6 +22,7 @@ def test_load_config_defaults():
             assert cfg.whisper.model == "small"
             assert cfg.whisper.language is None
             assert cfg.output.stdout is True
+            assert cfg.output.jsonl is False
         finally:
             os.chdir(old_cwd)
 
@@ -45,6 +46,7 @@ language = "es"
 
 [output]
 stdout = false
+jsonl = true
 jsonl_dir = "logs"
 """
     with tempfile.TemporaryDirectory() as tmpdir:
@@ -62,6 +64,7 @@ jsonl_dir = "logs"
         assert cfg.whisper.device == "cuda"
         assert cfg.whisper.language == "es"
         assert cfg.output.stdout is False
+        assert cfg.output.jsonl is True
         assert cfg.output.jsonl_dir == "logs"
 
 

+ 16 - 4
tests/test_output.py

@@ -14,7 +14,7 @@ def _result(text="hello world"):
 
 
 def test_jsonl_includes_level_and_reason(tmp_path):
-    sink = TranscriptSink(stdout=False, jsonl_dir=str(tmp_path))
+    sink = TranscriptSink(stdout=False, jsonl_dir=str(tmp_path), jsonl=True)
     sink.emit(_result(), 2.5, rms=0.041234, reason="pause")
 
     files = list(tmp_path.glob("*.jsonl"))
@@ -29,7 +29,7 @@ def test_jsonl_includes_level_and_reason(tmp_path):
 
 
 def test_jsonl_includes_transcribe_seconds(tmp_path):
-    sink = TranscriptSink(stdout=False, jsonl_dir=str(tmp_path))
+    sink = TranscriptSink(stdout=False, jsonl_dir=str(tmp_path), jsonl=True)
     sink.emit(_result(), 2.5, rms=0.041234, reason="pause", transcribe_s=0.81234)
 
     record = json.loads(next(tmp_path.glob("*.jsonl")).read_text().splitlines()[0])
@@ -37,13 +37,22 @@ def test_jsonl_includes_transcribe_seconds(tmp_path):
 
 
 def test_transcribe_seconds_omitted_when_unknown(tmp_path):
-    sink = TranscriptSink(stdout=False, jsonl_dir=str(tmp_path))
+    sink = TranscriptSink(stdout=False, jsonl_dir=str(tmp_path), jsonl=True)
     sink.emit(_result(), 2.5, rms=0.041234, reason="pause")
 
     record = json.loads(next(tmp_path.glob("*.jsonl")).read_text().splitlines()[0])
     assert "transcribe_s" not in record
 
 
+def test_jsonl_disabled_writes_nothing(tmp_path, capsys):
+    """jsonl=False keeps transcripts off disk even when jsonl_dir is set."""
+    sink = TranscriptSink(stdout=True, jsonl_dir=str(tmp_path), jsonl=False)
+    sink.emit(_result(), 1.0, rms=0.02, reason="pause")
+
+    assert "hello world" in capsys.readouterr().out
+    assert list(tmp_path.glob("*.jsonl")) == []
+
+
 def test_stdout_includes_level_and_reason(capsys):
     sink = TranscriptSink(stdout=True, jsonl_dir=None)
     sink.emit(_result(), 1.0, rms=0.02, reason="end_of_turn")
@@ -55,7 +64,7 @@ def test_stdout_includes_level_and_reason(capsys):
 
 
 def test_empty_text_is_skipped(tmp_path, capsys):
-    sink = TranscriptSink(stdout=True, jsonl_dir=str(tmp_path))
+    sink = TranscriptSink(stdout=True, jsonl_dir=str(tmp_path), jsonl=True)
     sink.emit(_result(text=""), 1.0, rms=0.02, reason="pause")
 
     assert capsys.readouterr().out == ""
@@ -70,6 +79,7 @@ def test_saves_wav_when_enabled(tmp_path):
     sink = TranscriptSink(
         stdout=False,
         jsonl_dir=str(logs),
+        jsonl=True,
         save_audio=True,
         audio_dir=str(tmp_path / "audio"),
         sample_rate=16000,
@@ -110,6 +120,7 @@ def test_single_timestamp_per_emit(tmp_path, monkeypatch):
     sink = TranscriptSink(
         stdout=False,
         jsonl_dir=str(logs),
+        jsonl=True,
         save_audio=True,
         audio_dir=str(tmp_path / "audio"),
         sample_rate=16000,
@@ -163,6 +174,7 @@ def test_failed_audio_save_is_non_fatal(tmp_path, monkeypatch, capsys, caplog):
     sink = TranscriptSink(
         stdout=True,
         jsonl_dir=str(logs),
+        jsonl=True,
         save_audio=True,
         audio_dir=str(tmp_path / "audio"),
         sample_rate=16000,