langgraph-agent-toolkit 0.9.1__tar.gz → 0.9.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/CHANGELOG.md +27 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/PKG-INFO +1 -1
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/components/middlewares/__init__.py +2 -0
- langgraph_agent_toolkit-0.9.2/langgraph_agent_toolkit/agents/components/middlewares/_history.py +39 -0
- langgraph_agent_toolkit-0.9.2/langgraph_agent_toolkit/agents/components/middlewares/token_trim.py +81 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/components/middlewares/trim_messages.py +5 -1
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/core/_base_settings.py +3 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/pyproject.toml +1 -1
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/tests/agents/test_middlewares.py +138 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/uv.lock +100 -100
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/.claude/settings.json +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/.coveragerc +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/.dockerignore +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/.env.example +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/.github/RELEASE_TEMPLATE/release-template.md +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/.github/codecov.yml +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/.github/dependabot.yml +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/.github/scripts/tag_from_pyproject.sh +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/.github/workflows/release.yml +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/.github/workflows/sphinx.yml +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/.github/workflows/test.yml +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/.gitignore +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/.pre-commit-config.yaml +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/.project-root +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/CLAUDE.md +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/CONTRIBUTING.md +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/LICENSE +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/Makefile +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/README.md +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/configs/clickhouse/.clickhouse.env.example +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/configs/langfuse/.langfuse.env.example +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/configs/litellm/.litellm.env.example +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/configs/litellm/config.example.yaml +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/configs/minio/.minio.env.example +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/configs/postgres/.postgres.env.example +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/configs/redis/.redis.env.example +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/docker/api/Dockerfile +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/docker/app/Dockerfile +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/docker-compose.yaml +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/docs/.gitignore +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/docs/Makefile +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/docs/_static/custom.css +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/docs/_templates/class.rst +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/docs/_templates/layout.html +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/docs/_templates/module.rst +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/docs/conf.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/docs/contributing.rst +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/docs/environment_setup.rst +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/docs/index.rst +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/docs/installation.rst +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/docs/make.bat +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/docs/media/agent_architecture.excalidraw +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/docs/media/agent_architecture.png +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/docs/media/agent_diagram.png +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/docs/media/logo.svg +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/docs/usage.rst +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph.json +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/__init__.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/__init__.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/agent.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/agent_executor.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/blueprints/__init__.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/blueprints/bg_task_agent/__init__.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/blueprints/bg_task_agent/agent.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/blueprints/bg_task_agent/task.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/blueprints/bg_task_agent/utils.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/blueprints/chatbot/__init__.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/blueprints/chatbot/agent.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/blueprints/command_agent/__init__.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/blueprints/command_agent/agent.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/blueprints/create_agent/__init__.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/blueprints/create_agent/agent.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/blueprints/create_agent_structured/__init__.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/blueprints/create_agent_structured/agent.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/blueprints/hitl_agent/__init__.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/blueprints/hitl_agent/agent.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/blueprints/interrupt_agent/__init__.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/blueprints/interrupt_agent/agent.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/blueprints/knowledge_base_agent/__init__.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/blueprints/knowledge_base_agent/agent.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/blueprints/react/__init__.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/blueprints/react/agent.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/blueprints/supervisor_agent/__init__.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/blueprints/supervisor_agent/agent.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/components/__init__.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/components/checkpoint/__init__.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/components/checkpoint/empty.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/components/creators/__init__.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/components/creators/create_react_agent.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/components/middlewares/clear_intermediate_tool_calls.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/components/middlewares/immediate_generation.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/components/middlewares/sanitize_history.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/components/tools.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/agents/components/utils.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/client/__init__.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/client/client.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/core/__init__.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/core/memory/__init__.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/core/memory/base.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/core/memory/factory.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/core/memory/postgres.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/core/memory/sqlite.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/core/memory/types.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/core/models/__init__.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/core/models/chat_openai.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/core/models/factory.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/core/models/fake.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/core/observability/__init__.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/core/observability/base.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/core/observability/empty.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/core/observability/factory.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/core/observability/langfuse.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/core/observability/langsmith.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/core/observability/types.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/core/prompts/__init__.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/core/prompts/chat_prompt_template.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/core/prompts/prompt_manager.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/core/settings.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/helper/__init__.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/helper/constants.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/helper/exceptions.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/helper/logging.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/helper/types.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/helper/utils.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/run_api.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/run_app.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/schema/__init__.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/schema/models.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/schema/schema.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/schema/task_data.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/service/__init__.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/service/exception_handlers.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/service/factory.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/service/handler.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/service/middleware.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/service/routes.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/service/types.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/service/utils.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/ui/__init__.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/ui/components/__init__.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/ui/components/draw_message.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/ui/components/side_panel.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/ui/main_page.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/ui/utils/__init__.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/ui/utils/constants.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/langgraph_agent_toolkit/ui/utils/message.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/scripts/postgres-init/create_databases.sql +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/scripts/postgres-init/create_schema.sql +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/scripts/python/01-invoke-proxy.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/scripts/python/02-test-local-pm.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/scripts/python/03-test-langfuse-pm.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/scripts/python/04-test-langsmith-pm.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/scripts/python/05-test-prompt-types.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/scripts/python/06-test-custom-chat-prompt-template.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/scripts/python/07-test-get-chat-history.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/scripts/python/08-test-update-chat-history.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/scripts/python/09-test-clear-chat-history.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/scripts/python/098-cls-example.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/scripts/python/099-t.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/tests/agents/test_agent_executor.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/tests/agents/test_blueprints.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/tests/agents/test_checkpoint_empty.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/tests/agents/test_components_utils.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/tests/agents/test_create_agent_stack.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/tests/agents/test_hitl_agent.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/tests/agents/test_tools.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/tests/client/conftest.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/tests/client/test_client.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/tests/conftest.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/tests/core/test_llm.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/tests/core/test_memory.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/tests/core/test_observability.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/tests/core/test_prompt_manager.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/tests/core/test_prompts.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/tests/core/test_settings.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/tests/helper/test_create_ai_message.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/tests/helper/test_helper_utils.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/tests/integration/test_docker_e2e.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/tests/service/conftest.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/tests/service/test_auth.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/tests/service/test_exception_handlers.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/tests/service/test_factory.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/tests/service/test_handler.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/tests/service/test_middleware.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/tests/service/test_service.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/tests/service/test_service_e2e.py +0 -0
- {langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/tests/ui/test_draw_messages.py +0 -0
|
@@ -6,6 +6,33 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
|
6
6
|
and this project adheres to
|
|
7
7
|
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
8
8
|
|
|
9
|
+
## [0.9.2]
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- `TrimMessagesMiddleware` / `TokenTrimMiddleware` no longer silently destroy
|
|
14
|
+
the model's view when the **latest turn does not fit the budget**.
|
|
15
|
+
`trim_messages` (`strategy="last"`, `start_on="human"`) returns `[]` whenever
|
|
16
|
+
the current turn exceeds the budget — e.g. a single user message larger than
|
|
17
|
+
the token budget, or a turn with more tool calls than the message budget —
|
|
18
|
+
which invoked the model with the system prompt only and made it answer with
|
|
19
|
+
the user's question dropped. Both middlewares now floor to the latest turn
|
|
20
|
+
(kept verbatim, over budget but answerable) rather than emptying it.
|
|
21
|
+
Regression tests cover an oversize user message, an oversize tool result, and
|
|
22
|
+
a 6+ tool-call burst.
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- `TokenTrimMiddleware` — bounds the model's message view by a **token** budget
|
|
27
|
+
(the token-counting companion to `TrimMessagesMiddleware`, which bounds by
|
|
28
|
+
message count). View-only, so the full history stays in state and the system
|
|
29
|
+
prompt is preserved. The token counter is configurable (`token_counter`,
|
|
30
|
+
default `count_tokens_approximately` — no extra dependency; pass a
|
|
31
|
+
`tiktoken`-backed counter or the model for an exact count), and the budget
|
|
32
|
+
defaults to the new `DEFAULT_MAX_TOKENS_HISTORY_LENGTH` setting (unset by
|
|
33
|
+
default, since a sensible budget is model-specific). Compose it after
|
|
34
|
+
`TrimMessagesMiddleware` to cap both message count and token size.
|
|
35
|
+
|
|
9
36
|
## [0.9.1]
|
|
10
37
|
|
|
11
38
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: langgraph-agent-toolkit
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.2
|
|
4
4
|
Summary: Full toolkit for running an AI agent service built with LangGraph, FastAPI and Streamlit
|
|
5
5
|
Project-URL: documentation, https://kryvokhyzha.github.io/langgraph-agent-toolkit
|
|
6
6
|
Project-URL: repository, https://github.com/kryvokhyzha/langgraph-agent-toolkit
|
|
@@ -5,6 +5,7 @@ from langgraph_agent_toolkit.agents.components.middlewares.clear_intermediate_to
|
|
|
5
5
|
)
|
|
6
6
|
from langgraph_agent_toolkit.agents.components.middlewares.immediate_generation import ImmediateGenerationMiddleware
|
|
7
7
|
from langgraph_agent_toolkit.agents.components.middlewares.sanitize_history import SanitizeHistoryMiddleware
|
|
8
|
+
from langgraph_agent_toolkit.agents.components.middlewares.token_trim import TokenTrimMiddleware
|
|
8
9
|
from langgraph_agent_toolkit.agents.components.middlewares.trim_messages import TrimMessagesMiddleware
|
|
9
10
|
|
|
10
11
|
|
|
@@ -12,5 +13,6 @@ __all__ = [
|
|
|
12
13
|
"ClearIntermediateToolCallsMiddleware",
|
|
13
14
|
"ImmediateGenerationMiddleware",
|
|
14
15
|
"SanitizeHistoryMiddleware",
|
|
16
|
+
"TokenTrimMiddleware",
|
|
15
17
|
"TrimMessagesMiddleware",
|
|
16
18
|
]
|
langgraph_agent_toolkit-0.9.2/langgraph_agent_toolkit/agents/components/middlewares/_history.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Internal helper shared by the history-trimming middleware (``TrimMessages`` / ``TokenTrim``)."""
|
|
2
|
+
|
|
3
|
+
from langchain_core.messages import BaseMessage
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def keep_latest_turn_if_emptied(
|
|
7
|
+
original: list[BaseMessage],
|
|
8
|
+
trimmed: list[BaseMessage],
|
|
9
|
+
) -> list[BaseMessage]:
|
|
10
|
+
"""Guard against ``trim_messages`` destroying the model's view.
|
|
11
|
+
|
|
12
|
+
``trim_messages(strategy="last", start_on="human", allow_partial=False)`` returns an empty (or
|
|
13
|
+
system-only) list whenever the latest human turn does not fit the budget — e.g. a single user
|
|
14
|
+
message larger than the budget, or a current turn with more tool calls than the budget allows. The
|
|
15
|
+
model would then be invoked with no user content and answer from the system prompt alone.
|
|
16
|
+
|
|
17
|
+
When trimming leaves no human/tool message, fall back to the latest turn (from the last
|
|
18
|
+
``HumanMessage`` to the end, plus any leading system messages) verbatim, bypassing the budget for
|
|
19
|
+
that turn: a too-long-but-answerable prompt beats a hallucinated answer to nothing, and an
|
|
20
|
+
over-budget turn surfaces as a visible model context error rather than silent corruption.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
original: The messages before trimming.
|
|
24
|
+
trimmed: The result of ``trim_messages``.
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
``trimmed`` when it still carries user content, else the latest turn from ``original``.
|
|
28
|
+
|
|
29
|
+
"""
|
|
30
|
+
if any(m.type in ("human", "tool") for m in trimmed):
|
|
31
|
+
return trimmed
|
|
32
|
+
|
|
33
|
+
last_human = next((i for i in range(len(original) - 1, -1, -1) if original[i].type == "human"), None)
|
|
34
|
+
if last_human is None:
|
|
35
|
+
# No human turn to anchor on (pathological); preserve everything rather than wipe the view.
|
|
36
|
+
return original
|
|
37
|
+
|
|
38
|
+
leading_system = [m for m in original[:last_human] if m.type == "system"]
|
|
39
|
+
return leading_system + original[last_human:]
|
langgraph_agent_toolkit-0.9.2/langgraph_agent_toolkit/agents/components/middlewares/token_trim.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""Middleware that trims the model's input to a bounded token budget (view-only)."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Awaitable, Callable
|
|
4
|
+
|
|
5
|
+
from langchain.agents.middleware import AgentMiddleware
|
|
6
|
+
from langchain.agents.middleware.types import ModelRequest, ModelResponse
|
|
7
|
+
from langchain_core.language_models import BaseLanguageModel
|
|
8
|
+
from langchain_core.messages import BaseMessage
|
|
9
|
+
from langchain_core.messages.utils import count_tokens_approximately, trim_messages
|
|
10
|
+
|
|
11
|
+
from langgraph_agent_toolkit.agents.components.middlewares._history import keep_latest_turn_if_emptied
|
|
12
|
+
from langgraph_agent_toolkit.core.settings import settings
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class TokenTrimMiddleware(AgentMiddleware):
|
|
16
|
+
"""Bound the model's input to a token budget, non-destructively.
|
|
17
|
+
|
|
18
|
+
The token-counting companion to ``TrimMessagesMiddleware`` (which bounds by *message* count). Each
|
|
19
|
+
model call sees at most ``max_tokens`` tokens of recent history (trimmed to start on a human turn
|
|
20
|
+
and to keep the system prompt), while the full history stays in state. Compose the two to cap both
|
|
21
|
+
message count and token size, as the custom ``create_react_agent``'s ``pre_model_hook`` did.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
max_tokens: Token budget for the model's message view. Defaults to
|
|
25
|
+
``settings.DEFAULT_MAX_TOKENS_HISTORY_LENGTH``. ``None`` (the toolkit default) disables
|
|
26
|
+
trimming — set it, or pass ``max_tokens``, to a budget appropriate for your model.
|
|
27
|
+
token_counter: How to count tokens — a per-message or per-list callable, or a chat model.
|
|
28
|
+
Defaults to ``count_tokens_approximately`` (fast, model-agnostic, no extra dependency).
|
|
29
|
+
Pass a ``tiktoken``-backed counter (or the model itself) for an exact count.
|
|
30
|
+
|
|
31
|
+
Note:
|
|
32
|
+
On ``create_agent`` the system prompt is never part of ``request.messages`` (it lives on
|
|
33
|
+
``request.system_message`` and is prepended at call time), so it is always preserved
|
|
34
|
+
regardless of the budget.
|
|
35
|
+
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(
|
|
39
|
+
self,
|
|
40
|
+
max_tokens: int | None = None,
|
|
41
|
+
token_counter: (
|
|
42
|
+
Callable[[list[BaseMessage]], int] | Callable[[BaseMessage], int] | BaseLanguageModel
|
|
43
|
+
) = count_tokens_approximately,
|
|
44
|
+
) -> None:
|
|
45
|
+
super().__init__()
|
|
46
|
+
resolved = max_tokens if max_tokens is not None else settings.DEFAULT_MAX_TOKENS_HISTORY_LENGTH
|
|
47
|
+
if resolved is not None and resolved < 1:
|
|
48
|
+
raise ValueError("max_tokens must be >= 1 (or None to disable)")
|
|
49
|
+
self.max_tokens = resolved
|
|
50
|
+
self.token_counter = token_counter
|
|
51
|
+
|
|
52
|
+
def _trim(self, messages: list[BaseMessage]) -> list[BaseMessage]:
|
|
53
|
+
if not self.max_tokens:
|
|
54
|
+
return messages
|
|
55
|
+
trimmed = trim_messages(
|
|
56
|
+
messages,
|
|
57
|
+
token_counter=self.token_counter,
|
|
58
|
+
max_tokens=self.max_tokens,
|
|
59
|
+
strategy="last",
|
|
60
|
+
start_on="human",
|
|
61
|
+
end_on=("human", "tool"),
|
|
62
|
+
include_system=True,
|
|
63
|
+
allow_partial=False,
|
|
64
|
+
)
|
|
65
|
+
# A latest turn larger than the budget collapses to []; keep that turn (over budget but
|
|
66
|
+
# answerable) instead of invoking the model with no user content.
|
|
67
|
+
return keep_latest_turn_if_emptied(messages, trimmed)
|
|
68
|
+
|
|
69
|
+
def wrap_model_call(
|
|
70
|
+
self,
|
|
71
|
+
request: ModelRequest,
|
|
72
|
+
handler: Callable[[ModelRequest], ModelResponse],
|
|
73
|
+
) -> ModelResponse:
|
|
74
|
+
return handler(request.override(messages=self._trim(request.messages)))
|
|
75
|
+
|
|
76
|
+
async def awrap_model_call(
|
|
77
|
+
self,
|
|
78
|
+
request: ModelRequest,
|
|
79
|
+
handler: Callable[[ModelRequest], Awaitable[ModelResponse]],
|
|
80
|
+
) -> ModelResponse:
|
|
81
|
+
return await handler(request.override(messages=self._trim(request.messages)))
|
|
@@ -7,6 +7,7 @@ from langchain.agents.middleware.types import ModelRequest, ModelResponse
|
|
|
7
7
|
from langchain_core.messages import BaseMessage
|
|
8
8
|
from langchain_core.messages.utils import trim_messages
|
|
9
9
|
|
|
10
|
+
from langgraph_agent_toolkit.agents.components.middlewares._history import keep_latest_turn_if_emptied
|
|
10
11
|
from langgraph_agent_toolkit.core.settings import settings
|
|
11
12
|
|
|
12
13
|
|
|
@@ -32,7 +33,7 @@ class TrimMessagesMiddleware(AgentMiddleware):
|
|
|
32
33
|
self.max_messages = resolved
|
|
33
34
|
|
|
34
35
|
def _trim(self, messages: list[BaseMessage]) -> list[BaseMessage]:
|
|
35
|
-
|
|
36
|
+
trimmed = trim_messages(
|
|
36
37
|
messages,
|
|
37
38
|
token_counter=len, # count messages, not tokens
|
|
38
39
|
max_tokens=self.max_messages,
|
|
@@ -42,6 +43,9 @@ class TrimMessagesMiddleware(AgentMiddleware):
|
|
|
42
43
|
include_system=True,
|
|
43
44
|
allow_partial=False,
|
|
44
45
|
)
|
|
46
|
+
# A current turn longer than max_messages collapses to []; keep that turn instead of
|
|
47
|
+
# invoking the model with no user content.
|
|
48
|
+
return keep_latest_turn_if_emptied(messages, trimmed)
|
|
45
49
|
|
|
46
50
|
def wrap_model_call(
|
|
47
51
|
self,
|
|
@@ -162,6 +162,9 @@ class Settings(BaseSettings):
|
|
|
162
162
|
# Agent configuration
|
|
163
163
|
DEFAULT_AGENT: str = "create-agent"
|
|
164
164
|
DEFAULT_MAX_MESSAGE_HISTORY_LENGTH: int = 18
|
|
165
|
+
# Token budget for the model's message view (TokenTrimMiddleware). None disables it (opt-in,
|
|
166
|
+
# since a sensible budget is model-specific); set a value to bound history by tokens.
|
|
167
|
+
DEFAULT_MAX_TOKENS_HISTORY_LENGTH: int | None = None
|
|
165
168
|
DEFAULT_RECURSION_LIMIT: int = 64
|
|
166
169
|
MULTIMODAL_MAX_ATTACHMENTS: int | None = Field(
|
|
167
170
|
default=None,
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "langgraph-agent-toolkit"
|
|
7
|
-
version = "0.9.
|
|
7
|
+
version = "0.9.2"
|
|
8
8
|
description = "Full toolkit for running an AI agent service built with LangGraph, FastAPI and Streamlit"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [
|
{langgraph_agent_toolkit-0.9.1 → langgraph_agent_toolkit-0.9.2}/tests/agents/test_middlewares.py
RENAMED
|
@@ -11,6 +11,7 @@ from langgraph_agent_toolkit.agents.components.middlewares import (
|
|
|
11
11
|
ClearIntermediateToolCallsMiddleware,
|
|
12
12
|
ImmediateGenerationMiddleware,
|
|
13
13
|
SanitizeHistoryMiddleware,
|
|
14
|
+
TokenTrimMiddleware,
|
|
14
15
|
TrimMessagesMiddleware,
|
|
15
16
|
)
|
|
16
17
|
from langgraph_agent_toolkit.core.models.fake import FakeToolModel
|
|
@@ -246,6 +247,143 @@ def test_trim_messages_rejects_bad_max():
|
|
|
246
247
|
TrimMessagesMiddleware(max_messages=0)
|
|
247
248
|
|
|
248
249
|
|
|
250
|
+
def _content_len(messages: list) -> int:
|
|
251
|
+
"""Per-list token-counter stub: total characters of message content."""
|
|
252
|
+
return sum(len(m.content) for m in messages)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def test_token_trim_bounds_to_token_budget():
|
|
256
|
+
"""The model's view is trimmed until the token count fits the budget, starting on a human turn."""
|
|
257
|
+
mw = TokenTrimMiddleware(max_tokens=12, token_counter=_content_len)
|
|
258
|
+
request = MagicMock()
|
|
259
|
+
request.messages = [
|
|
260
|
+
HumanMessage("aaaa"),
|
|
261
|
+
AIMessage("bbbb"),
|
|
262
|
+
HumanMessage("cccc"),
|
|
263
|
+
AIMessage("dddd"),
|
|
264
|
+
HumanMessage("eeee"),
|
|
265
|
+
]
|
|
266
|
+
request.override.return_value = "TRIMMED"
|
|
267
|
+
handler = MagicMock(return_value="R")
|
|
268
|
+
|
|
269
|
+
mw.wrap_model_call(request, handler)
|
|
270
|
+
|
|
271
|
+
trimmed = request.override.call_args.kwargs["messages"]
|
|
272
|
+
assert _content_len(trimmed) <= 12
|
|
273
|
+
assert trimmed[0].type == "human"
|
|
274
|
+
handler.assert_called_once_with("TRIMMED")
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def test_token_trim_keeps_system_message():
|
|
278
|
+
"""include_system keeps a system message even when the rest is trimmed to fit the budget."""
|
|
279
|
+
mw = TokenTrimMiddleware(max_tokens=8, token_counter=_content_len)
|
|
280
|
+
request = MagicMock()
|
|
281
|
+
request.messages = [SystemMessage("sys"), HumanMessage("aaaa"), AIMessage("bbbb"), HumanMessage("cccc")]
|
|
282
|
+
handler = MagicMock()
|
|
283
|
+
|
|
284
|
+
mw.wrap_model_call(request, handler)
|
|
285
|
+
|
|
286
|
+
trimmed = request.override.call_args.kwargs["messages"]
|
|
287
|
+
assert any(m.type == "system" for m in trimmed)
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def test_token_trim_disabled_passes_through():
|
|
291
|
+
"""With no budget (None), the middleware is a no-op."""
|
|
292
|
+
mw = TokenTrimMiddleware(max_tokens=None)
|
|
293
|
+
assert mw.max_tokens is None # toolkit default is opt-in
|
|
294
|
+
request = MagicMock()
|
|
295
|
+
request.messages = [HumanMessage("q1"), AIMessage("a1"), HumanMessage("q2")]
|
|
296
|
+
handler = MagicMock()
|
|
297
|
+
|
|
298
|
+
mw.wrap_model_call(request, handler)
|
|
299
|
+
|
|
300
|
+
assert request.override.call_args.kwargs["messages"] == request.messages
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def test_token_trim_default_from_settings():
|
|
304
|
+
assert TokenTrimMiddleware().max_tokens == settings.DEFAULT_MAX_TOKENS_HISTORY_LENGTH
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def test_token_trim_rejects_bad_max():
|
|
308
|
+
with pytest.raises(ValueError, match="max_tokens"):
|
|
309
|
+
TokenTrimMiddleware(max_tokens=0)
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def _latest_human_present(view: list, text: str) -> bool:
|
|
313
|
+
return any(m.type == "human" and text in str(m.content) for m in view)
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def test_token_trim_preserves_oversize_latest_human():
|
|
317
|
+
"""A single user message larger than the budget is kept, not dropped to an empty/system-only view."""
|
|
318
|
+
mw = TokenTrimMiddleware(max_tokens=5, token_counter=_content_len)
|
|
319
|
+
request = MagicMock()
|
|
320
|
+
request.messages = [HumanMessage("this question is far longer than the tiny budget UNIQUE")]
|
|
321
|
+
handler = MagicMock()
|
|
322
|
+
|
|
323
|
+
mw.wrap_model_call(request, handler)
|
|
324
|
+
|
|
325
|
+
kept = request.override.call_args.kwargs["messages"]
|
|
326
|
+
assert _latest_human_present(kept, "UNIQUE") # question preserved despite exceeding the budget
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def test_token_trim_preserves_oversize_tool_turn():
|
|
330
|
+
"""A current turn (human + big tool result) over budget is kept verbatim, not emptied."""
|
|
331
|
+
mw = TokenTrimMiddleware(max_tokens=5, token_counter=_content_len)
|
|
332
|
+
request = MagicMock()
|
|
333
|
+
request.messages = [
|
|
334
|
+
HumanMessage("q UNIQUE"),
|
|
335
|
+
_ai_tool("search", "s1"),
|
|
336
|
+
ToolMessage("x" * 200, tool_call_id="s1", name="search"),
|
|
337
|
+
]
|
|
338
|
+
handler = MagicMock()
|
|
339
|
+
|
|
340
|
+
mw.wrap_model_call(request, handler)
|
|
341
|
+
|
|
342
|
+
kept = request.override.call_args.kwargs["messages"]
|
|
343
|
+
assert _latest_human_present(kept, "UNIQUE")
|
|
344
|
+
ai_ids = {c["id"] for m in kept if m.type == "ai" and m.tool_calls for c in m.tool_calls}
|
|
345
|
+
assert all(m.tool_call_id in ai_ids for m in kept if m.type == "tool") # pairing intact
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def test_trim_messages_preserves_long_tool_burst_turn():
|
|
349
|
+
"""A current turn with more messages than max_messages is kept, not wiped to an empty view."""
|
|
350
|
+
mw = TrimMessagesMiddleware(max_messages=12)
|
|
351
|
+
burst = [HumanMessage("BURST_Q")]
|
|
352
|
+
for i in range(6): # 1 human + 6*(AI tool_call, Tool) = 13 messages > 12
|
|
353
|
+
burst += [_ai_tool("search", f"s{i}"), ToolMessage("r", tool_call_id=f"s{i}", name="search")]
|
|
354
|
+
request = MagicMock()
|
|
355
|
+
request.messages = burst
|
|
356
|
+
handler = MagicMock()
|
|
357
|
+
|
|
358
|
+
mw.wrap_model_call(request, handler)
|
|
359
|
+
|
|
360
|
+
kept = request.override.call_args.kwargs["messages"]
|
|
361
|
+
assert _latest_human_present(kept, "BURST_Q") # the human survives the burst
|
|
362
|
+
ai_ids = {c["id"] for m in kept if m.type == "ai" and m.tool_calls for c in m.tool_calls}
|
|
363
|
+
assert all(m.tool_call_id in ai_ids for m in kept if m.type == "tool")
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
def test_keep_latest_turn_floor_keeps_only_latest_turn_and_leading_system():
|
|
367
|
+
"""The floor preserves leading system messages + the latest turn, dropping older over-budget turns."""
|
|
368
|
+
from langgraph_agent_toolkit.agents.components.middlewares._history import keep_latest_turn_if_emptied
|
|
369
|
+
|
|
370
|
+
original = [SystemMessage("sys"), HumanMessage("old"), AIMessage("a"), HumanMessage("latest")]
|
|
371
|
+
floored = keep_latest_turn_if_emptied(original, []) # [] simulates a budget collapse
|
|
372
|
+
|
|
373
|
+
assert floored[0].type == "system"
|
|
374
|
+
assert floored[-1].content == "latest"
|
|
375
|
+
assert [m.content for m in floored if m.type == "human"] == ["latest"] # only the latest turn's human
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def test_keep_latest_turn_floor_passes_through_healthy_trim():
|
|
379
|
+
"""When trimming kept user content, the floor returns it unchanged."""
|
|
380
|
+
from langgraph_agent_toolkit.agents.components.middlewares._history import keep_latest_turn_if_emptied
|
|
381
|
+
|
|
382
|
+
original = [HumanMessage("a"), AIMessage("b"), HumanMessage("c")]
|
|
383
|
+
trimmed = [HumanMessage("c")]
|
|
384
|
+
assert keep_latest_turn_if_emptied(original, trimmed) is trimmed
|
|
385
|
+
|
|
386
|
+
|
|
249
387
|
def test_create_agent_with_middleware_runs_end_to_end():
|
|
250
388
|
"""create_agent composes both middleware and runs without error (FAKE model, no tool loop)."""
|
|
251
389
|
|