Feat/tool hang logs#28
Conversation
- Split monolithic exceptions.py into per-domain error modules with ErrorCode enum for HTTP status mapping - Introduce ErrorMessage/LogMessage string enums as single-source catalogs for error text and log templates - Replace ad-hoc logging setup with centralized configure_logging() and RequestIdMiddleware for correlation IDs - Add MCP tool timeout, agent stream idle/invocation timeouts, and mcp>=1.27.0 dependency - Bump deepagents, langchain-core, langgraph, cryptography, pyjwt, and langchain-mcp-adapters; add integration test for deepagents graph
CVE-2026-42496: Path traversal in Archive::Tar, unfixed in bookworm, app does not use Perl. CVE-2026-8376: Heap buffer overflow in regex compiler, 32-bit only (amd64/arm64 unaffected).
Kaiohz
left a comment
There was a problem hiding this comment.
PR Review — Feat/tool hang logs
Score : 7.5/10
Le cœur de la PR (timeout par outil MCP + timeout sur stream/ainvoke) est solide et bien testé. La refacto erreurs/logs est propre et améliore la traçabilité. Mais le scope a largement dépassé le titre, et il y a quelques points à corriger avant de merger.
✅ Ce qui est bien
- Vrai fix anti-hang :
LangchainMcpToolLoader._patch_sync_supportenveloppe désormais chaquecoroutinedansasyncio.wait_for(tool_timeout)→ToolException→ToolMessage(error). C'est exactement la bonne approche : le LLM peut récupérer (retry/skip) au lieu d'avoir un agent tué. Le testtest_hung_tool_raises_tool_exceptioncouvre le cas réel. - Stream idle detection :
DeepAgentRunner._yield_chunksconsomme les chunks un par un avecasyncio.wait_for(stream_idle_timeout)sur chaqueanext(). Couvre le cas "tool result perdu" (SSE qui drop).aclose()sur le stream iter dans lefinallyest une bonne hygiène. - Wall-time timeout sur
ainvoke+ timeout dédié sur les 3 calls HITL (approve/reject/edit) → cohérent. - Timeouts configurables via
Settings(mcp_tool_timeout,agent_stream_idle_timeout,agent_invoke_timeout) avec défauts sensés (60s/120s/120s). - Centralisation des messages d'erreur et de log dans
domain/errors/messages.pyetdomain/logging/messages.py(StrEnum) — uniformise, évite la duplication, facilite l'audit et la future i18n. - Refacto
main.py:_error_response(exc)qui litexc.status_code/exc.detailau lieu d'avoir 8 handlers presque identiques — gros gain en DSU. - Tests :
test_hung_tool_raises_tool_exception,test_fast_tool_succeeds,test_stream_idle_timeout_raises_agent_error,test_invoke_timeout_raises_agent_error— couvrent les vrais chemins de panne.
⚠️ À corriger avant merge
-
.DS_Storeest commité (diffBinary files a/.DS_Store and b/.DS_Store differ). C'est un artefact macOS qui n'a rien à faire dans le repo, et.gitignorene le contient pas. →git rm .DS_Store+ ajouter**/.DS_Storedans.gitignore+ idéalement un pre-commit hook. -
Scope creep vs titre : la PR fait 1679+/721- sur 67 fichiers alors que le titre annonce "tool hang logs". Le vrai fix tient en 4 fichiers (
logging.py,deepagent/adapter.py,mcp/adapter.py,config.py). Tout le reste (refactodomain/errors/,domain/logging/messages.py, tous les swapsexceptions.py → errors/*.py, les changements de wording dans 25 fichiers) devrait être dans une PR séparée "Refactor: centralize errors & log messages". Ça facilitera le revert si un des deux pose problème. -
PR sans description : aucun
bodyn'a été renseigné. Pour une PR de cette taille, c'est rédhibitoire. Il faut au minimum : le problème résolu, les breaking changes, comment tester, le lien vers le ticket/incident qui a motivé les timeouts. -
_patch_sync_supportest maintenant@staticmethod→ méthode d'instance (diffmcp/adapter.pyligne ~94). Pas un bug, mais ça veut dire qu'on instancieLangchainMcpToolLoader(tool_timeout=…)même quand on n'a aucun serveur MCP configuré — les tests qui fontLangchainMcpToolLoader()sans kwarg sont cassés silencieusement s'il n'y a pas de default (il y en a un :60.0, donc OK, mais à documenter).
💡 Suggestions d'amélioration
asyncio.get_event_loop()danssync_wrapper(mcp/adapter.py) — déprécié en Python 3.12+ quand il n'y a pas de loop courante. Préférerasyncio.run_coroutine_threadsafe(_tc(...), ...)si on est dans un thread séparé, ou documenter que cette branche n'est appelée que depuis deepagents (thread principal).- Pas de log quand un tool timeout déclenche un retry par le LLM : on a
MCP_TOOL_TIMEOUT(warning) mais pas de compteur/métrique. Si tu veux piloter la stabilité MCP, expose un compteur (Prometheus/StatsD) ou au moins un log agrégé toutes les N occurrences. - Stream idle timeout est par chunk : un LLM qui réfléchit 90s entre deux chunks (réponse longue, tool complexe) déclenchera un faux positif. 120s par défaut est OK mais expose-le en config par agent (certains tools RAG sont lents). Le setting existe déjà au niveau global, mais pas overridable par agent.
LogMessageetErrorMessagesont desStrEnum: pratique pour le typage, mais ça veut dire qu'on perd l'info de "ce message est utilisé 3 fois, ce warning est mort" qu'aurait donnée une fonction. OK pour ce use case, mais ajoute un test qui vérifie qu'aucun message n'est dupliqué entreLogMessageet les anciennes chaînes hardcodées restantes (il en reste probablement quelques-unes dans les fichiers non touchés).src/domain/exceptions.pyest supprimé mais le commit ne le mentionne pas dans le message → vérifier qu'aucun import externe ne pointe encore dessus (ungrep -r "from src.domain.exceptions" .dans le repo mergé confirmera).
🧪 Manque côté tests
- Pas de test sur le nouveau
RequestIdMiddleware(vérifier qu'il génère un id, propage unX-Request-IDentrant, et que l'id apparaît dans les logs). - Pas de test sur la propagation de
mcp_tool_timeoutdepuisSettings→LangchainMcpToolLoader. - Pas de test d'intégration qui simule un vrai hang SSE entre un agent et un serveur MCP (le test unitaire mock
coroutineàsleep(10), pas la couche transport). Letests/integration/test_deepagent_real_graph.pyajouté va peut-être couvrir une partie — à vérifier. - Le test
test_hung_tool_raises_tool_exceptionne vérifie pas la branche sync (sync_wrapper), qui est celle que deepagents utilise réellement.
📋 Verdict
Le fix principal est bon et prêt à merger après : suppression du .DS_Store, ajout dans .gitignore, description de PR. Le reste de la refacto (errors + log centralization) est propre mais gagnerait à être splitté en une PR dédiée pour faciliter le review et le revert.
Score 7.5/10 — fix utile et bien testé, mais portée trop large + hygiene (.DS_Store, no description) à corriger.
No description provided.