Skip to content

Commit af73028

Browse files
authored
chore: add ci (#254)
Add CI
1 parent a409014 commit af73028

File tree

19 files changed

+321
-84
lines changed

19 files changed

+321
-84
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Helper to set up Python and uv with caching
2+
3+
name: uv-install
4+
description: Set up Python and uv with caching
5+
6+
inputs:
7+
python-version:
8+
description: Python version, supporting MAJOR.MINOR only
9+
required: true
10+
enable-cache:
11+
description: Enable caching for uv dependencies
12+
required: false
13+
default: "true"
14+
cache-suffix:
15+
description: Custom cache key suffix for cache invalidation
16+
required: false
17+
default: ""
18+
working-directory:
19+
description: Working directory for cache glob scoping
20+
required: false
21+
default: "**"
22+
23+
env:
24+
UV_VERSION: "0.5.25"
25+
26+
runs:
27+
using: composite
28+
steps:
29+
- name: Install uv and set the python version
30+
uses: astral-sh/setup-uv@v6
31+
with:
32+
version: ${{ env.UV_VERSION }}
33+
python-version: ${{ inputs.python-version }}
34+
enable-cache: ${{ inputs.enable-cache }}
35+
cache-dependency-glob: |
36+
${{ inputs.working-directory }}/pyproject.toml
37+
${{ inputs.working-directory }}/uv.lock
38+
${{ inputs.working-directory }}/requirements*.txt
39+
cache-suffix: ${{ inputs.cache-suffix }}

.github/workflows/_lint.yml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Reusable workflow for running linting
2+
3+
name: "🧹 Linting"
4+
5+
on:
6+
workflow_call:
7+
inputs:
8+
working-directory:
9+
required: true
10+
type: string
11+
description: "From which folder this pipeline executes"
12+
python-version:
13+
required: true
14+
type: string
15+
description: "Python version to use"
16+
17+
permissions:
18+
contents: read
19+
20+
env:
21+
WORKDIR: ${{ inputs.working-directory == '' && '.' || inputs.working-directory }}
22+
RUFF_OUTPUT_FORMAT: github
23+
UV_FROZEN: "true"
24+
25+
jobs:
26+
build:
27+
name: "Python ${{ inputs.python-version }}"
28+
runs-on: ubuntu-latest
29+
timeout-minutes: 20
30+
steps:
31+
- name: "📋 Checkout Code"
32+
uses: actions/checkout@v5
33+
34+
- name: "🐍 Set up Python ${{ inputs.python-version }} + UV"
35+
uses: "./.github/actions/uv_setup"
36+
with:
37+
python-version: ${{ inputs.python-version }}
38+
cache-suffix: lint-${{ inputs.working-directory }}
39+
working-directory: ${{ inputs.working-directory }}
40+
41+
- name: "📦 Install Dependencies"
42+
working-directory: ${{ inputs.working-directory }}
43+
run: |
44+
uv sync --group test --inexact
45+
46+
- name: "🔍 Run Linters"
47+
working-directory: ${{ inputs.working-directory }}
48+
run: |
49+
make lint

.github/workflows/_test.yml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Reusable workflow for running unit tests
2+
3+
name: "🧪 Unit Testing"
4+
5+
on:
6+
workflow_call:
7+
inputs:
8+
working-directory:
9+
required: true
10+
type: string
11+
description: "From which folder this pipeline executes"
12+
python-version:
13+
required: true
14+
type: string
15+
description: "Python version to use"
16+
17+
permissions:
18+
contents: read
19+
20+
env:
21+
UV_FROZEN: "true"
22+
UV_NO_SYNC: "true"
23+
24+
jobs:
25+
build:
26+
defaults:
27+
run:
28+
working-directory: ${{ inputs.working-directory }}
29+
runs-on: ubuntu-latest
30+
timeout-minutes: 20
31+
name: "Python ${{ inputs.python-version }}"
32+
steps:
33+
- name: "📋 Checkout Code"
34+
uses: actions/checkout@v5
35+
36+
- name: "🐍 Set up Python ${{ inputs.python-version }} + UV"
37+
uses: "./.github/actions/uv_setup"
38+
id: setup-python
39+
with:
40+
python-version: ${{ inputs.python-version }}
41+
cache-suffix: test-${{ inputs.working-directory }}
42+
working-directory: ${{ inputs.working-directory }}
43+
44+
- name: "📦 Install Test Dependencies"
45+
shell: bash
46+
run: uv sync --group test --dev
47+
48+
- name: "🧪 Run Unit Tests"
49+
shell: bash
50+
run: make test
51+
52+
- name: "🧹 Verify Clean Working Directory"
53+
shell: bash
54+
run: |
55+
set -eu
56+
STATUS="$(git status)"
57+
echo "$STATUS"
58+
echo "$STATUS" | grep 'nothing to commit, working tree clean'

.github/workflows/ci.yml

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Main CI workflow for DeepAgents monorepo
2+
#
3+
# Runs on every pull request:
4+
# - Linting for both packages (deepagents core and deepagents-cli)
5+
# - Unit Tests for both packages
6+
#
7+
# Tests run in parallel for optimal performance
8+
9+
name: "🔧 CI"
10+
11+
on:
12+
push:
13+
branches: [master]
14+
pull_request:
15+
merge_group:
16+
17+
# Cancel redundant workflow runs
18+
concurrency:
19+
group: ${{ github.workflow }}-${{ github.ref }}
20+
cancel-in-progress: true
21+
22+
permissions:
23+
contents: read
24+
25+
env:
26+
UV_FROZEN: "true"
27+
UV_NO_SYNC: "true"
28+
29+
jobs:
30+
# Run linting on both packages in parallel
31+
lint:
32+
if: false # Temporarily disabled - change to 'true' to re-enable
33+
strategy:
34+
matrix:
35+
include:
36+
# Root package: deepagents core
37+
- working-directory: "."
38+
python-version: "3.11"
39+
# CLI package
40+
- working-directory: "libs/deepagents-cli"
41+
python-version: "3.11"
42+
fail-fast: false
43+
uses: ./.github/workflows/_lint.yml
44+
with:
45+
working-directory: ${{ matrix.working-directory }}
46+
python-version: ${{ matrix.python-version }}
47+
secrets: inherit
48+
49+
# Run unit tests on both packages in parallel
50+
test:
51+
strategy:
52+
matrix:
53+
include:
54+
# Root package: test on multiple Python versions (3.11 = min, 3.13 = max)
55+
- working-directory: "."
56+
python-version: "3.11"
57+
- working-directory: "."
58+
python-version: "3.12"
59+
- working-directory: "."
60+
python-version: "3.13"
61+
# CLI package: test on min and max Python versions
62+
- working-directory: "libs/deepagents-cli"
63+
python-version: "3.11"
64+
- working-directory: "libs/deepagents-cli"
65+
python-version: "3.13"
66+
fail-fast: false
67+
uses: ./.github/workflows/_test.yml
68+
with:
69+
working-directory: ${{ matrix.working-directory }}
70+
python-version: ${{ matrix.python-version }}
71+
secrets: inherit
72+
73+
# Final status check - ensures all jobs passed
74+
ci_success:
75+
name: "✅ CI Success"
76+
needs: [test]
77+
if: always()
78+
runs-on: ubuntu-latest
79+
env:
80+
RESULTS_JSON: ${{ toJSON(needs.*.result) }}
81+
EXIT_CODE: ${{ !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') && '0' || '1' }}
82+
steps:
83+
- name: "🎉 All Checks Passed"
84+
run: |
85+
echo "$RESULTS_JSON"
86+
echo "Exiting with $EXIT_CODE"
87+
exit $EXIT_CODE

Makefile

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,10 @@ lint lint_diff lint_package lint_tests:
1212

1313
format format_diff:
1414
[ "$(PYTHON_FILES)" = "" ] || uv run --all-groups ruff format $(PYTHON_FILES)
15-
[ "$(PYTHON_FILES)" = "" ] || uv run --all-groups ruff check --fix $(PYTHON_FILES)
15+
[ "$(PYTHON_FILES)" = "" ] || uv run --all-groups ruff check --fix $(PYTHON_FILES)
16+
17+
test:
18+
uv run pytest tests/unit_tests --cov=deepagents --cov-report=term-missing
19+
20+
integration_test:
21+
uv run pytest tests/unit_tests --cov=deepagents --cov-report=term-missing

libs/deepagents-cli/deepagents_cli/agent_memory.py

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,15 @@
11
"""Middleware for loading agent-specific long-term memory into the system prompt."""
22

33
from collections.abc import Awaitable, Callable
4-
from typing import TYPE_CHECKING, Any
5-
6-
if TYPE_CHECKING:
7-
from langgraph.runtime import Runtime
4+
from typing import NotRequired
85

6+
from deepagents.backends.protocol import BackendProtocol
97
from langchain.agents.middleware.types import (
108
AgentMiddleware,
119
AgentState,
1210
ModelRequest,
1311
ModelResponse,
1412
)
15-
from typing_extensions import NotRequired, TypedDict
16-
17-
from deepagents.backends.protocol import BackendProtocol
1813

1914

2015
class AgentMemoryState(AgentState):
@@ -83,6 +78,7 @@ class AgentMemoryState(AgentState):
8378
</agent_memory>
8479
"""
8580

81+
8682
class AgentMemoryMiddleware(AgentMiddleware):
8783
"""Middleware for loading agent-specific long-term memory.
8884
@@ -105,7 +101,7 @@ class AgentMemoryMiddleware(AgentMiddleware):
105101
# Set up backend pointing to agent's directory
106102
agent_dir = Path.home() / ".deepagents" / "my-agent"
107103
backend = FilesystemBackend(root_dir=agent_dir)
108-
104+
109105
# Create middleware
110106
middleware = AgentMemoryMiddleware(backend=backend)
111107
```
@@ -185,13 +181,17 @@ def wrap_model_call(
185181
"""
186182
# Get agent memory from state
187183
agent_memory = request.state.get("agent_memory", "")
188-
184+
189185
memory_section = self.system_prompt_template.format(agent_memory=agent_memory)
190186
if request.system_prompt:
191187
request.system_prompt = memory_section + "\n\n" + request.system_prompt
192188
else:
193189
request.system_prompt = memory_section
194-
request.system_prompt = request.system_prompt + "\n\n" + LONGTERM_MEMORY_SYSTEM_PROMPT.format(memory_path=self.memory_path)
190+
request.system_prompt = (
191+
request.system_prompt
192+
+ "\n\n"
193+
+ LONGTERM_MEMORY_SYSTEM_PROMPT.format(memory_path=self.memory_path)
194+
)
195195

196196
return handler(request)
197197

@@ -211,12 +211,16 @@ async def awrap_model_call(
211211
"""
212212
# Get agent memory from state
213213
agent_memory = request.state.get("agent_memory", "")
214-
214+
215215
memory_section = self.system_prompt_template.format(agent_memory=agent_memory)
216216
if request.system_prompt:
217217
request.system_prompt = memory_section + "\n\n" + request.system_prompt
218218
else:
219219
request.system_prompt = memory_section
220-
request.system_prompt = request.system_prompt + "\n\n" + LONGTERM_MEMORY_SYSTEM_PROMPT.format(memory_path=self.memory_path)
220+
request.system_prompt = (
221+
request.system_prompt
222+
+ "\n\n"
223+
+ LONGTERM_MEMORY_SYSTEM_PROMPT.format(memory_path=self.memory_path)
224+
)
221225

222226
return await handler(request)

libs/deepagents-cli/deepagents_cli/ui.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ class TokenTracker:
166166

167167
def __init__(self):
168168
self.baseline_context = 0 # Baseline system context (system + agent.md + tools)
169-
self.current_context = 0 # Total context including messages
169+
self.current_context = 0 # Total context including messages
170170
self.last_output = 0
171171

172172
def set_baseline(self, tokens: int):

pyproject.toml

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,19 @@ Twitter = "https://x.com/LangChainAI"
2121
Slack = "https://www.langchain.com/join-community"
2222
Reddit = "https://www.reddit.com/r/LangChain/"
2323

24-
[project.optional-dependencies]
25-
dev = [
26-
"pytest",
27-
"pytest-cov",
28-
"build",
29-
"twine",
30-
"langchain-openai",
31-
]
3224

3325
[dependency-groups]
34-
lint = [
35-
"ruff>=0.12.2,<0.13.0",
26+
test = [
27+
"pytest",
28+
"pytest-cov",
29+
"ruff>=0.12.2,<0.13.0",
30+
"mypy>=1.18.1,<1.19.0",
3631
]
37-
typing = [
38-
"mypy>=1.18.1,<1.19.0",
32+
33+
dev = [
34+
"langchain-openai",
35+
"twine",
36+
"build",
3937
]
4038

4139
[build-system]

src/deepagents/backends/composite.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
"""CompositeBackend: Route operations to different backends based on path prefix."""
22

3-
43
from deepagents.backends.protocol import BackendProtocol, EditResult, WriteResult
54
from deepagents.backends.state import StateBackend
65
from deepagents.backends.utils import FileInfo, GrepMatch

src/deepagents/backends/state.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
"""StateBackend: Store files in LangGraph agent state (ephemeral)."""
22

3-
43
from langchain.tools import ToolRuntime
54

65
from deepagents.backends.protocol import EditResult, WriteResult

0 commit comments

Comments
 (0)