diff --git a/bin/worker-quota-probe.sh b/bin/worker-quota-probe.sh new file mode 100755 index 0000000..908f33f --- /dev/null +++ b/bin/worker-quota-probe.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash +# worker-quota-probe.sh — pre-flight Anthropic rate-limit-quota meting. +# +# Doet een lichtgewicht /v1/messages-call (1 token max) en parsed de +# `anthropic-ratelimit-*-tokens`-headers. Output is JSON op stdout. +# +# Output (success): +# {"remaining": 9982000, "limit": 10000000, "pct": 99, "reset_at_iso": "..."} +# +# Output (fail): +# {"error": "...", "http_status": NNN} +# +# Exit-codes: 0 success, 1 op fout. De worker-loop gebruikt de pct + reset +# om te beslissen of-ie wait_for_job mag aanroepen (gate via min_quota_pct +# uit get_worker_settings). +# +# Gebruikt env-var ANTHROPIC_API_KEY of CLAUDE_CODE_OAUTH_TOKEN. OAuth-tokens +# worden door de Anthropic API herkend als Bearer; de header-set is dezelfde. + +set -uo pipefail + +KEY="${ANTHROPIC_API_KEY:-${CLAUDE_CODE_OAUTH_TOKEN:-}}" +if [[ -z "$KEY" ]]; then + echo '{"error":"no ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN set"}' + exit 1 +fi + +HDR_FILE=$(mktemp /tmp/quota-probe.XXXXXX.headers) +trap 'rm -f "$HDR_FILE"' EXIT + +# Minimale call: claude-haiku-4-5 met 1 max_tokens en een single-char input. +# Kost ~1 outputtoken; doel is alleen de rate-limit-headers binnen te halen. +HTTP_STATUS=$(curl -sS -o /dev/null \ + -D "$HDR_FILE" \ + -w '%{http_code}' \ + -H "Authorization: Bearer ${KEY}" \ + -H "anthropic-version: 2023-06-01" \ + -H "content-type: application/json" \ + -d '{"model":"claude-haiku-4-5","max_tokens":1,"messages":[{"role":"user","content":"."}]}' \ + https://api.anthropic.com/v1/messages 2>/dev/null || echo "000") + +# Parse rate-limit headers (case-insensitive grep). +REMAINING=$(grep -i '^anthropic-ratelimit-output-tokens-remaining:' "$HDR_FILE" 2>/dev/null | awk '{print $2}' | tr -d '\r') +LIMIT=$(grep -i '^anthropic-ratelimit-output-tokens-limit:' "$HDR_FILE" 2>/dev/null | awk '{print $2}' | tr -d '\r') +RESET=$(grep -i '^anthropic-ratelimit-output-tokens-reset:' "$HDR_FILE" 2>/dev/null | awk '{print $2}' | tr -d '\r') + +# Fallback: sommige plans gebruiken `requests` ipv `tokens` voor de hoofdgrens. +if [[ -z "$REMAINING" ]]; then + REMAINING=$(grep -i '^anthropic-ratelimit-requests-remaining:' "$HDR_FILE" 2>/dev/null | awk '{print $2}' | tr -d '\r') + LIMIT=$(grep -i '^anthropic-ratelimit-requests-limit:' "$HDR_FILE" 2>/dev/null | awk '{print $2}' | tr -d '\r') + RESET=$(grep -i '^anthropic-ratelimit-requests-reset:' "$HDR_FILE" 2>/dev/null | awk '{print $2}' | tr -d '\r') +fi + +if [[ -z "$REMAINING" || -z "$LIMIT" ]]; then + printf '{"error":"no rate-limit headers in response","http_status":%s}\n' "$HTTP_STATUS" + exit 1 +fi + +# Pct als integer (rounded). Bij limit=0 zou je delen door nul — bescherm. +if [[ "$LIMIT" == "0" ]]; then + PCT=0 +else + PCT=$(awk -v r="$REMAINING" -v l="$LIMIT" 'BEGIN { printf("%d", (r/l)*100) }') +fi + +# Reset-time is al ISO-8601 in de header bij Anthropic; geef ongewijzigd door. +RESET_ESCAPED="${RESET:-}" + +printf '{"remaining":%s,"limit":%s,"pct":%s,"reset_at_iso":"%s","http_status":%s}\n' \ + "$REMAINING" "$LIMIT" "$PCT" "$RESET_ESCAPED" "$HTTP_STATUS"