Skip to main content

Anbieter-Erkennung

FIM One verwendet LiteLLM als universellen Adapter. Die Funktion _resolve_litellm_model() in core/model/openai_compatible.py ordnet die LLM_BASE_URL + LLM_MODEL des Benutzers einem LiteLLM-Modellbezeichner mit einem Anbieter-Präfix zu. Das Präfix bestimmt, wie LiteLLM die Anfrage weiterleitet — natives API-Protokoll (Anthropic Messages API, Gemini, etc.) oder generisches OpenAI-kompatibles /v1/chat/completions. Auflösungsreihenfolge:
  1. Expliziter Anbieter (aus DB-Feld ModelConfig.provider) — höchste Priorität. Wenn der Anbieter einer bekannten Domäne in der URL entspricht, wird keine api_base zurückgegeben (LiteLLM leitet nativ weiter). Andernfalls wird api_base auf die Relay-URL gesetzt.
  2. Domänen-Abgleich gegen KNOWN_DOMAINS — offizielle API-Endpunkte werden anhand des Hostnamens erkannt.
  3. URL-Pfad-Hinweis gegen PATH_PROVIDER_HINTS — häufig auf Relay-Plattformen wie UniAPI, wo /claude oder /anthropic im Pfad das Upstream-Protokoll anzeigt.
  4. Fallback — openai/-Präfix (generisches OpenAI-kompatibles).
Wenn das Anbieter-Präfix ein natives Protokoll ist (anthropic, gemini, etc.) und die URL nicht der offizielle Endpunkt ist, verwendet LiteLLM das native Protokoll, sendet Anfragen aber an die api_base des Relays. Dies bedeutet, dass anbieter-spezifische Verhaltensweisen — einschließlich des unten beschriebenen Bedrock-Prefill-Problems — gelten, unabhängig davon, ob die Anfrage zur offiziellen API oder über ein Relay geht.
Wenn Ihre Relay-URL /claude im Pfad enthält, leitet FIM One automatisch über das native Anthropic-Protokoll weiter. Dies ist normalerweise korrekt (besseres Streaming, Thinking-Unterstützung), bedeutet aber, dass anbieter-spezifische Verhaltensweisen gelten — einschließlich des unten beschriebenen Bedrock-Prefill-Problems.

tool_choice — die vier Modi

Der Parameter tool_choice ist über das OpenAI-Format standardisiert. LiteLLM übersetzt ihn vor dem Senden der Anfrage in das native Protokoll jedes Anbieters. Die Unterscheidung zwischen "auto" und erzwungen ({"type":"function",...}) ist der Kern jedes Kompatibilitätsproblems in FIM One. Diese beiden Modi werden von völlig unterschiedlichen Subsystemen mit unterschiedlichen Anforderungen verwendet.

Wo tool_choice verwendet wird

Zwei Subsysteme verwenden tool_choice, und sie verwenden es auf grundlegend unterschiedliche Weise.

ReAct-Engine — tool_choice=“auto”

Die ReAct-Schleife erfordert, dass das Modell in jeder Iteration entscheidet: ein Werkzeug aufrufen oder eine endgültige Antwort geben. Nur "auto" macht hier Sinn — das Modell wählt frei zwischen der Erzeugung von tool_calls oder Textinhalten. Dies ist mit allen Anbietern, allen Modellen und allen Modi kompatibel, einschließlich erweitertem Denken. Die ReAct-Engine verwendet natives Function Calling (_run_native), wenn abilities["tool_call"] = True, und fällt andernfalls auf den JSON-im-Inhalt-Modus (_run_json) zurück. Beide Modi verwenden "auto" — der Unterschied besteht darin, ob Werkzeuge über den tools-Parameter übergeben oder in der Systemaufforderung beschrieben werden. Weitere Informationen finden Sie unter ReAct-Engine — Dual-Mode-Ausführung.

structured_llm_call — tool_choice=forced

Einmalige strukturierte Extraktion (Schema-Annotation, DAG-Planung, Plananalyse). Erzwingt, dass das Modell eine bestimmte virtuelle Funktion aufruft, und garantiert dadurch eine strukturierte JSON-Ausgabe. Dies ist die Aufrufstelle, die anbieterspezifische Fehler auslöst. structured_llm_call implementiert eine dreistufige Degradationskette. Die drei Stufen sind nach dem Code benannt, und ihre Nummerierung entspricht nicht den vier Garantieebenen, die im folgenden Abschnitt beschrieben werden: Der entscheidende Designunterschied: Der Fallback von structured_llm_call erfolgt zur Laufzeit — es wird dynamisch jede Stufe ausprobiert, und Ausnahmen werden abgefangen, um zur nächsten Stufe überzugehen. Die Modusauswahl der ReAct-Engine erfolgt zur Build-Zeit — sie prüft _native_mode_active einmal zu Beginn und legt sich für die gesamte Schleife auf einen Modus fest. Das bedeutet, dass structured_llm_call anbieterspezifische 400-Fehler transparent abfangen kann, während ReAct darauf angewiesen ist, dass der Modus von Anfang an korrekt ausgewählt wird.

Strukturierte Ausgabe: die vier Garantieebenen

„Strukturierte Ausgabe“ ist ein Begriff für vier verschiedene Mechanismen. Sie alle erzeugen JSON, bieten aber nicht dieselbe Garantie. Der entscheidende Unterschied liegt darin, wo die Einschränkung greift: im Decoder, der kein Token ausgeben kann, das das Schema verbietet, oder im Prompt, der lediglich eine Aufforderung formulieren kann. Der Mechanismus hinter T1 und T2 ist Constrained Decoding. Der Anbieter kompiliert das Schema in eine Grammatik und maskiert bei jedem Decodierungsschritt jedes Token, das die Ausgabe gegenüber diesem Schema unparsebar machen würde. Ein erforderliches Feld kann nicht übersprungen werden, weil das Token zum Schließen des Objekts erst dann zur zulässigen Menge gehört, wenn dieses Feld ausgegeben wurde. Das ist eine andere Art von Aussage als „das Modell wurde freundlich gebeten und hat sich daran gehalten“ – und der Grund, warum T1/T2 bei Temperatur 1 und auch bei kleinen Modellen zuverlässig funktionieren, wo die Befolgung von Prompts dies nicht tut.

T1 und T2 sind nicht austauschbar

Sie bieten über unterschiedliche Kanäle dieselbe Garantie, und der Kanal ist in drei Punkten entscheidend. Die Form des Turns unterscheidet sich. T1 gibt einen Tool-Call-Turn zurück; das Modell hat entschieden zu handeln. T2 gibt eine gewöhnliche Assistant-Nachricht zurück; das Modell hat in einer bestimmten Form geantwortet. Wenn der Aufrufer eine einzelne Extraktion und keine Agentenschleife möchte, bringt T2 dies direkt zum Ausdruck, anstatt für das Modell eine virtuelle Funktion zu erfinden, die es „aufrufen“ soll. Das Erzwingen von T1 kollidiert mit Thinking. Um aus T1 eine schema-gesteuerte Nutzlast zu erhalten, muss die Auswahl in der Regel erzwungen werden, entweder mit required oder mit einer benannten Funktion. Mehrere Provider lehnen eine erzwungene Tool-Auswahl ab, wenn Extended Thinking aktiv ist. Tabelle B weiter unten zeigt, welche das betrifft. Bei T2 gibt es keinen solchen Konflikt: T2 ist eine Antwortbeschränkung und keine Tool-Beschränkung. Daher ist T2 der einzige schema-gesteuerte Pfad, der auch bei aktiviertem Thinking funktioniert. Für eine Bereitstellung, die auf Reasoning-Modellen standardisiert ist, ist das ein praktisches und kein stilistisches Argument für T2. T1 verursacht einen zusätzlichen Durchlauf für die Schema-Anbindung, den T2 nicht benötigt. Eine virtuelle Funktion benötigt einen Namen, eine Beschreibung und eine Entscheidung darüber, ob das Modell den Aufruf ablehnen darf.

Was T2 kostet

Constrained Decoding ist nicht kostenlos, und das Schema, das bereits vorhanden ist, entspricht normalerweise nicht dem Schema, das akzeptiert wird.
  • Eine Teilmenge des Schemas. Der Strict-Modus von OpenAI erfordert bei jedem Objekt additionalProperties: false und bei jeder Eigenschaft einen Eintrag in required; Optionalität wird durch eine Union mit null ausgedrückt und nicht durch das Weglassen aus required. Die Wurzel muss ein Objekt sein. Für die Gesamtzahl der Eigenschaften und die Verschachtelungstiefe gelten Obergrenzen. Die meisten von Hand geschriebenen Schemas müssen bearbeitet werden, bevor sie dafür geeignet sind.
  • Kompilierung der Grammatik. Die erste Anfrage mit einem neuen Schema verursacht auf der Seite des Anbieters eine einmalige Kompilierungslatenz. Durch die Wiederverwendung eines stabilen Schemas lässt sich diese amortisieren; bei der Erzeugung eines neuen Schemas pro Anfrage ist das nicht der Fall.
  • Eine neue Fehlerquelle. Ein Modell, das nicht antworten möchte, gibt statt eines schemaförmigen Objekts eine Ablehnung zurück. Daher benötigt der Aufrufer dafür einen eigenen Zweig.

Was keine Stufe garantiert

Jede Stufe schränkt die Form ein. Keine schränkt die Wahrheit ein. Eine T2-Antwort kann vollständig schema-konform und dennoch sachlich falsch sein. Ein enum-beschränktes Feld gibt einen der zulässigen Werte zurück, selbst wenn keiner davon korrekt ist, denn die Aufgabe des Decoders besteht darin, die Ausgabe innerhalb der Grammatik zu halten, nicht darin, die Antwort zu kennen. Die Schema-Steuerung verhindert Parsing-Fehler und Fehler bei der Feldstruktur. Sie macht jedoch nicht überflüssig, zu prüfen, was der Wert aussagt.
Zwei Nummerierungsschemata, die nicht übereinstimmen. Diese Seite verwendet T1–T4 für die oben genannten Garantie-Stufen. Für die drei Stufen der Degradationskette von structured_llm_call wird Level 1/2/3 verwendet; sie sind nach dem Code benannt (native_fc, json_mode, plain_text). FIM Ones Level 2 ist json_object, also T3, nicht T2. Die Kette hat drei Stufen, weil sie T2 vollständig überspringt, nicht weil es nur drei Stufen gibt.

Wo FIM One einzuordnen ist

Daraus ergeben sich fünf Konsequenzen – und das sind die ehrlichen Einschränkungen des aktuellen Designs.
  1. Die Kette verfügt über keine schema-gesteuerte Stufe, auf die sie zurückfallen kann. Wenn Ebene 1 fehlschlägt, ist die nächste Stufe json_object, was lediglich garantiert, dass der Text geparst werden kann. Es gibt keinen Zwischenschritt, der weiterhin Felder erzwingt.
  2. Die Garantie von Ebene 1 ist schwächer, als der Name vermuten lässt. Ohne strict entspricht native function calling der nicht-strikten Variante aus der Zeit vor 2024: Das Modell hält sich normalerweise an das Schema, wird aber nicht daran gehindert, ein erforderliches Feld auszulassen, einen eigenen Schlüssel zu erfinden oder eine Zeichenfolge zurückzugeben, obwohl eine Zahl deklariert wurde.
  3. Auf anthropic/-Routen ist Ebene 2 nicht wirklich T3. Die Anthropic Messages API verfügt über kein response_format, daher emuliert LiteLLM den JSON-Modus durch das Einfügen eines Assistant-Prefills. Das ist ein Mechanismus auf Prompt-Ebene, wodurch die effektive Garantie zwischen T4 und T3 liegt und nicht bei T3. Die weiter unten dokumentierte Bedrock-Prefill-Falle besteht darin, dass diese Emulation deutlich fehlschlägt. Die weniger offensichtlichen Kosten bestehen darin, dass die Garantie nie derjenigen entsprach, die der Parametername nahelegt.
  4. Das Ergebnis wird anschließend nicht gegen das Schema validiert. jsonschema ist keine Abhängigkeit. Jede Prüfung erfolgt innerhalb der optionalen parse_fn pro Aufrufstelle, und ihre Gründlichkeit variiert je nach Aufrufstelle.
  5. Fehler werden abgefangen, statt sichtbar gemacht zu werden. Fast jeder Aufrufer übergibt default_value, sodass eine erschöpfte Kette ein plausibel aussehendes Objekt zurückgibt, anstatt einen Fehler auszulösen. StructuredCallResult.level_used erfasst, welche Stufe den Wert erzeugt hat, aber keine Aufrufstelle liest diesen Wert aus. Beim default_value-Pfad meldet es außerdem plain_text für eine Kette, die tatsächlich nichts erzeugt hat.
