From 8c2910b48082f43ff5c076f5b45698f0bcee39a7 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sun, 16 Aug 2026 13:34:46 -0700 Subject: [PATCH] feat: separate gateway model identity --- lib/ai-gateway.sh | 17 ++++++++++++----- lib/summary.sh | 1 + setup.sh | 26 +++++++++++++++++++++----- tests/ai-gateway.sh | 33 +++++++++++++++++++++++++++++++++ upgrade.sh | 8 +++++++- 5 files changed, 74 insertions(+), 11 deletions(-) diff --git a/lib/ai-gateway.sh b/lib/ai-gateway.sh index 138777e..1d89ff4 100644 --- a/lib/ai-gateway.sh +++ b/lib/ai-gateway.sh @@ -1,8 +1,9 @@ #!/bin/bash # Optional WP AI Gateway integration for OpenCode runtimes. -AI_GATEWAY_PROVIDER_ID="wp-ai-gateway" +AI_GATEWAY_PROVIDER_ID="${AI_GATEWAY_PROVIDER_ID:-wp-ai-gateway}" AI_GATEWAY_MODEL_ID="${AI_GATEWAY_MODEL_ID:-site-default}" +AI_GATEWAY_API_MODEL_ID="${AI_GATEWAY_API_MODEL_ID:-}" AI_GATEWAY_ROUTE_PROVIDER="${AI_GATEWAY_ROUTE_PROVIDER:-openai}" AI_GATEWAY_ROUTE_MODEL="${AI_GATEWAY_ROUTE_MODEL:-gpt-4o-mini}" AI_GATEWAY_ENV_FILE="${AI_GATEWAY_ENV_FILE:-}" @@ -59,6 +60,10 @@ ai_gateway_base_url() { printf '%s/wp-json/wp-ai-gateway/v1' "${site_url%/}" } +ai_gateway_api_model_id() { + printf '%s' "${AI_GATEWAY_API_MODEL_ID:-$AI_GATEWAY_MODEL_ID}" +} + ai_gateway_env_file() { if [ -n "$AI_GATEWAY_ENV_FILE" ]; then printf '%s' "$AI_GATEWAY_ENV_FILE" @@ -147,14 +152,15 @@ ai_gateway_write_env() { ai_gateway_configure_opencode() { ai_gateway_enabled_for_opencode || return 0 - local config_file env_file base_url + local config_file env_file base_url api_model_id config_file="$SITE_PATH/opencode.json" env_file="$(ai_gateway_env_file)" base_url="$(ai_gateway_base_url)" + api_model_id="$(ai_gateway_api_model_id)" if [ "$DRY_RUN" = true ]; then echo -e "${BLUE}[dry-run]${NC} Would merge WP AI Gateway provider into $config_file" - echo -e "${BLUE}[dry-run]${NC} Provider: $AI_GATEWAY_PROVIDER_ID/$AI_GATEWAY_MODEL_ID via OPENAI_BASE_URL=$base_url and OPENAI_API_KEY=" + echo -e "${BLUE}[dry-run]${NC} Provider: $AI_GATEWAY_PROVIDER_ID/$AI_GATEWAY_MODEL_ID (API model: $api_model_id) via OPENAI_BASE_URL=$base_url and OPENAI_API_KEY=" return 0 fi @@ -163,7 +169,7 @@ ai_gateway_configure_opencode() { return 0 fi - python3 - "$config_file" "$AI_GATEWAY_PROVIDER_ID" "$AI_GATEWAY_MODEL_ID" <<'PY' + python3 - "$config_file" "$AI_GATEWAY_PROVIDER_ID" "$AI_GATEWAY_MODEL_ID" "$api_model_id" <<'PY' import json import sys from pathlib import Path @@ -171,6 +177,7 @@ from pathlib import Path path = Path(sys.argv[1]) provider_id = sys.argv[2] model_id = sys.argv[3] +api_model_id = sys.argv[4] data = json.loads(path.read_text(encoding="utf-8")) provider = data.setdefault("provider", {}).setdefault(provider_id, {}) @@ -183,7 +190,7 @@ provider["options"].setdefault("name", "wp-ai-gateway") models = provider.setdefault("models", {}) model = models.setdefault(model_id, {}) model.setdefault("name", "WP AI Gateway site default") -model.setdefault("id", model_id) +model["id"] = api_model_id model.setdefault("tool_call", True) model.setdefault("temperature", True) model.setdefault("limit", {"context": 128000, "output": 8192}) diff --git a/lib/summary.sh b/lib/summary.sh index 602196d..95ec900 100644 --- a/lib/summary.sh +++ b/lib/summary.sh @@ -29,6 +29,7 @@ print_summary() { echo " Base URL: $(ai_gateway_base_url)" echo " Env file: $(ai_gateway_env_file)" echo " Model: ${AI_GATEWAY_PROVIDER_ID}/${AI_GATEWAY_MODEL_ID}" + echo " API model: $(ai_gateway_api_model_id)" echo " Route: ${AI_GATEWAY_ROUTE_PROVIDER} / ${AI_GATEWAY_ROUTE_MODEL}" echo "" fi diff --git a/setup.sh b/setup.sh index 2bfc1a1..719c08a 100755 --- a/setup.sh +++ b/setup.sh @@ -199,10 +199,18 @@ while [[ $# -gt 0 ]]; do AI_GATEWAY_ROUTE_MODEL="$2" shift 2 ;; + --ai-gateway-opencode-provider) + AI_GATEWAY_PROVIDER_ID="$2" + shift 2 + ;; --ai-gateway-opencode-model) AI_GATEWAY_MODEL_ID="$2" shift 2 ;; + --ai-gateway-api-model) + AI_GATEWAY_API_MODEL_ID="$2" + shift 2 + ;; --rotate-ai-gateway-token) ROTATE_AI_GATEWAY_TOKEN=true shift @@ -363,11 +371,17 @@ OPTIONS: Backend WordPress AI Client provider for site-default routing (default: openai) --ai-gateway-model - Backend provider model for site-default routing - (default: gpt-4o-mini) + Backend provider model for site-default routing + (default: gpt-4o-mini) + --ai-gateway-opencode-provider + Provider identity shown by OpenCode + (default: wp-ai-gateway) --ai-gateway-opencode-model - OpenCode model ID exposed by the gateway provider - (default: site-default) + Model identity shown by OpenCode + (default: site-default) + --ai-gateway-api-model + Model ID sent to WP AI Gateway (default: the OpenCode + model identity) --rotate-ai-gateway-token Mint a new gateway token instead of reusing the existing .opencode/wp-ai-gateway.env value @@ -401,7 +415,9 @@ ENVIRONMENT VARIABLES: OPENCODE_SMALL_MODEL Override small model (e.g., anthropic/claude-haiku-4-5) AI_GATEWAY_ROUTE_PROVIDER Backend provider used by --with-ai-gateway AI_GATEWAY_ROUTE_MODEL Backend model used by --with-ai-gateway - AI_GATEWAY_MODEL_ID OpenCode gateway model id (default: site-default) + AI_GATEWAY_PROVIDER_ID Provider identity shown by OpenCode + AI_GATEWAY_MODEL_ID Model identity shown by OpenCode + AI_GATEWAY_API_MODEL_ID Model ID sent to WP AI Gateway AI_GATEWAY_SITE_URL Public site URL for OPENAI_BASE_URL override WITH_CLAUDE_CODE_AUTH false to skip direct OpenCode Claude Pro/Max auth KIMAKI_BOT_TOKEN Discord bot token (skip interactive setup) diff --git a/tests/ai-gateway.sh b/tests/ai-gateway.sh index 0e4dc3f..2c2f8b7 100755 --- a/tests/ai-gateway.sh +++ b/tests/ai-gateway.sh @@ -148,10 +148,43 @@ assert provider["npm"] == "@ai-sdk/openai-compatible" assert provider["env"] == ["OPENAI_API_KEY"] assert provider["options"]["baseURL"] == "{env:OPENAI_BASE_URL}" assert "site-default" in provider["models"] +assert provider["models"]["site-default"]["id"] == "site-default" assert data["model"] == "anthropic/claude-sonnet-4-5" assert "custom" in data["provider"] PY +echo "==> visible gateway identity is separate from the API model" +AI_GATEWAY_PROVIDER_ID="openai" +AI_GATEWAY_MODEL_ID="gpt-5.6-sol" +AI_GATEWAY_API_MODEL_ID="site-default" +cat > "$SITE_PATH/opencode.json" <<'JSON' +{ + "$schema": "https://opencode.ai/config.json", + "provider": { + "openai": { + "models": { + "gpt-5.6-sol": { + "id": "stale-model" + } + } + } + } +} +JSON + +ai_gateway_configure_opencode +python3 - "$SITE_PATH/opencode.json" <<'PY' +import json +import sys + +with open(sys.argv[1], encoding="utf-8") as handle: + data = json.load(handle) + +model = data["provider"]["openai"]["models"]["gpt-5.6-sol"] +assert data["model"] == "openai/gpt-5.6-sol" +assert model["id"] == "site-default" +PY + echo if [ "$FAIL" -gt 0 ]; then echo "FAILED: $FAIL of $((PASS+FAIL)) assertion(s)" diff --git a/upgrade.sh b/upgrade.sh index cccfa40..6a35b06 100755 --- a/upgrade.sh +++ b/upgrade.sh @@ -154,7 +154,9 @@ while [[ $# -gt 0 ]]; do --no-claude-code-auth) WITH_CLAUDE_CODE_AUTH=false; shift ;; --ai-gateway-provider) AI_GATEWAY_ROUTE_PROVIDER="$2"; shift 2 ;; --ai-gateway-model) AI_GATEWAY_ROUTE_MODEL="$2"; shift 2 ;; + --ai-gateway-opencode-provider) AI_GATEWAY_PROVIDER_ID="$2"; shift 2 ;; --ai-gateway-opencode-model) AI_GATEWAY_MODEL_ID="$2"; shift 2 ;; + --ai-gateway-api-model) AI_GATEWAY_API_MODEL_ID="$2"; shift 2 ;; --rotate-ai-gateway-token) ROTATE_AI_GATEWAY_TOKEN=true; shift ;; --source-mode|--posture) SOURCE_MODE="$2"; SOURCE_MODE_EXPLICIT=true; shift 2 ;; --not-owned) owned_discovery_add_exclusion "$2"; shift 2 ;; @@ -238,7 +240,10 @@ USAGE: ./upgrade.sh --with-ai-gateway --rotate-ai-gateway-token Explicitly mint a replacement gateway token. ./upgrade.sh --with-ai-gateway --ai-gateway-provider openai --ai-gateway-model gpt-4o-mini - Configure the WordPress gateway backend route. + Configure the WordPress gateway backend route. + ./upgrade.sh --with-ai-gateway --ai-gateway-opencode-provider openai --ai-gateway-opencode-model gpt-5.6-sol --ai-gateway-api-model site-default + Show the resolved provider/model identity in + OpenCode while routing through site-default. ./upgrade.sh --no-claude-code-auth Skip direct OpenCode Claude Pro/Max auth. The managed auth plugin is installed by default @@ -1322,6 +1327,7 @@ print_summary() { log " Base URL: $(ai_gateway_base_url)" log " Env file: $(ai_gateway_env_file)" log " Model: ${AI_GATEWAY_PROVIDER_ID}/${AI_GATEWAY_MODEL_ID}" + log " API model: $(ai_gateway_api_model_id)" log " Route: ${AI_GATEWAY_ROUTE_PROVIDER} / ${AI_GATEWAY_ROUTE_MODEL}" fi