diff --git a/README.md b/README.md
index ade40209..fb7a0b64 100644
--- a/README.md
+++ b/README.md
@@ -100,11 +100,11 @@ See the [usage guide](./Usage.md) for instructions how to run the different tool
|-------|---------------------------------------------|:------:|:------:|:---------:|:----------:|:----------:|:----------:|
| TOOLS | Review | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| | Ask | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
-| | Auto-Description | :white_check_mark: | :white_check_mark: | | :white_check_mark: | :white_check_mark: | :white_check_mark: |
-| | Improve Code | :white_check_mark: | :white_check_mark: | | :white_check_mark: | | :white_check_mark: |
-| | ⮑ Extended | :white_check_mark: | :white_check_mark: | | :white_check_mark: | | :white_check_mark: |
-| | Reflect and Review | :white_check_mark: | | | | :white_check_mark: | :white_check_mark: |
-| | Update CHANGELOG.md | :white_check_mark: | | | | | |
+| | Auto-Description | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
+| | Improve Code | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | | :white_check_mark: |
+| | ⮑ Extended | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | | :white_check_mark: |
+| | Reflect and Review | :white_check_mark: | | :white_check_mark: | | :white_check_mark: | :white_check_mark: |
+| | Update CHANGELOG.md | :white_check_mark: | | :white_check_mark: | | | |
| | | | | | | |
| USAGE | CLI | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| | App / webhook | :white_check_mark: | :white_check_mark: | | | |
diff --git a/pr_agent/algo/utils.py b/pr_agent/algo/utils.py
index 7ac4b468..ac865471 100644
--- a/pr_agent/algo/utils.py
+++ b/pr_agent/algo/utils.py
@@ -20,7 +20,7 @@ def get_setting(key: str) -> Any:
except Exception:
return global_settings.get(key, None)
-def convert_to_markdown(output_data: dict) -> str:
+def convert_to_markdown(output_data: dict, gfm_supported: bool) -> str:
"""
Convert a dictionary of data into markdown format.
Args:
@@ -49,11 +49,14 @@ def convert_to_markdown(output_data: dict) -> str:
continue
if isinstance(value, dict):
markdown_text += f"## {key}\n\n"
- markdown_text += convert_to_markdown(value)
+ markdown_text += convert_to_markdown(value, gfm_supported)
elif isinstance(value, list):
emoji = emojis.get(key, "")
if key.lower() == 'code feedback':
- markdown_text += f"\n\n- ** { emoji } Code feedback:**
\n\n"
+ if gfm_supported:
+ markdown_text += f"\n\n- ** { emoji } Code feedback:**
\n\n"
+ else:
+ markdown_text += f"\n\n- **{emoji} Code feedback:**\n\n"
else:
markdown_text += f"- {emoji} **{key}:**\n\n"
for item in value:
@@ -62,7 +65,10 @@ def convert_to_markdown(output_data: dict) -> str:
elif item:
markdown_text += f" - {item}\n"
if key.lower() == 'code feedback':
- markdown_text += " \n\n"
+ if gfm_supported:
+ markdown_text += " \n\n"
+ else:
+ markdown_text += "\n\n"
elif value != 'n/a':
emoji = emojis.get(key, "")
markdown_text += f"- {emoji} **{key}:** {value}\n"
diff --git a/pr_agent/git_providers/azuredevops_provider.py b/pr_agent/git_providers/azuredevops_provider.py
index 71ae0947..8a7693ce 100644
--- a/pr_agent/git_providers/azuredevops_provider.py
+++ b/pr_agent/git_providers/azuredevops_provider.py
@@ -38,7 +38,8 @@ class AzureDevopsProvider:
self.set_pr(pr_url)
def is_supported(self, capability: str) -> bool:
- if capability in ['get_issue_comments', 'create_inline_comment', 'publish_inline_comments', 'get_labels', 'remove_initial_comment']:
+ if capability in ['get_issue_comments', 'create_inline_comment', 'publish_inline_comments', 'get_labels',
+ 'remove_initial_comment', 'gfm_markdown']:
return False
return True
diff --git a/pr_agent/git_providers/bitbucket_provider.py b/pr_agent/git_providers/bitbucket_provider.py
index 0cd860fa..56b9f711 100644
--- a/pr_agent/git_providers/bitbucket_provider.py
+++ b/pr_agent/git_providers/bitbucket_provider.py
@@ -7,6 +7,7 @@ import requests
from atlassian.bitbucket import Cloud
from starlette_context import context
+from ..algo.pr_processing import clip_tokens, find_line_number_of_relevant_line_in_file
from ..config_loader import get_settings
from .git_provider import FilePatchInfo, GitProvider
@@ -35,9 +36,8 @@ class BitbucketProvider(GitProvider):
self.incremental = incremental
if pr_url:
self.set_pr(pr_url)
- self.bitbucket_comment_api_url = self.pr._BitbucketBase__data["links"][
- "comments"
- ]["href"]
+ self.bitbucket_comment_api_url = self.pr._BitbucketBase__data["links"]["comments"]["href"]
+ self.bitbucket_pull_request_api_url = self.pr._BitbucketBase__data["links"]['self']['href']
def get_repo_settings(self):
try:
@@ -101,12 +101,7 @@ class BitbucketProvider(GitProvider):
return False
def is_supported(self, capability: str) -> bool:
- if capability in [
- "get_issue_comments",
- "create_inline_comment",
- "publish_inline_comments",
- "get_labels",
- ]:
+ if capability in ['get_issue_comments', 'publish_inline_comments', 'get_labels', 'gfm_markdown']:
return False
return True
@@ -151,17 +146,30 @@ class BitbucketProvider(GitProvider):
except Exception as e:
logging.exception(f"Failed to remove temp comments, error: {e}")
- def publish_inline_comment(
- self, comment: str, from_line: int, to_line: int, file: str
- ):
- payload = json.dumps(
- {
- "content": {
- "raw": comment,
- },
- "inline": {"to": from_line, "path": file},
- }
- )
+
+ # funtion to create_inline_comment
+ def create_inline_comment(self, body: str, relevant_file: str, relevant_line_in_file: str):
+ position, absolute_position = find_line_number_of_relevant_line_in_file(self.get_diff_files(), relevant_file.strip('`'), relevant_line_in_file)
+ if position == -1:
+ if get_settings().config.verbosity_level >= 2:
+ logging.info(f"Could not find position for {relevant_file} {relevant_line_in_file}")
+ subject_type = "FILE"
+ else:
+ subject_type = "LINE"
+ path = relevant_file.strip()
+ return dict(body=body, path=path, position=absolute_position) if subject_type == "LINE" else {}
+
+
+ def publish_inline_comment(self, comment: str, from_line: int, file: str):
+ payload = json.dumps( {
+ "content": {
+ "raw": comment,
+ },
+ "inline": {
+ "to": from_line,
+ "path": file
+ },
+ })
response = requests.request(
"POST", self.bitbucket_comment_api_url, data=payload, headers=self.headers
)
@@ -169,9 +177,7 @@ class BitbucketProvider(GitProvider):
def publish_inline_comments(self, comments: list[dict]):
for comment in comments:
- self.publish_inline_comment(
- comment["body"], comment["start_line"], comment["line"], comment["path"]
- )
+ self.publish_inline_comment(comment['body'], comment['start_line'], comment['path'])
def get_title(self):
return self.pr.title
@@ -238,16 +244,22 @@ class BitbucketProvider(GitProvider):
def get_commit_messages(self):
return "" # not implemented yet
+
+ # bitbucket does not support labels
+ def publish_description(self, pr_title: str, description: str):
+ payload = json.dumps({
+ "description": description,
+ "title": pr_title
- def publish_description(self, pr_title: str, pr_body: str):
- pass
- def create_inline_comment(
- self, body: str, relevant_file: str, relevant_line_in_file: str
- ):
- pass
+ })
- def publish_labels(self, labels):
- pass
+ response = requests.request("PUT", self.bitbucket_pull_request_api_url, headers=self.headers, data=payload)
+ return response
+ # bitbucket does not support labels
+ def publish_labels(self, pr_types: list):
+ pass
+
+ # bitbucket does not support labels
def get_labels(self):
pass
diff --git a/pr_agent/git_providers/codecommit_client.py b/pr_agent/git_providers/codecommit_client.py
index 1112ee22..5f18c90d 100644
--- a/pr_agent/git_providers/codecommit_client.py
+++ b/pr_agent/git_providers/codecommit_client.py
@@ -54,11 +54,16 @@ class CodeCommitClient:
def __init__(self):
self.boto_client = None
+ def is_supported(self, capability: str) -> bool:
+ if capability in ["gfm_markdown"]:
+ return False
+ return True
+
def _connect_boto_client(self):
try:
self.boto_client = boto3.client("codecommit")
except Exception as e:
- raise ValueError(f"Failed to connect to AWS CodeCommit: {e}")
+ raise ValueError(f"Failed to connect to AWS CodeCommit: {e}") from e
def get_differences(self, repo_name: int, destination_commit: str, source_commit: str):
"""
diff --git a/pr_agent/git_providers/codecommit_provider.py b/pr_agent/git_providers/codecommit_provider.py
index 1f570a1a..5361f665 100644
--- a/pr_agent/git_providers/codecommit_provider.py
+++ b/pr_agent/git_providers/codecommit_provider.py
@@ -74,6 +74,7 @@ class CodeCommitProvider(GitProvider):
"create_inline_comment",
"publish_inline_comments",
"get_labels",
+ "gfm_markdown"
]:
return False
return True
diff --git a/pr_agent/git_providers/gerrit_provider.py b/pr_agent/git_providers/gerrit_provider.py
index 7f71ed6d..dd56803a 100644
--- a/pr_agent/git_providers/gerrit_provider.py
+++ b/pr_agent/git_providers/gerrit_provider.py
@@ -313,7 +313,8 @@ class GerritProvider(GitProvider):
# 'get_issue_comments',
'create_inline_comment',
'publish_inline_comments',
- 'get_labels'
+ 'get_labels',
+ 'gfm_markdown'
]:
return False
return True
diff --git a/pr_agent/git_providers/gitlab_provider.py b/pr_agent/git_providers/gitlab_provider.py
index 2deae177..a1d0b334 100644
--- a/pr_agent/git_providers/gitlab_provider.py
+++ b/pr_agent/git_providers/gitlab_provider.py
@@ -43,7 +43,7 @@ class GitLabProvider(GitProvider):
self.incremental = incremental
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', 'gfm_markdown']:
return False
return True
diff --git a/pr_agent/git_providers/local_git_provider.py b/pr_agent/git_providers/local_git_provider.py
index e6ee1456..ac750371 100644
--- a/pr_agent/git_providers/local_git_provider.py
+++ b/pr_agent/git_providers/local_git_provider.py
@@ -56,7 +56,8 @@ class LocalGitProvider(GitProvider):
raise KeyError(f'Branch: {self.target_branch_name} does not exist')
def is_supported(self, capability: str) -> bool:
- if capability in ['get_issue_comments', 'create_inline_comment', 'publish_inline_comments', 'get_labels']:
+ if capability in ['get_issue_comments', 'create_inline_comment', 'publish_inline_comments', 'get_labels',
+ 'gfm_markdown']:
return False
return True
diff --git a/pr_agent/settings/.secrets_template.toml b/pr_agent/settings/.secrets_template.toml
index f1971a3b..0271a2c3 100644
--- a/pr_agent/settings/.secrets_template.toml
+++ b/pr_agent/settings/.secrets_template.toml
@@ -55,8 +55,12 @@ webhook_secret = "" # Optional, may be commented out.
personal_access_token = ""
[bitbucket]
-# Bitbucket personal bearer token
+# For Bitbucket personal/repository bearer token
bearer_token = ""
+# For Bitbucket app
+app_key = ""
+base_url = ""
+
[litellm]
-LITELLM_TOKEN = "" # see https://docs.litellm.ai/docs/debugging/hosted_debugging for details and instructions on how to get a token
\ No newline at end of file
+LITELLM_TOKEN = "" # see https://docs.litellm.ai/docs/debugging/hosted_debugging for details and instructions on how to get a token
diff --git a/pr_agent/tools/pr_description.py b/pr_agent/tools/pr_description.py
index c45917f4..f30b0165 100644
--- a/pr_agent/tools/pr_description.py
+++ b/pr_agent/tools/pr_description.py
@@ -68,12 +68,12 @@ class PRDescription:
await retry_with_fallback_models(self._prepare_prediction)
logging.info('Preparing answer...')
- pr_title, pr_body, pr_types, markdown_text = self._prepare_pr_answer()
+ pr_title, pr_body, pr_types, markdown_text, description = self._prepare_pr_answer()
if get_settings().config.publish_output:
logging.info('Pushing answer...')
if get_settings().pr_description.publish_description_as_comment:
- self.git_provider.publish_comment(markdown_text)
+ self.git_provider.publish_comment(pr_body)
else:
self.git_provider.publish_description(pr_title, pr_body)
if self.git_provider.is_supported("get_labels"):
@@ -143,6 +143,7 @@ class PRDescription:
- pr_body: a string containing the PR body in a markdown format.
- pr_types: a list of strings containing the PR types.
- markdown_text: a string containing the AI prediction data in a markdown format. used for publishing a comment
+ - user_description: a string containing the user description
"""
# Load the AI prediction data into a dictionary
data = load_yaml(self.prediction.strip())
@@ -189,8 +190,9 @@ class PRDescription:
pr_body += "\n___\n"
markdown_text = f"## Title\n\n{title}\n\n___\n{pr_body}"
+ description = data['PR Description']
if get_settings().config.verbosity_level >= 2:
logging.info(f"title:\n{title}\n{pr_body}")
- return title, pr_body, pr_types, markdown_text
\ No newline at end of file
+ return title, pr_body, pr_types, markdown_text, description
\ No newline at end of file
diff --git a/pr_agent/tools/pr_reviewer.py b/pr_agent/tools/pr_reviewer.py
index a89c27a3..7f790d3b 100644
--- a/pr_agent/tools/pr_reviewer.py
+++ b/pr_agent/tools/pr_reviewer.py
@@ -214,7 +214,7 @@ class PRReviewer:
"⏮️ Review for commits since previous PR-Agent review": f"Starting from commit {last_commit_url}"}})
data.move_to_end('Incremental PR Review', last=False)
- markdown_text = convert_to_markdown(data)
+ markdown_text = convert_to_markdown(data, self.git_provider.is_supported("gfm_markdown"))
user = self.git_provider.get_user_id()
# Add help text if not in CLI mode
@@ -266,7 +266,7 @@ class PRReviewer:
self.git_provider.publish_inline_comment(content, relevant_file, relevant_line_in_file)
if comments:
- self.git_provider.publish_inline_comments(comments)
+ self.git_provider.publish_inline_comments(comments)
def _get_user_answers(self) -> Tuple[str, str]:
"""
diff --git a/pr_agent/tools/pr_update_changelog.py b/pr_agent/tools/pr_update_changelog.py
index 1ec62709..547ce84b 100644
--- a/pr_agent/tools/pr_update_changelog.py
+++ b/pr_agent/tools/pr_update_changelog.py
@@ -46,7 +46,7 @@ class PRUpdateChangelog:
get_settings().pr_update_changelog_prompt.user)
async def run(self):
- assert type(self.git_provider) == GithubProvider, "Currently only Github is supported"
+ # assert type(self.git_provider) == GithubProvider, "Currently only Github is supported"
logging.info('Updating the changelog...')
if get_settings().config.publish_output: