Mercurial
comparison infinite_canvas/run_orchestration_dev.sh @ 280:49e9e591c9bb
Add persistent dictation, prewarmed WebRTC speech input, Copilot SDK routing, animated conversation lifecycle controls, parking, and architecture coverage.
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Tue, 18 Aug 2026 19:14:53 -0700 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 279:b3b547563ec7 | 280:49e9e591c9bb |
|---|---|
| 1 #!/usr/bin/env bash | |
| 2 set -euo pipefail | |
| 3 | |
| 4 RUNFILES_ROOT="${RUNFILES_DIR:-$0.runfiles}" | |
| 5 export RUNFILES_DIR="$RUNFILES_ROOT" | |
| 6 | |
| 7 find_runfile() { | |
| 8 local pattern="$1" | |
| 9 local path | |
| 10 path="$(find -L "$RUNFILES_ROOT" -path "$pattern" -print -quit)" | |
| 11 if [[ -z "$path" ]]; then | |
| 12 echo "Could not locate orchestration runtime file: $pattern" >&2 | |
| 13 exit 1 | |
| 14 fi | |
| 15 realpath "$path" | |
| 16 } | |
| 17 | |
| 18 find_config() { | |
| 19 local candidate | |
| 20 for candidate in \ | |
| 21 "${BUILD_WORKSPACE_DIRECTORY:-}/mrjunejune/.config" \ | |
| 22 "$PWD/mrjunejune/.config" \ | |
| 23 "${HOME:-}/.config/mrjunejune/.config"; do | |
| 24 [[ "$candidate" != /mrjunejune/.config ]] || continue | |
| 25 [[ "$candidate" != /.config/mrjunejune/.config ]] || continue | |
| 26 if [[ -f "$candidate" ]]; then | |
| 27 realpath "$candidate" | |
| 28 return 0 | |
| 29 fi | |
| 30 done | |
| 31 return 1 | |
| 32 } | |
| 33 | |
| 34 load_config() { | |
| 35 local config_path="$1" | |
| 36 local line key value | |
| 37 while IFS= read -r line || [[ -n "$line" ]]; do | |
| 38 line="${line%$'\r'}" | |
| 39 [[ "$line" =~ ^[[:space:]]*$ ]] && continue | |
| 40 [[ "$line" =~ ^[[:space:]]*# ]] && continue | |
| 41 if [[ "$line" != *=* ]]; then | |
| 42 echo "Invalid config line in $config_path" >&2 | |
| 43 exit 1 | |
| 44 fi | |
| 45 key="${line%%=*}" | |
| 46 value="${line#*=}" | |
| 47 key="${key#"${key%%[![:space:]]*}"}" | |
| 48 key="${key%"${key##*[![:space:]]}"}" | |
| 49 case "$key" in | |
| 50 LITELLM_MASTER_KEY|LITELLM_HOST|LITELLM_PORT|LITELLM_MODEL|\ | |
| 51 LITELLM_WIRE_API|GITHUB_COPILOT_TOKEN_DIR|\ | |
| 52 MRJUNEJUNE_INFERENCE_STATE|COPILOT_SESSION_IDLE_SECONDS|\ | |
| 53 COPILOT_MAX_SESSIONS) | |
| 54 printf -v "$key" '%s' "$value" | |
| 55 ;; | |
| 56 esac | |
| 57 done < "$config_path" | |
| 58 } | |
| 59 | |
| 60 dictation="$(find_runfile '*/dictation/server')" | |
| 61 dictation_bin="$(find_runfile '*/dictation/dictation_bin')" | |
| 62 canvas="$(find_runfile '*/infinite_canvas/dev_linux')" | |
| 63 sidecar_launcher="$(find_runfile '*/mrjunejune/inference_sidecar_launcher.sh')" | |
| 64 sidecar_zip="$(find_runfile '*/mrjunejune/inference/copilot_sidecar.zip')" | |
| 65 litellm_zip="$(find_runfile '*/mrjunejune/inference/litellm_proxy.zip')" | |
| 66 litellm_config="$(find_runfile '*/mrjunejune/inference/litellm_config.yaml')" | |
| 67 copilot_cli="$(find_runfile '*copilot_cli_linux_x86_64/copilot')" | |
| 68 python="$(find_runfile '*python_3_11_x86_64-unknown-linux-gnu/bin/python3')" | |
| 69 | |
| 70 config_file="$(find_config || true)" | |
| 71 if [[ -z "$config_file" ]]; then | |
| 72 echo "Missing mrjunejune/.config." >&2 | |
| 73 echo "Copy mrjunejune/.config.development to mrjunejune/.config." >&2 | |
| 74 exit 1 | |
| 75 fi | |
| 76 load_config "$config_file" | |
| 77 | |
| 78 state_root="${MRJUNEJUNE_INFERENCE_STATE:-${XDG_STATE_HOME:-$HOME/.local/state}/mrjunejune/inference}" | |
| 79 token_dir="${GITHUB_COPILOT_TOKEN_DIR:-$state_root/litellm-copilot}" | |
| 80 cache_root="$state_root/cache" | |
| 81 mkdir -p "$state_root/copilot" "$token_dir" "$cache_root" | |
| 82 chmod 700 "$state_root" "$state_root/copilot" "$token_dir" "$cache_root" | |
| 83 | |
| 84 if [[ ! -s "$token_dir/access-token" ]]; then | |
| 85 echo "GitHub Copilot is not authenticated." >&2 | |
| 86 echo "Run: bazel run //mrjunejune:run_inference_stack -- --authenticate" >&2 | |
| 87 exit 1 | |
| 88 fi | |
| 89 | |
| 90 if [[ -z "${LITELLM_MASTER_KEY:-}" ]]; then | |
| 91 umask 077 | |
| 92 LITELLM_MASTER_KEY="sk-$("$python" -c 'import secrets; print(secrets.token_hex(24))')" | |
| 93 fi | |
| 94 | |
| 95 litellm_host="${LITELLM_HOST:-127.0.0.1}" | |
| 96 litellm_port="${LITELLM_PORT:-4000}" | |
| 97 service_pids=() | |
| 98 | |
| 99 cleanup() { | |
| 100 local status=$? | |
| 101 trap - EXIT INT TERM | |
| 102 for pid in "${service_pids[@]}"; do | |
| 103 kill -- "-$pid" 2>/dev/null || kill "$pid" 2>/dev/null || true | |
| 104 done | |
| 105 for pid in "${service_pids[@]}"; do | |
| 106 wait "$pid" 2>/dev/null || true | |
| 107 done | |
| 108 exit "$status" | |
| 109 } | |
| 110 | |
| 111 trap cleanup EXIT | |
| 112 trap 'exit 130' INT | |
| 113 trap 'exit 143' TERM | |
| 114 | |
| 115 export GITHUB_COPILOT_TOKEN_DIR="$token_dir" | |
| 116 export XDG_CACHE_HOME="$cache_root" | |
| 117 export DICTATION_MODEL_DIR="${DICTATION_MODEL_DIR:-$HOME/.cache/zenbu/faster-whisper-small}" | |
| 118 export LITELLM_MASTER_KEY | |
| 119 setsid "$python" "$litellm_zip" \ | |
| 120 --config "$litellm_config" \ | |
| 121 --host "$litellm_host" \ | |
| 122 --port "$litellm_port" & | |
| 123 service_pids+=("$!") | |
| 124 | |
| 125 setsid "$dictation" "$dictation_bin" server & | |
| 126 service_pids+=("$!") | |
| 127 | |
| 128 for _ in {1..150}; do | |
| 129 if curl --fail --silent \ | |
| 130 --connect-timeout 1 \ | |
| 131 --max-time 2 \ | |
| 132 "http://${litellm_host}:${litellm_port}/health/readiness" \ | |
| 133 >/dev/null; then | |
| 134 break | |
| 135 fi | |
| 136 if ! kill -0 "${service_pids[0]}" 2>/dev/null; then | |
| 137 echo "Copilot LiteLLM gateway exited before becoming ready." >&2 | |
| 138 exit 1 | |
| 139 fi | |
| 140 sleep 0.1 | |
| 141 done | |
| 142 if ! curl --fail --silent \ | |
| 143 --connect-timeout 1 \ | |
| 144 --max-time 2 \ | |
| 145 "http://${litellm_host}:${litellm_port}/health/readiness" \ | |
| 146 >/dev/null; then | |
| 147 echo "Copilot LiteLLM readiness timed out." >&2 | |
| 148 exit 1 | |
| 149 fi | |
| 150 | |
| 151 for _ in {1..120}; do | |
| 152 if curl --silent --fail --output /dev/null http://127.0.0.1:8090/; then | |
| 153 break | |
| 154 fi | |
| 155 if ! kill -0 "${service_pids[1]}" 2>/dev/null; then | |
| 156 echo "The dictation service exited before becoming ready." >&2 | |
| 157 exit 1 | |
| 158 fi | |
| 159 sleep 0.25 | |
| 160 done | |
| 161 if ! curl --silent --fail --output /dev/null http://127.0.0.1:8090/; then | |
| 162 echo "Timed out waiting for dictation at http://127.0.0.1:8090." >&2 | |
| 163 exit 1 | |
| 164 fi | |
| 165 | |
| 166 export MRJUNEJUNE_PYTHON_PATH="$python" | |
| 167 export MRJUNEJUNE_SIDECAR_ZIP="$sidecar_zip" | |
| 168 export COPILOT_SIDECAR_HOME="$state_root/copilot" | |
| 169 export LITELLM_BASE_URL="http://${litellm_host}:${litellm_port}/v1" | |
| 170 export LITELLM_MODEL="${LITELLM_MODEL:-jrpg-copilot}" | |
| 171 export LITELLM_WIRE_API="${LITELLM_WIRE_API:-completions}" | |
| 172 export LITELLM_API_KEY="$LITELLM_MASTER_KEY" | |
| 173 export INFINITE_CANVAS_COPILOT_SIDECAR_PATH="$sidecar_launcher" | |
| 174 export INFINITE_CANVAS_COPILOT_CLI_PATH="$copilot_cli" | |
| 175 | |
| 176 echo "Copilot orchestration is ready. Starting Infinite Canvas." | |
| 177 canvas_status=0 | |
| 178 "$canvas" "$@" || canvas_status=$? | |
| 179 exit "$canvas_status" |