Spaces:
Sleeping
Sleeping
feat: convert to async/await with parallel agent execution via asyncio.gather
Browse files
app/agents/bug_detection_agent.py
CHANGED
|
@@ -7,7 +7,7 @@ from app.core.prompts import BUG_DETECTION_PROMPT
|
|
| 7 |
from app.models.issue import IssueReport, Bug, Warning, Suggestion
|
| 8 |
|
| 9 |
|
| 10 |
-
def run(local_path: str) -> IssueReport:
|
| 11 |
"""
|
| 12 |
Detect bugs, warnings and suggestions in a repository.
|
| 13 |
"""
|
|
@@ -53,7 +53,7 @@ Code Samples:
|
|
| 53 |
|
| 54 |
# Step 5 - Call LLM
|
| 55 |
prompt = BUG_DETECTION_PROMPT.format(code_info=code_info)
|
| 56 |
-
llm_response = call_llm(prompt)
|
| 57 |
|
| 58 |
# Step 6 - Parse response
|
| 59 |
try:
|
|
|
|
| 7 |
from app.models.issue import IssueReport, Bug, Warning, Suggestion
|
| 8 |
|
| 9 |
|
| 10 |
+
async def run(local_path: str) -> IssueReport:
|
| 11 |
"""
|
| 12 |
Detect bugs, warnings and suggestions in a repository.
|
| 13 |
"""
|
|
|
|
| 53 |
|
| 54 |
# Step 5 - Call LLM
|
| 55 |
prompt = BUG_DETECTION_PROMPT.format(code_info=code_info)
|
| 56 |
+
llm_response = await call_llm(prompt)
|
| 57 |
|
| 58 |
# Step 6 - Parse response
|
| 59 |
try:
|
app/agents/code_review_agent.py
CHANGED
|
@@ -6,7 +6,7 @@ from app.models.review import ReviewSuggestions
|
|
| 6 |
from app.models.repository import RepositoryMetadata
|
| 7 |
|
| 8 |
|
| 9 |
-
def run(local_path: str, metadata: RepositoryMetadata) -> ReviewSuggestions:
|
| 10 |
"""
|
| 11 |
Perform AI-powered code review on repository.
|
| 12 |
"""
|
|
@@ -36,7 +36,7 @@ def run(local_path: str, metadata: RepositoryMetadata) -> ReviewSuggestions:
|
|
| 36 |
# Step 3 - Call LLM
|
| 37 |
source_code = "\n\n".join(source_samples)
|
| 38 |
prompt = CODE_REVIEW_PROMPT.format(source_code=source_code)
|
| 39 |
-
llm_response = call_llm(prompt)
|
| 40 |
|
| 41 |
# Step 4 - Parse response
|
| 42 |
try:
|
|
|
|
| 6 |
from app.models.repository import RepositoryMetadata
|
| 7 |
|
| 8 |
|
| 9 |
+
async def run(local_path: str, metadata: RepositoryMetadata) -> ReviewSuggestions:
|
| 10 |
"""
|
| 11 |
Perform AI-powered code review on repository.
|
| 12 |
"""
|
|
|
|
| 36 |
# Step 3 - Call LLM
|
| 37 |
source_code = "\n\n".join(source_samples)
|
| 38 |
prompt = CODE_REVIEW_PROMPT.format(source_code=source_code)
|
| 39 |
+
llm_response = await call_llm(prompt)
|
| 40 |
|
| 41 |
# Step 4 - Parse response
|
| 42 |
try:
|
app/agents/repo_analysis_agent.py
CHANGED
|
@@ -6,7 +6,7 @@ from app.core.prompts import REPO_ANALYSIS_PROMPT
|
|
| 6 |
from app.models.repository import RepositoryMetadata
|
| 7 |
|
| 8 |
|
| 9 |
-
def run(repo_url: str, local_path: str) -> RepositoryMetadata:
|
| 10 |
"""
|
| 11 |
Analyze a repository and return structured metadata.
|
| 12 |
"""
|
|
@@ -32,7 +32,7 @@ Functions Found: {sum(len(r['functions']) for r in ast_results)}
|
|
| 32 |
|
| 33 |
# Step 3 - Call LLM
|
| 34 |
prompt = REPO_ANALYSIS_PROMPT.format(repo_info=repo_info)
|
| 35 |
-
llm_response = call_llm(prompt)
|
| 36 |
|
| 37 |
# Step 4 - Parse response
|
| 38 |
try:
|
|
|
|
| 6 |
from app.models.repository import RepositoryMetadata
|
| 7 |
|
| 8 |
|
| 9 |
+
async def run(repo_url: str, local_path: str) -> RepositoryMetadata:
|
| 10 |
"""
|
| 11 |
Analyze a repository and return structured metadata.
|
| 12 |
"""
|
|
|
|
| 32 |
|
| 33 |
# Step 3 - Call LLM
|
| 34 |
prompt = REPO_ANALYSIS_PROMPT.format(repo_info=repo_info)
|
| 35 |
+
llm_response = await call_llm(prompt)
|
| 36 |
|
| 37 |
# Step 4 - Parse response
|
| 38 |
try:
|
app/agents/report_generator_agent.py
CHANGED
|
@@ -7,7 +7,7 @@ from app.models.review import ReviewSuggestions, GeneratedTests
|
|
| 7 |
from app.models.report import EngineeringReport
|
| 8 |
|
| 9 |
|
| 10 |
-
def run(
|
| 11 |
metadata: RepositoryMetadata,
|
| 12 |
issues: IssueReport,
|
| 13 |
tests: GeneratedTests,
|
|
@@ -56,7 +56,7 @@ Summary: {review.summary}
|
|
| 56 |
tests=tests_summary,
|
| 57 |
review=review_summary
|
| 58 |
)
|
| 59 |
-
full_report = call_llm(prompt, max_tokens=3000)
|
| 60 |
|
| 61 |
# Step 3 - Build final report
|
| 62 |
report = EngineeringReport(
|
|
|
|
| 7 |
from app.models.report import EngineeringReport
|
| 8 |
|
| 9 |
|
| 10 |
+
async def run(
|
| 11 |
metadata: RepositoryMetadata,
|
| 12 |
issues: IssueReport,
|
| 13 |
tests: GeneratedTests,
|
|
|
|
| 56 |
tests=tests_summary,
|
| 57 |
review=review_summary
|
| 58 |
)
|
| 59 |
+
full_report = await call_llm(prompt, max_tokens=3000)
|
| 60 |
|
| 61 |
# Step 3 - Build final report
|
| 62 |
report = EngineeringReport(
|
app/agents/test_generation_agent.py
CHANGED
|
@@ -5,7 +5,7 @@ from app.core.prompts import TEST_GENERATION_PROMPT
|
|
| 5 |
from app.models.review import GeneratedTests
|
| 6 |
|
| 7 |
|
| 8 |
-
def run(local_path: str) -> GeneratedTests:
|
| 9 |
"""
|
| 10 |
Generate pytest test cases for uncovered functions.
|
| 11 |
"""
|
|
@@ -48,7 +48,7 @@ def run(local_path: str) -> GeneratedTests:
|
|
| 48 |
# Step 3 - Call LLM
|
| 49 |
source_code = "\n\n".join(source_code_samples)
|
| 50 |
prompt = TEST_GENERATION_PROMPT.format(source_code=source_code)
|
| 51 |
-
llm_response = call_llm(prompt, max_tokens=3000)
|
| 52 |
|
| 53 |
# Step 4 - Count generated tests
|
| 54 |
test_count = llm_response.count("def test_")
|
|
|
|
| 5 |
from app.models.review import GeneratedTests
|
| 6 |
|
| 7 |
|
| 8 |
+
async def run(local_path: str) -> GeneratedTests:
|
| 9 |
"""
|
| 10 |
Generate pytest test cases for uncovered functions.
|
| 11 |
"""
|
|
|
|
| 48 |
# Step 3 - Call LLM
|
| 49 |
source_code = "\n\n".join(source_code_samples)
|
| 50 |
prompt = TEST_GENERATION_PROMPT.format(source_code=source_code)
|
| 51 |
+
llm_response = await call_llm(prompt, max_tokens=3000)
|
| 52 |
|
| 53 |
# Step 4 - Count generated tests
|
| 54 |
test_count = llm_response.count("def test_")
|
app/api/routes.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
|
|
| 1 |
from fastapi import APIRouter, HTTPException
|
| 2 |
from app.models.repository import RepositoryRequest, RepositoryResponse
|
| 3 |
from app.models.report import EngineeringReport
|
|
@@ -19,7 +20,7 @@ def health_check():
|
|
| 19 |
|
| 20 |
|
| 21 |
@router.post("/analyze", response_model=EngineeringReport)
|
| 22 |
-
def analyze_repository(request: RepositoryRequest):
|
| 23 |
"""
|
| 24 |
Full pipeline:
|
| 25 |
1. Clone repository
|
|
@@ -33,11 +34,15 @@ def analyze_repository(request: RepositoryRequest):
|
|
| 33 |
local_path = clone_repository(request.github_url)
|
| 34 |
|
| 35 |
# Step 2 - Run agents in order
|
| 36 |
-
metadata = repo_analysis_agent.run(request.github_url, local_path)
|
| 37 |
-
|
| 38 |
-
tests =
|
| 39 |
-
|
| 40 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
|
| 42 |
return report
|
| 43 |
|
|
@@ -51,7 +56,7 @@ def analyze_repository(request: RepositoryRequest):
|
|
| 51 |
|
| 52 |
|
| 53 |
@router.post("/quick-analyze", response_model=RepositoryResponse)
|
| 54 |
-
def quick_analyze(request: RepositoryRequest):
|
| 55 |
"""
|
| 56 |
Only run repository analysis agent.
|
| 57 |
Faster, for testing purposes.
|
|
@@ -60,7 +65,7 @@ def quick_analyze(request: RepositoryRequest):
|
|
| 60 |
|
| 61 |
try:
|
| 62 |
local_path = clone_repository(request.github_url)
|
| 63 |
-
metadata = repo_analysis_agent.run(request.github_url, local_path)
|
| 64 |
|
| 65 |
return RepositoryResponse(
|
| 66 |
success=True,
|
|
|
|
| 1 |
+
import asyncio
|
| 2 |
from fastapi import APIRouter, HTTPException
|
| 3 |
from app.models.repository import RepositoryRequest, RepositoryResponse
|
| 4 |
from app.models.report import EngineeringReport
|
|
|
|
| 20 |
|
| 21 |
|
| 22 |
@router.post("/analyze", response_model=EngineeringReport)
|
| 23 |
+
async def analyze_repository(request: RepositoryRequest):
|
| 24 |
"""
|
| 25 |
Full pipeline:
|
| 26 |
1. Clone repository
|
|
|
|
| 34 |
local_path = clone_repository(request.github_url)
|
| 35 |
|
| 36 |
# Step 2 - Run agents in order
|
| 37 |
+
metadata = await repo_analysis_agent.run(request.github_url, local_path)
|
| 38 |
+
|
| 39 |
+
issues, tests, review = await asyncio.gather(
|
| 40 |
+
bug_detection_agent.run(local_path),
|
| 41 |
+
test_generation_agent.run(local_path),
|
| 42 |
+
code_review_agent.run(local_path, metadata),
|
| 43 |
+
)
|
| 44 |
+
|
| 45 |
+
report = await report_generator_agent.run(metadata, issues, tests, review)
|
| 46 |
|
| 47 |
return report
|
| 48 |
|
|
|
|
| 56 |
|
| 57 |
|
| 58 |
@router.post("/quick-analyze", response_model=RepositoryResponse)
|
| 59 |
+
async def quick_analyze(request: RepositoryRequest):
|
| 60 |
"""
|
| 61 |
Only run repository analysis agent.
|
| 62 |
Faster, for testing purposes.
|
|
|
|
| 65 |
|
| 66 |
try:
|
| 67 |
local_path = clone_repository(request.github_url)
|
| 68 |
+
metadata = await repo_analysis_agent.run(request.github_url, local_path)
|
| 69 |
|
| 70 |
return RepositoryResponse(
|
| 71 |
success=True,
|
app/core/llm.py
CHANGED
|
@@ -6,7 +6,7 @@ settings = get_settings()
|
|
| 6 |
OPENROUTER_API_URL = "https://openrouter.ai/api/v1/chat/completions"
|
| 7 |
|
| 8 |
|
| 9 |
-
def call_llm(prompt: str, system_prompt: str = None, max_tokens: int = 2000) -> str:
|
| 10 |
"""
|
| 11 |
Call OpenRouter API with any open source model.
|
| 12 |
Returns the response text.
|
|
|
|
| 6 |
OPENROUTER_API_URL = "https://openrouter.ai/api/v1/chat/completions"
|
| 7 |
|
| 8 |
|
| 9 |
+
async def call_llm(prompt: str, system_prompt: str = None, max_tokens: int = 2000) -> str:
|
| 10 |
"""
|
| 11 |
Call OpenRouter API with any open source model.
|
| 12 |
Returns the response text.
|