diff --git a/config.default.yaml b/config.default.yaml index 702ee42..2a4d9c0 100644 --- a/config.default.yaml +++ b/config.default.yaml @@ -2,9 +2,9 @@ agent: character : hendrik # Directory name in agent/characters// - max_iterations : 40 # step(s) + max_iterations : 80 # step(s) max_history_chat : 0 # message(s) - 0 for unlimited (Dead Config) - max_tool_output : 0 # character(s) - 0 for unlimited (Connected to AGENT_MAX_TOOL_OUTPUT but Dead Config) + max_tool_output : 0 # character(s) - 0 for unlimited user: id: # Fill your user UUID here, empty = TUI/MUC guest @@ -15,26 +15,23 @@ llm: moop: # Model Option db_path: "~/.config/hendrik/moop.sqlite3" -asr: # Speech-to-Text (push-to-talk TUI). Model dikelola via moop tipe 'asr'. - record_dir : "~/.config/hendrik/recordings" # lokasi file hasil rekaman - record_key : f9 # tombol push-to-talk (f1..f12 / ctrl-) - record_backend : pulse # ffmpeg input: pulse | alsa | oss | windows - device : default # nama device input - windows_mic : "" # backend=windows: nama device dshow, - # contoh "Microphone Array (Realtek(R) Audio)" - format : wav # wav | ogg (opus) - sample_rate : 16000 # Hz - channels : 1 - 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) +asr: + record_dir : "~/.config/hendrik/recordings" + record_backend : pulse # ffmpeg input: pulse | alsa | oss | windows + device : default + windows_mic : "" # if backend==windows: device dshow name, example: "Microphone Array (Realtek(R) Audio)" + format : wav # wav | ogg (opus) + sample_rate : 16000 # Hz + channels : 1 + max_seconds : 120 # second + transcribe_timeout : 60 # second + keep_recordings : false + auto_submit : true -session: # Chat session +session: db_path: "~/.config/hendrik/sessions.json" -ragroleplay: # Use nomic-embed-text +ragroleplay: db_path : "~/.config/hendrik/ragroleplay" vector_size : 768 # used on table create only @@ -53,7 +50,7 @@ xmpp: # - "room2@conference.server" nickname: "" # custom MUC nickname (empty = use username) selective_response: true # true = only response if mentioned/relevant - debug: false # true = tampilkan log debug menyeluruh (semua stanza XML, lifecycle, dll) + debug: false # true = display debug log (stanza XML, lifecycle, etc) mute_session_end: false # true = tidak kirim pesan saat sesi berakhir (timeout) telegram: diff --git a/config.py b/config.py index d281d49..e23c22d 100644 --- a/config.py +++ b/config.py @@ -58,8 +58,8 @@ ASR_MAX_SECONDS = int(_yaml_get("asr", "max_seconds", default=60)) ASR_TIMEOUT = int(_yaml_get("asr", "transcribe_timeout", default=60)) ASR_KEEP_RECORDINGS = _as_bool(_yaml_get("asr", "keep_recordings", default=True), 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() +# Tombol push-to-talk dipatok tetap ke F9 (tidak bisa dikonfigurasi). +# Lihat interfaces/tui/actions.py -> ACTIONS["push_to_talk"]. # true = hasil transkripsi langsung dikirim sebagai pesan, # false = hasil hanya disisipkan ke form input (user kirim sendiri via Ctrl+Enter). diff --git a/hendrik b/hendrik index 32c3118..6ab73b1 100755 --- a/hendrik +++ b/hendrik @@ -1,5 +1,5 @@ #!/bin/bash -# wrapper to run the TUI agent from anywhere +# wrapper to run the hendrik agent from anywhere # Set HENDRIK_DIR env var to override, or update the default below DEFAULT_DIR="/opt/hendrik" diff --git a/hendrik.py b/hendrik.py index 5578b19..77f8b5a 100644 --- a/hendrik.py +++ b/hendrik.py @@ -14,20 +14,20 @@ from interfaces.tui import HendrikTUI tools_definition = [ # Coder Tools - gadget.tools_mapping( schema = coder.schema_read_file, handler = coder.read_file ), - gadget.tools_mapping( schema = coder.schema_write_file, handler = coder.write_file ), - gadget.tools_mapping( schema = coder.schema_edit_file, handler = coder.edit_file ), - gadget.tools_mapping( schema = coder.schema_run_bash, handler = coder.run_bash ), - gadget.tools_mapping( schema = coder.schema_search_code, handler = coder.search_code ), - gadget.tools_mapping( schema = coder.schema_git_operation, handler = coder.git_operation ), - gadget.tools_mapping( schema = coder.schema_search_grep, handler = coder.search_grep ), - gadget.tools_mapping( schema = coder.schema_search_glob, handler = coder.search_glob ), + gadget.tools_mapping( schema = coder.schema_read_file, handler = coder.read_file ), + gadget.tools_mapping( schema = coder.schema_write_file, handler = coder.write_file ), + gadget.tools_mapping( schema = coder.schema_edit_file, handler = coder.edit_file ), + gadget.tools_mapping( schema = coder.schema_run_bash, handler = coder.run_bash ), + gadget.tools_mapping( schema = coder.schema_search_code, handler = coder.search_code ), + gadget.tools_mapping( schema = coder.schema_git_operation, handler = coder.git_operation ), + gadget.tools_mapping( schema = coder.schema_search_grep, handler = coder.search_grep ), + gadget.tools_mapping( schema = coder.schema_search_glob, handler = coder.search_glob ), # Carrack Tools - gadget.tools_mapping( schema = carrack.schema_sendhttprequest, handler = carrack.sendhttprequest ), + gadget.tools_mapping( schema = carrack.schema_sendhttprequest, handler = carrack.sendhttprequest ), # Vision Tools - gadget.tools_mapping( schema = vision.schema_describe_image, handler = vision.describe_image ), + gadget.tools_mapping( schema = vision.schema_describe_image, handler = vision.describe_image ), # Image Generation Tools - gadget.tools_mapping( schema = imagegen.schema_generate_image, handler = imagegen.generate_image ), + gadget.tools_mapping( schema = imagegen.schema_generate_image, handler = imagegen.generate_image ), # Instruction Tools gadget.tools_mapping( schema = smart.schema_instruction_use, handler = smart.instruction_use ), # snippet (global) gadget.tools_mapping( schema = smart.schema_instruction_char_use, handler = smart.instruction_char_use ), # snippet (private per character) diff --git a/interfaces/tui/actions.py b/interfaces/tui/actions.py index ff4c73e..a77922a 100644 --- a/interfaces/tui/actions.py +++ b/interfaces/tui/actions.py @@ -16,6 +16,7 @@ from .keycodes import ( KEY_F2, KEY_F4, KEY_F6, + KEY_F9, ) from .input import ( new_session_popup, @@ -29,11 +30,8 @@ from .moopui import ( providers_manage_popup, ) from .agent import submit, log -from .keycodes import resolve_key from . import talk as talk_mod -import config as _config - # ---------------------------------------------------------------- handlers --- def _exit_app(app, stdscr): @@ -55,8 +53,8 @@ def _record_talk(app, stdscr): def _talk_configured(app): - # Tombol rekam valid (di-resolve dari config). Ketersediaan chain 'asr' - # divalidasi di handler supaya bisa memberi pesan panduan, bukan diam. + # Tombol rekam dipatok tetap (F9) sehingga selalu valid. Ketersediaan + # chain 'asr' divalidasi di handler supaya bisa memberi pesan panduan. return getattr(app, "talk_key", None) is not None @@ -118,12 +116,12 @@ ACTIONS = { "label": "Change Workspace", "mnemonic": "C", "shortcut": "F6", "handler": workspace_popup, "enabled": _ready, }, - # Push-to-Talk (ASR): tombol berasal dari config.asr.record_key (default F9). + # Push-to-Talk (ASR): tombol dipatok tetap F9 (tidak dikonfigurasi). # Handler talk_toggle memvalidasi chain moop 'asr' + ketersediaan mic. "push_to_talk": { - "key": resolve_key(getattr(_config, "ASR_RECORD_KEY", "f9")), + "key": KEY_F9, "label": "Push-to-Talk", "mnemonic": "T", - "shortcut": (getattr(_config, "ASR_RECORD_KEY", "f9") or "").upper(), + "shortcut": "F9", "handler": _record_talk, "enabled": lambda app: _ready(app) and _talk_configured(app), }, diff --git a/interfaces/tui/app.py b/interfaces/tui/app.py index f29f702..38c7bc7 100644 --- a/interfaces/tui/app.py +++ b/interfaces/tui/app.py @@ -60,7 +60,7 @@ class HendrikTUI: self.talk_done = threading.Event() self._talk_thread: threading.Thread | None = None self._talk_result: dict | None = None - # Kode tombol rekam dari config (None = tombol tidak valid/fitur mati) + # Kode tombol rekam: dipatok tetap F9 (lihat ACTIONS["push_to_talk"]) from .actions import ACTIONS as _ACTIONS self.talk_key = _ACTIONS["push_to_talk"]["key"] self.talk_label = _ACTIONS["push_to_talk"]["shortcut"] diff --git a/interfaces/tui/keycodes.py b/interfaces/tui/keycodes.py index de4c4c3..61ec6e3 100644 --- a/interfaces/tui/keycodes.py +++ b/interfaces/tui/keycodes.py @@ -263,30 +263,6 @@ def read_key(stdscr, timeout_ms: int = -1) -> int: return KEY_ESC -def resolve_key(name) -> int | None: - """Petakan nama tombol dari config menjadi kode kunci. - - Format: "f1".."f12", "ctrl-", atau "esc"/"enter". - Mengembalikan None jika tidak dikenal.""" - n = str(name or "").strip().lower().replace("+", "-").replace(" ", "") - if not n: - return None - _F_KEYS = {f"f{i}": k for i, k in enumerate( - [KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, - KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12], start=1)} - if n in _F_KEYS: - return _F_KEYS[n] - if n == "esc": - return KEY_ESC - if n == "enter": - return 13 - if n.startswith("ctrl-") and len(n) == 6: - ch = n[-1] - if "a" <= ch <= "z": - return ord(ch) & 0x1F - return None - - def is_alt_key(key: int) -> bool: return _KEY_ALT_BASE <= key <= _KEY_ALT_BASE + 126 diff --git a/interfaces/tui/talk.py b/interfaces/tui/talk.py index a4875da..5408d67 100644 --- a/interfaces/tui/talk.py +++ b/interfaces/tui/talk.py @@ -1,7 +1,7 @@ # talk.py — Push-to-Talk (ASR/STT) untuk TUI. # # Alur: -# * F9 (configurable) → mulai rekam (ffmpeg subprocess, non-blocking). +# * F9 (dipatok, tidak configurable) → mulai rekam (ffmpeg subprocess, non-blocking). # * F9 lagi / Enter → stop rekam → transkrip (thread) → hasil # disisipkan ke form input pada posisi kursor, # TIDAK submit (default). @@ -25,12 +25,13 @@ from tools import asr as asr_mod from .agent import log, submit +# Tombol push-to-talk dipatok tetap ke F9 (lihat actions.py & keycodes.py). +TALK_SHORTCUT_LABEL = "F9" + + def _shortcut_label(): - """Label tombol rekam untuk pesan UI (dari config.asr.record_key).""" - n = (config.ASR_RECORD_KEY or "f9").strip().lower() - if n.startswith("ctrl-") and len(n) == 6: - return f"Ctrl+{n[5].upper()}" - return n.upper() + """Label tombol rekam untuk pesan UI (selalu 'F9').""" + return TALK_SHORTCUT_LABEL # ------------------------------------------------------------------ helpers --- diff --git a/plan/asr-push-to-talk.md b/plan/asr-push-to-talk.md index 47116fe..6bd8860 100644 --- a/plan/asr-push-to-talk.md +++ b/plan/asr-push-to-talk.md @@ -13,7 +13,6 @@ tekan tombol → bicara → stop → teks hasil transkripsi masuk ke form input ## Konfigurasi (config.yaml, blok `asr:`) asr: record_dir : "~/.config/hendrik/recordings" # lokasi file rekaman - record_key : f9 # f1..f12 / ctrl- record_backend : pulse # pulse | alsa | oss | windows (dshow) device : default windows_mic : "" # backend=windows: nama device dshow diff --git a/plan/milestone/coding-standard.png b/plan/milestone/coding-standard.png index 6e8ef04..0468f58 100644 Binary files a/plan/milestone/coding-standard.png and b/plan/milestone/coding-standard.png differ diff --git a/plan/milestone/coding-standard.puml b/plan/milestone/coding-standard.puml index 85a4354..de199cd 100644 --- a/plan/milestone/coding-standard.puml +++ b/plan/milestone/coding-standard.puml @@ -28,12 +28,10 @@ mindmapDiagram { ;top to bottom direction * root <> -** .env <> -** .env.example <> ** .gitignore <> +** LICENSE <> +** config.default.yaml <> ** config.py -** config.yaml <> -** config.default.yaml ** hendrik <> ** hendrik.py <> ** requirements.txt <>