Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview
deepagents is a Python package implementing "Deep Agent" architecture for creating intelligent agents capable of handling complex, multi-step tasks. It uses LangGraph for agent orchestration and Claude Sonnet 4 as the default LLM.

## Common Development Commands

### Setup
```bash
# Install in development mode
pip install -e .

# Install dependencies
pip install langgraph>=0.2.6 langchain-anthropic>=0.1.23 langchain>=0.2.14
```

### Running Examples
```bash
# Run the research agent example
cd examples/research
pip install -r requirements.txt
langgraph test # Run with LangGraph Studio
```

### Building the Package
```bash
# Build distribution
python -m build

# Upload to PyPI (requires credentials)
python -m twine upload dist/*
```

### Running Tests
```bash
# Install test dependencies
pip install -e ".[dev]"

# Run all tests
pytest

# Run with coverage
pytest --cov=deepagents

# Run specific test file
pytest tests/test_create_deep_agent.py

# Run specific test class or method
pytest tests/test_create_deep_agent.py::TestCreateDeepAgent::test_create_deep_agent_happy_path
```

## Architecture Overview

### Core Components
- **`src/deepagents/graph.py`**: Main agent creation logic using LangGraph's `create_react_agent`
- **`src/deepagents/state.py`**: State management with `DeepAgentState` extending LangGraph's `AgentState`
- **`src/deepagents/tools.py`**: Built-in tools (file operations, todo management)
- **`src/deepagents/sub_agent.py`**: Sub-agent spawning and task delegation
- **`src/deepagents/prompts.py`**: System prompts and tool descriptions

### Key Design Patterns
1. **Mock Filesystem**: Uses LangGraph state instead of real files for safety. Files are stored in the agent's state with a file reducer for merging.
2. **Sub-Agent Architecture**: Main agent can spawn specialized sub-agents with isolated context and specific tool access.
3. **State Management**: Uses TypedDict for structured state with `todos` list and `files` dictionary.
4. **Tool System**: Tools are LangChain-compatible functions that modify agent state.

### Important Implementation Details
- Default model is Claude Sonnet 4 (`claude-sonnet-4-20250514`) with 64k token limit
- Sub-agents receive quarantined context to prevent information leakage
- File operations validate path safety and perform string replacement validation
- Todo management tracks task status (pending, in_progress, completed)

