From 5389d9acfcae9612f6f3a3e37624a118b5c78c9e Mon Sep 17 00:00:00 2001 From: Inferencebot Date: Tue, 6 Oct 2026 14:24:55 -0600 Subject: [PATCH] credit-pack hint: patch budget-exceeded message at BUILD time (runtime rootfs is read-only; boot-time patch crashed startup) --- Dockerfile | 9 +++++++ patch_budget_message.py | 60 +++++++++++++++++++++++++++++++++++++++++ start.sh | 38 -------------------------- 3 files changed, 69 insertions(+), 38 deletions(-) create mode 100644 patch_budget_message.py diff --git a/Dockerfile b/Dockerfile index 324bcbd..2a936ed 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,6 +20,15 @@ RUN chmod +x /app/code/start.sh # Default config — will be overridden by Cloudron env vars at runtime COPY config.yaml /app/data/config.yaml +# Append the co-op credit-pack hint to LiteLLM's hardcoded budget-exceeded +# messages. Done at BUILD time — the runtime rootfs is read-only under +# Cloudron, so a boot-time patch would crash the startup script (it did, +# 2026-10-06). patch_budget_message.py is non-fatal: it exits 0 even when the +# file/message can't be found, so a future LiteLLM version bump can't take the +# gateway down. +COPY patch_budget_message.py /app/code/patch_budget_message.py +RUN python3 /app/code/patch_budget_message.py + # Override the base image's ENTRYPOINT (which is `litellm`) so our # startup script runs instead of being passed as an argument to litellm. ENTRYPOINT ["/app/code/start.sh"] diff --git a/patch_budget_message.py b/patch_budget_message.py new file mode 100644 index 0000000..11cbcd2 --- /dev/null +++ b/patch_budget_message.py @@ -0,0 +1,60 @@ +"""Append the co-op credit-pack hint to LiteLLM's hardcoded budget-exceeded messages. + +Run at BUILD time (the runtime rootfs is read-only under Cloudron). Non-fatal: +if the target file or message strings are missing (e.g. after an upstream +version bump moves or rewrites them), it prints a warning and exits 0 so the +build — and the gateway — still succeeds. The hint is a nice-to-have, never a +reason to take the gateway down. +""" +import sys +import glob + +HINT = '. You can add credit packs at https://opencollective.com/inference-cooperative' + +TEAM_MSG = ( + 'message=f"Budget has been exceeded! Team={team_object.team_id} ' + 'Current cost: {spend}, Max budget: {team_object.max_budget}"' +) +KEY_MSG = ( + 'message=f"Budget has been exceeded! Key={key_descriptor} ' + 'Current cost: {spend}, Max budget: {valid_token.max_budget}"' +) + +# Find the proxy auth_checks.py wherever it lives in this image (venv path +# varies by Python minor version; the running CLI imports from the image's +# venv). Search the site-packages copies, not the type-stub one under +# litellm/types/. +candidates = glob.glob( + "/app/.venv/lib/python*/site-packages/litellm/proxy/auth/auth_checks.py" +) + +patched_any = False +for p in candidates: + with open(p) as f: + src = f.read() + + if "opencollective.com/inference-cooperative" in src: + print(f"INFO: credit-pack hint already present in {p}") + continue + + new = src.replace(TEAM_MSG, TEAM_MSG + ' + "' + HINT + '"') + new = new.replace(KEY_MSG, KEY_MSG + ' + "' + HINT + '"') + + if new == src: + print(f"WARN: budget-exceeded message format changed in {p}; " + "hint not applied (non-fatal)", file=sys.stderr) + continue + + with open(p, "w") as f: + f.write(new) + print(f"INFO: patched budget-exceeded messages in {p}") + patched_any = True + +if not candidates: + print("WARN: auth_checks.py not found; hint not applied (non-fatal)", + file=sys.stderr) +elif not patched_any: + print("WARN: no budget-exceeded messages patched (non-fatal)", + file=sys.stderr) + +sys.exit(0) diff --git a/start.sh b/start.sh index 9103bda..e792afd 100644 --- a/start.sh +++ b/start.sh @@ -172,42 +172,4 @@ echo "Tinfoil proxy PID: $TINFOIL_PROXY_PID" # Give the proxy a moment to verify the enclave attestation sleep 3 -# Append a co-op-specific hint to LiteLLM's budget-exceeded messages so a -# member who hits their limit is told they can top up with a credit pack. -# LiteLLM hardcodes these strings in -# litellm/proxy/auth/auth_checks.py (not configurable via env/settings), so we -# patch the running copy at boot. Idempotent (checks a marker first) and -# re-applied every start, so it survives upstream file moves on version bumps -# — unlike the old build-time `RUN sed` CORS patch, which broke silently when -# LiteLLM relocated the file. -CREDIT_PACK_HINT='. You can add credit packs at https://opencollective.com/inference-cooperative' -AUTH_CHECKS_PY="$(python3 -c 'import litellm.proxy.auth.auth_checks as m; print(m.__file__)')" -if [ -n "$AUTH_CHECKS_PY" ] && ! grep -q "opencollective.com/inference-cooperative" "$AUTH_CHECKS_PY" 2>/dev/null; then - echo "Patching budget-exceeded messages with credit-pack hint..." - python3 - "$AUTH_CHECKS_PY" <<'PYEOF' -import sys -p = sys.argv[1] -s = open(p).read() -hint = ". You can add credit packs at https://opencollective.com/inference-cooperative" -before = s -# Team budget message (the one that fires for members, enforced at team level) -s = s.replace( - 'message=f"Budget has been exceeded! Team={team_object.team_id} Current cost: {spend}, Max budget: {team_object.max_budget}"', - 'message=f"Budget has been exceeded! Team={team_object.team_id} Current cost: {spend}, Max budget: {team_object.max_budget}" + "' + hint.replace('"', '\\"') + '"', -) -# Key budget message (a few keys carry individual max_budget) -s = s.replace( - 'message=f"Budget has been exceeded! Key={key_descriptor} Current cost: {spend}, Max budget: {valid_token.max_budget}"', - 'message=f"Budget has been exceeded! Key={key_descriptor} Current cost: {spend}, Max budget: {valid_token.max_budget}" + "' + hint.replace('"', '\\"') + '"', -) -if s != before: - open(p, "w").write(s) - print("patched") -else: - print("no match (upstream message format changed) — leaving unpatched") -PYEOF -else - echo "Budget-exceeded hint already applied (or auth_checks.py not found); skipping." -fi - exec litellm --config "$CONFIG_FILE" --port 4000 --host 0.0.0.0 \ No newline at end of file