Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/agentic-systems-summer-2026_agentic-systems-course.json

1 line
30 KiB
JSON
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"repo": "agentic-systems-summer-2026/agentic-systems-course", "files": {"scripts/select-model.sh": {"58a6ea403f16224c5dd473e6cc06841240004c31": {"t": 1785747181.9367108, "n": 12938, "h": "0f0136da61844abe", "c": "#!/usr/bin/env bash\n# Switch the OpenClaw model.\n#\n# Asks where your PRIMARY model should come from — the OU LiteLLM Sandbox (the\n# course endpoint, no per-token cost) or OpenRouter (your own key and credit) —\n# then lets you pick from that catalog. Then asks the same for your FALLBACK,\n# which you can decline. Primary and fallback may come from different providers.\n#\n# Either key works on its own. With only one, the provider question is skipped\n# for the primary and reduced to \"that provider or no fallback\" for the backup.\n#\n# The gateway hot-reloads. For a chat you are already in, switch with /model.\nset -uo pipefail\n# Make 'openclaw' findable in non-interactive shells.\nsource \"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)/_env.sh\" 2>/dev/null || true\nLITELLM_BASE_URL=\"${LITELLM_BASE_URL:-https://litellm.lib.ou.edu}\"\nENV_FILE=\"${HOME}/.openclaw/.env\"\noubase=\"${LITELLM_BASE_URL%/}\"\n\nread_env() { # read_env VAR -> value from process env or ~/.openclaw/.env\n local var=\"$1\" val=\"${!1:-}\"\n [[ -z \"${val}\" && -f \"${ENV_FILE}\" ]] && val=\"$(grep -E \"^${var}=\" \"${ENV_FILE}\" | tail -n1 | cut -d= -f2- || true)\"\n printf '%s' \"${val}\"\n}\nusage() {\n cat <<'EOF'\nUsage: bash scripts/select-model.sh\n\n Walks you through choosing a primary model and an optional fallback.\n For each, you first choose a provider (OU AI Sandbox or OpenRouter),\n then pick from that provider's catalog.\nEOF\n}\nfor arg in \"$@\"; do\n case \"${arg}\" in\n -h|--help) usage; exit 0 ;;\n *) echo \"Unknown option '${arg}'.\"; usage; exit 1 ;;\n esac\ndone\n# ---- prerequisites --------------------------------------------------------\nneed() { command -v \"$1\" >/dev/null 2>&1 || { echo \"❌ Required tool '$1' not found — $2\"; exit 1; }; }\nneed curl \"rebuild the Codespace or install curl.\"\nneed python3 \"rebuild the Codespace or install python3.\"\nneed openclaw \"OpenClaw isn't on PATH yet — start the Gateway task first, or run: bash .devcontainer/setup.sh\"\n# ---- which providers are available? ---------------------------------------\nOR_KEY=\"$(read_env OPENROUTER_API_KEY)\"\nLL_KEY=\"$(read_env LITELLM_API_KEY)\"\n[[ \"${OR_KEY}\" == \"sk-or-REPLACE_ME\" ]] && OR_KEY=\"\"\n[[ \"${LL_KEY}\" == \"sk-REPLACE_ME\" ]] && LL_KEY=\"\"\nHAVE_OR=0; [[ -n \"${OR_KEY}\" ]] && HAVE_OR=1\nHAVE_LL=0; [[ -n \"${LL_KEY}\" ]] && HAVE_LL=1\nif (( ! HAVE_OR && ! HAVE_LL )); then\n echo \"❌ No model provider key found.\"\n echo\n echo \" You need at least one of:\"\n echo \" • OU AI Sandbox key (LITELLM_API_KEY) — the course's first choice, no per-token cost.\"\n echo \" • Your own OpenRouter key (OPENROUTER_API_KEY) — https://openrouter.ai (Settings → Keys).\"\n echo\n echo \" Set one with: bash scripts/set-key.sh\"\n exit 1\nfi\n# ---- catalogs (fetched once, on demand) -----------------------------------\nOU=(); OU_LOADED=0\nOR_IDS=(); OR_LABELS=(); OR_LOADED=0\n\nensure_ou_catalog() {\n (( OU_LOADED )) && return 0\n echo \"Fetching OU models from ${oubase} ...\" >&2\n local http=000 url\n for url in \"${oubase}/v1/models\" \"${oubase}/models\"; do\n http=\"$(curl -s -m 20 -o /tmp/ou_models.json -w '%{http_code}' -H \"Authorization: Bearer ${LL_KEY}\" \"${url}\" || echo 000)\"\n [[ \"${http}\" == \"200\" ]] && break\n done\n case \"${http}\" in\n 200) ;;\n 401|403) echo \"❌ OU Sandbox key rejected (HTTP ${http}).\" >&2; exit 1 ;;\n 000) echo \"❌ Could not reach ${oubase} (network/endpoint issue). Check the URL or try again.\" >&2; exit 1 ;;\n *) echo \"❌ OU gateway returned HTTP ${http}. Details in /tmp/ou_models.json\" >&2; exit 1 ;;\n esac\n mapfile -t OU < <(python3 -c 'import json\nfor m in json.load(open(\"/tmp/ou_models.json\")).get(\"data\",[]): print(m[\"id\"])' 2>/dev/null | sort -u)\n ((${#OU[@]})) || { echo \"❌ No models parsed from the OU response (/tmp/ou_models.json may be malformed).\" >&2; exit 1; }\n OU_LOADED=1\n}\nensure_or_catalog() {\n (( OR_LOADED )) && return 0\n # Listing is public, but a bad key makes the choice moot — check it first.\n local kc\n kc=\"$(curl -s -m 15 -o /dev/null -w '%{http_code}' -H \"Authorization: Bearer ${OR_KEY}\" https://openrouter.ai/api/v1/key || echo 000)\"\n if [[ \"${kc}\" != \"200\" ]]; then\n echo \"⚠️ OpenRouter key check returned HTTP ${kc} — it may be invalid, disabled, or out of credit.\" >&2\n (( HAVE_LL )) && echo \" The OU AI Sandbox is available instead.\" >&2\n local yn=\"\"\n read -rp \"Continue with OpenRouter anyway? [y/N] \" yn </dev/tty || true\n [[ \"${yn}\" =~ ^[Yy]$ ]] || return 1\n fi\n echo \"Fetching tool-capable OpenRouter models ...\" >&2\n if ! curl -fsS -m 30 \"https://openrouter.ai/api/v1/models?supported_parameters=tools\" -o /tmp/or_models.json; then\n echo \"❌ Could not reach OpenRouter (network/endpoint). Try again in a moment.\" >&2\n return 1\n fi\n local rows=()\n mapfile -t rows < <(python3 - <<'PY'\nimport json\ndata = json.load(open(\"/tmp/or_models.json\")).get(\"data\", [])\npopular = {\"anthropic\",\"openai\",\"google\",\"x-ai\",\"meta-llama\",\"mistralai\",\n \"qwen\",\"deepseek\",\"z-ai\",\"moonshotai\",\"minimax\"}\ndef perM(v):\n try: return float(v) * 1_000_000\n except Exception: return None\nrows = []\nfor m in data:\n mid = m.get(\"id\", \"\")\n vendor = mid.split(\"/\")[0] if \"/\" in mid else mid\n arch = m.get(\"architecture\", {}) or {}\n if \"text\" not in (arch.get(\"input_modalities\") or []): continue\n if \"tools\" not in (m.get(\"supported_parameters\") or []): continue\n if vendor not in popular: continue\n pr = m.get(\"pricing\", {}) or {}\n pin, pout = perM(pr.get(\"prompt\")), perM(pr.get(\"completion\"))\n free = (pin == 0 and pout == 0)\n ctx = m.get(\"context_length\") or 0\n ctxs = f\"{ctx // 1000}k\" if ctx else \"?\"\n if free: price = \"FREE\"\n elif pin is not None and pout is not None: price = f\"${pin:.2f}/${pout:.2f} /M\"\n else: price = \"price n/a\"\n rows.append((0 if free else 1, pin if pin is not None else 9e9, vendor, mid,\n f\"{price:<16} {mid} ({ctxs})\"))\nrows.sort(key=lambda r: (r[0], r[1], r[2], r[3]))\n# OpenRouter's own Free Models Router: zero-cost, picks a free model per\n# request and filters for tool support itself. The ?supported_parameters=tools\n# catalog filter excludes routers, so add it explicitly at the top. Other\n# openrouter/* routers stay hidden — some fan out to paid models and would\n# drain a student key fast.\nrows.insert(0, (0, 0.0, \"openrouter\", \"openrouter/free\",\n f\"{'FREE':<16} openrouter/free (router — picks a free, tool-capable model per request; rate-limited, fine for smoke tests)\"))\nfor r in rows: print(f\"{r[3]}\\t{r[4]}\")\nPY\n)\n if ! ((${#rows[@]})); then\n echo \"❌ No tool-capable models from popular vendors returned (OpenRouter's catalog may have shifted).\" >&2\n return 1\n fi\n local row\n OR_IDS=(); OR_LABELS=()\n for row in \"${rows[@]}\"; do\n OR_IDS+=(\"${row%%$'\\t'*}\")\n OR_LABELS+=(\"${row#*$'\\t'}\")\n done\n OR_LOADED=1\n}\n# ---- provider question ----------------------------------------------------\n# choose_provider <ROLE> <allow_none 0|1> -> prints \"ou\" | \"or\" | \"none\"\nchoose_provider() {\n local role=\"$1\" allow_none=\"$2\"\n local opts=() vals=() i ans=\"\"\n if (( HAVE_LL )); then opts+=(\"OU AI Sandbox (LiteLLM) — course endpoint, no per-token cost\"); vals+=(\"ou\"); fi\n if (( HAVE_OR )); then opts+=(\"OpenRouter — your own key and your own credit\"); vals+=(\"or\"); fi\n if (( allow_none )); then opts+=(\"No ${role,,} model\"); vals+=(\"none\"); fi\n # Only one real choice and nothing to decline: take it silently.\n if (( ${#vals[@]} == 1 )); then printf '%s' \"${vals[0]}\"; return 0; fi\n echo >&2\n echo \"Where should your ${role} model come from?\" >&2\n for i in \"${!opts[@]}\"; do printf \" %d) %s\\n\" \"$((i+1))\" \"${opts[$i]}\" >&2; done\n echo >&2\n while :; do\n read -rp \"Choice: \" ans </dev/tty || { echo \"No input — aborting.\" >&2; exit 1; }\n ans=\"${ans// /}\"\n if [[ \"${ans}\" =~ ^[0-9]+$ ]] && (( ans >= 1 && ans <= ${#vals[@]} )); then\n printf '%s' \"${vals[$((ans-1))]}\"; return 0\n fi\n echo \"Please enter a number between 1 and ${#vals[@]}.\" >&2\n done\n}\n# ---- model pickers --------------------------------------------------------\n# pick_ou <ROLE> <multi 0|1> -> prints newline-separated refs (may be empty)\npick_ou() {\n local role=\"$1\" multi=\"$2\" i ans=\"\"\n ensure_ou_catalog\n echo >&2; echo \"OU AI Sandbox models:\" >&2\n for i in \"${!OU[@]}\"; do printf \" %3d) %s\\n\" \"$((i+1))\" \"${OU[$i]}\" >&2; done\n echo >&2\n if (( multi )); then\n read -rp \"${role} number(s), comma-separated (blank = none): \" ans </dev/tty || true\n [[ -z \"${ans// /}\" ]] && return 0\n local IDX n\n IFS=',' read -ra IDX <<< \"${ans}\"\n for n in \"${IDX[@]}\"; do\n n=\"${n// /}\"\n if [[ \"${n}\" =~ ^[0-9]+$ ]] && (( n >= 1 && n <= ${#OU[@]} )); then\n printf 'litellm/%s\\n' \"${OU[$((n-1))]}\"\n else\n echo \"⚠️ Ignoring invalid entry '${n}'.\" >&2\n fi\n done\n return 0\n fi\n while :; do\n read -rp \"${role} model number: \" ans </dev/tty || { echo \"No input — aborting.\" >&2; exit 1; }\n ans=\"${ans// /}\"\n if [[ \"${ans}\" =~ ^[0-9]+$ ]] && (( ans >= 1 && ans <= ${#OU[@]} )); then\n printf 'litellm/%s\\n' \"${OU[$((ans-1))]}\"; return 0\n fi\n echo \"Please enter a number between 1 and ${#OU[@]}.\" >&2\n done\n}\n# pick_or <ROLE> <multi 0|1> -> prints newline-separated refs (may be empty)\npick_or() {\n local role=\"$1\" multi=\"$2\" i ans=\"\"\n ensure_or_catalog || return 1\n echo >&2\n echo \"OpenRouter models (tool-capable; free first, then by price — remember it's your own credit):\" >&2\n for i in \"${!OR_LABELS[@]}\"; do printf \" %3d) %s\\n\" \"$((i+1))\" \"${OR_LABELS[$i]}\" >&2; done\n echo >&2\n if (( multi )); then\n read -rp \"${role} number(s), comma-separated (blank = none): \" ans </dev/tty || true\n [[ -z \"${ans// /}\" ]] && return 0\n local IDX n\n IFS=',' read -ra IDX <<< \"${ans}\"\n for n in \"${IDX[@]}\"; do\n n=\"${n// /}\"\n if [[ \"${n}\" =~ ^[0-9]+$ ]] && (( n >= 1 && n <= ${#OR_IDS[@]} )); then\n printf 'openrouter/%s\\n' \"${OR_IDS[$((n-1))]}\"\n else\n echo \"⚠️ Ignoring invalid entry '${n}'.\" >&2\n fi\n done\n return 0\n fi\n while :; do\n read -rp \"${role} model number: \" ans </dev/tty || { echo \"No input — aborting.\" >&2; exit 1; }\n ans=\"${ans// /}\"\n if [[ \"${ans}\" =~ ^[0-9]+$ ]] && (( ans >= 1 && ans <= ${#OR_IDS[@]} )); then\n printf 'openrouter/%s\\n' \"${OR_IDS[$((ans-1))]}\"; return 0\n fi\n echo \"Please enter a number between 1 and ${#OR_IDS[@]}.\" >&2\n done\n}\n# ---- apply ----------------------------------------------------------------\napply_models() { # apply_models <primary-ref> [fallback-refs...]\n local primary=\"$1\"; shift\n echo\n echo \"→ primary: ${primary}\"\n if ! openclaw models set \"${primary}\"; then\n echo \"❌ Could not set primary '${primary}'.\"\n echo \" Run 'openclaw models list' for valid refs, and check the gateway is running.\"\n exit 1\n fi\n openclaw models fallbacks clear >/dev/null 2>&1 || true\n local f added=0\n for f in \"$@\"; do\n [[ -n \"${f}\" ]] || continue\n # A fallback identical to the primary can never help — drop it.\n if [[ \"${f}\" == \"${primary}\" ]]; then\n echo \"⚠️ Skipping fallback '${f}' — it is the same as your primary.\"\n continue\n fi\n echo \"→ fallback: ${f}\"\n if openclaw models fallbacks add \"${f}\"; then added=$((added+1)); else echo \"⚠️ Couldn't add fallback '${f}' — skipped.\"; fi\n done\n (( added == 0 )) && echo \"→ fallback: none\"\n echo; echo \"Current model configuration:\"\n openclaw models status 2>/dev/null || echo \"⚠️ 'openclaw models status' unavailable (is the gateway running?).\"\n echo \"(Hot-reloaded for new sessions. For the chat you're in now, switch with /model.)\"\n}\nwarn_weak_fallback() {\n local f\n for f in \"$@\"; do\n if [[ \"${f,,}\" == *\"30b\"* || \"${f,,}\" == *\"coder\"* ]]; then\n echo\n echo \"ℹ️ Note: '${f}' is a small model, used only when your primary is unavailable.\"\n echo \" If your agent starts inventing results or claiming it has no network access,\"\n echo \" check which model is actually answering — you may have failed over.\"\n return\n fi\n done\n}\n# ---- primary --------------------------------------------------------------\nPRIMARY=\"\"\nwhile [[ -z \"${PRIMARY}\" ]]; do\n prov=\"$(choose_provider \"PRIMARY\" 0)\"\n case \"${prov}\" in\n ou) PRIMARY=\"$(pick_ou \"Primary\" 0)\" ;;\n or) PRIMARY=\"$(pick_or \"Primary\" 0)\" || {\n if (( HAVE_LL )); then echo \"Falling back to the OU AI Sandbox.\" >&2; PRIMARY=\"$(pick_ou \"Primary\" 0)\"; else exit 1; fi\n } ;;\n esac\ndone\n# ---- fallback -------------------------------------------------------------\nFB=()\nprov=\"$(choose_provider \"FALLBACK\" 1)\"\ncase \"${prov}\" in\n ou) mapfile -t FB < <(pick_ou \"Fallback\" 1) ;;\n or) mapfile -t FB < <(pick_or \"Fallback\" 1) || FB=() ;;\n none) FB=() ;;\nesac\n# ---- go -------------------------------------------------------------------\napply_models \"${PRIMARY}\" ${FB[@]+\"${FB[@]}\"}\nwarn_weak_fallback ${FB[@]+\"${FB[@]}\"}\n"}, "7c08e34de3824170c0773eee343e09fd554458c6": {"t": 1785771078.936447, "n": 8131, "h": "31e053c496070a15", "c": "#!/usr/bin/env bash\n# Switch the OpenClaw model. Browses tool-capable OpenRouter models (your own\n# key) and — when an OU LiteLLM Sandbox key is present — the OU Sandbox\n# catalog (the course's first-choice endpoint). Sets primary + optional\n# fallback; the gateway hot-reloads.\nset -uo pipefail\n# Make 'openclaw' findable in non-interactive shells.\nsource \"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)/_env.sh\" 2>/dev/null || true\n\nLITELLM_BASE_URL=\"${LITELLM_BASE_URL:-https://litellm.lib.ou.edu}\"\nENV_FILE=\"${HOME}/.openclaw/.env\"\noubase=\"${LITELLM_BASE_URL%/}\"\n\nread_env() { # read_env VAR -> value from process env or ~/.openclaw/.env\n local var=\"$1\" val=\"${!1:-}\"\n [[ -z \"${val}\" && -f \"${ENV_FILE}\" ]] && val=\"$(grep -E \"^${var}=\" \"${ENV_FILE}\" | tail -n1 | cut -d= -f2- || true)\"\n printf '%s' \"${val}\"\n}\n\napply_models() { # apply_models <prefix> <primary> [fallbacks...]\n local prefix=\"$1\" primary=\"$2\"; shift 2\n echo \"→ primary: ${prefix}${primary}\"\n if ! openclaw models set \"${prefix}${primary}\"; then\n echo \"❌ Could not set primary '${prefix}${primary}'.\"\n echo \" Run 'openclaw models list' for valid refs, and check the gateway is running.\"\n exit 1\n fi\n openclaw models fallbacks clear >/dev/null 2>&1 || true\n local f\n for f in \"$@\"; do\n [[ -n \"${f}\" ]] || continue\n echo \"→ fallback: ${prefix}${f}\"\n openclaw models fallbacks add \"${prefix}${f}\" || echo \"⚠️ Couldn't add fallback '${prefix}${f}' — skipped.\"\n done\n echo; echo \"Current model configuration:\"\n openclaw models status 2>/dev/null || echo \"⚠️ 'openclaw models status' unavailable (is the gateway running?).\"\n echo \"(Hot-reloaded for new sessions. For the chat you're in now, switch with /model.)\"\n}\n\n# ---- prerequisites --------------------------------------------------------\nneed() { command -v \"$1\" >/dev/null 2>&1 || { echo \"❌ Required tool '$1' not found — $2\"; exit 1; }; }\nneed curl \"rebuild the Codespace or install curl.\"\nneed python3 \"rebuild the Codespace or install python3.\"\nneed openclaw \"OpenClaw isn't on PATH yet — start the Gateway task first, or run: bash .devcontainer/setup.sh\"\n\n# ---- OpenRouter catalog (primary) -----------------------------------------\nOR_KEY=\"$(read_env OPENROUTER_API_KEY)\"\n[[ -z \"${OR_KEY}\" || \"${OR_KEY}\" == \"sk-or-REPLACE_ME\" ]] && \\\n { echo \"No OpenRouter key. Create one at https://openrouter.ai (Settings → Keys), then run: bash scripts/set-key.sh\"; exit 1; }\n\n# Validate the key first — listing is public, but selecting a model is moot if the key is bad.\nkc=\"$(curl -s -m 15 -o /dev/null -w '%{http_code}' -H \"Authorization: Bearer ${OR_KEY}\" https://openrouter.ai/api/v1/key || echo 000)\"\nif [[ \"${kc}\" != \"200\" ]]; then\n echo \"⚠️ OpenRouter key check returned HTTP ${kc} — it may be invalid, disabled, or out of credit.\"\n echo \" You can still browse, but the model will fail at runtime until the key works.\"\n read -rp \"Continue anyway? [y/N] \" yn </dev/tty || yn=\"\"\n [[ \"${yn}\" =~ ^[Yy]$ ]] || { echo \"Aborted.\"; exit 1; }\nfi\n\necho \"Fetching tool-capable OpenRouter models ...\"\nif ! curl -fsS -m 30 \"https://openrouter.ai/api/v1/models?supported_parameters=tools\" -o /tmp/or_models.json; then\n echo \"❌ Could not reach OpenRouter (network/endpoint). Try again in a moment.\"; exit 1\nfi\nmapfile -t ORROWS < <(python3 - <<'PY'\nimport json\ndata = json.load(open(\"/tmp/or_models.json\")).get(\"data\", [])\npopular = {\"anthropic\",\"openai\",\"google\",\"x-ai\",\"meta-llama\",\"mistralai\",\n \"qwen\",\"deepseek\",\"z-ai\",\"moonshotai\",\"minimax\"}\ndef perM(v):\n try: return float(v) * 1_000_000\n except Exception: return None\nrows = []\nfor m in data:\n mid = m.get(\"id\", \"\")\n vendor = mid.split(\"/\")[0] if \"/\" in mid else mid\n arch = m.get(\"architecture\", {}) or {}\n if \"text\" not in (arch.get(\"input_modalities\") or []): continue\n if \"tools\" not in (m.get(\"supported_parameters\") or []): continue\n if vendor not in popular: continue\n pr = m.get(\"pricing\", {}) or {}\n pin, pout = perM(pr.get(\"prompt\")), perM(pr.get(\"completion\"))\n free = (pin == 0 and pout == 0)\n ctx = m.get(\"context_length\") or 0\n ctxs = f\"{ctx // 1000}k\" if ctx else \"?\"\n if free: price = \"FREE\"\n elif pin is not None and pout is not None: price = f\"${pin:.2f}/${pout:.2f} /M\"\n else: price = \"price n/a\"\n rows.append((0 if free else 1, pin if pin is not None else 9e9, vendor, mid,\n f\"{price:<16} {mid} ({ctxs})\"))\nrows.sort(key=lambda r: (r[0], r[1], r[2], r[3]))\n# OpenRouter's own Free Models Router: zero-cost, picks a free model per\n# request and filters for tool support itself. The ?supported_parameters=tools\n# catalog filter excludes routers, so add it explicitly at the top. Other\n# openrouter/* routers stay hidden — some fan out to paid models and would\n# drain a student key fast.\nrows.insert(0, (0, 0.0, \"openrouter\", \"openrouter/free\",\n f\"{'FREE':<16} openrouter/free (router — picks a free, tool-capable model per request; rate-limited, fine for smoke tests)\"))\nfor r in rows: print(f\"{r[3]}\\t{r[4]}\")\nPY\n)\n((${#ORROWS[@]})) || { echo \"❌ No tool-capable models from popular vendors returned (OpenRouter's catalog may have shifted).\"; exit 1; }\nOR_IDS=(); i=1\necho; echo \"OpenRouter models (tool-capable; free first, then by price — remember it's your own credit):\"\nfor row in \"${ORROWS[@]}\"; do\n OR_IDS+=(\"${row%%$'\\t'*}\")\n printf \" %3d) %s\\n\" \"$i\" \"${row#*$'\\t'}\"; ((i++))\ndone\nN=${#OR_IDS[@]}\n\n# ---- OU Sandbox option (key-gated; the course's first-choice endpoint) -----\nLL_KEY=\"$(read_env LITELLM_API_KEY)\"\n[[ \"${LL_KEY}\" == \"sk-REPLACE_ME\" ]] && LL_KEY=\"\"\nLL_OPTION=0\nif [[ -n \"${LL_KEY}\" ]]; then\n LL_OPTION=$((N+1))\n printf \" %3d) %s\\n\" \"${LL_OPTION}\" \"OU AI Sandbox (first-choice endpoint, no per-token cost) → browse the LiteLLM catalog\"\nfi\necho\nread -rp \"Primary model number [default 1]: \" choice </dev/tty; choice=\"${choice:-1}\"\n\n# ---- OU Sandbox branch ----------------------------------------------------\nif [[ -n \"${LL_KEY}\" && \"${choice}\" == \"${LL_OPTION}\" ]]; then\n echo \"Fetching OU models from ${oubase} ...\"\n http=000\n for url in \"${oubase}/v1/models\" \"${oubase}/models\"; do\n http=\"$(curl -s -m 20 -o /tmp/ou_models.json -w '%{http_code}' -H \"Authorization: Bearer ${LL_KEY}\" \"${url}\" || echo 000)\"\n [[ \"${http}\" == \"200\" ]] && break\n done\n case \"${http}\" in\n 200) ;;\n 401|403) echo \"❌ OU Sandbox key rejected (HTTP ${http}).\"; exit 1 ;;\n 000) echo \"❌ Could not reach ${oubase} (network/endpoint issue). Check the URL or try again.\"; exit 1 ;;\n *) echo \"❌ OU gateway returned HTTP ${http}. Details in /tmp/ou_models.json\"; exit 1 ;;\n esac\n mapfile -t OU < <(python3 -c 'import json\nfor m in json.load(open(\"/tmp/ou_models.json\")).get(\"data\",[]): print(m[\"id\"])' 2>/dev/null | sort -u)\n ((${#OU[@]})) || { echo \"❌ No models parsed from the OU response (/tmp/ou_models.json may be malformed).\"; exit 1; }\n echo; echo \"OU AI Sandbox models (OU LiteLLM):\"\n for i in \"${!OU[@]}\"; do printf \" %3d) %s\\n\" \"$((i+1))\" \"${OU[$i]}\"; done\n echo\n read -rp \"Primary model number [default 1]: \" p </dev/tty; p=\"${p:-1}\"\n if ! [[ \"${p}\" =~ ^[0-9]+$ ]] || (( p < 1 || p > ${#OU[@]} )); then echo \"Invalid choice.\"; exit 1; fi\n PRIMARY=\"${OU[$((p-1))]}\"\n read -rp \"Fallback number(s) from the list above, comma-separated (blank = none): \" s </dev/tty\n FB=()\n if [[ -n \"${s// /}\" ]]; then\n IFS=',' read -ra IDX <<< \"${s}\"\n for n in \"${IDX[@]}\"; do n=\"${n// /}\"; m=\"${OU[$((n-1))]:-}\"; [[ -n \"${m}\" ]] && FB+=(\"${m}\"); done\n fi\n apply_models \"litellm/\" \"${PRIMARY}\" ${FB[@]+\"${FB[@]}\"}\n exit 0\nfi\n\n# ---- OpenRouter branch (default) ------------------------------------------\nif ! [[ \"${choice}\" =~ ^[0-9]+$ ]] || (( choice < 1 || choice > N )); then\n echo \"Invalid choice.\"; exit 1\nfi\nPRIMARY=\"${OR_IDS[$((choice-1))]}\"\nread -rp \"Fallback number(s), comma-separated (blank = none): \" s </dev/tty\nFB=()\nif [[ -n \"${s// /}\" ]]; then\n IFS=',' read -ra IDX <<< \"${s}\"\n for n in \"${IDX[@]}\"; do n=\"${n// /}\"; m=\"${OR_IDS[$((n-1))]:-}\"; [[ -n \"${m}\" ]] && FB+=(\"${m}\"); done\nfi\napply_models \"openrouter/\" \"${PRIMARY}\" ${FB[@]+\"${FB[@]}\"}\n"}, "1efe4f4d012ed7cc377316a00ef12e1e68efb9ae": {"t": 1785771082.6997263, "n": 7461, "h": "86592727af1a846a", "c": "#!/usr/bin/env bash\n# Switch the OpenClaw model. Lists the OU LiteLLM catalog and — when an\n# OpenRouter key is present — an OpenRouter option that browses tool-capable\n# models. Sets primary + optional fallback; the gateway hot-reloads (no restart).\nset -uo pipefail\n# Make 'openclaw' findable in non-interactive shells.\nsource \"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)/_env.sh\" 2>/dev/null || true\n\nLITELLM_BASE_URL=\"${LITELLM_BASE_URL:-https://litellm.lib.ou.edu}\"\nENV_FILE=\"${HOME}/.openclaw/.env\"\noubase=\"${LITELLM_BASE_URL%/}\"\n\nread_env() { # read_env VAR -> value from process env or ~/.openclaw/.env\n local var=\"$1\" val=\"${!1:-}\"\n [[ -z \"${val}\" && -f \"${ENV_FILE}\" ]] && val=\"$(grep -E \"^${var}=\" \"${ENV_FILE}\" | tail -n1 | cut -d= -f2- || true)\"\n printf '%s' \"${val}\"\n}\n\napply_models() { # apply_models <prefix> <primary> [fallbacks...]\n local prefix=\"$1\" primary=\"$2\"; shift 2\n echo \"→ primary: ${prefix}${primary}\"\n if ! openclaw models set \"${prefix}${primary}\"; then\n echo \"❌ Could not set primary '${prefix}${primary}'.\"\n echo \" Run 'openclaw models list' for valid refs, and check the gateway is running.\"\n exit 1\n fi\n openclaw models fallbacks clear >/dev/null 2>&1 || true\n local f\n for f in \"$@\"; do\n [[ -n \"${f}\" ]] || continue\n echo \"→ fallback: ${prefix}${f}\"\n openclaw models fallbacks add \"${prefix}${f}\" || echo \"⚠️ Couldn't add fallback '${prefix}${f}' — skipped.\"\n done\n echo; echo \"Current model configuration:\"\n openclaw models status 2>/dev/null || echo \"⚠️ 'openclaw models status' unavailable (is the gateway running?).\"\n echo \"(Hot-reloaded for new sessions. For the chat you're in now, switch with /model.)\"\n}\n\n# ---- prerequisites --------------------------------------------------------\nneed() { command -v \"$1\" >/dev/null 2>&1 || { echo \"❌ Required tool '$1' not found — $2\"; exit 1; }; }\nneed curl \"rebuild the Codespace or install curl.\"\nneed python3 \"rebuild the Codespace or install python3.\"\nneed openclaw \"OpenClaw isn't on PATH yet — start the Gateway task first, or run: bash .devcontainer/setup.sh\"\n\n# ---- OU LiteLLM catalog ---------------------------------------------------\nOU_KEY=\"$(read_env LITELLM_API_KEY)\"\n[[ -z \"${OU_KEY}\" || \"${OU_KEY}\" == \"sk-REPLACE_ME\" ]] && { echo \"No OU LiteLLM key. Run: bash scripts/set-key.sh\"; exit 1; }\n\necho \"Fetching OU models from ${oubase} ...\"\nhttp=000\nfor url in \"${oubase}/v1/models\" \"${oubase}/models\"; do\n http=\"$(curl -s -m 20 -o /tmp/ou_models.json -w '%{http_code}' -H \"Authorization: Bearer ${OU_KEY}\" \"${url}\" || echo 000)\"\n [[ \"${http}\" == \"200\" ]] && break\ndone\ncase \"${http}\" in\n 200) ;;\n 401|403) echo \"❌ OU key rejected (HTTP ${http}). Fix it with: bash scripts/set-key.sh\"; exit 1 ;;\n 000) echo \"❌ Could not reach ${oubase} (network/endpoint issue). Check the URL or try again.\"; exit 1 ;;\n *) echo \"❌ OU gateway returned HTTP ${http}. Details in /tmp/ou_models.json\"; exit 1 ;;\nesac\nmapfile -t OU < <(python3 -c 'import json\nfor m in json.load(open(\"/tmp/ou_models.json\")).get(\"data\",[]): print(m[\"id\"])' 2>/dev/null | sort -u)\n((${#OU[@]})) || { echo \"❌ No models parsed from the OU response (/tmp/ou_models.json may be malformed).\"; exit 1; }\nN=${#OU[@]}\n\n# ---- list OU models + OpenRouter option (key-gated) -----------------------\nOR_KEY=\"$(read_env OPENROUTER_API_KEY)\"\nOR_OPTION=0\necho\necho \"Available models (OU LiteLLM):\"\nfor i in \"${!OU[@]}\"; do printf \" %2d) %s\\n\" \"$((i+1))\" \"${OU[$i]}\"; done\nif [[ -n \"${OR_KEY}\" ]]; then\n OR_OPTION=$((N+1))\n printf \" %2d) %s\\n\" \"${OR_OPTION}\" \"OpenRouter → browse tool-capable models\"\nelse\n printf \" %s\\n\" \"OpenRouter (no key present — add the OPENROUTER_API_KEY Codespaces secret)\"\nfi\necho\nread -rp \"Pick a number [default 1]: \" choice </dev/tty; choice=\"${choice:-1}\"\n\n# ---- OpenRouter branch ----------------------------------------------------\nif [[ -n \"${OR_KEY}\" && \"${choice}\" == \"${OR_OPTION}\" ]]; then\n # Validate the key first — listing is public, but selecting a model is moot if the key is bad.\n kc=\"$(curl -s -m 15 -o /dev/null -w '%{http_code}' -H \"Authorization: Bearer ${OR_KEY}\" https://openrouter.ai/api/v1/key || echo 000)\"\n if [[ \"${kc}\" != \"200\" ]]; then\n echo \"⚠️ OpenRouter key check returned HTTP ${kc} — it may be invalid, expired, or out of credit.\"\n echo \" You can still browse, but the model will fail at runtime until the key works.\"\n read -rp \"Continue anyway? [y/N] \" yn </dev/tty || yn=\"\"\n [[ \"${yn}\" =~ ^[Yy]$ ]] || { echo \"Aborted.\"; exit 1; }\n fi\n echo \"Fetching tool-capable OpenRouter models ...\"\n if ! curl -fsS -m 30 \"https://openrouter.ai/api/v1/models?supported_parameters=tools\" -o /tmp/or_models.json; then\n echo \"❌ Could not reach OpenRouter (network/endpoint). Try again in a moment.\"; exit 1\n fi\n mapfile -t ORROWS < <(python3 - <<'PY'\nimport json\ndata = json.load(open(\"/tmp/or_models.json\")).get(\"data\", [])\npopular = {\"anthropic\",\"openai\",\"google\",\"x-ai\",\"meta-llama\",\"mistralai\",\n \"qwen\",\"deepseek\",\"z-ai\",\"moonshotai\",\"minimax\"}\ndef perM(v):\n try: return float(v) * 1_000_000\n except Exception: return None\nrows = []\nfor m in data:\n mid = m.get(\"id\", \"\")\n vendor = mid.split(\"/\")[0] if \"/\" in mid else mid\n arch = m.get(\"architecture\", {}) or {}\n if \"text\" not in (arch.get(\"input_modalities\") or []): continue\n if \"tools\" not in (m.get(\"supported_parameters\") or []): continue\n if vendor not in popular: continue\n pr = m.get(\"pricing\", {}) or {}\n pin, pout = perM(pr.get(\"prompt\")), perM(pr.get(\"completion\"))\n free = (pin == 0 and pout == 0)\n ctx = m.get(\"context_length\") or 0\n ctxs = f\"{ctx // 1000}k\" if ctx else \"?\"\n if free: price = \"FREE\"\n elif pin is not None and pout is not None: price = f\"${pin:.2f}/${pout:.2f} /M\"\n else: price = \"price n/a\"\n rows.append((0 if free else 1, pin if pin is not None else 9e9, vendor, mid,\n f\"{price:<16} {mid} ({ctxs})\"))\nrows.sort(key=lambda r: (r[0], r[1], r[2], r[3]))\nfor r in rows: print(f\"{r[3]}\\t{r[4]}\")\nPY\n)\n ((${#ORROWS[@]})) || { echo \"❌ No tool-capable models from popular vendors returned (OpenRouter's catalog may have shifted).\"; exit 1; }\n OR_IDS=(); i=1\n echo; echo \"OpenRouter models (tool-capable; free first, then by price):\"\n for row in \"${ORROWS[@]}\"; do\n OR_IDS+=(\"${row%%$'\\t'*}\")\n printf \" %3d) %s\\n\" \"$i\" \"${row#*$'\\t'}\"; ((i++))\n done\n echo\n read -rp \"Primary model number [default 1]: \" p </dev/tty; p=\"${p:-1}\"\n PRIMARY=\"${OR_IDS[$((p-1))]:-}\"; [[ -z \"${PRIMARY}\" ]] && { echo \"Invalid choice.\"; exit 1; }\n read -rp \"Fallback number(s), comma-separated (blank = none): \" s </dev/tty\n FB=()\n if [[ -n \"${s// /}\" ]]; then\n IFS=',' read -ra IDX <<< \"${s}\"\n for n in \"${IDX[@]}\"; do n=\"${n// /}\"; m=\"${OR_IDS[$((n-1))]:-}\"; [[ -n \"${m}\" ]] && FB+=(\"${m}\"); done\n fi\n apply_models \"openrouter/\" \"${PRIMARY}\" ${FB[@]+\"${FB[@]}\"}\n exit 0\nfi\n\n# ---- OU LiteLLM branch ----------------------------------------------------\nif ! [[ \"${choice}\" =~ ^[0-9]+$ ]] || (( choice < 1 || choice > N )); then\n echo \"Invalid choice.\"; exit 1\nfi\nPRIMARY=\"${OU[$((choice-1))]}\"\nread -rp \"Fallback number(s) from the list above, comma-separated (blank = none): \" s </dev/tty\nFB=()\nif [[ -n \"${s// /}\" ]]; then\n IFS=',' read -ra IDX <<< \"${s}\"\n for n in \"${IDX[@]}\"; do n=\"${n// /}\"; m=\"${OU[$((n-1))]:-}\"; [[ -n \"${m}\" ]] && FB+=(\"${m}\"); done\nfi\napply_models \"litellm/\" \"${PRIMARY}\" ${FB[@]+\"${FB[@]}\"}\n"}}}}