Initial commit: OpenClaw ops workspace
This commit is contained in:
166
skills/model-selector/scripts/switch-models.sh
Executable file
166
skills/model-selector/scripts/switch-models.sh
Executable file
@@ -0,0 +1,166 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage:
|
||||
switch-models.sh --non-main-primary <catalog-id> --non-main-fallbacks <m1,m2,...> [--clear-session-pins] [--pattern <old-model-pattern>]
|
||||
|
||||
Example:
|
||||
switch-models.sh \
|
||||
--non-main-primary "nanogpt/zai-org/glm-5" \
|
||||
--non-main-fallbacks "lightning_ai/lightning-ai/kimi-k2.5,nanogpt/zai-org/glm-4.7" \
|
||||
--clear-session-pins \
|
||||
--pattern "gemini-3-flash"
|
||||
|
||||
Notes:
|
||||
- Updates agents.list[].model for home/security/research.
|
||||
- Keeps main/default model untouched.
|
||||
- Validates candidates against live /v1/models.
|
||||
- Optionally removes matching per-session model pins.
|
||||
EOF
|
||||
}
|
||||
|
||||
PRIMARY=""
|
||||
FALLBACKS=""
|
||||
CLEAR_PINS=0
|
||||
PATTERN="gemini-3-flash"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--non-main-primary)
|
||||
PRIMARY="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--non-main-fallbacks)
|
||||
FALLBACKS="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--clear-session-pins)
|
||||
CLEAR_PINS=1
|
||||
shift
|
||||
;;
|
||||
--pattern)
|
||||
PATTERN="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unknown arg: $1" >&2
|
||||
usage >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$PRIMARY" || -z "$FALLBACKS" ]]; then
|
||||
echo "ERROR: --non-main-primary and --non-main-fallbacks are required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
IFS=',' read -ra FB <<< "$FALLBACKS"
|
||||
if [[ ${#FB[@]} -lt 1 ]]; then
|
||||
echo "ERROR: at least 1 fallback required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for m in "$PRIMARY" "${FB[@]}"; do
|
||||
m_clean="$(echo "${m#llm-proxy/}" | xargs)"
|
||||
"$SCRIPT_DIR/validate-model.sh" "$m_clean" >/dev/null
|
||||
echo "validated-live: $m_clean"
|
||||
done
|
||||
|
||||
STATE_FILE="${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/openclaw.json"
|
||||
if [[ ! -f "$STATE_FILE" ]]; then
|
||||
for alt in \
|
||||
"${OPENCLAW_STATE_DIR:-$HOME/.openclaw/state}/openclaw.json" \
|
||||
"${OPENCLAW_CONFIG_DIR:-$HOME/.openclaw/config}/openclaw.json" \
|
||||
"/opt/openclaw/state/openclaw.json"; do
|
||||
if [[ -f "$alt" ]]; then
|
||||
STATE_FILE="$alt"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
[[ -f "$STATE_FILE" ]] || { echo "ERROR: openclaw.json not found" >&2; exit 1; }
|
||||
|
||||
# Validate against local configured catalog too (gateway uses this on restart)
|
||||
node - <<'NODE' "$STATE_FILE" "$PRIMARY" "$FALLBACKS"
|
||||
const fs=require('fs');
|
||||
const p=process.argv[2];
|
||||
const primary=process.argv[3].replace(/^llm-proxy\//,'');
|
||||
const fallbacks=process.argv[4].split(',').map(s=>s.trim().replace(/^llm-proxy\//,'')).filter(Boolean);
|
||||
const j=JSON.parse(fs.readFileSync(p,'utf8'));
|
||||
const catalog=new Set((j.models?.providers?.['llm-proxy']?.models||[]).map(m=>m.id));
|
||||
const missing=[];
|
||||
if(!catalog.has(primary)) missing.push(primary);
|
||||
for (const f of fallbacks) if(!catalog.has(f)) missing.push(f);
|
||||
if (missing.length) {
|
||||
console.error('ERROR: target models missing from local llm-proxy catalog in openclaw.json');
|
||||
for (const m of [...new Set(missing)]) console.error(' - '+m);
|
||||
process.exit(2);
|
||||
}
|
||||
console.log('validated-local-catalog: ok');
|
||||
NODE
|
||||
|
||||
primary_full="llm-proxy/${PRIMARY#llm-proxy/}"
|
||||
raw_fb="${FALLBACKS}"
|
||||
fb_json="$(node - <<'NODE' "$PRIMARY" "$raw_fb"
|
||||
const primary=process.argv[2].replace(/^llm-proxy\//,'');
|
||||
const raw=process.argv[3].split(',').map(s=>s.trim().replace(/^llm-proxy\//,'')).filter(Boolean);
|
||||
const seen=new Set();
|
||||
const out=[];
|
||||
for (const item of raw) {
|
||||
if (item===primary) continue;
|
||||
if (seen.has(item)) continue;
|
||||
seen.add(item);
|
||||
out.push(`llm-proxy/${item}`);
|
||||
}
|
||||
process.stdout.write(JSON.stringify(out));
|
||||
NODE
|
||||
)"
|
||||
|
||||
for aid in home security research; do
|
||||
cmd1="openclaw config set agents.list[\"$aid\"].model.primary $primary_full"
|
||||
echo "$cmd1"
|
||||
eval "$cmd1"
|
||||
cmd2="openclaw config set agents.list[\"$aid\"].model.fallbacks '$fb_json' --json"
|
||||
echo "$cmd2"
|
||||
eval "$cmd2"
|
||||
done
|
||||
|
||||
if [[ "$CLEAR_PINS" -eq 1 ]]; then
|
||||
echo "clearing matching session model pins pattern=$PATTERN"
|
||||
for aid in home security research; do
|
||||
sess="/home/node/.openclaw/agents/${aid}/sessions/sessions.json"
|
||||
[[ -f "$sess" ]] || continue
|
||||
node - <<'NODE' "$sess" "$PATTERN" "$aid"
|
||||
const fs=require('fs');
|
||||
const file=process.argv[2];
|
||||
const pattern=new RegExp(process.argv[3],'i');
|
||||
const aid=process.argv[4];
|
||||
const j=JSON.parse(fs.readFileSync(file,'utf8'));
|
||||
let removed=0;
|
||||
for (const [k,v] of Object.entries(j)) {
|
||||
const model=(v&&v.model)?String(v.model):'';
|
||||
if (model && pattern.test(model)) {
|
||||
delete v.model;
|
||||
removed++;
|
||||
}
|
||||
}
|
||||
fs.writeFileSync(file, JSON.stringify(j,null,2)+'\n');
|
||||
console.log(`agent=${aid} removed_model_pins=${removed}`);
|
||||
NODE
|
||||
done
|
||||
fi
|
||||
|
||||
echo "running post-change audit..."
|
||||
"$SCRIPT_DIR/audit-model-state.sh" "$PATTERN"
|
||||
|
||||
echo "done. restart gateway to apply runtime changes."
|
||||
Reference in New Issue
Block a user