So lässt sich feststellen, auf welcher Stufe man sich tatsächlich befindet. structured_llm_call protokolliert bei INFO eine Zeile pro abgeschlossenem Aufruf. Sie wird erst ausgegeben, nachdem die Daten parse_fn erfolgreich durchlaufen haben, und nennt daher eine Stufe, die tatsächlich erfolgreich war:
Ein vollständiger Fehlschlag wird mit WARNING und level=none outcome=default_value protokolliert. Diese beiden Zeilen über einen Tag hinweg im Datenverkehr zu suchen, ist die einzige Möglichkeit festzustellen, ob das fehlende T2 in einer bestimmten Bereitstellung Auswirkungen hat, da das default_value-Design als Symptom eine mittelmäßige Antwort statt eines Fehlers erzeugt.

Provider-Unterstützung für jede Stufe

Diese Tabelle dokumentiert Upstream-APIs, nicht die Codepfade von FIM One. Jede andere Tabelle auf dieser Seite nennt die Funktion, die das Verhalten implementiert. Diese Tabelle kann das nicht, da FIM One keine T2-Funktion eines Providers verwendet und bei keinem von ihnen strict setzt. Sie ist hier enthalten, damit eine Entscheidung zur Implementierung von T2 auf dem tatsächlich Verfügbaren basiert. Die Fähigkeiten der Anbieter entwickeln sich schnell weiter, und Unterstützung wird häufig zuerst für einige Modelle einer Familie verfügbar, bevor andere folgen. Prüfen Sie daher die Dokumentation des jeweiligen Anbieters erneut, bevor Sie sich auf eine Tabellenzeile verlassen.
Drei Muster sind eine eigene Erwähnung wert, weil sie die Struktur der Tabelle erklären, statt sie nur zu wiederholen. T3 ist universell, T2 nicht. Jeder von FIM One unterstützte Provider bietet json_object, während ungefähr die Hälfte eine Schema-Stufe anbietet. Ein portabler Pfad für Structured Output muss daher auf T3 oder darunter aufbauen. Das erklärt, warum die Kette von FIM One so aussieht. T2 hinzuzufügen bedeutet, ein Flag für die Fähigkeiten jedes einzelnen Modells hinzuzufügen, keinen globalen Schalter. Dauerhaft aktiviertes Thinking schließt tendenziell die T1-Tür. GLM, die Thinking-Modelle von Kimi und deepseek-reasoner schränken eine erzwungene Tool-Auswahl ein oder lehnen sie ab, und Anthropic lehnt sie ab, während Thinking aktiv ist. MiniMax ist das Gegenbeispiel. Wenn diese Tür geschlossen ist, bleibt T2 als einzige schema-gesteuerte Option. Deshalb ist die fehlende Stufe bei einem Deployment chinesischer Modelle von größerer Bedeutung als bei OpenAI. Selbsthosting kehrt die übliche Reihenfolge um. Beschränktes Decoding ist eine Eigenschaft des Serving-Stacks. Daher steht es auf einem lokalen Checkpoint zur Verfügung, dessen Befolgung von Anweisungen deutlich schwächer ist als die eines Frontier-Modells. Die Modelle, die Schema-Gating am dringendsten benötigen, können es am ehesten erhalten.

Die Bedrock-Prefill-Falle

Wenn response_format={"type":"json_object"} für ein Modell übergeben wird, das mit dem anthropic/-Präfix aufgelöst wird, injiziert LiteLLM intern eine Assistant-Prefill-Nachricht, um den JSON-Modus zu simulieren. Die Anthropic Messages API hat keinen nativen response_format-Parameter, daher approximiert LiteLLM dies, indem eine öffnende Klammer als Assistant-Inhalt vorangestellt wird:
Dies funktioniert auf Anthropics direkter API. Jedoch lehnen neuere AWS-Bedrock-Modellversionen jede Konversation ab, deren letzte Nachricht role: "assistant" hat — sie nennen dies „Assistant-Message-Prefill” und werfen:
Dieser Fehler tritt nur auf, wenn alle drei Bedingungen gleichzeitig erfüllt sind:
  1. Das Modell wird mit dem anthropic/-Präfix aufgelöst (über Domain-Matching oder URL-Pfad-Hinweis).
  2. response_format={"type":"json_object"} wird übergeben (der json_mode-Code-Pfad in structured_llm_call).
  3. Das eigentliche Backend ist AWS Bedrock (das Prefill ablehnt).
Bedrock über OpenAI-kompatiblen Endpoint? Wenn Ihr Bedrock-Relay einen OpenAI-kompatiblen /v1/chat/completions-Endpoint bereitstellt (entweder AWS’s eigenes OpenAI-kompatibles Gateway oder einen Drittanbieter-Proxy), und der URL-Pfad enthält nicht /claude oder /anthropic, löst FIM One es mit dem openai/-Präfix auf. LiteLLM behandelt das Backend dann als einen Standard-OpenAI-kompatiblen Server, übergibt response_format direkt ohne Prefill-Injektion, und der Server handhabt JSON-Constraining nativ. Die Prefill-Falle gilt nicht — Sie müssen json_mode_enabled=false nicht setzen.
Dies betrifft NICHT natives Tool-Calling (tool_choice="auto" mit tools=-Parameter). Die Prefill-Injektion erfolgt nur für response_format. Die ReAct-Agent-Ausführung ist völlig unbeeinträchtigt.
Wenn sowohl Level 1 (native_fc) als auch Level 2 (json_mode) auf Bedrock fehlschlagen, stellt sich das System auf Level 3 (plain_text) wieder her. Das unten beschriebene Flag json_mode_enabled eliminiert den verschwendeten Level-2-Aufruf.

