Compare commits

..

13 Commits

Author SHA1 Message Date
2c03a67312 Add labels 2023-07-24 16:00:51 +03:00
c9c95d60d4 Implement gitlab webhook 2023-07-24 15:05:24 +03:00
dd8f6eb923 Merge pull request #126 from Codium-ai/ok/preserve_labels
Add functionality to preserve existing labels in PRs
2023-07-24 10:22:51 +03:00
b9c25e487a On /describe, preserve the current labels 2023-07-24 10:17:26 +03:00
1f987380ed Merge pull request #124 from Xyand/bugfix/mising_model
Bugfix - missing function argument
2023-07-24 07:36:21 +03:00
cd8bbbf889 bugfix 2023-07-24 00:58:21 +03:00
8e5498ee97 Merge pull request #122 from Codium-ai/update-readme-gifs-2
Update README.md
2023-07-23 17:40:26 +03:00
0412d7aca0 Update README.md 2023-07-23 17:38:08 +03:00
1eac3245d9 Merge pull request #121 from Codium-ai/update-gifs
Update GIF URLs in README
2023-07-23 17:33:47 +03:00
cd51bef7f7 Merge pull request #119 from zmeir/zmeir-code_suggestions_single_api_call
Optimize Code Suggestions API Calls
2023-07-23 17:30:37 +03:00
e8aa33fa0b Update README.md 2023-07-23 17:27:26 +03:00
54b021b02c Merge pull request #120 from Codium-ai/ok/remove_gitlab_polling
Temporarily remove gitlab polling server until a rewrite is ready
2023-07-23 17:07:59 +03:00
32358678e6 Reduce the number of GitHub API calls when pushing code suggestions 2023-07-23 16:59:08 +03:00
8 changed files with 97 additions and 33 deletions

View File

