credit-pack hint: patch budget-exceeded message at BUILD time (runtime rootfs is read-only; boot-time patch crashed startup)
This commit is contained in:
1 parent
91aa0e0b1d
commit
5389d9acfc
3 files changed
+69
-38
No files matched your search
@@ -20,6 +20,15 @@ RUN chmod +x /app/code/start.sh
|
|||||||
# Default config — will be overridden by Cloudron env vars at runtime
|
# Default config — will be overridden by Cloudron env vars at runtime
|
||||||
COPY config.yaml /app/data/config.yaml
|
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
|
# Override the base image's ENTRYPOINT (which is `litellm`) so our
|
||||||
# startup script runs instead of being passed as an argument to litellm.
|
# startup script runs instead of being passed as an argument to litellm.
|
||||||
ENTRYPOINT ["/app/code/start.sh"]
|
ENTRYPOINT ["/app/code/start.sh"]
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -172,42 +172,4 @@ echo "Tinfoil proxy PID: $TINFOIL_PROXY_PID"
|
|||||||
# Give the proxy a moment to verify the enclave attestation
|
# Give the proxy a moment to verify the enclave attestation
|
||||||
sleep 3
|
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
|
exec litellm --config "$CONFIG_FILE" --port 4000 --host 0.0.0.0
|
||||||
Reference in new issue
Block a user