Die Lösung: json_mode_enabled

Ein modellbezogenes json_mode_enabled-Flag steuert, ob Level 2 (json_mode) überhaupt versucht wird:
  • Datenbankkonfigurierte Modelle: In Admin → Modelle → Erweiterte Einstellungen umschalten. Das Flag wird in ModelProviderModel.json_mode_enabled gespeichert (Standardwert TRUE).
  • ENV-konfigurierte Modelle: LLM_JSON_MODE_ENABLED=false in Ihrer Umgebung festlegen.
  • Auswirkung: Wenn das Flag deaktiviert ist, gibt abilities["json_mode"] False zurück → response_format wird nie übergeben → kein Prefill → Bedrock funktioniert. Die Degradationskette wird zu native_fc → plain_text und überspringt den zum Scheitern verurteilten json_mode-Aufruf vollständig.
  • Kosten des Überspringens: Das Modell gibt in der Praxis weiterhin gültiges JSON zurück, da der System-Prompt dies anfordert und extract_json() frei formatierten Inhalt bei modernen Modellen zuverlässig parst. Verloren geht nicht die Ausgabe, sondern die Garantie: Die Kette endet nun bei T4, wo ausschließlich der Prompt das Ergebnis einschränkt. Bei einer anthropic/-Route ist dieser Verlust weniger gravierend, als es zunächst scheint, da der emulierte JSON-Modus ohnehin keine Garantie auf Decoderebene bot.

Thinking models + forced tool_choice

Mehrere Anbieter lehnen ein erzwungenes tool_choice ab, während Extended Thinking aktiv ist, mit der Begründung, dass das Festlegen eines bestimmten Funktionsaufrufs der Freiheit des Modells widerspricht, zunächst zu überlegen:
Dies ist eine anbieterspezifische Regel, keine allgemeingültige Regel für Thinking Models. Anthropic erzwingt sie auf Protokollebene und Moonshot (Kimi) verhält sich genauso, aber MiniMax denkt bei jedem Aufruf und akzeptiert trotzdem ein erzwungenes Tool Choice. Tabelle B in der Provider Capability Matrix dokumentiert das Ergebnis für jeden Anbieter; verallgemeinern Sie nicht von einer Zeile auf eine andere. Für Anthropic-Modelle löst structured_llm_call den Konflikt automatisch, indem es auf der nativen-FC-Ebene reasoning_effort=None übergibt, was Thinking für diesen einen Aufruf ausschaltet (structured.py::_call_llm). Strukturierte Ausgabe benötigt Schema-Konformität, nicht tiefes Reasoning, daher ist das Ausschalten von Thinking dort sowohl korrekt als auch kostengünstiger. Wenn Thinking nicht über die API ausgeschaltet werden kann, schlägt native_fc bei jedem strukturierten Aufruf mit einem 400-Fehler fehl und kostet etwa zehn Sekunden, bevor die Chain zu json_mode fällt. Kimi ist der häufige Fall: Mit aktiviertem Thinking wird nur auto unterstützt, und ein erzwungenes Tool Choice erfordert das Ausschalten von Thinking, das Moonshot nur über die Modell-ID verfügbar macht (kimi-k2 hat es aus, kimi-k2.5 und kimi-k2-thinking haben es an). FIM One hat keinen Parameter, der es umschaltet, daher ist das Mittel das tool_choice_enabled-Flag unten.

Die Lösung: tool_choice_enabled

Ein Pro-Modell-Flag tool_choice_enabled steuert, ob Level 1 (native_fc) jemals versucht wird:
  • DB-konfigurierte Modelle: Umschalter in Admin → Models → Advanced → “Native Function Calling”. Das Flag wird auf ModelProviderModel.tool_choice_enabled gespeichert (Standard TRUE).
  • ENV-konfigurierte Modelle: setzen Sie LLM_TOOL_CHOICE_ENABLED=false in Ihrer Umgebung.
  • Auswirkung: wenn deaktiviert, gibt abilities["tool_choice"] False zurück → die Degradationskette beginnt bei Level 2 (json_mode) oder Level 3 (plain_text) und überspringt native_fc vollständig. Dies eliminiert die ~10s Strafe pro strukturiertem Aufruf für inkompatible Modelle.
  • ReAct-Agent nicht betroffen: tool_choice_enabled steuert nur die erzwungene Werkzeugauswahl in structured_llm_call. Die ReAct-Engine verwendet tool_choice="auto" (Modell entscheidet frei), was mit allen Modellen unabhängig von dieser Einstellung funktioniert.
tool_choice_enabled und tool_call sind separate Ability-Flags. tool_call (immer True für OpenAICompatibleLLM) steuert, ob Werkzeuge dem Modell überhaupt übergeben werden — das Deaktivieren würde den ReAct-Agent beschädigen. tool_choice steuert nur, ob erzwungene Werkzeugauswahl für die Strukturierte-Ausgabe-Extraktion versucht wird.
tool_choice="auto" wird durch den Thinking-Modus nicht beeinflusst. Die ReAct-Engine verwendet ausschließlich "auto", daher funktioniert die Agent-Ausführung mit aktiviertem Thinking.
Setzen Sie NICHT abilities["tool_call"] = False, um diese Einschränkung zu vermeiden. Das würde ReActs _run_native-Modus deaktivieren (der tool_choice="auto" verwendet und mit Thinking gut funktioniert) und würde es in den weniger zuverlässigen _run_json-Modus zwingen.
Hinweis zur Provider-Migration: Einige Drittanbieter-Relays löschen stillschweigend nicht unterstützte Parameter wie reasoning_effort (drop_params=True), daher wird Thinking nie aktiviert, auch wenn es konfiguriert ist. Bei der Migration zu einem Provider, der Thinking ordnungsgemäß unterstützt (Bedrock, direkte Anthropic API), stellt reasoning_effort=None in native_fc konsistentes Verhalten sicher. Es ist keine Benutzeraktion erforderlich — strukturierte Ausgabe funktioniert identisch über alle Provider hinweg.

