Skip to content

Commit 04e4b41

Browse files
authored
Merge pull request #7 from microsoft/feat/repo-config
Refactors code for improved structure and maintainability
2 parents b64c1c2 + db02d95 commit 04e4b41

File tree

16 files changed

+1025
-802
lines changed

16 files changed

+1025
-802
lines changed

backend/app/constants.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
"""
2+
Constants used throughout the application.
3+
"""
4+
5+
AGENT_ID_GITHUB_COPILOT_COMPLETIONS = 'github-copilot-completions'
6+
AGENT_ID_GITHUB_COPILOT_AGENT = 'github-copilot-agent'
7+
AGENT_ID_DEVIN = 'devin'
8+
AGENT_ID_CODEX_CLI = 'codex-cli'
9+
AGENT_ID_SREAGENT = 'sreagent'
10+
11+
LEGACY_AGENT_ID_MAP = {
12+
'github-copilot': AGENT_ID_GITHUB_COPILOT_COMPLETIONS,
13+
}
14+
15+
DEPENDENCY_FILES = ["requirements.txt", "package.json", "pom.xml", "build.gradle"]
16+
17+
LANGUAGE_MAP = {
18+
"requirements.txt": "Python",
19+
"package.json": "JavaScript/TypeScript",
20+
"pom.xml": "Java",
21+
"build.gradle": "Java/Kotlin",
22+
}

backend/app/services/agent.py

Lines changed: 60 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,32 @@
11
import asyncio
22
import time
3+
from typing import Dict, List, Optional, Any, Union
4+
35
from azure.ai.agents.aio import AgentsClient
46
from azure.core.credentials import AzureKeyCredential
7+
58
from ..config import AZURE_AI_PROJECT_CONNECTION_STRING, AZURE_AI_AGENTS_API_KEY
9+
from ..constants import (
10+
AGENT_ID_GITHUB_COPILOT_COMPLETIONS,
11+
AGENT_ID_GITHUB_COPILOT_AGENT,
12+
AGENT_ID_DEVIN,
13+
AGENT_ID_CODEX_CLI,
14+
AGENT_ID_SREAGENT,
15+
LEGACY_AGENT_ID_MAP,
16+
DEPENDENCY_FILES,
17+
LANGUAGE_MAP,
18+
)
19+
620

721
class AzureAgentService:
8-
def __init__(self):
22+
"""Service for interacting with Azure AI Agents."""
23+
24+
def __init__(self) -> None:
25+
"""Initialize the Azure Agent Service with credentials."""
926
self.endpoint = AZURE_AI_PROJECT_CONNECTION_STRING
1027
self.credential = None if not AZURE_AI_AGENTS_API_KEY or AZURE_AI_AGENTS_API_KEY == "your_api_key" else AzureKeyCredential(AZURE_AI_AGENTS_API_KEY)
1128

