Proxy: alle Anbieter
Der Proxy ist der Kern. Er reicht Ihre Anfrage unverändert an den Anbieter durch, liest die Nutzungsangaben aus der Antwort und kann vor dem Aufruf eingreifen. Ihr Anbieterschlüssel geht durch und wird nie gespeichert; Inhalte werden nie gespeichert.
Basis-URLs
| Anbieter | Basis-URL bei Tokenwacht | Ihr Anbieterschlüssel | Hinweis |
|---|---|---|---|
| Anthropic | https://tokenwacht.de/anthropic | x-api-key wie bisher | Messages API, Streaming, Werkzeuge, Prompt-Caching, Batches werden durchgereicht. |
| OpenAI | https://tokenwacht.de/openai/v1 | Authorization: Bearer wie bisher | Chat Completions und Responses API. Bei Streams setzt der Proxy stream_options.include_usage, damit die Zählung exakt bleibt. |
| Google Gemini | https://tokenwacht.de/gemini | x-goog-api-key oder ?key= | generateContent und streamGenerateContent?alt=sse. Modell aus dem Pfad. |
| Amazon Bedrock | https://tokenwacht.de/bedrock | Bedrock-API-Schlüssel als Authorization: Bearer | /model/<id>/invoke, /converse, jeweils mit -with-response-stream. SigV4-signierte Aufrufe funktionieren über einen Proxy nicht; verwenden Sie einen Bedrock-API-Schlüssel. Region der Installation: eu-central-1. |
| Azure OpenAI | https://tokenwacht.de/azure | api-key wie bisher, dazu X-TW-Ziel: https://<ressource>.openai.azure.com | Bereitstellungspfade /openai/deployments/… und /openai/v1/… werden unverändert an Ihre Ressource weitergereicht; nur Microsoft-Domänen sind als Ziel erlaubt. Preise: OpenAI-Listenpreise des Grundmodells, das Azure in der Antwort nennt. |
| Mistral | https://tokenwacht.de/mistral/v1 | Authorization: Bearer wie bisher | OpenAI-Format (/chat/completions). Der Proxy verändert den Anfragekörper nicht; die Nutzung steht bei Mistral im letzten Chunk. Preiskatalog mistral.yaml. |
| DeepSeek | https://tokenwacht.de/deepseek/v1 | Authorization: Bearer wie bisher | OpenAI-Format. DeepSeek rechnet nach Tageszeit; der Katalog führt den Spitzensatz als Obergrenze. |
| Groq | https://tokenwacht.de/groq/v1 | Authorization: Bearer wie bisher | OpenAI-Format; die Nutzung im Strom liest der Proxy aus x_groq.usage. Katalog kennt die belegten Modelle, weitere über eigene Preise. |
| OpenRouter | https://tokenwacht.de/openrouter/v1 | Authorization: Bearer wie bisher | OpenAI-Format. Der Proxy fordert usage.include an und übernimmt den Betrag aus usage.cost als verbindliche Kosten — kein Katalog nötig, jedes Modell hinter OpenRouter wird richtig gerechnet. |
| Eigenes Ziel | https://tokenwacht.de/ziel/<name>/v1 | wie beim Ziel (Ollama: keiner) | Ollama, vLLM, LM Studio, LiteLLM oder eine feste Azure-Ressource — im Cockpit unter Anbindung angelegt. Siehe Eigene Ziele und Preise. |
Mistral, DeepSeek, Groq und OpenRouter brauchen keinen eigenen Client: der OpenAI-Client mit der jeweiligen Basis-URL und dem Schlüssel des Anbieters genügt. Im Ereignis steht der echte Anbietername (mistral, azure, groq …), im Cockpit erscheinen sie getrennt.
Kopfzeilen
| Kopfzeile | Pflicht | Bedeutung |
|---|---|---|
X-TW-Key | ja | Ihr Tokenwacht-Schlüssel tw_live_…. Ohne ihn antwortet der Proxy mit 401 und reicht nichts weiter. |
X-TW-Agent | empfohlen | Der Agent als dauerhafte Einheit, z. B. nils-icp-research. Fehlt er, heißt der Agent „unbenannt“. |
X-TW-Run | empfohlen | Lauf-Kennung: UUID oder beliebiger Text (z. B. RL-0815). Fehlt sie, bildet Tokenwacht Läufe automatisch: gleicher Agent, weniger als 15 Minuten Pause = derselbe Lauf. |
X-TW-Step | optional | Schrittnummer im Lauf. Fehlt sie, zählt der Proxy hoch. |
X-TW-Kunde | optional | Endkunden-Kennung für die Weiterbelastung (Reiter Endkunden, CSV, DATEV, PDF). |
X-TW-Funktion | optional | Fachliche Funktion, z. B. leadliste_erzeugen. Erscheint im Lauf-Detail. |
X-TW-Benutzer | optional | Menschlicher Benutzer hinter dem Aufruf, wenn Sie Kosten je Person sehen wollen. |
Die Kopfzeilen X-TW-* werden vor der Weiterleitung entfernt. Der Anbieter sieht sie nicht.
Beispiele
Anthropic · Python
from anthropic import Anthropic
client = Anthropic(base_url="https://tokenwacht.de/anthropic",
default_headers={"X-TW-Key": TW_KEY, "X-TW-Agent": "nils-icp-research", "X-TW-Run": lauf_id, "X-TW-Kunde": "kunde-4711"})
with client.messages.stream(model="claude-sonnet-5", max_tokens=800, messages=[…]) as strom:
for text in strom.text_stream:
print(text, end="")
OpenAI · Python, Responses API
from openai import OpenAI
client = OpenAI(base_url="https://tokenwacht.de/openai/v1", default_headers={"X-TW-Key": TW_KEY, "X-TW-Agent": "erik-closer"})
antwort = client.responses.create(model="gpt-5", input="Fasse den Anruf zusammen.")
Gemini · Python (google-genai)
from google import genai
from google.genai import types
client = genai.Client(api_key=GOOGLE_KEY, http_options=types.HttpOptions(
base_url="https://tokenwacht.de/gemini", headers={"X-TW-Key": TW_KEY, "X-TW-Agent": "magnus-texter"}))
antwort = client.models.generate_content(model="gemini-2.5-flash", contents="Sag Hallo.")
Gemini · REST
curl "https://tokenwacht.de/gemini/v1beta/models/gemini-2.5-flash:generateContent" \
-H "x-goog-api-key: $GOOGLE_API_KEY" -H "X-TW-Key: $TOKENWACHT_KEY" -H "X-TW-Agent: probe" \
-H "content-type: application/json" -d '{"contents":[{"role":"user","parts":[{"text":"Sag Hallo."}]}]}'
Bedrock · Converse mit API-Schlüssel
curl "https://tokenwacht.de/bedrock/model/eu.anthropic.claude-sonnet-5-v1:0/converse" \
-H "Authorization: Bearer $AWS_BEARER_TOKEN_BEDROCK" -H "X-TW-Key: $TOKENWACHT_KEY" -H "X-TW-Agent: probe" \
-H "content-type: application/json" -d '{"messages":[{"role":"user","content":[{"text":"Sag Hallo."}]}]}'
# boto3 mit Bedrock-API-Schlüssel (ohne SigV4): Umgebung AWS_BEARER_TOKEN_BEDROCK setzen, dann
# client = boto3.client("bedrock-runtime", region_name="eu-central-1", endpoint_url="https://tokenwacht.de/bedrock")
# Kopfzeilen X-TW-* über ein botocore-Ereignis (before-send) ergänzen — Beispiel in der Fehlersuche.
Azure OpenAI · Python
from openai import AzureOpenAI
client = AzureOpenAI(azure_endpoint="https://tokenwacht.de/azure", api_key=os.environ["AZURE_OPENAI_API_KEY"], api_version="2024-10-21",
default_headers={"X-TW-Key": TW_KEY, "X-TW-Agent": "ingrid-termine",
"X-TW-Ziel": "https://meine-firma.openai.azure.com"}) # Ihre Ressource, je Aufruf
antwort = client.chat.completions.create(model="gpt-4.1-prod", messages=[…]) # model = Ihr Bereitstellungsname
Tokenwacht bei sich selbst betrieben? Dann genügt TW_AZURE_OPENAI_URL in der Umgebung, die Kopfzeile entfällt. Alternativ legen Sie die Ressource als eigenes Ziel an.
Mistral, DeepSeek, Groq, OpenRouter · Python mit dem OpenAI-Client
from openai import OpenAI
mistral = OpenAI(base_url="https://tokenwacht.de/mistral/v1", api_key=os.environ["MISTRAL_API_KEY"],
default_headers={"X-TW-Key": TW_KEY, "X-TW-Agent": "liv-connector"})
groq = OpenAI(base_url="https://tokenwacht.de/groq/v1", api_key=os.environ["GROQ_API_KEY"], default_headers={…})
router = OpenAI(base_url="https://tokenwacht.de/openrouter/v1", api_key=os.environ["OPENROUTER_API_KEY"], default_headers={…})
antwort = mistral.chat.completions.create(model="mistral-small-latest", messages=[{"role": "user", "content": "Sag Hallo."}])
Ollama im eigenen Netz · eigenes Ziel
# einmalig im Cockpit: Anbindung › Eigene Ziele → Name "ollama", Adresse http://127.0.0.1:11434, Format OpenAI (Selbstbetrieb)
from openai import OpenAI
client = OpenAI(base_url="https://tokenwacht.ihre-firma.de/ziel/ollama/v1", api_key="ollama",
default_headers={"X-TW-Key": TW_KEY, "X-TW-Agent": "lokal-zusammenfasser"})
Node · Anthropic und OpenAI
const client = new Anthropic({ baseURL: "https://tokenwacht.de/anthropic",
defaultHeaders: { "X-TW-Key": process.env.TOKENWACHT_KEY, "X-TW-Agent": "liv-connector" } });
const openai = new OpenAI({ baseURL: "https://tokenwacht.de/openai/v1",
defaultHeaders: { "X-TW-Key": process.env.TOKENWACHT_KEY, "X-TW-Agent": "liv-connector" } });
Was der Proxy misst
- Eingabe-, Ausgabe-, Cache-Lese-, Cache-Schreib- und Denk-Token exakt aus den Nutzungsangaben des Anbieters, auch bei Streams.
- Latenz bis zum ersten Zeichen und gesamt, HTTP-Status, Stop-Grund, Werkzeugaufrufe mit Fingerabdruck (für die Leerschleifen-Erkennung), Fingerabdruck des Prompt-Präfixes (für die Cache-Blindheit).
- Nicht: Prompt, Antwort, Werkzeugargumente, Systemtext. Nur Längen und Hash-Werte.
Ablehnung mit HTTP 402
Greift eine scharfe Regel mit Stufe „stoppen“ oder eine Schutzregel eines Detektors, antwortet der Proxy statt des Anbieters. Ihr Client bekommt ein JSON, mit dem der Agent geordnet enden kann:
HTTP/1.1 402 Payment Required
{"fehler": "tokenwacht_budget_ueberschritten", "regel": "lauf_budget", "regel_id": "…", "regel_name": "Lauf max. 3 €",
"grenze": "3.0000", "wert": "3.0412", "einheit": "eur", "verbraucht_eur": "2.9800", "schritte": 41,
"lauf_id": "…", "hinweis": "Lauf-Budget 3 € erschöpft (2.9800 € verbraucht)", "cockpit_url": "https://tokenwacht.de/cockpit"}
So fangen Sie es ab:
# Python (Anthropic-SDK wirft bei 402 einen APIStatusError)
from anthropic import APIStatusError
try:
client.messages.create(…)
except APIStatusError as e:
if e.status_code == 402 and e.body and str(e.body.get("fehler", "")).startswith("tokenwacht_"):
lauf_geordnet_beenden(e.body["hinweis"]); raise SystemExit(0)
raise
Stufe „drosseln“ verzögert den Aufruf vor der Weiterleitung um bis zu zehn Sekunden; der Client merkt nur eine längere Antwortzeit. Die SDKs von Tokenwacht (Python, Node) werfen bei 402 eine benannte Ausnahme BudgetUeberschritten.
Ausfallverhalten
- Ist die Regelprüfung gestört, ist der Proxy durchlässig: der Aufruf geht ohne Prüfung zum Anbieter.
- Ist die Datenbank gestört, werden Ereignisse in eine Nachtragsdatei geschrieben und später nachgetragen; der Aufruf selbst wird nicht verzögert.
- Ist Tokenwacht komplett nicht erreichbar, setzen Sie die Basis-URL zurück. Den Zustand zeigt die Statusseite. Empfehlung für kritische Agenten: ein Rückfall auf die Anbieteradresse nach einem Verbindungsfehler, wie es das Python-SDK vorsieht.
Grenzen
- Anfragekörper bis 20 MB, Antwortzeit bis 600 Sekunden (Streams eingeschlossen).
- Der Proxy ändert keine Parameter, bis auf
stream_options.include_usagebei OpenAI-Streams. - Anbieter außerhalb der vier genannten: Ingest (Weg D) oder Anfrage an uns.