@ -30,31 +30,31 @@ CodiumAI `PR-Agent` is an open-source tool aiming to help developers review pull
<h4>/describe:</h4> <h4>/describe:</h4>
<div align="center"> <div align="center">
<p float="center"> <p float="center">
<img src="https://www.codium.ai/wp-content/uploads/2023/07/describe.gif" width="800"> <img src="https://www.codium.ai/images/describe-2.gif" width="800">
</p> </p>
</div> </div>
<h4>/review:</h4> <h4>/review:</h4>
<div align="center"> <div align="center">
<p float="center"> <p float="center">
<img src="https://www.codium.ai/wp-content/uploads/2023/07/review.gif" width="800"> <img src="https://www.codium.ai/images/review-2.gif" width="800">
</p> </p>
</div> </div>
<h4>/reflect and review:</h4> <h4>/reflect_and_review:</h4>
<div align="center"> <div align="center">
<p float="center"> <p float="center">
<img src="https://www.codium.ai/wp-content/uploads/2023/07/reflect_and_review.gif" width="800"> <img src="https://www.codium.ai/images/reflect_and_review.gif" width="800">
</p> </p>
</div> </div>
<h4>/ask:</h4> <h4>/ask:</h4>
<div align="center"> <div align="center">
<p float="center"> <p float="center">
<img src="https://www.codium.ai/wp-content/uploads/2023/07/ask.gif" width="800"> <img src="https://www.codium.ai/images/ask-2.gif" width="800">
</p> </p>
</div> </div>
<h4>/improve:</h4> <h4>/improve:</h4>
<div align="center"> <div align="center">
<p float="center"> <p float="center">
<img src="https://www.codium.ai/wp-content/uploads/2023/07/improve-1.gif" width="800"> <img src="https://www.codium.ai/images/improve-2.gif" width="800">
</p> </p>
</div> </div>
<div align="left"> <div align="left">
@ -83,6 +83,7 @@ CodiumAI `PR-Agent` is an open-source tool aiming to help developers review pull
| | Reflect and Review | :white_check_mark: | | | | | Reflect and Review | :white_check_mark: | | |
| | | | | | | | | | | |
| USAGE | CLI | :white_check_mark: | :white_check_mark: | :white_check_mark: | | USAGE | CLI | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| | App / webhook | :white_check_mark: | :white_check_mark: | |
| | Tagging bot | :white_check_mark: | | | | | Tagging bot | :white_check_mark: | | |
| | Actions | :white_check_mark: | | | | | Actions | :white_check_mark: | | |
| | | | | | | | | | | |
@ -106,7 +107,7 @@ In the [configuration](./CONFIGURATION.md) file you can select your git provider
Try GPT-4 powered PR-Agent on your public GitHub repository for free. Just mention `@CodiumAI-Agent` and add the desired command in any PR comment! The agent will generate a response based on your command. Try GPT-4 powered PR-Agent on your public GitHub repository for free. Just mention `@CodiumAI-Agent` and add the desired command in any PR comment! The agent will generate a response based on your command.
![Review generation process](https://www.codium.ai/wp-content/uploads/2023/07/demo.gif) ![Review generation process](https://www.codium.ai/images/demo-2.gif)
To set up your own PR-Agent, see the [Installation](#installation) section To set up your own PR-Agent, see the [Installation](#installation) section

View File

@ -55,7 +55,7 @@ def get_pr_diff(git_provider: GitProvider, token_handler: TokenHandler, model: s
# if we are over the limit, start pruning # if we are over the limit, start pruning
patches_compressed, modified_file_names, deleted_file_names = \ patches_compressed, modified_file_names, deleted_file_names = \
pr_generate_compressed_diff(pr_languages, token_handler, add_line_numbers_to_hunks) pr_generate_compressed_diff(pr_languages, token_handler, model, add_line_numbers_to_hunks)
final_diff = "\n".join(patches_compressed) final_diff = "\n".join(patches_compressed)
if modified_file_names: if modified_file_names:

View File

@ -27,7 +27,7 @@ class BitbucketProvider:
self.set_pr(pr_url) self.set_pr(pr_url)
def is_supported(self, capability: str) -> bool: def is_supported(self, capability: str) -> bool:
if capability in ['get_issue_comments', 'create_inline_comment', 'publish_inline_comments']: if capability in ['get_issue_comments', 'create_inline_comment', 'publish_inline_comments', 'get_labels']:
return False return False
return True return True

View File

@ -60,6 +60,10 @@ class GitProvider(ABC):
def publish_labels(self, labels): def publish_labels(self, labels):
pass pass
@abstractmethod
def get_labels(self):
pass
@abstractmethod @abstractmethod
def remove_initial_comment(self): def remove_initial_comment(self):
pass pass

View File

@ -152,10 +152,8 @@ class GithubProvider(GitProvider):
def publish_code_suggestions(self, code_suggestions: list): def publish_code_suggestions(self, code_suggestions: list):
""" """
Publishes code suggestions as comments on the PR. Publishes code suggestions as comments on the PR.
In practice current APU enables to send only one code suggestion per comment. Might change in the future.
""" """
post_parameters_list = [] post_parameters_list = []
import github.PullRequestComment
for suggestion in code_suggestions: for suggestion in code_suggestions:
body = suggestion['body'] body = suggestion['body']
relevant_file = suggestion['relevant_file'] relevant_file = suggestion['relevant_file']
@ -178,7 +176,6 @@ class GithubProvider(GitProvider):
if relevant_lines_end > relevant_lines_start: if relevant_lines_end > relevant_lines_start:
post_parameters = { post_parameters = {
"body": body, "body": body,
"commit_id": self.last_commit_id._identity,
"path": relevant_file, "path": relevant_file,
"line": relevant_lines_end, "line": relevant_lines_end,
"start_line": relevant_lines_start, "start_line": relevant_lines_start,
@ -187,24 +184,19 @@ class GithubProvider(GitProvider):
else: # API is different for single line comments else: # API is different for single line comments
post_parameters = { post_parameters = {
"body": body, "body": body,
"commit_id": self.last_commit_id._identity,
"path": relevant_file, "path": relevant_file,
"line": relevant_lines_start, "line": relevant_lines_start,
"side": "RIGHT", "side": "RIGHT",
} }
post_parameters_list.append(post_parameters)
try: try:
headers, data = self.pr._requester.requestJsonAndCheck( self.pr.create_review(commit=self.last_commit_id, comments=post_parameters_list)
"POST", f"{self.pr.url}/comments", input=post_parameters return True
) except Exception as e:
github.PullRequestComment.PullRequestComment( if settings.config.verbosity_level >= 2:
self.pr._requester, headers, data, completed=True logging.error(f"Failed to publish code suggestion, error: {e}")
) return False
return True
except Exception as e:
if settings.config.verbosity_level >= 2:
logging.error(f"Failed to publish code suggestion, error: {e}")
return False
def remove_initial_comment(self): def remove_initial_comment(self):
try: try:
@ -330,5 +322,12 @@ class GithubProvider(GitProvider):
headers, data = self.pr._requester.requestJsonAndCheck( headers, data = self.pr._requester.requestJsonAndCheck(
"PUT", f"{self.pr.issue_url}/labels", input=post_parameters "PUT", f"{self.pr.issue_url}/labels", input=post_parameters
) )
except: except Exception as e:
logging.exception("Failed to publish labels") logging.exception(f"Failed to publish labels, error: {e}")
def get_labels(self):
try:
return [label.name for label in self.pr.labels]
except Exception as e:
logging.exception(f"Failed to get labels, error: {e}")
return []

View File

@ -8,11 +8,13 @@ from gitlab import GitlabGetError
from pr_agent.config_loader import settings from pr_agent.config_loader import settings
from .git_provider import EDIT_TYPE, FilePatchInfo, GitProvider
from ..algo.language_handler import is_valid_file from ..algo.language_handler import is_valid_file
from .git_provider import EDIT_TYPE, FilePatchInfo, GitProvider
class GitLabProvider(GitProvider): class GitLabProvider(GitProvider):
def __init__(self, merge_request_url: Optional[str] = None, incremental: Optional[bool] = False): def __init__(self, merge_request_url: Optional[str] = None, incremental: Optional[bool] = False):
gitlab_url = settings.get("GITLAB.URL", None) gitlab_url = settings.get("GITLAB.URL", None)
if not gitlab_url: if not gitlab_url:
@ -112,7 +114,7 @@ class GitLabProvider(GitProvider):
def create_inline_comment(self, body: str, relevant_file: str, relevant_line_in_file: str): def create_inline_comment(self, body: str, relevant_file: str, relevant_line_in_file: str):
raise NotImplementedError("Gitlab provider does not support creating inline comments yet") raise NotImplementedError("Gitlab provider does not support creating inline comments yet")
def create_inline_comment(self, comments: list[dict]): def create_inline_comments(self, comments: list[dict]):
raise NotImplementedError("Gitlab provider does not support publishing inline comments yet") raise NotImplementedError("Gitlab provider does not support publishing inline comments yet")
def send_inline_comment(self, body, edit_type, found, relevant_file, relevant_line_in_file, source_line_no, def send_inline_comment(self, body, edit_type, found, relevant_file, relevant_line_in_file, source_line_no,
@ -258,8 +260,15 @@ class GitLabProvider(GitProvider):
def get_user_id(self): def get_user_id(self):
return None return None
def publish_labels(self, labels): def publish_labels(self, pr_types):
pass try:
self.mr.labels = list(set(pr_types))
self.mr.save()
except Exception as e:
logging.exception(f"Failed to publish labels, error: {e}")
def publish_inline_comments(self, comments: list[dict]): def publish_inline_comments(self, comments: list[dict]):
pass pass
def get_labels(self):
return self.mr.labels

View File

@ -0,0 +1,47 @@
import logging
import uvicorn
from fastapi import APIRouter, FastAPI, Request, status
from fastapi.encoders import jsonable_encoder
from fastapi.responses import JSONResponse
from starlette.background import BackgroundTasks
from pr_agent.agent.pr_agent import PRAgent
from pr_agent.config_loader import settings
app = FastAPI()
router = APIRouter()
@router.post("/webhook")
async def gitlab_webhook(background_tasks: BackgroundTasks, request: Request):
data = await request.json()
if data.get('object_kind') == 'merge_request' and data['object_attributes'].get('action') in ['open', 'reopen']:
logging.info(f"A merge request has been opened: {data['object_attributes'].get('title')}")
url = data['object_attributes'].get('url')
background_tasks.add_task(PRAgent().handle_request, url, "/review")
elif data.get('object_kind') == 'note' and data['event_type'] == 'note':
if 'merge_request' in data:
mr = data['merge_request']
url = mr.get('url')
body = data.get('object_attributes', {}).get('note')
background_tasks.add_task(PRAgent().handle_request, url, body)
return JSONResponse(status_code=status.HTTP_200_OK, content=jsonable_encoder({"message": "success"}))
def start():
gitlab_url = settings.get("GITLAB.URL", None)
if not gitlab_url:
raise ValueError("GITLAB.URL is not set")
gitlab_token = settings.get("GITLAB.PERSONAL_ACCESS_TOKEN", None)
if not gitlab_token:
raise ValueError("GITLAB.PERSONAL_ACCESS_TOKEN is not set")
settings.config.git_provider = "gitlab"
app = FastAPI()
app.include_router(router)
uvicorn.run(app, host="0.0.0.0", port=3000)
if __name__ == '__main__':
start()

View File

@ -46,7 +46,11 @@ class PRDescription:
self.git_provider.publish_comment(markdown_text) self.git_provider.publish_comment(markdown_text)
else: else:
self.git_provider.publish_description(pr_title, pr_body) self.git_provider.publish_description(pr_title, pr_body)
self.git_provider.publish_labels(pr_types) if self.git_provider.is_supported("get_labels"):
current_labels = self.git_provider.get_labels()
if current_labels is None:
current_labels = []
self.git_provider.publish_labels(pr_types + current_labels)
self.git_provider.remove_initial_comment() self.git_provider.remove_initial_comment()
return "" return ""