#!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" CONFIG_PATH="${MODEL_PROFILES_CONFIG:-$ROOT_DIR/config/model-profiles.config.json}" SESSION_KEY="${MODEL_SWITCH_SESSION_KEY:-agent:main:main}" if ! command -v jq >/dev/null 2>&1; then echo "[model-switch] jq is required" >&2 exit 1 fi if ! command -v openclaw >/dev/null 2>&1; then echo "[model-switch] openclaw CLI is required" >&2 exit 1 fi usage() { cat <<'USAGE' Usage: bash ./scripts/model_profile_switch.sh status bash ./scripts/model_profile_switch.sh [--no-live] [--no-status] Examples: bash ./scripts/model_profile_switch.sh free bash ./scripts/model_profile_switch.sh paid --no-live USAGE } get_session_id() { local session_key="$1" local state_dir="${OPENCLAW_STATE_DIR:-$HOME/.openclaw}" local sessions_file="$state_dir/agents/main/sessions/sessions.json" if [[ ! -f "$sessions_file" ]]; then return 0 fi jq -r --arg key "$session_key" '.[$key].sessionId // empty' "$sessions_file" } if [[ ! -f "$CONFIG_PATH" ]]; then echo "[model-switch] missing config: $CONFIG_PATH" >&2 exit 1 fi profile="${1:-}" if [[ -z "$profile" ]]; then usage exit 1 fi shift || true apply_live="true" show_status="true" while (( "$#" )); do case "$1" in --no-live) apply_live="false" ;; --no-status) show_status="false" ;; -h|--help) usage exit 0 ;; *) echo "[model-switch] unknown argument: $1" >&2 usage exit 1 ;; esac shift done if [[ "$profile" == "status" ]]; then echo "Available profiles:" jq -r '.profiles | to_entries[] | "- \(.key): \(.value.primary) (\(.value.description // "no description"))"' "$CONFIG_PATH" echo "" openclaw models status exit 0 fi profile_exists="$(jq -r --arg p "$profile" '.profiles[$p] != null' "$CONFIG_PATH")" if [[ "$profile_exists" != "true" ]]; then echo "[model-switch] unknown profile '$profile' in $CONFIG_PATH" >&2 jq -r '.profiles | keys[]' "$CONFIG_PATH" | sed 's/^/ - /' exit 1 fi primary="$(jq -r --arg p "$profile" '.profiles[$p].primary // empty' "$CONFIG_PATH")" if [[ -z "$primary" ]]; then echo "[model-switch] profile '$profile' has no primary model" >&2 exit 1 fi fallbacks=() while IFS= read -r fb; do [[ -z "$fb" ]] && continue fallbacks+=("$fb") done < <(jq -r --arg p "$profile" '.profiles[$p].fallbacks[]? // empty' "$CONFIG_PATH") echo "[model-switch] Applying profile: $profile" echo "[model-switch] primary: $primary" if [[ ${#fallbacks[@]} -gt 0 ]]; then echo "[model-switch] fallbacks: ${fallbacks[*]}" else echo "[model-switch] fallbacks: (none)" fi openclaw models set "$primary" >/dev/null openclaw models fallbacks clear >/dev/null for fb in "${fallbacks[@]}"; do [[ -z "$fb" ]] && continue openclaw models fallbacks add "$fb" >/dev/null done provider_policy_len="$(jq -r --arg p "$profile" '.profiles[$p].providerPolicy // {} | length' "$CONFIG_PATH")" if [[ "$provider_policy_len" != "0" ]]; then while IFS=$'\t' read -r provider enabled; do if [[ -z "$provider" || -z "$enabled" ]]; then continue fi openclaw config set --json "providers.$provider.enabled" "$enabled" >/dev/null || true done < <(jq -r --arg p "$profile" '.profiles[$p].providerPolicy // {} | to_entries[] | "\(.key)\t\(.value)"' "$CONFIG_PATH") fi if [[ "$apply_live" == "true" ]]; then session_id="$(get_session_id "$SESSION_KEY")" if [[ -n "$session_id" ]]; then python3 - "$session_id" "$primary" <<'PY' import subprocess import sys session_id = sys.argv[1] primary = sys.argv[2] cmd = [ "openclaw", "agent", "--session-id", session_id, "--channel", "last", "--message", f"/model {primary}", ] try: subprocess.run( cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=False, timeout=20, ) except subprocess.TimeoutExpired: pass PY echo "[model-switch] live session updated: $session_id" else echo "[model-switch] no active session found for key $SESSION_KEY" fi fi if [[ "$show_status" == "true" ]]; then echo "" openclaw models status fi