response le retire
ou le réécrit avant que votre agent n’agisse dessus. La décision
d’application est identique sur chaque fournisseur — mêmes règles, mêmes
verdicts, mêmes événements. Ce qui diffère, c’est la forme du fil que
votre client voit une fois que le firewall a agi sur un appel d’outil streamé,
parce qu’OpenAI chat, l’API OpenAI Responses, et /v1/messages Claude natif
frament chacun les appels d’outils différemment.
Cette page est la note ciblée sur ces différences observables par le client.
Elle ne redocumente pas le langage des règles — voir
Règles du Firewall — ni le modèle de stage,
couvert dans Stages. Pour le mécanisme interne
de retenir-et-réassembler partagé par les trois, lisez
Rouages du streaming.
1. Pourquoi le firewall en streaming par fournisseur diffère selon le fil
Sur une réponse non streamée, le firewall voit toute la réponse d’un coup et décide. Sur un stream, l’appel d’outil du modèle arrive comme une séquence de fragments — un nom dans une frame, du JSON d’arguments distillé à travers de nombreuses autres. Un verdict a besoin de l’appel complet (nom et arguments complets), et un fragment d’appel d’outil, une fois transmis, ne peut pas être rétracté. Donc sur chaque fournisseur, la passerelle fait la même chose : elle laisse le contenu ordinaire streamer en live, et retient les frames d’appel d’outil jusqu’à ce que l’appel soit entièrement assemblé. À la fin du stream, elle évalue chaque appel assemblé et n’émet que les survivants — dans la forme d’événement propre à ce fournisseur.Votre texte ne cale jamais. Seules les frames d’appel d’outil sont
retenues. Le contenu de l’assistant, le raisonnement et les frames de role
streament en live et inchangés. La rétention s’applique du premier fragment
d’appel d’outil à la fin de ce tour — de sorte qu’une réponse uniquement de
chat streame exactement comme si aucun firewall n’était attaché.
2. OpenAI chat completions
Sur/v1/chat/completions, les appels d’outils streament comme des fragments
delta.tool_calls keyés par index. La porte retient ceux-là (et la forme
delta.function_call legacy) et, à la frame de clôture, émet les appels
survivants ré-indexés depuis zéro, suivis d’une frame de fin :
| Résultat | Ce que votre client reçoit |
|---|---|
| allow | Les frames retenues d’origine, octet pour octet — transmission directe véritable. |
| sanitize | Un delta tool_calls avec des arguments réécrits, puis finish_reason: "tool_calls". |
| deny (certains appels) | Seulement les appels survivants, puis finish_reason: "tool_calls". |
| deny (tous les appels) | Aucun appel d’outil, puis finish_reason: "stop" — le tour donne l’impression que le modèle a choisi de répondre en texte. |
3. API OpenAI Responses
Le stream natif/v1/responses a son propre modèle d’événements — un appel
d’outil est un item function_call qui s’ouvre avec
response.output_item.added, streame des fragments
response.function_call_arguments.delta, et se complète à
response.output_item.done. Le firewall évalue à done, le premier point où
l’appel est entier :
allow → événements en tampon vidés verbatim
allow → événements en tampon vidés verbatim
Les événements
added / argument-delta / done de l’item sont émis
inchangés une fois l’appel validé.sanitize → coquille d'item + done réécrit
sanitize → coquille d'item + done réécrit
La coquille
added streame, puis un done dont les arguments sont la
version redactée — les fragments d’argument-delta d’origine sont
abandonnés pour que la valeur non redactée ne vous atteigne jamais.deny → item retiré partout
deny → item retiré partout
Les événements en tampon sont abandonnés, et l’item refusé est aussi
filtré de l’objet terminal
response.completed à partir duquel votre
client construit son état final — aucune référence pendante à un appel qui
n’a jamais été exécuté.4. Claude /v1/messages natif
Un stream Anthropic natif est une autre bête : le contenu arrive comme des
blocs indexés — content_block_start → content_block_delta (fragments
input_json_delta) → content_block_stop — clôturés par un message_delta
portant stop_reason. Le firewall retient à partir du premier bloc
tool_use, évalue chacun, et reconstruit les blocs survivants avec des
indices contigus de sorte qu’un bloc retiré ne laisse aucun trou d’index.
L’indice spécifique à Claude est stop_reason. Si chaque bloc tool_use est
refusé, un stop_reason de tool_use promettrait à votre client un appel
d’outil qui n’arrive jamais — donc la passerelle le réécrit en end_turn :
tool_use survivants, ré-indexés de façon
contiguë, et laisse stop_reason: "tool_use" intact.
5. Un exemple concret
La même règle produit la même décision sur chaque fournisseur — seule la forme du fil que votre client lit diffère. Rédigez-la une fois, sur le stageresponse :
rm -rf à chaque fois. Ce que votre client observe :
| Fil | Signal terminal après un retrait complet |
|---|---|
| OpenAI chat | finish_reason: "stop" |
| OpenAI Responses | item absent de response.completed |
| Claude natif | stop_reason: "end_turn" |
6. Ce qui reste constant à travers les fournisseurs
Le fil diffère ; le contrat non :- Les verdicts et les règles sont indépendants du fil.
allow/audit/deny/sanitizesignifient la même chose sur chaque fournisseur. Voir Verdicts. - Sanitize ne touche que les arguments de l’appel d’outil, jamais le contenu qu’un outil renvoie — sur chaque fil. Voir Assainir les réponses.
- Allow est une transmission directe véritable. Quand le firewall ne prend aucune action, les frames retenues sont rejouées comme les octets exacts de l’amont — aucun re-batching, aucun champ spécifique au fournisseur perdu.
- Le mode shadow s’applique partout. Activez-le et les appels d’outils
retenus survivent toujours (rétrogradés en
audit) de sorte que vous pouvez mesurer l’impact d’une politique à travers les fournisseurs avant qu’elle ne change le trafic. Voir Mode shadow.
7. Où cela s’insère
Rouages du streaming
Le mécanisme retenir-assembler-réassembler que chaque fournisseur partage.
Stages
Pourquoi l’application des appels d’outils streamés vit sur la surface
response.Verdicts
Les décisions indépendantes du fournisseur en lesquelles un appel streamé
se résout.
Filtrage des réponses
Filtrer les appels d’outils qu’un modèle émet, stream ou non.