Provider Capability Matrix

This section is the authoritative record of what each provider supports and what FIM One does about it. Every row names the function that implements the behaviour, so any claim here can be checked against the code. Other pages link here instead of repeating the data; when the code changes, this section changes with it. A row describes a provider’s protocol, not a single model. Where models inside one family differ (DeepSeek chat against reasoner, Kimi with thinking on against off), the cell says so.

Tabelle A: Protokollrouting

Wie aus einer konfigurierten base_url und einem model ein LiteLLM-Aufruf wird und was passiert, wenn die erste Wahl der Schnittstelle nicht verfügbar ist. Wie GPT-5.x ein Protokoll auswählt. FIM_GPT5_RESPONSES_MODE legt es fest: native (Standard) kommuniziert direkt über litellm.aresponses mit /v1/responses, bridge verwendet die Chat-Completions-Übersetzung von LiteLLM und off erzwingt einfache Chat Completions. Der native Pfad ist nötig, weil die Bridge an entscheidender Stelle verlustbehaftet ist: Sie verwirft die Reasoning-Elemente, sodass ein GPT-5.x-Agent seine Gedankenkette bei jeder Tool-Runde erneut ableitet. Durch die direkte Kommunikation über das Protokoll lassen sich diese Elemente wiedergeben. Ein Aufruf, der explizit reasoning_effort=None übergibt – wie es structured_llm_call und die Prüfungen des Abschlusssignals tun –, bleibt bei Chat Completions, da bei einem Aufruf ohne Reasoning kein Reasoning-Zustand erhalten bleiben muss. Eine Ausnahme bilden Modelle, bei denen sich Reasoning nicht deaktivieren lässt (gpt-6.1-*, gpt-6-astra): Ihre Chat Completions lehnen Funktionstools unabhängig von reasoning_effort ab. Daher bleiben diese Aufrufe bei /v1/responses und verwenden die niedrigste Stufe, low. Aus demselben Grund lassen off oder ein Endpunkt ohne /v1/responses bei diesen Modellen keine Tool-Aufrufe zu. Zwei Eigenschaften dieser nativen Anfrage sind entscheidend und lassen sich leicht falsch umsetzen:
  • store=false sorgt dafür, dass die Unterhaltung beim Upstream zustandslos bleibt, und include=["reasoning.encrypted_content"] fordert die Rückgabe der verschlüsselten Nutzdaten an. Ohne das include werden leere Reasoning-Elemente zurückgegeben, und ihre Wiedergabe bleibt unbemerkt wirkungslos.
  • Bei einem wiedergegebenen Reasoning-Element muss die serverseitige id entfernt werden. Bei store=false wird Upstream nichts gespeichert. Wird die ID zurückgesendet, erhält man Item with id 'rs_...' not found. Items are not persisted when 'store' is set to false. Der encrypted_content-Blob enthält den Zustand bereits selbst, daher geht durch das Entfernen der ID nichts verloren (sanitize_reasoning_item).
Bedrock. Bei Claude auf Bedrock ist entscheidend, zu welcher Route das Modell aufgelöst wird, nicht, dass Bedrock es hostet. Wird es über ein Relay mit anthropic/-Route aufgerufen, übernimmt es das Anthropic-Protokollverhalten, einschließlich des JSON-Modus mit Assistant-Prefill von LiteLLM, den neuere Bedrock-Versionen ablehnen. Wird es über ein OpenAI-kompatibles Gateway aufgerufen, wird es als openai/ aufgelöst, es wird kein Prefill eingefügt und json_mode_enabled kann aktiviert bleiben.

Tabelle B: Konflikte und Workarounds

FIM One gibt drei der vier tool_choice-Zustände aus: auto aus der ReAct-Schleife (react.py::_run_native), eine benannte Funktion aus strukturierter Ausgabe (structured.py::_call_llm) und none, wenn die Finish-Signal-Antwort die Tools-Payload wiedergeben. Keine Aufrufstelle gibt required aus; diese Spalte dokumentiert die Einschränkung des Anbieters bei nicht-auto-Tool-Auswahl, die sowohl auf required als auch auf eine benannte Funktion zutrifft.

Tabelle C: Thinking-Protokoll

LLM_REASONING_EFFORT akzeptiert low, medium und high; jeder andere Wert wird als nicht gesetzt gelesen (deps.py::_reasoning_effort). Was FIM One dann auf die Leitung bringt, ist anbieterabhängig, und das ist das, was diese Tabelle dokumentiert. Die Spalte „Replay” ist der Rückgabewert von reasoning_replay_policy, eine kleine geschlossene Menge von vier Zuständen statt einer anbieterspezifischen Liste. unsupported und informational_only erzeugen die gleichen Bytes auf der Leitung: beide entfernen reasoning_content und signature aus der ausgehenden Historie. Sie unterscheiden sich in der Absicht, daher ist ein Modell, das eindeutig denkt, aber in unsupported landet, eine Lücke in der Fragment-Tabelle statt eines Live-Bugs.

Relay/Proxy-Fallstricke

