Add env-driven model sync workflow
This commit is contained in:
parent
3bab4b3b4f
commit
3ad9ad2274
@ -40,6 +40,8 @@ Profiles are defined in `config/model-profiles.config.json`.
|
|||||||
- `config/model-profiles.config.json`: model profile definitions (`paid`, `free`)
|
- `config/model-profiles.config.json`: model profile definitions (`paid`, `free`)
|
||||||
- `config/model-schedule.config.json`: schedule settings (`dayProfile`, `nightProfile`, hours)
|
- `config/model-schedule.config.json`: schedule settings (`dayProfile`, `nightProfile`, hours)
|
||||||
- `config/model-budget-guard.config.json`: high-cost warning and auto-revert settings
|
- `config/model-budget-guard.config.json`: high-cost warning and auto-revert settings
|
||||||
|
- `config/models.env`: single-source model + schedule variables
|
||||||
|
- `scripts/sync_models_from_env.sh`: sync JSON configs from `config/models.env`
|
||||||
- `scripts/model_profile_switch.sh`: manual live switch command
|
- `scripts/model_profile_switch.sh`: manual live switch command
|
||||||
- `scripts/model_hygiene_workflow.sh`: apply profile + prune legacy model/provider/auth config
|
- `scripts/model_hygiene_workflow.sh`: apply profile + prune legacy model/provider/auth config
|
||||||
- `scripts/model_schedule_guard.sh`: scheduled switch worker
|
- `scripts/model_schedule_guard.sh`: scheduled switch worker
|
||||||
@ -84,13 +86,16 @@ PULL_LOCAL_MODELS=false bash ./setup/setup_openclaw_ollama.sh
|
|||||||
|
|
||||||
## 4) Quick Start (Most Common)
|
## 4) Quick Start (Most Common)
|
||||||
|
|
||||||
Run the hygiene workflow (recommended after any model changes):
|
Use env-driven sync + hygiene workflow (recommended after any model changes):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /Volumes/Data/openclaw-setups/openclaw-setup-max
|
cd /Volumes/Data/openclaw-setups/openclaw-setup-max
|
||||||
|
bash ./scripts/sync_models_from_env.sh
|
||||||
bash ./scripts/model_hygiene_workflow.sh paid
|
bash ./scripts/model_hygiene_workflow.sh paid
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Edit `config/models.env` whenever you want to change paid/free models or schedule windows.
|
||||||
|
|
||||||
Switch to free mode now:
|
Switch to free mode now:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
"profiles": {
|
"profiles": {
|
||||||
"paid": {
|
"paid": {
|
||||||
"description": "High-quality paid model for deep work",
|
"description": "High-quality paid model for deep work",
|
||||||
"primary": "openrouter/qwen/qwen3-coder:free",
|
"primary": "openrouter/moonshotai/kimi-k2.5",
|
||||||
"fallbacks": [
|
"fallbacks": [
|
||||||
"openrouter/qwen/qwen3-coder-next",
|
"openrouter/qwen/qwen3-coder-next",
|
||||||
"openrouter/qwen/qwen3-14b"
|
"openrouter/qwen/qwen3-14b"
|
||||||
|
|||||||
10
openclaw-setup-max/config/models.env
Normal file
10
openclaw-setup-max/config/models.env
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# Model profile settings (single source of truth)
|
||||||
|
PAID_PRIMARY=openrouter/moonshotai/kimi-k2.5
|
||||||
|
PAID_FALLBACKS=openrouter/qwen/qwen3-coder-next,openrouter/qwen/qwen3-14b
|
||||||
|
|
||||||
|
FREE_PRIMARY=openrouter/qwen/qwen3-coder:free
|
||||||
|
FREE_FALLBACKS=openrouter/qwen/qwen3-14b
|
||||||
|
|
||||||
|
# Format: dayProfile,nightProfile,dayStartHour,nightStartHour
|
||||||
|
SCHEDULE=paid,free,7,22
|
||||||
|
SCHEDULE_ENABLED=true
|
||||||
163
openclaw-setup-max/scripts/sync_models_from_env.sh
Executable file
163
openclaw-setup-max/scripts/sync_models_from_env.sh
Executable file
@ -0,0 +1,163 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
|
||||||
|
MODELS_ENV_FILE="${MODELS_ENV_FILE:-$ROOT_DIR/config/models.env}"
|
||||||
|
PROFILES_CONFIG="${MODEL_PROFILES_CONFIG:-$ROOT_DIR/config/model-profiles.config.json}"
|
||||||
|
SCHEDULE_CONFIG="${MODEL_SCHEDULE_CONFIG:-$ROOT_DIR/config/model-schedule.config.json}"
|
||||||
|
BUDGET_CONFIG="${MODEL_GUARD_CONFIG:-$ROOT_DIR/config/model-budget-guard.config.json}"
|
||||||
|
|
||||||
|
if ! command -v jq >/dev/null 2>&1; then
|
||||||
|
echo "[model-sync] jq is required" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'USAGE'
|
||||||
|
Usage:
|
||||||
|
bash ./scripts/sync_models_from_env.sh
|
||||||
|
|
||||||
|
Env source:
|
||||||
|
config/models.env (override with MODELS_ENV_FILE)
|
||||||
|
|
||||||
|
Required variables:
|
||||||
|
PAID_PRIMARY
|
||||||
|
PAID_FALLBACKS
|
||||||
|
FREE_PRIMARY
|
||||||
|
FREE_FALLBACKS
|
||||||
|
SCHEDULE # dayProfile,nightProfile,dayStartHour,nightStartHour
|
||||||
|
|
||||||
|
Optional variables:
|
||||||
|
SCHEDULE_ENABLED # true/false (default: true)
|
||||||
|
USAGE
|
||||||
|
}
|
||||||
|
|
||||||
|
trim() {
|
||||||
|
local s="$1"
|
||||||
|
s="${s#"${s%%[![:space:]]*}"}"
|
||||||
|
s="${s%"${s##*[![:space:]]}"}"
|
||||||
|
printf '%s' "$s"
|
||||||
|
}
|
||||||
|
|
||||||
|
csv_to_json_array() {
|
||||||
|
local csv="$1"
|
||||||
|
jq -cn --arg csv "$csv" '
|
||||||
|
$csv
|
||||||
|
| split(",")
|
||||||
|
| map(gsub("^\\s+|\\s+$"; ""))
|
||||||
|
| map(select(length > 0))
|
||||||
|
'
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -f "$MODELS_ENV_FILE" ]]; then
|
||||||
|
echo "[model-sync] missing env file: $MODELS_ENV_FILE" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ ! -f "$PROFILES_CONFIG" || ! -f "$SCHEDULE_CONFIG" || ! -f "$BUDGET_CONFIG" ]]; then
|
||||||
|
echo "[model-sync] required config files are missing" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
set -a
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$MODELS_ENV_FILE"
|
||||||
|
set +a
|
||||||
|
|
||||||
|
required=(PAID_PRIMARY PAID_FALLBACKS FREE_PRIMARY FREE_FALLBACKS SCHEDULE)
|
||||||
|
for key in "${required[@]}"; do
|
||||||
|
if [[ -z "${!key:-}" ]]; then
|
||||||
|
echo "[model-sync] missing required variable: $key" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
paid_fallbacks_json="$(csv_to_json_array "$PAID_FALLBACKS")"
|
||||||
|
free_fallbacks_json="$(csv_to_json_array "$FREE_FALLBACKS")"
|
||||||
|
|
||||||
|
if [[ "$(jq -r 'length' <<<"$paid_fallbacks_json")" == "0" ]]; then
|
||||||
|
echo "[model-sync] PAID_FALLBACKS cannot be empty" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ "$(jq -r 'length' <<<"$free_fallbacks_json")" == "0" ]]; then
|
||||||
|
echo "[model-sync] FREE_FALLBACKS cannot be empty" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
IFS=',' read -r day_profile_raw night_profile_raw day_start_raw night_start_raw <<<"$SCHEDULE"
|
||||||
|
day_profile="$(trim "${day_profile_raw:-}")"
|
||||||
|
night_profile="$(trim "${night_profile_raw:-}")"
|
||||||
|
day_start="$(trim "${day_start_raw:-}")"
|
||||||
|
night_start="$(trim "${night_start_raw:-}")"
|
||||||
|
|
||||||
|
if [[ -z "$day_profile" || -z "$night_profile" || -z "$day_start" || -z "$night_start" ]]; then
|
||||||
|
echo "[model-sync] SCHEDULE must be: dayProfile,nightProfile,dayStartHour,nightStartHour" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! [[ "$day_start" =~ ^[0-9]+$ && "$night_start" =~ ^[0-9]+$ ]]; then
|
||||||
|
echo "[model-sync] schedule hours must be integers" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if (( day_start < 0 || day_start > 23 || night_start < 0 || night_start > 23 )); then
|
||||||
|
echo "[model-sync] schedule hours must be in range 0..23" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
schedule_enabled="$(trim "${SCHEDULE_ENABLED:-true}")"
|
||||||
|
if [[ "$schedule_enabled" != "true" && "$schedule_enabled" != "false" ]]; then
|
||||||
|
echo "[model-sync] SCHEDULE_ENABLED must be true or false" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
tmp_profiles="$(mktemp)"
|
||||||
|
jq \
|
||||||
|
--arg paid_primary "$PAID_PRIMARY" \
|
||||||
|
--arg free_primary "$FREE_PRIMARY" \
|
||||||
|
--argjson paid_fallbacks "$paid_fallbacks_json" \
|
||||||
|
--argjson free_fallbacks "$free_fallbacks_json" \
|
||||||
|
'
|
||||||
|
.profiles.paid.primary = $paid_primary
|
||||||
|
| .profiles.paid.fallbacks = $paid_fallbacks
|
||||||
|
| .profiles.free.primary = $free_primary
|
||||||
|
| .profiles.free.fallbacks = $free_fallbacks
|
||||||
|
' "$PROFILES_CONFIG" > "$tmp_profiles"
|
||||||
|
mv "$tmp_profiles" "$PROFILES_CONFIG"
|
||||||
|
|
||||||
|
tmp_schedule="$(mktemp)"
|
||||||
|
jq \
|
||||||
|
--arg day_profile "$day_profile" \
|
||||||
|
--arg night_profile "$night_profile" \
|
||||||
|
--argjson day_start "$day_start" \
|
||||||
|
--argjson night_start "$night_start" \
|
||||||
|
--arg enabled "$schedule_enabled" \
|
||||||
|
'
|
||||||
|
.enabled = ($enabled == "true")
|
||||||
|
| .dayProfile = $day_profile
|
||||||
|
| .nightProfile = $night_profile
|
||||||
|
| .dayStartHour = $day_start
|
||||||
|
| .nightStartHour = $night_start
|
||||||
|
' "$SCHEDULE_CONFIG" > "$tmp_schedule"
|
||||||
|
mv "$tmp_schedule" "$SCHEDULE_CONFIG"
|
||||||
|
|
||||||
|
tmp_budget="$(mktemp)"
|
||||||
|
jq \
|
||||||
|
--arg low_model "$FREE_PRIMARY" \
|
||||||
|
'.lowModel = $low_model' \
|
||||||
|
"$BUDGET_CONFIG" > "$tmp_budget"
|
||||||
|
mv "$tmp_budget" "$BUDGET_CONFIG"
|
||||||
|
|
||||||
|
echo "[model-sync] updated:"
|
||||||
|
echo " - $PROFILES_CONFIG"
|
||||||
|
echo " - $SCHEDULE_CONFIG"
|
||||||
|
echo " - $BUDGET_CONFIG"
|
||||||
|
echo ""
|
||||||
|
echo "[model-sync] paid primary: $(jq -r '.profiles.paid.primary' "$PROFILES_CONFIG")"
|
||||||
|
echo "[model-sync] free primary: $(jq -r '.profiles.free.primary' "$PROFILES_CONFIG")"
|
||||||
|
echo "[model-sync] schedule: $(jq -r '.enabled, .dayProfile, .nightProfile, .dayStartHour, .nightStartHour' "$SCHEDULE_CONFIG" | paste -sd ',' -)"
|
||||||
|
echo "[model-sync] budget lowModel: $(jq -r '.lowModel' "$BUDGET_CONFIG")"
|
||||||
Loading…
Reference in New Issue
Block a user