12-
async def analyze_repository(self, agent_id: str, repo_name: str, readme_content: str, dependencies: dict):
29+
async def analyze_repository(self, agent_id: str, repo_name: str, readme_content: str, dependencies: Dict[str, str]) -> str:
1330
"""
1431
Analyze a repository using Azure AI Agents.
1532
@@ -82,21 +99,28 @@ async def analyze_repository(self, agent_id: str, repo_name: str, readme_content
8299
except Exception as e:
83100
return f"Error connecting to Azure AI Agents: {str(e)}\n\nUsing mock data instead:\n\n{self._generate_mock_analysis(agent_id, repo_name, readme_content, dependencies)}"
84101

85-
def _generate_mock_analysis(self, agent_id: str, repo_name: str, readme_content: str, dependencies: dict):
86-
"""Generate mock analysis data for testing purposes."""
102+
def _generate_mock_analysis(self, agent_id: str, repo_name: str, readme_content: str, dependencies: Dict[str, str]) -> str:
103+
"""
104+
Generate mock analysis data for testing purposes.
105+
106+
Args:
107+
agent_id: The type of AI agent
108+
repo_name: The repository name in owner/repo format
109+
readme_content: The README content of the repository
110+
dependencies: Dictionary of dependency files and their contents
111+
112+
Returns:
113+
Mock analysis as a string
114+
"""
87115
language = "JavaScript"
88116
if dependencies:
89-
if "requirements.txt" in dependencies:
90-
language = "Python"
91-
elif "package.json" in dependencies:
92-
language = "JavaScript/TypeScript"
93-
elif "pom.xml" in dependencies:
94-
language = "Java"
95-
elif "build.gradle" in dependencies:
96-
language = "Java/Kotlin"
117+
for dep_file, lang in LANGUAGE_MAP.items():
118+
if dep_file in dependencies:
119+
language = lang
120+
break
97121

98122
analyses = {
99-
'github-copilot-completions': f"""## GitHub Copilot (Code Completions) Analysis for {repo_name}
123+
AGENT_ID_GITHUB_COPILOT_COMPLETIONS: f"""## GitHub Copilot (Code Completions) Analysis for {repo_name}
100124
101125
This repository is well-suited for GitHub Copilot code completions. Based on the codebase structure and {language} language, here's how to set up:
102126
@@ -122,7 +146,7 @@ def _generate_mock_analysis(self, agent_id: str, repo_name: str, readme_content:
122146
```
123147
124148
Enable GitHub Copilot in your editor settings and start coding with AI assistance!""",
125-
'github-copilot-agent': f"""## GitHub Copilot Coding Agent Analysis for {repo_name}
149+
AGENT_ID_GITHUB_COPILOT_AGENT: f"""## GitHub Copilot Coding Agent Analysis for {repo_name}
126150
127151
This repository can benefit from GitHub Copilot Coding Agent for asynchronous, issue-driven automation. Here's how to set up:
128152
@@ -142,7 +166,7 @@ def _generate_mock_analysis(self, agent_id: str, repo_name: str, readme_content:
142166
```
143167
144168
Copilot Coding Agent works best with clear, well-scoped issues and access to your repository's context.""",
145-
'devin': f"""## Devin Configuration for {repo_name}
169+
AGENT_ID_DEVIN: f"""## Devin Configuration for {repo_name}
146170
147171
This {language} repository can be effectively worked on using Devin. Here's the setup:
148172
@@ -169,7 +193,7 @@ def _generate_mock_analysis(self, agent_id: str, repo_name: str, readme_content:
169193
```
170194
171195
Devin works best with this repository by understanding the full context of files and dependencies.""",
172-
'codex-cli': f"""## Codex CLI Setup for {repo_name}
196+
AGENT_ID_CODEX_CLI: f"""## Codex CLI Setup for {repo_name}
173197
174198
This guide will help you set up Codex CLI to work with this {language} repository.
175199
@@ -206,7 +230,7 @@ def _generate_mock_analysis(self, agent_id: str, repo_name: str, readme_content:
206230
- Use for documentation generation
207231
208232
This repository's structure is compatible with Codex CLI's code generation capabilities.""",
209-
'sreagent': f"""## SREAgent Configuration for {repo_name}
233+
AGENT_ID_SREAGENT: f"""## SREAgent Configuration for {repo_name}
210234
211235
This guide will help you set up SREAgent for this {language} repository.
212236
@@ -243,54 +267,59 @@ def _generate_mock_analysis(self, agent_id: str, repo_name: str, readme_content:
243267
244268
SREAgent can help maintain reliability for services deployed from this repository."""
245269
}
270+
246271
# Support legacy IDs for backward compatibility
247-
legacy_map = {
248-
'github-copilot': 'github-copilot-completions',
249-
}
250-
lookup_id = legacy_map.get(agent_id, agent_id)
272+
lookup_id = LEGACY_AGENT_ID_MAP.get(agent_id, agent_id)
251273
return analyses.get(lookup_id, f"No specific analysis available for {agent_id} and {repo_name}. Please try another agent.")
252274

253-
def _get_agent_instructions(self, agent_id: str):
275+
def _get_agent_instructions(self, agent_id: str) -> str:
276+
"""
277+
Get agent-specific instructions for analysis.
278+
279+
Args:
280+
agent_id: The type of AI agent
281+
282+
Returns:
283+
Instructions string for the agent
284+
"""
254285
base_instructions = (
255286
"You are an AI assistant that analyzes GitHub repositories and provides detailed setup "
256287
"instructions for different AI agents. Your job is to analyze the repository README and "
257288
"dependency files to understand the project structure and requirements."
258289
)
259290
agent_specific_instructions = {
260-
"github-copilot-completions": (
291+
AGENT_ID_GITHUB_COPILOT_COMPLETIONS: (
261292
"Focus on how to set up GitHub Copilot (Code Completions) for this repository. Explain how to "
262293
"install GitHub Copilot in VS Code, JetBrains, or other supported IDEs, how to "
263294
"configure it for this specific project, and provide tips for getting the best "
264295
"code suggestions based on this repository's structure and languages."
265296
),
266-
"github-copilot-agent": (
297+
AGENT_ID_GITHUB_COPILOT_AGENT: (
267298
"Focus on how to set up GitHub Copilot Coding Agent for this repository. Explain how to "
268299
"assign issues to the agent, how it creates pull requests and runs CI/CD, and provide tips for "
269300
"effective use based on this repository's structure and requirements."
270301
),
271-
"devin": (
302+
AGENT_ID_DEVIN: (
272303
"Focus on how to set up Devin for this repository. Explain how to access Devin "
273304
"through Azure Marketplace, how to clone and configure this repository for Devin, "
274305
"and provide tips for effective collaboration with Devin based on this repository's "
275306
"structure and requirements."
276307
),
277-
"codex-cli": (
308+
AGENT_ID_CODEX_CLI: (
278309
"Focus on how to set up Codex CLI for this repository. Explain how to install "
279310
"and configure Codex CLI with Azure OpenAI or OpenAI, how to use it effectively with this "
280311
"repository, and provide example commands tailored to this repository's structure."
281312
),
282-
"sreagent": (
313+
AGENT_ID_SREAGENT: (
283314
"Focus on how to set up SREAgent for this repository. Explain how to configure "
284315
"SREAgent in an Azure environment, how to connect it with this repository, "
285316
"and recommend monitoring metrics and alert policies based on this repository's "
286317
"structure and purpose."
287318
)
288319
}
320+
289321
# Support legacy IDs for backward compatibility
290-
legacy_map = {
291-
'github-copilot': 'github-copilot-completions',
292-
}
293-
lookup_id = legacy_map.get(agent_id, agent_id)
322+
lookup_id = LEGACY_AGENT_ID_MAP.get(agent_id, agent_id)
294323
if lookup_id in agent_specific_instructions:
295324
return base_instructions + "\n\n" + agent_specific_instructions[lookup_id]
296325
return base_instructions

backend/app/services/github.py

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,34 @@
11
import base64
2+
from typing import Dict, Optional, Any
3+
24
import httpx
5+
36
from ..config import GITHUB_TOKEN, GITHUB_API_URL
7+
from ..constants import DEPENDENCY_FILES
8+
49

510
class GitHubService:
6-
def __init__(self):
11+
"""Service for interacting with the GitHub API."""
12+
13+
def __init__(self) -> None:
14+
"""Initialize the GitHub service with authentication headers."""
715
self.headers = {
816
"Accept": "application/vnd.github.v3+json",
917
}
1018
if GITHUB_TOKEN:
1119
self.headers["Authorization"] = f"token {GITHUB_TOKEN}"
1220

13-
async def get_repository_info(self, owner: str, repo: str):
14-
"""Get basic repository information."""
21+
async def get_repository_info(self, owner: str, repo: str) -> Optional[Dict[str, Any]]:
22+
"""
23+
Get basic repository information from GitHub API.
24+
25+
Args:
26+
owner: Repository owner/organization
27+
repo: Repository name
28+
29+
Returns:
30+
Repository information as a dictionary or None if not found
31+
"""
1532
async with httpx.AsyncClient() as client:
1633
response = await client.get(
1734
f"{GITHUB_API_URL}/repos/{owner}/{repo}",
@@ -21,8 +38,17 @@ async def get_repository_info(self, owner: str, repo: str):
2138
return response.json()
2239
return None
2340

24-
async def get_readme_content(self, owner: str, repo: str):
25-
"""Get the README content of a repository."""
41+
async def get_readme_content(self, owner: str, repo: str) -> Optional[str]:
42+
"""
43+
Get the README content of a repository.
44+
45+
Args:
46+
owner: Repository owner/organization
47+
repo: Repository name
48+
49+
Returns:
50+
README content as a string or None if not found
51+
"""
2652
async with httpx.AsyncClient() as client:
2753
response = await client.get(
2854
f"{GITHUB_API_URL}/repos/{owner}/{repo}/readme",
@@ -34,13 +60,21 @@ async def get_readme_content(self, owner: str, repo: str):
3460
return base64.b64decode(data["content"]).decode("utf-8")
3561
return None
3662

37-
async def get_requirements(self, owner: str, repo: str):
38-
"""Try to get requirements.txt or similar dependency files."""
39-
dependency_files = ["requirements.txt", "package.json", "pom.xml", "build.gradle"]
40-
results = {}
63+
async def get_requirements(self, owner: str, repo: str) -> Dict[str, str]:
64+
"""
65+
Try to get requirements.txt or similar dependency files.
66+
67+
Args:
68+
owner: Repository owner/organization
69+
repo: Repository name
70+
71+
Returns:
72+
Dictionary mapping file names to their contents
73+
"""
74+
results: Dict[str, str] = {}
4175

4276
async with httpx.AsyncClient() as client:
43-
for file in dependency_files:
77+
for file in DEPENDENCY_FILES:
4478
response = await client.get(
4579
f"{GITHUB_API_URL}/repos/{owner}/{repo}/contents/{file}",
4680
headers=self.headers

backend/pyproject.toml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
[tool.black]
2+
line-length = 88
3+
target-version = ['py38']
4+
include = '\.pyi?$'
5+
6+
[tool.isort]
7+
profile = "black"
8+
line_length = 88
9+
multi_line_output = 3
10+
11+
[tool.mypy]
12+
python_version = "3.8"
13+
warn_return_any = true
14+
warn_unused_configs = true
15+
disallow_untyped_defs = true
16+
disallow_incomplete_defs = true
17+
18+
[tool.flake8]
19+
max-line-length = 88
20+
extend-ignore = "E203"
21+
exclude = [".git", "__pycache__", "build", "dist"]

backend/requirements-dev.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
black==23.3.0
2+
isort==5.12.0
3+
flake8==6.0.0
4+
mypy==1.3.0
5+
pytest==7.3.1

frontend/.eslintrc.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
module.exports = {
2+
root: true,
3+
env: {
4+
browser: true,
5+
es2021: true,
6+
node: true,
7+
},
8+
extends: [
9+
'eslint:recommended',
10+
'plugin:react/recommended',
11+
'plugin:react-hooks/recommended',
12+
'plugin:@typescript-eslint/recommended',
13+
],
14+
parser: '@typescript-eslint/parser',
15+
parserOptions: {
16+
ecmaFeatures: {
17+
jsx: true,
18+
},
19+
ecmaVersion: 'latest',
20+
sourceType: 'module',
21+
},
22+
plugins: ['react', '@typescript-eslint'],
23+
settings: {
24+
react: {
25+
version: 'detect',
26+
},
27+
},
28+
rules: {
29+
'react/react-in-jsx-scope': 'off',
30+
'react/prop-types': 'off',
31+
'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
32+
'@typescript-eslint/no-explicit-any': 'warn',
33+
'no-console': ['warn', { allow: ['warn', 'error'] }],
34+
},
35+
};

frontend/.prettierrc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"semi": true,
3+
"singleQuote": true,
4+
"tabWidth": 2,
5+
"trailingComma": "es5",
6+
"printWidth": 100,
7+
"bracketSpacing": true,
8+
"arrowParens": "avoid"
9+
}

0 commit comments

Comments
 (0)