dotty-behaviour: load optional .env in compose#100
Merged
Conversation
VLM and audio-caption paths read OPENROUTER_API_KEY / VISION_API_KEY / VLM_API_KEY from the process env, but the compose service didn't expose those vars to the container — so on the running deployment all VLM calls aborted with "no api key set" (room-view recognition silent, scene synthesis silent, audio captioning silent). Add `env_file: [- path: .env, required: false]`. The file lives at \$REMOTE_DIR/.env on the host (Unraid: /mnt/user/appdata/dotty-behaviour-src/.env); it's not part of the deploy tar (deploy-behaviour.sh ships tracked files only) so it persists across deploys. `required: false` keeps startup working when no .env exists yet. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds optional .env injection to the dotty-behaviour Docker Compose service so host-resident secrets (e.g., VLM/OpenRouter API keys) can be passed through to the running container without breaking startup when the file is absent.
Changes:
- Add
env_file(long-form) pointing at.envwithrequired: falseto make secret injection optional. - Document intended operator workflow for keeping
.envuntracked and persistent across deploys.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # Optional host-resident .env at $REMOTE_DIR/.env (Unraid: | ||
| # /mnt/user/appdata/dotty-behaviour-src/.env). Stays out of the | ||
| # deploy tar (only tracked files ship) and survives recreates. | ||
| # Use for OPENROUTER_API_KEY / VISION_API_KEY / VLM_API_KEY etc. |
BrettKinny
added a commit
that referenced
this pull request
May 23, 2026
* #111: rip ACP/voice path from bridge.py ACPClient + MessageIn/MessageOut + /api/message + /api/message/stream + voice tool helpers + voice escalate/memory_log/remember/remember_person + _voice_preparer + _ConvoLogger are gone. /health drops acp_running / cached_session / session_turns. The dashboard's per-person memory page keeps its three brain.db read/mutate helpers (_voice_memory_person_records_blocking / _voice_memory_approve_blocking / _voice_memory_delete_blocking) since /ui/memory is still live. Perception consumers + vision/audio endpoints stay for commits 2-3 of the #111 rip. _greeter_llm_client is stubbed to RuntimeError so the ProactiveGreeter (still wired into lifespan until commit 2) fails fast on its first prompt() call rather than NameError on a missing acp ref. bridge.py: 6079 → 4564 lines. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * #111: rip perception consumers from bridge.py All 13 in-process perception consumers — face_lost_aborter, sound_turner, wake_word_turner, face_greeter, face_identified_refresher, purr_player, named_acknowledger, scene_synthesis (loop is partial — see commit 3), idle_photographer, sleep_dreamer, dance_reflector, security_capture, ProactiveGreeter wiring — are gone. The in-memory perception bus (_perception_state / _perception_listeners / _perception_recent_events / _sound_balance_history) goes with them, along with /api/perception/event + /api/perception/state + /api/perception/feed, the legacy convo-log last_user_line cache, and the HouseholdRegistry / SpeakerResolver wiring. Calendar machinery (_fetch_weather / _fetch_calendar_events / _refresh_caches / _calendar_poll_loop / summarize_for_prompt / _build_context / _build_perception_block / Event TypedDict) goes too — dotty-behaviour serves /api/calendar/today now (per #100 / dotty-behaviour/routes/calendar.py). The dashboard perception card now reads empty stubs (_dashboard_perception_state_getter / *_recent_getter / *_last_user_line_getter / *_sound_balance_series / *_vision_failures_last_hour all return empty), so the card renders without errors but shows no data until the dashboard ports to dotty-behaviour. _identity_display_name returns None for the same reason. Dispatch helpers kept: _dispatch_abort, _dispatch_set_state, _dispatch_set_tier1slim_model, _dispatch_set_toggle — all four are needed by the dashboard / admin endpoints. The consumer-only dispatchers (face_greeting / say / set_head_angles / set_face_identified / purr_audio) went with the consumers. bridge.py: 4564 → 2868 lines. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * #111: rip VLM / vision / audio code from bridge.py /api/vision/explain, /api/vision/latest/{device_id}, /api/audio/explain are gone. The room_view sentinel branch (VISION_ROOM_VIEW_SYSTEM_PROMPT, __ROOM_VIEW_V1__, _build_room_view_question, _parse_room_view_response, _ROOM_VIEW_* constants) goes with it — dotty-behaviour now owns the room_view roster recognition path (PR #101 / #102 / #103). _call_vision_api + VLM HTTP plumbing + _classify_vision_result + _call_narrative_llm + _call_audio_caption_api + _audio_format_from_upload go too — dotty-behaviour owns every VLM call now (and dotty-pi-ext owns narrative calls from voice tools). Scene-synthesis / idle-photographer / dream / dance NDJSON writers (_scene_synthesis_log_path / _idle_perception_log_path / _write_idle_perception_record / _is_notable_perception / _idle_photographer_pick_device / _dreams_log_path / _dances_log_path / _write_jsonl_record / _write_dream_record / _write_dance_record / _split_dream_text / _compose_scene_synthesis / _write_scene_synthesis_ndjson / _maybe_emit_scene_synthesis / _perception_sleep_dreamer / _perception_dance_reflector / _perception_idle_photographer / _scene_synthesis_loop) are gone — every consumer they fed lives in dotty-behaviour. The legacy /admin/* endpoints that targeted the retired ZeroClaw daemon (_apply_model_swap, _read_voice_model_from_cfg, _voice_profile_for_model, _admin_schedule_restart, _ADMIN_DAEMON_CFG, /admin/model) were retired alongside — /admin/persona stays for external persona-file edits, /admin/safety stays as a static MCP_TOOL_ALLOWLIST mutation surface, /admin/smart-mode keeps just the tier1slim hot-swap path, /admin/state + /admin/kid-mode keep their xiaozhi-admin passthrough. bridge.py: 2868 → 911 lines. Final state: dashboard-only. Cache stubs (_vision_cache / _audio_cache / _scene_synthesis_cache / _perception_state) all empty dicts so the dashboard perception card renders without errors but shows no data until it ports to dotty-behaviour. The brain.db memory funcs (_voice_memory_person_records_blocking / *_approve_blocking / *_delete_blocking) survive because /ui/memory is the only operator surface on per-person memory rows. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
env_file: [- path: .env, required: false]todotty-behaviour/docker-compose.ymlso the container picks up host-resident secrets at\$REMOTE_DIR/.env.Why
Surfaced during the PR #93 bench-test: the new bare-greet suppression worked, but the named-greet path stayed silent. Root cause was VLM calls aborting:
config.pyreads those keys fromos.environ, but the compose service didn't expose them. PR #91 documented the keys in.env.example; this is the missing plumbing step that actually makes them reach the running container. Without it the room-view, scene-synthesis, security-cycle, and audio-caption paths all silently no-op.Approach
env_file:long-form (compose v2.24+, host runs 2.35.1) withrequired: falseso a missing.envno longer breaks startup. The file lives at/mnt/user/appdata/dotty-behaviour-src/.envon Unraid, owned by the operator. It survives deploys becausedeploy-behaviour.shships only tracked files —.envis untracked and stays put.Test plan
docker compose configparses (the env_file long-form is valid for the deployed compose version)BEHAVIOUR_HOST=root@tower.local bash scripts/deploy-behaviour.sh— container recreates cleanly with no.envpresent (required: false).envwithOPENROUTER_API_KEY=..., restart, confirm/api/vision/explainreturns a non-error VLM response🤖 Generated with Claude Code