Compare commits

...
17 Commits
Author SHA1 Message Date
Inferencebot 79f0447089 gateway: build-time patch keeping image-gen pricing on the shared cost-map key (upstream #10023/#12471) so image spend is tracked 2026-10-19 09:26:45 -06:00
Inferencebot 9d95527e93 Remove PublicAI provider (billing never operational; Regolo replaces it for Apertus) 2026-10-16 10:46:04 -06:00
Inferencebot ad34da72a4 Add Regolo provider: apertus-70b, qwen3.5-122b (thinking off), qwen-image + Apertus fallback chain 2026-10-16 10:10:45 -06:00
Inferencebot d082957167 README: document the audio keep-alive scheduled task 2026-10-12 19:33:18 -06:00
Inferencebot 0d73c80562 keepalive: read master key from /app/data/.master_key (scheduler runs outside litellm's env) 2026-10-12 19:20:24 -06:00
Inferencebot acceb43995 Move audio keep-alive into the gateway container (Cloudron scheduler addon, every 10m) 2026-10-12 19:18:46 -06:00
Inferencebot 3397d07ec1 Replace tinfoil/glm-5-3-flash (deprecated 2026-10-09) with tinfoil/glm-5.3 flagship 2026-10-07 22:07:40 -06:00
Inferencebot cc8f7d237a Add tinfoil/nomic-embed-text embedding model (admin-team scoped, for co-op docs RAG) 2026-10-07 09:44:12 -06:00
Inferencebot 5389d9acfc credit-pack hint: patch budget-exceeded message at BUILD time (runtime rootfs is read-only; boot-time patch crashed startup) 2026-10-06 14:24:55 -06:00
Inferencebot 91aa0e0b1d start.sh: append credit-pack hint to LiteLLM's hardcoded budget-exceeded messages (team + key) 2026-10-06 14:04:22 -06:00
Inferencebot 1f2c330762 greenpt/glm-5.3: add cache-read price (0.275/1M per GreenPT pricing API) — member PR follow-up 2026-10-05 08:40:13 -06:00
Inferencebot a0ae99329f Add greenpt/deepseek-v4.1-flash as default (renewable, not Experimental); fallback → tinfoil deepseek 2026-10-02 22:15:44 -06:00
Inferencebot 516167aaf9 Remove green-l/green-r models (GreenPT marks them not recommended) 2026-10-02 22:04:47 -06:00
Inferencebot b6e8542146 config: remove duplicate broken audio-model entries (audio_transcription//audio_speech/ prefixes); keep the correct openai/ routing 2026-10-02 15:39:07 -06:00
Inferencebot b9af62a614 README: current base image v1.103.2 2026-10-02 15:32:49 -06:00
Inferencebot a2864ce34a Upgrade to LiteLLM v1.103.2 (schema pre-migrated live; CVE fix line + streaming-usage bugfix window) 2026-10-02 15:25:27 -06:00
Inferencebot 23f73cf6ae README: current architecture (3 providers, portal key injection, hardening), remove MVP model list (point to co-op/docs as source of truth), all provider env vars, member-key verification kept 2026-10-02 01:08:34 -06:00
8 changed files with 353 additions and 98 deletions

No files matched your search

+15 -4
View File
@@ -2,10 +2,10 @@
"id": "ai.coop.litellm", "id": "ai.coop.litellm",
"title": "LiteLLM Gateway", "title": "LiteLLM Gateway",
"author": "inference.coop", "author": "inference.coop",
"description": "LiteLLM AI Gateway — OpenAI-compatible proxy with per-user API keys, usage tracking, rate limiting, and model routing. Packaged for Cloudron with SSO/OIDC and PostgreSQL.", "description": "LiteLLM AI Gateway \u2014 OpenAI-compatible proxy with per-user API keys, usage tracking, rate limiting, and model routing. Packaged for Cloudron with SSO/OIDC and PostgreSQL.",
"tagline": "AI gateway for cooperative inference", "tagline": "AI gateway for cooperative inference",
"version": "1.0.0", "version": "1.0.0",
"upstreamVersion": "1.84.0", "upstreamVersion": "1.103.2",
"healthCheckPath": "/health/readiness", "healthCheckPath": "/health/readiness",
"httpPort": 4000, "httpPort": 4000,
"memoryLimit": 2147483648, "memoryLimit": 2147483648,
@@ -16,9 +16,20 @@
"addons": { "addons": {
"postgresql": {}, "postgresql": {},
"redis": {}, "redis": {},
"localstorage": {} "localstorage": {},
"scheduler": {
"audio_keepalive": {
"schedule": "*/10 * * * *",
"command": "/app/code/audio_keepalive.py"
}
}
}, },
"tags": ["ai", "gateway", "proxy", "api"], "tags": [
"ai",
"gateway",
"proxy",
"api"
],
"mediaLinks": [], "mediaLinks": [],
"changelog": "Initial package for inference.coop" "changelog": "Initial package for inference.coop"
} }
+21 -1
View File
@@ -1,4 +1,4 @@
FROM ghcr.io/berriai/litellm:v1.84.0 FROM ghcr.io/berriai/litellm:v1.103.2
# v1.84.0 is the fix line for CVE-2026-35029 (auth bypass on # v1.84.0 is the fix line for CVE-2026-35029 (auth bypass on
# /config/update, fixed 1.83.0) and CVE-2026-59822 (MCP session auth # /config/update, fixed 1.83.0) and CVE-2026-59822 (MCP session auth
@@ -20,6 +20,26 @@ 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
# Image-cost tracking: keep image-generation pricing on the shared cost-map
# key so successful image generations appear in SpendLogs (upstream bug
# BerriAI/litellm#10023/#12471 — closed not-planned). Non-fatal at build.
COPY patch_image_cost.py /app/code/patch_image_cost.py
RUN python3 /app/code/patch_image_cost.py
# Audio keep-alive: scheduled pings keep the Tinfoil TEE audio enclave
# warm so voice mode skips the ~24s cold-start (see audio_keepalive.py header).
COPY audio_keepalive.py /app/code/audio_keepalive.py
RUN chmod +x /app/code/audio_keepalive.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"]
+49 -31
View File
@@ -4,43 +4,45 @@ A Cloudron app package for [LiteLLM](https://litellm.ai/), the open-source AI ga
LiteLLM is the single gateway between members and the inference backends. It handles per-member API keys, usage metering, rate limiting (via budget caps), and model routing — all behind one OpenAI-compatible endpoint. LiteLLM is the single gateway between members and the inference backends. It handles per-member API keys, usage metering, rate limiting (via budget caps), and model routing — all behind one OpenAI-compatible endpoint.
**Current base image:** LiteLLM **v1.103.2**, pinned to the exact tag (fix line for CVE-2026-35029 / CVE-2026-59822, CISA KEV-listed; also carries the streaming-usage fix for prompt_tokens_details). See [Updating LiteLLM](#updating-litellm).
## What this provides ## What this provides
- **LiteLLM proxy** with an OpenAI-compatible API on port 4000 - **LiteLLM proxy** with an OpenAI-compatible API on port 4000
- **Per-member virtual API keys** with budget caps and rate limits - **Per-member virtual API keys** with budget caps and rate limits
- **Usage / spend tracking** per key, per model - **Usage / spend tracking** per key, per model
- **Model routing** to Tinfoil (TEE-protected inference) - **Model routing** to multiple providers, chosen by member values (private / green / public)
- **Cloudron PostgreSQL** for keys, teams, and spend logs - **Cloudron PostgreSQL** for keys, teams, and spend logs
- **Cloudron Redis** for rate limiting and caching - **Cloudron Redis** for rate limiting and caching
- **Automatic SSL, backups, and sandboxing** (Cloudron-managed) - **Automatic SSL, backups, and sandboxing** (Cloudron-managed)
- **Master key + salt key** auto-generated on first start, persisted in `/app/data` - **Master key + salt key** auto-generated on first start, persisted in `/app/data`
- Hardened for the public internet: admin UI and API docs disabled at the proxy; CORS locked to the chat origin
## Architecture ## Architecture
``` ```
Members → Open WebUI / LobeChat → LiteLLM (this app) → Tinfoil (TEE) Members → Open WebUI (chat) / own tools (API) → member portal (key injection)
→ LiteLLM gateway (this app)
├→ Tinfoil (TEE enclaves — architectural privacy)
├→ GreenPT (100% renewable energy)
└→ PublicAI (publicly developed, sovereign models)
``` ```
LiteLLM is the control plane (keys, metering, routing). It does **not** run models itself — it forwards to Tinfoil, which runs open models inside trusted execution environments (TEEs). LiteLLM is the control plane (keys, metering, routing). It does **not** run models itself — it forwards to providers, each chosen by members for a different value. A [tinfoil-proxy](https://github.com/tinfoilsh/tinfoil-proxy) sidecar provides EHBP end-to-end encryption to Tinfoil's enclaves.
## Files ## Files
- `CloudronManifest.json` — Cloudron app manifest (addons, ports, metadata) - `CloudronManifest.json` — Cloudron app manifest (addons, ports, memory limit, metadata)
- `Dockerfile` — wraps LiteLLM's official image for Cloudron's environment - `Dockerfile` — wraps LiteLLM's official image for Cloudron's environment (pinned to an exact tag)
- `start.sh` — startup script that wires up Cloudron's Postgres/Redis and writes the default config - `start.sh` — startup script: wires Cloudron's Postgres/Redis, downloads the tinfoil-proxy sidecar, exports `LITELLM_MIGRATION_DIR`
- `config.yaml` — default LiteLLM config (Tinfoil-only, two models) - `config.yaml` — LiteLLM config: model catalog, provider routing, retry policy, custom per-token pricing
- `logo.png` — app icon - `logo.png` — app icon
## Models (MVP) ## Models
Two models, both TEE-protected via Tinfoil: The catalog is defined in `config.yaml` and documented for members in [co-op/docs models](https://git.inference.coop/co-op/docs/src/branch/main/models.md) — that page is the single source of truth; this repo's `config.yaml` mirrors it. Models follow the `provider/model-name` convention so members can choose not just which model but which provider values it embodies (privacy, renewable energy, public development).
| Model | Role | Price (in/out per M tokens) | The catalog changes by member decision — expect it to evolve.
|-------|------|------------------------------|
| `deepseek-v4-flash` | Default — best agent performance, 1M context | $0.30 / $0.70 |
| `gpt-oss-120b` | Fallback — cheapest, built for agentic workflows | $0.15 / $0.60 |
If DeepSeek is down, LiteLLM automatically falls back to GPT-OSS.
--- ---
@@ -70,9 +72,11 @@ cloudron install --location gateway
### Configure ### Configure
After install, set the Tinfoil API key in Cloudron → app → Settings → Environment Variables: After install, set the provider API keys in Cloudron → app → Settings → Environment Variables:
- `TINFOIL_API_KEY` — your Tinfoil API key (from https://tinfoil.sh/) - `TINFOIL_API_KEY` — Tinfoil (https://tinfoil.sh/)
- `GREENPT_API_KEY` — GreenPT (https://greenpt.ai/)
- `PUBLICAI_API_KEY` — PublicAI (https://publicai.co/)
The following are auto-generated and should **not** be set manually: The following are auto-generated and should **not** be set manually:
@@ -81,11 +85,9 @@ The following are auto-generated and should **not** be set manually:
- `DATABASE_URL` — from Cloudron's PostgreSQL addon - `DATABASE_URL` — from Cloudron's PostgreSQL addon
- `REDIS_HOST`, `REDIS_PORT`, `REDIS_PASSWORD` — from Cloudron's Redis addon - `REDIS_HOST`, `REDIS_PORT`, `REDIS_PASSWORD` — from Cloudron's Redis addon
### Access the admin UI ### Admin access
1. Open `https://gateway.inference.coop/ui` The LiteLLM admin UI and API docs are **disabled** on the public gateway (hardened by default; see `start.sh`). Administrative operations run through the [member portal](https://git.inference.coop/code/member-portal), which holds the master key server-side. If direct LiteLLM admin access is ever needed, re-enable the UI in `start.sh`, redeploy, and use the master key from `/app/data/.master_key` — then disable it again.
2. Log in with username `admin` and the master key as password
3. Find the master key: `cloudron exec --app <app-id> cat /app/data/.master_key`
--- ---
@@ -165,21 +167,37 @@ expect a 15–20 min maintenance window and **never panic-restart mid-migration*
| Variable | Required | Purpose | | Variable | Required | Purpose |
|----------|----------|---------| |----------|----------|---------|
| `TINFOIL_API_KEY` | Yes | Tinfoil API key for TEE inference | | `TINFOIL_API_KEY` | Yes | Tinfoil provider (TEE inference) |
| `GREENPT_API_KEY` | Yes | GreenPT provider (renewable energy) |
| `PUBLICAI_API_KEY` | Yes | PublicAI provider (public models) |
| `LITELLM_CORS_ORIGINS` | Set by start.sh | CORS allowlist (chat origin only) |
| `LITELLM_MIGRATION_DIR` | Set by start.sh | Persistent Prisma migrations ledger |
## Connecting Open WebUI ## Connecting chat clients
In Open WebUI settings: Open WebUI does **not** talk to this gateway directly — it routes through the [member portal](https://git.inference.coop/code/member-portal), which authenticates the member and injects their personal LiteLLM key per request. Members building their own tools create API keys on the [member dashboard](https://dashboard.inference.coop) and use:
- **API Base URL:** `https://gateway.inference.coop/v1` - **API Base URL:** `https://gateway.inference.coop/v1` (or the portal's proxy endpoint for chat-integrated tools)
- **API Key:** a virtual key created in LiteLLM's Admin UI - **API Key:** their own virtual key from the dashboard
## OIDC / SSO (future)
The current package uses LiteLLM's built-in admin/master-key auth, which is sufficient for the MVP. Full OIDC SSO (admin UI behind Cloudron login) is a TODO — add the `"oidc": {}` addon to the manifest and configure LiteLLM's SSO hooks per https://docs.litellm.ai/docs/proxy/custom_sso.
## Related repos ## Related repos
- `co-op/org-dev` — the full inference.coop design doc and deploy scripts - `code/member-portal` — membership middleware: provisioning, billing webhooks, key injection
- `code/member-dashboard` — member-facing usage and API-key management
- `code/admin-panel` — admin overview
- `co-op/website` — the public landing page - `co-op/website` — the public landing page
- `co-op/docs` — documentation, including the member-facing model list
- `co-op/design-assets` — logos, fonts, brand materials - `co-op/design-assets` — logos, fonts, brand materials
## Scheduled tasks (scheduler addon)
The gateway pings the Tinfoil audio models every 10 minutes (`audio_keepalive.py`,
declared in `CloudronManifest.json` → `addons.scheduler`) to keep the TEE audio
enclave warm — voice mode then skips the ~24s cold-start after idle. TTS-only:
an STT ping would cost $0.01/request (~$52/mo at 6/hr) versus $0.002 for TTS.
The script reads the master key from `/app/data/.master_key` (written by
`start.sh`); the `LITELLM_MASTER_KEY` env var is only exported into the litellm
process, not scheduler tasks. Verify after deploys: `cloudron exec --app <id>
-- python3 /app/code/audio_keepalive.py` should exit 0 silently, and the deploy
log must show `setupAddons: ... scheduler ... audio_keepalive`.
+49
View File
@@ -0,0 +1,49 @@
#!/usr/bin/env python3
"""Audio keep-alive: ping the Tinfoil TEE audio models through localhost LiteLLM
so voice mode doesn't pay the ~24s cold-start after idle.
Runs INSIDE the gateway container via the Cloudron scheduler addon. Uses the
LITELLM_MASTER_KEY already in the container env. TTS-only (STT pings cost
$0.01/request each; TTS $0.002 — see docs, keep-alive economics). Silent on
success; prints + exits non-zero on failure so scheduler logs capture it.
"""
import json, os, urllib.request, urllib.error, sys
PORT = os.environ.get("PORT", "4000")
def master_key():
"""The gateway's start.sh generates the master key into /app/data/.master_key
and exports LITELLM_MASTER_KEY for the litellm process — scheduler tasks run
in a separate process tree, so read the key file directly."""
key = os.environ.get("LITELLM_MASTER_KEY", "")
if key:
return key
try:
with open("/app/data/.master_key") as f:
return f.read().strip()
except OSError:
return ""
KEY = master_key()
if not KEY:
print("keepalive: no master key (env or /app/data/.master_key)", file=sys.stderr)
sys.exit(1)
fail = 0
# TTS warm (voxtral-tts) — $0.002/request (Tinfoil request-priced)
req = urllib.request.Request(
f"http://127.0.0.1:{PORT}/v1/audio/speech",
data=json.dumps({"model": "tinfoil/voxtral-tts", "input": "ok",
"voice": "casual_female"}).encode(),
method="POST",
headers={"Authorization": f"Bearer {KEY}", "Content-Type": "application/json"})
try:
urllib.request.urlopen(req, timeout=30)
except Exception as e:
print(f"keepalive: TTS ping failed: {e}", file=sys.stderr)
fail = 1
# STT warm disabled 2026-10-11: $0.01/STT request × 6/hr ≈ $52/mo. Revisit only
# with a cheaper warm strategy.
sys.exit(fail)
+79 -62
View File
@@ -5,10 +5,11 @@
# with EHBP (HPKE) before forwarding to the Tinfoil enclave. Provider key set # with EHBP (HPKE) before forwarding to the Tinfoil enclave. Provider key set
# via environment variable TINFOIL_API_KEY. # via environment variable TINFOIL_API_KEY.
# #
# Pricing (model_info) is per-token, derived from Tinfoil's per-1M rates: # Pricing (model_info) is per-token, derived from provider per-1M rates:
# greenpt/deepseek-v4.1-flash: $0.22 in / $1.10 out / $0.011 cached per 1M
# tinfoil/deepseek-v4-1-flash: $0.65 in / $1.45 out / $0.13 cached per 1M # tinfoil/deepseek-v4-1-flash: $0.65 in / $1.45 out / $0.13 cached per 1M
# tinfoil/gpt-oss-120b: $0.15 in / $0.60 out per 1M # tinfoil/gpt-oss-120b: $0.15 in / $0.60 out per 1M
# tinfoil/glm-5-3-flash: $0.40 in / $1.25 out / $0.10 cached per 1M # tinfoil/glm-5.3: $1.80 in / $5.75 out / $0.45 cached per 1M
# Without these, LiteLLM can't price these custom models, so spend shows $0.00 # Without these, LiteLLM can't price these custom models, so spend shows $0.00
# and the team budget cap is not enforced. # and the team budget cap is not enforced.
# #
@@ -18,7 +19,22 @@
# models.md is the member-facing mirror. # models.md is the member-facing mirror.
model_list: model_list:
# DeepSeek V4.1 Flash — default model (replaces V4 Flash, deprecated 2026-09-15) # DeepSeek V4.1 Flash on GreenPT — DEFAULT model. Same model family as Tinfoil's
# deepseek-v4-1-flash, but on renewable energy and NOT marked Experimental by the
# provider (Tinfoil's deepseek + glm-flash carry an "Experimental: may be deprecated
# with minimal notice" flag). Policy-based privacy (GreenPT) vs TEE (Tinfoil).
# €0.22 in / €1.10 out / €0.011 cached per 1M.
- model_name: greenpt/deepseek-v4.1-flash
litellm_params:
model: openai/deepseek-v4.1-flash
api_base: https://api.greenpt.ai/v1
api_key: os.environ/GREENPT_API_KEY
model_info:
input_cost_per_token: 0.00000022
output_cost_per_token: 0.00000110
cache_read_input_token_cost: 0.000000011
# DeepSeek V4.1 Flash on Tinfoil (TEE) — private counterpart to the default.
- model_name: tinfoil/deepseek-v4-1-flash - model_name: tinfoil/deepseek-v4-1-flash
litellm_params: litellm_params:
model: openai/deepseek-v4-1-flash model: openai/deepseek-v4-1-flash
@@ -39,39 +55,23 @@ model_list:
input_cost_per_token: 0.00000015 input_cost_per_token: 0.00000015
output_cost_per_token: 0.00000060 output_cost_per_token: 0.00000060
# GLM-5.3 Flash — fast, efficient MoE model # GLM-5.3 (full flagship) on Tinfoil (TEE) — replaced tinfoil/glm-5-3-flash
- model_name: tinfoil/glm-5-3-flash # (Tinfoil deprecated the flash variant 2026-10-09). $1.80 in / $5.75 out /
# $0.45 cached per 1M. Text-only (no vision), reasoning + tool calling.
- model_name: tinfoil/glm-5.3
litellm_params: litellm_params:
model: openai/glm-5-3-flash model: openai/glm-5-3
api_base: http://127.0.0.1:3301/v1 api_base: http://127.0.0.1:3301/v1
api_key: os.environ/TINFOIL_API_KEY api_key: os.environ/TINFOIL_API_KEY
model_info: model_info:
input_cost_per_token: 0.00000040 input_cost_per_token: 0.0000018
output_cost_per_token: 0.00000125 output_cost_per_token: 0.00000575
cache_read_input_token_cost: 0.00000010 cache_read_input_token_cost: 0.00000045
# --- GreenPT (renewable energy) --- # --- GreenPT (renewable energy) ---
# OpenAI-compatible, EU-hosted on 100% renewable energy. No TEE proxy needed — # OpenAI-compatible, EU-hosted on 100% renewable energy. No TEE proxy needed —
# LiteLLM talks to GreenPT directly. Pricing from GreenPT /v1/pricing endpoint. # LiteLLM talks to GreenPT directly.
# green-r: $0.35 in / $0.95 out per 1M (128k ctx) # (Removed 2026-10: green-l/green-r — GreenPT now marks them "not recommended".)
# green-l: $0.25 in / $0.80 out per 1M (128k ctx)
- model_name: greenpt/green-r
litellm_params:
model: openai/green-r
api_base: https://api.greenpt.ai/v1
api_key: os.environ/GREENPT_API_KEY
model_info:
input_cost_per_token: 0.00000035
output_cost_per_token: 0.00000095
- model_name: greenpt/green-l
litellm_params:
model: openai/green-l
api_base: https://api.greenpt.ai/v1
api_key: os.environ/GREENPT_API_KEY
model_info:
input_cost_per_token: 0.00000025
output_cost_per_token: 0.00000080
# GLM-5.3 Flash on GreenPT (note the dotted slug — distinct from Tinfoil's # GLM-5.3 Flash on GreenPT (note the dotted slug — distinct from Tinfoil's
# glm-5-3-flash). $0.11 in / $0.44 out / $0.022 cached per 1M. # glm-5-3-flash). $0.11 in / $0.44 out / $0.022 cached per 1M.
@@ -95,6 +95,7 @@ model_list:
model_info: model_info:
input_cost_per_token: 0.0000011 input_cost_per_token: 0.0000011
output_cost_per_token: 0.0000044 output_cost_per_token: 0.0000044
cache_read_input_token_cost: 0.000000275
# --- Audio models (Tinfoil, inside the TEE) --- # --- Audio models (Tinfoil, inside the TEE) ---
# NOT chat models: these power /v1/audio/* routes (STT for dictation, TTS for # NOT chat models: these power /v1/audio/* routes (STT for dictation, TTS for
@@ -103,48 +104,44 @@ model_list:
# (Open WebUI) by their `mode` field, so they never appear as pickable chat # (Open WebUI) by their `mode` field, so they never appear as pickable chat
# options. Pricing per Tinfoil catalog: whisper $0.05/1M in; voxtral-tts # options. Pricing per Tinfoil catalog: whisper $0.05/1M in; voxtral-tts
# listed $0/$0 (verify on invoice — may be beta-free). # listed $0/$0 (verify on invoice — may be beta-free).
- model_name: tinfoil/whisper-large-v3-turbo
litellm_params:
model: audio_transcription/whisper-large-v3-turbo
api_base: http://127.0.0.1:3301/v1
api_key: os.environ/TINFOIL_API_KEY
model_info:
mode: audio_transcription
input_cost_per_token: 0.00000005
- model_name: tinfoil/voxtral-tts # --- Regolo (EU, zero data retention, 100% renewables) ---
# Seeweb S.r.l. (Italy). Added 2026-10-16 following member suggestion (#22).
# apertus-70b: replaced PublicAI (removed 2026-10-16) as our Apertus provider —
# same open-weights model, ~45% cheaper input, EU ZDR + renewables.
# qwen3.5-122b: large vision+reasoning MoE (262K ctx). REQUIRES
# chat_template_kwargs: {enable_thinking: false} — otherwise the reasoning
# phase consumes the entire token budget and content comes back empty.
# Qwen-Image: image generation via /v1/images/generations, billed per pixel.
- model_name: regolo/apertus-70b
litellm_params: litellm_params:
model: audio_speech/voxtral-tts model: openai/apertus-70b
api_base: http://127.0.0.1:3301/v1 api_base: https://api.regolo.ai/v1
api_key: os.environ/TINFOIL_API_KEY api_key: os.environ/REGOLO_API_KEY
model_info: model_info:
mode: audio_speech mode: chat
input_cost_per_token: 0.0 input_cost_per_token: 4.6e-7
output_cost_per_token: 0.0 output_cost_per_token: 2.44e-6
# --- PublicAI (publicly developed / sovereign models) --- - model_name: regolo/qwen3.5-122b
# OpenAI-compatible gateway for public open models. Apertus is the Swiss AI
# Initiative's fully-open model (Apache-2.0: weights, code, and training data).
# Pricing from PublicAI /v1/models (per 1M): 8b $0.10/$0.20, 70b $0.82/$2.92.
# Member-facing name drops the upstream `swiss-ai/` owner prefix for brevity;
# the litellm_params.model keeps the full PublicAI id.
- model_name: publicai/apertus-v1.5-8b
litellm_params: litellm_params:
model: openai/swiss-ai/apertus-v1.5-8b model: openai/qwen3.5-122b
api_base: https://api.publicai.co/v1 api_base: https://api.regolo.ai/v1
api_key: os.environ/PUBLICAI_API_KEY api_key: os.environ/REGOLO_API_KEY
chat_template_kwargs: {"enable_thinking": false}
model_info: model_info:
input_cost_per_token: 0.00000010 mode: chat
output_cost_per_token: 0.00000020 input_cost_per_token: 1.16e-6
output_cost_per_token: 4.87e-6
- model_name: publicai/apertus-v1.5-70b - model_name: regolo/qwen-image
litellm_params: litellm_params:
model: openai/swiss-ai/apertus-v1.5-70b model: openai/Qwen-Image
api_base: https://api.publicai.co/v1 api_base: https://api.regolo.ai/v1
api_key: os.environ/PUBLICAI_API_KEY api_key: os.environ/REGOLO_API_KEY
model_info: model_info:
input_cost_per_token: 0.00000082 mode: image_generation
output_cost_per_token: 0.00000292 input_cost_per_pixel: 1.5e-8
# --- Audio models (Tinfoil, in-TEE) --- # --- Audio models (Tinfoil, in-TEE) ---
# Used for chat dictation (STT) and voice mode (TTS). These are NOT chat # Used for chat dictation (STT) and voice mode (TTS). These are NOT chat
@@ -172,6 +169,24 @@ model_list:
model_info: model_info:
mode: audio_speech mode: audio_speech
# --- Embedding model (Tinfoil, in-TEE) ---
# nomic-embed-text — 768-dim text embeddings for RAG/semantic search.
# NOT a chat model: it powers /v1/embeddings only. Scoped to the admin
# account's team (model_info.team_id) so member keys cannot call it — it is
# an operator resource for the co-op's own docs-RAG, not a member-facing
# model. The portal also filters it out of the chat-facing /v1/models list by
# its `mode: embedding`. Pricing per Tinfoil catalog: $0.05/1M input, $0 out.
- model_name: tinfoil/nomic-embed-text
litellm_params:
model: openai/nomic-embed-text
api_base: http://127.0.0.1:3301/v1
api_key: os.environ/TINFOIL_API_KEY
model_info:
mode: embedding
team_id: cdfdf422-633f-4432-8893-cffee82a8325
input_cost_per_token: 0.00000005
output_cost_per_token: 0.0
# Fallback: if DeepSeek is down, use GPT-OSS # Fallback: if DeepSeek is down, use GPT-OSS
# NOTE: DeepSeek V4.1 Flash is a reasoning model — generations routinely run # NOTE: DeepSeek V4.1 Flash is a reasoning model — generations routinely run
# longer than 30s. A 30s timeout caused LiteLLM to abandon in-flight DeepSeek # longer than 30s. A 30s timeout caused LiteLLM to abandon in-flight DeepSeek
@@ -187,6 +202,8 @@ router_settings:
retry_policy: retry_policy:
TimeoutErrorRetries: 0 TimeoutErrorRetries: 0
fallbacks: fallbacks:
- greenpt/deepseek-v4.1-flash:
- tinfoil/deepseek-v4-1-flash
- tinfoil/deepseek-v4-1-flash: - tinfoil/deepseek-v4-1-flash:
- tinfoil/gpt-oss-120b - tinfoil/gpt-oss-120b
+60
View File
@@ -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)
+75
View File
@@ -0,0 +1,75 @@
"""Keep image-generation pricing on the shared cost-map key.
Run at BUILD time (the runtime rootfs is read-only under Cloudron). Non-fatal:
if the target file or anchor strings are missing (e.g. after an upstream
version bump), it prints a warning and exits 0 so the build — and the
gateway — still succeeds.
Why: LiteLLM's image cost calculator (default_image_cost_calculator) looks
pricing up ONLY in the shared ``{provider}/{model}`` key of
``litellm.model_cost`` — it never consults the per-deployment entry. But the
router deliberately strips per-deployment pricing from that shared key
("so one deployment's overrides don't pollute sibling deployments"), which
makes every successful image generation on a custom backend invisible:
the cost lookup raises, the spend-log write is skipped, and SpendLogs /
the dashboards show nothing. Upstream: BerriAI/litellm#10023 and #12471,
closed not-planned.
Patch: for image_generation deployments, re-add the custom pricing fields to
the shared-key entry. Safe for us: the co-op gateway has a single deployment
per image backend, so there are no sibling deployments to pollute.
"""
import sys
import glob
ANCHOR = " # Always register the (possibly mode-preserved) shared backend info."
INSERT = ''' # CO-OP PATCH (2026-10-19): image-generation deployments must keep
# their pricing on the shared cost-map key — the image cost calculator
# (default_image_cost_calculator) reads ONLY this key and never the
# per-deployment model_id entry, so stripping pricing here makes every
# successful image generation invisible in SpendLogs (upstream:
# BerriAI/litellm#10023, #12471, closed not-planned). Safe: the co-op
# gateway has a single deployment per image backend — no siblings.
if deployment_mode == "image_generation":
for _pk, _pv in model_info.items():
if _pk in CustomPricingLiteLLMParams.model_fields and _pv is not None:
shared_model_info[_pk] = _pv
'''
candidates = glob.glob(
"/app/.venv/lib/python*/site-packages/litellm/router.py"
)
patched_any = False
for p in candidates:
with open(p) as f:
src = f.read()
if "CO-OP PATCH (2026-10-19)" in src:
print(f"INFO: image-cost patch already present in {p}")
continue
if ANCHOR not in src:
print(f"WARN: anchor not found in {p}; patch not applied (non-fatal)",
file=sys.stderr)
continue
new = src.replace(ANCHOR, INSERT + ANCHOR, 1)
if new == src:
print(f"WARN: insertion failed in {p} (non-fatal)", file=sys.stderr)
continue
with open(p, "w") as f:
f.write(new)
print(f"INFO: patched image-cost registration in {p}")
patched_any = True
if not candidates:
print("WARN: router.py not found; patch not applied (non-fatal)",
file=sys.stderr)
elif not patched_any:
print("WARN: no files patched (non-fatal)", file=sys.stderr)
sys.exit(0)
+5
View File
@@ -37,6 +37,11 @@ if [ ! -f "$MASTER_KEY_FILE" ]; then
python3 -c "import secrets; print('sk-' + secrets.token_urlsafe(32))" > "$MASTER_KEY_FILE" python3 -c "import secrets; print('sk-' + secrets.token_urlsafe(32))" > "$MASTER_KEY_FILE"
fi fi
export LITELLM_MASTER_KEY=$(cat "$MASTER_KEY_FILE") export LITELLM_MASTER_KEY=$(cat "$MASTER_KEY_FILE")
# Regolo API key (member-suggested provider, 2026-10-16): stored in /app/data by the operator
# via the Cloudron file manager — kept out of app env config and git.
if [ -f "/app/data/regolo_key.txt" ]; then
export REGOLO_API_KEY=$(cat /app/data/regolo_key.txt | tr -d '[:space:]')
fi
# Generate a salt key if not already in /app/data # Generate a salt key if not already in /app/data
SALT_KEY_FILE="/app/data/.salt_key" SALT_KEY_FILE="/app/data/.salt_key"