Skip to content
Merged
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
248 changes: 172 additions & 76 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,84 +1,180 @@
# apricot
apricot is a self-hostable code agent , unlike tools that only look at diffs, apricot understands your entire codebase — reviews PR, write testcases
--
testing this works!!!
--
# repo structure

[![Typing SVG](https://readme-typing-svg.demolab.com?font=Fira+Code&duration=2800&pause=800&color=7C3AED&width=900&lines=Apricot+reviews+PRs+with+full-codebase+context;Apricot+generates+pytest+tests+from+diffs;Apricot+can+comment+on+PRs+and+push+generated+tests)](https://github.com/Neuropole/apricot)

Apricot is a self-hosted AI code agent for pull requests.
It analyzes the latest Git diff, retrieves relevant code context from the repository, generates:

1. **A structured review comment**
2. **Pytest test cases** (saved to `tests/test_generated.py`)

Then it can post the review on the PR and commit generated tests back to the PR branch.

---

## What it does

- Reads recent code changes (`git diff HEAD~1 HEAD`)
- Builds context from Python files in the repo
- Chunks + embeds code with Sentence Transformers
- Stores/query embeddings with ChromaDB
- Uses Groq LLM for:
- review generation
- pytest generation
- Publishes output to GitHub PR comments
- Commits generated tests to the source PR branch

---

## Architecture

```mermaid
flowchart LR
A[PR Event / Local Run] --> B[agent.main]
B --> C[Diff Reader]
B --> D[Codebase Parser]
D --> E[Chunker]
E --> F[Embedder]
F --> G[Vector Store]
C --> H[Diff Embedding]
H --> G
G --> I[Relevant Context]
I --> J[Groq Review Generator]
I --> K[Groq Test Generator]
K --> L[tests/test_generated.py]
J --> M[GitHub PR Commenter]
L --> N[GitHub Committer]
```

### Runtime flow

```mermaid
sequenceDiagram
participant GH as GitHub Actions
participant Agent as agent.main
participant VS as Chroma Vector Store
participant LLM as Groq API
participant PR as Pull Request

GH->>Agent: Run on pull_request (opened/synchronize)
Agent->>Agent: git diff HEAD~1 HEAD
Agent->>Agent: Parse + chunk Python files
Agent->>LLM: Create embeddings (via local model)
Agent->>VS: Store code embeddings
Agent->>VS: Query relevant chunks using diff embedding
VS-->>Agent: Context chunks
Agent->>LLM: Generate review + pytest tests
Agent->>PR: Post review comment
Agent->>PR: Commit tests/test_generated.py
```
ai-code-agent/
├── .github/
│ └── workflows/
│ └── agent.yml # GitHub Actions pipeline
├── agent/ # Core agent logic
│ ├── __init__.py
│ ├── main.py # Entry point (called by CI)
│ │
│ ├── config.py # API keys, configs
│ │

---

## Project structure

```text
apricot/
├── .github/workflows/agent.yml
├── agent/
│ ├── main.py
│ ├── config.py
│ ├── llm/
│ │ ├── groq_client.py # Groq API wrapper
│ │ ── prompts.py # All prompts (centralized 🔥)
│ │
│ │ ├── groq_client.py
│ │ ── prompts.py
│ │ └── test_generator.py
│ ├── indexing/
│ │ ├── parser.py # tree-sitter parsing
│ │ ├── chunker.py # split code into chunks
│ │ ├── embedder.py # embeddings logic
│ │ └── vector_store.py # ChromaDB wrapper
│ │
│ ├── retrieval/
│ │ └── retriever.py # top-k context fetch
│ │
│ ├── intent/
│ │ └── infer.py # 🔥 intent + property inference
│ │
│ ├── testgen/
│ │ ├── generator.py # test generation
│ │ └── style_matcher.py # detect pytest/jest etc.
│ │
│ ├── execution/
│ │ ├── runner.py # run tests (pytest etc.)
│ │ └── coverage.py # coverage analysis
│ │
│ ├── feedback/
│ │ └── loop.py # refine tests (self-improve 🔥)
│ │
│ │ ├── parser.py
│ │ ├── chunker.py
│ │ ├── embedder.py
│ │ └── vector_store.py
│ └── github/
│ ├── commenter.py # PR comments
│ └── committer.py # push test files
├── scripts/
│ ├── setup.sh
│ └── run_local.py # run agent locally (debugging)
├── .agent/ # persistent memory (IMPORTANT)
│ ├── context.md # codebase summary
│ ├── embeddings/ # cached embeddings
│ └── intent_cache.json # inferred properties
├── tests/ # agent’s own tests
├── requirements.txt
├── README.md
└── .gitignore
│ ├── commenter.py
│ └── committer.py
├── tests/
│ ├── test_generated.py
│ └── test_test_generator_formatting.py
└── requirements.txt
```

---

## Quick start (local)

### 1) Install dependencies

```bash
pip install -r requirements.txt
```

### 2) Configure environment variables

Create a `.env` file in the repository root:

```env
GROQ_API_KEY=your_groq_api_key
GITHUB_TOKEN=your_github_token
GITHUB_REPOSITORY=owner/repo
PR_NUMBER=123
GITHUB_HEAD_REF=feature-branch-name
```
# end to end flow

### 3) Run the agent

```bash
python -m agent.main
```
PR opened →
GitHub Actions triggered →
1. Clone repo
2. Load cached embeddings (.agent/)
3. Parse changed files (diff)
4. Embed only changed/new files
5. Retrieve relevant context (RAG)
6. Infer intent + properties 🔥
7. Detect test framework
8. Generate tests
9. Run tests (pytest)
10. Collect failures + coverage
11. Feedback loop (optional refine) 🔥
12. Commit tests OR comment on PR
```

---

## GitHub Actions integration

This repo includes `.github/workflows/agent.yml` that triggers on:

- `pull_request` events: `opened`, `synchronize`

The workflow:

1. checks out code with full history
2. sets PR number
3. installs Python + requirements
4. runs `python -m agent.main`

Required secrets:

- `GROQ_API_KEY`
- `GITHUB_TOKEN` (GitHub-provided token works in Actions)

---

## Outputs

Apricot produces:

- **PR review comment** (bugs, improvements, suggestions)
- **Generated tests file** at `tests/test_generated.py`

---



### CI pulse

![Workflow Status](https://img.shields.io/github/actions/workflow/status/Neuropole/apricot/agent.yml?branch=main&style=for-the-badge)

### AI activity banner (animated)

![AI Activity](https://readme-typing-svg.demolab.com?font=JetBrains+Mono&duration=2200&pause=700&color=22C55E&width=900&lines=Diff+%E2%86%92+Context+Retrieval+%E2%86%92+LLM+Review;Diff+%E2%86%92+Context+Retrieval+%E2%86%92+Test+Generation;PR+Comment+%2B+Auto-Commit+of+Generated+Tests)

---

## Notes and current scope

- Current implementation focuses on **Python codebases**
- Test generation currently targets **pytest**
- Diff source is the latest commit range (`HEAD~1..HEAD`)

---

## License

MIT License (see `LICENSE`).
27 changes: 26 additions & 1 deletion tests/test_generated.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,27 @@
def test_empty_diff():
def test_get_pr_diff():
try:
subprocess.check_output(["git", "diff", "HEAD~1", "HEAD"], text=True)
except subprocess.CalledProcessError as e:
assert e.returncode != 0

def test_generate_review():
review = generate_review("test_diff")
assert review is not None

def test_post_comment():
post_comment("test_comment")
assert True

def test_generate_pytest_test():
test_case = generate_pytest_test("test_diff")
assert test_case is not None

def test_main_workflow():
# Simulate GitHub Actions workflow
os.environ["GROQ_API_KEY"] = "test_api_key"
os.environ["GITHUB_TOKEN"] = "test_token"
os.environ["GITHUB_REPOSITORY"] = "test_repo"
os.environ["PR_NUMBER"] = "123"
os.environ["GITHUB_HEAD_REF"] = "feature-branch-name"
main()
assert True