mirror of
https://github.com/qodo-ai/pr-agent.git
synced 2025-07-04 21:00:40 +08:00
@ -108,7 +108,7 @@ If you prefer to have the file summaries appear in the "Files changed" tab on ev
|
|||||||
|
|
||||||
{width=512}
|
{width=512}
|
||||||
|
|
||||||
- `true`: A collapsable file comment with changes title and a changes summary for each file in the PR.
|
- `true`: A collapsible file comment with changes title and a changes summary for each file in the PR.
|
||||||
|
|
||||||
{width=512}
|
{width=512}
|
||||||
|
|
||||||
@ -194,7 +194,7 @@ The description should be comprehensive and detailed, indicating when to add the
|
|||||||
```
|
```
|
||||||
pr_commands = ["/describe", ...]
|
pr_commands = ["/describe", ...]
|
||||||
```
|
```
|
||||||
meaning the `describe` tool will run automatically on every PR, with the default configurations.
|
meaning the `describe` tool will run automatically on every PR, with the default configurations.
|
||||||
|
|
||||||
|
|
||||||
- Markers are an alternative way to control the generated description, to give maximal control to the user. If you set:
|
- Markers are an alternative way to control the generated description, to give maximal control to the user. If you set:
|
||||||
|
@ -157,7 +157,7 @@ the tool can automatically approve the PR when the user checks the self-review c
|
|||||||
|
|
||||||
!!! tip "Extra instructions"
|
!!! tip "Extra instructions"
|
||||||
|
|
||||||
Extra instructions are very important for the `imrpove` tool, since they enable you to guide the model to suggestions that are more relevant to the specific needs of the project.
|
Extra instructions are very important for the `improve` tool, since they enable you to guide the model to suggestions that are more relevant to the specific needs of the project.
|
||||||
|
|
||||||
Be specific, clear, and concise in the instructions. With extra instructions, you are the prompter. Specify relevant aspects that you want the model to focus on.
|
Be specific, clear, and concise in the instructions. With extra instructions, you are the prompter. Specify relevant aspects that you want the model to focus on.
|
||||||
|
|
||||||
@ -190,6 +190,6 @@ the tool can automatically approve the PR when the user checks the self-review c
|
|||||||
- Only if the `Category` header is relevant, the user should move to the summarized suggestion description
|
- Only if the `Category` header is relevant, the user should move to the summarized suggestion description
|
||||||
- Only if the summarized suggestion description is relevant, the user should click on the collapsible, to read the full suggestion description with a code preview example.
|
- Only if the summarized suggestion description is relevant, the user should click on the collapsible, to read the full suggestion description with a code preview example.
|
||||||
|
|
||||||
In addition, we recommend to use the `exra_instructions` field to guide the model to suggestions that are more relevant to the specific needs of the project.
|
In addition, we recommend to use the `extra_instructions` field to guide the model to suggestions that are more relevant to the specific needs of the project.
|
||||||
<br>
|
<br>
|
||||||
Consider also trying the [Custom Prompt Tool](./custom_prompt.md) 💎, that will **only** propose code suggestions that follow specific guidelines defined by user.
|
Consider also trying the [Custom Prompt Tool](./custom_prompt.md) 💎, that will **only** propose code suggestions that follow specific guidelines defined by user.
|
||||||
|
@ -235,7 +235,7 @@ class BitbucketProvider(GitProvider):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
get_logger().exception(f"Failed to remove comment, error: {e}")
|
get_logger().exception(f"Failed to remove comment, error: {e}")
|
||||||
|
|
||||||
# funtion to create_inline_comment
|
# function to create_inline_comment
|
||||||
def create_inline_comment(self, body: str, relevant_file: str, relevant_line_in_file: str, absolute_position: int = None):
|
def create_inline_comment(self, body: str, relevant_file: str, relevant_line_in_file: str, absolute_position: int = None):
|
||||||
position, absolute_position = find_line_number_of_relevant_line_in_file(self.get_diff_files(),
|
position, absolute_position = find_line_number_of_relevant_line_in_file(self.get_diff_files(),
|
||||||
relevant_file.strip('`'),
|
relevant_file.strip('`'),
|
||||||
@ -404,7 +404,7 @@ class BitbucketProvider(GitProvider):
|
|||||||
|
|
||||||
def get_commit_messages(self):
|
def get_commit_messages(self):
|
||||||
return "" # not implemented yet
|
return "" # not implemented yet
|
||||||
|
|
||||||
# bitbucket does not support labels
|
# bitbucket does not support labels
|
||||||
def publish_description(self, pr_title: str, description: str):
|
def publish_description(self, pr_title: str, description: str):
|
||||||
payload = json.dumps({
|
payload = json.dumps({
|
||||||
@ -424,7 +424,7 @@ class BitbucketProvider(GitProvider):
|
|||||||
# bitbucket does not support labels
|
# bitbucket does not support labels
|
||||||
def publish_labels(self, pr_types: list):
|
def publish_labels(self, pr_types: list):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# bitbucket does not support labels
|
# bitbucket does not support labels
|
||||||
def get_pr_labels(self, update=False):
|
def get_pr_labels(self, update=False):
|
||||||
pass
|
pass
|
||||||
|
@ -211,7 +211,7 @@ class BitbucketServerProvider(GitProvider):
|
|||||||
def remove_comment(self, comment):
|
def remove_comment(self, comment):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# funtion to create_inline_comment
|
# function to create_inline_comment
|
||||||
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,
|
||||||
absolute_position: int = None):
|
absolute_position: int = None):
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ class GithubProvider(GitProvider):
|
|||||||
git_files = context.get("git_files", None)
|
git_files = context.get("git_files", None)
|
||||||
if git_files:
|
if git_files:
|
||||||
return git_files
|
return git_files
|
||||||
self.git_files = list(self.pr.get_files()) # 'list' to hanlde pagination
|
self.git_files = list(self.pr.get_files()) # 'list' to handle pagination
|
||||||
context["git_files"] = self.git_files
|
context["git_files"] = self.git_files
|
||||||
return self.git_files
|
return self.git_files
|
||||||
except Exception:
|
except Exception:
|
||||||
|
@ -41,7 +41,7 @@ def handle_request(
|
|||||||
):
|
):
|
||||||
log_context["action"] = body
|
log_context["action"] = body
|
||||||
log_context["api_url"] = url
|
log_context["api_url"] = url
|
||||||
|
|
||||||
async def inner():
|
async def inner():
|
||||||
try:
|
try:
|
||||||
with get_logger().contextualize(**log_context):
|
with get_logger().contextualize(**log_context):
|
||||||
@ -89,7 +89,7 @@ async def handle_webhook(background_tasks: BackgroundTasks, request: Request):
|
|||||||
get_logger().info(json.dumps(data))
|
get_logger().info(json.dumps(data))
|
||||||
|
|
||||||
actions = []
|
actions = []
|
||||||
if data["eventType"] == "git.pullrequest.created":
|
if data["eventType"] == "git.pullrequest.created":
|
||||||
# API V1 (latest)
|
# API V1 (latest)
|
||||||
pr_url = unquote(data["resource"]["_links"]["web"]["href"].replace("_apis/git/repositories", "_git"))
|
pr_url = unquote(data["resource"]["_links"]["web"]["href"].replace("_apis/git/repositories", "_git"))
|
||||||
log_context["event"] = data["eventType"]
|
log_context["event"] = data["eventType"]
|
||||||
@ -102,7 +102,7 @@ async def handle_webhook(background_tasks: BackgroundTasks, request: Request):
|
|||||||
repo = data["resource"]["pullRequest"]["repository"]["webUrl"]
|
repo = data["resource"]["pullRequest"]["repository"]["webUrl"]
|
||||||
pr_url = unquote(f'{repo}/pullrequest/{data["resource"]["pullRequest"]["pullRequestId"]}')
|
pr_url = unquote(f'{repo}/pullrequest/{data["resource"]["pullRequest"]["pullRequestId"]}')
|
||||||
actions = [data["resource"]["comment"]["content"]]
|
actions = [data["resource"]["comment"]["content"]]
|
||||||
else:
|
else:
|
||||||
# API V1 not supported as it does not contain the PR URL
|
# API V1 not supported as it does not contain the PR URL
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
status_code=status.HTTP_400_BAD_REQUEST,
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
@ -120,7 +120,7 @@ async def handle_webhook(background_tasks: BackgroundTasks, request: Request):
|
|||||||
|
|
||||||
log_context["event"] = data["eventType"]
|
log_context["event"] = data["eventType"]
|
||||||
log_context["api_url"] = pr_url
|
log_context["api_url"] = pr_url
|
||||||
|
|
||||||
for action in actions:
|
for action in actions:
|
||||||
try:
|
try:
|
||||||
handle_request(background_tasks, pr_url, action, log_context)
|
handle_request(background_tasks, pr_url, action, log_context)
|
||||||
@ -131,13 +131,13 @@ async def handle_webhook(background_tasks: BackgroundTasks, request: Request):
|
|||||||
content=json.dumps({"message": "Internal server error"}),
|
content=json.dumps({"message": "Internal server error"}),
|
||||||
)
|
)
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
status_code=status.HTTP_202_ACCEPTED, content=jsonable_encoder({"message": "webhook triggerd successfully"})
|
status_code=status.HTTP_202_ACCEPTED, content=jsonable_encoder({"message": "webhook triggered successfully"})
|
||||||
)
|
)
|
||||||
|
|
||||||
@router.get("/")
|
@router.get("/")
|
||||||
async def root():
|
async def root():
|
||||||
return {"status": "ok"}
|
return {"status": "ok"}
|
||||||
|
|
||||||
def start():
|
def start():
|
||||||
app = FastAPI(middleware=[Middleware(RawContextMiddleware)])
|
app = FastAPI(middleware=[Middleware(RawContextMiddleware)])
|
||||||
app.include_router(router)
|
app.include_router(router)
|
||||||
|
@ -40,8 +40,8 @@ After that, rank each response. Criterions to rank each response:
|
|||||||
- How well does the response follow the specific task instructions and requirements?
|
- How well does the response follow the specific task instructions and requirements?
|
||||||
- How well does the response analyze and understand the PR code diff?
|
- How well does the response analyze and understand the PR code diff?
|
||||||
- How well will a person perceive it as a good response that correctly addresses the task?
|
- How well will a person perceive it as a good response that correctly addresses the task?
|
||||||
- How well does the reponse prioritize key feedback, related to the task instructions, that a human reader seeing that feedback would also consider as important?
|
- How well does the response prioritize key feedback, related to the task instructions, that a human reader seeing that feedback would also consider as important?
|
||||||
- Don't neccessarily rank higher a response that is longer. A shorter response might be better if it is more concise, and still addresses the task better.
|
- Don't necessarily rank higher a response that is longer. A shorter response might be better if it is more concise, and still addresses the task better.
|
||||||
|
|
||||||
|
|
||||||
The output must be a YAML object equivalent to type $PRRankRespones, according to the following Pydantic definitions:
|
The output must be a YAML object equivalent to type $PRRankRespones, according to the following Pydantic definitions:
|
||||||
|
@ -73,7 +73,7 @@ class Review(BaseModel):
|
|||||||
security_concerns: str = Field(description="does this PR code introduce possible vulnerabilities such as exposure of sensitive information (e.g., API keys, secrets, passwords), or security concerns like SQL injection, XSS, CSRF, and others ? Answer 'No' if there are no possible issues. If there are security concerns or issues, start your answer with a short header, such as: 'Sensitive information exposure: ...', 'SQL injection: ...' etc. Explain your answer. Be specific and give examples if possible")
|
security_concerns: str = Field(description="does this PR code introduce possible vulnerabilities such as exposure of sensitive information (e.g., API keys, secrets, passwords), or security concerns like SQL injection, XSS, CSRF, and others ? Answer 'No' if there are no possible issues. If there are security concerns or issues, start your answer with a short header, such as: 'Sensitive information exposure: ...', 'SQL injection: ...' etc. Explain your answer. Be specific and give examples if possible")
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
{%- if require_can_be_split_review %}
|
{%- if require_can_be_split_review %}
|
||||||
can_be_split: List[SubPR] = Field(min_items=0, max_items=3, description="Can this PR, which contains {{ num_pr_files }} changed files in total, be divided into smaller sub-PRs with distinct tasks that can be reviewed and merged independently, regardless of the order ? Make sure that the sub-PRs are indeed independent, with no code dependencies between them, and that each sub-PR represent a meaningfull independent task. Output an empty list if the PR code does not needd to be split.")
|
can_be_split: List[SubPR] = Field(min_items=0, max_items=3, description="Can this PR, which contains {{ num_pr_files }} changed files in total, be divided into smaller sub-PRs with distinct tasks that can be reviewed and merged independently, regardless of the order ? Make sure that the sub-PRs are indeed independent, with no code dependencies between them, and that each sub-PR represent a meaningful independent task. Output an empty list if the PR code does not need to be split.")
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
{%- if num_code_suggestions > 0 %}
|
{%- if num_code_suggestions > 0 %}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user