"""Tests for middleware factories and context editing defaults.""" from __future__ import annotations import pytest from langchain_core.messages import AIMessage, HumanMessage, ToolMessage from graphs.middleware import ( ClearToolUsesEdit, ContextEditingMiddleware, _MIDDLEWARES_AVAILABLE, _SUMMARIZATION_AVAILABLE, build_main_agent_middlewares, build_project_subagent_middlewares, ) pytestmark = pytest.mark.skipif( not _MIDDLEWARES_AVAILABLE, reason="ContextEditingMiddleware unavailable in installed LangChain version", ) def test_main_agent_middlewares_returns_context_editing() -> None: middlewares = build_main_agent_middlewares() assert len(middlewares) == (2 if _SUMMARIZATION_AVAILABLE else 1) context_middleware = middlewares[0] assert isinstance(context_middleware, ContextEditingMiddleware) assert len(context_middleware.edits) == 1 edit = context_middleware.edits[0] assert isinstance(edit, ClearToolUsesEdit) assert edit.trigger == 4_000 assert edit.clear_at_least == 1_500 assert edit.keep == 2 assert edit.clear_tool_inputs is False assert "check_project_id" in edit.exclude_tools assert "generate_summary_pdf" in edit.exclude_tools def test_subagent_middlewares_returns_context_editing() -> None: middlewares = build_project_subagent_middlewares() assert len(middlewares) == (2 if _SUMMARIZATION_AVAILABLE else 1) context_middleware = middlewares[0] assert isinstance(context_middleware, ContextEditingMiddleware) assert len(context_middleware.edits) == 1 edit = context_middleware.edits[0] assert isinstance(edit, ClearToolUsesEdit) assert edit.trigger == 8_000 assert edit.clear_at_least == 2_500 assert edit.keep == 3 assert edit.clear_tool_inputs is False assert "check_project_id" in edit.exclude_tools def test_clear_tool_uses_edit_actually_prunes() -> None: ai_message = AIMessage( content="Je consulte les pages.", tool_calls=[ {"name": "search_project_docs", "args": {"offset": 0}, "id": "call-1", "type": "tool_call"}, {"name": "search_project_docs", "args": {"offset": 20}, "id": "call-2", "type": "tool_call"}, {"name": "search_project_docs", "args": {"offset": 40}, "id": "call-3", "type": "tool_call"}, {"name": "search_project_docs", "args": {"offset": 60}, "id": "call-4", "type": "tool_call"}, ], ) messages = [ HumanMessage(content="Donne-moi une vue globale du projet."), ai_message, ToolMessage(content="chunk-page-1", tool_call_id="call-1", name="search_project_docs"), ToolMessage(content="chunk-page-2", tool_call_id="call-2", name="search_project_docs"), ToolMessage(content="chunk-page-3", tool_call_id="call-3", name="search_project_docs"), ToolMessage(content="chunk-page-4", tool_call_id="call-4", name="search_project_docs"), AIMessage(content="Voici une premiere synthese."), ] edit = ClearToolUsesEdit( trigger=1, clear_at_least=0, keep=2, clear_tool_inputs=False, exclude_tools=(), placeholder="[cleared-for-test]", ) edit.apply(messages, count_tokens=lambda _messages: 1_000) assert isinstance(messages[2], ToolMessage) assert messages[2].content == "[cleared-for-test]" assert messages[3].content == "[cleared-for-test]" assert messages[4].content == "chunk-page-3" assert messages[5].content == "chunk-page-4"