## Development Notes
- Test suite is in `tests/` directory - run with `pytest`
- No linting/formatting configuration - consider adding black, flake8, mypy
- Package version is in `pyproject.toml` - update when releasing
- Examples in `examples/` directory demonstrate usage patterns
52 changes: 52 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,58 @@ You can also specify [custom sub agents](#subagents--optional-) with their own i
Sub agents are useful for ["context quarantine"](https://www.dbreunig.com/2025/06/26/how-to-fix-your-context.html#context-quarantine) (to help not pollute the overall context of the main agent)
as well as custom instructions.

## Development

This project includes a test suite and build tools for development. You'll need Python 3.11 or higher installed on your system.

### Setting Up a Development Environment

1. Create a virtual environment (use one of these names which are already in `.gitignore`):
```bash
python -m venv venv # or .venv, env, or ENV
source venv/bin/activate # On Windows: venv\Scripts\activate
```

2. Install the package with development dependencies:
```bash
pip install -e ".[dev]"
```

### Running Tests

Run all tests:
```bash
pytest
```

Run tests with coverage report:
```bash
pytest --cov=deepagents
```

Run a specific test file:
```bash
pytest tests/test_create_deep_agent.py
```

### Building the Package

To build the package for distribution:

1. Install the build tool:
```bash
pip install build
```

2. Build the package:
```bash
python -m build
```

This will create both wheel and source distributions in the `dist/` directory:
- `deepagents-{version}-py3-none-any.whl` - wheel distribution
- `deepagents-{version}.tar.gz` - source distribution

## Roadmap
- [ ] Allow users to customize full system prompt
- [ ] Code cleanliness (type hinting, docstrings, formating)
Expand Down
15 changes: 14 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,20 @@ name = "deepagents"
version = "0.0.3"
description = "General purpose 'deep agent' with sub-agent spawning, todo list capabilities, and mock file system. Built on LangGraph."
readme = "README.md"
license = { text = "MIT" }
license = "MIT"
requires-python = ">=3.11,<4.0"
dependencies = [
"langgraph>=0.2.6",
"langchain-anthropic>=0.1.23",
"langchain>=0.2.14",
]

[project.optional-dependencies]
dev = [
"pytest>=8.0.0",
"pytest-cov>=6.0.0",
]


[build-system]
requires = ["setuptools>=73.0.0", "wheel"]
Expand All @@ -23,3 +29,10 @@ packages = ["deepagents"]

[tool.setuptools.package-data]
"*" = ["py.typed"]

[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = "-v"
Empty file added tests/__init__.py
Empty file.
161 changes: 161 additions & 0 deletions tests/test_create_deep_agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import pytest
from unittest.mock import patch, MagicMock
from langchain_core.tools import tool
from langgraph.graph.state import CompiledStateGraph

from deepagents import create_deep_agent, DeepAgentState, SubAgent


class TestCreateDeepAgent:
"""Test suite for create_deep_agent function."""

@patch("deepagents.graph.get_default_model")
def test_create_deep_agent_happy_path(self, mock_get_default_model):
"""Test creating a deep agent with basic configuration."""
mock_model = MagicMock()
mock_get_default_model.return_value = mock_model

@tool
def sample_tool(query: str) -> str:
"""A sample tool for testing."""
return f"Result for: {query}"

instructions = "You are a helpful AI assistant for testing purposes."

agent = create_deep_agent(tools=[sample_tool], instructions=instructions)

assert agent is not None
assert isinstance(agent, CompiledStateGraph)
mock_get_default_model.assert_called_once()

@patch("deepagents.graph.get_default_model")
def test_create_deep_agent_with_subagents(self, mock_get_default_model):
"""Test creating a deep agent with subagents."""
mock_model = MagicMock()
mock_get_default_model.return_value = mock_model

research_agent = SubAgent(
name="research",
description="Research information on the web",
prompt="You are a research assistant. Focus on finding accurate information.",
tools=[],
)

agent = create_deep_agent(
tools=[],
instructions="Main agent with subagents",
subagents=[research_agent],
)

assert agent is not None
assert isinstance(agent, CompiledStateGraph)
mock_get_default_model.assert_called_once()

@patch("deepagents.graph.get_default_model")
def test_create_deep_agent_with_custom_model(self, mock_get_default_model):
"""Test creating a deep agent with a custom model."""
custom_model = MagicMock()

agent = create_deep_agent(
tools=[], instructions="Agent with custom model", model=custom_model
)

assert agent is not None
assert isinstance(agent, CompiledStateGraph)
mock_get_default_model.assert_not_called()

@patch("deepagents.graph.get_default_model")
def test_create_deep_agent_with_custom_state_schema(self, mock_get_default_model):
"""Test creating a deep agent with custom state schema."""
mock_model = MagicMock()
mock_get_default_model.return_value = mock_model

class CustomAgentState(DeepAgentState):
custom_field: str = ""

agent = create_deep_agent(
tools=[],
instructions="Agent with custom state",
state_schema=CustomAgentState,
)

assert agent is not None
assert isinstance(agent, CompiledStateGraph)
mock_get_default_model.assert_called_once()

@patch("deepagents.graph.get_default_model")
def test_create_deep_agent_with_multiple_tools(self, mock_get_default_model):
"""Test creating a deep agent with multiple custom tools."""
mock_model = MagicMock()
mock_get_default_model.return_value = mock_model

@tool
def calculator(expression: str) -> str:
"""Calculate mathematical expressions."""
return f"Calculated: {expression}"

@tool
def translator(text: str, target_language: str) -> str:
"""Translate text to target language."""
return f"Translated '{text}' to {target_language}"

agent = create_deep_agent(
tools=[calculator, translator], instructions="Multi-tool agent for testing"
)

assert agent is not None
assert isinstance(agent, CompiledStateGraph)
mock_get_default_model.assert_called_once()

@patch("deepagents.graph.get_default_model")
def test_create_deep_agent_empty_tools(self, mock_get_default_model):
"""Test creating a deep agent with no additional tools."""
mock_model = MagicMock()
mock_get_default_model.return_value = mock_model

agent = create_deep_agent(
tools=[], instructions="Basic agent with only built-in tools"
)

assert agent is not None
assert isinstance(agent, CompiledStateGraph)
mock_get_default_model.assert_called_once()

@patch("deepagents.graph.get_default_model")
def test_create_deep_agent_complex_scenario(self, mock_get_default_model):
"""Test creating a deep agent with all features combined."""
mock_model = MagicMock()
mock_get_default_model.return_value = mock_model

@tool
def web_search(query: str) -> str:
"""Search the web for information."""
return f"Search results for: {query}"

coding_agent = SubAgent(
name="coder",
description="Write and review code",
prompt="You are an expert programmer.",
tools=[],
)

writer_agent = SubAgent(
name="writer",
description="Write and edit text content",
prompt="You are a professional writer.",
tools=[],
)

class ProjectState(DeepAgentState):
project_name: str = "test_project"

agent = create_deep_agent(
tools=[web_search],
instructions="You are a project manager coordinating various tasks.",
subagents=[coding_agent, writer_agent],
state_schema=ProjectState,
)

assert agent is not None
assert isinstance(agent, CompiledStateGraph)
mock_get_default_model.assert_called_once()