diff --git a/src/oss/python/integrations/middleware/anthropic.mdx b/src/oss/python/integrations/middleware/anthropic.mdx index 5e07937f48..65bfdcacdb 100644 --- a/src/oss/python/integrations/middleware/anthropic.mdx +++ b/src/oss/python/integrations/middleware/anthropic.mdx @@ -37,7 +37,7 @@ from langchain.agents import create_agent agent = create_agent( model=ChatAnthropic(model="claude-sonnet-4-5-20250929"), system_prompt="", - middleware=[AnthropicPromptCachingMiddleware(ttl="5m")], + middleware=[AnthropicPromptCachingMiddleware(ttl="5m")], # [!code highlight] ) ``` @@ -70,11 +70,17 @@ The middleware caches content up to and including the latest message in each req 2. Second request: The cached content (system prompt, tools, and first message) is retrieved from cache. Only the new message *"What's my name?"* needs to be processed, plus the model's response from the first request 3. This pattern continues for each turn, with each request reusing the cached conversation history + + Prompt caching reduces API costs by caching tokens, but does **not** provide conversation memory. To persist conversation history across invocations, use a [checkpointer](https://langchain-ai.github.io/langgraph/concepts/persistence/#checkpointer-libraries) like `MemorySaver`. + + ```python from langchain_anthropic import ChatAnthropic from langchain_anthropic.middleware import AnthropicPromptCachingMiddleware from langchain.agents import create_agent from langchain.messages import HumanMessage +from langchain_core.runnables import RunnableConfig +from langgraph.checkpoint.memory import MemorySaver LONG_PROMPT = """ @@ -86,15 +92,24 @@ Please be a helpful assistant. agent = create_agent( model=ChatAnthropic(model="claude-sonnet-4-5-20250929"), system_prompt=LONG_PROMPT, - middleware=[AnthropicPromptCachingMiddleware(ttl="5m")], + middleware=[AnthropicPromptCachingMiddleware(ttl="5m")], # [!code highlight] + checkpointer=MemorySaver(), # Persists conversation history ) +# Use a thread_id to maintain conversation state +config: RunnableConfig = {"configurable": {"thread_id": "user-123"}} + # First invocation: Creates cache with system prompt, tools, and "Hi, my name is Bob" -agent.invoke({"messages": [HumanMessage("Hi, my name is Bob")]}) +agent.invoke({"messages": [HumanMessage("Hi, my name is Bob")]}, config=config) # Second invocation: Reuses cached system prompt, tools, and previous messages -# Only processes the new message "What's my name?" and the previous AI response -agent.invoke({"messages": [HumanMessage("What's my name?")]}) +# The checkpointer maintains conversation history, so the agent remembers "Bob" +result = agent.invoke({"messages": [HumanMessage("What's my name?")]}, config=config) +print(result["messages"][-1].content) +``` + +```output +Your name is Bob! You told me that when you introduced yourself at the start of our conversation. ``` @@ -123,11 +138,11 @@ from langchain.agents import create_agent agent = create_agent( model=ChatAnthropic(model="claude-sonnet-4-5-20250929"), tools=[], - middleware=[ - ClaudeBashToolMiddleware( - workspace_root="/workspace", - ), - ], + middleware=[ # [!code highlight] + ClaudeBashToolMiddleware( # [!code highlight] + workspace_root="/workspace", # [!code highlight] + ), # [!code highlight] + ], # [!code highlight] ) ``` @@ -158,30 +173,40 @@ See [Shell tool](/oss/langchain/middleware/built-in#shell-tool) for full configu ```python +import tempfile + from langchain_anthropic import ChatAnthropic from langchain_anthropic.middleware import ClaudeBashToolMiddleware from langchain.agents import create_agent from langchain.agents.middleware import DockerExecutionPolicy +# Create a temporary workspace directory for this demo. +# In production, use a persistent directory path. +workspace = tempfile.mkdtemp(prefix="agent-workspace-") agent = create_agent( model=ChatAnthropic(model="claude-sonnet-4-5-20250929"), tools=[], - middleware=[ - ClaudeBashToolMiddleware( - workspace_root="/workspace", - startup_commands=["pip install requests"], - execution_policy=DockerExecutionPolicy( - image="python:3.11-slim", - ), - ), - ], + middleware=[ # [!code highlight] + ClaudeBashToolMiddleware( # [!code highlight] + workspace_root=workspace, # [!code highlight] + startup_commands=["echo 'Session initialized'"], # [!code highlight] + execution_policy=DockerExecutionPolicy( # [!code highlight] + image="python:3.11-slim", # [!code highlight] + ), # [!code highlight] + ), # [!code highlight] + ], # [!code highlight] ) # Claude can now use its native bash tool -result = agent.invoke({ - "messages": [{"role": "user", "content": "List files in the workspace"}] -}) +result = agent.invoke( + {"messages": [{"role": "user", "content": "What version of Python is installed?"}]} +) +print(result["messages"][-1].content) +``` + +```output +Python 3.11.14 is installed. ``` @@ -206,21 +231,43 @@ The text editor middleware is useful for the following: - @[`StateClaudeTextEditorMiddleware`] - @[`FilesystemClaudeTextEditorMiddleware`] -```python +```python State-based text editor from langchain_anthropic import ChatAnthropic from langchain_anthropic.middleware import StateClaudeTextEditorMiddleware from langchain.agents import create_agent -# State-based (files in LangGraph state) agent = create_agent( model=ChatAnthropic(model="claude-sonnet-4-5-20250929"), tools=[], - middleware=[ - StateClaudeTextEditorMiddleware(), - ], + middleware=[StateClaudeTextEditorMiddleware()], # [!code highlight] +) +``` + +```python Filesystem-based text editor +from langchain_anthropic import ChatAnthropic +from langchain_anthropic.middleware import FilesystemClaudeTextEditorMiddleware +from langchain.agents import create_agent + +agent = create_agent( + model=ChatAnthropic(model="claude-sonnet-4-5-20250929"), + tools=[], + middleware=[ # [!code highlight] + FilesystemClaudeTextEditorMiddleware( # [!code highlight] + root_path="/workspace", # [!code highlight] + ), # [!code highlight] + ], # [!code highlight] ) ``` +Claude's text editor tool supports the following commands: + +- `view` - View file contents or list directory +- `create` - Create a new file +- `str_replace` - Replace string in file +- `insert` - Insert text at line number +- `delete` - Delete a file +- `rename` - Rename/move a file + **@[`StateClaudeTextEditorMiddleware`] (state-based)** @@ -245,52 +292,87 @@ agent = create_agent( - + -Claude's text editor tool supports the following commands: -- `view` - View file contents or list directory -- `create` - Create a new file -- `str_replace` - Replace string in file -- `insert` - Insert text at line number -- `delete` - Delete a file -- `rename` - Rename/move a file + ```python from langchain_anthropic import ChatAnthropic -from langchain_anthropic.middleware import ( - StateClaudeTextEditorMiddleware, - FilesystemClaudeTextEditorMiddleware, -) +from langchain_anthropic.middleware import StateClaudeTextEditorMiddleware from langchain.agents import create_agent +from langchain_core.runnables import RunnableConfig +from langgraph.checkpoint.memory import MemorySaver -# State-based: Files persist in LangGraph state -agent_state = create_agent( +agent = create_agent( model=ChatAnthropic(model="claude-sonnet-4-5-20250929"), tools=[], middleware=[ - StateClaudeTextEditorMiddleware( - allowed_path_prefixes=["/project"], - ), + StateClaudeTextEditorMiddleware( # [!code highlight] + allowed_path_prefixes=["/project"], # [!code highlight] + ), # [!code highlight] ], + checkpointer=MemorySaver(), ) -# Filesystem-based: Files persist on disk -agent_fs = create_agent( +# Use a thread_id to persist state across invocations +config: RunnableConfig = {"configurable": {"thread_id": "my-session"}} + +# Claude can now create and edit files (stored in LangGraph state) +result = agent.invoke( + {"messages": [{"role": "user", "content": "Create a file at /project/hello.py with a simple hello world program"}]}, + config=config, +) +print(result["messages"][-1].content) +``` + +```output +I've created a simple "Hello, World!" program at `/project/hello.py`. The program uses Python's `print()` function to display "Hello, World!" to the console when executed. +``` + + + + + +```python +import tempfile + +from langchain_anthropic import ChatAnthropic +from langchain_anthropic.middleware import FilesystemClaudeTextEditorMiddleware +from langchain.agents import create_agent + + +# Create a temporary workspace directory for this demo. +# In production, use a persistent directory path. +workspace = tempfile.mkdtemp(prefix="editor-workspace-") + +agent = create_agent( model=ChatAnthropic(model="claude-sonnet-4-5-20250929"), tools=[], middleware=[ - FilesystemClaudeTextEditorMiddleware( - root_path="/workspace", - allowed_prefixes=["/src"], - max_file_size_mb=10, - ), + FilesystemClaudeTextEditorMiddleware( # [!code highlight] + root_path=workspace, # [!code highlight] + allowed_prefixes=["/src"], # [!code highlight] + max_file_size_mb=10, # [!code highlight] + ), # [!code highlight] ], ) + +# Claude can now create and edit files (stored on disk) +result = agent.invoke( + {"messages": [{"role": "user", "content": "Create a file at /src/hello.py with a simple hello world program"}]} +) +print(result["messages"][-1].content) +``` + +```output +I've created a simple "Hello, World!" program at `/src/hello.py`. The program uses Python's `print()` function to display "Hello, World!" to the console when executed. ``` + + ## Memory @@ -309,18 +391,31 @@ The memory middleware is useful for the following: **API reference:** @[`StateClaudeMemoryMiddleware`], @[`FilesystemClaudeMemoryMiddleware`] -```python +```python State-based memory from langchain_anthropic import ChatAnthropic from langchain_anthropic.middleware import StateClaudeMemoryMiddleware from langchain.agents import create_agent -# State-based memory agent = create_agent( model=ChatAnthropic(model="claude-sonnet-4-5-20250929"), tools=[], - middleware=[ - StateClaudeMemoryMiddleware(), - ], + middleware=[StateClaudeMemoryMiddleware()], # [!code highlight] +) +``` + +```python Filesystem-based memory +from langchain_anthropic import ChatAnthropic +from langchain_anthropic.middleware import FilesystemClaudeMemoryMiddleware +from langchain.agents import create_agent + +agent_fs = create_agent( + model=ChatAnthropic(model="claude-sonnet-4-5-20250929"), + tools=[], + middleware=[ # [!code highlight] + FilesystemClaudeMemoryMiddleware( # [!code highlight] + root_path="/workspace", # [!code highlight] + ), # [!code highlight] + ], # [!code highlight] ) ``` @@ -356,45 +451,91 @@ agent = create_agent( - + + + + +The agent will automatically: +1. Check `/memories` directory at start +2. Record progress and thoughts during execution +3. Update memory files as work progresses ```python from langchain_anthropic import ChatAnthropic -from langchain_anthropic.middleware import ( - StateClaudeMemoryMiddleware, - FilesystemClaudeMemoryMiddleware, -) +from langchain_anthropic.middleware import StateClaudeMemoryMiddleware from langchain.agents import create_agent +from langchain_core.runnables import RunnableConfig +from langgraph.checkpoint.memory import MemorySaver -# State-based: Memory persists in LangGraph state -agent_state = create_agent( +agent = create_agent( model=ChatAnthropic(model="claude-sonnet-4-5-20250929"), tools=[], - middleware=[ - StateClaudeMemoryMiddleware(), - ], + middleware=[StateClaudeMemoryMiddleware()], # [!code highlight] + checkpointer=MemorySaver(), ) -# Filesystem-based: Memory persists on disk -agent_fs = create_agent( +# Use a thread_id to persist state across invocations +config: RunnableConfig = {"configurable": {"thread_id": "my-session"}} + +# Claude can now use memory to track progress (stored in LangGraph state) +result = agent.invoke( + {"messages": [{"role": "user", "content": "Remember that my favorite color is blue, then confirm what you stored."}]}, + config=config, +) +print(result["messages"][-1].content) +``` + +```output +Perfect! I've stored your favorite color as **blue** in my memory system. The information is saved in my user preferences file where I can access it in future conversations. +``` + + + + + +The agent will automatically: +1. Check `/memories` directory at start +2. Record progress and thoughts during execution +3. Update memory files as work progresses + +```python +import tempfile + +from langchain_anthropic import ChatAnthropic +from langchain_anthropic.middleware import FilesystemClaudeMemoryMiddleware +from langchain.agents import create_agent + + +# Create a temporary workspace directory for this demo. +# In production, use a persistent directory path. +workspace = tempfile.mkdtemp(prefix="memory-workspace-") + +agent = create_agent( model=ChatAnthropic(model="claude-sonnet-4-5-20250929"), tools=[], middleware=[ - FilesystemClaudeMemoryMiddleware( - root_path="/workspace", - ), + FilesystemClaudeMemoryMiddleware( # [!code highlight] + root_path=workspace, # [!code highlight] + ), # [!code highlight] ], ) -# The agent will automatically: -# 1. Check /memories directory at start -# 2. Record progress and thoughts during execution -# 3. Update memory files as work progresses +# Claude can now use memory to track progress (stored on disk) +result = agent.invoke( + {"messages": [{"role": "user", "content": "Remember that my favorite color is blue, then confirm what you stored."}]} +) +print(result["messages"][-1].content) +``` + +```output +Perfect! I've stored your favorite color as **blue** in my memory system. The information is saved in my user preferences file where I can access it in future conversations. ``` + + ## File search @@ -418,10 +559,10 @@ from langchain.agents import create_agent agent = create_agent( model=ChatAnthropic(model="claude-sonnet-4-5-20250929"), tools=[], - middleware=[ - StateClaudeTextEditorMiddleware(), - StateFileSearchMiddleware(), # Search text editor files - ], + middleware=[ # [!code highlight] + StateClaudeTextEditorMiddleware(), # [!code highlight] + StateFileSearchMiddleware(), # Search text editor files [!code highlight] + ], # [!code highlight] ) ``` @@ -433,7 +574,9 @@ agent = create_agent( - + + + The middleware adds Glob and Grep search tools that work with state-based files. @@ -441,31 +584,126 @@ The middleware adds Glob and Grep search tools that work with state-based files. from langchain_anthropic import ChatAnthropic from langchain_anthropic.middleware import ( StateClaudeTextEditorMiddleware, - StateClaudeMemoryMiddleware, StateFileSearchMiddleware, ) from langchain.agents import create_agent +from langchain.messages import HumanMessage +from langchain_core.runnables import RunnableConfig +from langgraph.checkpoint.memory import MemorySaver -# Search text editor files agent = create_agent( model=ChatAnthropic(model="claude-sonnet-4-5-20250929"), tools=[], middleware=[ StateClaudeTextEditorMiddleware(), - StateFileSearchMiddleware(state_key="text_editor_files"), + StateFileSearchMiddleware(state_key="text_editor_files"), # [!code highlight] ], + checkpointer=MemorySaver(), ) -# Search memory files -agent_memory = create_agent( +# Use a thread_id to persist state across invocations +config: RunnableConfig = {"configurable": {"thread_id": "my-session"}} + +# First invocation: Create some files using the text editor tool +result = agent.invoke( + {"messages": [HumanMessage("Create a Python project with main.py, utils/helpers.py, and tests/test_main.py")]}, + config=config, +) + +# The agent creates files, which are stored in state +print("Files created:", list(result["text_editor_files"].keys())) + +# Second invocation: Search the files we just created +# State is automatically persisted via the checkpointer +result = agent.invoke( + {"messages": [HumanMessage("Find all Python files in the project")]}, + config=config, +) +print(result["messages"][-1].content) +``` + +```output +Files created: ['/project/main.py', '/project/utils/helpers.py', '/project/utils/__init__.py', '/project/tests/test_main.py', '/project/tests/__init__.py', '/project/README.md'] +``` + +```output +I found 5 Python files in the project: + +1. `/project/main.py` - Main application file +2. `/project/utils/__init__.py` - Utils package initialization +3. `/project/utils/helpers.py` - Helper utilities +4. `/project/tests/__init__.py` - Tests package initialization +5. `/project/tests/test_main.py` - Main test file + +Would you like me to view the contents of any of these files? +``` + + + + + +```python +from langchain_anthropic import ChatAnthropic +from langchain_anthropic.middleware import ( + StateClaudeMemoryMiddleware, + StateFileSearchMiddleware, +) +from langchain.agents import create_agent +from langchain.messages import HumanMessage +from langchain_core.runnables import RunnableConfig +from langgraph.checkpoint.memory import MemorySaver + + +agent = create_agent( model=ChatAnthropic(model="claude-sonnet-4-5-20250929"), tools=[], middleware=[ StateClaudeMemoryMiddleware(), - StateFileSearchMiddleware(state_key="memory_files"), + StateFileSearchMiddleware(state_key="memory_files"), # [!code highlight] ], + checkpointer=MemorySaver(), +) + +# Use a thread_id to persist state across invocations +config: RunnableConfig = {"configurable": {"thread_id": "my-session"}} + +# First invocation: Record some memories +result = agent.invoke( + {"messages": [HumanMessage("Remember that the project deadline is March 15th and code review deadline is March 10th")]}, + config=config, +) + +# The agent creates memory files, which are stored in state +print("Memory files created:", list(result["memory_files"].keys())) + +# Second invocation: Search the memories we just recorded +# State is automatically persisted via the checkpointer +result = agent.invoke( + {"messages": [HumanMessage("Search my memories for project deadlines")]}, + config=config, ) +print(result["messages"][-1].content) +``` + +```output +Memory files created: ['/memories/project_info.md'] +``` + +```output +I found your project deadlines in my memory! Here's what I have recorded: + +## Important Deadlines +- **Code Review Deadline:** March 10th +- **Project Deadline:** March 15th + +## Notes +- Code review must be completed 5 days before final project deadline +- Need to ensure all code is ready for review by March 10th + +Is there anything specific about these deadlines you'd like to know or update? ``` + +