Documentation Index
Fetch the complete documentation index at: https://mintlify.com/bytedance/deer-flow/llms.txt
Use this file to discover all available pages before exploring further.
DeerFlow follows consistent code style guidelines to ensure maintainability and readability across the codebase.
Python Code Style
We use Ruff for both linting and formatting:
- Linter: Catches code quality issues, unused imports, and potential bugs
- Formatter: Ensures consistent code formatting across the project
Ruff Configuration
Configuration is defined in backend/ruff.toml:
line-length = 240
target-version = "py312"
[lint]
select = ["E", "F", "I", "UP"]
ignore = []
[format]
quote-style = "double"
indent-style = "space"
Configuration Details
Line Length: 240 characters
- Longer than PEP 8’s 79 characters
- Allows for more readable complex expressions
- Reduces need for line continuations
- Modern wide-screen displays accommodate longer lines
Target Version: Python 3.12+
- Uses latest Python features
- Ruff suggests modern syntax improvements
Lint Rules:
E - pycodestyle error rules
F - Pyflakes rules (undefined names, unused imports)
I - isort import sorting rules
UP - pyupgrade rules (syntax modernization)
Quote Style: Double quotes (")
- Consistent with JSON and YAML
- More readable for strings containing apostrophes
Indent Style: Spaces (4 spaces per level)
- Standard Python indentation
- More consistent across editors
Running Ruff
From the backend/ directory:
# Check for linting issues
make lint
# or
uvx ruff check .
# Format code and fix auto-fixable issues
make format
# or
uvx ruff check . --fix && uvx ruff format .
# Format code only
uvx ruff format .
# Check specific file
uvx ruff check src/agents/lead_agent/agent.py
# Format specific file
uvx ruff format src/agents/lead_agent/agent.py
Pre-commit Integration
Run Ruff automatically before committing:
# .pre-commit-config.yaml
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.14.11
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
Install pre-commit hooks:
pip install pre-commit
pre-commit install
Python Style Guidelines
Type Hints
Always use type hints for function parameters and return values:
# Good
def process_message(message: str, thread_id: str) -> dict[str, Any]:
return {"status": "success", "thread_id": thread_id}
# Bad
def process_message(message, thread_id):
return {"status": "success", "thread_id": thread_id}
For complex types, use typing module:
from typing import Any, Optional
from collections.abc import Sequence
def get_tools(
groups: Sequence[str],
include_mcp: bool = True,
model_name: Optional[str] = None,
) -> list[Any]:
pass
Imports
Order imports using isort rules (automatically handled by Ruff):
- Standard library imports
- Third-party imports
- Local application imports
# Standard library
import os
import sys
from pathlib import Path
# Third-party
import httpx
import yaml
from fastapi import FastAPI
from langchain_core.messages import AIMessage
# Local
from src.agents.thread_state import ThreadState
from src.config import get_app_config
Use absolute imports for project modules:
# Good
from src.agents.lead_agent import make_lead_agent
from src.sandbox.local import LocalSandboxProvider
# Bad (relative imports)
from ..lead_agent import make_lead_agent
from .local import LocalSandboxProvider
Naming Conventions
Modules and packages: lowercase_with_underscores
src/agents/middlewares/
src/sandbox/tools.py
Classes: PascalCase
class ThreadState:
pass
class LocalSandboxProvider:
pass
Functions and variables: lowercase_with_underscores
def create_chat_model(name: str) -> Any:
pass
thread_id = "abc123"
max_concurrent_subagents = 3
Constants: UPPERCASE_WITH_UNDERSCORES
MAX_CONCURRENT_SUBAGENTS = 3
DEFAULT_TIMEOUT = 15 * 60
KUBECONFIG_PATH = "/etc/k8s/config"
Private attributes: Prefix with single underscore
class Client:
def __init__(self):
self._agent = None
self._model_name = None
Docstrings
Use docstrings for modules, classes, and functions:
def create_chat_model(name: str, thinking_enabled: bool = False) -> Any:
"""Create a chat model instance from configuration.
Args:
name: Model name as defined in config.yaml
thinking_enabled: Whether to enable extended thinking mode
Returns:
Instantiated chat model object
Raises:
ValueError: If model name not found in configuration
"""
pass
For simple functions, a one-line docstring is sufficient:
def get_thread_path(thread_id: str) -> Path:
"""Get the filesystem path for a thread's data directory."""
return Path(f".deer-flow/threads/{thread_id}")
Use comments sparingly - prefer self-documenting code:
# Good: Code is self-explanatory
if model.supports_vision and image_data:
messages.append(create_image_message(image_data))
# Bad: Comment states the obvious
# Check if model supports vision and image data exists
if model.supports_vision and image_data:
# Append image message
messages.append(create_image_message(image_data))
Use comments for complex logic:
# Virtual path system maps agent-visible paths to physical locations:
# /mnt/user-data/{workspace,uploads,outputs} -> backend/.deer-flow/threads/{thread_id}/user-data/...
# /mnt/skills -> deer-flow/skills/
def replace_virtual_path(virtual_path: str, thread_id: str) -> str:
pass
Function Length
Keep functions focused - aim for functions under 50 lines:
# Good: Single responsibility
def validate_config(config: dict) -> None:
"""Validate configuration schema."""
if not config.get("models"):
raise ValueError("No models configured")
validate_model_configs(config["models"])
validate_tool_configs(config.get("tools", []))
def validate_model_configs(models: list) -> None:
"""Validate model configurations."""
for model in models:
if not model.get("name"):
raise ValueError("Model missing name")
# ...
Error Handling
Use specific exceptions:
# Good
if not config_path.exists():
raise FileNotFoundError(f"Config file not found: {config_path}")
if not api_key:
raise ValueError("API key required for OpenAI models")
# Bad
if not config_path.exists():
raise Exception("Config file not found")
Provide context in error messages:
# Good
raise ValueError(
f"Model '{model_name}' not found in configuration. "
f"Available models: {', '.join(available_models)}"
)
# Bad
raise ValueError("Model not found")
Use f-strings for string formatting:
# Good
path = f"/mnt/user-data/{thread_id}/workspace"
message = f"Processing {len(items)} items"
# Acceptable for very simple cases
path = "/mnt/user-data/" + thread_id + "/workspace"
# Bad (outdated)
path = "/mnt/user-data/{}/workspace".format(thread_id)
path = "/mnt/user-data/%s/workspace" % thread_id
Dictionary and List Handling
Use dict.get() with defaults:
# Good
api_key = config.get("api_key", os.getenv("OPENAI_API_KEY"))
max_tokens = config.get("max_tokens", 2000)
# Bad (raises KeyError if missing)
api_key = config["api_key"]
Use comprehensions for simple transformations:
# Good
enabled_skills = [s for s in skills if s.enabled]
model_names = [m.name for m in config.models]
# Bad (verbose)
enabled_skills = []
for s in skills:
if s.enabled:
enabled_skills.append(s)
Context Managers
Use context managers for resource management:
# Good
with open(config_path) as f:
config = yaml.safe_load(f)
with tempfile.TemporaryDirectory() as tmpdir:
process_files(tmpdir)
# Bad (resource leak risk)
f = open(config_path)
config = yaml.safe_load(f)
f.close()
Data Classes
Use Pydantic models for configuration and data validation:
from pydantic import BaseModel, Field
class ModelConfig(BaseModel):
"""Configuration for a single LLM model."""
name: str
display_name: str
use: str = Field(..., description="Module path to model class")
api_key: str | None = None
supports_thinking: bool = False
supports_vision: bool = False
Frontend Code Style
TypeScript Guidelines
The frontend uses TypeScript with ESLint and Prettier:
# From frontend/ directory
# Run linter
pnpm lint
# Run linter with auto-fix
pnpm lint:fix
# Format code
pnpm format
TypeScript Style Highlights
- Interfaces over types for object shapes
- Explicit return types for functions
- Functional components with hooks
- Named exports for components
- Async/await over promises
Documentation Standards
Code Documentation Policy
CRITICAL: Always update documentation after code changes
When making code changes:
- Update
README.md for user-facing changes
- Update
CLAUDE.md for development/architecture changes
- Update inline code documentation
- Update API documentation if endpoints change
- Add migration notes for breaking changes
- Use clear, concise language
- Include code examples
- Explain why, not just what
- Keep documentation in sync with code
- Use proper Markdown formatting
Linting CI Integration
Linting runs automatically in CI:
# In CI pipeline
cd backend
make lint
# Must pass for PR approval
Fix linting issues before pushing:
# Fix issues automatically
make format
# Check remaining issues
make lint
Configuration Files Reference
Backend Configuration
backend/ruff.toml - Ruff linter and formatter configuration
line-length = 240
target-version = "py312"
[lint]
select = ["E", "F", "I", "UP"]
ignore = []
[format]
quote-style = "double"
indent-style = "space"
backend/pyproject.toml - Python project metadata and dependencies
[project]
name = "deer-flow"
version = "0.1.0"
requires-python = ">=3.12"
[dependency-groups]
dev = ["pytest>=8.0.0", "ruff>=0.14.11"]
Style Checklist
Before committing code:
Resources