diff --git a/README.md b/README.md index 1c408d3..4283b3d 100644 --- a/README.md +++ b/README.md @@ -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 -``` \ No newline at end of file + +--- + +## 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`). diff --git a/tests/test_generated.py b/tests/test_generated.py index 1f0cf89..253ce4e 100644 --- a/tests/test_generated.py +++ b/tests/test_generated.py @@ -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 \ No newline at end of file