Repository navigation
Appariement tool_calls/tool : un stop en pleine série peut bloquer la conversation - #171
Merged
Merged
Conversation
…la conversation Un arrêt entre deux appels d'outil laissait dans la vue modèle l'assistant porteur de TOUS ses tool_calls et une partie seulement des réponses `tool`. Persistée, cette demi-paire faisait refuser tous les tours suivants en 400 (« An assistant message with 'tool_calls' must be followed by tool messages responding to each 'tool_call_id' ») : la conversation devenait définitivement inutilisable, « Vider le contexte » étant le seul recours. Relevé en base le 2026-10-22 : deux ids d'appel sans réponse, dont un en dernier message de la conversation active (308 appels contre 306 réponses). repairToolPairs garantit qu'après chaque assistant porteur d'appels suivent immédiatement une réponse par tool_call_id, dans l'ordre : les réponses présentes sont conservées, les manquantes reçoivent un marqueur « interrompu », un id vide est synthétisé, les messages média injectés par see_image/see_video sont replacés après le bloc et les réponses réellement orphelines sont retirées. Appliquée à l'envoi, à la sortie sur stop et dans persist() : les conversations déjà empoisonnées se réparent seules au tour suivant, et le blob reste sain. Co-Authored-By: Claude Code <noreply@anthropic.com>
Owner
|
Merci, diagnostic juste : j'ai vérifié dans le code, le stop sortait de la boucle avec l'assistant porteur de tous les appels et seulement une partie des réponses. J'ai aussi confirmé le cas de l'image de see_image glissée entre deux réponses d'outil. Fusionné sur main, ce sera dans la prochaine version. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Contexte
Après un arrêt (bouton stop) pendant une série d'appels d'outils, tous les tours suivants échouent avec :
La conversation est définitivement bloquée : « Vider le contexte » ne sert à rien si la demi-paire est recréée ensuite.
Cause
llm_client.go(~1586-1600) ajoute le message assistant porteur de tous lestool_calls, puis sort parif ctx.Err() != nil { return extra, nil }entre deux appels.chat_conversation.go(~921-930) persisteextraavec un garde surepochmais aucun garde sur l'annulation — le client CLI, lui, vérifie (cli_chat.go:464-472).Preuve
Relevé en usage réel (2026-10-22) : 308 ids d'appels côté assistant contre 306 réponses
tool;call_00_h80I…(write {}) sans réponse et dernier message de la conversation active,call_01_CWX…(bash) sans réponse dans une session archivée.Correctif
repairToolPairs(chat_tool_pairs.go) garantit qu'après chaque assistant porteur d'appels suivent immédiatement une réponsetoolpartool_call_id, dans l'ordre :[interrompu];call_repair_<i>_<k>) ;see_image/see_videoreplacés après le bloc (ils créaientassistant(a,b) → tool(a) → user(image) → tool(b), forme que le validateur strict refuse) ;Appliquée à l'envoi, à la sortie sur stop et dans
persist(): les conversations déjà empoisonnées se réparent au tour suivant, sans action manuelle.Tests
go test ./internal/ajean -run RepairToolPairs— les deux formes exactes relevées en base servent de cas de test (paire complète, appel unique sans réponse, série partielle, orphelin, id vide, média intercalé, idempotence, non-mutation de l'entrée).gofmt,go vetetstaticcheckpropres.go test ./...conserve trois échecs préexistants surmain(vérifiés sur un checkout propre) :TestMoeMigrate(privilège symlink Windows),TestShrinkToFit,TestSystemPromptStaysLean.Vérification en conditions réelles
Binaire reconstruit et relancé : la conversation bloquée répond de nouveau, la vue modèle ne contient plus aucune paire orpheline.
🤖 Generated with Claude Code