Gateways von Drittanbietern schlagen auf Weise fehl, die ein direkter Anbieter nicht tut, und die meisten dieser Fehler sind stumm. Jede Zeile unten paart das Symptom mit seinem Mechanismus und mit dem, was FIM One bereits dagegen tut.
Support-Grenze. FIM One garantiert das auf dieser Seite dokumentierte Verhalten für First-Party-Endpunkte: OpenAIs eigene API, Anthropic, Google und jeden Anbieter, der seine eigenen Modelle direkt bereitstellt. Relays von Drittanbietern werden nach bestem Bemühen unterstützt und sind nicht von dieser Garantie abgedeckt, da das, was ein Relay mit einer Anfrage tut, außerhalb unserer Kontrolle liegt und häufig außerhalb seiner eigenen Dokumentation. Ein Relay kann einen Parameter verwerfen, den Verlauf umschreiben, einen Cache-Breakpoint entfernen oder ein Protokoll beantworten, das es nur teilweise implementiert, und in den meisten dieser Fälle gibt es eine 200 zurück, anstatt einen Fehler zu machen.Dies ist eine Aussage darüber, was wir versprechen, keine Einschränkung dessen, was läuft. FIM One führt keine Allowlist genehmigter Hosts und nichts hier ist an eine Domain gebunden. Die Fähigkeit wird dadurch entschieden, was ein Endpunkt tatsächlich tut: Eine fehlende Route antwortet mit 404 und wird gespeichert, ein ignoriertes include ergibt leere Reasoning-Elemente und die Wiedergabe wird zu einem No-Op, und eine abgelehnte Anfrage fällt für diesen Aufruf zurück. Das Testen des Endpunkts ist genauer als das Ableiten seiner Fähigkeiten aus seinem Hostnamen, und es ist der einzige Ansatz, der für Azure OpenAI, Enterprise-Gateways und selbst gehostete Proxys funktioniert, die das Protokoll korrekt implementieren.Wenn sich ein Relay auf eine Weise verhält, die die Fallbacks nicht abfangen, fixieren Sie das Protokoll selbst mit FIM_GPT5_RESPONSES_MODE (bridge oder off) oder den Pro-Modell-Toggles tool_choice_enabled und json_mode_enabled, und reproduzieren Sie gegen den First-Party-Endpunkt, bevor Sie es als FIM One-Bug einreichen.

Empfohlene Pro-Modell-Konfiguration

Sowohl tool_choice_enabled als auch json_mode_enabled können pro Modell in Admin → Models → Advanced settings umgeschaltet werden. Die Standardwerte, beide TRUE, sind für die meisten Anbieter korrekt; passen Sie diese nur an, wenn Sie Fehler oder verschwendete Latenz sehen. Welche Anbieter eine Anpassung benötigen, ist in Tabelle B oben aufgezeichnet, und die Pro-Modell-Ansicht, die ein Operator ausfüllt, befindet sich in Model Management.
Wann ändern: Wenn Sie structured_llm_call: native_fc call raised Warnungen in Ihren Logs gefolgt von erfolgreicher json_mode Extraktion sehen, profitiert das Modell nicht von native_fc. Deaktivieren Sie “Native Function Calling” für dieses Modell, um den verschwendeten API-Aufruf zu eliminieren (~10s pro strukturierter Ausgabeanfrage).
ENV-level Overrides gelten für alle Modelle, die über Umgebungsvariablen konfiguriert sind (nicht Admin UI):

Reasoning effort and thinking configuration

FIM One stellt zwei Umgebungsvariablen zur Steuerung von erweitertem Denken / Reasoning bereit: Zwei Verhaltensweisen folgen automatisch, sobald Thinking aktiviert ist, und keine erfordert Benutzerkonfiguration:
  1. Temperatur wird für Sie verwaltet. Bei einer anthropic/-Route mit aktivem Thinking setzt _build_request_kwargs temperature auf 1.0, was Bedrock verlangt. Modelle, die Sampling-Parameter vollständig ablehnen (Opus 4.7 und 4.8, Fable 5, Mythos 5), haben temperature vollständig aus der Anfrage entfernt, unabhängig davon, ob Thinking aktiv ist oder nicht. Setzen Sie nicht manuell LLM_TEMPERATURE=1 dafür.
  2. GPT-5.x hält Tools und Reasoning zusammen, wo möglich. FIM One prüft zuerst die Responses-Bridge für GPT-5.x, da dies die einzige Oberfläche ist, wo die beiden kombiniert werden. Ein Endpoint ohne nutzbaren /v1/responses-Route fällt auf Chat Completions zurück, das Ergebnis wird pro Endpoint gecacht, und auf diesem Pfad sendet eine Anfrage mit tools ein explizites reasoning_effort von none. Das Feld wegzulassen ist nicht gleichwertig, da der Server-Standard nicht none ist.

Defensive Parsing für strukturierte Ausgabe

Auch wenn native_fc korrekt funktioniert, enthält die Pipeline für strukturierte Ausgabe eine defensive Parsing-Schicht, um Grenzfälle von beliebigen Anbietern oder Kompatibilitätsschichten zu behandeln. Der _dict_to_steps-Parser des DAG-Planers behandelt drei häufige Grenzfälle:
  1. Einzelnes Objekt statt Array. Einige Modelle geben {"steps": {"id": "1", "task": "..."}} (ein einzelnes Step-Objekt) statt {"steps": [{"id": "1", "task": "..."}]} (ein Array) zurück. Der Parser erkennt dies, indem er auf id- oder task-Schlüssel prüft und das Objekt in eine Liste einbindet.
  2. Doppelt kodierter JSON-String. Wenn strukturierte Ausgabe auf json_mode zurückfällt (das keine Schema-Erzwingung hat), geben einige Anbieter den steps-Wert als JSON-String statt als natives Array zurück – z. B. {"steps": "[{\"id\": \"1\", ...}]"}. Dieser String kann auch Zeilenumbrüche enthalten (aus der Formatierung des Modells), die Standard-json.loads unterbrechen. Der Parser verwendet extract_json_value() (das _repair_json_strings enthält), um folgende Fälle zu behandeln:
    • Literale Zeilenumbrüche innerhalb von JSON-Stringwerten
    • Ungültige Escape-Sequenzen (häufig bei LaTeX- oder Code-Inhalten)
    • Andere Serialisierungsbesonderheiten von Kompatibilitätsschichten
  3. Fehlender steps-Wrapper. Das Modell kann einen einzelnen Step als Top-Level-Objekt ohne den steps-Wrapper-Schlüssel zurückgeben. Der Parser erkennt id und task auf der Root-Ebene und bindet sie entsprechend ein.
