Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 52 additions & 15 deletions contrib/ccyproxy/ccyproxy.zsh
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ _ccyproxy_unset_provider_env() {
# [--list-models|--list-routes|--list-all|--show-config] [claude arguments...]
function ccyproxy {
local proxy_config="${CCYPROXY_CONFIG:-${HOME}/Library/Application Support/AIUsage/CLIProxyAPI/config.yaml}"
local proxy_host proxy_port proxy_key proxy_base proxy_models proxy_catalog
local proxy_host proxy_port proxy_key proxy_base proxy_models proxy_catalog proxy_raw_ids
local proxy_routes proxy_route_config proxy_model proxy_requested_model proxy_haiku_model
local proxy_context_window proxy_max_output_tokens proxy_translation_margin proxy_provider_compact_limit
local proxy_output_allowance proxy_summary_reserve proxy_compact_trigger proxy_auto_compact_window
Expand All @@ -35,6 +35,30 @@ function ccyproxy {
return 1
fi

local council_command
if (( $+commands[reason-assembly] )); then
council_command=reason-assembly
elif (( $+commands[ccycouncil] )); then
council_command=ccycouncil
if [[ -z "${_REASON_ASSEMBLY_LEGACY_NOTICE:-}" ]]; then
print -u2 -- "ccyproxy: warning: using deprecated ccycouncil fallback; install reason-assembly"
typeset -g _REASON_ASSEMBLY_LEGACY_NOTICE=1
fi
else
print -u2 -- "ccyproxy: warning: reason-assembly is unavailable; continuing with live raw-model filtering"
council_command=
fi

if [[ "$council_command" == ccycouncil ]]; then
if ! REASON_ASSEMBLY_SUPPRESS_DEPRECATION=1 CCYPROXY_CONFIG="$proxy_config" command ccycouncil sync --json >/dev/null; then
print -u2 -- "ccyproxy: warning: catalogue synchronization failed; continuing with live raw-model filtering"
fi
elif [[ "$council_command" == reason-assembly ]]; then
if ! CCYPROXY_CONFIG="$proxy_config" command reason-assembly sync --json >/dev/null; then
print -u2 -- "ccyproxy: warning: catalogue synchronization failed; continuing with live raw-model filtering"
fi
fi

local -a proxy_connection
proxy_connection=("${(@f)$(ruby -ryaml -e '
c = YAML.safe_load(File.read(ARGV.fetch(0)), aliases: true)
Expand Down Expand Up @@ -72,14 +96,20 @@ function ccyproxy {
echo "ccyproxy: CLIProxyAPI model metadata is unavailable at $proxy_base" >&2
return 1
}
proxy_raw_ids="$(printf '%s' "$proxy_models" | ruby -rjson -e '
puts JSON.generate(JSON.parse(STDIN.read).fetch("data", []).map { |m| m["id"] }.compact.uniq.sort)
' 2>/dev/null)" || proxy_raw_ids='[]'

case "${1:-}" in
--list-models)
printf '%s' "$proxy_catalog" | ruby -rjson -e '
printf '%s' "$proxy_catalog" | PROXY_RAW_IDS="$proxy_raw_ids" ruby -rjson -e '
raw_ids = JSON.parse(ENV.fetch("PROXY_RAW_IDS"))
models = JSON.parse(STDIN.read).fetch("models", [])
models.sort_by { |m| m["slug"].to_s }.each do |m|
by_slug = models.each_with_object({}) { |m, out| out[m["slug"]] = m }
raw_ids.each do |slug|
m = by_slug[slug] || {"slug" => slug}
availability = m["credential_availability"] || {}
status = availability["status"] || "unknown"
status = by_slug[slug] ? (availability["status"] || "unknown") : "listed-only"
eligible = availability.key?("eligible_credentials") ? "#{availability["eligible_credentials"]}/#{availability["total_credentials"]}" : "-"
puts "%-48s %10s %-11s %s" % [m["slug"], m["context_window"] || m["max_context_window"] || "unknown", status, eligible]
end
Expand All @@ -98,11 +128,14 @@ function ccyproxy {
puts JSON.parse(ENV.fetch("PROXY_ROUTES")).map { |id| " #{id}" }
'
printf 'Models:\n'
printf '%s' "$proxy_catalog" | ruby -rjson -e '
printf '%s' "$proxy_catalog" | PROXY_RAW_IDS="$proxy_raw_ids" ruby -rjson -e '
raw_ids = JSON.parse(ENV.fetch("PROXY_RAW_IDS"))
models = JSON.parse(STDIN.read).fetch("models", [])
models.sort_by { |m| m["slug"].to_s }.each do |m|
by_slug = models.each_with_object({}) { |m, out| out[m["slug"]] = m }
raw_ids.each do |slug|
m = by_slug[slug] || {"slug" => slug}
availability = m["credential_availability"] || {}
status = availability["status"] || "unknown"
status = by_slug[slug] ? (availability["status"] || "unknown") : "listed-only"
eligible = availability.key?("eligible_credentials") ? "#{availability["eligible_credentials"]}/#{availability["total_credentials"]}" : "-"
puts " %-46s %10s %-11s %s" % [m["slug"], m["context_window"] || m["max_context_window"] || "unknown", status, eligible]
end
Expand Down Expand Up @@ -153,8 +186,9 @@ function ccyproxy {

proxy_requested_model="$proxy_model"
proxy_model="$(printf '%s' "$proxy_catalog" |
PROXY_MODEL="$proxy_model" PROXY_ROUTE_CONFIG="$proxy_route_config" ruby -rjson -e '
models = JSON.parse(STDIN.read).fetch("models", [])
PROXY_MODEL="$proxy_model" PROXY_ROUTE_CONFIG="$proxy_route_config" PROXY_RAW_IDS="$proxy_raw_ids" ruby -rjson -e '
raw_ids = JSON.parse(ENV.fetch("PROXY_RAW_IDS"))
models = JSON.parse(STDIN.read).fetch("models", []).select { |m| raw_ids.include?(m["slug"]) }
by_slug = models.each_with_object({}) { |m, out| out[m["slug"]] = m }
selected = ENV.fetch("PROXY_MODEL")
if by_slug[selected]
Expand All @@ -176,8 +210,9 @@ function ccyproxy {
fi

if [[ -z "$proxy_haiku_model" ]]; then
proxy_haiku_model="$(printf '%s' "$proxy_catalog" | PROXY_MAIN_MODEL="$proxy_model" ruby -rjson -e '
models = JSON.parse(STDIN.read).fetch("models", [])
proxy_haiku_model="$(printf '%s' "$proxy_catalog" | PROXY_MAIN_MODEL="$proxy_model" PROXY_RAW_IDS="$proxy_raw_ids" ruby -rjson -e '
raw_ids = JSON.parse(ENV.fetch("PROXY_RAW_IDS"))
models = JSON.parse(STDIN.read).fetch("models", []).select { |m| raw_ids.include?(m["slug"]) }
by_slug = models.each_with_object({}) { |m, out| out[m["slug"]] = m }
preferred = %w[gpt-5.6-luna gpt-5.4-mini gemini-3.1-flash-lite gemini-3-flash]
selected = preferred.find do |slug|
Expand All @@ -203,8 +238,9 @@ function ccyproxy {
fi

proxy_availability_error="$(printf '%s' "$proxy_catalog" |
PROXY_MODELS="${proxy_model},${proxy_haiku_model}" PROXY_ROUTE_CONFIG="$proxy_route_config" ruby -rjson -rtime -e '
catalog = JSON.parse(STDIN.read).fetch("models", [])
PROXY_MODELS="${proxy_model},${proxy_haiku_model}" PROXY_ROUTE_CONFIG="$proxy_route_config" PROXY_RAW_IDS="$proxy_raw_ids" ruby -rjson -rtime -e '
raw_ids = JSON.parse(ENV.fetch("PROXY_RAW_IDS"))
catalog = JSON.parse(STDIN.read).fetch("models", []).select { |m| raw_ids.include?(m["slug"]) }
by_slug = catalog.each_with_object({}) { |m, out| out[m["slug"]] = m }
routes = JSON.parse(ENV.fetch("PROXY_ROUTE_CONFIG"))
resolve_all = lambda do |selected, seen = []|
Expand Down Expand Up @@ -263,8 +299,9 @@ function ccyproxy {

local -a proxy_capacity
proxy_capacity=("${(@f)$(printf '%s' "$proxy_catalog" |
PROXY_MODEL="$proxy_model" ruby -rjson -e '
model = JSON.parse(STDIN.read).fetch("models", []).find { |m| m["slug"] == ENV.fetch("PROXY_MODEL") }
PROXY_MODEL="$proxy_model" PROXY_RAW_IDS="$proxy_raw_ids" ruby -rjson -e '
raw_ids = JSON.parse(ENV.fetch("PROXY_RAW_IDS"))
model = JSON.parse(STDIN.read).fetch("models", []).find { |m| m["slug"] == ENV.fetch("PROXY_MODEL") && raw_ids.include?(m["slug"]) }
exit 1 unless model
puts model["context_window"].to_i
puts model["max_output_tokens"].to_i
Expand Down
57 changes: 56 additions & 1 deletion contrib/ccyproxy/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,25 @@ esac
EOF
chmod +x "$workdir/bin/curl"

cat >"$workdir/bin/reason-assembly" <<'EOF'
#!/bin/sh
set -eu
if [ -n "${MOCK_SYNC_LOG:-}" ]; then
printf 'reason-assembly %s\n' "$*" >>"$MOCK_SYNC_LOG"
fi
EOF
cat >"$workdir/bin/ccycouncil" <<'EOF'
#!/bin/sh
set -eu
if [ -n "${MOCK_SYNC_LOG:-}" ]; then
printf 'ccycouncil %s suppress=%s\n' "$*" "${REASON_ASSEMBLY_SUPPRESS_DEPRECATION:-}" >>"$MOCK_SYNC_LOG"
fi
if [ "${REASON_ASSEMBLY_SUPPRESS_DEPRECATION:-}" != 1 ]; then
printf '%s\n' 'deprecated: use reason-assembly' >&2
fi
EOF
chmod +x "$workdir/bin/reason-assembly" "$workdir/bin/ccycouncil"

cat >"$workdir/config.yaml" <<'EOF'
host: 127.0.0.1
port: 8317
Expand All @@ -86,10 +105,11 @@ run_launcher() {
models=$1
catalog=$2
shift 2
PATH="$workdir/bin:$PATH" \
PATH="$workdir/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin" \
CCYPROXY_CONFIG="$workdir/config.yaml" \
MOCK_MODELS="$models" \
MOCK_CATALOG="$catalog" \
MOCK_SYNC_LOG="${MOCK_SYNC_LOG:-}" \
zsh -c 'source "$1"; shift; ccyproxy "$@"' -- "$launcher" "$@"
}

Expand All @@ -98,6 +118,18 @@ printf '%s\n' "$complete_output" | grep -q '^main_model=gpt-5.6-sol$'
printf '%s\n' "$complete_output" | grep -q '^compact_trigger=287000$'
printf '%s\n' "$complete_output" | grep -q '^auto_compact_window=320000$'

cat >"$workdir/catalog-with-phantom.json" <<'EOF'
{"models":[{"slug":"gpt-5.6-sol","context_window":372000,"max_output_tokens":128000,"translation_margin_tokens":52000,"auto_compact_token_limit":287000,"capacity_complete":true,"capacity_source":"test","capacity_blockers":[],"credential_availability":{"status":"available","availability_complete":true,"total_credentials":1,"eligible_credentials":1,"cooling_credentials":0,"blocked_credentials":0,"availability_blockers":[]}},{"slug":"gpt-5.6-luna","context_window":200000,"max_output_tokens":64000,"translation_margin_tokens":13000,"auto_compact_token_limit":160000,"capacity_complete":true,"capacity_source":"test","capacity_blockers":[],"credential_availability":{"status":"available","availability_complete":true,"total_credentials":1,"eligible_credentials":1,"cooling_credentials":0,"blocked_credentials":0,"availability_blockers":[]}}]}
EOF
phantom_output=$(run_launcher "$workdir/models-direct.json" "$workdir/catalog-with-phantom.json" --show-config)
printf '%s\n' "$phantom_output" | grep -q '^haiku_model=gpt-5.6-sol$'
listed_models=$(run_launcher "$workdir/models-direct.json" "$workdir/catalog-with-phantom.json" --list-models)
printf '%s\n' "$listed_models" | grep -q '^gpt-5.6-sol '
if printf '%s\n' "$listed_models" | grep -q '^gpt-5.6-luna '; then
printf '%s\n' 'launcher test: metadata-only model leaked through raw catalogue filter' >&2
exit 1
fi

cat >"$workdir/catalog-invalid-margin.json" <<'EOF'
{"models":[{"slug":"gpt-5.6-sol","context_window":372000,"max_output_tokens":128000,"capacity_complete":true,"capacity_source":"test","capacity_blockers":[],"credential_availability":{"status":"available","availability_complete":true,"total_credentials":1,"eligible_credentials":1,"cooling_credentials":0,"blocked_credentials":0,"availability_blockers":[]}}]}
EOF
Expand Down Expand Up @@ -165,3 +197,26 @@ if run_launcher "$workdir/models-route.json" "$workdir/catalog-route-incomplete.
exit 1
fi
grep -q "route 'worker' has no currently eligible candidate" "$workdir/route-incomplete.err"

: >"$workdir/sync.log"
MOCK_SYNC_LOG="$workdir/sync.log" run_launcher "$workdir/models-direct.json" "$workdir/catalog-complete.json" --show-config >"$workdir/canonical-sync.out" 2>"$workdir/canonical-sync.err"
grep -q '^reason-assembly sync --json$' "$workdir/sync.log"
if grep -q '^ccycouncil ' "$workdir/sync.log"; then
printf '%s\n' 'launcher test: deprecated command ran despite canonical command availability' >&2
exit 1
fi

rm "$workdir/bin/reason-assembly"
: >"$workdir/sync.log"
MOCK_SYNC_LOG="$workdir/sync.log" run_launcher "$workdir/models-direct.json" "$workdir/catalog-complete.json" --show-config >"$workdir/compat-sync.out" 2>"$workdir/compat-sync.err"
grep -q '^ccycouncil sync --json suppress=1$' "$workdir/sync.log"
grep -q 'using deprecated ccycouncil fallback' "$workdir/compat-sync.err"
if grep -q 'deprecated: use reason-assembly' "$workdir/compat-sync.err"; then
printf '%s\n' 'launcher test: compatibility wrapper warning was not suppressed' >&2
exit 1
fi

rm "$workdir/bin/ccycouncil"
run_launcher "$workdir/models-direct.json" "$workdir/catalog-complete.json" --show-config >"$workdir/no-sync.out" 2>"$workdir/no-sync.err"
grep -q '^main_model=gpt-5.6-sol$' "$workdir/no-sync.out"
grep -q 'reason-assembly is unavailable; continuing with live raw-model filtering' "$workdir/no-sync.err"
Loading