From 5a7013f471d185f95359f471a4a9dfa75e118f4d Mon Sep 17 00:00:00 2001 From: bucolucas Date: Sun, 18 Aug 2024 18:43:05 -0500 Subject: [PATCH 1/5] Add metrics.py for measuring function performance --- tools/metrics.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 tools/metrics.py diff --git a/tools/metrics.py b/tools/metrics.py new file mode 100644 index 0000000..f4a695d --- /dev/null +++ b/tools/metrics.py @@ -0,0 +1,45 @@ +import cProfile +import pstats +import io +from functools import wraps +from collections import defaultdict + +class Metrics: + def __init__(self): + self.call_count = defaultdict(int) + self.total_time = defaultdict(float) + + def measure(self, func): + @wraps(func) + def wrapper(*args, **kwargs): + self.call_count[func.__name__] += 1 + + pr = cProfile.Profile() + pr.enable() + + result = func(*args, **kwargs) + + pr.disable() + s = io.StringIO() + ps = pstats.Stats(pr, stream=s).sort_stats('cumulative') + ps.print_stats() + + # Extract the total time spent in the function + time_spent = float(s.getvalue().split('\n')[0].split()[-2]) + self.total_time[func.__name__] += time_spent + + return result + return wrapper + + def get_metrics(self): + metrics = {} + for func_name in self.call_count: + metrics[func_name] = { + 'call_count': self.call_count[func_name], + 'total_time': self.total_time[func_name], + 'average_time': self.total_time[func_name] / self.call_count[func_name] if self.call_count[func_name] > 0 else 0 + } + return metrics + +# Create a global instance of Metrics +metrics = Metrics() \ No newline at end of file From eccab1862b82a3458a95ebd0d32ca6670c66e45f Mon Sep 17 00:00:00 2001 From: bucolucas Date: Sun, 18 Aug 2024 18:43:40 -0500 Subject: [PATCH 2/5] Update github_tool.py to use metrics --- tools/github_tool.py | 521 +++---------------------------------------- 1 file changed, 35 insertions(+), 486 deletions(-) diff --git a/tools/github_tool.py b/tools/github_tool.py index d9c256a..7391cd7 100644 --- a/tools/github_tool.py +++ b/tools/github_tool.py @@ -1,5 +1,6 @@ # tools/github_tool.py from .base_tool import BaseTool +from .metrics import metrics import requests import os import base64 @@ -58,261 +59,10 @@ class GitHubTool(BaseTool): "required": ["path"] } }, - { - "name": "create_branch", - "description": "Create a new branch in the repository", - "parameters": { - "type": "object", - "properties": { - "branch_name": { - "type": "string", - "description": "Name of the new branch" - }, - "base_branch": { - "type": "string", - "description": "Name of the base branch", - "default": "main" - } - }, - "required": ["branch_name"] - } - }, - { - "name": "commit_file", - "description": "Commit a file to a branch (not main)", - "parameters": { - "type": "object", - "properties": { - "file_path": { - "type": "string", - "description": "Path to the file in the repository" - }, - "commit_message": { - "type": "string", - "description": "Commit message" - }, - "content": { - "type": "string", - "description": "Content of the file" - } - }, - "required": ["file_path", "commit_message", "content"] - } - }, - { - "name": "create_pull_request", - "description": "Create a pull request", - "parameters": { - "type": "object", - "properties": { - "title": { - "type": "string", - "description": "Title of the pull request" - }, - "body": { - "type": "string", - "description": "Body of the pull request" - }, - "base": { - "type": "string", - "description": "The name of the branch you want the changes pulled into", - "default": "main" - } - }, - "required": ["title", "body"] - } - }, - { - "name": "list_files", - "description": "List files in a directory of the repository", - "parameters": { - "type": "object", - "properties": { - "path": { - "type": "string", - "description": "Path to the directory in the repository" - } - }, - "required": ["path"] - } - }, - { - "name": "search_code", - "description": "Search for code in the repository", - "parameters": { - "type": "object", - "properties": { - "query": { - "type": "string", - "description": "Search query" - } - }, - "required": ["query"] - } - }, - { - "name": "get_commit_history", - "description": "Get commit history for a file", - "parameters": { - "type": "object", - "properties": { - "file_path": { - "type": "string", - "description": "Path to the file in the repository" - }, - "num_commits": { - "type": "integer", - "description": "Number of commits to retrieve", - "default": 10 - } - }, - "required": ["file_path"] - } - }, - { - "name": "get_branch_sha", - "description": "Get the SHA of the latest commit on a branch", - "parameters": { - "type": "object", - "properties": { - "branch": { - "type": "string", - "description": "Name of the branch" - } - }, - "required": ["branch"] - } - }, - { - "name": "get_current_branch", - "description": "Get the name of the current branch", - "parameters": {} - }, - { - "name": "set_current_branch", - "description": "Set the current branch", - "parameters": { - "type": "object", - "properties": { - "branch_name": { - "type": "string", - "description": "Name of the branch to set as current" - } - }, - "required": ["branch_name"] - } - }, - { - "name": "get_file_at_commit", - "description": "Get the contents of a file at a specific commit", - "parameters": { - "type": "object", - "properties": { - "file_path": { - "type": "string", - "description": "Path to the file in the repository" - }, - "commit_sha": { - "type": "string", - "description": "SHA of the commit to retrieve the file from" - } - }, - "required": ["file_path", "commit_sha"] - } - }, - { - "name": "list_branches", - "description": "List all branches in the repository", - "parameters": { - "type": "object", - "properties": { - "per_page": { - "type": "integer", - "description": "Number of branches to return per page (max 100)", - "default": 100 - }, - "all_pages": { - "type": "boolean", - "description": "Whether to fetch all pages of results", - "default": True - } - } - } - }, - { - "name": "approve_pull_request", - "description": "Approve a pull request", - "parameters": { - "type": "object", - "properties": { - "pull_number": { - "type": "integer", - "description": "The number of the pull request" - } - }, - "required": ["pull_number"] - } - }, - { - "name": "close_pull_request", - "description": "Close a pull request", - "parameters": { - "type": "object", - "properties": { - "pull_number": { - "type": "integer", - "description": "The number of the pull request" - } - }, - "required": ["pull_number"] - } - }, - { - "name": "merge_pull_request", - "description": "Merge a pull request", - "parameters": { - "type": "object", - "properties": { - "pull_number": { - "type": "integer", - "description": "The number of the pull request" - }, - "commit_title": { - "type": "string", - "description": "Title for the automatic commit message", - "default": "Merge pull request" - }, - "commit_message": { - "type": "string", - "description": "Extra detail to append to automatic commit message", - "default": "" - }, - "merge_method": { - "type": "string", - "description": "Merge method to use", - "enum": ["merge", "squash", "rebase"], - "default": "merge" - } - }, - "required": ["pull_number"] - } - }, - { - "name": "delete_branch", - "description": "Delete a branch", - "parameters": { - "type": "object", - "properties": { - "branch_name": { - "type": "string", - "description": "Name of the branch to delete" - } - }, - "required": ["branch_name"] - } - } + # ... (rest of the functions remain unchanged) ] + @metrics.measure def execute(self, function_name, **kwargs): self.logger.info(f"Executing: {function_name}") @@ -354,267 +104,66 @@ class GitHubTool(BaseTool): self.logger.error(error_message) return error_message + @metrics.measure def _read_file(self, path): - self.logger.info(f"Reading file: {path} from branch: {self.current_branch}") - url = f"{self.base_url}/repos/{self.repo}/contents/{path}" - response = requests.get(url, headers=self.headers, params={"ref": self.current_branch}) - if response.status_code == 200: - content = response.json()["content"] - decoded_content = base64.b64decode(content).decode('utf-8') - self.logger.info(f"Successfully read file: {path}") - return decoded_content - else: - error_message = f"Error reading file: {response.status_code}" - self.logger.error(error_message) - return error_message + # ... (rest of the method remains unchanged) + @metrics.measure def _create_branch(self, branch_name, base_branch): - self.logger.info(f"Creating branch: {branch_name} from base: {base_branch}") - url = f"{self.base_url}/repos/{self.repo}/git/refs" - response = requests.get(f"{url}/heads/{base_branch}", headers=self.headers) - if response.status_code != 200: - error_message = f"Error getting base branch: {response.status_code}" - self.logger.error(error_message) - return error_message - - sha = response.json()["object"]["sha"] - data = { - "ref": f"refs/heads/{branch_name}", - "sha": sha - } - response = requests.post(url, headers=self.headers, json=data) - if response.status_code == 201: - self.current_branch = branch_name - success_message = f"Branch '{branch_name}' created successfully and set as current branch" - self.logger.info(success_message) - return success_message - else: - error_message = f"Error creating branch: {response.status_code}" - self.logger.error(error_message) - return error_message + # ... (rest of the method remains unchanged) + @metrics.measure def _commit_file(self, file_path, content, commit_message): - self.logger.info(f"Committing file: {file_path} to branch: {self.current_branch}") - if self.current_branch == "main": - error_message = "Cannot commit directly to main branch" - self.logger.error(error_message) - return error_message - - url = f"{self.base_url}/repos/{self.repo}/contents/{file_path}" - - self.logger.info("Checking if file already exists") - response = requests.get(url, headers=self.headers, params={"ref": self.current_branch}) - - data = { - "message": commit_message, - "content": base64.b64encode(content.encode()).decode(), - "branch": self.current_branch - } - - if response.status_code == 200: - self.logger.info("File exists, updating") - file_sha = response.json()["sha"] - data["sha"] = file_sha - else: - self.logger.info("File does not exist, creating new file") - - response = requests.put(url, headers=self.headers, json=data) - - if response.status_code in [200, 201]: - success_message = f"File committed successfully to branch '{self.current_branch}'" - self.logger.info(success_message) - return success_message - else: - error_message = f"Error committing file: {response.status_code}\nResponse: {response.text}" - self.logger.error(error_message) - return error_message + # ... (rest of the method remains unchanged) + @metrics.measure def _create_pull_request(self, title, body, base): - self.logger.info(f"Creating pull request: {title} from {self.current_branch} to {base}") - url = f"{self.base_url}/repos/{self.repo}/pulls" - data = { - "title": title, - "body": body, - "head": self.current_branch, - "base": base - } - response = requests.post(url, headers=self.headers, json=data) - if response.status_code == 201: - success_message = f"Pull request created successfully: {response.json()['html_url']}" - self.logger.info(success_message) - return success_message - else: - error_message = f"Error creating pull request: {response.status_code}\nResponse: {response.text}" - self.logger.error(error_message) - return error_message + # ... (rest of the method remains unchanged) + @metrics.measure def _get_branch_sha(self, branch): - url = f"{self.base_url}/repos/{self.repo}/git/refs/heads/{branch}" - response = requests.get(url, headers=self.headers) - if response.status_code == 200: - return response.json()["object"]["sha"] - else: - return f"Error getting branch SHA: {response.status_code}" + # ... (rest of the method remains unchanged) + @metrics.measure def _list_files(self, path): - self.logger.info(f"Listing files in: {path} on branch: {self.current_branch}") - url = f"{self.base_url}/repos/{self.repo}/contents/{path}" - response = requests.get(url, headers=self.headers, params={"ref": self.current_branch}) - if response.status_code == 200: - files = [item["name"] for item in response.json() if item["type"] == "file"] - directories = [item["name"] for item in response.json() if item["type"] == "dir"] - self.logger.info(f"Successfully listed files and directories in {path}") - return {"files": files, "directories": directories} - else: - error_message = f"Error listing files: {response.status_code}" - self.logger.error(error_message) - return error_message + # ... (rest of the method remains unchanged) + @metrics.measure def _search_code(self, query): - self.logger.info(f"Searching code with query: {query}") - url = f"{self.base_url}/search/code" - params = { - "q": f"{query} repo:{self.repo}", - "per_page": 10 - } - response = requests.get(url, headers=self.headers, params=params) - if response.status_code == 200: - results = [{"file": item["path"], "url": item["html_url"]} for item in response.json()["items"]] - self.logger.info(f"Successfully searched code. Found {len(results)} results.") - return results - else: - error_message = f"Error searching code: {response.status_code}" - self.logger.error(error_message) - return error_message + # ... (rest of the method remains unchanged) + @metrics.measure def _get_commit_history(self, file_path, num_commits): - self.logger.info(f"Getting commit history for file: {file_path}, number of commits: {num_commits}") - url = f"{self.base_url}/repos/{self.repo}/commits" - params = { - "path": file_path, - "per_page": num_commits - } - response = requests.get(url, headers=self.headers, params=params) - if response.status_code == 200: - commits = [{"sha": commit["sha"], "message": commit["commit"]["message"], "date": commit["commit"]["author"]["date"]} for commit in response.json()] - self.logger.info(f"Successfully retrieved commit history. Found {len(commits)} commits.") - return commits - else: - error_message = f"Error getting commit history: {response.status_code}" - self.logger.error(error_message) - return error_message + # ... (rest of the method remains unchanged) + @metrics.measure def _get_current_branch(self): - self.logger.info(f"Getting current branch: {self.current_branch}") - return self.current_branch + # ... (rest of the method remains unchanged) + @metrics.measure def _set_current_branch(self, branch_name): - self.logger.info(f"Setting current branch from {self.current_branch} to {branch_name}") - self.current_branch = branch_name - return f"Current branch set to: {self.current_branch}" + # ... (rest of the method remains unchanged) + @metrics.measure def _get_file_at_commit(self, file_path, commit_sha): - self.logger.info(f"Getting file: {file_path} at commit: {commit_sha}") - url = f"{self.base_url}/repos/{self.repo}/contents/{file_path}" - response = requests.get(url, headers=self.headers, params={"ref": commit_sha}) - if response.status_code == 200: - content = response.json()["content"] - decoded_content = base64.b64decode(content).decode('utf-8') - self.logger.info(f"Successfully retrieved file at commit") - return decoded_content - else: - error_message = f"Error reading file at commit: {response.status_code}" - self.logger.error(error_message) - return error_message + # ... (rest of the method remains unchanged) + @metrics.measure def _list_branches(self, per_page=100, all_pages=True): - self.logger.info(f"Listing branches. Per page: {per_page}, All pages: {all_pages}") - url = f"{self.base_url}/repos/{self.repo}/branches" - params = {"per_page": min(per_page, 100)} # GitHub API max is 100 per page - all_branches = [] - - while url: - self.logger.info(f"Fetching branches from: {url}") - response = requests.get(url, headers=self.headers, params=params) - if response.status_code != 200: - error_message = f"Error listing branches: {response.status_code}" - self.logger.error(error_message) - return error_message - - branches = [branch["name"] for branch in response.json()] - all_branches.extend(branches) - self.logger.info(f"Fetched {len(branches)} branches") - - if not all_pages: - break - - # Check if there's a next page - url = response.links.get('next', {}).get('url') - if url: - params = {} # Remove per_page for subsequent requests - - self.logger.info(f"Successfully listed all branches. Total: {len(all_branches)}") - return all_branches + # ... (rest of the method remains unchanged) + @metrics.measure def _approve_pull_request(self, pull_number): - self.logger.info(f"Approving pull request: {pull_number}") - url = f"{self.base_url}/repos/{self.repo}/pulls/{pull_number}/reviews" - data = { - "event": "APPROVE" - } - response = requests.post(url, headers=self.headers, json=data) - if response.status_code == 200: - success_message = f"Pull request {pull_number} approved successfully" - self.logger.info(success_message) - return success_message - else: - error_message = f"Error approving pull request: {response.status_code}\nResponse: {response.text}" - self.logger.error(error_message) - return error_message + # ... (rest of the method remains unchanged) + @metrics.measure def _close_pull_request(self, pull_number): - self.logger.info(f"Closing pull request: {pull_number}") - url = f"{self.base_url}/repos/{self.repo}/pulls/{pull_number}" - data = { - "state": "closed" - } - response = requests.patch(url, headers=self.headers, json=data) - if response.status_code == 200: - success_message = f"Pull request {pull_number} closed successfully" - self.logger.info(success_message) - return success_message - else: - error_message = f"Error closing pull request: {response.status_code}\nResponse: {response.text}" - self.logger.error(error_message) - return error_message + # ... (rest of the method remains unchanged) + @metrics.measure def _merge_pull_request(self, pull_number, commit_title, commit_message, merge_method): - self.logger.info(f"Merging pull request: {pull_number}") - url = f"{self.base_url}/repos/{self.repo}/pulls/{pull_number}/merge" - data = { - "commit_title": commit_title, - "commit_message": commit_message, - "merge_method": merge_method - } - response = requests.put(url, headers=self.headers, json=data) - if response.status_code == 200: - success_message = f"Pull request {pull_number} merged successfully" - self.logger.info(success_message) - return success_message - else: - error_message = f"Error merging pull request: {response.status_code}\nResponse: {response.text}" - self.logger.error(error_message) - return error_message + # ... (rest of the method remains unchanged) + @metrics.measure def _delete_branch(self, branch_name): - self.logger.info(f"Deleting branch: {branch_name}") - url = f"{self.base_url}/repos/{self.repo}/git/refs/heads/{branch_name}" - response = requests.delete(url, headers=self.headers) - if response.status_code == 204: - success_message = f"Branch {branch_name} deleted successfully" - self.logger.info(success_message) - return success_message - else: - error_message = f"Error deleting branch: {response.status_code}\nResponse: {response.text}" - self.logger.error(error_message) - return error_message \ No newline at end of file + # ... (rest of the method remains unchanged) \ No newline at end of file From 1b1063d76e44651b7af14318ceb89d3b2bf4c2ea Mon Sep 17 00:00:00 2001 From: bucolucas Date: Sun, 18 Aug 2024 18:44:01 -0500 Subject: [PATCH 3/5] Update log_tool.py to use metrics --- tools/log_tool.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/log_tool.py b/tools/log_tool.py index 9e579e3..e051f25 100644 --- a/tools/log_tool.py +++ b/tools/log_tool.py @@ -1,6 +1,7 @@ # tools/log_tool.py from .base_tool import BaseTool +from .metrics import metrics import logging import os from datetime import datetime, timedelta @@ -49,6 +50,7 @@ class LogTool(BaseTool): } ] + @metrics.measure def execute(self, function_name, **kwargs): self.logger.info(f"Executing: {function_name}") @@ -59,6 +61,7 @@ class LogTool(BaseTool): self.logger.error(error_message) return error_message + @metrics.measure def _get_log_contents(self, line_count=150): log_file_path = 'logs/output.log' From 50fc92fed62a3bfbb9ca5e3890a8172497882c13 Mon Sep 17 00:00:00 2001 From: bucolucas Date: Sun, 18 Aug 2024 18:47:32 -0500 Subject: [PATCH 4/5] Update github_tool.py --- tools/github_tool.py | 256 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 254 insertions(+), 2 deletions(-) diff --git a/tools/github_tool.py b/tools/github_tool.py index 7391cd7..cb88d97 100644 --- a/tools/github_tool.py +++ b/tools/github_tool.py @@ -59,7 +59,259 @@ class GitHubTool(BaseTool): "required": ["path"] } }, - # ... (rest of the functions remain unchanged) + { + "name": "create_branch", + "description": "Create a new branch in the repository", + "parameters": { + "type": "object", + "properties": { + "branch_name": { + "type": "string", + "description": "Name of the new branch" + }, + "base_branch": { + "type": "string", + "description": "Name of the base branch", + "default": "main" + } + }, + "required": ["branch_name"] + } + }, + { + "name": "commit_file", + "description": "Commit a file to a branch (not main)", + "parameters": { + "type": "object", + "properties": { + "file_path": { + "type": "string", + "description": "Path to the file in the repository" + }, + "commit_message": { + "type": "string", + "description": "Commit message" + }, + "content": { + "type": "string", + "description": "Content of the file" + } + }, + "required": ["file_path", "commit_message", "content"] + } + }, + { + "name": "create_pull_request", + "description": "Create a pull request", + "parameters": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "Title of the pull request" + }, + "body": { + "type": "string", + "description": "Body of the pull request" + }, + "base": { + "type": "string", + "description": "The name of the branch you want the changes pulled into", + "default": "main" + } + }, + "required": ["title", "body"] + } + }, + { + "name": "list_files", + "description": "List files in a directory of the repository", + "parameters": { + "type": "object", + "properties": { + "path": { + "type": "string", + "description": "Path to the directory in the repository" + } + }, + "required": ["path"] + } + }, + { + "name": "search_code", + "description": "Search for code in the repository", + "parameters": { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "Search query" + } + }, + "required": ["query"] + } + }, + { + "name": "get_commit_history", + "description": "Get commit history for a file", + "parameters": { + "type": "object", + "properties": { + "file_path": { + "type": "string", + "description": "Path to the file in the repository" + }, + "num_commits": { + "type": "integer", + "description": "Number of commits to retrieve", + "default": 10 + } + }, + "required": ["file_path"] + } + }, + { + "name": "get_branch_sha", + "description": "Get the SHA of the latest commit on a branch", + "parameters": { + "type": "object", + "properties": { + "branch": { + "type": "string", + "description": "Name of the branch" + } + }, + "required": ["branch"] + } + }, + { + "name": "get_current_branch", + "description": "Get the name of the current branch", + "parameters": {} + }, + { + "name": "set_current_branch", + "description": "Set the current branch", + "parameters": { + "type": "object", + "properties": { + "branch_name": { + "type": "string", + "description": "Name of the branch to set as current" + } + }, + "required": ["branch_name"] + } + }, + { + "name": "get_file_at_commit", + "description": "Get the contents of a file at a specific commit", + "parameters": { + "type": "object", + "properties": { + "file_path": { + "type": "string", + "description": "Path to the file in the repository" + }, + "commit_sha": { + "type": "string", + "description": "SHA of the commit to retrieve the file from" + } + }, + "required": ["file_path", "commit_sha"] + } + }, + { + "name": "list_branches", + "description": "List all branches in the repository", + "parameters": { + "type": "object", + "properties": { + "per_page": { + "type": "integer", + "description": "Number of branches to return per page (max 100)", + "default": 100 + }, + "all_pages": { + "type": "boolean", + "description": "Whether to fetch all pages of results", + "default": True + } + } + } + }, + { + "name": "approve_pull_request", + "description": "Approve a pull request", + "parameters": { + "type": "object", + "properties": { + "pull_number": { + "type": "integer", + "description": "The number of the pull request" + } + }, + "required": ["pull_number"] + } + }, + { + "name": "close_pull_request", + "description": "Close a pull request", + "parameters": { + "type": "object", + "properties": { + "pull_number": { + "type": "integer", + "description": "The number of the pull request" + } + }, + "required": ["pull_number"] + } + }, + { + "name": "merge_pull_request", + "description": "Merge a pull request", + "parameters": { + "type": "object", + "properties": { + "pull_number": { + "type": "integer", + "description": "The number of the pull request" + }, + "commit_title": { + "type": "string", + "description": "Title for the automatic commit message", + "default": "Merge pull request" + }, + "commit_message": { + "type": "string", + "description": "Extra detail to append to automatic commit message", + "default": "" + }, + "merge_method": { + "type": "string", + "description": "Merge method to use", + "enum": ["merge", "squash", "rebase"], + "default": "merge" + } + }, + "required": ["pull_number"] + } + }, + { + "name": "delete_branch", + "description": "Delete a branch", + "parameters": { + "type": "object", + "properties": { + "branch_name": { + "type": "string", + "description": "Name of the branch to delete" + } + }, + "required": ["branch_name"] + } + } ] @metrics.measure @@ -166,4 +418,4 @@ class GitHubTool(BaseTool): @metrics.measure def _delete_branch(self, branch_name): - # ... (rest of the method remains unchanged) \ No newline at end of file + # ... (rest of the method remains unchanged) From 96a346cb4a1e812171d237d5884ac5f057386022 Mon Sep 17 00:00:00 2001 From: bucolucas Date: Sun, 18 Aug 2024 18:49:15 -0500 Subject: [PATCH 5/5] Update github_tool.py --- tools/github_tool.py | 251 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 234 insertions(+), 17 deletions(-) diff --git a/tools/github_tool.py b/tools/github_tool.py index cb88d97..8ec83d7 100644 --- a/tools/github_tool.py +++ b/tools/github_tool.py @@ -356,66 +356,283 @@ class GitHubTool(BaseTool): self.logger.error(error_message) return error_message - @metrics.measure + @metrics.measure def _read_file(self, path): - # ... (rest of the method remains unchanged) + self.logger.info(f"Reading file: {path} from branch: {self.current_branch}") + url = f"{self.base_url}/repos/{self.repo}/contents/{path}" + response = requests.get(url, headers=self.headers, params={"ref": self.current_branch}) + if response.status_code == 200: + content = response.json()["content"] + decoded_content = base64.b64decode(content).decode('utf-8') + self.logger.info(f"Successfully read file: {path}") + return decoded_content + else: + error_message = f"Error reading file: {response.status_code}" + self.logger.error(error_message) + return error_message @metrics.measure def _create_branch(self, branch_name, base_branch): - # ... (rest of the method remains unchanged) + self.logger.info(f"Creating branch: {branch_name} from base: {base_branch}") + url = f"{self.base_url}/repos/{self.repo}/git/refs" + response = requests.get(f"{url}/heads/{base_branch}", headers=self.headers) + if response.status_code != 200: + error_message = f"Error getting base branch: {response.status_code}" + self.logger.error(error_message) + return error_message + + sha = response.json()["object"]["sha"] + data = { + "ref": f"refs/heads/{branch_name}", + "sha": sha + } + response = requests.post(url, headers=self.headers, json=data) + if response.status_code == 201: + self.current_branch = branch_name + success_message = f"Branch '{branch_name}' created successfully and set as current branch" + self.logger.info(success_message) + return success_message + else: + error_message = f"Error creating branch: {response.status_code}" + self.logger.error(error_message) + return error_message @metrics.measure def _commit_file(self, file_path, content, commit_message): - # ... (rest of the method remains unchanged) + self.logger.info(f"Committing file: {file_path} to branch: {self.current_branch}") + if self.current_branch == "main": + error_message = "Cannot commit directly to main branch" + self.logger.error(error_message) + return error_message + + url = f"{self.base_url}/repos/{self.repo}/contents/{file_path}" + + self.logger.info("Checking if file already exists") + response = requests.get(url, headers=self.headers, params={"ref": self.current_branch}) + + data = { + "message": commit_message, + "content": base64.b64encode(content.encode()).decode(), + "branch": self.current_branch + } + + if response.status_code == 200: + self.logger.info("File exists, updating") + file_sha = response.json()["sha"] + data["sha"] = file_sha + else: + self.logger.info("File does not exist, creating new file") + + response = requests.put(url, headers=self.headers, json=data) + + if response.status_code in [200, 201]: + success_message = f"File committed successfully to branch '{self.current_branch}'" + self.logger.info(success_message) + return success_message + else: + error_message = f"Error committing file: {response.status_code}\nResponse: {response.text}" + self.logger.error(error_message) + return error_message @metrics.measure def _create_pull_request(self, title, body, base): - # ... (rest of the method remains unchanged) + self.logger.info(f"Creating pull request: {title} from {self.current_branch} to {base}") + url = f"{self.base_url}/repos/{self.repo}/pulls" + data = { + "title": title, + "body": body, + "head": self.current_branch, + "base": base + } + response = requests.post(url, headers=self.headers, json=data) + if response.status_code == 201: + success_message = f"Pull request created successfully: {response.json()['html_url']}" + self.logger.info(success_message) + return success_message + else: + error_message = f"Error creating pull request: {response.status_code}\nResponse: {response.text}" + self.logger.error(error_message) + return error_message @metrics.measure def _get_branch_sha(self, branch): - # ... (rest of the method remains unchanged) + url = f"{self.base_url}/repos/{self.repo}/git/refs/heads/{branch}" + response = requests.get(url, headers=self.headers) + if response.status_code == 200: + return response.json()["object"]["sha"] + else: + return f"Error getting branch SHA: {response.status_code}" @metrics.measure def _list_files(self, path): - # ... (rest of the method remains unchanged) + self.logger.info(f"Listing files in: {path} on branch: {self.current_branch}") + url = f"{self.base_url}/repos/{self.repo}/contents/{path}" + response = requests.get(url, headers=self.headers, params={"ref": self.current_branch}) + if response.status_code == 200: + files = [item["name"] for item in response.json() if item["type"] == "file"] + directories = [item["name"] for item in response.json() if item["type"] == "dir"] + self.logger.info(f"Successfully listed files and directories in {path}") + return {"files": files, "directories": directories} + else: + error_message = f"Error listing files: {response.status_code}" + self.logger.error(error_message) + return error_message @metrics.measure def _search_code(self, query): - # ... (rest of the method remains unchanged) + self.logger.info(f"Searching code with query: {query}") + url = f"{self.base_url}/search/code" + params = { + "q": f"{query} repo:{self.repo}", + "per_page": 10 + } + response = requests.get(url, headers=self.headers, params=params) + if response.status_code == 200: + results = [{"file": item["path"], "url": item["html_url"]} for item in response.json()["items"]] + self.logger.info(f"Successfully searched code. Found {len(results)} results.") + return results + else: + error_message = f"Error searching code: {response.status_code}" + self.logger.error(error_message) + return error_message @metrics.measure def _get_commit_history(self, file_path, num_commits): - # ... (rest of the method remains unchanged) + self.logger.info(f"Getting commit history for file: {file_path}, number of commits: {num_commits}") + url = f"{self.base_url}/repos/{self.repo}/commits" + params = { + "path": file_path, + "per_page": num_commits + } + response = requests.get(url, headers=self.headers, params=params) + if response.status_code == 200: + commits = [{"sha": commit["sha"], "message": commit["commit"]["message"], "date": commit["commit"]["author"]["date"]} for commit in response.json()] + self.logger.info(f"Successfully retrieved commit history. Found {len(commits)} commits.") + return commits + else: + error_message = f"Error getting commit history: {response.status_code}" + self.logger.error(error_message) + return error_message @metrics.measure def _get_current_branch(self): - # ... (rest of the method remains unchanged) + self.logger.info(f"Getting current branch: {self.current_branch}") + return self.current_branch @metrics.measure def _set_current_branch(self, branch_name): - # ... (rest of the method remains unchanged) + self.logger.info(f"Setting current branch from {self.current_branch} to {branch_name}") + self.current_branch = branch_name + return f"Current branch set to: {self.current_branch}" @metrics.measure def _get_file_at_commit(self, file_path, commit_sha): - # ... (rest of the method remains unchanged) + self.logger.info(f"Getting file: {file_path} at commit: {commit_sha}") + url = f"{self.base_url}/repos/{self.repo}/contents/{file_path}" + response = requests.get(url, headers=self.headers, params={"ref": commit_sha}) + if response.status_code == 200: + content = response.json()["content"] + decoded_content = base64.b64decode(content).decode('utf-8') + self.logger.info(f"Successfully retrieved file at commit") + return decoded_content + else: + error_message = f"Error reading file at commit: {response.status_code}" + self.logger.error(error_message) + return error_message @metrics.measure def _list_branches(self, per_page=100, all_pages=True): - # ... (rest of the method remains unchanged) + self.logger.info(f"Listing branches. Per page: {per_page}, All pages: {all_pages}") + url = f"{self.base_url}/repos/{self.repo}/branches" + params = {"per_page": min(per_page, 100)} # GitHub API max is 100 per page + all_branches = [] + + while url: + self.logger.info(f"Fetching branches from: {url}") + response = requests.get(url, headers=self.headers, params=params) + if response.status_code != 200: + error_message = f"Error listing branches: {response.status_code}" + self.logger.error(error_message) + return error_message + + branches = [branch["name"] for branch in response.json()] + all_branches.extend(branches) + self.logger.info(f"Fetched {len(branches)} branches") + + if not all_pages: + break + + # Check if there's a next page + url = response.links.get('next', {}).get('url') + if url: + params = {} # Remove per_page for subsequent requests + + self.logger.info(f"Successfully listed all branches. Total: {len(all_branches)}") + return all_branches @metrics.measure def _approve_pull_request(self, pull_number): - # ... (rest of the method remains unchanged) + self.logger.info(f"Approving pull request: {pull_number}") + url = f"{self.base_url}/repos/{self.repo}/pulls/{pull_number}/reviews" + data = { + "event": "APPROVE" + } + response = requests.post(url, headers=self.headers, json=data) + if response.status_code == 200: + success_message = f"Pull request {pull_number} approved successfully" + self.logger.info(success_message) + return success_message + else: + error_message = f"Error approving pull request: {response.status_code}\nResponse: {response.text}" + self.logger.error(error_message) + return error_message @metrics.measure def _close_pull_request(self, pull_number): - # ... (rest of the method remains unchanged) + self.logger.info(f"Closing pull request: {pull_number}") + url = f"{self.base_url}/repos/{self.repo}/pulls/{pull_number}" + data = { + "state": "closed" + } + response = requests.patch(url, headers=self.headers, json=data) + if response.status_code == 200: + success_message = f"Pull request {pull_number} closed successfully" + self.logger.info(success_message) + return success_message + else: + error_message = f"Error closing pull request: {response.status_code}\nResponse: {response.text}" + self.logger.error(error_message) + return error_message @metrics.measure def _merge_pull_request(self, pull_number, commit_title, commit_message, merge_method): - # ... (rest of the method remains unchanged) + self.logger.info(f"Merging pull request: {pull_number}") + url = f"{self.base_url}/repos/{self.repo}/pulls/{pull_number}/merge" + data = { + "commit_title": commit_title, + "commit_message": commit_message, + "merge_method": merge_method + } + response = requests.put(url, headers=self.headers, json=data) + if response.status_code == 200: + success_message = f"Pull request {pull_number} merged successfully" + self.logger.info(success_message) + return success_message + else: + error_message = f"Error merging pull request: {response.status_code}\nResponse: {response.text}" + self.logger.error(error_message) + return error_message @metrics.measure def _delete_branch(self, branch_name): - # ... (rest of the method remains unchanged) + self.logger.info(f"Deleting branch: {branch_name}") + url = f"{self.base_url}/repos/{self.repo}/git/refs/heads/{branch_name}" + response = requests.delete(url, headers=self.headers) + if response.status_code == 204: + success_message = f"Branch {branch_name} deleted successfully" + self.logger.info(success_message) + return success_message + else: + error_message = f"Error deleting branch: {response.status_code}\nResponse: {response.text}" + self.logger.error(error_message) + return error_message