Unter normalen Bedingungen gibt native_fc korrekt strukturierte Tool-Call-Argumente zurück und diese Grenzfälle treten nicht auf. Die defensiven Parser existieren als Sicherheitsnetz für benutzerdefinierte BaseLLM-Unterklassen, ungewöhnliche Anbieterverhalten oder Fallback-Szenarien, in denen strukturierte Ausgabe zu json_mode oder plain_text degradiert.

Prompt-Caching (anbieterübergreifend)

FIM One implementiert Anthropics explizites Prompt-Caching über cache_control-Breakpoints und profitiert gleichzeitig vom automatischen Prefix-Caching aller anderen Anbieter durch die Prompt Section Registry. Das Ziel ist ein einziger Prompt-Assembly-Pfad, der über alle Anbieter hinweg funktioniert, ohne dass sich die Prompt-Form bei jedem Aufruf unterscheidet.

Architektur

Das Modul fim_one.core.prompt stellt drei Primitive bereit:
  • PromptSection — ein benanntes Fragment mit entweder statischem content: str oder dynamischem content: Callable
  • PromptRegistry — ein memoized Store (statische Abschnitte werden einmal gerendert, dynamische Abschnitte werden pro Aufruf neu gerendert)
  • DYNAMIC_BOUNDARY — ein Sentinel-Marker, den die Registry zwischen dem letzten statischen Abschnitt und dem ersten dynamischen einfügt, damit Aufrufer den gerenderten Prompt am Cache-Breakpoint aufteilen können
System-Prompts für ReAct (JSON-Modus, nativer Function-Calling-Modus, Synthese) werden aufgeteilt in:
  • Statisches Präfix (~95% des Prompts) — Identität, Kernrichtlinien, Tool-Beschreibungen
  • Dynamisches Suffix — aktuelles Datum/Uhrzeit, sprachspezifische Direktive pro Anfrage, Handoff-Kontext

Funktionserkennung

fim_one.core.prompt.caching.is_cache_capable(model_id) gibt True zurück, wenn die Modell-ID eines der folgenden Elemente enthält: claude, anthropic, bedrock/anthropic, vertex_ai/claude. Diese Provider erhalten zwei role="system"-Nachrichten mit cache_control: {"type": "ephemeral"} in der ersten (statischen) Nachricht. Alle anderen Provider erhalten eine einzelne verkettete Systemnachricht ohne cache_control-Feld — notwendig, da Nicht-Anthropic-Endpunkte das Feld entweder ablehnen oder stillschweigend ignorieren, und das Senden über einige Relays führt zu 400 unknown parameter-Fehlern.

Anbieterübergreifende Abdeckung

Die PromptRegistry kommt jedem Anbieter mit automatischem Präfix-Caching „kostenlos“ zugute: Da der statische Teil über Aufrufe hinweg byte-identisch bleibt (das aktuelle Datum und die aktuelle Uhrzeit stehen im dynamischen Suffix und nicht im Präfix), stimmt der Hash bei jedem Anbieter mit automatischem Caching überein und erzielt einen Cache-Treffer. Deshalb ist die Registry ein grundlegender modellunabhängiger Vorteil, noch bevor man das speziell für Anthropic vorgesehene cache_control berücksichtigt.

Observability

