Migration

Basis-URL und Schlüssel genügen, um einen OpenAI-Client umzustellen. Diese Seite nennt auch, was die API nicht tut, denn eine Migration, die das erst im Produktivbetrieb entdeckt, ist keine Migration.

Die Drop-in-Form

Die Basis-URL des Clients auf https://api.llmeu.com/v1 richten und einen API-Schlüssel aus der Konsole verwenden. Die Anfrageform ändert sich nicht.

from openai import OpenAI

client = OpenAI(base_url="https://api.llmeu.com/v1", api_key="llmeu_live_xxxxxxxx.your-secret")

resp = client.chat.completions.create(
    model="llmeu-auto",
    messages=[{"role": "user", "content": "Say hello from EU hosting."}],
)
print(resp.choices[0].message.content)

Was der Chat-Endpunkt akzeptiert

Der Anfrage-Validator in apps/api/src/app.mjs prüft Folgendes; was er nicht prüft, wird durchgereicht oder ignoriert statt abgelehnt, damit ein neuerer Client weiter funktioniert.

  • model und messages sind erforderlich; messages muss ein nicht leeres Array sein.
  • Jede Nachricht braucht eine Rolle (system, user, assistant, tool oder developer) und content als Zeichenkette oder Array von Teilen.
  • stream muss, wenn vorhanden, ein boolescher Wert sein.
  • temperature, top_p, max_tokens, presence_penalty, frequency_penalty, seed und n müssen, wenn vorhanden, Zahlen sein.
  • max_tokens muss mindestens 1 sein.
  • n muss genau 1 sein; alles andere wird abgelehnt.
  • stop darf eine Zeichenkette oder ein Array von Zeichenketten sein.
  • tools muss, wenn vorhanden, ein Array sein.
  • llmeu muss ein Objekt sein; erforderlich ist es nie.
  • tool_choice und response_format werden zusammen mit den obigen Feldern an das Backend weitergegeben; ein unbekanntes Zusatzfeld wird nicht abgelehnt.

Anfrage-Erweiterungen unter llmeu

Ein namensraumgebundenes llmeu-Objekt im Anfragekörper; ein Client, der unbekannte Felder ignoriert, kann es gefahrlos senden.

Feld Wert Wirkung
residencyeu-hosted | eu-owned | de | anyEngt das Routing auf Endpunkte dieser Residenz ein; eine leere zulässige Menge ergibt 409 no_endpoint_for_policy statt einer Erweiterung des Filters.
taskein AufgabennameDie Aufgabe, auf die der Router das Modell abstimmt.
retentionzeroNur zero ist implementiert. Eine Anfrage oberhalb der Richtlinie ergibt 403 retention_exceeds_policy; eine erlaubte effektive Aufbewahrung ungleich zero ergibt 501 retention_not_implemented.
data_classpublic | internal | confidential | restrictedWird gegen die erlaubten Datenklassen der Richtlinie geprüft: eine Klasse außerhalb ergibt 403 data_class_not_allowed, eine unbekannte Klasse 400 invalid_data_class.
policy_iduuidBenennt eine Richtlinie derselben Organisation; eine unbekannte ID ergibt 404 policy_not_found.
max_usd_per_1meine ZahlEine Preisobergrenze pro Million Tokens für diese Anfrage.
allow_partnerstrue | falseOb für diese Anfrage Partner-Endpunkte verwendet werden dürfen.

Unbekannte Schlüssel innerhalb von llmeu werden ignoriert; ein bekannter Schlüssel mit ungültigem Wert wird abgelehnt, denn eine falsche data_class muss geschlossen fehlschlagen statt still keine Datenklasse zu bedeuten.

curl https://api.llmeu.com/v1/chat/completions \
  -H "Authorization: Bearer $LLMEU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "llmeu-auto",
    "messages": [{"role": "user", "content": "Summarise this contract."}],
    "llmeu": {"residency": "eu-owned", "data_class": "confidential", "retention": "zero"}
  }'

Was es nicht gibt

Diese Antworten sind beabsichtigt; ein Client, der sie erwartet, sieht den Fehlercode statt einer stillen Ersetzung.

Anfrage Ergebnis
POST /v1/completions501 not_implemented — der alte Completions-Endpunkt wird nicht angeboten; /v1/chat/completions verwenden.
n > 1400 unsupported_n — nur n = 1 wird unterstützt.
POST /v1/embeddingsEmbeddings brauchen ein Embedding-Modell; die standardmäßig angefragte ID ist bge-m3. Sie werden derzeit vom deterministischen lokalen Backend bedient, dessen Antwort llmeu.mock: true trägt und den Hinweis, dass deterministische Platzhaltervektoren zurückgegeben werden und in dieser Bereitstellung keine Embedding-Gewichte laufen.
/v1/files, /v1/batches, /v1/assistants, /v1/fine_tuning, /v1/audio, /v1/images404 unknown_route — überhaupt nicht geroutet; ein /v1-Pfad, den der Router nicht kennt, ergibt 404 unknown_route, jeder andere Pfad 404 not_found.

Daten wieder herausbekommen

Der Kontoexport me.json enthält ausgewählte Kontofelder, Organisationen, API-Schlüsselmetadaten ohne Geheimnisse, aktuelle Richtlinien, die neuesten 200 Traces je Organisation und tägliche Nutzungsaggregate für 365 Tage. Er ist keine vollständige Kopie aller personenbezogenen Daten und keine historische Richtlinienfassung.

Der NDJSON-Audit-Export umfasst standardmäßig 7 Tage, höchstens 90 Tage und maximal 5000 Traces; sein Manifest enthält counts.truncated, das einen Export markiert, der die Grenze erreicht hat.

GET /app/exports/me.json
GET /app/exports/audit.ndjson?days=7
GET /app/exports/traces.json
GET /app/exports/usage.json
GET /app/exports/policies.json

Das Löschen des Kontos erfolgt auf der Profilseite und unterliegt der Eigentumsregel auf der Konsolenseite.

Ein Sitzungs-Cookie ist keine API-Berechtigung

Eine Browser-Sitzung authentifiziert die Konsole, nicht die API. Ein OpenAI-Client, der eine sendet, wird mit 403 api_key_required abgelehnt und muss einen API-Schlüssel verwenden.

Eine Ablehnung nennt hier einen Code, auf den eine Migration verzweigen kann, und der llmeu-Block jeder Antwort sagt, was tatsächlich geschah.