feat: wire VoiceDNA VoiceAdapter into live OpenClaw per-agent voice pipeline - #7
feat: wire VoiceDNA VoiceAdapter into live OpenClaw per-agent voice pipeline#7lukejmorrison wants to merge 3 commits into
Conversation
- voicedna/openclaw_live_voice.py: render_agent_voice() entry point wires VoiceAdapter preset routing into the live OpenClaw agent voice pipeline. Default map covers agent:namshub (neutral), agent:david-hardman (friendly), agent:dr-voss-thorne (flair). Env override via VOICEDNA_OPENCLAW_PRESETS_MAP. Singleton adapter, lazy init, reset_adapter() for tests. - tests/test_openclaw_live_voice.py: 12 tests, all pass. - Existing code paths untouched. Opt-in only. 30/30 tests pass. ruff clean. py_compile clean.
- DEPLOY_NOTES.md: full deploy/rollback/wiring guide - demo/: validation run outputs — namshub.wav, david-hardman.wav, dr-voss-thorne.wav, validation_log_20260418.txt - demos/: updated demo WAVs (mirrors examples/openclaw/output/) - research/: integration plan, preflight, logs, packet artifacts - All 46 tests pass locally (30 adapter+live-voice, 16 core suite) Signed-off-by: Dr Voss Thorne <subagent>
There was a problem hiding this comment.
Pull request overview
Adds an opt-in, per-agent VoiceDNA preset routing entry point intended to be called from OpenClaw’s live TTS seam, plus tests and rollout docs to validate and deploy the integration.
Changes:
- Introduces
render_agent_voice()(lazy singletonVoiceAdapter) with default agent→preset routing and env override support. - Adds integration tests for routing, singleton/reset behavior, env override behavior, and WAV file writing.
- Adds/updates deployment + integration planning docs and captures validation logs/artifacts.
Reviewed changes
Copilot reviewed 18 out of 27 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
voicedna/openclaw_live_voice.py |
New live entry point render_agent_voice() with default mapping + env override merge and adapter singleton/reset. |
tests/test_openclaw_live_voice.py |
New integration tests for the live entry point and mapping behavior. |
DEPLOY_NOTES.md |
Deployment/wiring/rollback guidance for enabling the OpenClaw integration. |
IMPLEMENTATION_NOTE.md |
Implementation notes, reproduction commands, and rollout/rollback guidance. |
demo/validation_log_20260418.txt |
Captured manual validation run output for the live voice entry point. |
research/voicedna_openclaw_integration_plan.md |
Integration plan outlining OpenClaw seam wiring and rollout expectations. |
research/pr_description.md |
Draft PR description content for the target OpenClaw-side PR. |
research/merge_ready_report.md |
Merge readiness report and verification notes. |
research/logs/test_voice_adapter_20260418.txt |
Captured adapter test run output. |
research/logs/test_full_suite_20260418.txt |
Captured full-suite pytest output. |
research/logs/demo_voicedemo_validation_20260418.txt |
Captured WAV validation (sizes/format) for demo outputs. |
research/logs/demo_voicedemo_20260418.txt |
Captured demo script console output. |
research/integration_preflight.md |
Preflight checklist and exact rollout steps for the OpenClaw wiring. |
research/integration_checklist.md |
Go/no-go checklist for integration and rollout verification. |
research/integration-packet/local_run_commands.txt |
Local command sequence used for validation. |
research/integration-packet/implementation_checklist.md |
Implementation checklist for wiring and validation steps. |
research/integration-packet/blockers.txt |
Notes on potential blockers/approvals for pushing/wiring. |
research/integration-packet/DESIGN_DOC.md |
Design overview for the per-agent voice pilot approach. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # Also clear the module-level AGENT_PRESETS dict from adapter | ||
| import voicedna.openclaw_adapter as adp | ||
| adp.AGENT_PRESETS.clear() | ||
| adapter = _get_adapter() |
There was a problem hiding this comment.
This test has to reach into voicedna.openclaw_adapter.AGENT_PRESETS and clear it to make env overrides deterministic. That’s a sign the live-voice module’s reset path doesn’t fully reset the underlying preset cache. Prefer moving this reset into reset_adapter() (or a public reset helper in openclaw_adapter) so tests don’t depend on another module’s mutable globals.
| import sys | ||
|
|
||
| import pytest | ||
|
|
||
| # Ensure the project root is importable | ||
| sys.path.insert(0, str(__import__("pathlib").Path(__file__).resolve().parents[1])) | ||
|
|
There was a problem hiding this comment.
The repo already inserts the project root onto sys.path in the root conftest.py, so this per-test sys.path.insert(...) is redundant and makes import order harder to reason about. Consider removing it (or at least using the consistent from pathlib import Path pattern used elsewhere) and rely on conftest.py for path setup.
| import sys | |
| import pytest | |
| # Ensure the project root is importable | |
| sys.path.insert(0, str(__import__("pathlib").Path(__file__).resolve().parents[1])) | |
| import pytest |
| If push/auth is required for new commits, set the remote with a PAT: | ||
|
|
||
| ```bash | ||
| git remote set-url origin https://<YOUR_PAT>@github.com/lukejmorrison/VoiceDNA.git | ||
| git push origin feature/voicedna-openclaw-integration | ||
| ``` |
There was a problem hiding this comment.
The docs suggest embedding a PAT directly in the remote URL (https://<YOUR_PAT>@github.com/...). This is easy to leak via shell history, git remote -v, logs, or screenshots. Prefer recommending gh auth login, GITHUB_TOKEN with gh, or git push https://github.com/... and let Git prompt/credential-manager handle auth (or use a one-off HTTPS URL without saving it to origin).
| ## Pushing to origin (PAT required) | ||
|
|
||
| 4. **Preset names**: `neutral`, `friendly`, `flair` match the design doc verbatim. | ||
| Push is blocked without a PAT (HTTPS remote, no stored credential). | ||
|
|
||
| 5. **No new packaging deps**: The adapter imports only from `voicedna.*` and stdlib. | ||
| **If you provide a PAT:** | ||
| ```bash | ||
| cd /home/namshub/dev/VoiceDNA | ||
| git remote set-url origin https://<YOUR_PAT>@github.com/lukejmorrison/VoiceDNA.git | ||
| git push origin feature/voicedna-openclaw-integration | ||
| ``` |
There was a problem hiding this comment.
This guidance recommends putting a PAT into the origin URL. That pattern frequently results in accidental token disclosure (git config, terminal history, logs) and can persist beyond the intended short-lived use. Prefer documenting gh auth login --with-token, Git credential manager, or a one-time git push https://github.com/... without permanently changing the remote URL.
| ### Option B — Env-driven (zero-code) | ||
|
|
||
| ```bash | ||
| export VOICEDNA_OPENCLAW_PRESETS=1 | ||
| export VOICEDNA_OPENCLAW_PRESETS_MAP='{"agent:namshub":"neutral","agent:dr-voss-thorne":"flair"}' | ||
| ``` | ||
|
|
||
| Set these in the OpenClaw agent's environment before starting the gateway. | ||
|
|
There was a problem hiding this comment.
“Option B — Env-driven (zero-code)” reads as if setting env vars alone enables the OpenClaw routing. In this repo, VOICEDNA_OPENCLAW_PRESETS is only a convention (the library doesn’t gate/auto-hook on it), and VOICEDNA_OPENCLAW_PRESETS_MAP is only configuration for code that already calls render_agent_voice(). Consider clarifying that an OpenClaw-side seam/shim still needs to invoke render_agent_voice() (Option A/C), and the env vars only control whether that seam is enabled and how routing is configured.
| def reset_adapter() -> None: | ||
| """Reset the shared adapter (useful in tests or after env changes).""" | ||
| global _adapter | ||
| _adapter = None |
There was a problem hiding this comment.
reset_adapter() claims it’s useful “after env changes”, but resetting only _adapter is not enough because _get_adapter() pulls env overrides via load_presets_from_env(), which populates/returns the global voicedna.openclaw_adapter.AGENT_PRESETS and does not clear it when the env var is removed/changed. This can cause stale agent→preset mappings to persist across resets. Consider having reset_adapter() also clear AGENT_PRESETS (or add a dedicated reset in openclaw_adapter and call it here), or change _get_adapter() to parse the env JSON without using the cached module global.
Summary
Wires
VoiceAdapterpreset routing into the live OpenClaw agent voice pipeline via a newrender_agent_voice()entry point.What's new
voicedna/openclaw_live_voice.pyrender_agent_voice()— per-agent voice entry point. Lazy singletonVoiceAdapterwith env override viaVOICEDNA_OPENCLAW_PRESETS_MAP.tests/test_openclaw_live_voice.pyDEPLOY_NOTES.mddemo/Default agent preset mapping
agent:namshubneutralagent:david-hardmanfriendlyagent:dr-voss-thorneflairTest results (local)
Wiring options
See
DEPLOY_NOTES.mdfor three integration options (programmatic, env-driven, full skill hook).Rollback
Remove
voicedna/openclaw_live_voice.pyandtests/test_openclaw_live_voice.py. All existing code paths are unaffected.