sessionvoice 0.3.1__tar.gz → 0.3.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/PKG-INFO +28 -15
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/README.md +27 -14
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/pyproject.toml +14 -2
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/_keeper/call-ears +8 -5
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/_keeper/ears-keeper.sh +57 -18
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/_keeper/ears-mail.py +83 -45
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/_keeper/ears-sounds.py +25 -1
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/_keeper/mouth-sounds.py +24 -3
- sessionvoice-0.3.2/src/ears/_keeper/phone_roots.py +73 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/_settings/ears.schema.json +1 -1
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/_settings/mouth.schema.json +6 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/_speakers/run.sh +2 -3
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/_speakers/speaker-sidecar2.py +162 -34
- sessionvoice-0.3.2/src/ears/callrec.py +26 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/eval.py +2 -3
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/heard.py +1 -1
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/home.py +82 -1
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/listen/loop.py +39 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/listen/sources.py +47 -12
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/services.py +197 -38
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/__init__.py +83 -8
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/__main__.py +42 -3
- sessionvoice-0.3.2/src/mouth/_separator/requirements.lock +703 -0
- sessionvoice-0.3.2/src/mouth/_separator/run.py +302 -0
- sessionvoice-0.3.2/src/mouth/autosplit.py +260 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/backends.py +31 -9
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/bed.py +12 -3
- sessionvoice-0.3.2/src/mouth/instrumental.py +121 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/notice.py +2 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/output.py +9 -13
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/player.py +67 -8
- sessionvoice-0.3.2/src/mouth/separator.py +429 -0
- sessionvoice-0.3.2/src/mouth/sidecar.py +318 -0
- sessionvoice-0.3.2/src/mouth/split.py +619 -0
- sessionvoice-0.3.2/src/mouth/splitwait.py +162 -0
- sessionvoice-0.3.2/src/mouth/stems.py +155 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/voices.py +65 -4
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/sessionvoice.egg-info/PKG-INFO +28 -15
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/sessionvoice.egg-info/SOURCES.txt +20 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/sessionvoice.egg-info/entry_points.txt +4 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/talk/cli.py +29 -4
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/talk/index.html +3 -1
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/talk/server.py +119 -9
- sessionvoice-0.3.2/src/talk/web.py +76 -0
- sessionvoice-0.3.2/tests/test_call_sock_search.py +292 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_call_tap_silero.py +1 -1
- sessionvoice-0.3.2/tests/test_callrec.py +55 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_clock_check.py +9 -7
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_configuration_docs.py +5 -2
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_ears_mail.py +124 -33
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_ears_wake.py +9 -9
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_eval.py +13 -9
- sessionvoice-0.3.2/tests/test_instrumental_docs.py +251 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_deaf.py +1 -1
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_defaults.py +16 -9
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_in_wheel.py +2 -1
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_speakers_deaf.py +84 -1
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_launchd_templates.py +73 -10
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_redecode.py +70 -0
- sessionvoice-0.3.2/tests/test_log_dir.py +443 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_say_again_e2e.py +3 -1
- sessionvoice-0.3.2/tests/test_separator_runner.py +690 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_service_hash_pins.py +5 -1
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_install_checks.py +93 -14
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_install_golden.py +18 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_install_load_fail.py +11 -8
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_phone_only.py +39 -4
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_sounds.py +1 -1
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_speakers.py +3 -2
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_speech.py +3 -1
- sessionvoice-0.3.2/tests/test_services_speech_cache.py +217 -0
- sessionvoice-0.3.2/tests/test_services_stt_row.py +98 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_talk.py +1 -1
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_settings_dir.py +13 -1
- sessionvoice-0.3.2/tests/test_settings_schema_refresh.py +241 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_speakers_call.py +114 -15
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_speakers_follow.py +331 -15
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_speakers_parity.py +3 -2
- sessionvoice-0.3.2/tests/test_talk_kit.py +410 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_talk_launchd.py +6 -3
- sessionvoice-0.3.2/tests/test_talk_prefix.py +250 -0
- sessionvoice-0.3.2/tests/test_talk_web_room.py +333 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_wheel_build.py +16 -5
- sessionvoice-0.3.1/src/ears/callrec.py +0 -34
- sessionvoice-0.3.1/tests/test_call_sock_search.py +0 -90
- sessionvoice-0.3.1/tests/test_callrec.py +0 -39
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/LICENSE +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/setup.cfg +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/__init__.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/_keeper/held_stream.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/_keeper/input-watch.swift +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/_settings/ears-sounds.schema.json +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/_speakers/cluster-room.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/_speakers/make-refs.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/_speakers/requirements.lock +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/_speech/build-constraints.txt +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/_speech/requirements.lock +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/backends/__init__.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/backends/apple.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/backends/nemotron.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/backends/proc.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/backends/whisper.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/cli.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/envnames.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/eval_mlx.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/events.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/extras.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/heard_wait.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/keys.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/lag.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/listen/__init__.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/listen/__main__.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/listen/aged.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/listen/chunker.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/listen/detector.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/listen/missed.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/listen/silero.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/listen/sink.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/listen/speakers.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/listen/streaming.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/listen/stt.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/listen/turnaudio.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/listen/watchdog.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/maildir.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/message_bin.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/operator_config.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/previews.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/recording.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/redecode.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/serve.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/smartturn.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/speakers.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/speech.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/turns.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/vad.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/wake.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/audio.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/bargeswitch.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/box.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/canplay.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/coreaudio.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/devices.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/duckswitch.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/hold.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/inbox.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/level.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/maillog.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/mpv.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/onset.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/paths.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/reply.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/resolver.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/timeline.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/sessionvoice.egg-info/dependency_links.txt +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/sessionvoice.egg-info/requires.txt +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/sessionvoice.egg-info/top_level.txt +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/talk/__init__.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/talk/__main__.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/talk/manifest.json +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/talk/token_store.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_anchor.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_apple_assets_why_not.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_arm_finds_heard_first.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_arm_help.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_audio_devices_schema.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_audio_fence.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_bin_tool_fallback.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_call_tap_help.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_call_tap_listen_off.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_capture_mislabel.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_changelog_union.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_child_pytest.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_conftest_ffmpeg.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_conftest_order.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_dependencies_docs.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_docs_layout.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_ears_keys.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_ears_no_backend.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_ears_serve_cli.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_ears_settings_schema.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_ears_sounds_call.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_ears_sounds_stream.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_ears_stt_key.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_echo_cut.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_echo_speaker.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_emitted_neutral.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_envnames.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_export_ignore.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_extras.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_github_links.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_heard_contract.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_heard_cursor_by.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_heard_partials_only.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_heard_wait.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_heard_writer.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_home.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_install_apple_assets.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_install_apple_optin.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_install_silero_pin.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_adopt.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_again.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_beat.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_calls.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_fallback.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_lag.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_lid.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_listen_off.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_missed_reply.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_model.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_room.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_stop.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_stt_deaf.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_swiftc_guard.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_zeros.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_launchd_no_machine_paths.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_aged.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_audio_stamp.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_call_source.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_clean_exit.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_hygiene.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_loop.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_missed_reply.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_parts.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_silero.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_speaker_agent.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_speaker_split.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_stack_dump.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_stream_feed.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_streaming.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_stt_follow.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_stt_waits.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_turn_speaker.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_vad_edges.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_watchdog.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_wedge_scenarios.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_live_home_guard.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_lock_wheels.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_maildir_private.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_marketplace_description.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_message_bin.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_mouth_notice_fence.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_mouth_schema_plain.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_mouth_sounds_scoop_by.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_mouth_sounds_wake.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_name_hint_bench.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_no_real_times.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_no_time_of_day_stamps.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_no_tracker_ids.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_no_work_hour_stamps.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_operator_config.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_persona_defaults.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_plugin_install_doc.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_readme_links.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_recording.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_recording_retention.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_release_script.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_scanlist.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_sendmail_tripwire.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_service_build_constraints.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_adopt_cold.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_bootstrap.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_dry_run.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_install_preflight.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_kit_labels.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_label_move.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_legacy_plists.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_one_keeper.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_prefetch.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_speakers_checks.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_stt_model.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_weights.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_settings_in_wheel.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_settings_stranded.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_speakers.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_speakers_enrol.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_speakers_in_package.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_speakers_name.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_speech.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_stt_name_hint.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_system_python_scripts.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_talk_cli.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_talk_generalise.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_talk_host_check.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_talk_page_pair.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_talk_pair.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_talk_server.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_talk_token.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_talk_token_gate.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_talk_unix.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_turn_audio.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_turns.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_version_sync.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_waiter_shims.py +0 -0
- {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_waiters.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sessionvoice
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: One listening engine for coding agents and the Delta bot: Apple / Nemotron / whisper -> one event stream -> VAD -> turns
|
|
5
5
|
Author-email: Mike Bailey <mike@sessionkit.ai>
|
|
6
6
|
License-Expression: AGPL-3.0-or-later
|
|
@@ -133,10 +133,15 @@ pair of ears on a Mac (depth in [docs/architecture.md](docs/architecture.md)):
|
|
|
133
133
|
package or a checkout; a bare install never does.
|
|
134
134
|
- **Turn mail** (`keeper/ears-mail.py`, launchd `dev.sessionkit.voice.ears-mail`):
|
|
135
135
|
mails each of the user's finished turns To and From
|
|
136
|
-
`heard
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
136
|
+
`heard+room@<host>`, whatever input device the capture is following, with
|
|
137
|
+
the `Ears-*` headers of the pager genie's contract (sessionmail
|
|
138
|
+
`openspec/changes/pager-genie`). The device is named by the `Ears-Device`
|
|
139
|
+
header. A session hears the room, on every device, by subscribing to that
|
|
140
|
+
one address. While `--device-copy` is on (the default), the same send (one
|
|
141
|
+
message, one Message-ID) also goes Cc `heard+<device>@<host>`, so a
|
|
142
|
+
session subscribed to a device's address keeps hearing that device. The
|
|
143
|
+
room's tag is `--room` (`EARS_MAIL_ROOM`, default `room`). The box is
|
|
144
|
+
`heard` (`--sender`), because `ears` is this project's own box.
|
|
140
145
|
sessionmail trusts only its keeper's From (`_voice.KEEPER`), so the
|
|
141
146
|
plist loads only once that says `heard` too. `--to seats` is the older mail, To
|
|
142
147
|
each seat a turn is for. **Never run `--to seats` and `--to device` for
|
|
@@ -239,21 +244,23 @@ Every key, its type and its default:
|
|
|
239
244
|
- **The heard log:** `<home>/logs/conversations/heard_YYYY-MM-DD.jsonl`,
|
|
240
245
|
one JSON line for each thing the ears heard and the mouth said, where
|
|
241
246
|
`<home>` is the home above. A new file starts each day.
|
|
242
|
-
- **The services' logs:**
|
|
247
|
+
- **The services' logs:** `~/.sessionkit/voice/log/` (`EARS_LOG_DIR` moves it).
|
|
243
248
|
`ears-keeper.log` is what the keeper did (devices, restarts, calls, a
|
|
244
249
|
deaf mic); `ears-keeper-capture.log` is the room capture's own output;
|
|
245
250
|
`ears-mail.log` is each turn mail sent or skipped; `ears-mouth.log` is
|
|
246
|
-
the mouth's player.
|
|
251
|
+
the mouth's player. Logs written before the move stay where they
|
|
252
|
+
were, and nothing deletes them. `ears services status` prints the folder.
|
|
247
253
|
|
|
248
254
|
Follow one with `tail -F`, for example
|
|
249
|
-
`tail -F
|
|
255
|
+
`tail -F ~/.sessionkit/voice/log/ears-keeper.log`, or today's heard log with
|
|
250
256
|
`tail -F <home>/logs/conversations/heard_$(date +%F).jsonl`.
|
|
251
257
|
Every log file and what each line means:
|
|
252
258
|
[docs/logs.md](docs/logs.md).
|
|
253
259
|
|
|
254
260
|
- **Health:** `ears services status` prints one row per launch agent
|
|
255
|
-
(`running, pid N`, `loaded, not running` or `not installed
|
|
256
|
-
|
|
261
|
+
(`running, pid N`, `loaded, not running` or `not installed`; the
|
|
262
|
+
`stt` row, the optional `--apple` server, also names what the ears hear
|
|
263
|
+
through), the keeper's `mode` (`input-watch`, or `polling system_profiler` and why),
|
|
257
264
|
the one that serves speech on `127.0.0.1:8890`, and the home in use.
|
|
258
265
|
|
|
259
266
|
## Use
|
|
@@ -297,11 +304,17 @@ at `http://127.0.0.1:<port>/`, never at `localhost`. The code is `src/talk/`.
|
|
|
297
304
|
`ears services install --only talk` keeps it running as a launch agent
|
|
298
305
|
(opt-in; a bare install never does).
|
|
299
306
|
|
|
307
|
+
In the kit's web page, Talk is a room: the kit's front door runs
|
|
308
|
+
`talk serve --kit` on a unix socket and logs you in, so there is nothing to
|
|
309
|
+
pair and you never run `--kit` by hand
|
|
310
|
+
([docs/talk.md](docs/talk.md#in-the-kits-web-page)).
|
|
311
|
+
|
|
300
312
|
## Install
|
|
301
313
|
|
|
302
314
|
The distribution is `sessionvoice` (`sessionvoice[nemotron]`, `sessionvoice[all]`);
|
|
303
|
-
it installs the `ears`, `ears-serve`, `mouth` and `
|
|
304
|
-
`pip install sessionvoice` carries what `mouth say` needs.
|
|
315
|
+
it installs the `ears`, `ears-serve`, `mouth`, `talk` and `sessionvoice-web`
|
|
316
|
+
commands. A bare `pip install sessionvoice` carries what `mouth say` needs.
|
|
317
|
+
The extras are
|
|
305
318
|
`capture` (the mic and call ears), `silero` (the Silero VAD), `smart-turn`
|
|
306
319
|
(the Smart Turn gate's ONNX backend), `nemotron` (mlx-audio) and `all`. An
|
|
307
320
|
import that needs one fails naming it, as `pip install 'sessionvoice[EXTRA]'`.
|
|
@@ -339,7 +352,7 @@ with its fix, writes nothing and exits 2. A launch agent that fails to load
|
|
|
339
352
|
also makes it exit 2, once every other service has been tried: one block on
|
|
340
353
|
stderr names each service that did not load, the install log and `ears
|
|
341
354
|
services status`. Every real run appends its output and launchctl's answers,
|
|
342
|
-
timestamped, to
|
|
355
|
+
timestamped, to `install.log` in the log folder, `~/.sessionkit/voice/log/`, and prints that path
|
|
343
356
|
as its `log` row ([logs](docs/logs.md#the-log-files)). It then:
|
|
344
357
|
|
|
345
358
|
- first finds the mlx-audio speech server on `:8890`, or installs its own
|
|
@@ -390,7 +403,7 @@ as its `log` row ([logs](docs/logs.md#the-log-files)). It then:
|
|
|
390
403
|
read (no `message`, or one older than 0.2.0), it warns, makes no box,
|
|
391
404
|
checks neither, and goes on.
|
|
392
405
|
On a postfix host it also prints the box to subscribe to:
|
|
393
|
-
`message subscription add heard+
|
|
406
|
+
`message subscription add heard+room`;
|
|
394
407
|
- `--agent NAME` adds NAME's wake seat (`ears wake add NAME`) and sends the
|
|
395
408
|
keeper's notices to NAME (`EARS_NOTIFY`; `--notify ADDR` sends them
|
|
396
409
|
elsewhere). Without it, and when the wake file names no seat, nobody would
|
|
@@ -455,7 +468,7 @@ the agent's next tool call. `EARS_HEARD_HOOK=off` silences it without
|
|
|
455
468
|
editing the file.
|
|
456
469
|
|
|
457
470
|
`ears heard hook` is the canonical heard hook. A seat that also gets heard
|
|
458
|
-
mail (it subscribes to `heard
|
|
471
|
+
mail (it subscribes to `heard+room`) gets each finished turn by mail, so
|
|
459
472
|
give it `ears heard hook --partials-only || exit 1` instead: the hook then
|
|
460
473
|
shows only the partial preview of a turn still being spoken and the shown
|
|
461
474
|
events (wake word, barge-in, device change), never the finished turn a
|
|
@@ -89,10 +89,15 @@ pair of ears on a Mac (depth in [docs/architecture.md](docs/architecture.md)):
|
|
|
89
89
|
package or a checkout; a bare install never does.
|
|
90
90
|
- **Turn mail** (`keeper/ears-mail.py`, launchd `dev.sessionkit.voice.ears-mail`):
|
|
91
91
|
mails each of the user's finished turns To and From
|
|
92
|
-
`heard
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
92
|
+
`heard+room@<host>`, whatever input device the capture is following, with
|
|
93
|
+
the `Ears-*` headers of the pager genie's contract (sessionmail
|
|
94
|
+
`openspec/changes/pager-genie`). The device is named by the `Ears-Device`
|
|
95
|
+
header. A session hears the room, on every device, by subscribing to that
|
|
96
|
+
one address. While `--device-copy` is on (the default), the same send (one
|
|
97
|
+
message, one Message-ID) also goes Cc `heard+<device>@<host>`, so a
|
|
98
|
+
session subscribed to a device's address keeps hearing that device. The
|
|
99
|
+
room's tag is `--room` (`EARS_MAIL_ROOM`, default `room`). The box is
|
|
100
|
+
`heard` (`--sender`), because `ears` is this project's own box.
|
|
96
101
|
sessionmail trusts only its keeper's From (`_voice.KEEPER`), so the
|
|
97
102
|
plist loads only once that says `heard` too. `--to seats` is the older mail, To
|
|
98
103
|
each seat a turn is for. **Never run `--to seats` and `--to device` for
|
|
@@ -195,21 +200,23 @@ Every key, its type and its default:
|
|
|
195
200
|
- **The heard log:** `<home>/logs/conversations/heard_YYYY-MM-DD.jsonl`,
|
|
196
201
|
one JSON line for each thing the ears heard and the mouth said, where
|
|
197
202
|
`<home>` is the home above. A new file starts each day.
|
|
198
|
-
- **The services' logs:**
|
|
203
|
+
- **The services' logs:** `~/.sessionkit/voice/log/` (`EARS_LOG_DIR` moves it).
|
|
199
204
|
`ears-keeper.log` is what the keeper did (devices, restarts, calls, a
|
|
200
205
|
deaf mic); `ears-keeper-capture.log` is the room capture's own output;
|
|
201
206
|
`ears-mail.log` is each turn mail sent or skipped; `ears-mouth.log` is
|
|
202
|
-
the mouth's player.
|
|
207
|
+
the mouth's player. Logs written before the move stay where they
|
|
208
|
+
were, and nothing deletes them. `ears services status` prints the folder.
|
|
203
209
|
|
|
204
210
|
Follow one with `tail -F`, for example
|
|
205
|
-
`tail -F
|
|
211
|
+
`tail -F ~/.sessionkit/voice/log/ears-keeper.log`, or today's heard log with
|
|
206
212
|
`tail -F <home>/logs/conversations/heard_$(date +%F).jsonl`.
|
|
207
213
|
Every log file and what each line means:
|
|
208
214
|
[docs/logs.md](docs/logs.md).
|
|
209
215
|
|
|
210
216
|
- **Health:** `ears services status` prints one row per launch agent
|
|
211
|
-
(`running, pid N`, `loaded, not running` or `not installed
|
|
212
|
-
|
|
217
|
+
(`running, pid N`, `loaded, not running` or `not installed`; the
|
|
218
|
+
`stt` row, the optional `--apple` server, also names what the ears hear
|
|
219
|
+
through), the keeper's `mode` (`input-watch`, or `polling system_profiler` and why),
|
|
213
220
|
the one that serves speech on `127.0.0.1:8890`, and the home in use.
|
|
214
221
|
|
|
215
222
|
## Use
|
|
@@ -253,11 +260,17 @@ at `http://127.0.0.1:<port>/`, never at `localhost`. The code is `src/talk/`.
|
|
|
253
260
|
`ears services install --only talk` keeps it running as a launch agent
|
|
254
261
|
(opt-in; a bare install never does).
|
|
255
262
|
|
|
263
|
+
In the kit's web page, Talk is a room: the kit's front door runs
|
|
264
|
+
`talk serve --kit` on a unix socket and logs you in, so there is nothing to
|
|
265
|
+
pair and you never run `--kit` by hand
|
|
266
|
+
([docs/talk.md](docs/talk.md#in-the-kits-web-page)).
|
|
267
|
+
|
|
256
268
|
## Install
|
|
257
269
|
|
|
258
270
|
The distribution is `sessionvoice` (`sessionvoice[nemotron]`, `sessionvoice[all]`);
|
|
259
|
-
it installs the `ears`, `ears-serve`, `mouth` and `
|
|
260
|
-
`pip install sessionvoice` carries what `mouth say` needs.
|
|
271
|
+
it installs the `ears`, `ears-serve`, `mouth`, `talk` and `sessionvoice-web`
|
|
272
|
+
commands. A bare `pip install sessionvoice` carries what `mouth say` needs.
|
|
273
|
+
The extras are
|
|
261
274
|
`capture` (the mic and call ears), `silero` (the Silero VAD), `smart-turn`
|
|
262
275
|
(the Smart Turn gate's ONNX backend), `nemotron` (mlx-audio) and `all`. An
|
|
263
276
|
import that needs one fails naming it, as `pip install 'sessionvoice[EXTRA]'`.
|
|
@@ -295,7 +308,7 @@ with its fix, writes nothing and exits 2. A launch agent that fails to load
|
|
|
295
308
|
also makes it exit 2, once every other service has been tried: one block on
|
|
296
309
|
stderr names each service that did not load, the install log and `ears
|
|
297
310
|
services status`. Every real run appends its output and launchctl's answers,
|
|
298
|
-
timestamped, to
|
|
311
|
+
timestamped, to `install.log` in the log folder, `~/.sessionkit/voice/log/`, and prints that path
|
|
299
312
|
as its `log` row ([logs](docs/logs.md#the-log-files)). It then:
|
|
300
313
|
|
|
301
314
|
- first finds the mlx-audio speech server on `:8890`, or installs its own
|
|
@@ -346,7 +359,7 @@ as its `log` row ([logs](docs/logs.md#the-log-files)). It then:
|
|
|
346
359
|
read (no `message`, or one older than 0.2.0), it warns, makes no box,
|
|
347
360
|
checks neither, and goes on.
|
|
348
361
|
On a postfix host it also prints the box to subscribe to:
|
|
349
|
-
`message subscription add heard+
|
|
362
|
+
`message subscription add heard+room`;
|
|
350
363
|
- `--agent NAME` adds NAME's wake seat (`ears wake add NAME`) and sends the
|
|
351
364
|
keeper's notices to NAME (`EARS_NOTIFY`; `--notify ADDR` sends them
|
|
352
365
|
elsewhere). Without it, and when the wake file names no seat, nobody would
|
|
@@ -411,7 +424,7 @@ the agent's next tool call. `EARS_HEARD_HOOK=off` silences it without
|
|
|
411
424
|
editing the file.
|
|
412
425
|
|
|
413
426
|
`ears heard hook` is the canonical heard hook. A seat that also gets heard
|
|
414
|
-
mail (it subscribes to `heard
|
|
427
|
+
mail (it subscribes to `heard+room`) gets each finished turn by mail, so
|
|
415
428
|
give it `ears heard hook --partials-only || exit 1` instead: the hook then
|
|
416
429
|
shows only the partial preview of a turn still being spoken and the shown
|
|
417
430
|
events (wake word, barge-in, device change), never the finished turn a
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "sessionvoice"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.2"
|
|
4
4
|
description = "One listening engine for coding agents and the Delta bot: Apple / Nemotron / whisper -> one event stream -> VAD -> turns"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "AGPL-3.0-or-later"
|
|
@@ -44,6 +44,12 @@ ears = "ears.cli:main"
|
|
|
44
44
|
ears-serve = "ears.serve:main"
|
|
45
45
|
mouth = "mouth.__main__:main"
|
|
46
46
|
talk = "talk.cli:main"
|
|
47
|
+
# the kit's web front door asks it for Talk's room: `sessionvoice-web web-room`, src/talk/web.py
|
|
48
|
+
sessionvoice-web = "talk.web:main"
|
|
49
|
+
|
|
50
|
+
# the same room, for a front door installed in this environment: it loads and calls room()
|
|
51
|
+
[project.entry-points."sessionkit.web"]
|
|
52
|
+
talk = "talk.web:room"
|
|
47
53
|
|
|
48
54
|
[dependency-groups]
|
|
49
55
|
# what `make test` runs on: `uv sync` installs this group by default. The capture
|
|
@@ -64,8 +70,9 @@ where = ["src"]
|
|
|
64
70
|
[tool.setuptools.package-data]
|
|
65
71
|
# What the keeper agent runs, shipped so `ears services install` from a wheel points it at a
|
|
66
72
|
# real script: the keeper, its call ears (a child) and the input watcher it
|
|
67
|
-
# builds, and the turn mailer the mail agent runs (stdlib only
|
|
73
|
+
# builds, and the turn mailer the mail agent runs (stdlib only). The sounds
|
|
68
74
|
# agents' two watchers, and held_stream.py, which ears-sounds.py imports from beside itself.
|
|
75
|
+
# phone_roots.py: where a Delta bot keeps its call sockets, imported by call-ears and ears-mail.py.
|
|
69
76
|
# src/ears/_keeper/ holds symlinks into keeper/; the build copies the files.
|
|
70
77
|
# The speaker side-car: run.sh, the side-car, the enrol's cluster-room.py and
|
|
71
78
|
# make-refs.py, and the service venv's requirements.lock; src/ears/_speakers/ links into
|
|
@@ -76,7 +83,12 @@ where = ["src"]
|
|
|
76
83
|
# Apple helper from a source checkout only, and refuses without one.
|
|
77
84
|
ears = ["_keeper/ears-keeper.sh", "_keeper/call-ears", "_keeper/input-watch.swift", "_keeper/ears-mail.py",
|
|
78
85
|
"_keeper/ears-sounds.py", "_keeper/mouth-sounds.py", "_keeper/held_stream.py",
|
|
86
|
+
"_keeper/phone_roots.py",
|
|
79
87
|
"_speakers/*", "_speech/*", "_settings/*.schema.json"]
|
|
88
|
+
# The separator's runner and its venv's lock: src/mouth/_separator/ links into separator/ the
|
|
89
|
+
# same way. They are data, never imported by the mouth: `mouth split` runs run.py in the
|
|
90
|
+
# separator venv. torch and demucs, that venv's, are in no dependency list here.
|
|
91
|
+
mouth = ["_separator/*"]
|
|
80
92
|
# talk serves its page and manifest from beside server.py
|
|
81
93
|
talk = ["index.html", "manifest.json"]
|
|
82
94
|
|
|
@@ -23,6 +23,12 @@ as a child and restarts it, so the room and the calls are one job.
|
|
|
23
23
|
import json, os, re, socket, subprocess, sys, time
|
|
24
24
|
from pathlib import Path
|
|
25
25
|
|
|
26
|
+
# phone_roots.py sits beside this file (keeper/, or ears/_keeper/ in a wheel); a loader
|
|
27
|
+
# that runs this without its dir on sys.path still finds it.
|
|
28
|
+
if os.path.dirname(os.path.abspath(__file__)) not in sys.path:
|
|
29
|
+
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
30
|
+
import phone_roots # noqa: E402 (keeper/ is not a package)
|
|
31
|
+
|
|
26
32
|
HOME = Path.home()
|
|
27
33
|
# The keeper passes its own; the default is the keeper's (keeper/ears-keeper.sh), change both.
|
|
28
34
|
DIR = Path(os.environ.get("CALL_EARS_DIR")
|
|
@@ -58,11 +64,8 @@ def socks():
|
|
|
58
64
|
env = os.environ.get("EARS_CALL_SOCKS", os.environ.get("VOICEMODE_CALL_EARS_SOCKS"))
|
|
59
65
|
if env:
|
|
60
66
|
return [p for p in env.split(":") if p]
|
|
61
|
-
|
|
62
|
-
return
|
|
63
|
-
| {str(p) for p in HOME.glob(".*/delta-chat/*/call-ears.sock")}
|
|
64
|
-
| {str(p) for p in sp.glob("call-ears.sock")}
|
|
65
|
-
| {str(p) for p in sp.glob("*/call-ears.sock")})
|
|
67
|
+
# else every bot root's: phone_roots.py, beside this file (the rule ears-mail.py shares)
|
|
68
|
+
return phone_roots.bot_socks("call-ears.sock")
|
|
66
69
|
|
|
67
70
|
|
|
68
71
|
def live_calls():
|
|
@@ -94,16 +94,23 @@ fi
|
|
|
94
94
|
NOTIFY="${EARS_NOTIFY:-}"
|
|
95
95
|
# Paths: settings in the voice settings folder, VOICE_SETTINGS (a copy of
|
|
96
96
|
# ears.home.SETTINGS; tests/test_settings_dir.py holds it to the module),
|
|
97
|
-
# logs in
|
|
98
|
-
# services.py LOGS) has it
|
|
99
|
-
#
|
|
97
|
+
# logs in the voice log folder, asked of the ONE resolver (ears.home.log_dir, as `ears
|
|
98
|
+
# services` (src/ears/services.py LOGS) has it; $EARS_LOG_DIR moves it, and the resolver
|
|
99
|
+
# reads that too). No copy of the path here: a python that cannot answer stops the keeper
|
|
100
|
+
# (exit 2) before it writes anything. RUN (stop files, the input-watch binary
|
|
100
101
|
# and state) has no fixed home yet: it defaults beside the package's other assets
|
|
101
102
|
# in ~/.cache/ears, and EARS_RUN moves it.
|
|
102
103
|
VOICE_SETTINGS="$HOME/.sessionkit/voice/settings"
|
|
103
104
|
CONF="${EARS_CONF:-$VOICE_SETTINGS/ears.json}"
|
|
104
|
-
LOGDIR="${
|
|
105
|
+
LOGDIR="$("$PY" -m ears.home log-dir)" && [ -n "$LOGDIR" ] || { echo "ears-keeper: $PY -m ears.home log-dir named no log folder - run \`uv sync\` in $ROOT, or set EARS_PY" >&2; exit 2; }
|
|
105
106
|
RUN="${EARS_RUN:-$HOME/.cache/ears/run}"
|
|
106
107
|
mkdir -p "$LOGDIR" "$RUN"
|
|
108
|
+
# The settings-schema copies (.ears.schema.json and the rest), brought up to date with the
|
|
109
|
+
# schemas this python's package ships, once per start: an upgrade replaces the schemas, never
|
|
110
|
+
# the copies. A copy whose bytes already match is left alone, and the log gets a line only
|
|
111
|
+
# when one was written. It never stops the keeper.
|
|
112
|
+
SCHEMA_LINE="$("$PY" -m ears.services schema --quiet 2>/dev/null || true)"
|
|
113
|
+
[ -n "$SCHEMA_LINE" ] && echo "$(date +%H:%M:%S) $SCHEMA_LINE"
|
|
107
114
|
SILERO="${EARS_SILERO_ONNX:-$HOME/.cache/ears/silero_vad.onnx}" # where `ears install` puts it (src/ears/vad.py)
|
|
108
115
|
WATCH_SRC="$(cd "$(dirname "$0")" && pwd)/input-watch.swift"
|
|
109
116
|
WATCH_BIN="${EARS_WATCH_BIN:-$RUN/input-watch}"
|
|
@@ -506,6 +513,9 @@ deaf_subject() { # $1 channel, $2 cause, $3 detail, $4 device -> the DEAF mail'
|
|
|
506
513
|
if [ "$1" = room ]; then echo "$who DEAF, mic gives only zeros${4:+ on ${4:0:15}} at $(date +%H:%M) EOM"; return; fi ;;
|
|
507
514
|
cover) # the side-car's: "ears: speakers DEAF, 0% of N lines on DEVICE EOM" (detail N, <= 3 digits)
|
|
508
515
|
if [ "$1" = speakers ]; then echo "$who DEAF, 0% of ${3:0:3} lines${dev:+ on $dev} EOM"; return; fi ;;
|
|
516
|
+
speech) # the side-car's: "ears: speakers DEAF, no speech in N turns on DEVICE EOM" (detail N,
|
|
517
|
+
# <= 2 digits). The device is cut to 14 to keep 64.
|
|
518
|
+
if [ "$1" = speakers ]; then echo "$who DEAF, no speech in ${3:0:2} turns${4:+ on ${4:0:14}} EOM"; return; fi ;;
|
|
509
519
|
esac
|
|
510
520
|
max=$(( 64 - ${#head} - ${#tail} )); [ "$max" -ge 1 ] || max=1
|
|
511
521
|
body="${body:0:$max}"; body="${body%% }"; body="${body%:}" # no dangling ": " when cut
|
|
@@ -648,14 +658,21 @@ heard_events() { # $1 = heard log, $2 = byte offset -> line 1 the offset after
|
|
|
648
658
|
# heard PID SESSION SOURCE DEVICE a partial or turn with text: speech to text works.
|
|
649
659
|
# A run of the same line is printed once
|
|
650
660
|
# With $3 = the side-car's speakers log and $4 = its byte offset, it reads that too, after them:
|
|
651
|
-
# spk
|
|
652
|
-
# room's).
|
|
661
|
+
# spk STATE DEVICE a mic row (no `leg`: a call's rows are not the
|
|
662
|
+
# room's). STATE 0 = 0% of the line covered:
|
|
653
663
|
# `cover` == 0, or, before the side-car writes
|
|
654
664
|
# `cover`, a why ending "(0% of the line covered)".
|
|
655
|
-
#
|
|
656
|
-
# "
|
|
657
|
-
#
|
|
658
|
-
#
|
|
665
|
+
# Some covered (a row with no why, or a why saying
|
|
666
|
+
# "(N% ...)" with N > 0) is one of three:
|
|
667
|
+
# 1 = heard: no why, and a speaker named;
|
|
668
|
+
# 2 = covered and silent: a `turn` of 2 or more
|
|
669
|
+
# words, no `mouth_utt`, whose why starts "words in
|
|
670
|
+
# the line, no speech found" (the capture heard
|
|
671
|
+
# words; the side-car's audio held no speech);
|
|
672
|
+
# 3 = covered, no evidence either way (zeros, a
|
|
673
|
+
# partial, one word, the mouth's words, `silent`).
|
|
674
|
+
# A row that says neither (a window in the future)
|
|
675
|
+
# is not printed
|
|
659
676
|
# spkoff OFFSET last: the offset after its last whole line
|
|
660
677
|
# Every field is non-empty ("?" for none): the TAB read would close up an empty one.
|
|
661
678
|
python3 -c 'import json,re,sys
|
|
@@ -717,6 +734,10 @@ if len(sys.argv)>4:
|
|
|
717
734
|
elif why.endswith("(0% of the line covered)"): z=0
|
|
718
735
|
elif re.search(r"\(\d+% of the line covered\)$",why): z=1
|
|
719
736
|
else: continue
|
|
737
|
+
if z==1 and (isinstance(why,str) or d.get("speaker") in ("silent","unheard")):
|
|
738
|
+
w="".join(ch.lower() if ch.isalnum() else " " for ch in str(d.get("text") or "")).split()
|
|
739
|
+
z=2 if (isinstance(why,str) and why.startswith("words in the line, no speech found")
|
|
740
|
+
and d.get("kind")=="turn" and len(w)>=2 and not d.get("mouth_utt")) else 3
|
|
720
741
|
print("spk\t%d\t%s" % (z,txt(d.get("device"),20)))
|
|
721
742
|
print("spkoff\t%d" % (so+se))' "$@"
|
|
722
743
|
}
|
|
@@ -943,7 +964,7 @@ zeros_check() { # the ONE reopen; call it every loop, after heard_watch (and be
|
|
|
943
964
|
stop_capture || note "capture $zeros_pid did not stop"
|
|
944
965
|
}
|
|
945
966
|
# --- end zeros ---
|
|
946
|
-
# --- speakers: the side-car hears nothing
|
|
967
|
+
# --- speakers: the side-car hears nothing: 0% of its lines covered, or covered with no speech (tests/test_keeper_speakers_deaf.py runs this alone) ---
|
|
947
968
|
# The speaker side-car (speakers/speaker-sidecar2.py) opens the room capture's mic and names who
|
|
948
969
|
# spoke each heard line. Its stream can open, stay active and deliver nothing: every line then gets
|
|
949
970
|
# a row `why: "no audio from 'DEV' (0% of the line covered)"`. 27k such rows in 3h19m went unread
|
|
@@ -953,19 +974,37 @@ zeros_check() { # the ONE reopen; call it every loop, after heard_watch (and be
|
|
|
953
974
|
# N lines on DEVICE`. The reopen is the side-car's own; the keeper only reports.
|
|
954
975
|
# - A mic row with any coverage ends it: ONE recovery mail, `ears: speakers hearing again`. A
|
|
955
976
|
# "no signal: the device gave only zeros" row is covered (the room's zeros cause says that one).
|
|
977
|
+
# - The other deaf: the stream is covered and its audio holds no speech while the capture heard
|
|
978
|
+
# words in the line. EARS_SPEAKERS_SILENT_TURNS (6) such turns in a row (state 2: a turn of 2+
|
|
979
|
+
# words, no mouth_utt; see heard_events) is DEAF, cause `speech`: ONE mail, `ears: speakers
|
|
980
|
+
# DEAF, no speech in N turns on DEVICE`. 6 because the side-car reopens its stream at 3 of
|
|
981
|
+
# them: the keeper reports what the reopen did not cure. A row with no words is no evidence,
|
|
982
|
+
# so a quiet room never counts. Only a heard row (state 1) ends this run and clears `speech`;
|
|
983
|
+
# a 0% row or a row that is no evidence (3) leaves it, so the two causes cannot hide each
|
|
984
|
+
# other. Every covered row (1, 2, 3) ends the 0% run and clears `cover`, as before.
|
|
956
985
|
# - MUTED while the room is DEAF or no room capture runs: the side-car hears only while the keeper's
|
|
957
986
|
# capture runs, so its 0% rows are then a consequence, not a second fault. A muted row counts
|
|
958
|
-
# nothing and starts
|
|
987
|
+
# nothing and starts its run again. "No room capture" is the beat region's beat_pid: what
|
|
959
988
|
# capture_pid said at the top of this loop (beat_track), so no row costs a second ps.
|
|
960
989
|
SPK_DEAF_LINES="${EARS_SPEAKERS_DEAF_LINES:-20}"
|
|
961
990
|
[[ "$SPK_DEAF_LINES" =~ ^[1-9][0-9]{0,2}$ ]] || SPK_DEAF_LINES=20 # 1-999: N fits the subject
|
|
962
|
-
|
|
991
|
+
SPK_SILENT_TURNS="${EARS_SPEAKERS_SILENT_TURNS:-6}"
|
|
992
|
+
[[ "$SPK_SILENT_TURNS" =~ ^[1-9][0-9]?$ ]] || SPK_SILENT_TURNS=6 # 1-99: N fits the subject
|
|
993
|
+
spk_n=0; spk_q=0
|
|
963
994
|
spk_muted() { deaf_room || [ -z "${beat_pid:-}" ]; }
|
|
964
|
-
spk_seen() { # $1 =
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
995
|
+
spk_seen() { # $1 = the row's state (heard_events: 0 none covered, 1 heard, 2 covered and
|
|
996
|
+
# silent, 3 covered, no evidence), $2 = its device
|
|
997
|
+
case "$1" in
|
|
998
|
+
0) if spk_muted; then spk_n=0; return 0; fi
|
|
999
|
+
spk_n=$(( spk_n + 1 ))
|
|
1000
|
+
[ "$spk_n" -ge "$SPK_DEAF_LINES" ] && deaf_set speakers cover "$SPK_DEAF_LINES" "$2" ;;
|
|
1001
|
+
1) spk_n=0; spk_q=0; deaf_clear speakers cover "$2"; deaf_clear speakers speech "$2" ;;
|
|
1002
|
+
2) spk_n=0; deaf_clear speakers cover "$2"
|
|
1003
|
+
if spk_muted; then spk_q=0; return 0; fi
|
|
1004
|
+
spk_q=$(( spk_q + 1 ))
|
|
1005
|
+
[ "$spk_q" -ge "$SPK_SILENT_TURNS" ] && deaf_set speakers speech "$SPK_SILENT_TURNS" "$2" ;;
|
|
1006
|
+
*) spk_n=0; deaf_clear speakers cover "$2" ;;
|
|
1007
|
+
esac
|
|
969
1008
|
return 0
|
|
970
1009
|
}
|
|
971
1010
|
# --- end speakers ---
|