Project Overview
DAIV is an AI-powered development assistant built on Django with Django Tasks for async task processing, LangChain/LangGraph for LLM integration, and includes daiv-sandbox for sandboxed command execution. It integrates with GitLab and GitHub to automate issue resolution, code reviews, and CI/CD pipeline repairs.
Project Structure
daiv/- Django project with all the application code.automation/- Automation module with all the agents logic and tools.codebase/- Codebase module with all the repository interaction and related logic.chat/- Chat module with the OpenAI compatible API.core/- Core module with common logic.slash_commands/- Slash commands module.daiv/- Main logic of the Django project: settings, urls, wsgi, asgi, tasks, etc.
docker/- Dockerfiles and configurations for local and production deployments.docs/- Documentation for the project.evals/- Evaluation suite for the project (openevals + langsmith + pytest).tests/- Test suite for the project (pytest).
Build, Lint, and Test Commands
Testing
make test # run all unit tests with coverage
uv run pytest tests/automation/test_utils.py # run a specific test file
uv run pytest tests/automation/test_utils.py::TestFileReadFunctions::test_register_file_read # run a single test
uv run pytest tests/ -k "test_notes" # run tests matching pattern
uv run pytest tests/ -v # verbose output
Linting & Formatting
make lint-fix # check and fix linting and formatting issues (recommended)
make lint # check linting and formatting without fixing
make lint-check # run ruff linter check only
make lint-format # check code formatting only
make lint-typing # run type checking with mypy
IMPORTANT: Always run make lint-fix instead of make lint to automatically fix issues. Only manually fix issues that cannot be auto-fixed by make lint-fix.
Building Documentation
make docs-serve # serve documentation locally at localhost:4000
Code Style Guidelines
Formatting
- Line length: 120 characters maximum
- Use double quotes for strings
- No trailing commas in function calls (skip-magic-trailing-comma enabled)
- Use type hints for all function parameters and return types
Naming Conventions
- Functions/variables:
snake_case - Classes:
PascalCase - Constants:
UPPER_SNAKE_CASE - Private methods/attributes: prefix with single underscore
_private_method - Django models: use verbose names with
gettext_lazyfor i18n
Type Annotations
- Always provide type hints for function parameters and return values
- Use
str | Noneinstead ofOptional[str](Python 3.10+ union syntax) - Use
list[Type]instead ofList[Type](built-in generics) - Use
dict[str, Any]instead ofDict[str, Any] - Annotate class attributes in Django models when needed
Example:
def commit_changes(
self,
commit_message: str,
*,
branch_name: str,
skip_ci: bool = False,
) -> str:
"""Commit changes to the repository."""
...
Docstrings
- Use Google-style docstrings for all public functions and classes
- Include Args, Returns, and Raises sections as needed
- Keep descriptions concise but informative
Example:
def get_repo_ref(repo: Repo) -> str:
"""
Get the current reference (branch name or commit SHA) from a repository.
When HEAD is attached to a branch, returns the branch name.
When HEAD is detached (e.g., checking out a specific commit), returns the commit SHA.
Args:
repo: The Git repository object.
Returns:
The branch name if HEAD is attached, or the commit SHA if HEAD is detached.
"""
Error Handling
- Raise
ValueErrorfor invalid arguments - Raise
RuntimeErrorfor operation failures - Use
contextlib.suppress()for expected, ignorable exceptions - Log warnings with
logger.warning()for non-critical issues - Chain exceptions with
raise ... from eto preserve context
Example:
try:
self.repo.git.apply(*diff_args, tmp_path)
except GitCommandError as e:
raise RuntimeError("git apply failed. The patch is not valid.") from e
Async Code
- Use
async/awaitfor async functions - Use
asyncio.gather()for concurrent operations
Tool State Updates
CRITICAL: Tools CANNOT directly modify runtime.state. To update state, tools must return a Command object from langgraph.types:
from langgraph.types import Command
from langchain_core.messages import ToolMessage
@tool("my_tool")
async def my_tool(param: str, runtime: ToolRuntime) -> str | Command:
# Simple case: just return output as string
if not needs_state_update:
return "output"
# When state update is needed: return Command with ToolMessage
output = "tool output"
state_update = {
"key": "value",
"messages": [ToolMessage(content=output, tool_call_id=runtime.tool_call_id)]
}
return Command(update=state_update)
Examples:
- ❌
runtime.state["key"] = "value"- WRONG! Direct state modification doesn't work - ❌
return Command(update={"key": "value"}, resume=output)- WRONG! Output should be in ToolMessage - ✅
return Command(update={"key": "value", "messages": [ToolMessage(content=output, tool_call_id=runtime.tool_call_id)]})- CORRECT!
Note: When a tool needs to both return output and update state, the output MUST be included as a ToolMessage in the messages key of the state update. The tool_call_id must match runtime.tool_call_id.
Testing Command returns: In unit tests, when calling a tool directly (not through the agent framework), you must manually handle Command returns:
result = await tool.coroutine(params, runtime=runtime)
if isinstance(result, Command):
# Extract output from ToolMessage
messages = result.update.get("messages", [])
output = messages[0].content if messages else None
# Apply state updates (excluding messages which are handled by framework)
state_updates = {k: v for k, v in result.update.items() if k != "messages"}
runtime.state.update(state_updates)
else:
output = result
Dependency Management
Use uv to manage dependencies. All dependencies are defined in pyproject.toml.
IMPORTANT: Never edit pyproject.toml directly. Use uv commands and pin dependencies to exact versions with ==.
uv sync --all-groups # install all dependencies
uv sync --only-group=dev # install only dev dependencies
uv add <package>==<version> # add a new dependency with exact version
uv remove <package> # remove a dependency
uv lock # update the lock file
Testing Guidelines
- Use
pytestwithpytest-asynciofor async tests - Organize tests in functions.
- Use descriptive test names:
test_<what_it_does> - Use fixtures for common setup (e.g.,
@pytest.fixture) - Mock external dependencies for unit tests with
unittest.mockorpytest-mockorpytest-httpx. - All unit tests should be in the
tests/unit_tests/directory mirroring thedaiv/structure - All integration tests should be in the
tests/integration_tests/directory.
Documentation
- Use
mkdocsfor documentation (Material theme) - Documentation is located in
docs/directory - Add/update docs when adding new features or making significant changes
- Run
make docs-serveto preview documentation locally
Changelog
ALWAYS update CHANGELOG.md after making changes to the project using the changelog-curator subagent.
Translations
Translations are in locale/ directories with subdirectories for each language.
make makemessages # generate translation files
# Edit *.po files with translations
make compilemessages # compile translations
Repository Conventions
Branch Naming
Format: <prefix>/<short-kebab-summary>
Prefixes:
feat/- new features (e.g.,feat/add-github-integration)fix/- bug fixes (e.g.,fix/resolve-memory-leak)chore/- maintenance tasks (e.g.,chore/update-dependencies)
Commit Messages
Follow Conventional Commits:
- Format:
<type>: <short summary> - Types:
feat,fix,chore,docs,refactor,test,style,perf,ci,build - Summary: lowercase, no period, max 72 characters
Examples:
feat: add gitlab webhook supportfix: resolve race condition in task queuedocs: update installation instructions