Every chat/* response’s done_payload now includes:
TurnProfiler emits a structured log line per turn: turn_cache summary | model=claude-sonnet-4-6 | read_tokens=1067 | create_tokens=0 | saved_input_tokens=961 (~90%). This also functions as a relay honesty probe — if you route through an API relay, compare actual billed input vs read_tokens to detect whether the relay strips cache_control or keeps the 0.10× discount. No dollar estimate is returned at the LLM layer — pricing and relay markup are applied above, so the LLM layer only returns objective token counts.

Multi-turn Cache ROI

Gemessen auf Claude 4 ReAct Turns mit dem Standard-Agent-Prompt: Ein 10-Iterations-ReAct-Lauf mit 10 Tools spart ~8.640 Input-Tokens pro Turn nach dem ersten (9 Cache-Treffer × 1067 Tokens × 90%). Anthropic berechnet 1,25× für Cache-Schreibvorgänge beim ersten Aufruf, daher ist die Gewinnschwelle beim zweiten Aufruf — Single-Shot-Abfragen profitieren nicht davon.

Reasoning Replay Policy (modelloses Reasoning)

Extended Thinking / Reasoning Blocks verhalten sich bei verschiedenen Anbietern unterschiedlich. Eine einheitliche Serialisierungsrichtlinie verstößt gegen Protokollverträge und automatische Prefix-Caches. fim_one.core.prompt.reasoning.reasoning_replay_policy(model_id) gibt einen von vier Werten zurück und steuert ChatMessage.to_openai_dict(replay_policy=...) in OpenAICompatibleLLM._build_request_kwargs().

Vier Richtlinien

  • anthropic_thinking — Claude-Familie (einschließlich anthropic/, bedrock/anthropic, vertex_ai/claude). Thinking Blocks MÜSSEN mit signature replayed werden; Anthropic lehnt nachfolgende Turns ab, wenn die Signature fehlt oder verändert ist.
  • informational_only — Modelle, die CoT ausgeben, aber KEINE Replay erwarten: DeepSeek Reasoning Mode (deepseek-reasoner auf V3.2 und die älteren deepseek-r1 und R1-Distill IDs, die die Fragment-Tabelle noch abgleicht), Qwen QwQ, Gemini flash-thinking, OpenAI o1 / o3 / o4. Ihre Dokumentation besagt explizit „sende reasoning_content nicht in der Message History zurück”. Es trotzdem zu senden:
    • Verstößt gegen den Provider-Vertrag (kann in zukünftigen Versionen zu Ablehnungen führen)
    • Invalidiert automatisch ihren Prefix Cache stillschweigend — Message Bytes mutieren bei jedem Turn und brechen den Hash
  • openai_responses — GPT-5.x, abgeglichen am gpt-5 Fragment. Sein Reasoning State ist kein Text, sondern eine Sequenz undurchsichtiger Items mit verschlüsselten Payloads, und nur /v1/responses hat einen Slot dafür. Bei diesem Protokoll werden die Items wörtlich replayed, was die Chain of Thought des Modells über Tool Rounds hinweg erhält. Die lesbare Zusammenfassung wird trotzdem aus ausgehenden Requests gelöscht, daher verhält sich dies beim Chat-Completions Fallback genau wie informational_only. Wird vor den Informations-Fragmenten geprüft, deren generischer reasoning Eintrag sonst proxy-getaggte GPT-5 IDs aufschlucken würde.
  • unsupported — der Catch-All: Modelle ohne Reasoning-Fähigkeit (GPT-4o, Gemini 1.5, Mistral, Llama) und Reasoning-Modelle, deren ID mit keinem Fragment abgleicht (GLM, MiniMax, Kimi, Doubao). Kein Feld sollte in beide Richtungen replayed werden, daher setzt diese Richtlinie die gleichen Bytes auf den Draht wie informational_only. Es ist auch der sichere Standard für unbekannte Model IDs.
Der lesbare reasoning_content und die undurchsichtigen reasoning_items sind unabhängige Felder auf ChatMessage. to_openai_dict() serialisiert die Items überhaupt nie, daher sind sie strukturell unfähig, auf einen Chat-Completions Request zu lecken, was auch immer die Richtlinie sagt.

Durchsetzung

Die gesamte Richtlinienbewertung erfolgt an einer Stelle (_build_request_kwargs). ChatMessage.to_openai_dict(replay_policy=None) behält die A3-permissive Standardeinstellung bei, damit unkoordinierte Aufrufer nicht zurückgehen. Die Cross-Provider-Testmatrix befindet sich in tests/test_reasoning_replay_policy.py mit umgekehrten Assertions, die beweisen, dass Nicht-Anthropic-Anfragen reasoning_content NICHT durchsickern lassen.

Für Benutzer

Sowohl Feature- als auch Bug-Verhalten ist automatisch — Sie müssen nichts konfigurieren. Workflow-Implikationen:
  • Wenn Sie Agenten zwischen Claude und DeepSeek in derselben Konversation wechseln, wird der Verlauf mit Thinking Blocks intakt gespeichert; beim nächsten Turn passt sich die ausgehende Nachrichtenform an das aktuelle Modell an.
  • Wenn Sie einen Proxy / eine benutzerdefinierte BaseLLM-Unterklasse verwenden, stellen Sie sicher, dass seine Modell-ID erkennbar ist (enthält eines der Fragmente) oder die Standard-unsupported-Richtlinie wird angewendet — was sicher ist, aber bedeutet, dass Claude hinter einem ungewöhnlichen Proxy möglicherweise die Thinking-Wiedergabe verliert. Fügen Sie das Modell-ID-Fragment zu _CACHE_CAPABLE_MODEL_FRAGMENTS (in core/prompt/caching.py) und/oder zur Reasoning-Richtlinien-Suche hinzu.

Fehlerbehebung

“This model does not support assistant message prefill” Bedrock + json_mode. Zwei Lösungen: (1) setzen Sie LLM_JSON_MODE_ENABLED=false oder deaktivieren Sie JSON Mode in den Admin-Modelleinstellungen; oder (2) wenn Ihr Bedrock-Provider einen OpenAI-kompatiblen /v1/chat/completions-Endpunkt anbietet, wechseln Sie dorthin — FIM One löst es als openai/ auf und die Prefill-Injektion tritt nie auf. “Thinking may not be enabled when tool_choice forces tool use” / “tool_choice ‘specified’ is incompatible with thinking enabled” Bei Anthropic-Modellen deaktiviert structured_llm_call das Thinking für native_fc-Aufrufe automatisch. Wenn Thinking nicht über die API ausgeschaltet werden kann, wie bei kimi-k2.5 und kimi-k2-thinking oder deepseek-reasoner, deaktivieren Sie “Native Function Calling” in den erweiterten Einstellungen des Modells, oder setzen Sie LLM_TOOL_CHOICE_ENABLED=false global. Die Degradationskette überspringt native_fc und extrahiert strukturierte Ausgaben stattdessen über json_mode oder plain_text. Überprüfen Sie Tabelle B der Provider Capability Matrix, bevor Sie davon ausgehen, dass ein Thinking-Modell dieses Problem hat; MiniMax hat es nicht. “DAG pipeline failed: LLM ‘steps’ is not an array” Das LLM hat das Feld steps als String oder einzelnes Objekt statt als Array zurückgegeben. Dies bedeutet typischerweise, dass die strukturierte Ausgabe auf json_mode zurückgefallen ist (das keine Schema-Erzwingung hat). Überprüfen Sie das Protokoll auf structured_llm_call: level=xxx — wenn es json_mode statt native_fc anzeigt, schlägt native_fc stillschweigend fehl. Wenn Sie eine benutzerdefinierte BaseLLM-Unterklasse verwenden, überprüfen Sie, dass sie das Kwarg reasoning_effort akzeptiert. ReAct fällt unerwartet auf JSON Mode zurück Überprüfen Sie, dass abilities["tool_call"] des Modells True ist. Dies ist immer True für OpenAICompatibleLLM, aber eine benutzerdefinierte BaseLLM-Unterklasse könnte es überschreiben. Überprüfen Sie mit dem Modelldetail-Endpunkt in der Admin-API. structured_llm_call erschöpft alle Ebenen und wirft StructuredOutputError Das Modell konnte auf keiner Ebene gültiges JSON erzeugen. Dies ist selten bei modernen Modellen. Überprüfen Sie: (1) das Schema ist gültiges JSON Schema, (2) das Modell hat genug max_tokens, um die vollständige Antwort zu erzeugen, (3) der System-Prompt widerspricht nicht den Schema-Anweisungen. Der DAG-Planer und Analyzer bieten beide default_value-Fallbacks, daher wird dieser Fehler nur von Aufrufstellen weitergegeben, die Defaults explizit weglassen.