From d3d16e85c7327b7d2a10349b5018b0ff45991fa8 Mon Sep 17 00:00:00 2001 From: Dita Aji Pratama Date: Thu, 8 Oct 2026 21:10:02 +0700 Subject: [PATCH] ASR auto-submit --- config.default.yaml | 2 ++ config.py | 4 ++++ interfaces/tui/talk.py | 34 ++++++++++++++++++++++++++++++---- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/config.default.yaml b/config.default.yaml index b70c25a..702ee42 100644 --- a/config.default.yaml +++ b/config.default.yaml @@ -28,6 +28,8 @@ asr: # Speech-to-Text (push-to-talk TUI). Model dikelola via moop tipe 'asr'. max_seconds : 60 # auto-stop pengaman transcribe_timeout: 60 # detik timeout request ASR keep_recordings : true # false = hapus file setelah ditranskrip + auto_submit : false # true = hasil transkrip langsung dikirim + # false = hanya mengisi form (Ctrl+Enter) session: # Chat session db_path: "~/.config/hendrik/sessions.json" diff --git a/config.py b/config.py index 5e0845f..d281d49 100644 --- a/config.py +++ b/config.py @@ -61,6 +61,10 @@ ASR_KEEP_RECORDINGS = _as_bool(_yaml_get("asr", "keep_recordings", default=True) # Tombol push-to-talk: "f1".."f12" atau "ctrl-". Default F9. ASR_RECORD_KEY = str(_yaml_get("asr", "record_key", default="f9")).strip().lower() +# true = hasil transkripsi langsung dikirim sebagai pesan, +# false = hasil hanya disisipkan ke form input (user kirim sendiri via Ctrl+Enter). +ASR_AUTO_SUBMIT = _as_bool(_yaml_get("asr", "auto_submit", default=False), default=False) + XMPP_USERNAME = _yaml_get("xmpp", "username", default="") XMPP_PASSWORD = _yaml_get("xmpp", "password", default="") diff --git a/interfaces/tui/talk.py b/interfaces/tui/talk.py index 8594708..a4875da 100644 --- a/interfaces/tui/talk.py +++ b/interfaces/tui/talk.py @@ -2,9 +2,13 @@ # # Alur: # * F9 (configurable) → mulai rekam (ffmpeg subprocess, non-blocking). -# * F9 lagi / Enter → stop rekam → transkrip (thread) → hasil DISISIPKAN -# ke form input pada posisi kursor, TIDAK submit. +# * F9 lagi / Enter → stop rekam → transkrip (thread) → hasil +# disisipkan ke form input pada posisi kursor, +# TIDAK submit (default). # * Backspace / Esc → batal: rekaman dibuang / hasil transkrip diabaikan. +# * config.asr.auto_submit: true → hasil transkrip langsung dikirim sebagai +# pesan (setara Ctrl+Enter). Jika form sudah terisi +# saat transkrip selesai, hasil tetap disisipkan. # # State disimpan di app: # app.talk_recorder — instance Recorder saat merekam, else None @@ -18,7 +22,7 @@ import threading import config from tools import asr as asr_mod -from .agent import log +from .agent import log, submit def _shortcut_label(): @@ -209,7 +213,8 @@ def shutdown(app): def finish_talk(app, stdscr): - """Dipanggil main loop saat app.talk_done ter-set: sisipkan hasil.""" + """Dipanggil main loop saat app.talk_done ter-set: sisipkan hasil (atau + langsung submit bila config.asr.auto_submit aktif).""" res = getattr(app, "_talk_result", None) app._talk_result = None if getattr(app, "talk_cancelled", False): @@ -222,6 +227,27 @@ def finish_talk(app, stdscr): if not text: log(app, "system", " Tidak ada ucapan terdeteksi.") return + if config.ASR_AUTO_SUBMIT: + # Auto-submit: taruh teks di buffer lalu kirim (submit() yang + # membersihkan buffer). Jika user sudah mengetik sesuatu sebelum + # transkrip selesai, teks tidak ditimpa — disisipkan saja. + if not "\n".join(app.input_buffer).strip(): + app.input_buffer = text.split("\n") + app.input_line = len(app.input_buffer) - 1 + app.input_col = len(app.input_buffer[-1]) + submit(app, stdscr) + log(app, "system", + f" \u266a [{res.get('provider', '?')} / {res.get('model', '?')}] " + "teks otomatis dikirim") + else: + insert_text(app, text) + log(app, "system", + f" \u266a [{res.get('provider', '?')} / {res.get('model', '?')}] " + "form sedang terisi \u2014 hasil disisipkan, tidak auto-submit " + "(Ctrl+Enter untuk kirim)") + if app.scroll_follow: + app.scroll = 999999 + return if insert_text(app, text): log(app, "system", f" \u266a [{res.get('provider', '?')} / {res.get('model', '?')}] "