codeguardian-offline 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- codeguardian_offline-0.1.0/PKG-INFO +155 -0
- codeguardian_offline-0.1.0/README.md +117 -0
- codeguardian_offline-0.1.0/codeguardian/__init__.py +1 -0
- codeguardian_offline-0.1.0/codeguardian/ai/__init__.py +1 -0
- codeguardian_offline-0.1.0/codeguardian/ai/health.py +62 -0
- codeguardian_offline-0.1.0/codeguardian/ai/ollama_client.py +26 -0
- codeguardian_offline-0.1.0/codeguardian/ai/rag.py +68 -0
- codeguardian_offline-0.1.0/codeguardian/ai/test_generator.py +51 -0
- codeguardian_offline-0.1.0/codeguardian/cli/__init__.py +1 -0
- codeguardian_offline-0.1.0/codeguardian/cli/ask.py +83 -0
- codeguardian_offline-0.1.0/codeguardian/cli/ci.py +47 -0
- codeguardian_offline-0.1.0/codeguardian/cli/generate.py +20 -0
- codeguardian_offline-0.1.0/codeguardian/cli/init.py +68 -0
- codeguardian_offline-0.1.0/codeguardian/cli/report.py +42 -0
- codeguardian_offline-0.1.0/codeguardian/cli/scan.py +39 -0
- codeguardian_offline-0.1.0/codeguardian/cli/serve.py +23 -0
- codeguardian_offline-0.1.0/codeguardian/cli/test.py +93 -0
- codeguardian_offline-0.1.0/codeguardian/core/__init__.py +1 -0
- codeguardian_offline-0.1.0/codeguardian/core/chroma_compat.py +26 -0
- codeguardian_offline-0.1.0/codeguardian/core/config_loader.py +41 -0
- codeguardian_offline-0.1.0/codeguardian/core/config_models.py +79 -0
- codeguardian_offline-0.1.0/codeguardian/core/db.py +90 -0
- codeguardian_offline-0.1.0/codeguardian/core/extras.py +20 -0
- codeguardian_offline-0.1.0/codeguardian/core/logger.py +16 -0
- codeguardian_offline-0.1.0/codeguardian/main.py +21 -0
- codeguardian_offline-0.1.0/codeguardian/reports/__init__.py +1 -0
- codeguardian_offline-0.1.0/codeguardian/reports/generator.py +78 -0
- codeguardian_offline-0.1.0/codeguardian/reports/junit.py +56 -0
- codeguardian_offline-0.1.0/codeguardian/scanner/__init__.py +1 -0
- codeguardian_offline-0.1.0/codeguardian/scanner/grammars.py +71 -0
- codeguardian_offline-0.1.0/codeguardian/scanner/ruff_runner.py +34 -0
- codeguardian_offline-0.1.0/codeguardian/scanner/semgrep_runner.py +36 -0
- codeguardian_offline-0.1.0/codeguardian/scanner/tree_sitter_runner.py +80 -0
- codeguardian_offline-0.1.0/codeguardian/testers/__init__.py +1 -0
- codeguardian_offline-0.1.0/codeguardian/testers/api.py +81 -0
- codeguardian_offline-0.1.0/codeguardian/testers/ui.py +82 -0
- codeguardian_offline-0.1.0/codeguardian_offline.egg-info/PKG-INFO +155 -0
- codeguardian_offline-0.1.0/codeguardian_offline.egg-info/SOURCES.txt +42 -0
- codeguardian_offline-0.1.0/codeguardian_offline.egg-info/dependency_links.txt +1 -0
- codeguardian_offline-0.1.0/codeguardian_offline.egg-info/entry_points.txt +2 -0
- codeguardian_offline-0.1.0/codeguardian_offline.egg-info/requires.txt +31 -0
- codeguardian_offline-0.1.0/codeguardian_offline.egg-info/top_level.txt +1 -0
- codeguardian_offline-0.1.0/pyproject.toml +74 -0
- codeguardian_offline-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: codeguardian-offline
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Offline AI QA Assistant
|
|
5
|
+
Author-email: CodeGuardian Maintainers <maintainers@example.com>
|
|
6
|
+
Classifier: Programming Language :: Python :: 3
|
|
7
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
8
|
+
Classifier: Operating System :: OS Independent
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
Requires-Dist: typer>=0.12.0
|
|
14
|
+
Requires-Dist: rich>=13.0.0
|
|
15
|
+
Requires-Dist: sqlmodel>=0.0.16
|
|
16
|
+
Requires-Dist: httpx>=0.27.0
|
|
17
|
+
Provides-Extra: ui
|
|
18
|
+
Requires-Dist: playwright>=1.42.0; extra == "ui"
|
|
19
|
+
Provides-Extra: ai
|
|
20
|
+
Requires-Dist: ollama<1.0,>=0.2.0; extra == "ai"
|
|
21
|
+
Requires-Dist: chromadb<0.6.0,>=0.4.24; extra == "ai"
|
|
22
|
+
Requires-Dist: sentence-transformers>=2.5.1; extra == "ai"
|
|
23
|
+
Provides-Extra: scan
|
|
24
|
+
Requires-Dist: tree-sitter>=0.21.0; extra == "scan"
|
|
25
|
+
Requires-Dist: tree-sitter-python>=0.21.0; extra == "scan"
|
|
26
|
+
Requires-Dist: tree-sitter-javascript>=0.21.0; extra == "scan"
|
|
27
|
+
Requires-Dist: tree-sitter-java>=0.21.0; extra == "scan"
|
|
28
|
+
Provides-Extra: server
|
|
29
|
+
Requires-Dist: fastapi>=0.110.0; extra == "server"
|
|
30
|
+
Requires-Dist: uvicorn>=0.29.0; extra == "server"
|
|
31
|
+
Provides-Extra: full
|
|
32
|
+
Requires-Dist: codeguardian[ai,scan,server,ui]; extra == "full"
|
|
33
|
+
Provides-Extra: dev
|
|
34
|
+
Requires-Dist: codeguardian[full]; extra == "dev"
|
|
35
|
+
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
36
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
|
|
37
|
+
Requires-Dist: ruff>=0.4.0; extra == "dev"
|
|
38
|
+
|
|
39
|
+
# CodeGuardian
|
|
40
|
+
|
|
41
|
+
An **Offline AI QA Assistant** that tests applications automatically and explains failures.
|
|
42
|
+
|
|
43
|
+
No internet. No API keys. No cloud services.
|
|
44
|
+
|
|
45
|
+
## Architecture & Features
|
|
46
|
+
|
|
47
|
+
- **CLI Automation**: Built with `Typer` and `Rich` for a premium terminal experience.
|
|
48
|
+
- **API Testing**: Uses `HTTPX` to validate JSON endpoints.
|
|
49
|
+
- **UI Testing**: Uses `Playwright` for headless browser automation and UI validation.
|
|
50
|
+
- **Static Analysis**: Hooks into `Semgrep` and `Ruff`.
|
|
51
|
+
- **RAG & Knowledge Base**: Parses source code with `Tree-sitter`, embeds it using `SentenceTransformers`, and stores it in `ChromaDB`.
|
|
52
|
+
- **Local AI Explanations**: Queries your local `Ollama` (`qwen2.5-coder` recommended) to explain test failures by correlating test errors with the relevant source code.
|
|
53
|
+
|
|
54
|
+
## Installation
|
|
55
|
+
|
|
56
|
+
Ensure you have Python 3.10+ installed.
|
|
57
|
+
|
|
58
|
+
CodeGuardian uses optional dependencies so you only install what you need.
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
git clone <repository>
|
|
62
|
+
cd CodeGuardian
|
|
63
|
+
python -m venv venv
|
|
64
|
+
|
|
65
|
+
# On Windows:
|
|
66
|
+
.\venv\Scripts\activate
|
|
67
|
+
# On Mac/Linux:
|
|
68
|
+
source venv/bin/activate
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Install CodeGuardian with the features you need:
|
|
72
|
+
|
|
73
|
+
| Use case | Command | Approx size |
|
|
74
|
+
|---|---|---|
|
|
75
|
+
| API testing only | `pip install -e .` | ~50MB |
|
|
76
|
+
| + UI testing | `pip install -e .[ui]` | ~200MB |
|
|
77
|
+
| + AI explanations | `pip install -e .[ai]` | ~3GB |
|
|
78
|
+
| + Code scanning | `pip install -e .[scan]` | ~100MB |
|
|
79
|
+
| Everything | `pip install -e .[full]` | ~3.5GB |
|
|
80
|
+
|
|
81
|
+
*(Note: AI explanations require PyTorch which is ~2GB. Playwright requires an extra step: `playwright install chromium`).*
|
|
82
|
+
|
|
83
|
+
### Install Ollama
|
|
84
|
+
|
|
85
|
+
Install [Ollama](https://ollama.com/) on your local machine if you plan to use AI features. CodeGuardian will automatically pull the recommended model (`qwen2.5-coder`) when you first use the `ask` command.
|
|
86
|
+
|
|
87
|
+
## Usage
|
|
88
|
+
|
|
89
|
+
### 1. Initialize
|
|
90
|
+
Initialize the CodeGuardian database and pre-compile any language grammars:
|
|
91
|
+
```bash
|
|
92
|
+
cd /path/to/your/project
|
|
93
|
+
codeguardian init
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### 2. Configure Tests
|
|
97
|
+
Create a `codeguardian-api.json` or `codeguardian-ui.json` in your project root.
|
|
98
|
+
CodeGuardian will validate your config using Pydantic before running.
|
|
99
|
+
|
|
100
|
+
**codeguardian-api.json**
|
|
101
|
+
```json
|
|
102
|
+
{
|
|
103
|
+
"base_url": "https://httpbin.org",
|
|
104
|
+
"tests": [
|
|
105
|
+
{"name": "Get Request", "url": "/get", "method": "GET", "expected_status": 200},
|
|
106
|
+
{"name": "Not Found", "url": "/status/404", "method": "GET", "expected_status": 404}
|
|
107
|
+
]
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**codeguardian-ui.json**
|
|
112
|
+
```json
|
|
113
|
+
{
|
|
114
|
+
"browser": "chromium",
|
|
115
|
+
"headless": true,
|
|
116
|
+
"tests": [
|
|
117
|
+
{
|
|
118
|
+
"name": "Check Example Domain",
|
|
119
|
+
"steps": [
|
|
120
|
+
{"action": "navigate", "url": "http://example.com"},
|
|
121
|
+
{"action": "assert_visible", "selector": "h1"}
|
|
122
|
+
]
|
|
123
|
+
}
|
|
124
|
+
]
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 3. Run Tests
|
|
129
|
+
Run both API and UI tests (results will be logged to SQLite):
|
|
130
|
+
```bash
|
|
131
|
+
codeguardian test
|
|
132
|
+
# Or target specific suites:
|
|
133
|
+
# codeguardian test --api-only
|
|
134
|
+
# codeguardian test --ui-only
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### 4. Build Knowledge Base
|
|
138
|
+
Scan the project to run static analysis and build the ChromaDB vector index:
|
|
139
|
+
```bash
|
|
140
|
+
codeguardian scan .
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### 5. Ask the AI
|
|
144
|
+
If tests fail, query the AI. CodeGuardian will fetch the latest failures, pull relevant source code from ChromaDB, and ask Ollama to explain what went wrong:
|
|
145
|
+
```bash
|
|
146
|
+
codeguardian ask "Why did the API tests fail?"
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### 6. Generate Reports
|
|
150
|
+
Export beautiful HTML reports or JUnit XML for CI/CD integrations:
|
|
151
|
+
```bash
|
|
152
|
+
codeguardian report --format html
|
|
153
|
+
codeguardian report --format junit
|
|
154
|
+
codeguardian report --format both
|
|
155
|
+
```
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# CodeGuardian
|
|
2
|
+
|
|
3
|
+
An **Offline AI QA Assistant** that tests applications automatically and explains failures.
|
|
4
|
+
|
|
5
|
+
No internet. No API keys. No cloud services.
|
|
6
|
+
|
|
7
|
+
## Architecture & Features
|
|
8
|
+
|
|
9
|
+
- **CLI Automation**: Built with `Typer` and `Rich` for a premium terminal experience.
|
|
10
|
+
- **API Testing**: Uses `HTTPX` to validate JSON endpoints.
|
|
11
|
+
- **UI Testing**: Uses `Playwright` for headless browser automation and UI validation.
|
|
12
|
+
- **Static Analysis**: Hooks into `Semgrep` and `Ruff`.
|
|
13
|
+
- **RAG & Knowledge Base**: Parses source code with `Tree-sitter`, embeds it using `SentenceTransformers`, and stores it in `ChromaDB`.
|
|
14
|
+
- **Local AI Explanations**: Queries your local `Ollama` (`qwen2.5-coder` recommended) to explain test failures by correlating test errors with the relevant source code.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
Ensure you have Python 3.10+ installed.
|
|
19
|
+
|
|
20
|
+
CodeGuardian uses optional dependencies so you only install what you need.
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
git clone <repository>
|
|
24
|
+
cd CodeGuardian
|
|
25
|
+
python -m venv venv
|
|
26
|
+
|
|
27
|
+
# On Windows:
|
|
28
|
+
.\venv\Scripts\activate
|
|
29
|
+
# On Mac/Linux:
|
|
30
|
+
source venv/bin/activate
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Install CodeGuardian with the features you need:
|
|
34
|
+
|
|
35
|
+
| Use case | Command | Approx size |
|
|
36
|
+
|---|---|---|
|
|
37
|
+
| API testing only | `pip install -e .` | ~50MB |
|
|
38
|
+
| + UI testing | `pip install -e .[ui]` | ~200MB |
|
|
39
|
+
| + AI explanations | `pip install -e .[ai]` | ~3GB |
|
|
40
|
+
| + Code scanning | `pip install -e .[scan]` | ~100MB |
|
|
41
|
+
| Everything | `pip install -e .[full]` | ~3.5GB |
|
|
42
|
+
|
|
43
|
+
*(Note: AI explanations require PyTorch which is ~2GB. Playwright requires an extra step: `playwright install chromium`).*
|
|
44
|
+
|
|
45
|
+
### Install Ollama
|
|
46
|
+
|
|
47
|
+
Install [Ollama](https://ollama.com/) on your local machine if you plan to use AI features. CodeGuardian will automatically pull the recommended model (`qwen2.5-coder`) when you first use the `ask` command.
|
|
48
|
+
|
|
49
|
+
## Usage
|
|
50
|
+
|
|
51
|
+
### 1. Initialize
|
|
52
|
+
Initialize the CodeGuardian database and pre-compile any language grammars:
|
|
53
|
+
```bash
|
|
54
|
+
cd /path/to/your/project
|
|
55
|
+
codeguardian init
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 2. Configure Tests
|
|
59
|
+
Create a `codeguardian-api.json` or `codeguardian-ui.json` in your project root.
|
|
60
|
+
CodeGuardian will validate your config using Pydantic before running.
|
|
61
|
+
|
|
62
|
+
**codeguardian-api.json**
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"base_url": "https://httpbin.org",
|
|
66
|
+
"tests": [
|
|
67
|
+
{"name": "Get Request", "url": "/get", "method": "GET", "expected_status": 200},
|
|
68
|
+
{"name": "Not Found", "url": "/status/404", "method": "GET", "expected_status": 404}
|
|
69
|
+
]
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**codeguardian-ui.json**
|
|
74
|
+
```json
|
|
75
|
+
{
|
|
76
|
+
"browser": "chromium",
|
|
77
|
+
"headless": true,
|
|
78
|
+
"tests": [
|
|
79
|
+
{
|
|
80
|
+
"name": "Check Example Domain",
|
|
81
|
+
"steps": [
|
|
82
|
+
{"action": "navigate", "url": "http://example.com"},
|
|
83
|
+
{"action": "assert_visible", "selector": "h1"}
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 3. Run Tests
|
|
91
|
+
Run both API and UI tests (results will be logged to SQLite):
|
|
92
|
+
```bash
|
|
93
|
+
codeguardian test
|
|
94
|
+
# Or target specific suites:
|
|
95
|
+
# codeguardian test --api-only
|
|
96
|
+
# codeguardian test --ui-only
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### 4. Build Knowledge Base
|
|
100
|
+
Scan the project to run static analysis and build the ChromaDB vector index:
|
|
101
|
+
```bash
|
|
102
|
+
codeguardian scan .
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### 5. Ask the AI
|
|
106
|
+
If tests fail, query the AI. CodeGuardian will fetch the latest failures, pull relevant source code from ChromaDB, and ask Ollama to explain what went wrong:
|
|
107
|
+
```bash
|
|
108
|
+
codeguardian ask "Why did the API tests fail?"
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### 6. Generate Reports
|
|
112
|
+
Export beautiful HTML reports or JUnit XML for CI/CD integrations:
|
|
113
|
+
```bash
|
|
114
|
+
codeguardian report --format html
|
|
115
|
+
codeguardian report --format junit
|
|
116
|
+
codeguardian report --format both
|
|
117
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""CodeGuardian - Offline AI QA Assistant"""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""AI and RAG module."""
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# codeguardian/ai/health.py
|
|
2
|
+
|
|
3
|
+
import ollama
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
6
|
+
|
|
7
|
+
console = Console()
|
|
8
|
+
|
|
9
|
+
RECOMMENDED_MODEL = "qwen2.5-coder"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def check_ollama(warn_only: bool = False) -> bool:
|
|
13
|
+
"""
|
|
14
|
+
Checks if the Ollama daemon is running.
|
|
15
|
+
If warn_only=True, prints a warning instead of raising.
|
|
16
|
+
Returns True if healthy, False otherwise.
|
|
17
|
+
"""
|
|
18
|
+
try:
|
|
19
|
+
ollama.list()
|
|
20
|
+
console.print("[green]✓[/green] Ollama daemon is running")
|
|
21
|
+
return True
|
|
22
|
+
except Exception:
|
|
23
|
+
msg = (
|
|
24
|
+
"[yellow]![/yellow] Ollama is not running.\n"
|
|
25
|
+
" Start it with: [bold]ollama serve[/bold]\n"
|
|
26
|
+
" (Required for [bold]codeguardian ask[/bold])"
|
|
27
|
+
)
|
|
28
|
+
if warn_only:
|
|
29
|
+
console.print(msg)
|
|
30
|
+
return False
|
|
31
|
+
raise RuntimeError(
|
|
32
|
+
"Ollama is not running. Start it with: ollama serve"
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def ensure_model(model: str = RECOMMENDED_MODEL):
|
|
37
|
+
"""
|
|
38
|
+
Checks if the model is pulled locally. Pulls it if not.
|
|
39
|
+
"""
|
|
40
|
+
check_ollama()
|
|
41
|
+
|
|
42
|
+
available = {m["name"] for m in ollama.list().get("models", [])}
|
|
43
|
+
|
|
44
|
+
if model in available:
|
|
45
|
+
console.print(f"[green]✓[/green] Model [bold]{model}[/bold] is ready")
|
|
46
|
+
return
|
|
47
|
+
|
|
48
|
+
console.print(f"[yellow]Model [bold]{model}[/bold] not found locally. Pulling now...[/yellow]")
|
|
49
|
+
console.print("[dim](This is a one-time download — may take several minutes)[/dim]\n")
|
|
50
|
+
|
|
51
|
+
with Progress(
|
|
52
|
+
SpinnerColumn(),
|
|
53
|
+
TextColumn("[progress.description]{task.description}"),
|
|
54
|
+
console=console,
|
|
55
|
+
) as progress:
|
|
56
|
+
task = progress.add_task(f"Pulling {model}...", total=None)
|
|
57
|
+
for chunk in ollama.pull(model, stream=True):
|
|
58
|
+
status = chunk.get("status", "")
|
|
59
|
+
if status:
|
|
60
|
+
progress.update(task, description=f"[cyan]{status}[/cyan]")
|
|
61
|
+
|
|
62
|
+
console.print(f"\n[green]✓[/green] Model [bold]{model}[/bold] ready")
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from codeguardian.core.logger import logger
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
def generate_explanation(prompt: str, model: str = "qwen2.5-coder") -> Optional[str]:
|
|
5
|
+
"""
|
|
6
|
+
Sends a prompt to the local Ollama instance and returns the generated explanation.
|
|
7
|
+
"""
|
|
8
|
+
from codeguardian.core.extras import require_extra
|
|
9
|
+
require_extra("ollama", "ai")
|
|
10
|
+
import ollama
|
|
11
|
+
try:
|
|
12
|
+
response = ollama.chat(model=model, messages=[
|
|
13
|
+
{
|
|
14
|
+
"role": "system",
|
|
15
|
+
"content": "You are an expert AI QA Engineer. You analyze failed tests and source code to explain failures and suggest fixes."
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"role": "user",
|
|
19
|
+
"content": prompt
|
|
20
|
+
}
|
|
21
|
+
])
|
|
22
|
+
return response['message']['content']
|
|
23
|
+
except Exception as e:
|
|
24
|
+
logger.error(f"Failed to communicate with local Ollama instance: {e}")
|
|
25
|
+
logger.error("Make sure Ollama is installed and running locally with the model available.")
|
|
26
|
+
return None
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from codeguardian.core.logger import logger
|
|
4
|
+
from typing import List, Dict
|
|
5
|
+
|
|
6
|
+
CODEGUARDIAN_DIR = Path(".codeguardian")
|
|
7
|
+
CHROMA_DB_DIR = CODEGUARDIAN_DIR / "chroma"
|
|
8
|
+
|
|
9
|
+
class CodeRAG:
|
|
10
|
+
def __init__(self):
|
|
11
|
+
from codeguardian.core.extras import require_extra
|
|
12
|
+
require_extra("chromadb", "ai")
|
|
13
|
+
require_extra("sentence_transformers", "ai")
|
|
14
|
+
from codeguardian.core.chroma_compat import get_client, get_or_create_collection
|
|
15
|
+
from sentence_transformers import SentenceTransformer
|
|
16
|
+
|
|
17
|
+
# Ensure directory exists
|
|
18
|
+
CHROMA_DB_DIR.mkdir(parents=True, exist_ok=True)
|
|
19
|
+
|
|
20
|
+
try:
|
|
21
|
+
self.client = get_client(str(CHROMA_DB_DIR))
|
|
22
|
+
self.collection = get_or_create_collection(self.client, "codebase")
|
|
23
|
+
# Load a lightweight embedding model
|
|
24
|
+
self.model = SentenceTransformer('all-MiniLM-L6-v2')
|
|
25
|
+
self.is_ready = True
|
|
26
|
+
except Exception as e:
|
|
27
|
+
logger.error(f"Failed to initialize ChromaDB or SentenceTransformers: {e}")
|
|
28
|
+
self.is_ready = False
|
|
29
|
+
|
|
30
|
+
def add_chunks(self, chunks: List[str], metadatas: List[Dict[str, str]], ids: List[str]):
|
|
31
|
+
if not self.is_ready or not chunks:
|
|
32
|
+
return
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
embeddings = self.model.encode(chunks).tolist()
|
|
36
|
+
self.collection.add(
|
|
37
|
+
documents=chunks,
|
|
38
|
+
embeddings=embeddings,
|
|
39
|
+
metadatas=metadatas,
|
|
40
|
+
ids=ids
|
|
41
|
+
)
|
|
42
|
+
logger.info(f"Added {len(chunks)} chunks to the vector database.")
|
|
43
|
+
except Exception as e:
|
|
44
|
+
logger.error(f"Failed to add chunks to RAG: {e}")
|
|
45
|
+
|
|
46
|
+
def query(self, query_text: str, n_results: int = 3) -> List[Dict]:
|
|
47
|
+
if not self.is_ready:
|
|
48
|
+
return []
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
query_embedding = self.model.encode([query_text]).tolist()
|
|
52
|
+
results = self.collection.query(
|
|
53
|
+
query_embeddings=query_embedding,
|
|
54
|
+
n_results=n_results
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
out = []
|
|
58
|
+
if results['documents'] and len(results['documents']) > 0:
|
|
59
|
+
for i in range(len(results['documents'][0])):
|
|
60
|
+
out.append({
|
|
61
|
+
"document": results['documents'][0][i],
|
|
62
|
+
"metadata": results['metadatas'][0][i],
|
|
63
|
+
"distance": results['distances'][0][i]
|
|
64
|
+
})
|
|
65
|
+
return out
|
|
66
|
+
except Exception as e:
|
|
67
|
+
logger.error(f"Failed to query RAG: {e}")
|
|
68
|
+
return []
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
from codeguardian.core.logger import logger
|
|
4
|
+
import ollama
|
|
5
|
+
|
|
6
|
+
def generate_api_tests(project_path: str, output_file: str = "codeguardian-api.json"):
|
|
7
|
+
"""
|
|
8
|
+
Scans the project to guess endpoints and uses Ollama to generate an API test config.
|
|
9
|
+
"""
|
|
10
|
+
logger.info("[bold cyan]Generating API tests using local AI...[/bold cyan]")
|
|
11
|
+
|
|
12
|
+
# We will simply ask Ollama to generate a config based on a standard template.
|
|
13
|
+
# In a real scenario, this would first use Tree-sitter to find all endpoints.
|
|
14
|
+
prompt = """
|
|
15
|
+
You are an expert QA Engineer.
|
|
16
|
+
I have a local FastAPI server running on http://127.0.0.1:8000.
|
|
17
|
+
It has the following endpoints:
|
|
18
|
+
1. GET /
|
|
19
|
+
2. GET /users
|
|
20
|
+
3. POST /users (requires JSON body with 'name')
|
|
21
|
+
4. GET /error
|
|
22
|
+
|
|
23
|
+
Generate a complete JSON configuration for my testing tool.
|
|
24
|
+
The JSON must have a 'base_url' and a list of 'endpoints'.
|
|
25
|
+
Each endpoint object must have 'path', 'method', and 'expected_status'.
|
|
26
|
+
Return ONLY the raw JSON without any markdown formatting or backticks.
|
|
27
|
+
"""
|
|
28
|
+
try:
|
|
29
|
+
response = ollama.chat(model='qwen2.5-coder', messages=[{"role": "user", "content": prompt}])
|
|
30
|
+
raw_json = response['message']['content'].strip()
|
|
31
|
+
|
|
32
|
+
# Strip potential markdown formatting if the model still includes it
|
|
33
|
+
if raw_json.startswith("```json"):
|
|
34
|
+
raw_json = raw_json[7:]
|
|
35
|
+
if raw_json.startswith("```"):
|
|
36
|
+
raw_json = raw_json[3:]
|
|
37
|
+
if raw_json.endswith("```"):
|
|
38
|
+
raw_json = raw_json[:-3]
|
|
39
|
+
|
|
40
|
+
config = json.loads(raw_json)
|
|
41
|
+
|
|
42
|
+
out_path = os.path.join(project_path, output_file)
|
|
43
|
+
with open(out_path, "w") as f:
|
|
44
|
+
json.dump(config, f, indent=2)
|
|
45
|
+
|
|
46
|
+
logger.info(f"[bold green]Successfully generated {output_file}[/bold green]")
|
|
47
|
+
except json.JSONDecodeError:
|
|
48
|
+
logger.error("[bold red]Failed to parse AI output as JSON.[/bold red]")
|
|
49
|
+
logger.debug(raw_json)
|
|
50
|
+
except Exception as e:
|
|
51
|
+
logger.error(f"[bold red]Failed to generate tests: {e}[/bold red]")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""CLI modules."""
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
from rich.console import Console
|
|
3
|
+
from codeguardian.core.logger import logger
|
|
4
|
+
from codeguardian.core.db import get_session, TestResult, is_scanned, get_scan_summary, get_engine
|
|
5
|
+
from codeguardian.ai.rag import CodeRAG
|
|
6
|
+
from codeguardian.ai.ollama_client import generate_explanation
|
|
7
|
+
from sqlmodel import select
|
|
8
|
+
from codeguardian.ai.health import check_ollama, ensure_model, RECOMMENDED_MODEL
|
|
9
|
+
|
|
10
|
+
console = Console()
|
|
11
|
+
app = typer.Typer(help="Ask the local AI about failures.")
|
|
12
|
+
|
|
13
|
+
@app.callback(invoke_without_command=True)
|
|
14
|
+
def ask(query: str = typer.Argument(..., help="Question to ask the AI")):
|
|
15
|
+
"""
|
|
16
|
+
Queries the local Ollama LLM with context from the vector DB and recent test results.
|
|
17
|
+
"""
|
|
18
|
+
engine = get_engine()
|
|
19
|
+
|
|
20
|
+
# Guard: knowledge base must exist
|
|
21
|
+
if not is_scanned(engine):
|
|
22
|
+
console.print(
|
|
23
|
+
"\n[bold red]No knowledge base found.[/bold red]\n"
|
|
24
|
+
"The AI needs your codebase indexed before it can explain failures.\n\n"
|
|
25
|
+
"Run first: [bold]codeguardian scan .[/bold]\n"
|
|
26
|
+
)
|
|
27
|
+
raise typer.Exit(1)
|
|
28
|
+
|
|
29
|
+
summary = get_scan_summary(engine)
|
|
30
|
+
console.print(
|
|
31
|
+
f"[dim]Knowledge base: {summary.file_count} files, "
|
|
32
|
+
f"{summary.chunk_count} chunks "
|
|
33
|
+
f"(last scanned: {summary.scanned_at.strftime('%Y-%m-%d %H:%M')})[/dim]\n"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
check_ollama()
|
|
37
|
+
ensure_model()
|
|
38
|
+
|
|
39
|
+
logger.info(f"[bold magenta]Querying AI with:[/bold magenta] {query}")
|
|
40
|
+
|
|
41
|
+
# 1. Fetch recent failed tests from SQLite
|
|
42
|
+
session = next(get_session())
|
|
43
|
+
failed_tests = session.exec(
|
|
44
|
+
select(TestResult).where(TestResult.status == "FAIL").order_by(TestResult.id.desc()).limit(5)
|
|
45
|
+
).all()
|
|
46
|
+
|
|
47
|
+
test_context = "Recent Failed Tests:\n"
|
|
48
|
+
if not failed_tests:
|
|
49
|
+
test_context += "None found.\n"
|
|
50
|
+
for t in failed_tests:
|
|
51
|
+
test_context += f"- {t.endpoint_or_element}: {t.error_message}\n"
|
|
52
|
+
|
|
53
|
+
# 2. Fetch relevant code from RAG
|
|
54
|
+
rag = CodeRAG()
|
|
55
|
+
rag_results = rag.query(query, n_results=3)
|
|
56
|
+
code_context = "Relevant Code Snippets:\n"
|
|
57
|
+
|
|
58
|
+
if not rag_results:
|
|
59
|
+
code_context += "None found.\n"
|
|
60
|
+
for r in rag_results:
|
|
61
|
+
meta = r.get("metadata", {})
|
|
62
|
+
code = r.get("document", "")
|
|
63
|
+
code_context += f"--- {meta.get('file', 'Unknown File')} ({meta.get('name', '')}) ---\n"
|
|
64
|
+
code_context += f"{code}\n\n"
|
|
65
|
+
|
|
66
|
+
# 3. Construct prompt
|
|
67
|
+
prompt = f"""
|
|
68
|
+
User Query: {query}
|
|
69
|
+
|
|
70
|
+
{test_context}
|
|
71
|
+
|
|
72
|
+
{code_context}
|
|
73
|
+
|
|
74
|
+
Based on the above context, explain the failure and suggest a fix.
|
|
75
|
+
"""
|
|
76
|
+
logger.info("[bold]Generating explanation (this may take a moment)...[/bold]")
|
|
77
|
+
explanation = generate_explanation(prompt)
|
|
78
|
+
|
|
79
|
+
if explanation:
|
|
80
|
+
logger.info("\n[bold green]AI Explanation:[/bold green]")
|
|
81
|
+
logger.info(explanation)
|
|
82
|
+
else:
|
|
83
|
+
logger.error("Could not generate explanation.")
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
import os
|
|
3
|
+
from codeguardian.core.logger import logger
|
|
4
|
+
|
|
5
|
+
app = typer.Typer(help="CI/CD Integration.")
|
|
6
|
+
|
|
7
|
+
@app.callback(invoke_without_command=True)
|
|
8
|
+
def ci(
|
|
9
|
+
platform: str = typer.Option("github", help="CI platform (github, gitlab)")
|
|
10
|
+
):
|
|
11
|
+
"""
|
|
12
|
+
Generates a template workflow for your CI/CD pipeline.
|
|
13
|
+
"""
|
|
14
|
+
logger.info(f"[bold cyan]Generating CI template for {platform}...[/bold cyan]")
|
|
15
|
+
|
|
16
|
+
if platform.lower() == "github":
|
|
17
|
+
template = """name: CodeGuardian QA
|
|
18
|
+
on: [push, pull_request]
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
qa:
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v3
|
|
25
|
+
- name: Set up Python
|
|
26
|
+
uses: actions/setup-python@v4
|
|
27
|
+
with:
|
|
28
|
+
python-version: "3.10"
|
|
29
|
+
- name: Install Dependencies
|
|
30
|
+
run: |
|
|
31
|
+
pip install -r requirements.txt
|
|
32
|
+
pip install codeguardian
|
|
33
|
+
- name: Run App
|
|
34
|
+
run: |
|
|
35
|
+
# Start your app in the background here
|
|
36
|
+
# e.g., uvicorn main:app --port 8000 &
|
|
37
|
+
- name: Run CodeGuardian
|
|
38
|
+
run: |
|
|
39
|
+
codeguardian init
|
|
40
|
+
codeguardian test --fail-on-error
|
|
41
|
+
"""
|
|
42
|
+
os.makedirs(".github/workflows", exist_ok=True)
|
|
43
|
+
with open(".github/workflows/codeguardian.yml", "w") as f:
|
|
44
|
+
f.write(template)
|
|
45
|
+
logger.info("[bold green]Created .github/workflows/codeguardian.yml[/bold green]")
|
|
46
|
+
else:
|
|
47
|
+
logger.warning(f"Platform {platform} is not fully supported yet.")
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
from codeguardian.core.logger import logger
|
|
3
|
+
from codeguardian.ai.test_generator import generate_api_tests
|
|
4
|
+
|
|
5
|
+
app = typer.Typer(help="Generate tests automatically using AI.")
|
|
6
|
+
|
|
7
|
+
@app.callback(invoke_without_command=True)
|
|
8
|
+
def generate(
|
|
9
|
+
project_path: str = typer.Argument(".", help="Path to the project"),
|
|
10
|
+
type: str = typer.Option("api", help="Type of tests to generate (api or ui)")
|
|
11
|
+
):
|
|
12
|
+
"""
|
|
13
|
+
Scans the project and generates tests using Ollama.
|
|
14
|
+
"""
|
|
15
|
+
logger.info(f"[bold yellow]Generating {type} tests for {project_path}[/bold yellow]")
|
|
16
|
+
|
|
17
|
+
if type.lower() == "api":
|
|
18
|
+
generate_api_tests(project_path)
|
|
19
|
+
else:
|
|
20
|
+
logger.warning(f"Generation for type '{type}' is not yet implemented.")
|