From bc575e5a67d59aede12ee4cb1c6659d9d37b8955 Mon Sep 17 00:00:00 2001 From: ishaansehgal99 Date: Thu, 20 Mar 2025 13:55:07 -0700 Subject: [PATCH 001/365] fix: Logging and Commenting --- pr_agent/servers/github_app.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pr_agent/servers/github_app.py b/pr_agent/servers/github_app.py index 9c9708fb31..d460c2b218 100644 --- a/pr_agent/servers/github_app.py +++ b/pr_agent/servers/github_app.py @@ -138,7 +138,7 @@ async def handle_new_pr_opened(body: Dict[str, Any], # logic to ignore PRs with specific titles (e.g. "[Auto] ...") apply_repo_settings(api_url) if get_identity_provider().verify_eligibility("github", sender_id, api_url) is not Eligibility.NOT_ELIGIBLE: - await _perform_auto_commands_github("pr_commands", agent, body, api_url, log_context) + await _perform_auto_commands_github("pr_commands", agent, body, api_url, log_context) else: get_logger().info(f"User {sender=} is not eligible to process PR {api_url=}") @@ -196,8 +196,8 @@ async def handle_push_trigger_for_new_commits(body: Dict[str, Any], try: if get_identity_provider().verify_eligibility("github", sender_id, api_url) is not Eligibility.NOT_ELIGIBLE: - get_logger().info(f"Performing incremental review for {api_url=} because of {event=} and {action=}") - await _perform_auto_commands_github("push_commands", agent, body, api_url, log_context) + get_logger().info(f"Performing incremental review for {api_url=} because of {event=} and {action=}") + await _perform_auto_commands_github("push_commands", agent, body, api_url, log_context) finally: # release the waiting task block @@ -233,7 +233,7 @@ def get_log_context(body, event, action, build_number): "request_id": uuid.uuid4().hex, "build_number": build_number, "app_name": app_name, "repo": repo, "git_org": git_org, "installation_id": installation_id} except Exception as e: - get_logger().error("Failed to get log context", e) + get_logger().error(f"Error parsing request body: {e}") log_context = {} return log_context, sender, sender_id, sender_type @@ -310,16 +310,20 @@ async def handle_request(body: Dict[str, Any], event: str): event: The GitHub event type (e.g. "pull_request", "issue_comment", etc.). """ action = body.get("action") # "created", "opened", "reopened", "ready_for_review", "review_requested", "synchronize" + get_logger().debug(f"Handling request with event: {event}, action: {action}") if not action: + get_logger().debug(f"No action found in request body, exiting handle_request") return {} agent = PRAgent() log_context, sender, sender_id, sender_type = get_log_context(body, event, action, build_number) # logic to ignore PRs opened by bot, PRs with specific titles, labels, source branches, or target branches if is_bot_user(sender, sender_type) and 'check_run' not in body: + get_logger().debug(f"Request ignored: bot user detected") return {} if action != 'created' and 'check_run' not in body: if not should_process_pr_logic(body): + get_logger().debug(f"Request ignored: PR logic filtering") return {} if 'check_run' in body: # handle failed checks From 52d4312c9a3050817f8e87cb6a2aba4a04a366d8 Mon Sep 17 00:00:00 2001 From: ishaansehgal99 Date: Thu, 20 Mar 2025 13:59:43 -0700 Subject: [PATCH 002/365] fix: Logging and Commenting --- pr_agent/servers/github_app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/servers/github_app.py b/pr_agent/servers/github_app.py index d460c2b218..09a521323e 100644 --- a/pr_agent/servers/github_app.py +++ b/pr_agent/servers/github_app.py @@ -233,7 +233,7 @@ def get_log_context(body, event, action, build_number): "request_id": uuid.uuid4().hex, "build_number": build_number, "app_name": app_name, "repo": repo, "git_org": git_org, "installation_id": installation_id} except Exception as e: - get_logger().error(f"Error parsing request body: {e}") + get_logger().error(f"Failed to get log context: {e}") log_context = {} return log_context, sender, sender_id, sender_type From 5e7e35367069a39423958eb52c1122af4f181adf Mon Sep 17 00:00:00 2001 From: Eyal Sharon Date: Thu, 20 Mar 2025 16:32:16 +0200 Subject: [PATCH 003/365] Added help_docs feature. --- pr_agent/agent/pr_agent.py | 2 + pr_agent/cli.py | 3 + pr_agent/config_loader.py | 1 + pr_agent/git_providers/bitbucket_provider.py | 27 ++ .../bitbucket_server_provider.py | 25 ++ pr_agent/git_providers/git_provider.py | 15 +- pr_agent/git_providers/github_provider.py | 47 +++ pr_agent/git_providers/gitlab_provider.py | 32 ++ pr_agent/settings/configuration.toml | 6 + pr_agent/settings/pr_help_docs_prompts.toml | 77 ++++ pr_agent/tools/pr_help_docs.py | 365 ++++++++++++++++++ pr_agent/tools/pr_help_message.py | 1 - 12 files changed, 599 insertions(+), 2 deletions(-) create mode 100644 pr_agent/settings/pr_help_docs_prompts.toml create mode 100644 pr_agent/tools/pr_help_docs.py diff --git a/pr_agent/agent/pr_agent.py b/pr_agent/agent/pr_agent.py index 314c993ef0..54e249b732 100644 --- a/pr_agent/agent/pr_agent.py +++ b/pr_agent/agent/pr_agent.py @@ -13,6 +13,7 @@ from pr_agent.tools.pr_config import PRConfig from pr_agent.tools.pr_description import PRDescription from pr_agent.tools.pr_generate_labels import PRGenerateLabels +from pr_agent.tools.pr_help_docs import PRHelpDocs from pr_agent.tools.pr_help_message import PRHelpMessage from pr_agent.tools.pr_line_questions import PR_LineQuestions from pr_agent.tools.pr_questions import PRQuestions @@ -39,6 +40,7 @@ "similar_issue": PRSimilarIssue, "add_docs": PRAddDocs, "generate_labels": PRGenerateLabels, + "help_docs": PRHelpDocs, } commands = list(command2class.keys()) diff --git a/pr_agent/cli.py b/pr_agent/cli.py index 32be260ef9..ef1614378a 100644 --- a/pr_agent/cli.py +++ b/pr_agent/cli.py @@ -22,6 +22,7 @@ def set_parser(): - cli.py --pr_url=... ask "write me a poem about this PR" - cli.py --pr_url=... reflect - cli.py --issue_url=... similar_issue + - cli.py --pr_url/--issue_url= help_docs [] Supported commands: - review / review_pr - Add a review that includes a summary of the PR and specific suggestions for improvement. @@ -40,6 +41,8 @@ def set_parser(): - add_docs - generate_labels + + - help_docs - Ask a question, from either an issue or PR context, on a given repo (current context or a different one) Configuration: diff --git a/pr_agent/config_loader.py b/pr_agent/config_loader.py index 5dba0f39a7..575c02a336 100644 --- a/pr_agent/config_loader.py +++ b/pr_agent/config_loader.py @@ -28,6 +28,7 @@ "settings/pr_add_docs.toml", "settings/custom_labels.toml", "settings/pr_help_prompts.toml", + "settings/pr_help_docs_prompts.toml", "settings/.secrets.toml", "settings_prod/.secrets.toml", ]] diff --git a/pr_agent/git_providers/bitbucket_provider.py b/pr_agent/git_providers/bitbucket_provider.py index 7243ca5fc6..9967c31003 100644 --- a/pr_agent/git_providers/bitbucket_provider.py +++ b/pr_agent/git_providers/bitbucket_provider.py @@ -67,6 +67,33 @@ def get_repo_settings(self): except Exception: return "" + def get_git_repo_url(self, pr_url: str=None) -> str: #bitbucket does not support issue url, so ignore param + try: + parsed_url = urlparse(self.pr_url) + return f"{parsed_url.scheme}://{parsed_url.netloc}/{self.workspace_slug}/{self.repo_slug}.git" + except Exception as e: + get_logger().exception(f"url is not a valid merge requests url: {self.pr_url}") + return "" + + def get_canonical_url_parts(self, repo_git_url:str=None, desired_branch:str=None) -> Tuple[str, str]: + scheme_and_netloc = None + if repo_git_url: + parsed_git_url = urlparse(repo_git_url) + scheme_and_netloc = parsed_git_url.scheme + "://" + parsed_git_url.netloc + repo_path = parsed_git_url.path.split('.git')[0][1:] #//.git -> / + if repo_path.count('/') != 1: + get_logger().error(f"repo_git_url is not a valid git repo url: {repo_git_url}") + return ("", "") + workspace_name, project_name = repo_path.split('/') + else: + parsed_pr_url = urlparse(self.pr_url) + scheme_and_netloc = parsed_pr_url.scheme + "://" + parsed_pr_url.netloc + workspace_name, project_name = (self.workspace_slug, self.repo_slug) + prefix = f"{scheme_and_netloc}/{workspace_name}/{project_name}/src/{desired_branch}" + suffix = "" #None + return (prefix, suffix) + + def publish_code_suggestions(self, code_suggestions: list) -> bool: """ Publishes code suggestions as comments on the PR. diff --git a/pr_agent/git_providers/bitbucket_server_provider.py b/pr_agent/git_providers/bitbucket_server_provider.py index 22f85e50c8..fef1bc0ee8 100644 --- a/pr_agent/git_providers/bitbucket_server_provider.py +++ b/pr_agent/git_providers/bitbucket_server_provider.py @@ -138,6 +138,31 @@ def is_supported(self, capability: str) -> bool: return False return True + def get_git_repo_url(self, pr_url: str=None) -> str: #bitbucket server does not support issue url, so ignore param + try: + parsed_url = urlparse(self.pr_url) + return f"{parsed_url.scheme}://{parsed_url.netloc}/scm/{self.workspace_slug.lower()}/{self.repo_slug.lower()}.git" + except Exception as e: + get_logger().exception(f"url is not a valid merge requests url: {self.pr_url}") + return "" + + def get_canonical_url_parts(self, repo_git_url:str=None, desired_branch:str=None) -> Tuple[str, str]: + workspace_name = None + project_name = None + if not repo_git_url: + workspace_name = self.workspace_slug + project_name = self.repo_slug + else: + repo_path = repo_git_url.split('.git')[0].split('scm/')[-1] + if repo_path.count('/') == 1: # Has to have the form / + workspace_name, project_name = repo_path.split('/') + if not workspace_name or not project_name: + get_logger().error(f"workspace_name or project_name not found in context, either git url: {repo_git_url} or uninitialized workspace/project.") + return ("", "") + prefix = f"{self.bitbucket_server_url}/projects/{workspace_name}/repos/{project_name}/browse" + suffix = f"?at=refs%2Fheads%2F{desired_branch}" + return (prefix, suffix) + def set_pr(self, pr_url: str): self.workspace_slug, self.repo_slug, self.pr_num = self._parse_pr_url(pr_url) self.pr = self._get_pr() diff --git a/pr_agent/git_providers/git_provider.py b/pr_agent/git_providers/git_provider.py index bc081aa493..e5a2927fd6 100644 --- a/pr_agent/git_providers/git_provider.py +++ b/pr_agent/git_providers/git_provider.py @@ -1,6 +1,6 @@ from abc import ABC, abstractmethod # enum EDIT_TYPE (ADDED, DELETED, MODIFIED, RENAMED) -from typing import Optional +from typing import Optional, Tuple from pr_agent.algo.types import FilePatchInfo from pr_agent.algo.utils import Range, process_description @@ -14,6 +14,19 @@ class GitProvider(ABC): def is_supported(self, capability: str) -> bool: pass + #Given a url (issues or PR/MR) - get the .git repo url to which they belong. Needs to be implemented by the provider. + def get_git_repo_url(self, issues_or_pr_url: str) -> str: + get_logger().warning("Not implemented! Returning empty url") + return "" + + # Given a git repo url, return prefix and suffix of the provider in order to view a given file belonging to that repo. Needs to be implemented by the provider. + # For example: For a git: https://git_provider.com/MY_PROJECT/MY_REPO.git then it should return ('https://git_provider.com/projects/MY_PROJECT/repos/MY_REPO', '?=') + # so that to properly view the file: docs/readme.md -> /docs/readme.md -> https://git_provider.com/projects/MY_PROJECT/repos/MY_REPO/docs/readme.md?=) + def get_canonical_url_parts(self, repo_git_url:str, desired_branch:str) -> Tuple[str, str]: + get_logger().warning("Not implemented! Returning empty prefix and suffix") + return ("", "") + + @abstractmethod def get_files(self) -> list: pass diff --git a/pr_agent/git_providers/github_provider.py b/pr_agent/git_providers/github_provider.py index 58b72f1736..e020cceb4a 100644 --- a/pr_agent/git_providers/github_provider.py +++ b/pr_agent/git_providers/github_provider.py @@ -63,6 +63,53 @@ def get_incremental_commits(self, incremental=IncrementalPR(False)): def is_supported(self, capability: str) -> bool: return True + def _get_owner_and_repo_path(self, given_url: str) -> str: + try: + repo_path = None + if 'issues' in given_url: + repo_path, _ = self._parse_issue_url(given_url) + elif 'pull' in given_url: + repo_path, _ = self._parse_pr_url(given_url) + elif given_url.endswith('.git'): + parsed_url = urlparse(given_url) + repo_path = (parsed_url.path.split('.git')[0])[1:] # //.git -> / + if not repo_path: + get_logger().error(f"url is neither an issues url nor a pr url nor a valid git url: {given_url}. Returning empty result.") + return "" + return repo_path + except Exception as e: + get_logger().exception(f"unable to parse url: {given_url}. Returning empty result.") + return "" + + def get_git_repo_url(self, issues_or_pr_url: str) -> str: + repo_path = self._get_owner_and_repo_path(issues_or_pr_url) + return f"{issues_or_pr_url.split(repo_path)[0]}{repo_path}.git" + + def get_canonical_url_parts(self, repo_git_url:str, desired_branch:str) -> Tuple[str, str]: + owner = None + repo = None + scheme_and_netloc = None + + if repo_git_url: #If user provided an external git url, which may be different than what this provider was initialized with, we cannot use self.repo + repo_path = self._get_owner_and_repo_path(repo_git_url) + parsed_git_url = urlparse(repo_git_url) + scheme_and_netloc = parsed_git_url.scheme + "://" + parsed_git_url.netloc + if repo_path.count('/') == 1: #Has to have the form / + owner, repo = repo_path.split('/') + else: + get_logger().error(f"Invalid repo_path: {repo_path} from repo_git_url: {repo_git_url}") + return ("", "") + if (not owner or not repo) and self.repo: #"else" - User did not provide an external git url, use self.repo object: + owner, repo = self.repo.split('/') + scheme_and_netloc = self.base_url_html + if not any([scheme_and_netloc, owner, repo]): #"else": Not invoked from a PR context,but no provided git url for context + get_logger().error(f"Unable to get canonical url parts since missing context (PR or explicit git url)") + return ("", "") + + prefix = f"{scheme_and_netloc}/{owner}/{repo}/blob/{desired_branch}" + suffix = "" # github does not add a suffix + return (prefix, suffix) + def get_pr_url(self) -> str: return self.pr.html_url diff --git a/pr_agent/git_providers/gitlab_provider.py b/pr_agent/git_providers/gitlab_provider.py index 179459ce86..1040f3b5e2 100644 --- a/pr_agent/git_providers/gitlab_provider.py +++ b/pr_agent/git_providers/gitlab_provider.py @@ -57,6 +57,38 @@ def is_supported(self, capability: str) -> bool: return False return True + def _get_project_path_from_pr_or_issue_url(self, pr_or_issue_url: str) -> str: + repo_project_path = None + if 'issues' in pr_or_issue_url: + #replace 'issues' with 'merge_requests', since gitlab provider does not support issue urls, just to get the git repo url: + pr_or_issue_url = pr_or_issue_url.replace('issues', 'merge_requests') + if 'merge_requests' in pr_or_issue_url: + repo_project_path, _ = self._parse_merge_request_url(pr_or_issue_url) + if not repo_project_path: + get_logger().error(f"url is not a valid merge requests url: {pr_or_issue_url}") + return "" + return repo_project_path + + def get_git_repo_url(self, issues_or_pr_url: str) -> str: + provider_url = issues_or_pr_url + repo_path = self._get_project_path_from_pr_or_issue_url(issues_or_pr_url) + if not repo_path: + return "" + return f"{provider_url.split(repo_path)[0]}{repo_path}.git" + + def get_canonical_url_parts(self, repo_git_url:str=None, desired_branch:str=None) -> Tuple[str, str]: + repo_path = "" + if not repo_git_url and not self.pr_url: + get_logger().error("Cannot get canonical URL parts: missing either context PR URL or a repo GIT URL") + return ("", "") + if not repo_git_url: #Use PR url as context + repo_path = self._get_project_path_from_pr_or_issue_url(self.pr_url) + else: #Use repo git url + repo_path = repo_git_url.split('.git')[0].split('.com/')[-1] + prefix = f"{self.gitlab_url}/{repo_path}/-/blob/{desired_branch}" + suffix = "?ref_type=heads" # gitlab cloud adds this suffix. gitlab server does not, but it is harmless. + return (prefix, suffix) + @property def pr(self): '''The GitLab terminology is merge request (MR) instead of pull request (PR)''' diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index d2640c1879..e9ee6e6d94 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -212,6 +212,12 @@ num_retrieved_snippets=5 [pr_config] # /config # +[pr_help_docs] +repo_url = "" #If not overwritten, will use the repo from where the context came from (issue or PR) +docs_path = "docs" +exclude_root_readme = false +supported_doc_exts = [".md", ".mdx", ".rst"] + [github] # The type of deployment to create. Valid values are 'app' or 'user'. deployment_type = "user" diff --git a/pr_agent/settings/pr_help_docs_prompts.toml b/pr_agent/settings/pr_help_docs_prompts.toml new file mode 100644 index 0000000000..df68a4b681 --- /dev/null +++ b/pr_agent/settings/pr_help_docs_prompts.toml @@ -0,0 +1,77 @@ +[pr_help_docs_prompts] +system="""You are Doc-helper, a language model designed to answer questions about a documentation website for a given repository. +You will receive a question, a repository url and the full documentation content for that repository (either as markdown or as restructred text). +Your goal is to provide the best answer to the question using the documentation provided. + +Additional instructions: +- Be short and concise in your answers. Give examples if needed. +- Answer only questions that are related to the documentation website content. If the question is completely unrelated to the documentation, return an empty response. + + +The output must be a YAML object equivalent to type $DocHelper, according to the following Pydantic definitions: +===== +class relevant_section(BaseModel): + file_name: str = Field(description="The name of the relevant file") + relevant_section_header_string: str = Field(description="The exact text of the relevant markdown/restructured text section heading from the relevant file (starting with '#', '##', etc.). Return empty string if the entire file is the relevant section, or if the relevant section has no heading") + +class DocHelper(BaseModel): + user_question: str = Field(description="The user's question") + response: str = Field(description="The response to the user's question") + relevant_sections: List[relevant_section] = Field(description="A list of the relevant markdown/restructured text sections in the documentation that answer the user's question, ordered by importance (most relevant first)") + question_is_relevant: int = Field(description="Return 1 if the question is somewhat relevant to documenation. 0 - otherwise") +===== + + +Example output: +```yaml +user_question: | + ... +response: | + ... +relevant_sections: +- file_name: "src/file1.py" + relevant_section_header_string: | + ... +- ... +question_is_relevant: | + 1 +""" + +user="""\ +Documentation url: '{{ docs_url| trim }}' +----- + + +User's Question: +===== +{{ question|trim }} +===== + + +Documentation website content: +===== +{{ snippets|trim }} +===== + + +Reminder: The output must be a YAML object equivalent to type $DocHelper, similar to the following example output: +===== +Example output: +```yaml +user_question: | + ... +response: | + ... +relevant_sections: +- file_name: "src/file1.py" + relevant_section_header_string: | + ... +- ... +question_is_relevant: | + 1 +===== + + +Response (should be a valid YAML, and nothing else). +```yaml +""" diff --git a/pr_agent/tools/pr_help_docs.py b/pr_agent/tools/pr_help_docs.py new file mode 100644 index 0000000000..3958ac5254 --- /dev/null +++ b/pr_agent/tools/pr_help_docs.py @@ -0,0 +1,365 @@ +import copy +from functools import partial +from jinja2 import Environment, StrictUndefined +import math +import os +import re +from tempfile import TemporaryDirectory +from typing import Dict, List, Optional, Tuple + +from pr_agent.algo import MAX_TOKENS +from pr_agent.algo.ai_handlers.base_ai_handler import BaseAiHandler +from pr_agent.algo.ai_handlers.litellm_ai_handler import LiteLLMAIHandler +from pr_agent.algo.pr_processing import retry_with_fallback_models +from pr_agent.algo.token_handler import TokenHandler +from pr_agent.algo.utils import clip_tokens, get_max_tokens, load_yaml, ModelType +from pr_agent.config_loader import get_settings +from pr_agent.git_providers import get_git_provider_with_context +from pr_agent.log import get_logger + + +#Common code that can be called from similar tools: +def modify_answer_section(ai_response: str) -> str | None: + # Gets the model's answer and relevant sources section, repacing the heading of the answer section with: + # :bulb: Auto-generated documentation-based answer: + """ + For example: The following input: + + ### Question: \nThe following general issue was asked by a user: Title: How does one request to re-review a PR? More Info: I cannot seem to find to do this. + ### Answer:\nAccording to the documentation, one needs to invoke the command: /review + #### Relevant Sources... + + Should become: + + ### :bulb: Auto-generated documentation-based answer:\n + According to the documentation, one needs to invoke the command: /review + #### Relevant Sources... + """ + model_answer_and_relevant_sections_in_response \ + = _extract_model_answer_and_relevant_sources(ai_response) + if model_answer_and_relevant_sections_in_response is not None: + cleaned_question_with_answer = "### :bulb: Auto-generated documentation-based answer:\n" + cleaned_question_with_answer += model_answer_and_relevant_sections_in_response + return cleaned_question_with_answer + get_logger().warning(f"Either no answer section found, or that section is malformed: {ai_response}") + return None + +def _extract_model_answer_and_relevant_sources(ai_response: str) -> str | None: + # It is assumed that the input contains several sections with leading "### ", + # where the answer is the last one of them having the format: "### Answer:\n"), since the model returns the answer + # AFTER the user question. By splitting using the string: "### Answer:\n" and grabbing the last part, + # the model answer is guaranteed to be in that last part, provided it is followed by a "#### Relevant Sources:\n\n". + # (for more details, see here: https://github.com/Codium-ai/pr-agent-pro/blob/main/pr_agent/tools/pr_help_message.py#L173) + """ + For example: + ### Question: \nHow does one request to re-review a PR?\n\n + ### Answer:\nAccording to the documentation, one needs to invoke the command: /review\n\n + #### Relevant Sources:\n\n... + + The answer part is: "According to the documentation, one needs to invoke the command: /review\n\n" + followed by "Relevant Sources:\n\n". + """ + if "### Answer:\n" in ai_response: + model_answer_and_relevant_sources_sections_in_response = ai_response.split("### Answer:\n")[-1] + # Split such part by "Relevant Sources" section to contain only the model answer: + if "#### Relevant Sources:\n\n" in model_answer_and_relevant_sources_sections_in_response: + model_answer_section_in_response \ + = model_answer_and_relevant_sources_sections_in_response.split("#### Relevant Sources:\n\n")[0] + get_logger().info(f"Found model answer: {model_answer_section_in_response}") + return model_answer_and_relevant_sources_sections_in_response \ + if len(model_answer_section_in_response) > 0 else None + get_logger().warning(f"Either no answer section found, or that section is malformed: {ai_response}") + return None + + +def get_maximal_text_input_length_for_token_count_estimation(): + model = get_settings().config.model + if 'claude-3-7-sonnet' in model.lower(): + return 900000 #Claude API for token estimation allows maximal text input of 900K chars + return math.inf #Otherwise, no known limitation on input text just for token estimation + +# Load documentation files to memory, decorating them with a header to mark where each file begins, +# as to help the LLM to give a better answer. +def aggregate_documentation_files_for_prompt_contents(base_path: str, doc_files: List[str]) -> Optional[str]: + docs_prompt = "" + for file in doc_files: + try: + with open(file, 'r', encoding='utf-8') as f: + content = f.read() + # Skip files with no text content + if not re.search(r'[a-zA-Z]', content): + continue + file_path = str(file).replace(str(base_path), '') + docs_prompt += f"\n==file name==\n\n{file_path}\n\n==file content==\n\n{content.strip()}\n=========\n\n" + except Exception as e: + get_logger().warning(f"Error while reading the file {file}: {e}") + continue + if not docs_prompt: + get_logger().error("Couldn't find any usable documentation files. Returning None.") + return None + return docs_prompt + +def format_markdown_q_and_a_response(question_str: str, response_str: str, relevant_sections: List[Dict[str, str]], + supported_suffixes: List[str], base_url_prefix: str, base_url_suffix: str="") -> str: + answer_str = "" + answer_str += f"### Question: \n{question_str}\n\n" + answer_str += f"### Answer:\n{response_str.strip()}\n\n" + answer_str += f"#### Relevant Sources:\n\n" + for section in relevant_sections: + file = section.get('file_name').strip() + ext = [suffix for suffix in supported_suffixes if file.endswith(suffix)] + if not ext: + get_logger().warning(f"Unsupported file extension: {file}") + continue + if str(section['relevant_section_header_string']).strip(): + markdown_header = format_markdown_header(section['relevant_section_header_string']) + if base_url_prefix: + answer_str += f"> - {base_url_prefix}{file}{base_url_suffix}#{markdown_header}\n" + else: + answer_str += f"> - {base_url_prefix}{file}{base_url_suffix}\n" + return answer_str + +def format_markdown_header(header: str) -> str: + try: + # First, strip common characters from both ends + cleaned = header.strip('# 💎\n') + + # Define all characters to be removed/replaced in a single pass + replacements = { + "'": '', + "`": '', + '(': '', + ')': '', + ',': '', + '.': '', + '?': '', + '!': '', + ' ': '-' + } + + # Compile regex pattern for characters to remove + pattern = re.compile('|'.join(map(re.escape, replacements.keys()))) + + # Perform replacements in a single pass and convert to lowercase + return pattern.sub(lambda m: replacements[m.group()], cleaned).lower() + except Exception: + get_logger().exception(f"Error while formatting markdown header", artifacts={'header': header}) + return "" + +def clean_markdown_content(content: str) -> str: + """ + Remove hidden comments and unnecessary elements from markdown content to reduce size. + + Args: + content: The original markdown content + + Returns: + Cleaned markdown content + """ + # Remove HTML comments + content = re.sub(r'', '', content, flags=re.DOTALL) + + # Remove frontmatter (YAML between --- or +++ delimiters) + content = re.sub(r'^---\s*\n.*?\n---\s*\n', '', content, flags=re.DOTALL) + content = re.sub(r'^\+\+\+\s*\n.*?\n\+\+\+\s*\n', '', content, flags=re.DOTALL) + + # Remove excessive blank lines (more than 2 consecutive) + content = re.sub(r'\n{3,}', '\n\n', content) + + # Remove HTML tags that are often used for styling only + content = re.sub(r'|||', '', content, flags=re.DOTALL) + + # Remove image alt text which can be verbose + content = re.sub(r'!\[(.*?)\]', '![]', content) + + # Remove images completely + content = re.sub(r'!\[.*?\]\(.*?\)', '', content) + + # Remove simple HTML tags but preserve content between them + content = re.sub(r'<(?!table|tr|td|th|thead|tbody)([a-zA-Z][a-zA-Z0-9]*)[^>]*>(.*?)', + r'\2', content, flags=re.DOTALL) + return content.strip() + +class PredictionPreparator: + def __init__(self, ai_handler, vars, system_prompt, user_prompt): + self.ai_handler = ai_handler + variables = copy.deepcopy(vars) + environment = Environment(undefined=StrictUndefined) + self.system_prompt = environment.from_string(system_prompt).render(variables) + self.user_prompt = environment.from_string(user_prompt).render(variables) + + async def __call__(self, model: str) -> str: + try: + response, finish_reason = await self.ai_handler.chat_completion( + model=model, temperature=get_settings().config.temperature, system=self.system_prompt, user=self.user_prompt) + return response + except Exception as e: + get_logger().error(f"Error while preparing prediction: {e}") + return "" + + +class PRHelpDocs(object): + def __init__(self, ctx_url, ai_handler:partial[BaseAiHandler,] = LiteLLMAIHandler, args: Tuple[str]=None, return_as_string: bool=False): + self.ctx_url = ctx_url + self.question = args[0] if args else None + self.return_as_string = return_as_string + self.repo_url_given_explicitly = True + self.repo_url = get_settings()['PR_HELP_DOCS.REPO_URL'] + self.include_root_readme_file = not(get_settings()['PR_HELP_DOCS.EXCLUDE_ROOT_README']) + self.supported_doc_exts = get_settings()['PR_HELP_DOCS.SUPPORTED_DOC_EXTS'] + self.docs_path = get_settings()['PR_HELP_DOCS.DOCS_PATH'] + + retrieved_settings = [self.include_root_readme_file, self.supported_doc_exts, self.docs_path] + if any([setting is None for setting in retrieved_settings]): + raise Exception(f"One of the settings is invalid: {retrieved_settings}") + + self.git_provider = get_git_provider_with_context(ctx_url) + if not self.git_provider: + raise Exception(f"No git provider found at {ctx_url}") + if not self.repo_url: + self.repo_url_given_explicitly = False + get_logger().debug(f"No explicit repo url provided, deducing it from type: {self.git_provider.__class__.__name__} " + f"context url: {self.ctx_url}") + self.repo_url = self.git_provider.get_git_repo_url(self.ctx_url) + get_logger().debug(f"deduced repo url: {self.repo_url}") + try: #Try to get the same branch in case triggered from a PR: + self.repo_desired_branch = self.git_provider.get_pr_branch() + except: #Otherwise (such as in issues) + self.repo_desired_branch = get_settings()['PR_HELP_DOCS.REPO_DEFAULT_BRANCH'] + finally: + get_logger().debug(f"repo_desired_branch: {self.repo_desired_branch}") + + self.ai_handler = ai_handler() + self.vars = { + "docs_url": self.repo_url, + "question": self.question, + "snippets": "", + } + self.token_handler = TokenHandler(None, + self.vars, + get_settings().pr_help_docs_prompts.system, + get_settings().pr_help_docs_prompts.user) + + async def run(self): + if not self.question: + get_logger().warning('No question provided. Will do nothing.') + return None + + try: + # Clone the repository and gather relevant documentation files. + docs_prompt = None + with TemporaryDirectory() as tmp_dir: + get_logger().debug(f"About to clone repository: {self.repo_url} to temporary directory: {tmp_dir}...") + returned_cloned_repo_root = self.git_provider.clone(self.repo_url, tmp_dir, remove_dest_folder=False) + if not returned_cloned_repo_root: + raise Exception(f"Failed to clone {self.repo_url} to {tmp_dir}") + + get_logger().debug(f"About to gather relevant documentation files...") + doc_files = [] + if self.include_root_readme_file: + for root, _, files in os.walk(returned_cloned_repo_root.path): + # Only look at files in the root directory, not subdirectories + if root == returned_cloned_repo_root.path: + for file in files: + if file.lower().startswith("readme."): + doc_files.append(os.path.join(root, file)) + abs_docs_path = os.path.join(returned_cloned_repo_root.path, self.docs_path) + if os.path.exists(abs_docs_path): + doc_files.extend(self._find_all_document_files_matching_exts(abs_docs_path, + ignore_readme=(self.docs_path=='.'))) + if not doc_files: + get_logger().warning(f"No documentation files found matching file extensions: " + f"{self.supported_doc_exts} under repo: {self.repo_url} path: {self.docs_path}") + return None + + get_logger().info(f'Answering a question inside context {self.ctx_url} for repo: {self.repo_url}' + f' using the following documentation files: ', artifacts={'doc_files': doc_files}) + + docs_prompt = aggregate_documentation_files_for_prompt_contents(returned_cloned_repo_root.path, doc_files) + if not docs_prompt: + get_logger().warning(f"Error reading one of the documentation files. Returning with no result...") + return None + docs_prompt_to_send_to_model = docs_prompt + + # Estimate how many tokens will be needed. Trim in case of exceeding limit. + # Firstly, check if text needs to be trimmed, as some models fail to return the estimated token count if the input text is too long. + max_allowed_txt_input = get_maximal_text_input_length_for_token_count_estimation() + if len(docs_prompt_to_send_to_model) >= max_allowed_txt_input: + get_logger().warning(f"Text length: {len(docs_prompt_to_send_to_model)} exceeds the current returned limit of {max_allowed_txt_input} just for token count estimation. Trimming the text...") + docs_prompt_to_send_to_model = docs_prompt_to_send_to_model[:max_allowed_txt_input] + # Then, count the tokens in the prompt. If the count exceeds the limit, trim the text. + token_count = self.token_handler.count_tokens(docs_prompt_to_send_to_model, force_accurate=True) + get_logger().debug(f"Estimated token count of documentation to send to model: {token_count}") + model = get_settings().config.model + if model in MAX_TOKENS: + max_tokens_full = MAX_TOKENS[model] # note - here we take the actual max tokens, without any reductions. we do aim to get the full documentation website in the prompt + else: + max_tokens_full = get_max_tokens(model) + delta_output = 5000 #Elbow room to reduce chance of exceeding token limit or model paying less attention to prompt guidelines. + if token_count > max_tokens_full - delta_output: + docs_prompt_to_send_to_model = clean_markdown_content(docs_prompt_to_send_to_model) #Reduce unnecessary text/images/etc. + get_logger().info(f"Token count {token_count} exceeds the limit {max_tokens_full - delta_output}. Attempting to clip text to fit within the limit...") + docs_prompt_to_send_to_model = clip_tokens(docs_prompt_to_send_to_model, max_tokens_full - delta_output, + num_input_tokens=token_count) + self.vars['snippets'] = docs_prompt_to_send_to_model.strip() + + # Run the AI model and extract sections from its response + response = await retry_with_fallback_models(PredictionPreparator(self.ai_handler, self.vars, + get_settings().pr_help_docs_prompts.system, + get_settings().pr_help_docs_prompts.user), + model_type=ModelType.REGULAR) + response_yaml = load_yaml(response) + if not response_yaml: + get_logger().exception("Failed to parse the AI response.", artifacts={'response': response}) + raise Exception(f"Failed to parse the AI response.") + response_str = response_yaml.get('response') + relevant_sections = response_yaml.get('relevant_sections') + if not response_str or not relevant_sections: + get_logger().exception("Failed to extract response/relevant sections.", + artifacts={'response_str': response_str, 'relevant_sections': relevant_sections}) + raise Exception(f"Failed to extract response/relevant sections.") + + # Format the response as markdown + canonical_url_prefix, canonical_url_suffix = self.git_provider.get_canonical_url_parts(repo_git_url=self.repo_url if self.repo_url_given_explicitly else None, + desired_branch=self.repo_desired_branch) + answer_str = format_markdown_q_and_a_response(self.question, response_str, relevant_sections, self.supported_doc_exts, canonical_url_prefix, canonical_url_suffix) + if answer_str: + #Remove the question phrase and replace with light bulb and a heading mentioning this is an automated answer: + answer_str = modify_answer_section(answer_str) + # For PR help docs, we return the answer string instead of publishing it + if answer_str and self.return_as_string: + if int(response_yaml.get('question_is_relevant', '1')) == 0: + get_logger().warning(f"Chat help docs answer would be ignored due to an invalid question.", + artifacts={'answer_str': answer_str}) + return "" + get_logger().info(f"Chat help docs answer", artifacts={'answer_str': answer_str}) + return answer_str + + # Publish the answer + if not answer_str or int(response_yaml.get('question_is_relevant', '1')) == 0: + get_logger().info(f"No answer found") + return "" + if get_settings().config.publish_output: + self.git_provider.publish_comment(answer_str) + else: + get_logger().info("Answer:", artifacts={'answer_str': answer_str}) + + except: + get_logger().exception('failed to provide answer to given user question as a result of a thrown exception (see above)') + + + def _find_all_document_files_matching_exts(self, abs_docs_path: str, ignore_readme=False) -> List[str]: + matching_files = [] + + # Ensure extensions don't have leading dots and are lowercase + dotless_extensions = [ext.lower().lstrip('.') for ext in self.supported_doc_exts] + + # Walk through directory and subdirectories + for root, _, files in os.walk(abs_docs_path): + for file in files: + if ignore_readme and root == abs_docs_path and file.lower() in [f"readme.{ext}" for ext in dotless_extensions]: + continue + # Check if file has one of the specified extensions + if any(file.lower().endswith(f'.{ext}') for ext in dotless_extensions): + matching_files.append(os.path.join(root, file)) + return matching_files diff --git a/pr_agent/tools/pr_help_message.py b/pr_agent/tools/pr_help_message.py index d22e4f2da5..9ff5d6e34c 100644 --- a/pr_agent/tools/pr_help_message.py +++ b/pr_agent/tools/pr_help_message.py @@ -35,7 +35,6 @@ def __init__(self, pr_url: str, args=None, ai_handler: partial[BaseAiHandler,] = self.ai_handler = ai_handler() self.question_str = self.parse_args(args) self.return_as_string = return_as_string - self.num_retrieved_snippets = get_settings().get('pr_help.num_retrieved_snippets', 5) if self.question_str: self.vars = { "question": self.question_str, From e925f31ac00ab60eb5e7fa28611ed559c5aa7123 Mon Sep 17 00:00:00 2001 From: cdornano <156191933+cdornano@users.noreply.github.com> Date: Fri, 21 Mar 2025 12:57:03 +0000 Subject: [PATCH 004/365] Update azuredevops_provider.py Will make qodo agent comments "Active" by default, and not "ByDesign" which is renders to "unknown" on Azure DevOps PRs. With this, PR authors are obliged to treat the PR comment of the qodo agent before Merging. This will help companies in analysing the impact of qodo agent on their PR, as every comment needs to be treated as either "Resolved" "Won't fix" "Close" --- pr_agent/git_providers/azuredevops_provider.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/git_providers/azuredevops_provider.py b/pr_agent/git_providers/azuredevops_provider.py index e305d7c2bd..2e1c57b6af 100644 --- a/pr_agent/git_providers/azuredevops_provider.py +++ b/pr_agent/git_providers/azuredevops_provider.py @@ -383,7 +383,7 @@ def publish_comment(self, pr_comment: str, is_temporary: bool = False, thread_co get_logger().debug(f"Skipping publish_comment for temporary comment: {pr_comment}") return None comment = Comment(content=pr_comment) - thread = CommentThread(comments=[comment], thread_context=thread_context, status=5) + thread = CommentThread(comments=[comment], thread_context=thread_context, status=1) thread_response = self.azure_devops_client.create_thread( comment_thread=thread, project=self.workspace_slug, From dde362bd4736956d719056fe369acd74f40a478d Mon Sep 17 00:00:00 2001 From: Slava Eliseev Date: Sat, 22 Mar 2025 00:48:25 +0300 Subject: [PATCH 005/365] doc: Add info about ollama context length --- docs/docs/usage-guide/changing_a_model.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/docs/usage-guide/changing_a_model.md b/docs/docs/usage-guide/changing_a_model.md index 844cd6a5ac..cc4bd25327 100644 --- a/docs/docs/usage-guide/changing_a_model.md +++ b/docs/docs/usage-guide/changing_a_model.md @@ -54,6 +54,10 @@ duplicate_examples=true # will duplicate the examples in the prompt, to help the api_base = "http://localhost:11434" # or whatever port you're running Ollama on ``` +By default, Ollama uses a context window size of 2048 tokens. In most cases this is not enough to cover pr-agent promt and pull-request diff. Context window size can be overridden with the `OLLAMA_CONTEXT_LENGTH` environment variable. For example, to set the default context length to 8K, use: `OLLAMA_CONTEXT_LENGTH=8192 ollama serve`. More information you can find on the [official ollama faq](https://github.com/ollama/ollama/blob/main/docs/faq.md#how-can-i-specify-the-context-window-size). + +Please note that the `custom_model_max_tokens` setting should be configured in accordance with the `OLLAMA_CONTEXT_LENGTH`. Failure to do so may result in unexpected model output. + !!! note "Local models vs commercial models" Qodo Merge is compatible with almost any AI model, but analyzing complex code repositories and pull requests requires a model specifically optimized for code analysis. From 6efb6949451512ee8995e3a231c0c4a9e9963dfc Mon Sep 17 00:00:00 2001 From: Tal Date: Sun, 23 Mar 2025 09:07:01 +0200 Subject: [PATCH 006/365] Generated best practices file --- best_practices.md | 147 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 best_practices.md diff --git a/best_practices.md b/best_practices.md new file mode 100644 index 0000000000..19eee22d78 --- /dev/null +++ b/best_practices.md @@ -0,0 +1,147 @@ + +Pattern 1: Use proper error handling with get_logger() instead of print statements for consistent logging throughout the codebase. + + +Example code before: +``` +try: + # Some code that might fail + result = process_data() +except Exception as e: + print(f"Failed to process data: {e}") +``` + +Example code after: +``` +try: + # Some code that might fail + result = process_data() +except Exception as e: + get_logger().error(f"Failed to process data", artifact={"error": str(e)}) +``` + +
Relevant past discussions: +- https://github.com/qodo-ai/pr-agent/pull/1529#discussion_r1958684550 +- https://github.com/qodo-ai/pr-agent/pull/1529#discussion_r1958686068 +- https://github.com/qodo-ai/pr-agent/pull/1529#discussion_r1964110734 +- https://github.com/qodo-ai/pr-agent/pull/1529#discussion_r1964107962 +
+ + +Pattern 2: Add defensive null/type checking for dictionary access to prevent potential runtime errors, especially when working with API responses or user inputs. + + +Example code before: +``` +if suggestion.get('score') >= threshold and suggestion.get('improved_code'): + process_suggestion(suggestion) +``` + +Example code after: +``` +if suggestion.get('score') is not None and suggestion.get('improved_code') and int(suggestion['score']) >= threshold: + process_suggestion(suggestion) +``` + +
Relevant past discussions: +- https://github.com/qodo-ai/pr-agent/pull/1391#discussion_r1879875496 +- https://github.com/qodo-ai/pr-agent/pull/1290#discussion_r1798939921 +- https://github.com/qodo-ai/pr-agent/pull/1391#discussion_r1879875489 +
+ + +Pattern 3: Add descriptive comments for complex logic or non-obvious code to improve maintainability and help future developers understand the purpose of the code. + + +Example code before: +``` +if not issue or not isinstance(issue, dict): + continue +``` + +Example code after: +``` +# Skip empty issues or non-dictionary items to ensure valid data structure +if not issue or not isinstance(issue, dict): + continue +``` + +
Relevant past discussions: +- https://github.com/qodo-ai/pr-agent/pull/1262#discussion_r1782097205 +- https://github.com/qodo-ai/pr-agent/pull/1583#discussion_r1971790979 +
+ + +Pattern 4: Wrap API calls and external service interactions with proper try-except blocks and add specific error handling for different failure scenarios. + + +Example code before: +``` +data_above_threshold = {'code_suggestions': []} +for suggestion in data['code_suggestions']: + if int(suggestion.get('score', 0)) >= threshold: + data_above_threshold['code_suggestions'].append(suggestion) +self.push_inline_code_suggestions(data_above_threshold) +``` + +Example code after: +``` +data_above_threshold = {'code_suggestions': []} +try: + for suggestion in data['code_suggestions']: + if int(suggestion.get('score', 0)) >= threshold: + data_above_threshold['code_suggestions'].append(suggestion) + if data_above_threshold['code_suggestions']: + self.push_inline_code_suggestions(data_above_threshold) +except Exception as e: + get_logger().error(f"Failed to publish suggestions, error: {e}") +``` + +
Relevant past discussions: +- https://github.com/qodo-ai/pr-agent/pull/1391#discussion_r1879870807 +- https://github.com/qodo-ai/pr-agent/pull/1263#discussion_r1782129216 +
+ + +Pattern 5: Use consistent formatting and capitalization in documentation, especially in field descriptions and configuration comments, to improve readability. + + +Example code before: +``` +class ConfigFields(BaseModel): + field_one: str = Field(description="the first field that does something") + field_two: str = Field(description="The second field for another purpose") +``` + +Example code after: +``` +class ConfigFields(BaseModel): + field_one: str = Field(description="The first field that does something") + field_two: str = Field(description="The second field for another purpose") +``` + +
Relevant past discussions: +- https://github.com/qodo-ai/pr-agent/pull/1262#discussion_r1782097204 +- https://github.com/qodo-ai/pr-agent/pull/1543#discussion_r1958093666 +
+ + +Pattern 6: Fix typos and grammatical errors in documentation, comments, and user-facing messages to maintain professionalism and clarity. + + +Example code before: +``` +# Create a webhook in GitLab. Set the URL to http[s]:///webhook, the secret token to the generated secret from step 2, andenable the triggers. +``` + +Example code after: +``` +# Create a webhook in GitLab. Set the URL to http[s]:///webhook, the secret token to the generated secret from step 2, and enable the triggers. +``` + +
Relevant past discussions: +- https://github.com/qodo-ai/pr-agent/pull/1307#discussion_r1817699788 +- https://github.com/qodo-ai/pr-agent/pull/1307#discussion_r1817699656 +- https://github.com/qodo-ai/pr-agent/pull/1517#discussion_r1942896094 +
+ From ad17cb4d925ccef98eb5109ac4bf45d309c7557b Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sun, 23 Mar 2025 09:26:44 +0200 Subject: [PATCH 007/365] cleanup --- best_practices.md | 147 ---------------------------------------------- 1 file changed, 147 deletions(-) delete mode 100644 best_practices.md diff --git a/best_practices.md b/best_practices.md deleted file mode 100644 index 19eee22d78..0000000000 --- a/best_practices.md +++ /dev/null @@ -1,147 +0,0 @@ - -Pattern 1: Use proper error handling with get_logger() instead of print statements for consistent logging throughout the codebase. - - -Example code before: -``` -try: - # Some code that might fail - result = process_data() -except Exception as e: - print(f"Failed to process data: {e}") -``` - -Example code after: -``` -try: - # Some code that might fail - result = process_data() -except Exception as e: - get_logger().error(f"Failed to process data", artifact={"error": str(e)}) -``` - -
Relevant past discussions: -- https://github.com/qodo-ai/pr-agent/pull/1529#discussion_r1958684550 -- https://github.com/qodo-ai/pr-agent/pull/1529#discussion_r1958686068 -- https://github.com/qodo-ai/pr-agent/pull/1529#discussion_r1964110734 -- https://github.com/qodo-ai/pr-agent/pull/1529#discussion_r1964107962 -
- - -Pattern 2: Add defensive null/type checking for dictionary access to prevent potential runtime errors, especially when working with API responses or user inputs. - - -Example code before: -``` -if suggestion.get('score') >= threshold and suggestion.get('improved_code'): - process_suggestion(suggestion) -``` - -Example code after: -``` -if suggestion.get('score') is not None and suggestion.get('improved_code') and int(suggestion['score']) >= threshold: - process_suggestion(suggestion) -``` - -
Relevant past discussions: -- https://github.com/qodo-ai/pr-agent/pull/1391#discussion_r1879875496 -- https://github.com/qodo-ai/pr-agent/pull/1290#discussion_r1798939921 -- https://github.com/qodo-ai/pr-agent/pull/1391#discussion_r1879875489 -
- - -Pattern 3: Add descriptive comments for complex logic or non-obvious code to improve maintainability and help future developers understand the purpose of the code. - - -Example code before: -``` -if not issue or not isinstance(issue, dict): - continue -``` - -Example code after: -``` -# Skip empty issues or non-dictionary items to ensure valid data structure -if not issue or not isinstance(issue, dict): - continue -``` - -
Relevant past discussions: -- https://github.com/qodo-ai/pr-agent/pull/1262#discussion_r1782097205 -- https://github.com/qodo-ai/pr-agent/pull/1583#discussion_r1971790979 -
- - -Pattern 4: Wrap API calls and external service interactions with proper try-except blocks and add specific error handling for different failure scenarios. - - -Example code before: -``` -data_above_threshold = {'code_suggestions': []} -for suggestion in data['code_suggestions']: - if int(suggestion.get('score', 0)) >= threshold: - data_above_threshold['code_suggestions'].append(suggestion) -self.push_inline_code_suggestions(data_above_threshold) -``` - -Example code after: -``` -data_above_threshold = {'code_suggestions': []} -try: - for suggestion in data['code_suggestions']: - if int(suggestion.get('score', 0)) >= threshold: - data_above_threshold['code_suggestions'].append(suggestion) - if data_above_threshold['code_suggestions']: - self.push_inline_code_suggestions(data_above_threshold) -except Exception as e: - get_logger().error(f"Failed to publish suggestions, error: {e}") -``` - -
Relevant past discussions: -- https://github.com/qodo-ai/pr-agent/pull/1391#discussion_r1879870807 -- https://github.com/qodo-ai/pr-agent/pull/1263#discussion_r1782129216 -
- - -Pattern 5: Use consistent formatting and capitalization in documentation, especially in field descriptions and configuration comments, to improve readability. - - -Example code before: -``` -class ConfigFields(BaseModel): - field_one: str = Field(description="the first field that does something") - field_two: str = Field(description="The second field for another purpose") -``` - -Example code after: -``` -class ConfigFields(BaseModel): - field_one: str = Field(description="The first field that does something") - field_two: str = Field(description="The second field for another purpose") -``` - -
Relevant past discussions: -- https://github.com/qodo-ai/pr-agent/pull/1262#discussion_r1782097204 -- https://github.com/qodo-ai/pr-agent/pull/1543#discussion_r1958093666 -
- - -Pattern 6: Fix typos and grammatical errors in documentation, comments, and user-facing messages to maintain professionalism and clarity. - - -Example code before: -``` -# Create a webhook in GitLab. Set the URL to http[s]:///webhook, the secret token to the generated secret from step 2, andenable the triggers. -``` - -Example code after: -``` -# Create a webhook in GitLab. Set the URL to http[s]:///webhook, the secret token to the generated secret from step 2, and enable the triggers. -``` - -
Relevant past discussions: -- https://github.com/qodo-ai/pr-agent/pull/1307#discussion_r1817699788 -- https://github.com/qodo-ai/pr-agent/pull/1307#discussion_r1817699656 -- https://github.com/qodo-ai/pr-agent/pull/1517#discussion_r1942896094 -
- From dd80276f3fba4428c4ec0f904b0131e63e459dfe Mon Sep 17 00:00:00 2001 From: Eyal Sharon Date: Sun, 23 Mar 2025 09:55:58 +0200 Subject: [PATCH 008/365] Support cloning repo Support forcing accurate token calculation (claude) Help docs: Add desired branch in case of user supplied git repo, with default set to "main" Better documentation for getting canonical url parts --- pr_agent/algo/token_handler.py | 34 +++++++- .../git_providers/azuredevops_provider.py | 3 + pr_agent/git_providers/bitbucket_provider.py | 22 +++++ .../bitbucket_server_provider.py | 81 +++++++++++++------ pr_agent/git_providers/codecommit_provider.py | 3 + pr_agent/git_providers/gerrit_provider.py | 3 + pr_agent/git_providers/git_provider.py | 63 ++++++++++++++- pr_agent/git_providers/github_provider.py | 54 +++++++++++-- pr_agent/git_providers/gitlab_provider.py | 25 ++++++ pr_agent/git_providers/local_git_provider.py | 3 + pr_agent/settings/configuration.toml | 1 + pr_agent/tools/pr_help_docs.py | 15 ++-- requirements.txt | 3 +- 13 files changed, 263 insertions(+), 47 deletions(-) diff --git a/pr_agent/algo/token_handler.py b/pr_agent/algo/token_handler.py index 72f6184ec1..aab94894ca 100644 --- a/pr_agent/algo/token_handler.py +++ b/pr_agent/algo/token_handler.py @@ -76,7 +76,35 @@ def _get_system_user_tokens(self, pr, encoder, vars: dict, system, user): get_logger().error(f"Error in _get_system_user_tokens: {e}") return 0 - def count_tokens(self, patch: str) -> int: + def calc_claude_tokens(self, patch): + try: + import anthropic + from pr_agent.algo import MAX_TOKENS + client = anthropic.Anthropic(api_key=get_settings(use_context=False).get('anthropic.key')) + MaxTokens = MAX_TOKENS[get_settings().config.model] + + # Check if the content size is too large (9MB limit) + if len(patch.encode('utf-8')) > 9_000_000: + get_logger().warning( + "Content too large for Anthropic token counting API, falling back to local tokenizer" + ) + return MaxTokens + + response = client.messages.count_tokens( + model="claude-3-7-sonnet-20250219", + system="system", + messages=[{ + "role": "user", + "content": patch + }], + ) + return response.input_tokens + + except Exception as e: + get_logger().error( f"Error in Anthropic token counting: {e}") + return MaxTokens + + def count_tokens(self, patch: str, force_accurate=False) -> int: """ Counts the number of tokens in a given patch string. @@ -86,4 +114,6 @@ def count_tokens(self, patch: str) -> int: Returns: The number of tokens in the patch string. """ - return len(self.encoder.encode(patch, disallowed_special=())) + if force_accurate and 'claude' in get_settings().config.model.lower() and get_settings(use_context=False).get('anthropic.key'): + return self.calc_claude_tokens(patch) # API call to Anthropic for accurate token counting for Claude models + return len(self.encoder.encode(patch, disallowed_special=())) \ No newline at end of file diff --git a/pr_agent/git_providers/azuredevops_provider.py b/pr_agent/git_providers/azuredevops_provider.py index e305d7c2bd..67ab0b6ec8 100644 --- a/pr_agent/git_providers/azuredevops_provider.py +++ b/pr_agent/git_providers/azuredevops_provider.py @@ -623,3 +623,6 @@ def publish_file_comments(self, file_comments: list) -> bool: def get_line_link(self, relevant_file: str, relevant_line_start: int, relevant_line_end: int = None) -> str: return self.pr_url+f"?_a=files&path={relevant_file}" + + def _prepare_clone_url_with_token(self, repo_url_to_clone: str) -> str | None: + raise Exception("Not implemented!") diff --git a/pr_agent/git_providers/bitbucket_provider.py b/pr_agent/git_providers/bitbucket_provider.py index 9967c31003..1a2421e2ca 100644 --- a/pr_agent/git_providers/bitbucket_provider.py +++ b/pr_agent/git_providers/bitbucket_provider.py @@ -75,6 +75,9 @@ def get_git_repo_url(self, pr_url: str=None) -> str: #bitbucket does not support get_logger().exception(f"url is not a valid merge requests url: {self.pr_url}") return "" + # Given a git repo url, return prefix and suffix of the provider in order to view a given file belonging to that repo. + # Example: git clone git clone https://bitbucket.org/codiumai/pr-agent.git and branch: main -> prefix: "https://bitbucket.org/codiumai/pr-agent/src/main", suffix: "" + # In case git url is not provided, provider will use PR context (which includes branch) to determine the prefix and suffix. def get_canonical_url_parts(self, repo_git_url:str=None, desired_branch:str=None) -> Tuple[str, str]: scheme_and_netloc = None if repo_git_url: @@ -86,6 +89,7 @@ def get_canonical_url_parts(self, repo_git_url:str=None, desired_branch:str=None return ("", "") workspace_name, project_name = repo_path.split('/') else: + desired_branch = self.get_pr_branch() parsed_pr_url = urlparse(self.pr_url) scheme_and_netloc = parsed_pr_url.scheme + "://" + parsed_pr_url.netloc workspace_name, project_name = (self.workspace_slug, self.repo_slug) @@ -586,3 +590,21 @@ def publish_labels(self, pr_types: list): # bitbucket does not support labels def get_pr_labels(self, update=False): pass + #Clone related + def _prepare_clone_url_with_token(self, repo_url_to_clone: str) -> str | None: + if "bitbucket.org" not in repo_url_to_clone: + get_logger().error("Repo URL is not a valid bitbucket URL.") + return None + bearer_token = self.bearer_token + if not bearer_token: + get_logger().error("No bearer token provided. Returning None") + return None + + #For example: For repo: https://bitbucket.org/codiumai/pr-agent-tests.git + #clone url will be: https://x-token-auth:@bitbucket.org/codiumai/pr-agent-tests.git + (scheme, base_url) = repo_url_to_clone.split("bitbucket.org") + if not all([scheme, base_url]): + get_logger().error(f"repo_url_to_clone: {repo_url_to_clone} is not a valid bitbucket URL.") + return None + clone_url = f"{scheme}x-token-auth:{bearer_token}@bitbucket.org{base_url}" + return clone_url diff --git a/pr_agent/git_providers/bitbucket_server_provider.py b/pr_agent/git_providers/bitbucket_server_provider.py index fef1bc0ee8..849b86d2d8 100644 --- a/pr_agent/git_providers/bitbucket_server_provider.py +++ b/pr_agent/git_providers/bitbucket_server_provider.py @@ -7,6 +7,8 @@ from atlassian.bitbucket import Bitbucket from requests.exceptions import HTTPError +import shlex +import subprocess from ..algo.git_patch_processing import decode_if_bytes from ..algo.language_handler import is_valid_file @@ -47,6 +49,35 @@ def __init__( if pr_url: self.set_pr(pr_url) + def get_git_repo_url(self, pr_url: str=None) -> str: #bitbucket server does not support issue url, so ignore param + try: + parsed_url = urlparse(self.pr_url) + return f"{parsed_url.scheme}://{parsed_url.netloc}/scm/{self.workspace_slug.lower()}/{self.repo_slug.lower()}.git" + except Exception as e: + get_logger().exception(f"url is not a valid merge requests url: {self.pr_url}") + return "" + + # Given a git repo url, return prefix and suffix of the provider in order to view a given file belonging to that repo. + # Example: https://bitbucket.dev.my_inc.com/scm/my_work/my_repo.git and branch: my_branch -> prefix: "https://bitbucket.dev.my_inc.com/projects/MY_WORK/repos/my_repo/browse/src", suffix: "?at=refs%2Fheads%2Fmy_branch" + # In case git url is not provided, provider will use PR context (which includes branch) to determine the prefix and suffix. + def get_canonical_url_parts(self, repo_git_url:str=None, desired_branch:str=None) -> Tuple[str, str]: + workspace_name = None + project_name = None + if not repo_git_url: + desired_branch = self.get_pr_branch() + workspace_name = self.workspace_slug + project_name = self.repo_slug + else: + repo_path = repo_git_url.split('.git')[0].split('scm/')[-1] + if repo_path.count('/') == 1: # Has to have the form / + workspace_name, project_name = repo_path.split('/') + if not workspace_name or not project_name: + get_logger().error(f"workspace_name or project_name not found in context, either git url: {repo_git_url} or uninitialized workspace/project.") + return ("", "") + prefix = f"{self.bitbucket_server_url}/projects/{workspace_name}/repos/{project_name}/browse" + suffix = f"?at=refs%2Fheads%2F{desired_branch}" + return (prefix, suffix) + def get_repo_settings(self): try: content = self.bitbucket_client.get_content_of_file(self.workspace_slug, self.repo_slug, ".pr_agent.toml", self.get_pr_branch()) @@ -138,31 +169,6 @@ def is_supported(self, capability: str) -> bool: return False return True - def get_git_repo_url(self, pr_url: str=None) -> str: #bitbucket server does not support issue url, so ignore param - try: - parsed_url = urlparse(self.pr_url) - return f"{parsed_url.scheme}://{parsed_url.netloc}/scm/{self.workspace_slug.lower()}/{self.repo_slug.lower()}.git" - except Exception as e: - get_logger().exception(f"url is not a valid merge requests url: {self.pr_url}") - return "" - - def get_canonical_url_parts(self, repo_git_url:str=None, desired_branch:str=None) -> Tuple[str, str]: - workspace_name = None - project_name = None - if not repo_git_url: - workspace_name = self.workspace_slug - project_name = self.repo_slug - else: - repo_path = repo_git_url.split('.git')[0].split('scm/')[-1] - if repo_path.count('/') == 1: # Has to have the form / - workspace_name, project_name = repo_path.split('/') - if not workspace_name or not project_name: - get_logger().error(f"workspace_name or project_name not found in context, either git url: {repo_git_url} or uninitialized workspace/project.") - return ("", "") - prefix = f"{self.bitbucket_server_url}/projects/{workspace_name}/repos/{project_name}/browse" - suffix = f"?at=refs%2Fheads%2F{desired_branch}" - return (prefix, suffix) - def set_pr(self, pr_url: str): self.workspace_slug, self.repo_slug, self.pr_num = self._parse_pr_url(pr_url) self.pr = self._get_pr() @@ -506,3 +512,28 @@ def _get_pr_comments_path(self): def _get_merge_base(self): return f"rest/api/latest/projects/{self.workspace_slug}/repos/{self.repo_slug}/pull-requests/{self.pr_num}/merge-base" + # Clone related + def _prepare_clone_url_with_token(self, repo_url_to_clone: str) -> str | None: + if 'bitbucket.' not in repo_url_to_clone: + get_logger().error("Repo URL is not a valid bitbucket URL.") + return None + bearer_token = self.bearer_token + if not bearer_token: + get_logger().error("No bearer token provided. Returning None") + return None + # Return unmodified URL as the token is passed via HTTP headers in _clone_inner, as seen below. + return repo_url_to_clone + + #Overriding the shell command, since for some reason usage of x-token-auth doesn't work, as mentioned here: + # https://stackoverflow.com/questions/56760396/cloning-bitbucket-server-repo-with-access-tokens + def _clone_inner(self, repo_url: str, dest_folder: str, operation_timeout_in_seconds: int=None): + bearer_token = self.bearer_token + if not bearer_token: + #Shouldn't happen since this is checked in _prepare_clone, therefore - throwing an exception. + raise RuntimeError(f"Bearer token is required!") + + cli_args = shlex.split(f"git clone -c http.extraHeader='Authorization: Bearer {bearer_token}' " + f"--filter=blob:none --depth 1 {repo_url} {dest_folder}") + + subprocess.run(cli_args, check=True, # check=True will raise an exception if the command fails + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=operation_timeout_in_seconds) diff --git a/pr_agent/git_providers/codecommit_provider.py b/pr_agent/git_providers/codecommit_provider.py index c4f1ed7bf9..3dbe15bcac 100644 --- a/pr_agent/git_providers/codecommit_provider.py +++ b/pr_agent/git_providers/codecommit_provider.py @@ -495,3 +495,6 @@ def _get_language_percentages(extensions): lang: round(count / total_files * 100) for lang, count in lang_count.items() } return lang_percentage + + def _prepare_clone_url_with_token(self, repo_url_to_clone: str) -> str | None: + raise Exception("Not implemented!") diff --git a/pr_agent/git_providers/gerrit_provider.py b/pr_agent/git_providers/gerrit_provider.py index ba5876563f..cce4fad4b9 100644 --- a/pr_agent/git_providers/gerrit_provider.py +++ b/pr_agent/git_providers/gerrit_provider.py @@ -397,3 +397,6 @@ def remove_comment(self, comment): def get_pr_branch(self): return self.repo.head + + def _prepare_clone_url_with_token(self, repo_url_to_clone: str) -> str | None: + raise Exception("Not implemented!") diff --git a/pr_agent/git_providers/git_provider.py b/pr_agent/git_providers/git_provider.py index e5a2927fd6..4aecbf0d58 100644 --- a/pr_agent/git_providers/git_provider.py +++ b/pr_agent/git_providers/git_provider.py @@ -1,5 +1,8 @@ from abc import ABC, abstractmethod # enum EDIT_TYPE (ADDED, DELETED, MODIFIED, RENAMED) +import os +import shutil +import subprocess from typing import Optional, Tuple from pr_agent.algo.types import FilePatchInfo @@ -20,13 +23,69 @@ def get_git_repo_url(self, issues_or_pr_url: str) -> str: return "" # Given a git repo url, return prefix and suffix of the provider in order to view a given file belonging to that repo. Needs to be implemented by the provider. - # For example: For a git: https://git_provider.com/MY_PROJECT/MY_REPO.git then it should return ('https://git_provider.com/projects/MY_PROJECT/repos/MY_REPO', '?=') - # so that to properly view the file: docs/readme.md -> /docs/readme.md -> https://git_provider.com/projects/MY_PROJECT/repos/MY_REPO/docs/readme.md?=) + # For example: For a git: https://git_provider.com/MY_PROJECT/MY_REPO.git and desired branch: then it should return ('https://git_provider.com/projects/MY_PROJECT/repos/MY_REPO/.../', '?=') + # so that to properly view the file: docs/readme.md -> /docs/readme.md -> https://git_provider.com/projects/MY_PROJECT/repos/MY_REPO//docs/readme.md?=) def get_canonical_url_parts(self, repo_git_url:str, desired_branch:str) -> Tuple[str, str]: get_logger().warning("Not implemented! Returning empty prefix and suffix") return ("", "") + #Clone related API + #An object which ensures deletion of a cloned repo, once it becomes out of scope. + # Example usage: + # with TemporaryDirectory() as tmp_dir: + # returned_obj: GitProvider.ScopedClonedRepo = self.git_provider.clone(self.repo_url, tmp_dir, remove_dest_folder=False) + # print(returned_obj.path) #Use returned_obj.path. + # #From this point, returned_obj.path may be deleted at any point and therefore must not be used. + class ScopedClonedRepo(object): + def __init__(self, dest_folder): + self.path = dest_folder + + def __del__(self): + if self.path and os.path.exists(self.path): + shutil.rmtree(self.path, ignore_errors=True) + + @abstractmethod + #Method to allow implementors to manipulate the repo url to clone (such as embedding tokens in the url string). + def _prepare_clone_url_with_token(self, repo_url_to_clone: str) -> str | None: + pass + + # Does a shallow clone, using a forked process to support a timeout guard. + # In case operation has failed, it is expected to throw an exception as this method does not return a value. + def _clone_inner(self, repo_url: str, dest_folder: str, operation_timeout_in_seconds: int=None) -> None: + #The following ought to be equivalent to: + # #Repo.clone_from(repo_url, dest_folder) + # , but with throwing an exception upon timeout. + # Note: This can only be used in context that supports using pipes. + subprocess.run([ + "git", "clone", + "--filter=blob:none", + "--depth", "1", + repo_url, dest_folder + ], check=True, # check=True will raise an exception if the command fails + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=operation_timeout_in_seconds) + + CLONE_TIMEOUT_SEC = 20 + # Clone a given url to a destination folder. If successful, returns an object that wraps the destination folder, + # deleting it once it is garbage collected. See: GitProvider.ScopedClonedRepo for more details. + def clone(self, repo_url_to_clone: str, dest_folder: str, remove_dest_folder: bool = True, + operation_timeout_in_seconds: int=CLONE_TIMEOUT_SEC) -> ScopedClonedRepo|None: + returned_obj = None + clone_url = self._prepare_clone_url_with_token(repo_url_to_clone) + if not clone_url: + get_logger().error("Clone failed: Unable to obtain url to clone.") + return returned_obj + try: + if remove_dest_folder and os.path.exists(dest_folder) and os.path.isdir(dest_folder): + shutil.rmtree(dest_folder) + self._clone_inner(clone_url, dest_folder, operation_timeout_in_seconds) + returned_obj = GitProvider.ScopedClonedRepo(dest_folder) + except Exception as e: + get_logger().exception(f"Clone failed: Could not clone url.", + artifact={"error": str(e), "url": clone_url, "dest_folder": dest_folder}) + finally: + return returned_obj + @abstractmethod def get_files(self) -> list: pass diff --git a/pr_agent/git_providers/github_provider.py b/pr_agent/git_providers/github_provider.py index e020cceb4a..d83223bc37 100644 --- a/pr_agent/git_providers/github_provider.py +++ b/pr_agent/git_providers/github_provider.py @@ -85,6 +85,9 @@ def get_git_repo_url(self, issues_or_pr_url: str) -> str: repo_path = self._get_owner_and_repo_path(issues_or_pr_url) return f"{issues_or_pr_url.split(repo_path)[0]}{repo_path}.git" + # Given a git repo url, return prefix and suffix of the provider in order to view a given file belonging to that repo. + # Example: https://github.com/qodo-ai/pr-agent.git and branch: v0.8 -> prefix: "https://github.com/qodo-ai/pr-agent/blob/v0.8", suffix: "" + # In case git url is not provided, provider will use PR context (which includes branch) to determine the prefix and suffix. def get_canonical_url_parts(self, repo_git_url:str, desired_branch:str) -> Tuple[str, str]: owner = None repo = None @@ -102,6 +105,7 @@ def get_canonical_url_parts(self, repo_git_url:str, desired_branch:str) -> Tuple if (not owner or not repo) and self.repo: #"else" - User did not provide an external git url, use self.repo object: owner, repo = self.repo.split('/') scheme_and_netloc = self.base_url_html + desired_branch = self.get_pr_branch() if not any([scheme_and_netloc, owner, repo]): #"else": Not invoked from a PR context,but no provided git url for context get_logger().error(f"Unable to get canonical url parts since missing context (PR or explicit git url)") return ("", "") @@ -750,9 +754,8 @@ def _parse_issue_url(self, issue_url: str) -> Tuple[str, int]: return repo_name, issue_number def _get_github_client(self): - deployment_type = get_settings().get("GITHUB.DEPLOYMENT_TYPE", "user") - - if deployment_type == 'app': + self.deployment_type = get_settings().get("GITHUB.DEPLOYMENT_TYPE", "user") + if self.deployment_type == 'app': try: private_key = get_settings().github.private_key app_id = get_settings().github.app_id @@ -762,16 +765,19 @@ def _get_github_client(self): raise ValueError("GitHub app installation ID is required when using GitHub app deployment") auth = AppAuthentication(app_id=app_id, private_key=private_key, installation_id=self.installation_id) - return Github(app_auth=auth, base_url=self.base_url) - - if deployment_type == 'user': + self.auth = auth + elif self.deployment_type == 'user': try: token = get_settings().github.user_token except AttributeError as e: raise ValueError( "GitHub token is required when using user deployment. See: " "https://github.com/Codium-ai/pr-agent#method-2-run-from-source") from e - return Github(auth=Auth.Token(token), base_url=self.base_url) + self.auth = Auth.Token(token) + if self.auth: + return Github(auth=self.auth, base_url=self.base_url) + else: + raise ValueError("Could not authenticate to GitHub") def _get_repo(self): if hasattr(self, 'repo_obj') and \ @@ -1111,3 +1117,37 @@ def validate_comments_inside_hunks(self, code_suggestions): get_logger().error(f"Failed to process patch for committable comment, error: {e}") return code_suggestions_copy + #Clone related + def _prepare_clone_url_with_token(self, repo_url_to_clone: str) -> str | None: + scheme = "https://" + + #For example, to clone: + #https://github.com/Codium-ai/pr-agent-pro.git + #Need to embed inside the github token: + #https://@github.com/Codium-ai/pr-agent-pro.git + + github_token = self.auth.token + github_base_url = self.base_url_html + if not all([github_token, github_base_url]): + get_logger().error("Either missing auth token or missing base url") + return None + if scheme not in github_base_url: + get_logger().error(f"Base url: {github_base_url} is missing prefix: {scheme}") + return None + github_com = github_base_url.split(scheme)[1] # e.g. 'github.com' or github..com + if not github_com: + get_logger().error(f"Base url: {github_base_url} has an empty base url") + return None + if github_com not in repo_url_to_clone: + get_logger().error(f"url to clone: {repo_url_to_clone} does not contain {github_com}") + return None + repo_full_name = repo_url_to_clone.split(github_com)[-1] + if not repo_full_name: + get_logger().error(f"url to clone: {repo_url_to_clone} is malformed") + return None + + clone_url = scheme + if self.deployment_type == 'app': + clone_url += "git:" + clone_url += f"{github_token}@{github_com}{repo_full_name}" + return clone_url diff --git a/pr_agent/git_providers/gitlab_provider.py b/pr_agent/git_providers/gitlab_provider.py index 1040f3b5e2..c1c589c6b8 100644 --- a/pr_agent/git_providers/gitlab_provider.py +++ b/pr_agent/git_providers/gitlab_provider.py @@ -76,6 +76,9 @@ def get_git_repo_url(self, issues_or_pr_url: str) -> str: return "" return f"{provider_url.split(repo_path)[0]}{repo_path}.git" + # Given a git repo url, return prefix and suffix of the provider in order to view a given file belonging to that repo. + # Example: https://gitlab.com/codiumai/pr-agent.git and branch: t1 -> prefix: "https://gitlab.com/codiumai/pr-agent/-/blob/t1", suffix: "?ref_type=heads" + # In case git url is not provided, provider will use PR context (which includes branch) to determine the prefix and suffix. def get_canonical_url_parts(self, repo_git_url:str=None, desired_branch:str=None) -> Tuple[str, str]: repo_path = "" if not repo_git_url and not self.pr_url: @@ -83,6 +86,7 @@ def get_canonical_url_parts(self, repo_git_url:str=None, desired_branch:str=None return ("", "") if not repo_git_url: #Use PR url as context repo_path = self._get_project_path_from_pr_or_issue_url(self.pr_url) + desired_branch = self.get_pr_branch() else: #Use repo git url repo_path = repo_git_url.split('.git')[0].split('.com/')[-1] prefix = f"{self.gitlab_url}/{repo_path}/-/blob/{desired_branch}" @@ -629,3 +633,24 @@ def generate_link_to_relevant_line_number(self, suggestion) -> str: get_logger().info(f"Failed adding line link, error: {e}") return "" + #Clone related + def _prepare_clone_url_with_token(self, repo_url_to_clone: str) -> str | None: + if "gitlab." not in repo_url_to_clone: + get_logger().error(f"Repo URL: {repo_url_to_clone} is not a valid gitlab URL.") + return None + (scheme, base_url) = repo_url_to_clone.split("gitlab.") + access_token = self.gl.oauth_token + if not all([scheme, access_token, base_url]): + get_logger().error(f"Either no access token found, or repo URL: {repo_url_to_clone} " + f"is missing prefix: {scheme} and/or base URL: {base_url}.") + return None + + #Note that the ""official"" method found here: + # https://docs.gitlab.com/user/profile/personal_access_tokens/#clone-repository-using-personal-access-token + # requires a username, which may not be applicable. + # The following solution is taken from: https://stackoverflow.com/questions/25409700/using-gitlab-token-to-clone-without-authentication/35003812#35003812 + # For example: For repo url: https://gitlab.codium-inc.com/qodo/autoscraper.git + # Then to clone one will issue: 'git clone https://oauth2:@gitlab.codium-inc.com/qodo/autoscraper.git' + + clone_url = f"{scheme}oauth2:{access_token}@gitlab.{base_url}" + return clone_url diff --git a/pr_agent/git_providers/local_git_provider.py b/pr_agent/git_providers/local_git_provider.py index 420289761c..beaab24d5f 100644 --- a/pr_agent/git_providers/local_git_provider.py +++ b/pr_agent/git_providers/local_git_provider.py @@ -190,3 +190,6 @@ def get_issue_comments(self): def get_pr_labels(self, update=False): raise NotImplementedError('Getting labels is not implemented for the local git provider') + + def _prepare_clone_url_with_token(self, repo_url_to_clone: str) -> str | None: + raise Exception("Not implemented!") diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index e9ee6e6d94..899e9c1fca 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -214,6 +214,7 @@ num_retrieved_snippets=5 [pr_help_docs] repo_url = "" #If not overwritten, will use the repo from where the context came from (issue or PR) +repo_default_branch = "main" docs_path = "docs" exclude_root_readme = false supported_doc_exts = [".md", ".mdx", ".rst"] diff --git a/pr_agent/tools/pr_help_docs.py b/pr_agent/tools/pr_help_docs.py index 3958ac5254..d8dbcc66d7 100644 --- a/pr_agent/tools/pr_help_docs.py +++ b/pr_agent/tools/pr_help_docs.py @@ -36,7 +36,7 @@ def modify_answer_section(ai_response: str) -> str | None: #### Relevant Sources... """ model_answer_and_relevant_sections_in_response \ - = _extract_model_answer_and_relevant_sources(ai_response) + = extract_model_answer_and_relevant_sources(ai_response) if model_answer_and_relevant_sections_in_response is not None: cleaned_question_with_answer = "### :bulb: Auto-generated documentation-based answer:\n" cleaned_question_with_answer += model_answer_and_relevant_sections_in_response @@ -44,7 +44,7 @@ def modify_answer_section(ai_response: str) -> str | None: get_logger().warning(f"Either no answer section found, or that section is malformed: {ai_response}") return None -def _extract_model_answer_and_relevant_sources(ai_response: str) -> str | None: +def extract_model_answer_and_relevant_sources(ai_response: str) -> str | None: # It is assumed that the input contains several sections with leading "### ", # where the answer is the last one of them having the format: "### Answer:\n"), since the model returns the answer # AFTER the user question. By splitting using the string: "### Answer:\n" and grabbing the last part, @@ -71,7 +71,6 @@ def _extract_model_answer_and_relevant_sources(ai_response: str) -> str | None: get_logger().warning(f"Either no answer section found, or that section is malformed: {ai_response}") return None - def get_maximal_text_input_length_for_token_count_estimation(): model = get_settings().config.model if 'claude-3-7-sonnet' in model.lower(): @@ -204,7 +203,8 @@ def __init__(self, ctx_url, ai_handler:partial[BaseAiHandler,] = LiteLLMAIHandle self.question = args[0] if args else None self.return_as_string = return_as_string self.repo_url_given_explicitly = True - self.repo_url = get_settings()['PR_HELP_DOCS.REPO_URL'] + self.repo_url = get_settings().get('PR_HELP_DOCS.REPO_URL', '') + self.repo_desired_branch = get_settings().get('PR_HELP_DOCS.REPO_DEFAULT_BRANCH', 'main') #Ignored if self.repo_url is empty self.include_root_readme_file = not(get_settings()['PR_HELP_DOCS.EXCLUDE_ROOT_README']) self.supported_doc_exts = get_settings()['PR_HELP_DOCS.SUPPORTED_DOC_EXTS'] self.docs_path = get_settings()['PR_HELP_DOCS.DOCS_PATH'] @@ -222,12 +222,7 @@ def __init__(self, ctx_url, ai_handler:partial[BaseAiHandler,] = LiteLLMAIHandle f"context url: {self.ctx_url}") self.repo_url = self.git_provider.get_git_repo_url(self.ctx_url) get_logger().debug(f"deduced repo url: {self.repo_url}") - try: #Try to get the same branch in case triggered from a PR: - self.repo_desired_branch = self.git_provider.get_pr_branch() - except: #Otherwise (such as in issues) - self.repo_desired_branch = get_settings()['PR_HELP_DOCS.REPO_DEFAULT_BRANCH'] - finally: - get_logger().debug(f"repo_desired_branch: {self.repo_desired_branch}") + self.repo_desired_branch = None #Inferred from the repo provider. self.ai_handler = ai_handler() self.vars = { diff --git a/requirements.txt b/requirements.txt index ad42140fa1..2625ad6628 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,6 @@ aiohttp==3.9.5 -anthropic[vertex]==0.47.1 +anthropic>=0.48 +#anthropic[vertex]==0.47.1 atlassian-python-api==3.41.4 azure-devops==7.1.0b3 azure-identity==1.15.0 From 88a17848eb28bb26538299b1b0103854cfdb939b Mon Sep 17 00:00:00 2001 From: Eyal Sharon Date: Sun, 23 Mar 2025 20:04:33 +0200 Subject: [PATCH 009/365] - Documentation - Better error handling in case could not deduce repo url --- docs/docs/tools/help_docs.md | 25 +++++++++++++++++++++++++ docs/mkdocs.yml | 1 + pr_agent/servers/help.py | 15 +++++++++++++++ pr_agent/settings/configuration.toml | 1 + pr_agent/tools/pr_help_docs.py | 9 +++++++++ pr_agent/tools/pr_help_message.py | 4 ++++ 6 files changed, 55 insertions(+) create mode 100644 docs/docs/tools/help_docs.md diff --git a/docs/docs/tools/help_docs.md b/docs/docs/tools/help_docs.md new file mode 100644 index 0000000000..70a59ff946 --- /dev/null +++ b/docs/docs/tools/help_docs.md @@ -0,0 +1,25 @@ +## Overview + +The `help_docs` tool answers a question based on a given relative path of documentation, either from the repository of this merge request or from a given one. +It can be invoked manually by commenting on any PR: +``` +/help_docs "..." +``` + +## Example usage + +![TODO help_docs on the documentation of this repository](https://codium.ai/images/pr_agent/help_docs_comment.png){width=512} + +![TODO help_docs on the documentation of another repository](https://codium.ai/images/pr_agent/help_docs_comment_implicit_git.png){width=512} + +![TODO help_docs](https://codium.ai/images/pr_agent/help_docs_result.png){width=512} + +## Configuration options + +Under the section `--pr_help_docs`, the [configuration file](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml#L50) contains options to customize the 'help docs' tool: + +- `repo_url`: If not overwritten, will use the repo from where the context came from (issue or PR), otherwise - use the given repo as context. +- `repo_default_branch`: The branch to use in case repo_url overwritten, otherwise - has no effect. +- `docs_path`: Relative path from root of repository (either the one this PR has been issued for, or above repo url). +- `exclude_root_readme`: Whether or not to exclude the root README file for querying the model. +- `supported_doc_exts` : Which file extensions should be included for the purpose of querying the model. diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index c14773b3e6..86a8b07d65 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -28,6 +28,7 @@ nav: - Improve: 'tools/improve.md' - Ask: 'tools/ask.md' - Update Changelog: 'tools/update_changelog.md' + - Help Docs: 'tools/help_docs.md' - Help: 'tools/help.md' - 💎 Analyze: 'tools/analyze.md' - 💎 Test: 'tools/test.md' diff --git a/pr_agent/servers/help.py b/pr_agent/servers/help.py index 7edd13db5f..e3cdf0de96 100644 --- a/pr_agent/servers/help.py +++ b/pr_agent/servers/help.py @@ -6,6 +6,7 @@ def get_general_commands_text(): "> - **/improve [--extended]**: Suggest code improvements. Extended mode provides a higher quality feedback. \n" \ "> - **/ask \\**: Ask a question about the PR. \n" \ "> - **/update_changelog**: Update the changelog based on the PR's contents. \n" \ + "> - **/help_docs \\**: Given a path to documentation (either for this repository or for a given one), ask a question. \n" \ "> - **/add_docs** 💎: Generate docstring for new components introduced in the PR. \n" \ "> - **/generate_labels** 💎: Generate labels for the PR based on the PR's contents. \n" \ "> - **/analyze** 💎: Automatically analyzes the PR, and presents changes walkthrough for each component. \n\n" \ @@ -201,3 +202,17 @@ def get_improve_usage_guide(): output += f"\n\nSee the improve [usage page](https://pr-agent-docs.codium.ai/tools/improve/) for a comprehensive guide on using this tool.\n\n" return output + + + @staticmethod + def get_help_docs_usage_guide(): + output = "**Overview:**\n" + output += """\ +The help docs tool, named `help_docs`, answers a question based on a given relative path of documentation, either from the repository of this merge request or from a given one." +It can be invoked manually by commenting on any PR: +``` +/help_docs "..." +``` +""" + output += f"\n\nSee the [help_docs usage](https://pr-agent-docs.codium.ai/tools/help_docs/) page for a comprehensive guide on using this tool.\n\n" + return output diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index 899e9c1fca..7902546748 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -218,6 +218,7 @@ repo_default_branch = "main" docs_path = "docs" exclude_root_readme = false supported_doc_exts = [".md", ".mdx", ".rst"] +enable_help_text=false [github] # The type of deployment to create. Valid values are 'app' or 'user'. diff --git a/pr_agent/tools/pr_help_docs.py b/pr_agent/tools/pr_help_docs.py index d8dbcc66d7..5695065a82 100644 --- a/pr_agent/tools/pr_help_docs.py +++ b/pr_agent/tools/pr_help_docs.py @@ -16,6 +16,7 @@ from pr_agent.config_loader import get_settings from pr_agent.git_providers import get_git_provider_with_context from pr_agent.log import get_logger +from pr_agent.servers.help import HelpMessage #Common code that can be called from similar tools: @@ -221,6 +222,8 @@ def __init__(self, ctx_url, ai_handler:partial[BaseAiHandler,] = LiteLLMAIHandle get_logger().debug(f"No explicit repo url provided, deducing it from type: {self.git_provider.__class__.__name__} " f"context url: {self.ctx_url}") self.repo_url = self.git_provider.get_git_repo_url(self.ctx_url) + if not self.repo_url: + raise Exception(f"Unable to deduce repo url from type: {self.git_provider.__class__.__name__} url: {self.ctx_url}") get_logger().debug(f"deduced repo url: {self.repo_url}") self.repo_desired_branch = None #Inferred from the repo provider. @@ -334,6 +337,12 @@ async def run(self): if not answer_str or int(response_yaml.get('question_is_relevant', '1')) == 0: get_logger().info(f"No answer found") return "" + + if self.git_provider.is_supported("gfm_markdown") and get_settings().pr_help_docs.enable_help_text: + answer_str += "
\n\n
💡 Tool usage guide:
\n\n" + answer_str += HelpMessage.get_help_docs_usage_guide() + answer_str += "\n
\n" + if get_settings().config.publish_output: self.git_provider.publish_comment(answer_str) else: diff --git a/pr_agent/tools/pr_help_message.py b/pr_agent/tools/pr_help_message.py index 9ff5d6e34c..f7ff9948ce 100644 --- a/pr_agent/tools/pr_help_message.py +++ b/pr_agent/tools/pr_help_message.py @@ -208,6 +208,7 @@ async def run(self): tool_names.append(f"[REVIEW]({base_path}/review/)") tool_names.append(f"[IMPROVE]({base_path}/improve/)") tool_names.append(f"[UPDATE CHANGELOG]({base_path}/update_changelog/)") + tool_names.append(f"[HELP DOCS]({base_path}/help_docs/)") tool_names.append(f"[ADD DOCS]({base_path}/documentation/) 💎") tool_names.append(f"[TEST]({base_path}/test/) 💎") tool_names.append(f"[IMPROVE COMPONENT]({base_path}/improve_component/) 💎") @@ -223,6 +224,7 @@ async def run(self): descriptions.append("Adjustable feedback about the PR, possible issues, security concerns, review effort and more") descriptions.append("Code suggestions for improving the PR") descriptions.append("Automatically updates the changelog") + descriptions.append("Answers a question regarding this repository, or a given one, based on given documentation path") descriptions.append("Generates documentation to methods/functions/classes that changed in the PR") descriptions.append("Generates unit tests for a specific component, based on the PR code change") descriptions.append("Code suggestions for a specific component that changed in the PR") @@ -239,6 +241,7 @@ async def run(self): commands.append("`/review`") commands.append("`/improve`") commands.append("`/update_changelog`") + commands.append("`/help_docs`") commands.append("`/add_docs`") commands.append("`/test`") commands.append("`/improve_component`") @@ -254,6 +257,7 @@ async def run(self): checkbox_list.append(" - [ ] Run ") checkbox_list.append(" - [ ] Run ") checkbox_list.append(" - [ ] Run ") + checkbox_list.append(" - [ ] Run ") checkbox_list.append(" - [ ] Run ") checkbox_list.append(" - [ ] Run ") checkbox_list.append(" - [ ] Run ") From e14fc7e02d474e8d349d608b76a837d700492a26 Mon Sep 17 00:00:00 2001 From: Eyal Sharon Date: Mon, 24 Mar 2025 10:21:23 +0200 Subject: [PATCH 010/365] Fix missing self.bearer_token for bitbucket related providers --- pr_agent/git_providers/bitbucket_provider.py | 9 ++++++--- pr_agent/git_providers/bitbucket_server_provider.py | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/pr_agent/git_providers/bitbucket_provider.py b/pr_agent/git_providers/bitbucket_provider.py index 1a2421e2ca..d3882fdadd 100644 --- a/pr_agent/git_providers/bitbucket_provider.py +++ b/pr_agent/git_providers/bitbucket_provider.py @@ -30,12 +30,15 @@ def __init__( ): s = requests.Session() try: - bearer = context.get("bitbucket_bearer_token", None) + self.bearer_token = bearer = context.get("bitbucket_bearer_token", None) + if not bearer and get_settings().get("BITBUCKET.BEARER_TOKEN", None): + self.bearer_token = bearer = get_settings().get("BITBUCKET.BEARER_TOKEN", None) s.headers["Authorization"] = f"Bearer {bearer}" except Exception: + self.bearer_token = get_settings().get("BITBUCKET.BEARER_TOKEN", None) s.headers[ "Authorization" - ] = f'Bearer {get_settings().get("BITBUCKET.BEARER_TOKEN", None)}' + ] = f'Bearer {self.bearer_token}' s.headers["Content-Type"] = "application/json" self.headers = s.headers self.bitbucket_client = Cloud(session=s) @@ -488,7 +491,7 @@ def remove_reaction(self, issue_comment_id: int, reaction_id: int) -> bool: return True @staticmethod - def _parse_pr_url(pr_url: str) -> Tuple[str, int]: + def _parse_pr_url(pr_url: str) -> Tuple[str, int, int]: parsed_url = urlparse(pr_url) if "bitbucket.org" not in parsed_url.netloc: diff --git a/pr_agent/git_providers/bitbucket_server_provider.py b/pr_agent/git_providers/bitbucket_server_provider.py index 849b86d2d8..ddbb60cc05 100644 --- a/pr_agent/git_providers/bitbucket_server_provider.py +++ b/pr_agent/git_providers/bitbucket_server_provider.py @@ -36,7 +36,7 @@ def __init__( self.incremental = incremental self.diff_files = None self.bitbucket_pull_request_api_url = pr_url - + self.bearer_token = get_settings().get("BITBUCKET_SERVER.BEARER_TOKEN", None) self.bitbucket_server_url = self._parse_bitbucket_server(url=pr_url) self.bitbucket_client = bitbucket_client or Bitbucket(url=self.bitbucket_server_url, token=get_settings().get("BITBUCKET_SERVER.BEARER_TOKEN", @@ -67,7 +67,7 @@ def get_canonical_url_parts(self, repo_git_url:str=None, desired_branch:str=None desired_branch = self.get_pr_branch() workspace_name = self.workspace_slug project_name = self.repo_slug - else: + elif '.git' in repo_git_url and 'scm/' in repo_git_url: repo_path = repo_git_url.split('.git')[0].split('scm/')[-1] if repo_path.count('/') == 1: # Has to have the form / workspace_name, project_name = repo_path.split('/') From fe98779a8846dd8f18d464ea74872b6eb4737845 Mon Sep 17 00:00:00 2001 From: Eyal Sharon Date: Mon, 24 Mar 2025 10:32:55 +0200 Subject: [PATCH 011/365] Update images in help_docs md --- docs/docs/tools/help_docs.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/docs/tools/help_docs.md b/docs/docs/tools/help_docs.md index 70a59ff946..872affed9f 100644 --- a/docs/docs/tools/help_docs.md +++ b/docs/docs/tools/help_docs.md @@ -8,11 +8,11 @@ It can be invoked manually by commenting on any PR: ## Example usage -![TODO help_docs on the documentation of this repository](https://codium.ai/images/pr_agent/help_docs_comment.png){width=512} +![help_docs on the documentation of this repository](https://codium.ai/images/pr_agent/help_docs_comment.png){width=512} -![TODO help_docs on the documentation of another repository](https://codium.ai/images/pr_agent/help_docs_comment_implicit_git.png){width=512} +![help_docs on the documentation of another repository](https://codium.ai/images/pr_agent/help_docs_comment_explicit_git.png){width=512} -![TODO help_docs](https://codium.ai/images/pr_agent/help_docs_result.png){width=512} +![help_docs response](https://codium.ai/images/pr_agent/help_docs_response.png){width=512} ## Configuration options From 477ebf49263d18089892f2f7d3beaa7ba1205df0 Mon Sep 17 00:00:00 2001 From: Eyal Sharon Date: Mon, 24 Mar 2025 10:54:50 +0200 Subject: [PATCH 012/365] Code recommendations from /improve --- pr_agent/git_providers/github_provider.py | 3 +++ pr_agent/git_providers/gitlab_provider.py | 7 ++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/pr_agent/git_providers/github_provider.py b/pr_agent/git_providers/github_provider.py index d83223bc37..11b35072cd 100644 --- a/pr_agent/git_providers/github_provider.py +++ b/pr_agent/git_providers/github_provider.py @@ -83,6 +83,9 @@ def _get_owner_and_repo_path(self, given_url: str) -> str: def get_git_repo_url(self, issues_or_pr_url: str) -> str: repo_path = self._get_owner_and_repo_path(issues_or_pr_url) + if not repo_path or repo_path not in issues_or_pr_url: + get_logger().error(f"Unable to retrieve owner/path from url: {issues_or_pr_url}") + return "" return f"{issues_or_pr_url.split(repo_path)[0]}{repo_path}.git" # Given a git repo url, return prefix and suffix of the provider in order to view a given file belonging to that repo. diff --git a/pr_agent/git_providers/gitlab_provider.py b/pr_agent/git_providers/gitlab_provider.py index c1c589c6b8..331bdf654e 100644 --- a/pr_agent/git_providers/gitlab_provider.py +++ b/pr_agent/git_providers/gitlab_provider.py @@ -71,10 +71,11 @@ def _get_project_path_from_pr_or_issue_url(self, pr_or_issue_url: str) -> str: def get_git_repo_url(self, issues_or_pr_url: str) -> str: provider_url = issues_or_pr_url - repo_path = self._get_project_path_from_pr_or_issue_url(issues_or_pr_url) - if not repo_path: + repo_path = self._get_project_path_from_pr_or_issue_url(provider_url) + if not repo_path or repo_path not in issues_or_pr_url: + get_logger().error(f"Unable to retrieve project path from url: {issues_or_pr_url}") return "" - return f"{provider_url.split(repo_path)[0]}{repo_path}.git" + return f"{issues_or_pr_url.split(repo_path)[0]}{repo_path}.git" # Given a git repo url, return prefix and suffix of the provider in order to view a given file belonging to that repo. # Example: https://gitlab.com/codiumai/pr-agent.git and branch: t1 -> prefix: "https://gitlab.com/codiumai/pr-agent/-/blob/t1", suffix: "?ref_type=heads" From a4387b58296013993b06b15d2b0ae47fa37dac11 Mon Sep 17 00:00:00 2001 From: Eyal Sharon Date: Mon, 24 Mar 2025 11:19:23 +0200 Subject: [PATCH 013/365] Fix more /improve suggestions --- pr_agent/git_providers/github_provider.py | 1 + pr_agent/settings/pr_help_docs_prompts.toml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pr_agent/git_providers/github_provider.py b/pr_agent/git_providers/github_provider.py index 11b35072cd..2312641170 100644 --- a/pr_agent/git_providers/github_provider.py +++ b/pr_agent/git_providers/github_provider.py @@ -758,6 +758,7 @@ def _parse_issue_url(self, issue_url: str) -> Tuple[str, int]: def _get_github_client(self): self.deployment_type = get_settings().get("GITHUB.DEPLOYMENT_TYPE", "user") + self.auth = None if self.deployment_type == 'app': try: private_key = get_settings().github.private_key diff --git a/pr_agent/settings/pr_help_docs_prompts.toml b/pr_agent/settings/pr_help_docs_prompts.toml index df68a4b681..c73e1d958c 100644 --- a/pr_agent/settings/pr_help_docs_prompts.toml +++ b/pr_agent/settings/pr_help_docs_prompts.toml @@ -18,7 +18,7 @@ class DocHelper(BaseModel): user_question: str = Field(description="The user's question") response: str = Field(description="The response to the user's question") relevant_sections: List[relevant_section] = Field(description="A list of the relevant markdown/restructured text sections in the documentation that answer the user's question, ordered by importance (most relevant first)") - question_is_relevant: int = Field(description="Return 1 if the question is somewhat relevant to documenation. 0 - otherwise") + question_is_relevant: int = Field(description="Return 1 if the question is somewhat relevant to documentation. 0 - otherwise") ===== From fb324d106c712fccc0bc2752ff414761937bdb5e Mon Sep 17 00:00:00 2001 From: Eyal Sharon Date: Mon, 24 Mar 2025 14:06:06 +0200 Subject: [PATCH 014/365] Remove non implemented method _prepare_clone_url_with_token from providers which don't support it and instead, issue a warning in base class. --- pr_agent/git_providers/azuredevops_provider.py | 3 --- pr_agent/git_providers/codecommit_provider.py | 3 --- pr_agent/git_providers/gerrit_provider.py | 3 --- pr_agent/git_providers/git_provider.py | 5 +++-- pr_agent/git_providers/local_git_provider.py | 3 --- 5 files changed, 3 insertions(+), 14 deletions(-) diff --git a/pr_agent/git_providers/azuredevops_provider.py b/pr_agent/git_providers/azuredevops_provider.py index 197b47c501..2e1c57b6af 100644 --- a/pr_agent/git_providers/azuredevops_provider.py +++ b/pr_agent/git_providers/azuredevops_provider.py @@ -623,6 +623,3 @@ def publish_file_comments(self, file_comments: list) -> bool: def get_line_link(self, relevant_file: str, relevant_line_start: int, relevant_line_end: int = None) -> str: return self.pr_url+f"?_a=files&path={relevant_file}" - - def _prepare_clone_url_with_token(self, repo_url_to_clone: str) -> str | None: - raise Exception("Not implemented!") diff --git a/pr_agent/git_providers/codecommit_provider.py b/pr_agent/git_providers/codecommit_provider.py index 3dbe15bcac..c4f1ed7bf9 100644 --- a/pr_agent/git_providers/codecommit_provider.py +++ b/pr_agent/git_providers/codecommit_provider.py @@ -495,6 +495,3 @@ def _get_language_percentages(extensions): lang: round(count / total_files * 100) for lang, count in lang_count.items() } return lang_percentage - - def _prepare_clone_url_with_token(self, repo_url_to_clone: str) -> str | None: - raise Exception("Not implemented!") diff --git a/pr_agent/git_providers/gerrit_provider.py b/pr_agent/git_providers/gerrit_provider.py index cce4fad4b9..ba5876563f 100644 --- a/pr_agent/git_providers/gerrit_provider.py +++ b/pr_agent/git_providers/gerrit_provider.py @@ -397,6 +397,3 @@ def remove_comment(self, comment): def get_pr_branch(self): return self.repo.head - - def _prepare_clone_url_with_token(self, repo_url_to_clone: str) -> str | None: - raise Exception("Not implemented!") diff --git a/pr_agent/git_providers/git_provider.py b/pr_agent/git_providers/git_provider.py index 4aecbf0d58..e5afe42bd6 100644 --- a/pr_agent/git_providers/git_provider.py +++ b/pr_agent/git_providers/git_provider.py @@ -46,9 +46,10 @@ def __del__(self): shutil.rmtree(self.path, ignore_errors=True) @abstractmethod - #Method to allow implementors to manipulate the repo url to clone (such as embedding tokens in the url string). + #Method to allow implementors to manipulate the repo url to clone (such as embedding tokens in the url string). Needs to be implemented by the provider. def _prepare_clone_url_with_token(self, repo_url_to_clone: str) -> str | None: - pass + get_logger().warning("Not implemented! Returning None") + return None # Does a shallow clone, using a forked process to support a timeout guard. # In case operation has failed, it is expected to throw an exception as this method does not return a value. diff --git a/pr_agent/git_providers/local_git_provider.py b/pr_agent/git_providers/local_git_provider.py index beaab24d5f..420289761c 100644 --- a/pr_agent/git_providers/local_git_provider.py +++ b/pr_agent/git_providers/local_git_provider.py @@ -190,6 +190,3 @@ def get_issue_comments(self): def get_pr_labels(self, update=False): raise NotImplementedError('Getting labels is not implemented for the local git provider') - - def _prepare_clone_url_with_token(self, repo_url_to_clone: str) -> str | None: - raise Exception("Not implemented!") From 8495e4d54984b1664a0d75010ad30fe0d866b5bc Mon Sep 17 00:00:00 2001 From: Eyal Sharon Date: Mon, 24 Mar 2025 15:47:35 +0200 Subject: [PATCH 015/365] More comprehensive handling in count_tokens(force_accurate==True): In case model is neither OpenAI nor Anthropic Claude, simply use an elbow room factor in order to force a more conservative estimate. --- pr_agent/algo/token_handler.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/pr_agent/algo/token_handler.py b/pr_agent/algo/token_handler.py index aab94894ca..9bc801ed9f 100644 --- a/pr_agent/algo/token_handler.py +++ b/pr_agent/algo/token_handler.py @@ -1,6 +1,7 @@ from threading import Lock from jinja2 import Environment, StrictUndefined +from math import ceil from tiktoken import encoding_for_model, get_encoding from pr_agent.config_loader import get_settings @@ -114,6 +115,22 @@ def count_tokens(self, patch: str, force_accurate=False) -> int: Returns: The number of tokens in the patch string. """ - if force_accurate and 'claude' in get_settings().config.model.lower() and get_settings(use_context=False).get('anthropic.key'): + encoder_estimate = len(self.encoder.encode(patch, disallowed_special=())) + if not force_accurate: + return encoder_estimate + #else, need to provide an accurate estimation: + + model = get_settings().config.model.lower() + if force_accurate and 'claude' in model and get_settings(use_context=False).get('anthropic.key'): return self.calc_claude_tokens(patch) # API call to Anthropic for accurate token counting for Claude models - return len(self.encoder.encode(patch, disallowed_special=())) \ No newline at end of file + #else: Non Anthropic provided model + + import re + model_is_from_o_series = re.match(r"^o[1-9](-mini|-preview)?$", model) + if ('gpt' in get_settings().config.model.lower() or model_is_from_o_series) and get_settings(use_context=False).get('openai.key'): + return encoder_estimate + #else: Model is neither an OpenAI, nor an Anthropic model - therefore, cannot provide an accurate token count and instead, return a higher number as best effort. + + elbow_factor = 1 + get_settings().get('config.model_token_count_estimate_factor', 0) + get_logger().warning(f"{model}'s expected token count cannot be accurately estimated. Using {elbow_factor} of encoder output as best effort estimate") + return ceil(elbow_factor * encoder_estimate) From 5bc253e1d969f97f13d718efab19fc455a91e36b Mon Sep 17 00:00:00 2001 From: Eyal Sharon Date: Mon, 24 Mar 2025 15:53:22 +0200 Subject: [PATCH 016/365] Add token count estimation factor to config --- pr_agent/settings/configuration.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index 7902546748..73e97cf40c 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -9,6 +9,7 @@ model="o3-mini" fallback_models=["gpt-4o-2024-11-20"] #model_weak="gpt-4o-mini-2024-07-18" # optional, a weaker model to use for some easier tasks +model_token_count_estimate_factor=0.3 # factor to increase the token count estimate, in order to reduce likelihood of model failure due to too many tokens. # CLI git_provider="github" publish_output=true From b161672218c5f96830c0dd084399f75ded5c3769 Mon Sep 17 00:00:00 2001 From: Eyal Sharon Date: Mon, 24 Mar 2025 15:56:45 +0200 Subject: [PATCH 017/365] Remove redundant abstractmethod for _prepare_clone_url_with_token --- pr_agent/git_providers/git_provider.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pr_agent/git_providers/git_provider.py b/pr_agent/git_providers/git_provider.py index e5afe42bd6..0228955e07 100644 --- a/pr_agent/git_providers/git_provider.py +++ b/pr_agent/git_providers/git_provider.py @@ -45,7 +45,6 @@ def __del__(self): if self.path and os.path.exists(self.path): shutil.rmtree(self.path, ignore_errors=True) - @abstractmethod #Method to allow implementors to manipulate the repo url to clone (such as embedding tokens in the url string). Needs to be implemented by the provider. def _prepare_clone_url_with_token(self, repo_url_to_clone: str) -> str | None: get_logger().warning("Not implemented! Returning None") From c94aa58ae4f6c8ba88d1ec71d163a861c4ac14fd Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Tue, 25 Mar 2025 09:23:30 +0200 Subject: [PATCH 018/365] Fix typo in Qodo Merge models documentation --- docs/docs/usage-guide/qodo_merge_models.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/usage-guide/qodo_merge_models.md b/docs/docs/usage-guide/qodo_merge_models.md index 8a83dfe45c..2c48dd0a8b 100644 --- a/docs/docs/usage-guide/qodo_merge_models.md +++ b/docs/docs/usage-guide/qodo_merge_models.md @@ -1,5 +1,5 @@ -The default models used by Qodo Merge (March 2025) is Claude Sonnet 3.7. +The default model used by Qodo Merge (March 2025) is Claude Sonnet 3.7. ### Selecting a Specific Model From 605eef64e738e45afe14b277da8b464f80a60deb Mon Sep 17 00:00:00 2001 From: Eyal Sharon Date: Tue, 25 Mar 2025 12:08:00 +0200 Subject: [PATCH 019/365] Generate git url correctly in case invoked from both user and GitHub app contexts --- pr_agent/git_providers/github_provider.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pr_agent/git_providers/github_provider.py b/pr_agent/git_providers/github_provider.py index 2312641170..feee84a0f1 100644 --- a/pr_agent/git_providers/github_provider.py +++ b/pr_agent/git_providers/github_provider.py @@ -82,11 +82,11 @@ def _get_owner_and_repo_path(self, given_url: str) -> str: return "" def get_git_repo_url(self, issues_or_pr_url: str) -> str: - repo_path = self._get_owner_and_repo_path(issues_or_pr_url) + repo_path = self._get_owner_and_repo_path(issues_or_pr_url) #Return: / if not repo_path or repo_path not in issues_or_pr_url: get_logger().error(f"Unable to retrieve owner/path from url: {issues_or_pr_url}") return "" - return f"{issues_or_pr_url.split(repo_path)[0]}{repo_path}.git" + return f"{self.base_url_html}/{repo_path}.git" #https://github.com / /.git # Given a git repo url, return prefix and suffix of the provider in order to view a given file belonging to that repo. # Example: https://github.com/qodo-ai/pr-agent.git and branch: v0.8 -> prefix: "https://github.com/qodo-ai/pr-agent/blob/v0.8", suffix: "" @@ -109,7 +109,7 @@ def get_canonical_url_parts(self, repo_git_url:str, desired_branch:str) -> Tuple owner, repo = self.repo.split('/') scheme_and_netloc = self.base_url_html desired_branch = self.get_pr_branch() - if not any([scheme_and_netloc, owner, repo]): #"else": Not invoked from a PR context,but no provided git url for context + if not all([scheme_and_netloc, owner, repo]): #"else": Not invoked from a PR context,but no provided git url for context get_logger().error(f"Unable to get canonical url parts since missing context (PR or explicit git url)") return ("", "") From b0ed5848210b347c14dcd2837906c5b483f678da Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Tue, 25 Mar 2025 12:27:36 +0200 Subject: [PATCH 020/365] Update company codebase documentation link and rename file --- .../docs/core-abilities/code_oriented_yaml.md | 2 - docs/docs/core-abilities/company_codebase.md | 53 +++++++++++++++++++ docs/docs/core-abilities/index.md | 2 +- docs/mkdocs.yml | 2 +- 4 files changed, 55 insertions(+), 4 deletions(-) delete mode 100644 docs/docs/core-abilities/code_oriented_yaml.md create mode 100644 docs/docs/core-abilities/company_codebase.md diff --git a/docs/docs/core-abilities/code_oriented_yaml.md b/docs/docs/core-abilities/code_oriented_yaml.md deleted file mode 100644 index e65f836649..0000000000 --- a/docs/docs/core-abilities/code_oriented_yaml.md +++ /dev/null @@ -1,2 +0,0 @@ -## Overview -TBD diff --git a/docs/docs/core-abilities/company_codebase.md b/docs/docs/core-abilities/company_codebase.md new file mode 100644 index 0000000000..480db2884c --- /dev/null +++ b/docs/docs/core-abilities/company_codebase.md @@ -0,0 +1,53 @@ +# Company Codebase 💎 +`Supported Git Platforms: GitHub` + + +## Overview + +### What is Company Codebase? + +An organized, semantic database that aggregates all your company’s source code into one searchable repository, enabling efficient code discovery and analysis. + +### How does Company Codebase work? + +By indexing your company's code and using Retrieval-Augmented Generation (RAG), it retrieves contextual code segments on demand, improving pull request (PR) insights and accelerating review accuracy. + + +## Getting started + +!!! info "Prerequisites" + - Database setup and codebase indexing must be completed before proceeding. [Contact support](https://www.qodo.ai/contact/) for assistance. + +### Configuration options + +In order to enable the RAG feature, add the following lines to your configuration file: +``` +[rag_arguments] +enable_rag=true +``` + +!!! example "RAG Arguments Options" + + + + + + + + + + +
enable_ragIf set to true, codebase enrichment using RAG will be enabled. Default is false.
rag_repo_listA list of repositories that will be used by the semantic search for RAG. Use `['all']` to consider the entire codebase or a select list or repositories, for example: ['my-org/my-repo', ...]. Default: the repository from which the PR was opened.
+ + +## Limitations + +### Querying the codebase presents significant challenges: +- **Search Method**: RAG uses natural language queries to find semantically relevant code sections +- **Result Quality**: No guarantee that RAG results will be useful for all queries +- **Scope Recommendation**: To reduce noise, avoid using the whole codebase; focus on PR repository instead + +### This feature has several requirements and restrictions: +- **Codebase**: Must be properly indexed for search functionality +- **Security**: Requires secure and private indexed codebase implementation +- **Deployment**: Only available for Qodo Merge Enterprise plan using single tenant or on-premises setup diff --git a/docs/docs/core-abilities/index.md b/docs/docs/core-abilities/index.md index be61cd4c0d..ba431bc4f4 100644 --- a/docs/docs/core-abilities/index.md +++ b/docs/docs/core-abilities/index.md @@ -9,7 +9,7 @@ Qodo Merge utilizes a variety of core abilities to provide a comprehensive and e - [Impact evaluation](https://qodo-merge-docs.qodo.ai/core-abilities/impact_evaluation/) - [Interactivity](https://qodo-merge-docs.qodo.ai/core-abilities/interactivity/) - [Compression strategy](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) -- [Code-oriented YAML](https://qodo-merge-docs.qodo.ai/core-abilities/code_oriented_yaml/) +- [Company Codebase](https://qodo-merge-docs.qodo.ai/core-abilities/company_codebase/) - [Static code analysis](https://qodo-merge-docs.qodo.ai/core-abilities/static_code_analysis/) - [Code fine-tuning benchmark](https://qodo-merge-docs.qodo.ai/finetuning_benchmark/) diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 86a8b07d65..aa1a075896 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -49,7 +49,7 @@ nav: - Impact evaluation: 'core-abilities/impact_evaluation.md' - Interactivity: 'core-abilities/interactivity.md' - Compression strategy: 'core-abilities/compression_strategy.md' - - Code-oriented YAML: 'core-abilities/code_oriented_yaml.md' + - Company Codebase: 'core-abilities/company_codebase.md' - Static code analysis: 'core-abilities/static_code_analysis.md' - Code Fine-tuning Benchmark: 'finetuning_benchmark/index.md' - Chrome Extension: From d6f79f486aead730582300bbbf09f45e6f904181 Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Tue, 25 Mar 2025 12:42:11 +0200 Subject: [PATCH 021/365] Update code block syntax highlighting in company_codebase documentation --- docs/docs/core-abilities/company_codebase.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/core-abilities/company_codebase.md b/docs/docs/core-abilities/company_codebase.md index 480db2884c..6a746e82ad 100644 --- a/docs/docs/core-abilities/company_codebase.md +++ b/docs/docs/core-abilities/company_codebase.md @@ -21,7 +21,7 @@ By indexing your company's code and using Retrieval-Augmented Generation (RAG), ### Configuration options In order to enable the RAG feature, add the following lines to your configuration file: -``` +``` toml [rag_arguments] enable_rag=true ``` From 9a210690754e8133009189efa39eafda79841580 Mon Sep 17 00:00:00 2001 From: "Hussam.lawen" Date: Tue, 25 Mar 2025 13:53:22 +0200 Subject: [PATCH 022/365] docs: update configuration options and improve wiki documentation clarity --- docs/docs/tools/improve.md | 10 +++++----- docs/docs/usage-guide/configuration_options.md | 3 ++- docs/docs/usage-guide/enabling_a_wiki.md | 3 +-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/docs/tools/improve.md b/docs/docs/tools/improve.md index 2e7f0d76dc..45d7d5ca33 100644 --- a/docs/docs/tools/improve.md +++ b/docs/docs/tools/improve.md @@ -122,10 +122,10 @@ Use triple quotes to write multi-line instructions. Use bullet points or numbers > `💎 feature. Platforms supported: GitHub, GitLab, Bitbucket` -Another option to give additional guidance to the AI model is by creating a `best_practices.md` file, either in your repository's root directory or as a [**wiki page**](https://github.com/Codium-ai/pr-agent/wiki) (we recommend the wiki page, as editing and maintaining it over time is easier). +Another option to give additional guidance to the AI model is by creating a `best_practices.md` file in your repository's root directory. This page can contain a list of best practices, coding standards, and guidelines that are specific to your repo/organization. -The AI model will use this wiki page as a reference, and in case the PR code violates any of the guidelines, it will create additional suggestions, with a dedicated label: `Organization +The AI model will use this `best_practices.md` file as a reference, and in case the PR code violates any of the guidelines, it will create additional suggestions, with a dedicated label: `Organization best practice`. Example for a python `best_practices.md` content: @@ -149,16 +149,16 @@ Tips for writing an effective `best_practices.md` file: - Long files tend to contain generic guidelines already known to AI #### Local and global best practices -By default, Qodo Merge will look for a local `best_practices.md` wiki file in the root of the relevant local repo. +By default, Qodo Merge will look for a local `best_practices.md` in the root of the relevant local repo. -If you want to enable also a global `best_practices.md` wiki file, set first in the global configuration file: +If you want to enable also a global `best_practices.md` file, set first in the global configuration file: ```toml [best_practices] enable_global_best_practices = true ``` -Then, create a `best_practices.md` wiki file in the root of [global](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/#global-configuration-file) configuration repository, `pr-agent-settings`. +Then, create a `best_practices.md` file in the root of [global](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/#global-configuration-file) configuration repository, `pr-agent-settings`. #### Best practices for multiple languages For a git organization working with multiple programming languages, you can maintain a centralized global `best_practices.md` file containing language-specific guidelines. diff --git a/docs/docs/usage-guide/configuration_options.md b/docs/docs/usage-guide/configuration_options.md index 1d671a6102..4030fb9970 100644 --- a/docs/docs/usage-guide/configuration_options.md +++ b/docs/docs/usage-guide/configuration_options.md @@ -87,7 +87,8 @@ Create a dedicated project to hold a global configuration file that affects all 1. Create a new project with both the name and key: PR_AGENT_SETTINGS. 2. Inside the PR_AGENT_SETTINGS project, create a repository named pr-agent-settings. -3. In this repository, add a .pr_agent.toml configuration file—structured similarly to the global configuration file described above. +3. In this repository, add a `.pr_agent.toml` configuration file—structured similarly to the global configuration file described above. +4. Optionally, you can add organizational-level [global best practices file](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/#global-configuration-file). Repositories across your entire Bitbucket organization will inherit the configuration from this file. diff --git a/docs/docs/usage-guide/enabling_a_wiki.md b/docs/docs/usage-guide/enabling_a_wiki.md index ff82327fb6..3d1c7af1e9 100644 --- a/docs/docs/usage-guide/enabling_a_wiki.md +++ b/docs/docs/usage-guide/enabling_a_wiki.md @@ -6,7 +6,6 @@ For optimal functionality of Qodo Merge, we recommend enabling a wiki for each r **Key Wiki Features: 💎** - Storing a [configuration file](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/#wiki-configuration-file) -- Defining a [`best_practices.md`](https://qodo-merge-docs.qodo.ai/tools/improve/#best-practices) file - Track [accepted suggestions](https://qodo-merge-docs.qodo.ai/tools/improve/#suggestion-tracking) - Facilitates learning over time by creating an [auto_best_practices.md](https://qodo-merge-docs.qodo.ai/core-abilities/auto_best_practices) file @@ -26,7 +25,7 @@ To enable a wiki for your repository: ### Why Wiki? - Your code (and its derivatives, including accepted code suggestions) is yours. Qodo Merge will never store it on external servers. -- Repository changes typically require pull requests, which create overhead and are time-consuming. This process is too cumbersome for auto data aggregation, and is not very convenient even for managing frequently updated content like configuration files and best practices. +- Repository changes typically require pull requests, which create overhead and are time-consuming. This process is too cumbersome for auto data aggregation, and is not very convenient even for managing frequently updated content like configuration files. - A repository wiki page provides an ideal balance: - It lives within your repository, making it suitable for code-related documentation - It enables quick updates without the overhead of pull requests From b300cfa84da1d31b358043ddc1135f1039d5fe96 Mon Sep 17 00:00:00 2001 From: Ishaan Sehgal Date: Tue, 25 Mar 2025 22:49:24 -0700 Subject: [PATCH 023/365] Improve error logging with artifacts --- pr_agent/servers/github_app.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pr_agent/servers/github_app.py b/pr_agent/servers/github_app.py index 09a521323e..1e753a9581 100644 --- a/pr_agent/servers/github_app.py +++ b/pr_agent/servers/github_app.py @@ -64,7 +64,7 @@ async def get_body(request): try: body = await request.json() except Exception as e: - get_logger().error("Error parsing request body", e) + get_logger().error("Error parsing request body", artifact={'error': e}) raise HTTPException(status_code=400, detail="Error parsing request body") from e webhook_secret = getattr(get_settings().github, 'webhook_secret', None) if webhook_secret: @@ -107,7 +107,7 @@ async def handle_comments_on_pr(body: Dict[str, Any], comment_body = handle_line_comments(body, comment_body) disable_eyes = True except Exception as e: - get_logger().error(f"Failed to handle line comments: {e}") + get_logger().error("Failed to get log context", artifact={'error': e}) else: return {} log_context["api_url"] = api_url @@ -233,7 +233,7 @@ def get_log_context(body, event, action, build_number): "request_id": uuid.uuid4().hex, "build_number": build_number, "app_name": app_name, "repo": repo, "git_org": git_org, "installation_id": installation_id} except Exception as e: - get_logger().error(f"Failed to get log context: {e}") + get_logger().error(f"Failed to get log context", artifact={'error': e}) log_context = {} return log_context, sender, sender_id, sender_type From 7aa3d1287665c5447480ac35f37733bc03c081c2 Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Wed, 26 Mar 2025 08:41:56 +0200 Subject: [PATCH 024/365] Add codebase references screenshot to company_codebase documentation --- docs/docs/core-abilities/company_codebase.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/docs/core-abilities/company_codebase.md b/docs/docs/core-abilities/company_codebase.md index 6a746e82ad..8f09586a67 100644 --- a/docs/docs/core-abilities/company_codebase.md +++ b/docs/docs/core-abilities/company_codebase.md @@ -40,6 +40,9 @@ enable_rag=true +References from the codebase will be shown in a collapsible bookmark, allowing you to easily access relevant code snippets: +![implement2](https://codium.ai/images/pr_agent/company_codebase_references.png){width=640} + ## Limitations ### Querying the codebase presents significant challenges: From 1d57dd744382c66606fa5261b86c8dc88fefcb94 Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Wed, 26 Mar 2025 08:44:11 +0200 Subject: [PATCH 025/365] Update image alt text in company_codebase documentation --- docs/docs/core-abilities/company_codebase.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/core-abilities/company_codebase.md b/docs/docs/core-abilities/company_codebase.md index 8f09586a67..1496ca0aad 100644 --- a/docs/docs/core-abilities/company_codebase.md +++ b/docs/docs/core-abilities/company_codebase.md @@ -41,7 +41,7 @@ enable_rag=true References from the codebase will be shown in a collapsible bookmark, allowing you to easily access relevant code snippets: -![implement2](https://codium.ai/images/pr_agent/company_codebase_references.png){width=640} +![References](https://codium.ai/images/pr_agent/company_codebase_references.png){width=640} ## Limitations From 351b9d911547d7fa6f1aff07b3134442d6b1e2c8 Mon Sep 17 00:00:00 2001 From: ofir-frd <85901822+ofir-frd@users.noreply.github.com> Date: Wed, 26 Mar 2025 09:47:59 +0200 Subject: [PATCH 026/365] Update company_codebase.md --- docs/docs/core-abilities/company_codebase.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/core-abilities/company_codebase.md b/docs/docs/core-abilities/company_codebase.md index 1496ca0aad..9d94775cea 100644 --- a/docs/docs/core-abilities/company_codebase.md +++ b/docs/docs/core-abilities/company_codebase.md @@ -34,8 +34,8 @@ enable_rag=true If set to true, codebase enrichment using RAG will be enabled. Default is false. - rag_repo_list - A list of repositories that will be used by the semantic search for RAG. Use `['all']` to consider the entire codebase or a select list or repositories, for example: ['my-org/my-repo', ...]. Default: the repository from which the PR was opened. + rag_repo_list + A list of repositories that will be used by the semantic search for RAG. Use `['all']` to consider the entire codebase or a select list or repositories, for example: ['my-org/my-repo', ...]. Default: the repository from which the PR was opened. From 839b6093cbc3b859469659b0ddfec4f7b4ab477e Mon Sep 17 00:00:00 2001 From: ofir-frd <85901822+ofir-frd@users.noreply.github.com> Date: Wed, 26 Mar 2025 09:50:01 +0200 Subject: [PATCH 027/365] Update company_codebase.md --- docs/docs/core-abilities/company_codebase.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/docs/core-abilities/company_codebase.md b/docs/docs/core-abilities/company_codebase.md index 9d94775cea..d22e05e1ca 100644 --- a/docs/docs/core-abilities/company_codebase.md +++ b/docs/docs/core-abilities/company_codebase.md @@ -41,6 +41,7 @@ enable_rag=true References from the codebase will be shown in a collapsible bookmark, allowing you to easily access relevant code snippets: + ![References](https://codium.ai/images/pr_agent/company_codebase_references.png){width=640} ## Limitations From 7117e9fe0e0abd22d58afa0e1d17f766c003206f Mon Sep 17 00:00:00 2001 From: Eyal Sharon Date: Wed, 26 Mar 2025 13:36:37 +0200 Subject: [PATCH 028/365] Github Provider: Support publishing a comment on a non pr issue --- pr_agent/git_providers/github_provider.py | 38 ++++++++++++++++++++--- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/pr_agent/git_providers/github_provider.py b/pr_agent/git_providers/github_provider.py index feee84a0f1..0c5992863d 100644 --- a/pr_agent/git_providers/github_provider.py +++ b/pr_agent/git_providers/github_provider.py @@ -10,6 +10,7 @@ from typing import Optional, Tuple from urllib.parse import urlparse +from github.Issue import Issue from github import AppAuthentication, Auth, Github, GithubException from retry import retry from starlette_context import context @@ -51,9 +52,29 @@ def __init__(self, pr_url: Optional[str] = None): self.pr_commits = list(self.pr.get_commits()) self.last_commit_id = self.pr_commits[-1] self.pr_url = self.get_pr_url() # pr_url for github actions can be as api.github.com, so we need to get the url from the pr object - else: + elif pr_url and 'issue' in pr_url: #url is an issue + self.issue_main = self._get_issue_handle(pr_url) + else: #Instantiated the provider without a PR / Issue self.pr_commits = None + def _get_issue_handle(self, issue_url) -> Optional[Issue]: + repo_name, issue_number = self._parse_issue_url(issue_url) + if not repo_name or not issue_number: + get_logger().error(f"Given url: {issue_url} is not a valid issue.") + return None + # else: Check if can get a valid Repo handle: + try: + repo_obj = self.github_client.get_repo(repo_name) + if not repo_obj: + get_logger().error(f"Given url: {issue_url}, belonging to owner/repo: {repo_name} does " + f"not have a valid repository: {self.get_git_repo_url(issue_url)}") + return None + # else: Valid repo handle: + return repo_obj.get_issue(issue_number) + except Exception as e: + get_logger().exception(f"Failed to get an issue object for issue: {issue_url}, belonging to owner/repo: {repo_name}") + return None + def get_incremental_commits(self, incremental=IncrementalPR(False)): self.incremental = incremental if self.incremental.is_incremental: @@ -344,10 +365,19 @@ def publish_persistent_comment(self, pr_comment: str, self.publish_persistent_comment_full(pr_comment, initial_header, update_header, name, final_update_message) def publish_comment(self, pr_comment: str, is_temporary: bool = False): + if not self.pr and not self.issue_main: + get_logger().error("Cannot publish a comment if missing PR/Issue context") + return None + if is_temporary and not get_settings().config.publish_output_progress: get_logger().debug(f"Skipping publish_comment for temporary comment: {pr_comment}") return None pr_comment = self.limit_output_characters(pr_comment, self.max_comment_chars) + + # In case this is an issue, can publish the comment on the issue. + if self.issue_main: + return self.issue_main.create_comment(pr_comment) + response = self.pr.create_issue_comment(pr_comment) if hasattr(response, "user") and hasattr(response.user, "login"): self.github_user_id = response.user.login @@ -731,11 +761,11 @@ def _parse_pr_url(self, pr_url: str) -> Tuple[str, int]: def _parse_issue_url(self, issue_url: str) -> Tuple[str, int]: parsed_url = urlparse(issue_url) - if 'github.com' not in parsed_url.netloc: - raise ValueError("The provided URL is not a valid GitHub URL") + if parsed_url.path.startswith('/api/v3'): #Check if came from github app + parsed_url = urlparse(issue_url.replace("/api/v3", "")) path_parts = parsed_url.path.strip('/').split('/') - if 'api.github.com' in parsed_url.netloc: + if 'api.github.com' in parsed_url.netloc or '/api/v3' in issue_url: #Check if came from github app if len(path_parts) < 5 or path_parts[3] != 'issues': raise ValueError("The provided URL does not appear to be a GitHub ISSUE URL") repo_name = '/'.join(path_parts[1:3]) From 255e1d0fc19e065c8a098f8c335a511fc3cea391 Mon Sep 17 00:00:00 2001 From: Eyal Sharon Date: Wed, 26 Mar 2025 14:29:50 +0200 Subject: [PATCH 029/365] Missing class member --- pr_agent/git_providers/github_provider.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pr_agent/git_providers/github_provider.py b/pr_agent/git_providers/github_provider.py index 0c5992863d..3a6264ebbc 100644 --- a/pr_agent/git_providers/github_provider.py +++ b/pr_agent/git_providers/github_provider.py @@ -43,6 +43,7 @@ def __init__(self, pr_url: Optional[str] = None): self.repo = None self.pr_num = None self.pr = None + self.issue_main = None self.github_user_id = None self.diff_files = None self.git_files = None From fcd94161292aa198b0ddf60f446e2b340de3928e Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 26 Mar 2025 20:06:32 +0200 Subject: [PATCH 030/365] Replace Qodo Merge references with PR-Agent in Bitbucket documentation --- docs/docs/installation/bitbucket.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/docs/installation/bitbucket.md b/docs/docs/installation/bitbucket.md index 39ce54f147..a15da9f1ec 100644 --- a/docs/docs/installation/bitbucket.md +++ b/docs/docs/installation/bitbucket.md @@ -1,7 +1,7 @@ ## Run as a Bitbucket Pipeline -You can use the Bitbucket Pipeline system to run Qodo Merge on every pull request open or update. +You can use the Bitbucket Pipeline system to run PR-Agent on every pull request open or update. 1. Add the following file in your repository bitbucket-pipelines.yml @@ -11,7 +11,7 @@ pipelines: '**': - step: name: PR Agent Review - image: python:3.10 + image: python:3.12 services: - docker script: @@ -54,7 +54,7 @@ python cli.py --pr_url https://git.onpreminstanceofbitbucket.com/projects/PROJEC ### Run it as service -To run Qodo Merge as webhook, build the docker image: +To run PR-Agent as webhook, build the docker image: ``` docker build . -t codiumai/pr-agent:bitbucket_server_webhook --target bitbucket_server_webhook -f docker/Dockerfile docker push codiumai/pr-agent:bitbucket_server_webhook # Push to your Docker repository From 991a866368537d68bfda58f4f005b4f7c6ae97d4 Mon Sep 17 00:00:00 2001 From: Eyal Sharon Date: Wed, 26 Mar 2025 21:58:15 +0200 Subject: [PATCH 031/365] documents' link generation: Make sure prefix ends with '/' --- pr_agent/tools/pr_help_docs.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pr_agent/tools/pr_help_docs.py b/pr_agent/tools/pr_help_docs.py index 5695065a82..ddd4250973 100644 --- a/pr_agent/tools/pr_help_docs.py +++ b/pr_agent/tools/pr_help_docs.py @@ -101,6 +101,7 @@ def aggregate_documentation_files_for_prompt_contents(base_path: str, doc_files: def format_markdown_q_and_a_response(question_str: str, response_str: str, relevant_sections: List[Dict[str, str]], supported_suffixes: List[str], base_url_prefix: str, base_url_suffix: str="") -> str: + base_url_prefix = base_url_prefix.strip('/') #Sanitize base_url_prefix answer_str = "" answer_str += f"### Question: \n{question_str}\n\n" answer_str += f"### Answer:\n{response_str.strip()}\n\n" @@ -114,9 +115,9 @@ def format_markdown_q_and_a_response(question_str: str, response_str: str, relev if str(section['relevant_section_header_string']).strip(): markdown_header = format_markdown_header(section['relevant_section_header_string']) if base_url_prefix: - answer_str += f"> - {base_url_prefix}{file}{base_url_suffix}#{markdown_header}\n" + answer_str += f"> - {base_url_prefix}/{file}{base_url_suffix}#{markdown_header}\n" else: - answer_str += f"> - {base_url_prefix}{file}{base_url_suffix}\n" + answer_str += f"> - {base_url_prefix}/{file}{base_url_suffix}\n" return answer_str def format_markdown_header(header: str) -> str: From 66a667d509b8fa238d3756d50fc52b4048cefbd4 Mon Sep 17 00:00:00 2001 From: sharoneyal Date: Thu, 27 Mar 2025 16:23:50 +0200 Subject: [PATCH 032/365] Update main readme file + improve help docs (#1653) * Update main readme file + improve help docs * Add guide on running help_docs as a GitHub action. --- README.md | 47 +++++++++++++------------ docs/docs/tools/help_docs.md | 68 +++++++++++++++++++++++++++++++++--- 2 files changed, 89 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index ddce1fa6e1..7a5506b41d 100644 --- a/README.md +++ b/README.md @@ -97,18 +97,19 @@ Supported commands per platform: | | | GitHub | GitLab | Bitbucket | Azure DevOps | |-------|---------------------------------------------------------------------------------------------------------|:--------------------:|:--------------------:|:---------:|:------------:| -| TOOLS | [Review](https://qodo-merge-docs.qodo.ai/tools/review/) | ✅ | ✅ | ✅ | ✅ | -| | [Describe](https://qodo-merge-docs.qodo.ai/tools/describe/) | ✅ | ✅ | ✅ | ✅ | -| | [Improve](https://qodo-merge-docs.qodo.ai/tools/improve/) | ✅ | ✅ | ✅ | ✅ | -| | [Ask](https://qodo-merge-docs.qodo.ai/tools/ask/) | ✅ | ✅ | ✅ | ✅ | -| | ⮑ [Ask on code lines](https://qodo-merge-docs.qodo.ai/tools/ask/#ask-lines) | ✅ | ✅ | | | -| | [Update CHANGELOG](https://qodo-merge-docs.qodo.ai/tools/update_changelog/) | ✅ | ✅ | ✅ | ✅ | -| | [Ticket Context](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/) 💎 | ✅ | ✅ | ✅ | | -| | [Utilizing Best Practices](https://qodo-merge-docs.qodo.ai/tools/improve/#best-practices) 💎 | ✅ | ✅ | ✅ | | -| | [PR Chat](https://qodo-merge-docs.qodo.ai/chrome-extension/features/#pr-chat) 💎 | ✅ | | | | -| | [Suggestion Tracking](https://qodo-merge-docs.qodo.ai/tools/improve/#suggestion-tracking) 💎 | ✅ | ✅ | | | +| TOOLS | [Review](https://qodo-merge-docs.qodo.ai/tools/review/) | ✅ | ✅ | ✅ | ✅ | +| | [Describe](https://qodo-merge-docs.qodo.ai/tools/describe/) | ✅ | ✅ | ✅ | ✅ | +| | [Improve](https://qodo-merge-docs.qodo.ai/tools/improve/) | ✅ | ✅ | ✅ | ✅ | +| | [Ask](https://qodo-merge-docs.qodo.ai/tools/ask/) | ✅ | ✅ | ✅ | ✅ | +| | ⮑ [Ask on code lines](https://qodo-merge-docs.qodo.ai/tools/ask/#ask-lines) | ✅ | ✅ | | | +| | [Update CHANGELOG](https://qodo-merge-docs.qodo.ai/tools/update_changelog/) | ✅ | ✅ | ✅ | ✅ | +| | [Help Docs](https://qodo-merge-docs.qodo.ai/tools/help_docs/?h=auto#auto-approval) | ✅ | ✅ | ✅ | | +| | [Ticket Context](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/) 💎 | ✅ | ✅ | ✅ | | +| | [Utilizing Best Practices](https://qodo-merge-docs.qodo.ai/tools/improve/#best-practices) 💎 | ✅ | ✅ | ✅ | | +| | [PR Chat](https://qodo-merge-docs.qodo.ai/chrome-extension/features/#pr-chat) 💎 | ✅ | | | | +| | [Suggestion Tracking](https://qodo-merge-docs.qodo.ai/tools/improve/#suggestion-tracking) 💎 | ✅ | ✅ | | | | | [CI Feedback](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/) 💎 | ✅ | | | | -| | [PR Documentation](https://qodo-merge-docs.qodo.ai/tools/documentation/) 💎 | ✅ | ✅ | | | +| | [PR Documentation](https://qodo-merge-docs.qodo.ai/tools/documentation/) 💎 | ✅ | ✅ | | | | | [Custom Labels](https://qodo-merge-docs.qodo.ai/tools/custom_labels/) 💎 | ✅ | ✅ | | | | | [Analyze](https://qodo-merge-docs.qodo.ai/tools/analyze/) 💎 | ✅ | ✅ | | | | | [Similar Code](https://qodo-merge-docs.qodo.ai/tools/similar_code/) 💎 | ✅ | | | | @@ -117,21 +118,21 @@ Supported commands per platform: | | [Implement](https://qodo-merge-docs.qodo.ai/tools/implement/) 💎 | ✅ | ✅ | ✅ | | | | [Auto-Approve](https://qodo-merge-docs.qodo.ai/tools/improve/?h=auto#auto-approval) 💎 | ✅ | ✅ | ✅ | | | | | | | | | -| USAGE | [CLI](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#local-repo-cli) | ✅ | ✅ | ✅ | ✅ | -| | [App / webhook](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#github-app) | ✅ | ✅ | ✅ | ✅ | -| | [Tagging bot](https://github.com/Codium-ai/pr-agent#try-it-now) | ✅ | | | | -| | [Actions](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-action) | ✅ |✅| ✅ |✅| +| USAGE | [CLI](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#local-repo-cli) | ✅ | ✅ | ✅ | ✅ | +| | [App / webhook](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#github-app) | ✅ | ✅ | ✅ | ✅ | +| | [Tagging bot](https://github.com/Codium-ai/pr-agent#try-it-now) | ✅ | | | | +| | [Actions](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-action) | ✅ |✅| ✅ |✅| | | | | | | | -| CORE | [PR compression](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) | ✅ | ✅ | ✅ | ✅ | +| CORE | [PR compression](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) | ✅ | ✅ | ✅ | ✅ | | | Adaptive and token-aware file patch fitting | ✅ | ✅ | ✅ | ✅ | -| | [Multiple models support](https://qodo-merge-docs.qodo.ai/usage-guide/changing_a_model/) | ✅ | ✅ | ✅ | ✅ | -| | [Local and global metadata](https://qodo-merge-docs.qodo.ai/core-abilities/metadata/) | ✅ | ✅ | ✅ | ✅ | -| | [Dynamic context](https://qodo-merge-docs.qodo.ai/core-abilities/dynamic_context/) | ✅ | ✅ | ✅ | ✅ | -| | [Self reflection](https://qodo-merge-docs.qodo.ai/core-abilities/self_reflection/) | ✅ | ✅ | ✅ | ✅ | +| | [Multiple models support](https://qodo-merge-docs.qodo.ai/usage-guide/changing_a_model/) | ✅ | ✅ | ✅ | ✅ | +| | [Local and global metadata](https://qodo-merge-docs.qodo.ai/core-abilities/metadata/) | ✅ | ✅ | ✅ | ✅ | +| | [Dynamic context](https://qodo-merge-docs.qodo.ai/core-abilities/dynamic_context/) | ✅ | ✅ | ✅ | ✅ | +| | [Self reflection](https://qodo-merge-docs.qodo.ai/core-abilities/self_reflection/) | ✅ | ✅ | ✅ | ✅ | | | [Static code analysis](https://qodo-merge-docs.qodo.ai/core-abilities/static_code_analysis/) 💎 | ✅ | ✅ | | | | | [Global and wiki configurations](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/) 💎 | ✅ | ✅ | ✅ | | -| | [PR interactive actions](https://www.qodo.ai/images/pr_agent/pr-actions.mp4) 💎 | ✅ | ✅ | | | -| | [Impact Evaluation](https://qodo-merge-docs.qodo.ai/core-abilities/impact_evaluation/) 💎 | ✅ | ✅ | | | +| | [PR interactive actions](https://www.qodo.ai/images/pr_agent/pr-actions.mp4) 💎 | ✅ | ✅ | | | +| | [Impact Evaluation](https://qodo-merge-docs.qodo.ai/core-abilities/impact_evaluation/) 💎 | ✅ | ✅ | | | - 💎 means this feature is available only in [Qodo-Merge](https://www.qodo.ai/pricing/) [//]: # (- Support for additional git providers is described in [here](./docs/Full_environments.md)) @@ -147,6 +148,8 @@ ___ \ ‣ **Update Changelog ([`/update_changelog`](https://qodo-merge-docs.qodo.ai/tools/update_changelog/))**: Automatically updating the CHANGELOG.md file with the PR changes. \ +‣ **Help Docs ([`/help_docs`](https://qodo-merge-docs.qodo.ai/tools/help_docs/))**: Answers a question on any repository by utilizing given documentation. +\ ‣ **Add Documentation 💎 ([`/add_docs`](https://qodo-merge-docs.qodo.ai/tools/documentation/))**: Generates documentation to methods/functions/classes that changed in the PR. \ ‣ **Generate Custom Labels 💎 ([`/generate_labels`](https://qodo-merge-docs.qodo.ai/tools/custom_labels/))**: Generates custom labels for the PR, based on specific guidelines defined by the user. diff --git a/docs/docs/tools/help_docs.md b/docs/docs/tools/help_docs.md index 872affed9f..ba85915510 100644 --- a/docs/docs/tools/help_docs.md +++ b/docs/docs/tools/help_docs.md @@ -1,25 +1,85 @@ ## Overview -The `help_docs` tool answers a question based on a given relative path of documentation, either from the repository of this merge request or from a given one. -It can be invoked manually by commenting on any PR: +The `help_docs` tool answers a question based on a given relative path of documentation (which defaults to docs/), either from the repository of this merge request or from a given one. +It can be invoked manually by commenting on any PR or Issue: ``` /help_docs "..." ``` ## Example usage - +#### Asking a question about this repository: ![help_docs on the documentation of this repository](https://codium.ai/images/pr_agent/help_docs_comment.png){width=512} +#### Asking a question about another repository under branch: main: ![help_docs on the documentation of another repository](https://codium.ai/images/pr_agent/help_docs_comment_explicit_git.png){width=512} +#### Response for the first question: ![help_docs response](https://codium.ai/images/pr_agent/help_docs_response.png){width=512} ## Configuration options -Under the section `--pr_help_docs`, the [configuration file](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml#L50) contains options to customize the 'help docs' tool: +Under the section `pr_help_docs`, the [configuration file](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml#L50) contains options to customize the 'help docs' tool: - `repo_url`: If not overwritten, will use the repo from where the context came from (issue or PR), otherwise - use the given repo as context. - `repo_default_branch`: The branch to use in case repo_url overwritten, otherwise - has no effect. - `docs_path`: Relative path from root of repository (either the one this PR has been issued for, or above repo url). - `exclude_root_readme`: Whether or not to exclude the root README file for querying the model. - `supported_doc_exts` : Which file extensions should be included for the purpose of querying the model. + +--- +## Run as a GitHub Action + +You can use our pre-built Github Action Docker image to run help_docs on any newly created issue, as a Github Action. Here's how: + +1) Follow the steps depicted under [Run as a Github Action](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-action) to create a new workflow, such as:`.github/workflows/help_docs.yml`: + +2) Edit your yaml file to the following: + +```yaml +name: Run pr agent on every opened issue, respond to user comments on an issue + +#When the action is triggered +on: + issues: + types: [opened] #New issue + +# Read env. variables +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_API_URL: ${{ github.api_url }} + GIT_REPO_URL: ${{ github.event.repository.clone_url }} + ISSUE_URL: ${{ github.event.issue.html_url || github.event.comment.html_url }} + ISSUE_BODY: ${{ github.event.issue.body || github.event.comment.body }} + OPENAI_KEY: ${{ secrets.OPENAI_KEY }} + +# The actual set of actions +jobs: + issue_agent: + runs-on: ubuntu-latest + if: ${{ github.event.sender.type != 'Bot' }} #Do not respond to bots + + # Set required permissions + permissions: + contents: read # For reading repository contents + issues: write # For commenting on issues + + steps: + - name: Run PR Agent on Issues + if: ${{ env.ISSUE_URL != '' }} + uses: docker://codiumai/pr-agent:latest + with: + entrypoint: /bin/bash #Replace invoking cli.py directly with a shell + args: | + -c "cd /app && \ + echo 'Running Issue Agent action step on ISSUE_URL=$ISSUE_URL' && \ + export config__git_provider='github' && \ + export github__user_token=$GITHUB_TOKEN && \ + export github__base_url=$GITHUB_API_URL && \ + export openai__key=$OPENAI_KEY && \ + export config__verbosity_level=2 && \ + python -m pr_agent.cli --issue_url=$ISSUE_URL help_docs \"$ISSUE_BODY\"" +``` + +3) Following completion of the remaining steps (such as adding secrets and any env. variables), merge this change to your main branch. +When a new issue is opened, you should see a comment from `github-actions` bot with an auto response, assuming the question is a relevant one. +--- From e422f50cfe9ce8e93030631f7780e9424118cb89 Mon Sep 17 00:00:00 2001 From: Eyal Sharon Date: Thu, 27 Mar 2025 17:22:56 +0200 Subject: [PATCH 033/365] Fix for bug in get_canonical_url_parts when a new issue created, without git url provided. --- pr_agent/git_providers/github_provider.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/pr_agent/git_providers/github_provider.py b/pr_agent/git_providers/github_provider.py index 3a6264ebbc..92e256fde9 100644 --- a/pr_agent/git_providers/github_provider.py +++ b/pr_agent/git_providers/github_provider.py @@ -118,16 +118,19 @@ def get_canonical_url_parts(self, repo_git_url:str, desired_branch:str) -> Tuple repo = None scheme_and_netloc = None - if repo_git_url: #If user provided an external git url, which may be different than what this provider was initialized with, we cannot use self.repo - repo_path = self._get_owner_and_repo_path(repo_git_url) - parsed_git_url = urlparse(repo_git_url) + if repo_git_url or self.issue_main: #Either user provided an external git url, which may be different than what this provider was initialized with, or an issue: + desired_branch = desired_branch if repo_git_url else self.issue_main.repository.default_branch + html_url = repo_git_url if repo_git_url else self.issue_main.html_url + parsed_git_url = urlparse(html_url) scheme_and_netloc = parsed_git_url.scheme + "://" + parsed_git_url.netloc + repo_path = self._get_owner_and_repo_path(html_url) if repo_path.count('/') == 1: #Has to have the form / owner, repo = repo_path.split('/') else: - get_logger().error(f"Invalid repo_path: {repo_path} from repo_git_url: {repo_git_url}") + get_logger().error(f"Invalid repo_path: {repo_path} from url: {html_url}") return ("", "") - if (not owner or not repo) and self.repo: #"else" - User did not provide an external git url, use self.repo object: + + if (not owner or not repo) and self.repo: #"else" - User did not provide an external git url, or not an issue, use self.repo object owner, repo = self.repo.split('/') scheme_and_netloc = self.base_url_html desired_branch = self.get_pr_branch() From 7d57edf959473b164a185c5585e9ec9bbcb9213f Mon Sep 17 00:00:00 2001 From: mrT23 Date: Fri, 28 Mar 2025 15:22:08 +0300 Subject: [PATCH 034/365] Improve help_docs documentation with clearer structure and usage examples --- docs/docs/tools/help_docs.md | 65 ++++++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 21 deletions(-) diff --git a/docs/docs/tools/help_docs.md b/docs/docs/tools/help_docs.md index ba85915510..4ce24a94f3 100644 --- a/docs/docs/tools/help_docs.md +++ b/docs/docs/tools/help_docs.md @@ -1,35 +1,46 @@ ## Overview -The `help_docs` tool answers a question based on a given relative path of documentation (which defaults to docs/), either from the repository of this merge request or from a given one. +The `help_docs` tool can answer a free-text question based on a git documentation folder. + It can be invoked manually by commenting on any PR or Issue: ``` /help_docs "..." +``` +Or configured to be triggered automatically when a [new issue is opened](#run-as-a-github-action). + +The tool assumes by default that the documentation is located in the root of the repository, at `/docs` folder. +However, this can be customized by setting the `docs_path` configuration option: + +```toml +[pr_help_docs] +repo_url = "" # The repository to use as context +docs_path = "docs" # The documentation folder +repo_default_branch = "main" # The branch to use in case repo_url overwritten + ``` +See more configuration options in the [Configuration options](#configuration-options) section. + ## Example usage -#### Asking a question about this repository: -![help_docs on the documentation of this repository](https://codium.ai/images/pr_agent/help_docs_comment.png){width=512} -#### Asking a question about another repository under branch: main: -![help_docs on the documentation of another repository](https://codium.ai/images/pr_agent/help_docs_comment_explicit_git.png){width=512} +[//]: # (#### Asking a question about this repository:) -#### Response for the first question: -![help_docs response](https://codium.ai/images/pr_agent/help_docs_response.png){width=512} +[//]: # (![help_docs on the documentation of this repository](https://codium.ai/images/pr_agent/help_docs_comment.png){width=512}) -## Configuration options +**Asking a question about another repository** -Under the section `pr_help_docs`, the [configuration file](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml#L50) contains options to customize the 'help docs' tool: +![help_docs on the documentation of another repository](https://codium.ai/images/pr_agent/help_docs_comment_explicit_git.png){width=512} -- `repo_url`: If not overwritten, will use the repo from where the context came from (issue or PR), otherwise - use the given repo as context. -- `repo_default_branch`: The branch to use in case repo_url overwritten, otherwise - has no effect. -- `docs_path`: Relative path from root of repository (either the one this PR has been issued for, or above repo url). -- `exclude_root_readme`: Whether or not to exclude the root README file for querying the model. -- `supported_doc_exts` : Which file extensions should be included for the purpose of querying the model. +**Response**: ---- -## Run as a GitHub Action +![help_docs response](https://codium.ai/images/pr_agent/help_docs_response.png){width=512} + +## Run automatically when a new issue is opened -You can use our pre-built Github Action Docker image to run help_docs on any newly created issue, as a Github Action. Here's how: +You can configure PR-Agent to run `help_docs` automatically on any newly created issue. +This can be useful, for example, for providing immediate feedback to users who open issues with questions on open-source projects with extensive documentation. + +Here's how: 1) Follow the steps depicted under [Run as a Github Action](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-action) to create a new workflow, such as:`.github/workflows/help_docs.yml`: @@ -76,10 +87,22 @@ jobs: export github__user_token=$GITHUB_TOKEN && \ export github__base_url=$GITHUB_API_URL && \ export openai__key=$OPENAI_KEY && \ - export config__verbosity_level=2 && \ - python -m pr_agent.cli --issue_url=$ISSUE_URL help_docs \"$ISSUE_BODY\"" + python -m pr_agent.cli --issue_url=$ISSUE_URL --pr_help_docs.repo_url="..." --pr_help_docs.docs_path="..." --pr_help_docs.openai_key=$OPENAI_KEY && \help_docs \"$ISSUE_BODY\"" ``` -3) Following completion of the remaining steps (such as adding secrets and any env. variables), merge this change to your main branch. -When a new issue is opened, you should see a comment from `github-actions` bot with an auto response, assuming the question is a relevant one. +3) Following completion of the remaining steps (such as adding secrets and relevant configurations, such as `repo_url` and `docs_path`) merge this change to your main branch. +When a new issue is opened, you should see a comment from `github-actions` bot with an auto response, assuming the question is related to the documentation of the repository. +--- + + +## Configuration options + +Under the section `pr_help_docs`, the [configuration file](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml#L50) contains options to customize the 'help docs' tool: + +- `repo_url`: If not overwritten, will use the repo from where the context came from (issue or PR), otherwise - use the given repo as context. +- `repo_default_branch`: The branch to use in case repo_url overwritten, otherwise - has no effect. +- `docs_path`: Relative path from root of repository (either the one this PR has been issued for, or above repo url). +- `exclude_root_readme`: Whether or not to exclude the root README file for querying the model. +- `supported_doc_exts` : Which file extensions should be included for the purpose of querying the model. + --- From 069f5eb86fe0cb7c5f831ebc2b94413892d5765c Mon Sep 17 00:00:00 2001 From: mrT23 Date: Fri, 28 Mar 2025 15:48:10 +0300 Subject: [PATCH 035/365] Add Help Docs tool announcement to README.md with March 28, 2025 release notes --- README.md | 31 +++++++------------------------ 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 7a5506b41d..a2b62a90c7 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,13 @@ PR-Agent aims to help efficiently review and handle pull requests, by providing ## News and Updates +## March 28, 2025 +A new version, v0.28, was released. See release notes [here](https://github.com/qodo-ai/pr-agent/releases/tag/v0.28). + +This version includes a new tool, [Help Docs](https://qodo-merge-docs.qodo.ai/tools/help_docs/), which can answer free-text questions based on a documentation folder. + +`/help_docs` is now being used to provide immediate automatic feedback to any user who [opens an issue](https://github.com/qodo-ai/pr-agent/issues/1608#issue-2897328825) on PR-Agent's open-source project + ### Feb 28, 2025 A new version, v0.27, was released. See release notes [here](https://github.com/qodo-ai/pr-agent/releases/tag/v0.27). @@ -60,30 +67,6 @@ A new version, v0.27, was released. See release notes [here](https://github.com/ - Important updates and bug fixes for Azure DevOps, see [here](https://github.com/qodo-ai/pr-agent/pull/1583) - Added support for adjusting the [response language](https://qodo-merge-docs.qodo.ai/usage-guide/additional_configurations/#language-settings) of the PR-Agent tools. -### Feb 6, 2025 -New design for the `/improve` tool: - - - -### Jan 25, 2025 - -The open-source GitHub organization was updated: -`https://github.com/codium-ai/pr-agent` → -`https://github.com/qodo-ai/pr-agent` - -The docker should be redirected automatically to the new location. -However, if you have any issues, please update the GitHub action docker image from -`uses: Codium-ai/pr-agent@main` -to -`uses: qodo-ai/pr-agent@main` - - -### Jan 2, 2025 - -New tool [/Implement](https://qodo-merge-docs.qodo.ai/tools/implement/) (💎), which converts human code review discussions and feedback into ready-to-commit code changes. - - - ### December 30, 2024 From 4a5c115d20b92a7a0dab158e1583ba2f65dcbdef Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Fri, 28 Mar 2025 16:54:52 +0300 Subject: [PATCH 036/365] Rename company_codebase to rag_context_enrichment in documentation --- docs/docs/core-abilities/index.md | 2 +- ..._codebase.md => rag_context_enrichment.md} | 29 +++++++++---------- docs/mkdocs.yml | 2 +- 3 files changed, 16 insertions(+), 17 deletions(-) rename docs/docs/core-abilities/{company_codebase.md => rag_context_enrichment.md} (52%) diff --git a/docs/docs/core-abilities/index.md b/docs/docs/core-abilities/index.md index ba431bc4f4..7a97a41682 100644 --- a/docs/docs/core-abilities/index.md +++ b/docs/docs/core-abilities/index.md @@ -9,7 +9,7 @@ Qodo Merge utilizes a variety of core abilities to provide a comprehensive and e - [Impact evaluation](https://qodo-merge-docs.qodo.ai/core-abilities/impact_evaluation/) - [Interactivity](https://qodo-merge-docs.qodo.ai/core-abilities/interactivity/) - [Compression strategy](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) -- [Company Codebase](https://qodo-merge-docs.qodo.ai/core-abilities/company_codebase/) +- [RAG Context Enrichment](https://qodo-merge-docs.qodo.ai/core-abilities/rag_context_enrichment/) - [Static code analysis](https://qodo-merge-docs.qodo.ai/core-abilities/static_code_analysis/) - [Code fine-tuning benchmark](https://qodo-merge-docs.qodo.ai/finetuning_benchmark/) diff --git a/docs/docs/core-abilities/company_codebase.md b/docs/docs/core-abilities/rag_context_enrichment.md similarity index 52% rename from docs/docs/core-abilities/company_codebase.md rename to docs/docs/core-abilities/rag_context_enrichment.md index d22e05e1ca..2ac6056a50 100644 --- a/docs/docs/core-abilities/company_codebase.md +++ b/docs/docs/core-abilities/rag_context_enrichment.md @@ -1,17 +1,16 @@ -# Company Codebase 💎 -`Supported Git Platforms: GitHub` +# RAG Context Enrichment 💎 +`Supported Git Platforms: GitHub` ## Overview -### What is Company Codebase? - -An organized, semantic database that aggregates all your company’s source code into one searchable repository, enabling efficient code discovery and analysis. +### What is RAG Context Enrichment? -### How does Company Codebase work? +A feature that enhances AI analysis by retrieving and referencing relevant code patterns from your project, enabling context-aware insights during code reviews. -By indexing your company's code and using Retrieval-Augmented Generation (RAG), it retrieves contextual code segments on demand, improving pull request (PR) insights and accelerating review accuracy. +### How does RAG Context Enrichment work? +Using Retrieval-Augmented Generation (RAG), it searches your configured repositories for contextually relevant code segments, enriching pull request (PR) insights and accelerating review accuracy. ## Getting started @@ -23,7 +22,7 @@ By indexing your company's code and using Retrieval-Augmented Generation (RAG), In order to enable the RAG feature, add the following lines to your configuration file: ``` toml [rag_arguments] -enable_rag=true +enable_rag=true ``` !!! example "RAG Arguments Options" @@ -31,27 +30,27 @@ enable_rag=true - + - +
enable_ragIf set to true, codebase enrichment using RAG will be enabled. Default is false.If set to true, repository enrichment using RAG will be enabled. Default is false.
rag_repo_listA list of repositories that will be used by the semantic search for RAG. Use `['all']` to consider the entire codebase or a select list or repositories, for example: ['my-org/my-repo', ...]. Default: the repository from which the PR was opened.A list of repositories that will be used by the semantic search for RAG. Use `['all']` to consider the entire codebase or a select list of repositories, for example: ['my-org/my-repo', ...]. Default: the repository from which the PR was opened.
-References from the codebase will be shown in a collapsible bookmark, allowing you to easily access relevant code snippets: +References from the repository will be shown in a collapsible bookmark, allowing you to easily access relevant code snippets: -![References](https://codium.ai/images/pr_agent/company_codebase_references.png){width=640} +![References](https://codium.ai/images/pr_agent/code_context_enrichment_references.png){width=640} ## Limitations -### Querying the codebase presents significant challenges: +### Querying the codebase presents significant challenges - **Search Method**: RAG uses natural language queries to find semantically relevant code sections - **Result Quality**: No guarantee that RAG results will be useful for all queries -- **Scope Recommendation**: To reduce noise, avoid using the whole codebase; focus on PR repository instead +- **Scope Recommendation**: To reduce noise, focus on the PR repository rather than searching across multiple repositories -### This feature has several requirements and restrictions: +### This feature has several requirements and restrictions - **Codebase**: Must be properly indexed for search functionality - **Security**: Requires secure and private indexed codebase implementation - **Deployment**: Only available for Qodo Merge Enterprise plan using single tenant or on-premises setup diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index aa1a075896..eed3f0c81d 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -49,7 +49,7 @@ nav: - Impact evaluation: 'core-abilities/impact_evaluation.md' - Interactivity: 'core-abilities/interactivity.md' - Compression strategy: 'core-abilities/compression_strategy.md' - - Company Codebase: 'core-abilities/company_codebase.md' + - RAG Context Enrichment: 'core-abilities/rag_context_enrichment.md' - Static code analysis: 'core-abilities/static_code_analysis.md' - Code Fine-tuning Benchmark: 'finetuning_benchmark/index.md' - Chrome Extension: From 6b2a1ff529d1d7a1aff106154ed90948680b1552 Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Fri, 28 Mar 2025 17:00:42 +0300 Subject: [PATCH 037/365] Update image reference in rag_context_enrichment documentation --- docs/docs/core-abilities/rag_context_enrichment.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/core-abilities/rag_context_enrichment.md b/docs/docs/core-abilities/rag_context_enrichment.md index 2ac6056a50..f00ef372b8 100644 --- a/docs/docs/core-abilities/rag_context_enrichment.md +++ b/docs/docs/core-abilities/rag_context_enrichment.md @@ -41,7 +41,7 @@ enable_rag=true References from the repository will be shown in a collapsible bookmark, allowing you to easily access relevant code snippets: -![References](https://codium.ai/images/pr_agent/code_context_enrichment_references.png){width=640} +![References](https://codium.ai/images/pr_agent/rag_context_enrichment_references.png){width=640} ## Limitations From ddf94c14a398484918216e5ed89a16b27a4926d9 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sat, 29 Mar 2025 09:33:31 +0300 Subject: [PATCH 038/365] Add configuration option for PR Code Suggestions to control publishing when no suggestions found --- docs/docs/tools/improve.md | 3 +++ pr_agent/settings/configuration.toml | 3 +-- pr_agent/tools/pr_code_suggestions.py | 5 +++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/docs/tools/improve.md b/docs/docs/tools/improve.md index 45d7d5ca33..97a1cc3949 100644 --- a/docs/docs/tools/improve.md +++ b/docs/docs/tools/improve.md @@ -434,6 +434,9 @@ Note: Chunking is primarily relevant for large PRs. For most PRs (up to 500 line enable_chat_text If set to true, the tool will display a reference to the PR chat in the comment. Default is true. + + publish_output_no_suggestions + If set to true, the tool will publish a comment even if no suggestions were found. Default is true. wiki_page_accepted_suggestions If set to true, the tool will automatically track accepted suggestions in a dedicated wiki page called `.pr_agent_accepted_suggestions`. Default is true. diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index 73e97cf40c..d5ddc95c63 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -14,7 +14,6 @@ model_token_count_estimate_factor=0.3 # factor to increase the token count estim git_provider="github" publish_output=true publish_output_progress=true -publish_output_no_suggestions=true verbosity_level=0 # 0,1,2 use_extra_bad_extensions=false # Configurations @@ -130,9 +129,9 @@ focus_only_on_problems=true extra_instructions = "" enable_help_text=false enable_chat_text=false -enable_intro_text=true persistent_comment=true max_history_len=4 +publish_output_no_suggestions=true # enable to apply suggestion 💎 apply_suggestions_checkbox=true # suggestions scoring diff --git a/pr_agent/tools/pr_code_suggestions.py b/pr_agent/tools/pr_code_suggestions.py index becee9a784..1d699f390d 100644 --- a/pr_agent/tools/pr_code_suggestions.py +++ b/pr_agent/tools/pr_code_suggestions.py @@ -217,7 +217,8 @@ async def add_self_review_text(self, pr_body): async def publish_no_suggestions(self): pr_body = "## PR Code Suggestions ✨\n\nNo code suggestions found for the PR." - if get_settings().config.publish_output and get_settings().config.publish_output_no_suggestions: + if (get_settings().config.publish_output and + get_settings().pr_code_suggestions.get('publish_output_no_suggestions', True)): get_logger().warning('No code suggestions found for the PR.') get_logger().debug(f"PR output", artifact=pr_body) if self.progress_response: @@ -784,7 +785,7 @@ def generate_summarized_suggestions(self, data: Dict) -> str: pr_body += "No suggestions found to improve this PR." return pr_body - if get_settings().pr_code_suggestions.enable_intro_text and get_settings().config.is_auto_command: + if get_settings().config.is_auto_command: pr_body += "Explore these optional code suggestions:\n\n" language_extension_map_org = get_settings().language_extension_map_org From 8f0df437dd740e14fb646f9742aeb73602036c46 Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Sat, 29 Mar 2025 15:38:12 +0300 Subject: [PATCH 039/365] Add video tutorials link to documentation index page --- docs/docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/docs/index.md b/docs/docs/index.md index adc29621d7..7452795df9 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -9,6 +9,7 @@ Qodo Merge is a hosted version of PR-Agent, designed for companies and teams tha - See the [Tools Guide](./tools/index.md) for a detailed description of the different tools. +- See the [Video Tutorials](https://www.youtube.com/playlist?list=PLRTpyDOSgbwFMA_VBeKMnPLaaZKwjGBFT) for practical demonstrations on how to use the tools. ## Docs Smart Search From 7d47bd5f5e1d96c5b53ff65c724422c13ad4ba1e Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sat, 29 Mar 2025 19:35:39 +0300 Subject: [PATCH 040/365] Add GitLab protections to prevent quick actions in PR questions --- pr_agent/tools/pr_questions.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/pr_agent/tools/pr_questions.py b/pr_agent/tools/pr_questions.py index 6f400b9641..3333fb3a67 100644 --- a/pr_agent/tools/pr_questions.py +++ b/pr_agent/tools/pr_questions.py @@ -9,7 +9,7 @@ from pr_agent.algo.token_handler import TokenHandler from pr_agent.algo.utils import ModelType from pr_agent.config_loader import get_settings -from pr_agent.git_providers import get_git_provider +from pr_agent.git_providers import get_git_provider, GitLabProvider from pr_agent.git_providers.git_provider import get_main_pr_language from pr_agent.log import get_logger from pr_agent.servers.help import HelpMessage @@ -116,10 +116,21 @@ async def _get_prediction(self, model: str): model=model, temperature=get_settings().config.temperature, system=system_prompt, user=user_prompt) return response + def gitlab_protctions(self, model_answer: str) -> str: + github_quick_actions_MR = ["/approve", "/close", "/merge", "/reopen", "/unapprove", "/title", "/assign", + "/copy_metadata", "/target_branch"] + if any(action in model_answer for action in github_quick_actions_MR): + str_err = "Model answer contains GitHub quick actions, which are not supported in GitLab" + get_logger().error(str_err) + return str_err + def _prepare_pr_answer(self) -> str: model_answer = self.prediction.strip() # sanitize the answer so that no line will start with "/" model_answer_sanitized = model_answer.replace("\n/", "\n /") + model_answer_sanitized = model_answer_sanitized.replace("\r/", "\r /") + if isinstance(self.git_provider, GitLabProvider): + model_answer_sanitized = self.gitlab_protctions(model_answer_sanitized) if model_answer_sanitized.startswith("/"): model_answer_sanitized = " " + model_answer_sanitized if model_answer_sanitized != model_answer: From 02d9aed7fef8136fa41009a2d1a8b9634915839d Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sat, 29 Mar 2025 19:39:56 +0300 Subject: [PATCH 041/365] Fix GitLab provider to use default branch instead of target branch for repo settings --- pr_agent/git_providers/gitlab_provider.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pr_agent/git_providers/gitlab_provider.py b/pr_agent/git_providers/gitlab_provider.py index 331bdf654e..590aa32e14 100644 --- a/pr_agent/git_providers/gitlab_provider.py +++ b/pr_agent/git_providers/gitlab_provider.py @@ -515,7 +515,8 @@ def get_issue_comments(self): def get_repo_settings(self): try: - contents = self.gl.projects.get(self.id_project).files.get(file_path='.pr_agent.toml', ref=self.mr.target_branch).decode() + main_branch = self.gl.projects.get(self.id_project).default_branch + contents = self.gl.projects.get(self.id_project).files.get(file_path='.pr_agent.toml', ref=main_branch).decode() return contents except Exception: return "" From 7bd0fefee43610b9a6e64c73743dc2fefbaf30c1 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sat, 29 Mar 2025 19:53:46 +0300 Subject: [PATCH 042/365] Fix GitLab protections function and return value in PR questions --- pr_agent/tools/pr_questions.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pr_agent/tools/pr_questions.py b/pr_agent/tools/pr_questions.py index 3333fb3a67..7cdb7984f7 100644 --- a/pr_agent/tools/pr_questions.py +++ b/pr_agent/tools/pr_questions.py @@ -116,13 +116,14 @@ async def _get_prediction(self, model: str): model=model, temperature=get_settings().config.temperature, system=system_prompt, user=user_prompt) return response - def gitlab_protctions(self, model_answer: str) -> str: + def gitlab_protections(self, model_answer: str) -> str: github_quick_actions_MR = ["/approve", "/close", "/merge", "/reopen", "/unapprove", "/title", "/assign", "/copy_metadata", "/target_branch"] if any(action in model_answer for action in github_quick_actions_MR): str_err = "Model answer contains GitHub quick actions, which are not supported in GitLab" get_logger().error(str_err) return str_err + return model_answer def _prepare_pr_answer(self) -> str: model_answer = self.prediction.strip() @@ -130,7 +131,7 @@ def _prepare_pr_answer(self) -> str: model_answer_sanitized = model_answer.replace("\n/", "\n /") model_answer_sanitized = model_answer_sanitized.replace("\r/", "\r /") if isinstance(self.git_provider, GitLabProvider): - model_answer_sanitized = self.gitlab_protctions(model_answer_sanitized) + model_answer_sanitized = self.gitlab_protections(model_answer_sanitized) if model_answer_sanitized.startswith("/"): model_answer_sanitized = " " + model_answer_sanitized if model_answer_sanitized != model_answer: From afa4adcb23dc5eb063d526cc9bd72e3dff8f01d3 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sun, 30 Mar 2025 09:00:45 +0300 Subject: [PATCH 043/365] Improve code analysis instructions for handling partial code blocks in PR reviews --- .../pr_code_suggestions_prompts_not_decoupled.toml | 3 ++- pr_agent/settings/pr_reviewer_prompts.toml | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/pr_agent/settings/code_suggestions/pr_code_suggestions_prompts_not_decoupled.toml b/pr_agent/settings/code_suggestions/pr_code_suggestions_prompts_not_decoupled.toml index 3661b71dbc..1f3213c7af 100644 --- a/pr_agent/settings/code_suggestions/pr_code_suggestions_prompts_not_decoupled.toml +++ b/pr_agent/settings/code_suggestions/pr_code_suggestions_prompts_not_decoupled.toml @@ -60,6 +60,7 @@ Specific guidelines for generating code suggestions: {%- endif %} - When mentioning code elements (variables, names, or files) in your response, surround them with backticks (`). For example: "verify that `user_id` is..." - Note that you only see changed code segments (diff hunks in a PR), not the entire codebase. Avoid suggestions that might duplicate existing functionality or questioning code elements (like variables declarations or import statements) that may be defined elsewhere in the codebase. +- Also note that if the code ends at an opening brace or statement that begins a new scope (like 'if', 'for', 'try'), don't treat it as incomplete. Instead, acknowledge the visible scope boundary and analyze only the code shown. {%- if extra_instructions %} @@ -76,7 +77,7 @@ The output must be a YAML object equivalent to type $PRCodeSuggestions, accordin class CodeSuggestion(BaseModel): relevant_file: str = Field(description="Full path of the relevant file") language: str = Field(description="Programming language used by the relevant file") - existing_code: str = Field(description="A short code snippet from the final state of the PR diff, that the suggestion aims to enhance or fix. Include only complete code lines, preserving all indentation, newlines, and original formatting. Use ellipsis (...) for brevity if needed. This snippet should represent the specific PR code targeted for improvement.") + existing_code: str = Field(description="A short code snippet from the final state of the PR diff that the suggestion will address. Select only the span of code that will be modified - without surrounding unchanged code. Preserve all indentation, newlines, and original formatting. Use ellipsis (...) for brevity if needed.") suggestion_content: str = Field(description="An actionable suggestion to enhance, improve or fix the new code introduced in the PR. Don't present here actual code snippets, just the suggestion. Be short and concise") improved_code: str = Field(description="A refined code snippet that replaces the 'existing_code' snippet after implementing the suggestion.") one_sentence_summary: str = Field(description="A concise, single-sentence overview (up to 6 words) of the suggested improvement. Focus on the 'what'. Be general, and avoid method or variable names.") diff --git a/pr_agent/settings/pr_reviewer_prompts.toml b/pr_agent/settings/pr_reviewer_prompts.toml index 1c3570563c..4b77d3c16f 100644 --- a/pr_agent/settings/pr_reviewer_prompts.toml +++ b/pr_agent/settings/pr_reviewer_prompts.toml @@ -44,7 +44,8 @@ __new hunk__ - If available, an AI-generated summary will appear and provide a high-level overview of the file changes. Note that this summary may not be fully accurate or complete. {%- endif %} - When quoting variables, names or file paths from the code, use backticks (`) instead of single quote ('). - +- Note that you only see changed code segments (diff hunks in a PR), not the entire codebase. Avoid suggestions that might duplicate existing functionality or questioning code elements (like variables declarations or import statements) that may be defined elsewhere in the codebase. +- Also note that if the code ends at an opening brace or statement that begins a new scope (like 'if', 'for', 'try'), don't treat it as incomplete. Instead, acknowledge the visible scope boundary and analyze only the code shown. {%- if extra_instructions %} @@ -67,7 +68,7 @@ class SubPR(BaseModel): class KeyIssuesComponentLink(BaseModel): relevant_file: str = Field(description="The full file path of the relevant file") issue_header: str = Field(description="One or two word title for the issue. For example: 'Possible Bug', etc.") - issue_content: str = Field(description="A short and concise summary of what should be further inspected and validated during the PR review process for this issue. Do not reference line numbers in this field.") + issue_content: str = Field(description="A short and concise summary of what should be further inspected and validated during the PR review process for this issue. Do not mention line numbers in this field.") start_line: int = Field(description="The start line that corresponds to this issue in the relevant file") end_line: int = Field(description="The end line that corresponds to this issue in the relevant file") From a7ab04ba8dd6dd02fd60e94015439c0617303bed Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sun, 30 Mar 2025 09:18:45 +0300 Subject: [PATCH 044/365] Update trial information in Qodo Merge documentation with grace usage details --- docs/docs/installation/qodo_merge.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/installation/qodo_merge.md b/docs/docs/installation/qodo_merge.md index fa6b83e491..dae48a7907 100644 --- a/docs/docs/installation/qodo_merge.md +++ b/docs/docs/installation/qodo_merge.md @@ -1,7 +1,7 @@ Qodo Merge is a versatile application compatible with GitHub, GitLab, and BitBucket, hosted by QodoAI. See [here](https://qodo-merge-docs.qodo.ai/overview/pr_agent_pro/) for more details about the benefits of using Qodo Merge. -A complimentary two-week trial is provided to all new users. Following the trial period, user licenses (seats) are required for continued access. +A complimentary two-week trial is provided to all new users (with three additional grace usages). Following the trial period, user licenses (seats) are required for continued access. To purchase user licenses, please visit our [pricing page](https://www.qodo.ai/pricing/). Once subscribed, users can seamlessly deploy the application across any of their code repositories. From 20cb139161ceb3f7d17fe49dc7230113ec811c9f Mon Sep 17 00:00:00 2001 From: mrT23 Date: Mon, 31 Mar 2025 08:39:40 +0300 Subject: [PATCH 045/365] Improve code suggestion prompt to clarify handling of partial code segments in PR reviews --- .../pr_code_suggestions_prompts_not_decoupled.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/settings/code_suggestions/pr_code_suggestions_prompts_not_decoupled.toml b/pr_agent/settings/code_suggestions/pr_code_suggestions_prompts_not_decoupled.toml index 1f3213c7af..3d68b36b0c 100644 --- a/pr_agent/settings/code_suggestions/pr_code_suggestions_prompts_not_decoupled.toml +++ b/pr_agent/settings/code_suggestions/pr_code_suggestions_prompts_not_decoupled.toml @@ -59,7 +59,7 @@ Specific guidelines for generating code suggestions: - use more specific exception types {%- endif %} - When mentioning code elements (variables, names, or files) in your response, surround them with backticks (`). For example: "verify that `user_id` is..." -- Note that you only see changed code segments (diff hunks in a PR), not the entire codebase. Avoid suggestions that might duplicate existing functionality or questioning code elements (like variables declarations or import statements) that may be defined elsewhere in the codebase. +- Note that you will only see partial code segments that were changed (diff hunks in a PR), and not the entire codebase. Avoid suggestions that might duplicate existing functionality or question the existence of code elements like variables, functions, classes, and import statements, that may be defined elsewhere in the codebase. - Also note that if the code ends at an opening brace or statement that begins a new scope (like 'if', 'for', 'try'), don't treat it as incomplete. Instead, acknowledge the visible scope boundary and analyze only the code shown. {%- if extra_instructions %} From c674c5ed02365c84ecd2d6589f0ee7de6dc79148 Mon Sep 17 00:00:00 2001 From: sharoneyal Date: Mon, 31 Mar 2025 09:09:43 +0300 Subject: [PATCH 046/365] Update bitbucket related documentation (#1659) --- docs/docs/usage-guide/automations_and_usage.md | 2 +- docs/docs/usage-guide/configuration_options.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/docs/usage-guide/automations_and_usage.md b/docs/docs/usage-guide/automations_and_usage.md index 26cb3eb09a..9b16c723cc 100644 --- a/docs/docs/usage-guide/automations_and_usage.md +++ b/docs/docs/usage-guide/automations_and_usage.md @@ -206,7 +206,7 @@ push_commands = [ Note that to use the 'handle_push_trigger' feature, you need to give the gitlab webhook also the "Push events" scope. ### BitBucket App -Similar to GitHub app, when running Qodo Merge from BitBucket App, the default [configuration file](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml) from a pre-built docker will be initially loaded. +Similar to GitHub app, when running Qodo Merge from BitBucket App, the default [configuration file](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml) will be initially loaded. By uploading a local `.pr_agent.toml` file to the root of the repo's main branch, you can edit and customize any configuration parameter. Note that you need to upload `.pr_agent.toml` prior to creating a PR, in order for the configuration to take effect. diff --git a/docs/docs/usage-guide/configuration_options.md b/docs/docs/usage-guide/configuration_options.md index 4030fb9970..9e233fbdaf 100644 --- a/docs/docs/usage-guide/configuration_options.md +++ b/docs/docs/usage-guide/configuration_options.md @@ -61,7 +61,7 @@ Then you can give a list of extra instructions to the `review` tool. `Platforms supported: GitHub, GitLab, Bitbucket` -If you create a repo called `pr-agent-settings` in your **organization**, it's configuration file `.pr_agent.toml` will be used as a global configuration file for any other repo that belongs to the same organization. +If you create a repo called `pr-agent-settings` in your **organization**, its configuration file `.pr_agent.toml` will be used as a global configuration file for any other repo that belongs to the same organization. Parameters from a local `.pr_agent.toml` file, in a specific repo, will override the global configuration parameters. For example, in the GitHub organization `Codium-ai`: @@ -71,9 +71,9 @@ For example, in the GitHub organization `Codium-ai`: - The repo [`https://github.com/Codium-ai/pr-agent`](https://github.com/Codium-ai/pr-agent/blob/main/.pr_agent.toml) inherits the global configuration file from `pr-agent-settings`. ### Bitbucket Organization level configuration file 💎 -`Relevant platforms: Bitbucket Cloud, Bitbucket Data Center` +`Relevant platforms: Bitbucket Data Center` -In Bitbucket, there are two levels where you can define a global configuration file: +In Bitbucket Data Center, there are two levels where you can define a global configuration file: * Project-level global configuration: From d749620ebb38b04cdced1823c7ceda59b498f19f Mon Sep 17 00:00:00 2001 From: mrT23 Date: Tue, 1 Apr 2025 08:15:09 +0300 Subject: [PATCH 047/365] Update default model reference from GPT-4 to o3-mini and improve model configuration docs --- docs/docs/usage-guide/changing_a_model.md | 26 ++++++++++++----------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/docs/docs/usage-guide/changing_a_model.md b/docs/docs/usage-guide/changing_a_model.md index cc4bd25327..2f8080ed36 100644 --- a/docs/docs/usage-guide/changing_a_model.md +++ b/docs/docs/usage-guide/changing_a_model.md @@ -1,7 +1,7 @@ ## Changing a model in PR-Agent See [here](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/algo/__init__.py) for a list of available models. -To use a different model than the default (GPT-4), you need to edit in the [configuration file](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml#L2) the fields: +To use a different model than the default (o3-mini), you need to edit in the [configuration file](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml#L2) the fields: ``` [config] model = "..." @@ -9,7 +9,10 @@ fallback_models = ["..."] ``` For models and environments not from OpenAI, you might need to provide additional keys and other parameters. -You can give parameters via a configuration file (see below for instructions), or from environment variables. See [litellm documentation](https://litellm.vercel.app/docs/proxy/quick_start#supported-llms) for the environment variables relevant per model. +You can give parameters via a configuration file, or from environment variables. + +!!! note "Model-specific environment variables" + See [litellm documentation](https://litellm.vercel.app/docs/proxy/quick_start#supported-llms) for the environment variables needed per model, as they may vary and change over time. Our documentation per-model may not always be up-to-date with the latest changes. ### Azure @@ -158,25 +161,24 @@ And also set the api key in the .secrets.toml file: KEY = "..." ``` +See [litellm](https://docs.litellm.ai/docs/providers/anthropic#usage) documentation for more information about the environment variables required for Anthropic. + ### Amazon Bedrock To use Amazon Bedrock and its foundational models, add the below configuration: ``` [config] # in configuration.toml -model="bedrock/anthropic.claude-3-sonnet-20240229-v1:0" -fallback_models=["bedrock/anthropic.claude-v2:1"] -``` +model="bedrock/anthropic.claude-3-5-sonnet-20240620-v1:0" +fallback_models=["bedrock/anthropic.claude-3-5-sonnet-20240620-v1:0"] -Note that you have to add access to foundational models before using them. Please refer to [this document](https://docs.aws.amazon.com/bedrock/latest/userguide/setting-up.html) for more details. - -If you are using the claude-3 model, please configure the following settings as there are parameters incompatible with claude-3. -``` -[litellm] -drop_params = true +[aws] +AWS_ACCESS_KEY_ID="..." +AWS_SECRET_ACCESS_KEY="..." +AWS_REGION_NAME="..." ``` -AWS session is automatically authenticated from your environment, but you can also explicitly set `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` and `AWS_REGION_NAME` environment variables. Please refer to [this document](https://litellm.vercel.app/docs/providers/bedrock) for more details. +See [litellm](https://docs.litellm.ai/docs/providers/bedrock#usage) documentation for more information about the environment variables required for Amazon Bedrock. ### DeepSeek From f0e0901b10551ea64f1eab57ff6290a48a5d3a06 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Tue, 1 Apr 2025 09:42:08 +0300 Subject: [PATCH 048/365] Update repository configuration references from main branch to default branch --- docs/docs/usage-guide/automations_and_usage.md | 2 +- docs/docs/usage-guide/configuration_options.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/usage-guide/automations_and_usage.md b/docs/docs/usage-guide/automations_and_usage.md index 9b16c723cc..25b962e0f8 100644 --- a/docs/docs/usage-guide/automations_and_usage.md +++ b/docs/docs/usage-guide/automations_and_usage.md @@ -208,7 +208,7 @@ Note that to use the 'handle_push_trigger' feature, you need to give the gitlab ### BitBucket App Similar to GitHub app, when running Qodo Merge from BitBucket App, the default [configuration file](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml) will be initially loaded. -By uploading a local `.pr_agent.toml` file to the root of the repo's main branch, you can edit and customize any configuration parameter. Note that you need to upload `.pr_agent.toml` prior to creating a PR, in order for the configuration to take effect. +By uploading a local `.pr_agent.toml` file to the root of the repo's default branch, you can edit and customize any configuration parameter. Note that you need to upload `.pr_agent.toml` prior to creating a PR, in order for the configuration to take effect. For example, if your local `.pr_agent.toml` file contains: ```toml diff --git a/docs/docs/usage-guide/configuration_options.md b/docs/docs/usage-guide/configuration_options.md index 9e233fbdaf..85ede6ba94 100644 --- a/docs/docs/usage-guide/configuration_options.md +++ b/docs/docs/usage-guide/configuration_options.md @@ -41,7 +41,7 @@ Qodo Merge will know to remove the surrounding quotes when reading the configura `Platforms supported: GitHub, GitLab, Bitbucket, Azure DevOps` -By uploading a local `.pr_agent.toml` file to the root of the repo's main branch, you can edit and customize any configuration parameter. Note that you need to upload `.pr_agent.toml` prior to creating a PR, in order for the configuration to take effect. +By uploading a local `.pr_agent.toml` file to the root of the repo's default branch, you can edit and customize any configuration parameter. Note that you need to upload `.pr_agent.toml` prior to creating a PR, in order for the configuration to take effect. For example, if you set in `.pr_agent.toml`: From dbfc07ccc1ce0d65c836109da58e09244c675450 Mon Sep 17 00:00:00 2001 From: Jose Martinez Date: Tue, 1 Apr 2025 15:54:51 -0500 Subject: [PATCH 049/365] Update logging configuration to use dynamic log level from settings --- pr_agent/servers/gitlab_webhook.py | 2 +- pr_agent/settings/configuration.toml | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pr_agent/servers/gitlab_webhook.py b/pr_agent/servers/gitlab_webhook.py index 76739a964e..098e16c721 100644 --- a/pr_agent/servers/gitlab_webhook.py +++ b/pr_agent/servers/gitlab_webhook.py @@ -19,7 +19,7 @@ from pr_agent.log import LoggingFormat, get_logger, setup_logger from pr_agent.secret_providers import get_secret_provider -setup_logger(fmt=LoggingFormat.JSON, level="DEBUG") +setup_logger(fmt=LoggingFormat.JSON, level=get_settings().get("CONFIG.LOG_LEVEL", "INFO")) router = APIRouter() secret_provider = get_secret_provider() if get_settings().get("CONFIG.SECRET_PROVIDER") else None diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index d5ddc95c63..4213eab132 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -16,6 +16,8 @@ publish_output=true publish_output_progress=true verbosity_level=0 # 0,1,2 use_extra_bad_extensions=false +# Log +log_level="INFO" # Configurations use_wiki_settings_file=true use_repo_settings_file=true From 3b0225544af89ea1d6eca59734b9df4f14a457f2 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 2 Apr 2025 08:23:01 +0300 Subject: [PATCH 050/365] Fix code example in PR reviewer prompts to correctly show added line --- pr_agent/settings/pr_reviewer_prompts.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/settings/pr_reviewer_prompts.toml b/pr_agent/settings/pr_reviewer_prompts.toml index 4b77d3c16f..a322b2b918 100644 --- a/pr_agent/settings/pr_reviewer_prompts.toml +++ b/pr_agent/settings/pr_reviewer_prompts.toml @@ -29,7 +29,7 @@ __old hunk__ @@ ... @@ def func2(): __new hunk__ unchanged code line4 -+new code line5 removed ++new code line5 added unchanged code line6 ## File: 'src/file2.py' From ceaca3e621806cb2a9de0ce91327373769a3f7fe Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 2 Apr 2025 19:39:23 +0300 Subject: [PATCH 051/365] Update feature comparison table with detailed links and new capabilities --- docs/docs/index.md | 76 +++++++++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/docs/docs/index.md b/docs/docs/index.md index 7452795df9..4dc69af9cd 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -23,40 +23,48 @@ To search the documentation site using natural language: 2) The bot will respond with an [answer](https://github.com/Codium-ai/pr-agent/pull/1241#issuecomment-2365259334) that includes relevant documentation links. -## Qodo Merge Features - -Qodo Merge offers extensive pull request functionalities across various git providers: - -| | | GitHub | Gitlab | Bitbucket | Azure DevOps | -|-------|-----------------------------------------------------------------------------------------------------------------------|:------:|:------:|:---------:|:------------:| -| TOOLS | Review | ✅ | ✅ | ✅ | ✅ | -| | ⮑ Incremental | ✅ | | | | -| | Ask | ✅ | ✅ | ✅ | ✅ | -| | Describe | ✅ | ✅ | ✅ | ✅ | -| | ⮑ [Inline file summary](https://qodo-merge-docs.qodo.ai/tools/describe/#inline-file-summary){:target="_blank"} 💎 | ✅ | ✅ | | ✅ | -| | Improve | ✅ | ✅ | ✅ | ✅ | -| | ⮑ Extended | ✅ | ✅ | ✅ | ✅ | -| | [Auto-Approve](https://qodo-merge-docs.qodo.ai/tools/improve/#auto-approval) 💎 | ✅ | ✅ | ✅ | | -| | [Custom Prompt](./tools/custom_prompt.md){:target="_blank"} 💎 | ✅ | ✅ | ✅ | ✅ | -| | Reflect and Review | ✅ | ✅ | ✅ | ✅ | -| | Update CHANGELOG.md | ✅ | ✅ | ✅ | ️ | -| | Find Similar Issue | ✅ | | | ️ | -| | [Add PR Documentation](./tools/documentation.md){:target="_blank"} 💎 | ✅ | ✅ | | ✅ | -| | [Generate Custom Labels](./tools/describe.md#handle-custom-labels-from-the-repos-labels-page-💎){:target="_blank"} 💎 | ✅ | ✅ | | ✅ | -| | [Analyze PR Components](./tools/analyze.md){:target="_blank"} 💎 | ✅ | ✅ | | ✅ | -| | [Test](https://pr-agent-docs.codium.ai/tools/test/) 💎 | ✅ | ✅ | | | -| | [Implement](https://pr-agent-docs.codium.ai/tools/implement/) 💎 | ✅ | ✅ | ✅ | | -| | | | | | ️ | -| USAGE | CLI | ✅ | ✅ | ✅ | ✅ | -| | App / webhook | ✅ | ✅ | ✅ | ✅ | -| | Actions | ✅ | | | ️ | -| | | | | | -| CORE | PR compression | ✅ | ✅ | ✅ | ✅ | -| | Repo language prioritization | ✅ | ✅ | ✅ | ✅ | -| | Adaptive and token-aware file patch fitting | ✅ | ✅ | ✅ | ✅ | -| | Multiple models support | ✅ | ✅ | ✅ | ✅ | -| | [Static code analysis](./core-abilities/static_code_analysis/){:target="_blank"} 💎 | ✅ | ✅ | | | -| | [Multiple configuration options](./usage-guide/configuration_options.md){:target="_blank"} 💎 | ✅ | ✅ | ✅ | ✅ | +## Features + +PR-Agent and Qodo Merge offers extensive pull request functionalities across various git providers: + +| | | GitHub | GitLab | Bitbucket | Azure DevOps | +|-------|---------------------------------------------------------------------------------------------------------|:--------------------:|:--------------------:|:---------:|:------------:| +| TOOLS | [Review](https://qodo-merge-docs.qodo.ai/tools/review/) | ✅ | ✅ | ✅ | ✅ | +| | [Describe](https://qodo-merge-docs.qodo.ai/tools/describe/) | ✅ | ✅ | ✅ | ✅ | +| | [Improve](https://qodo-merge-docs.qodo.ai/tools/improve/) | ✅ | ✅ | ✅ | ✅ | +| | [Ask](https://qodo-merge-docs.qodo.ai/tools/ask/) | ✅ | ✅ | ✅ | ✅ | +| | ⮑ [Ask on code lines](https://qodo-merge-docs.qodo.ai/tools/ask/#ask-lines) | ✅ | ✅ | | | +| | [Update CHANGELOG](https://qodo-merge-docs.qodo.ai/tools/update_changelog/) | ✅ | ✅ | ✅ | ✅ | +| | [Help Docs](https://qodo-merge-docs.qodo.ai/tools/help_docs/?h=auto#auto-approval) | ✅ | ✅ | ✅ | | +| | [Ticket Context](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/) 💎 | ✅ | ✅ | ✅ | | +| | [Utilizing Best Practices](https://qodo-merge-docs.qodo.ai/tools/improve/#best-practices) 💎 | ✅ | ✅ | ✅ | | +| | [PR Chat](https://qodo-merge-docs.qodo.ai/chrome-extension/features/#pr-chat) 💎 | ✅ | | | | +| | [Suggestion Tracking](https://qodo-merge-docs.qodo.ai/tools/improve/#suggestion-tracking) 💎 | ✅ | ✅ | | | +| | [CI Feedback](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/) 💎 | ✅ | | | | +| | [PR Documentation](https://qodo-merge-docs.qodo.ai/tools/documentation/) 💎 | ✅ | ✅ | | | +| | [Custom Labels](https://qodo-merge-docs.qodo.ai/tools/custom_labels/) 💎 | ✅ | ✅ | | | +| | [Analyze](https://qodo-merge-docs.qodo.ai/tools/analyze/) 💎 | ✅ | ✅ | | | +| | [Similar Code](https://qodo-merge-docs.qodo.ai/tools/similar_code/) 💎 | ✅ | | | | +| | [Custom Prompt](https://qodo-merge-docs.qodo.ai/tools/custom_prompt/) 💎 | ✅ | ✅ | ✅ | | +| | [Test](https://qodo-merge-docs.qodo.ai/tools/test/) 💎 | ✅ | ✅ | | | +| | [Implement](https://qodo-merge-docs.qodo.ai/tools/implement/) 💎 | ✅ | ✅ | ✅ | | +| | [Auto-Approve](https://qodo-merge-docs.qodo.ai/tools/improve/?h=auto#auto-approval) 💎 | ✅ | ✅ | ✅ | | +| | | | | | | +| USAGE | [CLI](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#local-repo-cli) | ✅ | ✅ | ✅ | ✅ | +| | [App / webhook](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#github-app) | ✅ | ✅ | ✅ | ✅ | +| | [Tagging bot](https://github.com/Codium-ai/pr-agent#try-it-now) | ✅ | | | | +| | [Actions](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-action) | ✅ |✅| ✅ |✅| +| | | | | | | +| CORE | [PR compression](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) | ✅ | ✅ | ✅ | ✅ | +| | Adaptive and token-aware file patch fitting | ✅ | ✅ | ✅ | ✅ | +| | [Multiple models support](https://qodo-merge-docs.qodo.ai/usage-guide/changing_a_model/) | ✅ | ✅ | ✅ | ✅ | +| | [Local and global metadata](https://qodo-merge-docs.qodo.ai/core-abilities/metadata/) | ✅ | ✅ | ✅ | ✅ | +| | [Dynamic context](https://qodo-merge-docs.qodo.ai/core-abilities/dynamic_context/) | ✅ | ✅ | ✅ | ✅ | +| | [Self reflection](https://qodo-merge-docs.qodo.ai/core-abilities/self_reflection/) | ✅ | ✅ | ✅ | ✅ | +| | [Static code analysis](https://qodo-merge-docs.qodo.ai/core-abilities/static_code_analysis/) 💎 | ✅ | ✅ | | | +| | [Global and wiki configurations](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/) 💎 | ✅ | ✅ | ✅ | | +| | [PR interactive actions](https://www.qodo.ai/images/pr_agent/pr-actions.mp4) 💎 | ✅ | ✅ | | | +| | [Impact Evaluation](https://qodo-merge-docs.qodo.ai/core-abilities/impact_evaluation/) 💎 | ✅ | ✅ | | | 💎 marks a feature available only in [Qodo Merge](https://www.codium.ai/pricing/){:target="_blank"}, and not in the open-source version. From 14971c4f5fa23bae133e2b5e12ab33698d0aceeb Mon Sep 17 00:00:00 2001 From: sharoneyal Date: Thu, 3 Apr 2025 11:51:26 +0300 Subject: [PATCH 052/365] Add support for documentation content exceeding token limits (#1670) * - Add support for documentation content exceeding token limits via two phase operation: 1. Ask LLM to rank headings which are most likely to contain an answer to a user question 2. Provide the corresponding files for the LLM to search for an answer. - Refactor of help_docs to make the code more readable - For the purpose of getting canonical path: git providers to use default branch and not the PR's source branch. - Refactor of token counting and making it clear on when an estimate factor will be used. * Code review changes: 1. Correctly handle exception during retry_with_fallback_models (to allow fallback model to run in case of failure) 2. Better naming for default_branch in bitbucket cloud provider --- pr_agent/algo/token_handler.py | 32 +- pr_agent/config_loader.py | 1 + pr_agent/git_providers/bitbucket_provider.py | 12 +- .../bitbucket_server_provider.py | 8 +- pr_agent/git_providers/github_provider.py | 2 +- pr_agent/git_providers/gitlab_provider.py | 6 +- pr_agent/settings/configuration.toml | 2 +- .../pr_help_docs_headings_prompts.toml | 101 ++++ pr_agent/tools/pr_help_docs.py | 536 ++++++++++++------ 9 files changed, 509 insertions(+), 191 deletions(-) create mode 100644 pr_agent/settings/pr_help_docs_headings_prompts.toml diff --git a/pr_agent/algo/token_handler.py b/pr_agent/algo/token_handler.py index 9bc801ed9f..f1393e38f5 100644 --- a/pr_agent/algo/token_handler.py +++ b/pr_agent/algo/token_handler.py @@ -1,7 +1,6 @@ from threading import Lock from jinja2 import Environment, StrictUndefined -from math import ceil from tiktoken import encoding_for_model, get_encoding from pr_agent.config_loader import get_settings @@ -105,6 +104,19 @@ def calc_claude_tokens(self, patch): get_logger().error( f"Error in Anthropic token counting: {e}") return MaxTokens + def estimate_token_count_for_non_anth_claude_models(self, model, default_encoder_estimate): + from math import ceil + import re + + model_is_from_o_series = re.match(r"^o[1-9](-mini|-preview)?$", model) + if ('gpt' in get_settings().config.model.lower() or model_is_from_o_series) and get_settings(use_context=False).get('openai.key'): + return default_encoder_estimate + #else: Model is not an OpenAI one - therefore, cannot provide an accurate token count and instead, return a higher number as best effort. + + elbow_factor = 1 + get_settings().get('config.model_token_count_estimate_factor', 0) + get_logger().warning(f"{model}'s expected token count cannot be accurately estimated. Using {elbow_factor} of encoder output as best effort estimate") + return ceil(elbow_factor * default_encoder_estimate) + def count_tokens(self, patch: str, force_accurate=False) -> int: """ Counts the number of tokens in a given patch string. @@ -116,21 +128,15 @@ def count_tokens(self, patch: str, force_accurate=False) -> int: The number of tokens in the patch string. """ encoder_estimate = len(self.encoder.encode(patch, disallowed_special=())) + + #If an estimate is enough (for example, in cases where the maximal allowed tokens is way below the known limits), return it. if not force_accurate: return encoder_estimate - #else, need to provide an accurate estimation: + #else, force_accurate==True: User requested providing an accurate estimation: model = get_settings().config.model.lower() - if force_accurate and 'claude' in model and get_settings(use_context=False).get('anthropic.key'): + if 'claude' in model and get_settings(use_context=False).get('anthropic.key'): return self.calc_claude_tokens(patch) # API call to Anthropic for accurate token counting for Claude models - #else: Non Anthropic provided model - - import re - model_is_from_o_series = re.match(r"^o[1-9](-mini|-preview)?$", model) - if ('gpt' in get_settings().config.model.lower() or model_is_from_o_series) and get_settings(use_context=False).get('openai.key'): - return encoder_estimate - #else: Model is neither an OpenAI, nor an Anthropic model - therefore, cannot provide an accurate token count and instead, return a higher number as best effort. - elbow_factor = 1 + get_settings().get('config.model_token_count_estimate_factor', 0) - get_logger().warning(f"{model}'s expected token count cannot be accurately estimated. Using {elbow_factor} of encoder output as best effort estimate") - return ceil(elbow_factor * encoder_estimate) + #else: Non Anthropic provided model: + return self.estimate_token_count_for_non_anth_claude_models(model, encoder_estimate) diff --git a/pr_agent/config_loader.py b/pr_agent/config_loader.py index 575c02a336..7a62adecc8 100644 --- a/pr_agent/config_loader.py +++ b/pr_agent/config_loader.py @@ -29,6 +29,7 @@ "settings/custom_labels.toml", "settings/pr_help_prompts.toml", "settings/pr_help_docs_prompts.toml", + "settings/pr_help_docs_headings_prompts.toml", "settings/.secrets.toml", "settings_prod/.secrets.toml", ]] diff --git a/pr_agent/git_providers/bitbucket_provider.py b/pr_agent/git_providers/bitbucket_provider.py index d3882fdadd..969ddf9eee 100644 --- a/pr_agent/git_providers/bitbucket_provider.py +++ b/pr_agent/git_providers/bitbucket_provider.py @@ -92,7 +92,7 @@ def get_canonical_url_parts(self, repo_git_url:str=None, desired_branch:str=None return ("", "") workspace_name, project_name = repo_path.split('/') else: - desired_branch = self.get_pr_branch() + desired_branch = self.get_repo_default_branch() parsed_pr_url = urlparse(self.pr_url) scheme_and_netloc = parsed_pr_url.scheme + "://" + parsed_pr_url.netloc workspace_name, project_name = (self.workspace_slug, self.repo_slug) @@ -470,6 +470,16 @@ def get_languages(self): def get_pr_branch(self): return self.pr.source_branch + # This function attempts to get the default branch of the repository. As a fallback, uses the PR destination branch. + # Note: Must be running from a PR context. + def get_repo_default_branch(self): + try: + url_repo = f"https://api.bitbucket.org/2.0/repositories/{self.workspace_slug}/{self.repo_slug}/" + response_repo = requests.request("GET", url_repo, headers=self.headers).json() + return response_repo['mainbranch']['name'] + except: + return self.pr.destination_branch + def get_pr_owner_id(self) -> str | None: return self.workspace_slug diff --git a/pr_agent/git_providers/bitbucket_server_provider.py b/pr_agent/git_providers/bitbucket_server_provider.py index ddbb60cc05..e10d031948 100644 --- a/pr_agent/git_providers/bitbucket_server_provider.py +++ b/pr_agent/git_providers/bitbucket_server_provider.py @@ -64,9 +64,15 @@ def get_canonical_url_parts(self, repo_git_url:str=None, desired_branch:str=None workspace_name = None project_name = None if not repo_git_url: - desired_branch = self.get_pr_branch() workspace_name = self.workspace_slug project_name = self.repo_slug + default_branch_dict = self.bitbucket_client.get_default_branch(workspace_name, project_name) + if 'displayId' in default_branch_dict: + desired_branch = default_branch_dict['displayId'] + else: + get_logger().error(f"Cannot obtain default branch for workspace_name={workspace_name}, " + f"project_name={project_name}, default_branch_dict={default_branch_dict}") + return ("", "") elif '.git' in repo_git_url and 'scm/' in repo_git_url: repo_path = repo_git_url.split('.git')[0].split('scm/')[-1] if repo_path.count('/') == 1: # Has to have the form / diff --git a/pr_agent/git_providers/github_provider.py b/pr_agent/git_providers/github_provider.py index 92e256fde9..e782f9cf8c 100644 --- a/pr_agent/git_providers/github_provider.py +++ b/pr_agent/git_providers/github_provider.py @@ -133,7 +133,7 @@ def get_canonical_url_parts(self, repo_git_url:str, desired_branch:str) -> Tuple if (not owner or not repo) and self.repo: #"else" - User did not provide an external git url, or not an issue, use self.repo object owner, repo = self.repo.split('/') scheme_and_netloc = self.base_url_html - desired_branch = self.get_pr_branch() + desired_branch = self.repo_obj.default_branch if not all([scheme_and_netloc, owner, repo]): #"else": Not invoked from a PR context,but no provided git url for context get_logger().error(f"Unable to get canonical url parts since missing context (PR or explicit git url)") return ("", "") diff --git a/pr_agent/git_providers/gitlab_provider.py b/pr_agent/git_providers/gitlab_provider.py index 590aa32e14..df18c9577e 100644 --- a/pr_agent/git_providers/gitlab_provider.py +++ b/pr_agent/git_providers/gitlab_provider.py @@ -87,7 +87,11 @@ def get_canonical_url_parts(self, repo_git_url:str=None, desired_branch:str=None return ("", "") if not repo_git_url: #Use PR url as context repo_path = self._get_project_path_from_pr_or_issue_url(self.pr_url) - desired_branch = self.get_pr_branch() + try: + desired_branch = self.gl.projects.get(self.id_project).default_branch + except Exception as e: + get_logger().exception(f"Cannot get PR: {self.pr_url} default branch. Tried project ID: {self.id_project}") + return ("", "") else: #Use repo git url repo_path = repo_git_url.split('.git')[0].split('.com/')[-1] prefix = f"{self.gitlab_url}/{repo_path}/-/blob/{desired_branch}" diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index d5ddc95c63..952dedd84f 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -9,7 +9,6 @@ model="o3-mini" fallback_models=["gpt-4o-2024-11-20"] #model_weak="gpt-4o-mini-2024-07-18" # optional, a weaker model to use for some easier tasks -model_token_count_estimate_factor=0.3 # factor to increase the token count estimate, in order to reduce likelihood of model failure due to too many tokens. # CLI git_provider="github" publish_output=true @@ -30,6 +29,7 @@ max_description_tokens = 500 max_commits_tokens = 500 max_model_tokens = 32000 # Limits the maximum number of tokens that can be used by any model, regardless of the model's default capabilities. custom_model_max_tokens=-1 # for models not in the default list +model_token_count_estimate_factor=0.3 # factor to increase the token count estimate, in order to reduce likelihood of model failure due to too many tokens - applicable only when requesting an accurate estimate. # patch extension logic patch_extension_skip_types =[".md",".txt"] allow_dynamic_context=true diff --git a/pr_agent/settings/pr_help_docs_headings_prompts.toml b/pr_agent/settings/pr_help_docs_headings_prompts.toml new file mode 100644 index 0000000000..da9d6e5334 --- /dev/null +++ b/pr_agent/settings/pr_help_docs_headings_prompts.toml @@ -0,0 +1,101 @@ + +[pr_help_docs_headings_prompts] +system="""You are Doc-helper, a language model that ranks documentation files based on their relevance to user questions. +You will receive a question, a repository url and file names along with optional groups of headings extracted from such files from that repository (either as markdown or as restructred text). +Your task is to rank file paths based on how likely they contain the answer to a user's question, using only the headings from each such file and the file name. + +====== +==file name== + +'src/file1.py' + +==index== + +0 based integer + +==file headings== +heading #1 +heading #2 +... + +==file name== + +'src/file2.py' + +==index== + +0 based integer + +==file headings== +heading #1 +heading #2 +... + +... +====== + +Additional instructions: +- Consider only the file names and section headings within each document +- Present the most relevant files first, based strictly on how well their headings and file names align with user question + +The output must be a YAML object equivalent to type $DocHeadingsHelper, according to the following Pydantic definitions: +===== +class file_idx_and_path(BaseModel): + idx: int = Field(description="The zero based index of file_name, as it appeared in the original list of headings. Cannot be negative.") + file_name: str = Field(description="The file_name exactly as it appeared in the question") + +class DocHeadingsHelper(BaseModel): + user_question: str = Field(description="The user's question") + relevant_files_ranking: List[file_idx_and_path] = Field(description="Files sorted in descending order by relevance to question") +===== + + +Example output: +```yaml +user_question: | + ... +relevant_files_ranking: +- idx: 101 + file_name: "src/file1.py" +- ... +""" + +user="""\ +Documentation url: '{{ docs_url|trim }}' +----- + + +User's Question: +===== +{{ question|trim }} +===== + + +Filenames with optional headings from documentation website content: +===== +{{ snippets|trim }} +===== + + +Reminder: The output must be a YAML object equivalent to type $DocHeadingsHelper, similar to the following example output: +===== + + +Example output: +```yaml +user_question: | + ... +relevant_files_ranking: +- idx: 101 + file_name: "src/file1.py" +- ... +===== + +Important Notes: +1. Output most relevant file names first, by descending order of relevancy. +2. Only include files with non-negative indices + + +Response (should be a valid YAML, and nothing else). +```yaml +""" diff --git a/pr_agent/tools/pr_help_docs.py b/pr_agent/tools/pr_help_docs.py index ddd4250973..89849aa701 100644 --- a/pr_agent/tools/pr_help_docs.py +++ b/pr_agent/tools/pr_help_docs.py @@ -1,11 +1,11 @@ import copy from functools import partial + from jinja2 import Environment, StrictUndefined import math import os import re from tempfile import TemporaryDirectory -from typing import Dict, List, Optional, Tuple from pr_agent.algo import MAX_TOKENS from pr_agent.algo.ai_handlers.base_ai_handler import BaseAiHandler @@ -78,47 +78,118 @@ def get_maximal_text_input_length_for_token_count_estimation(): return 900000 #Claude API for token estimation allows maximal text input of 900K chars return math.inf #Otherwise, no known limitation on input text just for token estimation -# Load documentation files to memory, decorating them with a header to mark where each file begins, -# as to help the LLM to give a better answer. -def aggregate_documentation_files_for_prompt_contents(base_path: str, doc_files: List[str]) -> Optional[str]: - docs_prompt = "" - for file in doc_files: - try: - with open(file, 'r', encoding='utf-8') as f: - content = f.read() - # Skip files with no text content - if not re.search(r'[a-zA-Z]', content): - continue - file_path = str(file).replace(str(base_path), '') - docs_prompt += f"\n==file name==\n\n{file_path}\n\n==file content==\n\n{content.strip()}\n=========\n\n" - except Exception as e: - get_logger().warning(f"Error while reading the file {file}: {e}") - continue - if not docs_prompt: - get_logger().error("Couldn't find any usable documentation files. Returning None.") - return None - return docs_prompt - -def format_markdown_q_and_a_response(question_str: str, response_str: str, relevant_sections: List[Dict[str, str]], - supported_suffixes: List[str], base_url_prefix: str, base_url_suffix: str="") -> str: - base_url_prefix = base_url_prefix.strip('/') #Sanitize base_url_prefix - answer_str = "" - answer_str += f"### Question: \n{question_str}\n\n" - answer_str += f"### Answer:\n{response_str.strip()}\n\n" - answer_str += f"#### Relevant Sources:\n\n" - for section in relevant_sections: - file = section.get('file_name').strip() - ext = [suffix for suffix in supported_suffixes if file.endswith(suffix)] - if not ext: - get_logger().warning(f"Unsupported file extension: {file}") - continue - if str(section['relevant_section_header_string']).strip(): - markdown_header = format_markdown_header(section['relevant_section_header_string']) - if base_url_prefix: - answer_str += f"> - {base_url_prefix}/{file}{base_url_suffix}#{markdown_header}\n" +def return_document_headings(text: str, ext: str) -> str: + try: + lines = text.split('\n') + headings = set() + + if not text or not re.search(r'[a-zA-Z]', text): + get_logger().error(f"Empty or non text content found in text: {text}.") + return "" + + if ext in ['.md', '.mdx']: + # Extract Markdown headings (lines starting with #) + headings = {line.strip() for line in lines if line.strip().startswith('#')} + elif ext == '.rst': + # Find indices of lines that have all same character: + #Allowed characters according to list from: https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#sections + section_chars = set('!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~') + + # Find potential section marker lines (underlines/overlines): They have to be the same character + marker_lines = [] + for i, line in enumerate(lines): + line = line.rstrip() + if line and all(c == line[0] for c in line) and line[0] in section_chars: + marker_lines.append((i, len(line))) + + # Check for headings adjacent to marker lines (below + text must be in length equal or less) + for idx, length in marker_lines: + # Check if it's an underline (heading is above it) + if idx > 0 and lines[idx - 1].rstrip() and len(lines[idx - 1].rstrip()) <= length: + headings.add(lines[idx - 1].rstrip()) else: - answer_str += f"> - {base_url_prefix}/{file}{base_url_suffix}\n" - return answer_str + get_logger().error(f"Unsupported file extension: {ext}") + return "" + + return '\n'.join(headings) + except Exception as e: + get_logger().exception(f"Unexpected exception thrown. Returning empty result.") + return "" + +# Load documentation files to memory: full file path (as will be given as prompt) -> doc contents +def map_documentation_files_to_contents(base_path: str, doc_files: list[str], max_allowed_file_len=5000) -> dict[str, str]: + try: + returned_dict = {} + for file in doc_files: + try: + with open(file, 'r', encoding='utf-8') as f: + content = f.read() + # Skip files with no text content + if not re.search(r'[a-zA-Z]', content): + continue + if len(content) > max_allowed_file_len: + get_logger().warning(f"File {file} length: {len(content)} exceeds limit: {max_allowed_file_len}, so it will be trimmed.") + content = content[:max_allowed_file_len] + file_path = str(file).replace(str(base_path), '') + returned_dict[file_path] = content.strip() + except Exception as e: + get_logger().warning(f"Error while reading the file {file}: {e}") + continue + if not returned_dict: + get_logger().error("Couldn't find any usable documentation files. Returning empty dict.") + return returned_dict + except Exception as e: + get_logger().exception(f"Unexpected exception thrown. Returning empty dict.") + return {} + +# Goes over files' contents, generating payload for prompt while decorating them with a header to mark where each file begins, +# as to help the LLM to give a better answer. +def aggregate_documentation_files_for_prompt_contents(file_path_to_contents: dict[str, str], return_just_headings=False) -> str: + try: + docs_prompt = "" + for idx, file_path in enumerate(file_path_to_contents): + file_contents = file_path_to_contents[file_path].strip() + if not file_contents: + get_logger().error(f"Got empty file contents for: {file_path}. Skipping this file.") + continue + if return_just_headings: + file_headings = return_document_headings(file_contents, os.path.splitext(file_path)[-1]).strip() + if file_headings: + docs_prompt += f"\n==file name==\n\n{file_path}\n\n==index==\n\n{idx}\n\n==file headings==\n\n{file_headings}\n=========\n\n" + else: + get_logger().warning(f"No headers for: {file_path}. Will only use filename") + docs_prompt += f"\n==file name==\n\n{file_path}\n\n==index==\n\n{idx}\n\n" + else: + docs_prompt += f"\n==file name==\n\n{file_path}\n\n==file content==\n\n{file_contents}\n=========\n\n" + return docs_prompt + except Exception as e: + get_logger().exception(f"Unexpected exception thrown. Returning empty result.") + return "" + +def format_markdown_q_and_a_response(question_str: str, response_str: str, relevant_sections: list[dict[str, str]], + supported_suffixes: list[str], base_url_prefix: str, base_url_suffix: str="") -> str: + try: + base_url_prefix = base_url_prefix.strip('/') #Sanitize base_url_prefix + answer_str = "" + answer_str += f"### Question: \n{question_str}\n\n" + answer_str += f"### Answer:\n{response_str.strip()}\n\n" + answer_str += f"#### Relevant Sources:\n\n" + for section in relevant_sections: + file = section.get('file_name').lstrip('/').strip() #Remove any '/' in the beginning, since some models do it anyway + ext = [suffix for suffix in supported_suffixes if file.endswith(suffix)] + if not ext: + get_logger().warning(f"Unsupported file extension: {file}") + continue + if str(section['relevant_section_header_string']).strip(): + markdown_header = format_markdown_header(section['relevant_section_header_string']) + if base_url_prefix: + answer_str += f"> - {base_url_prefix}/{file}{base_url_suffix}#{markdown_header}\n" + else: + answer_str += f"> - {base_url_prefix}/{file}{base_url_suffix}\n" + return answer_str + except Exception as e: + get_logger().exception(f"Unexpected exception thrown. Returning empty result.") + return "" def format_markdown_header(header: str) -> str: try: @@ -157,87 +228,103 @@ def clean_markdown_content(content: str) -> str: Returns: Cleaned markdown content """ - # Remove HTML comments - content = re.sub(r'', '', content, flags=re.DOTALL) + try: + # Remove HTML comments + content = re.sub(r'', '', content, flags=re.DOTALL) - # Remove frontmatter (YAML between --- or +++ delimiters) - content = re.sub(r'^---\s*\n.*?\n---\s*\n', '', content, flags=re.DOTALL) - content = re.sub(r'^\+\+\+\s*\n.*?\n\+\+\+\s*\n', '', content, flags=re.DOTALL) + # Remove frontmatter (YAML between --- or +++ delimiters) + content = re.sub(r'^---\s*\n.*?\n---\s*\n', '', content, flags=re.DOTALL) + content = re.sub(r'^\+\+\+\s*\n.*?\n\+\+\+\s*\n', '', content, flags=re.DOTALL) - # Remove excessive blank lines (more than 2 consecutive) - content = re.sub(r'\n{3,}', '\n\n', content) + # Remove excessive blank lines (more than 2 consecutive) + content = re.sub(r'\n{3,}', '\n\n', content) - # Remove HTML tags that are often used for styling only - content = re.sub(r'|||', '', content, flags=re.DOTALL) + # Remove HTML tags that are often used for styling only + content = re.sub(r'|||', '', content, flags=re.DOTALL) - # Remove image alt text which can be verbose - content = re.sub(r'!\[(.*?)\]', '![]', content) + # Remove image alt text which can be verbose + content = re.sub(r'!\[(.*?)\]', '![]', content) - # Remove images completely - content = re.sub(r'!\[.*?\]\(.*?\)', '', content) + # Remove images completely + content = re.sub(r'!\[.*?\]\(.*?\)', '', content) - # Remove simple HTML tags but preserve content between them - content = re.sub(r'<(?!table|tr|td|th|thead|tbody)([a-zA-Z][a-zA-Z0-9]*)[^>]*>(.*?)', - r'\2', content, flags=re.DOTALL) - return content.strip() + # Remove simple HTML tags but preserve content between them + content = re.sub(r'<(?!table|tr|td|th|thead|tbody)([a-zA-Z][a-zA-Z0-9]*)[^>]*>(.*?)', + r'\2', content, flags=re.DOTALL) + return content.strip() + except Exception as e: + get_logger().exception(f"Unexpected exception thrown. Returning empty result.") + return "" class PredictionPreparator: def __init__(self, ai_handler, vars, system_prompt, user_prompt): - self.ai_handler = ai_handler - variables = copy.deepcopy(vars) - environment = Environment(undefined=StrictUndefined) - self.system_prompt = environment.from_string(system_prompt).render(variables) - self.user_prompt = environment.from_string(user_prompt).render(variables) + try: + self.ai_handler = ai_handler + variables = copy.deepcopy(vars) + environment = Environment(undefined=StrictUndefined) + self.system_prompt = environment.from_string(system_prompt).render(variables) + self.user_prompt = environment.from_string(user_prompt).render(variables) + except Exception as e: + get_logger().exception(f"Caught exception during init. Setting ai_handler to None to prevent __call__.") + self.ai_handler = None + #Called by retry_with_fallback_models and therefore, on any failure must throw an exception: async def __call__(self, model: str) -> str: + if not self.ai_handler: + get_logger().error("ai handler not set. Cannot invoke model!") + raise ValueError("PredictionPreparator not initialized") try: response, finish_reason = await self.ai_handler.chat_completion( model=model, temperature=get_settings().config.temperature, system=self.system_prompt, user=self.user_prompt) return response except Exception as e: - get_logger().error(f"Error while preparing prediction: {e}") - return "" + get_logger().exception("Caught exception during prediction.", artifacts={'system': self.system_prompt, 'user': self.user_prompt}) + raise e class PRHelpDocs(object): - def __init__(self, ctx_url, ai_handler:partial[BaseAiHandler,] = LiteLLMAIHandler, args: Tuple[str]=None, return_as_string: bool=False): - self.ctx_url = ctx_url - self.question = args[0] if args else None - self.return_as_string = return_as_string - self.repo_url_given_explicitly = True - self.repo_url = get_settings().get('PR_HELP_DOCS.REPO_URL', '') - self.repo_desired_branch = get_settings().get('PR_HELP_DOCS.REPO_DEFAULT_BRANCH', 'main') #Ignored if self.repo_url is empty - self.include_root_readme_file = not(get_settings()['PR_HELP_DOCS.EXCLUDE_ROOT_README']) - self.supported_doc_exts = get_settings()['PR_HELP_DOCS.SUPPORTED_DOC_EXTS'] - self.docs_path = get_settings()['PR_HELP_DOCS.DOCS_PATH'] - - retrieved_settings = [self.include_root_readme_file, self.supported_doc_exts, self.docs_path] - if any([setting is None for setting in retrieved_settings]): - raise Exception(f"One of the settings is invalid: {retrieved_settings}") - - self.git_provider = get_git_provider_with_context(ctx_url) - if not self.git_provider: - raise Exception(f"No git provider found at {ctx_url}") - if not self.repo_url: - self.repo_url_given_explicitly = False - get_logger().debug(f"No explicit repo url provided, deducing it from type: {self.git_provider.__class__.__name__} " - f"context url: {self.ctx_url}") - self.repo_url = self.git_provider.get_git_repo_url(self.ctx_url) + def __init__(self, ctx_url, ai_handler:partial[BaseAiHandler,] = LiteLLMAIHandler, args: tuple[str]=None, return_as_string: bool=False): + try: + self.ctx_url = ctx_url + self.question = args[0] if args else None + self.return_as_string = return_as_string + self.repo_url_given_explicitly = True + self.repo_url = get_settings().get('PR_HELP_DOCS.REPO_URL', '') + self.repo_desired_branch = get_settings().get('PR_HELP_DOCS.REPO_DEFAULT_BRANCH', 'main') #Ignored if self.repo_url is empty + self.include_root_readme_file = not(get_settings()['PR_HELP_DOCS.EXCLUDE_ROOT_README']) + self.supported_doc_exts = get_settings()['PR_HELP_DOCS.SUPPORTED_DOC_EXTS'] + self.docs_path = get_settings()['PR_HELP_DOCS.DOCS_PATH'] + + retrieved_settings = [self.include_root_readme_file, self.supported_doc_exts, self.docs_path] + if any([setting is None for setting in retrieved_settings]): + raise Exception(f"One of the settings is invalid: {retrieved_settings}") + + self.git_provider = get_git_provider_with_context(ctx_url) + if not self.git_provider: + raise Exception(f"No git provider found at {ctx_url}") if not self.repo_url: - raise Exception(f"Unable to deduce repo url from type: {self.git_provider.__class__.__name__} url: {self.ctx_url}") - get_logger().debug(f"deduced repo url: {self.repo_url}") - self.repo_desired_branch = None #Inferred from the repo provider. - - self.ai_handler = ai_handler() - self.vars = { - "docs_url": self.repo_url, - "question": self.question, - "snippets": "", - } - self.token_handler = TokenHandler(None, - self.vars, - get_settings().pr_help_docs_prompts.system, - get_settings().pr_help_docs_prompts.user) + self.repo_url_given_explicitly = False + get_logger().debug(f"No explicit repo url provided, deducing it from type: {self.git_provider.__class__.__name__} " + f"context url: {self.ctx_url}") + self.repo_url = self.git_provider.get_git_repo_url(self.ctx_url) + if not self.repo_url: + raise Exception(f"Unable to deduce repo url from type: {self.git_provider.__class__.__name__} url: {self.ctx_url}") + get_logger().debug(f"deduced repo url: {self.repo_url}") + self.repo_desired_branch = None #Inferred from the repo provider. + + self.ai_handler = ai_handler() + self.vars = { + "docs_url": self.repo_url, + "question": self.question, + "snippets": "", + } + self.token_handler = TokenHandler(None, + self.vars, + get_settings().pr_help_docs_prompts.system, + get_settings().pr_help_docs_prompts.user) + except Exception as e: + get_logger().exception(f"Caught exception during init. Setting self.question to None to prevent run() to do anything.") + self.question = None async def run(self): if not self.question: @@ -246,7 +333,93 @@ async def run(self): try: # Clone the repository and gather relevant documentation files. - docs_prompt = None + docs_filepath_to_contents = self._gen_filenames_to_contents_map_from_repo() + + #Generate prompt for the AI model. This will be the full text of all the documentation files combined. + docs_prompt = aggregate_documentation_files_for_prompt_contents(docs_filepath_to_contents) + if not docs_filepath_to_contents or not docs_prompt: + get_logger().warning(f"Could not find any usable documentation. Returning with no result...") + return None + docs_prompt_to_send_to_model = docs_prompt + + # Estimate how many tokens will be needed. + # In case the expected number of tokens exceeds LLM limits, retry with just headings, asking the LLM to rank according to relevance to the question. + # Based on returned ranking, rerun but sort the documents accordingly, this time, trim in case of exceeding limit. + + #First, check if the text is not too long to even query the LLM provider: + max_allowed_txt_input = get_maximal_text_input_length_for_token_count_estimation() + invoke_llm_just_with_headings = self._trim_docs_input(docs_prompt_to_send_to_model, max_allowed_txt_input, + only_return_if_trim_needed=True) + if invoke_llm_just_with_headings: + #Entire docs is too long. Rank and return according to relevance. + docs_prompt_to_send_to_model = await self._rank_docs_and_return_them_as_prompt(docs_filepath_to_contents, + max_allowed_txt_input) + + if not docs_prompt_to_send_to_model: + get_logger().error("Failed to generate docs prompt for model. Returning with no result...") + return + # At this point, either all original documents be used (if their total length doesn't exceed limits), or only those selected. + self.vars['snippets'] = docs_prompt_to_send_to_model.strip() + # Run the AI model and extract sections from its response + response = await retry_with_fallback_models(PredictionPreparator(self.ai_handler, self.vars, + get_settings().pr_help_docs_prompts.system, + get_settings().pr_help_docs_prompts.user), + model_type=ModelType.REGULAR) + response_yaml = load_yaml(response) + if not response_yaml: + get_logger().error("Failed to parse the AI response.", artifacts={'response': response}) + return + response_str = response_yaml.get('response') + relevant_sections = response_yaml.get('relevant_sections') + if not response_str or not relevant_sections: + get_logger().error("Failed to extract response/relevant sections.", + artifacts={'raw_response': response, 'response_str': response_str, 'relevant_sections': relevant_sections}) + return + if int(response_yaml.get('question_is_relevant', '1')) == 0: + get_logger().warning(f"Question is not relevant. Returning without an answer...", + artifacts={'raw_response': response}) + return + + # Format the response as markdown + answer_str = self._format_model_answer(response_str, relevant_sections) + if self.return_as_string: #Skip publishing + return answer_str + #Otherwise, publish the answer if answer is non empty and publish is not turned off: + if answer_str and get_settings().config.publish_output: + self.git_provider.publish_comment(answer_str) + else: + get_logger().info("Answer:", artifacts={'answer_str': answer_str}) + return answer_str + except Exception as e: + get_logger().exception('failed to provide answer to given user question as a result of a thrown exception (see above)') + + def _find_all_document_files_matching_exts(self, abs_docs_path: str, ignore_readme=False, max_allowed_files=5000) -> list[str]: + try: + matching_files = [] + + # Ensure extensions don't have leading dots and are lowercase + dotless_extensions = [ext.lower().lstrip('.') for ext in self.supported_doc_exts] + + # Walk through directory and subdirectories + file_cntr = 0 + for root, _, files in os.walk(abs_docs_path): + for file in files: + if ignore_readme and root == abs_docs_path and file.lower() in [f"readme.{ext}" for ext in dotless_extensions]: + continue + # Check if file has one of the specified extensions + if any(file.lower().endswith(f'.{ext}') for ext in dotless_extensions): + file_cntr+=1 + matching_files.append(os.path.join(root, file)) + if file_cntr >= max_allowed_files: + get_logger().warning(f"Found at least {max_allowed_files} files in {abs_docs_path}, skipping the rest.") + return matching_files + return matching_files + except Exception as e: + get_logger().exception(f"Unexpected exception thrown. Returning empty list.") + return [] + + def _gen_filenames_to_contents_map_from_repo(self) -> dict[str, str]: + try: with TemporaryDirectory() as tmp_dir: get_logger().debug(f"About to clone repository: {self.repo_url} to temporary directory: {tmp_dir}...") returned_cloned_repo_root = self.git_provider.clone(self.repo_url, tmp_dir, remove_dest_folder=False) @@ -268,103 +441,120 @@ async def run(self): ignore_readme=(self.docs_path=='.'))) if not doc_files: get_logger().warning(f"No documentation files found matching file extensions: " - f"{self.supported_doc_exts} under repo: {self.repo_url} path: {self.docs_path}") - return None + f"{self.supported_doc_exts} under repo: {self.repo_url} " + f"path: {self.docs_path}. Returning empty dict.") + return {} - get_logger().info(f'Answering a question inside context {self.ctx_url} for repo: {self.repo_url}' - f' using the following documentation files: ', artifacts={'doc_files': doc_files}) + get_logger().info(f'For context {self.ctx_url} and repo: {self.repo_url}' + f' will be using the following documentation files: ', + artifacts={'doc_files': doc_files}) - docs_prompt = aggregate_documentation_files_for_prompt_contents(returned_cloned_repo_root.path, doc_files) - if not docs_prompt: - get_logger().warning(f"Error reading one of the documentation files. Returning with no result...") - return None - docs_prompt_to_send_to_model = docs_prompt + return map_documentation_files_to_contents(returned_cloned_repo_root.path, doc_files) + except Exception as e: + get_logger().exception(f"Unexpected exception thrown. Returning empty dict.") + return {} - # Estimate how many tokens will be needed. Trim in case of exceeding limit. - # Firstly, check if text needs to be trimmed, as some models fail to return the estimated token count if the input text is too long. - max_allowed_txt_input = get_maximal_text_input_length_for_token_count_estimation() - if len(docs_prompt_to_send_to_model) >= max_allowed_txt_input: - get_logger().warning(f"Text length: {len(docs_prompt_to_send_to_model)} exceeds the current returned limit of {max_allowed_txt_input} just for token count estimation. Trimming the text...") - docs_prompt_to_send_to_model = docs_prompt_to_send_to_model[:max_allowed_txt_input] + def _trim_docs_input(self, docs_input: str, max_allowed_txt_input: int, only_return_if_trim_needed=False) -> bool|str: + try: + if len(docs_input) >= max_allowed_txt_input: + get_logger().warning( + f"Text length: {len(docs_input)} exceeds the current returned limit of {max_allowed_txt_input} just for token count estimation. Trimming the text...") + if only_return_if_trim_needed: + return True + docs_input = docs_input[:max_allowed_txt_input] # Then, count the tokens in the prompt. If the count exceeds the limit, trim the text. - token_count = self.token_handler.count_tokens(docs_prompt_to_send_to_model, force_accurate=True) + token_count = self.token_handler.count_tokens(docs_input, force_accurate=True) get_logger().debug(f"Estimated token count of documentation to send to model: {token_count}") model = get_settings().config.model if model in MAX_TOKENS: - max_tokens_full = MAX_TOKENS[model] # note - here we take the actual max tokens, without any reductions. we do aim to get the full documentation website in the prompt + max_tokens_full = MAX_TOKENS[ + model] # note - here we take the actual max tokens, without any reductions. we do aim to get the full documentation website in the prompt else: max_tokens_full = get_max_tokens(model) - delta_output = 5000 #Elbow room to reduce chance of exceeding token limit or model paying less attention to prompt guidelines. + delta_output = 5000 # Elbow room to reduce chance of exceeding token limit or model paying less attention to prompt guidelines. if token_count > max_tokens_full - delta_output: - docs_prompt_to_send_to_model = clean_markdown_content(docs_prompt_to_send_to_model) #Reduce unnecessary text/images/etc. - get_logger().info(f"Token count {token_count} exceeds the limit {max_tokens_full - delta_output}. Attempting to clip text to fit within the limit...") - docs_prompt_to_send_to_model = clip_tokens(docs_prompt_to_send_to_model, max_tokens_full - delta_output, + if only_return_if_trim_needed: + return True + docs_input = clean_markdown_content( + docs_input) # Reduce unnecessary text/images/etc. + get_logger().info( + f"Token count {token_count} exceeds the limit {max_tokens_full - delta_output}. Attempting to clip text to fit within the limit...") + docs_input = clip_tokens(docs_input, max_tokens_full - delta_output, num_input_tokens=token_count) - self.vars['snippets'] = docs_prompt_to_send_to_model.strip() + if only_return_if_trim_needed: + return False + return docs_input + except Exception as e: + # Unexpected exception. Rethrowing it since: + # 1. This is an internal function. + # 2. An empty str/False result is a valid one - would require now checking also for None. + get_logger().exception(f"Unexpected exception thrown. Rethrowing it...") + raise e + + async def _rank_docs_and_return_them_as_prompt(self, docs_filepath_to_contents: dict[str, str], max_allowed_txt_input: int) -> str: + try: + #Return just file name and their headings (if exist): + docs_prompt_to_send_to_model = ( + aggregate_documentation_files_for_prompt_contents(docs_filepath_to_contents, + return_just_headings=True)) + # Verify list of headings does not exceed limits - trim it if it does. + docs_prompt_to_send_to_model = self._trim_docs_input(docs_prompt_to_send_to_model, max_allowed_txt_input, + only_return_if_trim_needed=False) + if not docs_prompt_to_send_to_model: + get_logger().error("_trim_docs_input returned an empty result.") + return "" + self.vars['snippets'] = docs_prompt_to_send_to_model.strip() # Run the AI model and extract sections from its response response = await retry_with_fallback_models(PredictionPreparator(self.ai_handler, self.vars, - get_settings().pr_help_docs_prompts.system, - get_settings().pr_help_docs_prompts.user), + get_settings().pr_help_docs_headings_prompts.system, + get_settings().pr_help_docs_headings_prompts.user), model_type=ModelType.REGULAR) response_yaml = load_yaml(response) if not response_yaml: - get_logger().exception("Failed to parse the AI response.", artifacts={'response': response}) - raise Exception(f"Failed to parse the AI response.") - response_str = response_yaml.get('response') - relevant_sections = response_yaml.get('relevant_sections') - if not response_str or not relevant_sections: - get_logger().exception("Failed to extract response/relevant sections.", - artifacts={'response_str': response_str, 'relevant_sections': relevant_sections}) - raise Exception(f"Failed to extract response/relevant sections.") + get_logger().error("Failed to parse the AI response.", artifacts={'response': response}) + return "" + # else: Sanitize the output so that the file names match 1:1 dictionary keys. Do this via the file index and not its name, which may be altered by the model. + valid_indices = [int(entry['idx']) for entry in response_yaml.get('relevant_files_ranking') + if int(entry['idx']) >= 0 and int(entry['idx']) < len(docs_filepath_to_contents)] + valid_file_paths = [list(docs_filepath_to_contents.keys())[idx] for idx in valid_indices] + selected_docs_dict = {file_path: docs_filepath_to_contents[file_path] for file_path in valid_file_paths} + docs_prompt = aggregate_documentation_files_for_prompt_contents(selected_docs_dict) + docs_prompt_to_send_to_model = docs_prompt - # Format the response as markdown - canonical_url_prefix, canonical_url_suffix = self.git_provider.get_canonical_url_parts(repo_git_url=self.repo_url if self.repo_url_given_explicitly else None, - desired_branch=self.repo_desired_branch) - answer_str = format_markdown_q_and_a_response(self.question, response_str, relevant_sections, self.supported_doc_exts, canonical_url_prefix, canonical_url_suffix) + # Check if the updated list of documents does not exceed limits and trim if it does: + docs_prompt_to_send_to_model = self._trim_docs_input(docs_prompt_to_send_to_model, max_allowed_txt_input, + only_return_if_trim_needed=False) + if not docs_prompt_to_send_to_model: + get_logger().error("_trim_docs_input returned an empty result.") + return "" + return docs_prompt_to_send_to_model + except Exception as e: + get_logger().exception(f"Unexpected exception thrown. Returning empty result.") + return "" + + def _format_model_answer(self, response_str: str, relevant_sections: list[dict[str, str]]) -> str: + try: + canonical_url_prefix, canonical_url_suffix = ( + self.git_provider.get_canonical_url_parts(repo_git_url=self.repo_url if self.repo_url_given_explicitly else None, + desired_branch=self.repo_desired_branch)) + answer_str = format_markdown_q_and_a_response(self.question, response_str, relevant_sections, + self.supported_doc_exts, canonical_url_prefix, canonical_url_suffix) if answer_str: #Remove the question phrase and replace with light bulb and a heading mentioning this is an automated answer: answer_str = modify_answer_section(answer_str) - # For PR help docs, we return the answer string instead of publishing it + #In case the response should not be published and returned as string, stop here: if answer_str and self.return_as_string: - if int(response_yaml.get('question_is_relevant', '1')) == 0: - get_logger().warning(f"Chat help docs answer would be ignored due to an invalid question.", - artifacts={'answer_str': answer_str}) - return "" get_logger().info(f"Chat help docs answer", artifacts={'answer_str': answer_str}) return answer_str - - # Publish the answer - if not answer_str or int(response_yaml.get('question_is_relevant', '1')) == 0: + if not answer_str: get_logger().info(f"No answer found") return "" - if self.git_provider.is_supported("gfm_markdown") and get_settings().pr_help_docs.enable_help_text: answer_str += "
\n\n
💡 Tool usage guide:
\n\n" answer_str += HelpMessage.get_help_docs_usage_guide() answer_str += "\n
\n" - - if get_settings().config.publish_output: - self.git_provider.publish_comment(answer_str) - else: - get_logger().info("Answer:", artifacts={'answer_str': answer_str}) - - except: - get_logger().exception('failed to provide answer to given user question as a result of a thrown exception (see above)') - - - def _find_all_document_files_matching_exts(self, abs_docs_path: str, ignore_readme=False) -> List[str]: - matching_files = [] - - # Ensure extensions don't have leading dots and are lowercase - dotless_extensions = [ext.lower().lstrip('.') for ext in self.supported_doc_exts] - - # Walk through directory and subdirectories - for root, _, files in os.walk(abs_docs_path): - for file in files: - if ignore_readme and root == abs_docs_path and file.lower() in [f"readme.{ext}" for ext in dotless_extensions]: - continue - # Check if file has one of the specified extensions - if any(file.lower().endswith(f'.{ext}') for ext in dotless_extensions): - matching_files.append(os.path.join(root, file)) - return matching_files + return answer_str + except Exception as e: + get_logger().exception(f"Unexpected exception thrown. Returning empty result.") + return "" From 92542259491a2043a35f466992bc42769801f157 Mon Sep 17 00:00:00 2001 From: ofir-frd <85901822+ofir-frd@users.noreply.github.com> Date: Thu, 3 Apr 2025 13:23:41 +0300 Subject: [PATCH 053/365] fix: add error handling for missing review data in PR reviewer --- pr_agent/tools/pr_reviewer.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pr_agent/tools/pr_reviewer.py b/pr_agent/tools/pr_reviewer.py index 0f4eff227b..ff48819f08 100644 --- a/pr_agent/tools/pr_reviewer.py +++ b/pr_agent/tools/pr_reviewer.py @@ -229,6 +229,10 @@ def _prepare_pr_review(self) -> str: first_key=first_key, last_key=last_key) github_action_output(data, 'review') + if 'review' not in data: + get_logger().exception("Failed to parse review data", artifact={"data": data}) + return "" + # move data['review'] 'key_issues_to_review' key to the end of the dictionary if 'key_issues_to_review' in data['review']: key_issues_to_review = data['review'].pop('key_issues_to_review') From c74a2efdb7c69820890837ed7ee710982492cd3c Mon Sep 17 00:00:00 2001 From: mrT23 Date: Thu, 3 Apr 2025 17:44:01 +0300 Subject: [PATCH 054/365] Add code validation documentation for PR code suggestions --- docs/docs/core-abilities/code_validation.md | 38 +++++++++++++++++++++ docs/docs/core-abilities/index.md | 5 +-- docs/mkdocs.yml | 5 +-- 3 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 docs/docs/core-abilities/code_validation.md diff --git a/docs/docs/core-abilities/code_validation.md b/docs/docs/core-abilities/code_validation.md new file mode 100644 index 0000000000..f074324d30 --- /dev/null +++ b/docs/docs/core-abilities/code_validation.md @@ -0,0 +1,38 @@ +[//]: # (## Code Validation 💎) + +## Introduction +The Git environment usually represents the final stage before code enters production. Hence, Detecting bugs and issues during the review process is critical. + +The [`improve`](https://qodo-merge-docs.qodo.ai/tools/improve/) tool provides actionable code suggestions for your pull requests, aiming to help detect and fix bugs and problems. +By default, suggestions appear as a comment in a table format: + +![code_suggestions_as_comment_closed.png](https://codium.ai/images/pr_agent/code_suggestions_as_comment_closed.png){width=512} + +![code_suggestions_as_comment_open.png](https://codium.ai/images/pr_agent/code_suggestions_as_comment_open.png){width=512} + +## Validation of Code Suggestions + +Each suggestion it the table can be "applied", converting it to a committable Git code change that can be committed directly to the PR. +This approach allows fix issues without returning to your IDE for manual edits — significantly faster and more convenient. + +However, committing a suggestion in a Git environment carries more risk than in a local IDE, as you don't have the opportunity to fully run and test the code before committing. + +To balance convenience with safety, Qodo Merge implements a dual validation system for each generated code suggestion: + +1) **Localization** - Qodo Merge confirms that the suggestion's line numbers and surrounding code, as predicted by the model, actually match the repo code. This means that the model correctly identified the context and location of the code to be changed. + +2) **"Compilation"** - Using static code analysis, Qodo Merge verifies that after applying the suggestion, the file will still be valid, meaning tree-sitter syntax processing will not throw an error. This process is relevant for multiple programming languages, see [here](https://pypi.org/project/tree-sitter-languages/) for the full list of supported languages. + +When a suggestion fails to meet these validation criteria, it may still provide valuable feedback, but isn't suitable for direct application to the PR. +In such cases, Qodo Merge will omit the 'apply' checkbox and instead display: + +`[To ensure code accuracy, apply this suggestion manually]` + +## Conclusion + +The validation methods described above enhance the reliability of code suggestions and help PR authors determine which suggestions are safer to apply in the Git environment. +Of course, additional factors should be considered, such as suggestion complexity and potential code impact. + +Human judgment remains essential. After clicking 'apply', Qodo Merge still presents the 'before' and 'after' code snippets for review, allowing you to assess the changes before finalizing the commit. + +![improve](https://codium.ai/images/pr_agent/improve.png){width=512} \ No newline at end of file diff --git a/docs/docs/core-abilities/index.md b/docs/docs/core-abilities/index.md index 7a97a41682..1e19829269 100644 --- a/docs/docs/core-abilities/index.md +++ b/docs/docs/core-abilities/index.md @@ -7,10 +7,11 @@ Qodo Merge utilizes a variety of core abilities to provide a comprehensive and e - [Dynamic context](https://qodo-merge-docs.qodo.ai/core-abilities/dynamic_context/) - [Self-reflection](https://qodo-merge-docs.qodo.ai/core-abilities/self_reflection/) - [Impact evaluation](https://qodo-merge-docs.qodo.ai/core-abilities/impact_evaluation/) -- [Interactivity](https://qodo-merge-docs.qodo.ai/core-abilities/interactivity/) -- [Compression strategy](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) - [RAG Context Enrichment](https://qodo-merge-docs.qodo.ai/core-abilities/rag_context_enrichment/) - [Static code analysis](https://qodo-merge-docs.qodo.ai/core-abilities/static_code_analysis/) +- [Compression strategy](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) +- [Code validation](https://qodo-merge-docs.qodo.ai/core-abilities/code_validation/) +- [Interactivity](https://qodo-merge-docs.qodo.ai/core-abilities/interactivity/) - [Code fine-tuning benchmark](https://qodo-merge-docs.qodo.ai/finetuning_benchmark/) ## Blogs diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index eed3f0c81d..ed7c309620 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -47,10 +47,11 @@ nav: - Dynamic context: 'core-abilities/dynamic_context.md' - Self-reflection: 'core-abilities/self_reflection.md' - Impact evaluation: 'core-abilities/impact_evaluation.md' - - Interactivity: 'core-abilities/interactivity.md' - - Compression strategy: 'core-abilities/compression_strategy.md' - RAG Context Enrichment: 'core-abilities/rag_context_enrichment.md' - Static code analysis: 'core-abilities/static_code_analysis.md' + - Compression strategy: 'core-abilities/compression_strategy.md' + - Code validation: 'core-abilities/code_validation.md' + - Interactivity: 'core-abilities/interactivity.md' - Code Fine-tuning Benchmark: 'finetuning_benchmark/index.md' - Chrome Extension: - Qodo Merge Chrome Extension: 'chrome-extension/index.md' From c2e61b7113a72a5163cdbdbebd0a08eb5fbd9977 Mon Sep 17 00:00:00 2001 From: Tal Date: Thu, 3 Apr 2025 17:52:06 +0300 Subject: [PATCH 055/365] Update docs/docs/core-abilities/code_validation.md Co-authored-by: qodo-merge-pro-for-open-source[bot] <189517486+qodo-merge-pro-for-open-source[bot]@users.noreply.github.com> --- docs/docs/core-abilities/code_validation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/core-abilities/code_validation.md b/docs/docs/core-abilities/code_validation.md index f074324d30..9c7a71cf7e 100644 --- a/docs/docs/core-abilities/code_validation.md +++ b/docs/docs/core-abilities/code_validation.md @@ -12,7 +12,7 @@ By default, suggestions appear as a comment in a table format: ## Validation of Code Suggestions -Each suggestion it the table can be "applied", converting it to a committable Git code change that can be committed directly to the PR. +Each suggestion in the table can be "applied", converting it to a committable Git code change that can be committed directly to the PR. This approach allows fix issues without returning to your IDE for manual edits — significantly faster and more convenient. However, committing a suggestion in a Git environment carries more risk than in a local IDE, as you don't have the opportunity to fully run and test the code before committing. From eac20ba0e2b9075c0b625f11a2eaaae7bd86e7b3 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Thu, 3 Apr 2025 17:52:38 +0300 Subject: [PATCH 056/365] Add code validation documentation for PR code suggestions --- docs/docs/core-abilities/code_validation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/core-abilities/code_validation.md b/docs/docs/core-abilities/code_validation.md index 9c7a71cf7e..83d1c676fa 100644 --- a/docs/docs/core-abilities/code_validation.md +++ b/docs/docs/core-abilities/code_validation.md @@ -13,7 +13,7 @@ By default, suggestions appear as a comment in a table format: ## Validation of Code Suggestions Each suggestion in the table can be "applied", converting it to a committable Git code change that can be committed directly to the PR. -This approach allows fix issues without returning to your IDE for manual edits — significantly faster and more convenient. +This approach allows to fix issues without returning to your IDE for manual edits — significantly faster and more convenient. However, committing a suggestion in a Git environment carries more risk than in a local IDE, as you don't have the opportunity to fully run and test the code before committing. From 2683b78e34634e010527f5d806ead7135d8e0ef2 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Thu, 3 Apr 2025 17:54:20 +0300 Subject: [PATCH 057/365] Add code validation documentation for PR code suggestions --- docs/docs/core-abilities/code_validation.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/docs/core-abilities/code_validation.md b/docs/docs/core-abilities/code_validation.md index 83d1c676fa..656fb2f41c 100644 --- a/docs/docs/core-abilities/code_validation.md +++ b/docs/docs/core-abilities/code_validation.md @@ -1,5 +1,3 @@ -[//]: # (## Code Validation 💎) - ## Introduction The Git environment usually represents the final stage before code enters production. Hence, Detecting bugs and issues during the review process is critical. From 1ffddaf7191c9a6b32895c902e268c77661d1ade Mon Sep 17 00:00:00 2001 From: mrT23 Date: Thu, 3 Apr 2025 19:36:05 +0300 Subject: [PATCH 058/365] Add self-reflection reference to code validation documentation --- docs/docs/core-abilities/code_validation.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/docs/core-abilities/code_validation.md b/docs/docs/core-abilities/code_validation.md index 656fb2f41c..849bc18324 100644 --- a/docs/docs/core-abilities/code_validation.md +++ b/docs/docs/core-abilities/code_validation.md @@ -26,6 +26,9 @@ In such cases, Qodo Merge will omit the 'apply' checkbox and instead display: `[To ensure code accuracy, apply this suggestion manually]` +All suggestions that pass these validations undergo a final stage of **self-reflection**, where the AI model evaluates, scores, and re-ranks its own suggestions, eliminating any that are irrelevant or incorrect. +Read more about this process in the [self-reflection](https://qodo-merge-docs.qodo.ai/core-abilities/self_reflection/) page. + ## Conclusion The validation methods described above enhance the reliability of code suggestions and help PR authors determine which suggestions are safer to apply in the Git environment. From 4fa2d821799f511a946273411dd814dc0d7fc22a Mon Sep 17 00:00:00 2001 From: Jose Martinez Date: Thu, 3 Apr 2025 14:07:10 -0500 Subject: [PATCH 059/365] Set default log level to DEBUG in configuration and logger setup --- pr_agent/servers/gitlab_webhook.py | 2 +- pr_agent/settings/configuration.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pr_agent/servers/gitlab_webhook.py b/pr_agent/servers/gitlab_webhook.py index 098e16c721..0aef933dee 100644 --- a/pr_agent/servers/gitlab_webhook.py +++ b/pr_agent/servers/gitlab_webhook.py @@ -19,7 +19,7 @@ from pr_agent.log import LoggingFormat, get_logger, setup_logger from pr_agent.secret_providers import get_secret_provider -setup_logger(fmt=LoggingFormat.JSON, level=get_settings().get("CONFIG.LOG_LEVEL", "INFO")) +setup_logger(fmt=LoggingFormat.JSON, level=get_settings().get("CONFIG.LOG_LEVEL", "DEBUG")) router = APIRouter() secret_provider = get_secret_provider() if get_settings().get("CONFIG.SECRET_PROVIDER") else None diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index 4213eab132..168ebc47d6 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -17,7 +17,7 @@ publish_output_progress=true verbosity_level=0 # 0,1,2 use_extra_bad_extensions=false # Log -log_level="INFO" +log_level="DEBUG" # Configurations use_wiki_settings_file=true use_repo_settings_file=true From db06a8e49ea07dde2851a6ea301a13654e52026c Mon Sep 17 00:00:00 2001 From: Jose Martinez Date: Thu, 3 Apr 2025 14:09:11 -0500 Subject: [PATCH 060/365] Update github logger setup to use dynamic log level from configuration --- pr_agent/servers/github_app.py | 2 +- pr_agent/servers/github_polling.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pr_agent/servers/github_app.py b/pr_agent/servers/github_app.py index 1e753a9581..4576bd9d7b 100644 --- a/pr_agent/servers/github_app.py +++ b/pr_agent/servers/github_app.py @@ -24,7 +24,7 @@ from pr_agent.log import LoggingFormat, get_logger, setup_logger from pr_agent.servers.utils import DefaultDictWithTimeout, verify_signature -setup_logger(fmt=LoggingFormat.JSON, level="DEBUG") +setup_logger(fmt=LoggingFormat.JSON, level=get_settings().get("CONFIG.LOG_LEVEL", "DEBUG")) base_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) build_number_path = os.path.join(base_path, "build_number.txt") if os.path.exists(build_number_path): diff --git a/pr_agent/servers/github_polling.py b/pr_agent/servers/github_polling.py index d11b73f6e6..95f9d911b5 100644 --- a/pr_agent/servers/github_polling.py +++ b/pr_agent/servers/github_polling.py @@ -13,7 +13,7 @@ from pr_agent.git_providers import get_git_provider from pr_agent.log import LoggingFormat, get_logger, setup_logger -setup_logger(fmt=LoggingFormat.JSON, level="DEBUG") +setup_logger(fmt=LoggingFormat.JSON, level=get_settings().get("CONFIG.LOG_LEVEL", "DEBUG")) NOTIFICATION_URL = "https://api.github.com/notifications" From 7987fd1be7fa8bc8711fec1d4773b1178a49227a Mon Sep 17 00:00:00 2001 From: Jose Martinez Date: Thu, 3 Apr 2025 14:10:11 -0500 Subject: [PATCH 061/365] Update bitbucket logger setup to use dynamic log level from configuration --- pr_agent/servers/bitbucket_app.py | 2 +- pr_agent/servers/bitbucket_server_webhook.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pr_agent/servers/bitbucket_app.py b/pr_agent/servers/bitbucket_app.py index 5d93f1676e..a641adebc5 100644 --- a/pr_agent/servers/bitbucket_app.py +++ b/pr_agent/servers/bitbucket_app.py @@ -25,7 +25,7 @@ from pr_agent.log import LoggingFormat, get_logger, setup_logger from pr_agent.secret_providers import get_secret_provider -setup_logger(fmt=LoggingFormat.JSON, level="DEBUG") +setup_logger(fmt=LoggingFormat.JSON, level=get_settings().get("CONFIG.LOG_LEVEL", "DEBUG")) router = APIRouter() secret_provider = get_secret_provider() if get_settings().get("CONFIG.SECRET_PROVIDER") else None diff --git a/pr_agent/servers/bitbucket_server_webhook.py b/pr_agent/servers/bitbucket_server_webhook.py index b3c59a675d..873e931fe6 100644 --- a/pr_agent/servers/bitbucket_server_webhook.py +++ b/pr_agent/servers/bitbucket_server_webhook.py @@ -21,7 +21,7 @@ from pr_agent.log import LoggingFormat, get_logger, setup_logger from pr_agent.servers.utils import verify_signature -setup_logger(fmt=LoggingFormat.JSON, level="DEBUG") +setup_logger(fmt=LoggingFormat.JSON, level=get_settings().get("CONFIG.LOG_LEVEL", "DEBUG")) router = APIRouter() From b7b533ddf6f87d133ae7e74d7b51b97183abfd59 Mon Sep 17 00:00:00 2001 From: Jose Martinez Date: Thu, 3 Apr 2025 14:10:52 -0500 Subject: [PATCH 062/365] Update azuredevops logger setup to use dynamic log level from configuration --- pr_agent/servers/azuredevops_server_webhook.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/servers/azuredevops_server_webhook.py b/pr_agent/servers/azuredevops_server_webhook.py index ce6e050b33..bb97b83962 100644 --- a/pr_agent/servers/azuredevops_server_webhook.py +++ b/pr_agent/servers/azuredevops_server_webhook.py @@ -25,7 +25,7 @@ from pr_agent.git_providers.utils import apply_repo_settings from pr_agent.log import LoggingFormat, get_logger, setup_logger -setup_logger(fmt=LoggingFormat.JSON, level="DEBUG") +setup_logger(fmt=LoggingFormat.JSON, level=get_settings().get("CONFIG.LOG_LEVEL", "DEBUG")) security = HTTPBasic() router = APIRouter() available_commands_rgx = re.compile(r"^\/(" + "|".join(command2class.keys()) + r")\s*") From 76b447a62a5bc0d93bef1e958d2025c10b187433 Mon Sep 17 00:00:00 2001 From: Jose Martinez Date: Thu, 3 Apr 2025 14:24:59 -0500 Subject: [PATCH 063/365] Add logging configuration details to usage guide --- docs/docs/usage-guide/additional_configurations.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/docs/usage-guide/additional_configurations.md b/docs/docs/usage-guide/additional_configurations.md index 810c95c82d..c24cb777da 100644 --- a/docs/docs/usage-guide/additional_configurations.md +++ b/docs/docs/usage-guide/additional_configurations.md @@ -139,6 +139,17 @@ user=""" ``` Note that the new prompt will need to generate an output compatible with the relevant [post-process function](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/tools/pr_description.py#L137). +## Log Level + +Qodo Merge allows you to control the verbosity of logging by using the `log_level` configuration parameter. This is particularly useful for troubleshooting and debugging issues with your PR workflows. + +``` +[config] +log_level = "DEBUG" # Options: "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL" +``` + +The default log level is "DEBUG", which provides detailed output of all operations. If you prefer less verbose logs, you can set higher log levels like "INFO" or "WARNING". + ## Integrating with Logging Observability Platforms Various logging observability tools can be used out-of-the box when using the default LiteLLM AI Handler. Simply configure the LiteLLM callback settings in `configuration.toml` and set environment variables according to the LiteLLM [documentation](https://docs.litellm.ai/docs/). From 9b19fcdc90bb47bcc01df4a809ef0f1c8eb6d06d Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Fri, 4 Apr 2025 05:13:15 +0800 Subject: [PATCH 064/365] Add support of OpenAI GPT-4.5 Preview model Reference: - https://openai.com/index/introducing-gpt-4-5/ - https://platform.openai.com/docs/models/gpt-4.5-preview --- pr_agent/algo/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pr_agent/algo/__init__.py b/pr_agent/algo/__init__.py index 483ea2e057..6bea799448 100644 --- a/pr_agent/algo/__init__.py +++ b/pr_agent/algo/__init__.py @@ -20,6 +20,8 @@ 'gpt-4o-mini-2024-07-18': 128000, # 128K, but may be limited by config.max_model_tokens 'gpt-4o-2024-08-06': 128000, # 128K, but may be limited by config.max_model_tokens 'gpt-4o-2024-11-20': 128000, # 128K, but may be limited by config.max_model_tokens + 'gpt-4.5-preview': 128000, # 128K, but may be limited by config.max_model_tokens + 'gpt-4.5-preview-2025-02-27': 128000, # 128K, but may be limited by config.max_model_tokens 'o1-mini': 128000, # 128K, but may be limited by config.max_model_tokens 'o1-mini-2024-09-12': 128000, # 128K, but may be limited by config.max_model_tokens 'o1-preview': 128000, # 128K, but may be limited by config.max_model_tokens From 282fb0ed2857ff955ba44220fa518692d3b3dbc4 Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Fri, 4 Apr 2025 11:00:44 +0300 Subject: [PATCH 065/365] Add interactive features documentation for PR workflow tools --- docs/docs/core-abilities/interactivity.md | 43 +++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/docs/docs/core-abilities/interactivity.md b/docs/docs/core-abilities/interactivity.md index e107f781d6..e0c2a19651 100644 --- a/docs/docs/core-abilities/interactivity.md +++ b/docs/docs/core-abilities/interactivity.md @@ -1,2 +1,41 @@ -## Interactive invocation 💎 -TBD +# Interactivity + +`Supported Git Platforms: GitHub, GitLab` + +## Overview + +Qodo Merge transforms static code reviews into interactive experiences by enabling direct actions from pull request (PR) comments. +Developers can immediately trigger actions and apply changes with simple checkbox clicks. This focused workflow maintains context while dramatically reducing the time between PR creation and final merge. +The approach eliminates manual steps, provides clear visual indicators, and creates immediate feedback loops all within the same interface. + +## Key Interactive Features + +### 1\. Interactive `/Improve` Tool + +The `/improve` command delivers a comprehensive interactive experience: + +- _**Apply this suggestion**_: Clicking this checkbox instantly converts a suggestion into a committable code change + +- _**More**_: Triggers additional suggestions generation while keeping each suggestion focused and relevant as the original set + +- _**Update**_: Triggers a re-analysis of the code, providing updated suggestions based on the latest changes + +- _**Author self-review**_: Interactive acknowledgment that developers have opened and reviewed collapsed suggestions + + +### 2\. Interactive `/Analyze` Tool + +The `/analyze` command provides component-level analysis with interactive options for each identified code component: + +- Interactive checkboxes to generate tests, documentation, and code suggestions for specific components + +- On-demand similar code search that activates when a checkbox is clicked + +- Component-specific actions that trigger only for the selected elements, providing focused assistance + + +### 3\. Interactive `/Help` Tool + +The `/help` command not only lists available tools and their descriptions but also enables immediate tool invocation through interactive checkboxes. +When a user checks a tool's checkbox, Qodo Merge instantly triggers that tool without requiring additional commands. +This transforms the standard help menu into an interactive launch pad for all Qodo Merge capabilities, eliminating context switching by keeping developers within their PR workflow. From ae6c4e741a0d5f472e48d0ba3a9fb4a1399a407f Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Fri, 4 Apr 2025 11:04:56 +0300 Subject: [PATCH 066/365] Standardize capitalization in documentation navigation links --- docs/docs/core-abilities/index.md | 2 +- docs/mkdocs.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/docs/core-abilities/index.md b/docs/docs/core-abilities/index.md index 1e19829269..ffb50cb61a 100644 --- a/docs/docs/core-abilities/index.md +++ b/docs/docs/core-abilities/index.md @@ -7,7 +7,7 @@ Qodo Merge utilizes a variety of core abilities to provide a comprehensive and e - [Dynamic context](https://qodo-merge-docs.qodo.ai/core-abilities/dynamic_context/) - [Self-reflection](https://qodo-merge-docs.qodo.ai/core-abilities/self_reflection/) - [Impact evaluation](https://qodo-merge-docs.qodo.ai/core-abilities/impact_evaluation/) -- [RAG Context Enrichment](https://qodo-merge-docs.qodo.ai/core-abilities/rag_context_enrichment/) +- [RAG context enrichment](https://qodo-merge-docs.qodo.ai/core-abilities/rag_context_enrichment/) - [Static code analysis](https://qodo-merge-docs.qodo.ai/core-abilities/static_code_analysis/) - [Compression strategy](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) - [Code validation](https://qodo-merge-docs.qodo.ai/core-abilities/code_validation/) diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index ed7c309620..7ed78331b1 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -47,12 +47,12 @@ nav: - Dynamic context: 'core-abilities/dynamic_context.md' - Self-reflection: 'core-abilities/self_reflection.md' - Impact evaluation: 'core-abilities/impact_evaluation.md' - - RAG Context Enrichment: 'core-abilities/rag_context_enrichment.md' + - RAG context enrichment: 'core-abilities/rag_context_enrichment.md' - Static code analysis: 'core-abilities/static_code_analysis.md' - Compression strategy: 'core-abilities/compression_strategy.md' - Code validation: 'core-abilities/code_validation.md' - Interactivity: 'core-abilities/interactivity.md' - - Code Fine-tuning Benchmark: 'finetuning_benchmark/index.md' + - Code fine-tuning benchmark: 'finetuning_benchmark/index.md' - Chrome Extension: - Qodo Merge Chrome Extension: 'chrome-extension/index.md' - Features: 'chrome-extension/features.md' From cb47cd51445719663e7f9cf7032af94e17191224 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Fri, 4 Apr 2025 11:36:40 +0300 Subject: [PATCH 067/365] Update interactivity documentation with consistent formatting and links --- docs/docs/core-abilities/interactivity.md | 16 +++++++++------- .../usage-guide/additional_configurations.md | 19 ------------------- docs/docs/usage-guide/index.md | 1 - 3 files changed, 9 insertions(+), 27 deletions(-) diff --git a/docs/docs/core-abilities/interactivity.md b/docs/docs/core-abilities/interactivity.md index e0c2a19651..8f7908f60e 100644 --- a/docs/docs/core-abilities/interactivity.md +++ b/docs/docs/core-abilities/interactivity.md @@ -5,14 +5,16 @@ ## Overview Qodo Merge transforms static code reviews into interactive experiences by enabling direct actions from pull request (PR) comments. -Developers can immediately trigger actions and apply changes with simple checkbox clicks. This focused workflow maintains context while dramatically reducing the time between PR creation and final merge. +Developers can immediately trigger actions and apply changes with simple checkbox clicks. + +This focused workflow maintains context while dramatically reducing the time between PR creation and final merge. The approach eliminates manual steps, provides clear visual indicators, and creates immediate feedback loops all within the same interface. ## Key Interactive Features -### 1\. Interactive `/Improve` Tool +### 1\. Interactive `/improve` Tool -The `/improve` command delivers a comprehensive interactive experience: +The [`/improve`](https://qodo-merge-docs.qodo.ai/tools/improve/) command delivers a comprehensive interactive experience: - _**Apply this suggestion**_: Clicking this checkbox instantly converts a suggestion into a committable code change @@ -23,9 +25,9 @@ The `/improve` command delivers a comprehensive interactive experience: - _**Author self-review**_: Interactive acknowledgment that developers have opened and reviewed collapsed suggestions -### 2\. Interactive `/Analyze` Tool +### 2\. Interactive `/analyze` Tool -The `/analyze` command provides component-level analysis with interactive options for each identified code component: +The [`/analyze`](https://qodo-merge-docs.qodo.ai/tools/analyze/) command provides component-level analysis with interactive options for each identified code component: - Interactive checkboxes to generate tests, documentation, and code suggestions for specific components @@ -34,8 +36,8 @@ The `/analyze` command provides component-level analysis with interactive option - Component-specific actions that trigger only for the selected elements, providing focused assistance -### 3\. Interactive `/Help` Tool +### 3\. Interactive `/help` Tool -The `/help` command not only lists available tools and their descriptions but also enables immediate tool invocation through interactive checkboxes. +The [`/help`](https://qodo-merge-docs.qodo.ai/tools/help/) command not only lists available tools and their descriptions but also enables immediate tool invocation through interactive checkboxes. When a user checks a tool's checkbox, Qodo Merge instantly triggers that tool without requiring additional commands. This transforms the standard help menu into an interactive launch pad for all Qodo Merge capabilities, eliminating context switching by keeping developers within their PR workflow. diff --git a/docs/docs/usage-guide/additional_configurations.md b/docs/docs/usage-guide/additional_configurations.md index c24cb777da..578223a7cc 100644 --- a/docs/docs/usage-guide/additional_configurations.md +++ b/docs/docs/usage-guide/additional_configurations.md @@ -120,25 +120,6 @@ Increasing this number provides more context to the model, but will also increas If the PR is too large (see [PR Compression strategy](https://github.com/Codium-ai/pr-agent/blob/main/PR_COMPRESSION.md)), Qodo Merge may automatically set this number to 0, and will use the original git patch. - -## Editing the prompts - -The prompts for the various Qodo Merge tools are defined in the `pr_agent/settings` folder. -In practice, the prompts are loaded and stored as a standard setting object. -Hence, editing them is similar to editing any other configuration value - just place the relevant key in `.pr_agent.toml`file, and override the default value. - -For example, if you want to edit the prompts of the [describe](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/pr_description_prompts.toml) tool, you can add the following to your `.pr_agent.toml` file: -``` -[pr_description_prompt] -system=""" -... -""" -user=""" -... -""" -``` -Note that the new prompt will need to generate an output compatible with the relevant [post-process function](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/tools/pr_description.py#L137). - ## Log Level Qodo Merge allows you to control the verbosity of logging by using the `log_level` configuration parameter. This is particularly useful for troubleshooting and debugging issues with your PR workflows. diff --git a/docs/docs/usage-guide/index.md b/docs/docs/usage-guide/index.md index ce89689309..087dd772b3 100644 --- a/docs/docs/usage-guide/index.md +++ b/docs/docs/usage-guide/index.md @@ -23,5 +23,4 @@ It includes information on how to adjust Qodo Merge configurations, define which - [Working with large PRs](./additional_configurations.md#working-with-large-prs) - [Changing a model](./additional_configurations.md#changing-a-model) - [Patch Extra Lines](./additional_configurations.md#patch-extra-lines) - - [Editing the prompts](./additional_configurations.md#editing-the-prompts) - [Qodo Merge Models](./qodo_merge_models) From d4cc57c32a76a98a8ae0be7acfb8289e5b9bc1ab Mon Sep 17 00:00:00 2001 From: mrT23 Date: Fri, 4 Apr 2025 16:54:23 +0300 Subject: [PATCH 068/365] Improve code validation documentation with clearer suggestion application details --- docs/docs/core-abilities/code_validation.md | 4 ++-- docs/docs/usage-guide/changing_a_model.md | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/docs/core-abilities/code_validation.md b/docs/docs/core-abilities/code_validation.md index 849bc18324..30f03f8b11 100644 --- a/docs/docs/core-abilities/code_validation.md +++ b/docs/docs/core-abilities/code_validation.md @@ -10,7 +10,7 @@ By default, suggestions appear as a comment in a table format: ## Validation of Code Suggestions -Each suggestion in the table can be "applied", converting it to a committable Git code change that can be committed directly to the PR. +Each suggestion in the table can be "applied" by clicking on the `Apply this suggestion` checkbox, converting it to a committable Git code change that can be committed directly to the PR. This approach allows to fix issues without returning to your IDE for manual edits — significantly faster and more convenient. However, committing a suggestion in a Git environment carries more risk than in a local IDE, as you don't have the opportunity to fully run and test the code before committing. @@ -19,7 +19,7 @@ To balance convenience with safety, Qodo Merge implements a dual validation syst 1) **Localization** - Qodo Merge confirms that the suggestion's line numbers and surrounding code, as predicted by the model, actually match the repo code. This means that the model correctly identified the context and location of the code to be changed. -2) **"Compilation"** - Using static code analysis, Qodo Merge verifies that after applying the suggestion, the file will still be valid, meaning tree-sitter syntax processing will not throw an error. This process is relevant for multiple programming languages, see [here](https://pypi.org/project/tree-sitter-languages/) for the full list of supported languages. +2) **"Compilation"** - Using static code analysis, Qodo Merge verifies that after applying the suggestion, the modified file will still be valid, meaning tree-sitter syntax processing will not throw an error. This process is relevant for multiple programming languages, see [here](https://pypi.org/project/tree-sitter-languages/) for the full list of supported languages. When a suggestion fails to meet these validation criteria, it may still provide valuable feedback, but isn't suitable for direct application to the PR. In such cases, Qodo Merge will omit the 'apply' checkbox and instead display: diff --git a/docs/docs/usage-guide/changing_a_model.md b/docs/docs/usage-guide/changing_a_model.md index 2f8080ed36..94861629f0 100644 --- a/docs/docs/usage-guide/changing_a_model.md +++ b/docs/docs/usage-guide/changing_a_model.md @@ -13,6 +13,7 @@ You can give parameters via a configuration file, or from environment variables. !!! note "Model-specific environment variables" See [litellm documentation](https://litellm.vercel.app/docs/proxy/quick_start#supported-llms) for the environment variables needed per model, as they may vary and change over time. Our documentation per-model may not always be up-to-date with the latest changes. + Failing to set the needed keys of a specific model will usually result in litellm not identifying the model type, and failing to utilize it. ### Azure From e8171b02896a2d758d73fc151f4d9a18c8607198 Mon Sep 17 00:00:00 2001 From: saumil-kapadia-stellantispublic <152901173+saumil-kapadia-stellantispublic@users.noreply.github.com> Date: Sun, 6 Apr 2025 22:40:04 +0530 Subject: [PATCH 069/365] Updated Dockerfile.lambda for python version 3.12 Updated Dockerfile.lambda for python version 3.12 --- docker/Dockerfile.lambda | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docker/Dockerfile.lambda b/docker/Dockerfile.lambda index d078b34ec3..46e777ad9b 100644 --- a/docker/Dockerfile.lambda +++ b/docker/Dockerfile.lambda @@ -1,10 +1,10 @@ -FROM public.ecr.aws/lambda/python:3.10 +FROM public.ecr.aws/lambda/python:3.12 -RUN yum update -y && \ - yum install -y gcc python3-devel git && \ - yum clean all +RUN dnf update -y && \ + dnf install -y gcc python3-devel git && \ + dnf clean all -ADD pyproject.toml requirements.txt . +ADD pyproject.toml requirements.txt ./ RUN pip install --no-cache-dir . && rm pyproject.toml RUN pip install --no-cache-dir mangum==0.17.0 COPY pr_agent/ ${LAMBDA_TASK_ROOT}/pr_agent/ From 629de489dda39d4aebdf2a020f0046656551383f Mon Sep 17 00:00:00 2001 From: mrT23 Date: Mon, 7 Apr 2025 08:46:09 +0300 Subject: [PATCH 070/365] Update RAG context enrichment documentation with enterprise plan requirements --- docs/docs/core-abilities/rag_context_enrichment.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/docs/core-abilities/rag_context_enrichment.md b/docs/docs/core-abilities/rag_context_enrichment.md index f00ef372b8..1e7d3ba742 100644 --- a/docs/docs/core-abilities/rag_context_enrichment.md +++ b/docs/docs/core-abilities/rag_context_enrichment.md @@ -2,6 +2,10 @@ `Supported Git Platforms: GitHub` +!!! info "Prerequisites" + - RAG is available only for Qodo enterprise plan users, with single tenant or on-premises setup. + - Database setup and codebase indexing must be completed before proceeding. [Contact support](https://www.qodo.ai/contact/) for more information. + ## Overview ### What is RAG Context Enrichment? @@ -14,9 +18,6 @@ Using Retrieval-Augmented Generation (RAG), it searches your configured reposito ## Getting started -!!! info "Prerequisites" - - Database setup and codebase indexing must be completed before proceeding. [Contact support](https://www.qodo.ai/contact/) for assistance. - ### Configuration options In order to enable the RAG feature, add the following lines to your configuration file: From 665fb90a98af3fc9f4a91fe791c19b7bfa25323a Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Tue, 8 Apr 2025 01:36:21 +0800 Subject: [PATCH 071/365] Add support of xAI and their Grok-2 model Close #1630 --- docs/docs/usage-guide/changing_a_model.md | 14 ++++++++++++++ pr_agent/algo/__init__.py | 3 +++ pr_agent/algo/ai_handlers/litellm_ai_handler.py | 2 ++ pr_agent/settings/.secrets_template.toml | 3 +++ 4 files changed, 22 insertions(+) diff --git a/docs/docs/usage-guide/changing_a_model.md b/docs/docs/usage-guide/changing_a_model.md index 94861629f0..3a978f82d1 100644 --- a/docs/docs/usage-guide/changing_a_model.md +++ b/docs/docs/usage-guide/changing_a_model.md @@ -113,6 +113,20 @@ key = ... # your Groq api key ``` (you can obtain a Groq key from [here](https://console.groq.com/keys)) +### xAI + +To use xAI's models with PR-Agent, set: +``` +[config] # in configuration.toml +model = "xai/grok-2-latest" +fallback_models = ["xai/grok-2-latest"] # or any other model as fallback + +[xai] # in .secrets.toml +key = "..." # your xAI API key +``` + +You can obtain an xAI API key from [xAI's console](https://console.x.ai/) by creating an account and navigating to the developer settings page. + ### Vertex AI To use Google's Vertex AI platform and its associated models (chat-bison/codechat-bison) set: diff --git a/pr_agent/algo/__init__.py b/pr_agent/algo/__init__.py index 6bea799448..449d89e19c 100644 --- a/pr_agent/algo/__init__.py +++ b/pr_agent/algo/__init__.py @@ -82,6 +82,9 @@ 'groq/llama-3.3-70b-versatile': 128000, 'groq/mixtral-8x7b-32768': 32768, 'groq/gemma2-9b-it': 8192, + 'xai/grok-2': 131072, + 'xai/grok-2-1212': 131072, + 'xai/grok-2-latest': 131072, 'ollama/llama3': 4096, 'watsonx/meta-llama/llama-3-8b-instruct': 4096, "watsonx/meta-llama/llama-3-70b-instruct": 4096, diff --git a/pr_agent/algo/ai_handlers/litellm_ai_handler.py b/pr_agent/algo/ai_handlers/litellm_ai_handler.py index 78d5f47917..2ca04ea3ee 100644 --- a/pr_agent/algo/ai_handlers/litellm_ai_handler.py +++ b/pr_agent/algo/ai_handlers/litellm_ai_handler.py @@ -67,6 +67,8 @@ def __init__(self): litellm.api_key = get_settings().groq.key if get_settings().get("REPLICATE.KEY", None): litellm.replicate_key = get_settings().replicate.key + if get_settings().get("XAI.KEY", None): + litellm.api_key = get_settings().xai.key if get_settings().get("HUGGINGFACE.KEY", None): litellm.huggingface_key = get_settings().huggingface.key if get_settings().get("HUGGINGFACE.API_BASE", None) and 'huggingface' in get_settings().config.model: diff --git a/pr_agent/settings/.secrets_template.toml b/pr_agent/settings/.secrets_template.toml index 42946da5db..28201a0dc5 100644 --- a/pr_agent/settings/.secrets_template.toml +++ b/pr_agent/settings/.secrets_template.toml @@ -32,6 +32,9 @@ key = "" # Optional, uncomment if you want to use Replicate. Acquire through htt [groq] key = "" # Acquire through https://console.groq.com/keys +[xai] +key = "" # Optional, uncomment if you want to use xAI. Acquire through https://console.x.ai/ + [huggingface] key = "" # Optional, uncomment if you want to use Huggingface Inference API. Acquire through https://huggingface.co/docs/api-inference/quicktour api_base = "" # the base url for your huggingface inference endpoint From 83bb3b25d8330176d9254743fd1b0abd939b86b3 Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Tue, 8 Apr 2025 01:47:15 +0800 Subject: [PATCH 072/365] Add support of Meta's Llama 4 Scout and Maverick 17b from Groq Cloud Reference: - https://ai.meta.com/blog/llama-4-multimodal-intelligence/ - https://console.groq.com/docs/models#preview-models - https://groq.com/llama-4-now-live-on-groq-build-fast-at-the-lowest-cost-without-compromise/ --- pr_agent/algo/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pr_agent/algo/__init__.py b/pr_agent/algo/__init__.py index 6bea799448..7f0d59accf 100644 --- a/pr_agent/algo/__init__.py +++ b/pr_agent/algo/__init__.py @@ -76,6 +76,8 @@ "bedrock/us.anthropic.claude-3-5-sonnet-20241022-v2:0": 100000, "bedrock/us.anthropic.claude-3-7-sonnet-20250219-v1:0": 200000, 'claude-3-5-sonnet': 100000, + 'groq/meta-llama/llama-4-scout-17b-16e-instruct': 131072, + 'groq/meta-llama/llama-4-maverick-17b-128e-instruct': 131072, 'groq/llama3-8b-8192': 8192, 'groq/llama3-70b-8192': 8192, 'groq/llama-3.1-8b-instant': 8192, From ef7a8eafb6ed7cc5b6e0045c65a549e2e3ec13f7 Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Tue, 8 Apr 2025 11:59:37 +0300 Subject: [PATCH 073/365] Add RAG context enrichment application details for review, implement, and ask tools --- .../core-abilities/rag_context_enrichment.md | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/docs/docs/core-abilities/rag_context_enrichment.md b/docs/docs/core-abilities/rag_context_enrichment.md index 1e7d3ba742..694972191a 100644 --- a/docs/docs/core-abilities/rag_context_enrichment.md +++ b/docs/docs/core-abilities/rag_context_enrichment.md @@ -6,6 +6,7 @@ - RAG is available only for Qodo enterprise plan users, with single tenant or on-premises setup. - Database setup and codebase indexing must be completed before proceeding. [Contact support](https://www.qodo.ai/contact/) for more information. + ## Overview ### What is RAG Context Enrichment? @@ -16,6 +17,7 @@ A feature that enhances AI analysis by retrieving and referencing relevant code Using Retrieval-Augmented Generation (RAG), it searches your configured repositories for contextually relevant code segments, enriching pull request (PR) insights and accelerating review accuracy. + ## Getting started ### Configuration options @@ -39,10 +41,29 @@ enable_rag=true +### Applications + +#### 1\. The `/review` Tool + +The [`/review`](https://qodo-merge-docs.qodo.ai/tools/review/) tool offers the _Focus area from RAG data_ which contains feedback based on the RAG references analysis. +The complete list of references found relevant to the PR will be shown in the _References_ section, helping developers understand the broader context by exploring the provided references. + +![References](https://codium.ai/images/pr_agent/rag_review.png){width=640} + +#### 2\. The `/implement` Tool + +The [`/implement`](https://qodo-merge-docs.qodo.ai/tools/implement/) tool utilizes the RAG feature to provide comprehensive context of the repository codebase, allowing it to generate more refined code output. +The _References_ section contains links to the content used to support the code generation. + +![References](https://codium.ai/images/pr_agent/rag_implement.png){width=640} + +#### 3\. The `/ask` Tool + +The [`/ask`](https://qodo-merge-docs.qodo.ai/tools/ask/) tool can access broader repository context through the RAG feature when answering questions that go beyond the PR scope alone. +The _References_ section displays the additional repository content consulted to formulate the answer. -References from the repository will be shown in a collapsible bookmark, allowing you to easily access relevant code snippets: +![References](https://codium.ai/images/pr_agent/rag_ask.png){width=640} -![References](https://codium.ai/images/pr_agent/rag_context_enrichment_references.png){width=640} ## Limitations From 4ee9f784ddbf9774d0edc469542e5b37fa1fe8fd Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Tue, 8 Apr 2025 12:06:11 +0300 Subject: [PATCH 074/365] Alphabetize core abilities documentation links for better navigation --- docs/docs/core-abilities/index.md | 14 +++++++------- docs/mkdocs.yml | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/docs/core-abilities/index.md b/docs/docs/core-abilities/index.md index ffb50cb61a..0a9507203f 100644 --- a/docs/docs/core-abilities/index.md +++ b/docs/docs/core-abilities/index.md @@ -1,18 +1,18 @@ # Core Abilities Qodo Merge utilizes a variety of core abilities to provide a comprehensive and efficient code review experience. These abilities include: -- [Fetching ticket context](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/) - [Auto best practices](https://qodo-merge-docs.qodo.ai/core-abilities/auto_best_practices/) -- [Local and global metadata](https://qodo-merge-docs.qodo.ai/core-abilities/metadata/) +- [Code fine-tuning benchmark](https://qodo-merge-docs.qodo.ai/finetuning_benchmark/) +- [Code validation](https://qodo-merge-docs.qodo.ai/core-abilities/code_validation/) +- [Compression strategy](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) - [Dynamic context](https://qodo-merge-docs.qodo.ai/core-abilities/dynamic_context/) -- [Self-reflection](https://qodo-merge-docs.qodo.ai/core-abilities/self_reflection/) +- [Fetching ticket context](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/) - [Impact evaluation](https://qodo-merge-docs.qodo.ai/core-abilities/impact_evaluation/) +- [Interactivity](https://qodo-merge-docs.qodo.ai/core-abilities/interactivity/) +- [Local and global metadata](https://qodo-merge-docs.qodo.ai/core-abilities/metadata/) - [RAG context enrichment](https://qodo-merge-docs.qodo.ai/core-abilities/rag_context_enrichment/) +- [Self-reflection](https://qodo-merge-docs.qodo.ai/core-abilities/self_reflection/) - [Static code analysis](https://qodo-merge-docs.qodo.ai/core-abilities/static_code_analysis/) -- [Compression strategy](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) -- [Code validation](https://qodo-merge-docs.qodo.ai/core-abilities/code_validation/) -- [Interactivity](https://qodo-merge-docs.qodo.ai/core-abilities/interactivity/) -- [Code fine-tuning benchmark](https://qodo-merge-docs.qodo.ai/finetuning_benchmark/) ## Blogs diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 7ed78331b1..99e018e3ca 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -41,18 +41,18 @@ nav: - 💎 Implement: 'tools/implement.md' - Core Abilities: - 'core-abilities/index.md' - - Fetching ticket context: 'core-abilities/fetching_ticket_context.md' - Auto best practices: 'core-abilities/auto_best_practices.md' - - Local and global metadata: 'core-abilities/metadata.md' + - Code fine-tuning benchmark: 'finetuning_benchmark/index.md' + - Code validation: 'core-abilities/code_validation.md' + - Compression strategy: 'core-abilities/compression_strategy.md' - Dynamic context: 'core-abilities/dynamic_context.md' - - Self-reflection: 'core-abilities/self_reflection.md' + - Fetching ticket context: 'core-abilities/fetching_ticket_context.md' - Impact evaluation: 'core-abilities/impact_evaluation.md' + - Interactivity: 'core-abilities/interactivity.md' + - Local and global metadata: 'core-abilities/metadata.md' - RAG context enrichment: 'core-abilities/rag_context_enrichment.md' + - Self-reflection: 'core-abilities/self_reflection.md' - Static code analysis: 'core-abilities/static_code_analysis.md' - - Compression strategy: 'core-abilities/compression_strategy.md' - - Code validation: 'core-abilities/code_validation.md' - - Interactivity: 'core-abilities/interactivity.md' - - Code fine-tuning benchmark: 'finetuning_benchmark/index.md' - Chrome Extension: - Qodo Merge Chrome Extension: 'chrome-extension/index.md' - Features: 'chrome-extension/features.md' From ca95e876eb916b4bcf6fec95a477f5263b8679a1 Mon Sep 17 00:00:00 2001 From: CT Wu Date: Tue, 8 Apr 2025 17:48:40 +0800 Subject: [PATCH 075/365] Enhance Bitbucket provider functionality and update secret configuration template. --- pr_agent/git_providers/bitbucket_provider.py | 47 +++++++++++++------- pr_agent/settings/.secrets_template.toml | 6 ++- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/pr_agent/git_providers/bitbucket_provider.py b/pr_agent/git_providers/bitbucket_provider.py index 969ddf9eee..74de2407fd 100644 --- a/pr_agent/git_providers/bitbucket_provider.py +++ b/pr_agent/git_providers/bitbucket_provider.py @@ -29,17 +29,26 @@ def __init__( self, pr_url: Optional[str] = None, incremental: Optional[bool] = False ): s = requests.Session() - try: - self.bearer_token = bearer = context.get("bitbucket_bearer_token", None) - if not bearer and get_settings().get("BITBUCKET.BEARER_TOKEN", None): - self.bearer_token = bearer = get_settings().get("BITBUCKET.BEARER_TOKEN", None) - s.headers["Authorization"] = f"Bearer {bearer}" - except Exception: - self.bearer_token = get_settings().get("BITBUCKET.BEARER_TOKEN", None) - s.headers[ - "Authorization" - ] = f'Bearer {self.bearer_token}' s.headers["Content-Type"] = "application/json" + + try: + auth_type = context.get("bitbucket_auth_type", None) or get_settings().get("BITBUCKET.AUTH_TYPE", "bearer") + + if auth_type == "basic": + self.basic_token = context.get("bitbucket_basic_token", None) or get_settings().get("BITBUCKET.BASIC_TOKEN", None) + if not self.basic_token: + raise ValueError("Basic auth requires a token") + s.headers["Authorization"] = f"Basic {self.basic_token}" + else: # default to bearer + self.bearer_token = context.get("bitbucket_bearer_token", None) or get_settings().get("BITBUCKET.BEARER_TOKEN", None) + if not self.bearer_token: + raise ValueError("Bearer token is required for bearer auth") + s.headers["Authorization"] = f"Bearer {self.bearer_token}" + + except Exception as e: + get_logger().exception(f"Failed to initialize Bitbucket authentication: {e}") + raise + self.headers = s.headers self.bitbucket_client = Cloud(session=s) self.max_comment_length = 31000 @@ -608,16 +617,20 @@ def _prepare_clone_url_with_token(self, repo_url_to_clone: str) -> str | None: if "bitbucket.org" not in repo_url_to_clone: get_logger().error("Repo URL is not a valid bitbucket URL.") return None - bearer_token = self.bearer_token - if not bearer_token: - get_logger().error("No bearer token provided. Returning None") - return None - #For example: For repo: https://bitbucket.org/codiumai/pr-agent-tests.git - #clone url will be: https://x-token-auth:@bitbucket.org/codiumai/pr-agent-tests.git (scheme, base_url) = repo_url_to_clone.split("bitbucket.org") if not all([scheme, base_url]): get_logger().error(f"repo_url_to_clone: {repo_url_to_clone} is not a valid bitbucket URL.") return None - clone_url = f"{scheme}x-token-auth:{bearer_token}@bitbucket.org{base_url}" + + if hasattr(self, 'basic_token'): + # Basic auth with token + clone_url = f"{scheme}x-token-auth:{self.basic_token}@bitbucket.org{base_url}" + elif hasattr(self, 'bearer_token'): + # Bearer token + clone_url = f"{scheme}x-token-auth:{self.bearer_token}@bitbucket.org{base_url}" + else: + get_logger().error("No valid authentication method provided. Returning None") + return None + return clone_url diff --git a/pr_agent/settings/.secrets_template.toml b/pr_agent/settings/.secrets_template.toml index 42946da5db..845f98334d 100644 --- a/pr_agent/settings/.secrets_template.toml +++ b/pr_agent/settings/.secrets_template.toml @@ -66,8 +66,12 @@ personal_access_token = "" shared_secret = "" # webhook secret [bitbucket] -# For Bitbucket personal/repository bearer token +# For Bitbucket authentication +auth_type = "bearer" # "bearer" or "basic" +# For bearer token authentication bearer_token = "" +# For basic authentication (uses token only) +basic_token = "" [bitbucket_server] # For Bitbucket Server bearer token From eb9c4fa110169b4f42e99affe54e74c3018b50be Mon Sep 17 00:00:00 2001 From: Ratish Panda Date: Tue, 8 Apr 2025 20:41:59 +0530 Subject: [PATCH 076/365] add gemini 2.5 pro preview model token limit --- pr_agent/algo/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pr_agent/algo/__init__.py b/pr_agent/algo/__init__.py index 6bea799448..1d2b6f497f 100644 --- a/pr_agent/algo/__init__.py +++ b/pr_agent/algo/__init__.py @@ -53,6 +53,7 @@ 'gemini/gemini-1.5-pro': 1048576, 'gemini/gemini-1.5-flash': 1048576, 'gemini/gemini-2.0-flash': 1048576, + 'gemini/gemini-2.5-pro-preview-03-25': 1048576, 'codechat-bison': 6144, 'codechat-bison-32k': 32000, 'anthropic.claude-instant-v1': 100000, From cdc354c33b02c64792a431031468b05a1f7a724a Mon Sep 17 00:00:00 2001 From: mrT23 Date: Tue, 8 Apr 2025 21:07:45 +0300 Subject: [PATCH 077/365] disable dynamic variables --- pr_agent/git_providers/utils.py | 1 + pr_agent/log/__init__.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pr_agent/git_providers/utils.py b/pr_agent/git_providers/utils.py index 6038e86109..1b4232dfb3 100644 --- a/pr_agent/git_providers/utils.py +++ b/pr_agent/git_providers/utils.py @@ -12,6 +12,7 @@ def apply_repo_settings(pr_url): + os.environ["AUTO_CAST_FOR_DYNACONF"] = "false" git_provider = get_git_provider_with_context(pr_url) if get_settings().config.use_repo_settings_file: repo_settings_file = None diff --git a/pr_agent/log/__init__.py b/pr_agent/log/__init__.py index 53430ea1a6..1d02fec79f 100644 --- a/pr_agent/log/__init__.py +++ b/pr_agent/log/__init__.py @@ -1,6 +1,7 @@ +import os +os.environ["AUTO_CAST_FOR_DYNACONF"] = "false" import json import logging -import os import sys from enum import Enum From 526d7ff5d21a9bd567f7df0f4dfc7a236410d7e5 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 9 Apr 2025 08:24:26 +0300 Subject: [PATCH 078/365] Update default number of code suggestions per chunk from 4 to 3 --- docs/docs/index.md | 3 ++- docs/docs/tools/custom_prompt.md | 2 +- docs/docs/tools/improve.md | 4 ++-- pr_agent/settings/configuration.toml | 4 ++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/docs/index.md b/docs/docs/index.md index 4dc69af9cd..5bdc8030f9 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -66,7 +66,8 @@ PR-Agent and Qodo Merge offers extensive pull request functionalities across var | | [PR interactive actions](https://www.qodo.ai/images/pr_agent/pr-actions.mp4) 💎 | ✅ | ✅ | | | | | [Impact Evaluation](https://qodo-merge-docs.qodo.ai/core-abilities/impact_evaluation/) 💎 | ✅ | ✅ | | | -💎 marks a feature available only in [Qodo Merge](https://www.codium.ai/pricing/){:target="_blank"}, and not in the open-source version. +!!! note "💎 means Qodo Merge only" + All along the documentation, 💎 marks a feature available only in [Qodo Merge](https://www.codium.ai/pricing/){:target="_blank"}, and not in the open-source version. ## Example Results diff --git a/docs/docs/tools/custom_prompt.md b/docs/docs/tools/custom_prompt.md index 7b31a5a5a1..31823c8e25 100644 --- a/docs/docs/tools/custom_prompt.md +++ b/docs/docs/tools/custom_prompt.md @@ -53,6 +53,6 @@ Results obtained with the prompt above: - `prompt`: the prompt for the tool. It should be a multi-line string. -- `num_code_suggestions_per_chunk`: number of code suggestions provided by the 'custom_prompt' tool, per chunk. Default is 4. +- `num_code_suggestions_per_chunk`: number of code suggestions provided by the 'custom_prompt' tool, per chunk. Default is 3. - `enable_help_text`: if set to true, the tool will display a help text in the comment. Default is true. diff --git a/docs/docs/tools/improve.md b/docs/docs/tools/improve.md index 97a1cc3949..ed361865f7 100644 --- a/docs/docs/tools/improve.md +++ b/docs/docs/tools/improve.md @@ -379,7 +379,7 @@ Qodo Merge uses a dynamic strategy to generate code suggestions based on the siz - Each chunk contains up to `pr_code_suggestions.max_context_tokens` tokens (default: 14,000). #### 2. Generating suggestions -- For each chunk, Qodo Merge generates up to `pr_code_suggestions.num_code_suggestions_per_chunk` suggestions (default: 4). +- For each chunk, Qodo Merge generates up to `pr_code_suggestions.num_code_suggestions_per_chunk` suggestions (default: 3). This approach has two main benefits: @@ -456,7 +456,7 @@ Note: Chunking is primarily relevant for large PRs. For most PRs (up to 500 line num_code_suggestions_per_chunk - Number of code suggestions provided by the 'improve' tool, per chunk. Default is 4. + Number of code suggestions provided by the 'improve' tool, per chunk. Default is 3. max_number_of_calls diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index e44a465c78..1a58cc0b1b 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -143,7 +143,7 @@ new_score_mechanism_th_high=9 new_score_mechanism_th_medium=7 # params for '/improve --extended' mode auto_extended_mode=true -num_code_suggestions_per_chunk=4 +num_code_suggestions_per_chunk=3 max_number_of_calls = 3 parallel_calls = true @@ -167,7 +167,7 @@ The code suggestions should focus only on the following: ... """ suggestions_score_threshold=0 -num_code_suggestions_per_chunk=4 +num_code_suggestions_per_chunk=3 self_reflect_on_custom_suggestions=true enable_help_text=false From 2476dadf5345be73f9ed6eb4626933380cd40448 Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Wed, 9 Apr 2025 11:25:56 +0300 Subject: [PATCH 079/365] Enhance interactivity documentation with details on suggestion tracking --- docs/docs/core-abilities/interactivity.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/core-abilities/interactivity.md b/docs/docs/core-abilities/interactivity.md index 8f7908f60e..0e0dbd7d6a 100644 --- a/docs/docs/core-abilities/interactivity.md +++ b/docs/docs/core-abilities/interactivity.md @@ -16,7 +16,7 @@ The approach eliminates manual steps, provides clear visual indicators, and crea The [`/improve`](https://qodo-merge-docs.qodo.ai/tools/improve/) command delivers a comprehensive interactive experience: -- _**Apply this suggestion**_: Clicking this checkbox instantly converts a suggestion into a committable code change +- _**Apply this suggestion**_: Clicking this checkbox instantly converts a suggestion into a committable code change. When committed to the PR, changes made to code that was flagged for improvement will be marked with a check mark, allowing developers to easily track and review implemented recommendations. - _**More**_: Triggers additional suggestions generation while keeping each suggestion focused and relevant as the original set From b53d2773a9eb17c343888b339842fa601ba43d47 Mon Sep 17 00:00:00 2001 From: "benedict.lee" Date: Wed, 9 Apr 2025 23:45:04 +0900 Subject: [PATCH 080/365] improve ask_line tool(add conversation history context) --- pr_agent/git_providers/git_provider.py | 9 ++ pr_agent/git_providers/github_provider.py | 124 ++++++++++++++++++ pr_agent/settings/configuration.toml | 1 + .../settings/pr_line_questions_prompts.toml | 9 ++ pr_agent/tools/pr_line_questions.py | 75 +++++++++++ 5 files changed, 218 insertions(+) diff --git a/pr_agent/git_providers/git_provider.py b/pr_agent/git_providers/git_provider.py index 0228955e07..9f469e562a 100644 --- a/pr_agent/git_providers/git_provider.py +++ b/pr_agent/git_providers/git_provider.py @@ -285,6 +285,15 @@ def get_issue_comments(self): def get_comment_url(self, comment) -> str: return "" + + def get_review_comment_by_id(self, comment_id: int): + pass + + def get_review_id_by_comment_id(self, comment_id: int): + pass + + def get_review_thread_comments(self, comment_id: int): + pass #### labels operations #### @abstractmethod diff --git a/pr_agent/git_providers/github_provider.py b/pr_agent/git_providers/github_provider.py index e782f9cf8c..8f021fb1b3 100644 --- a/pr_agent/git_providers/github_provider.py +++ b/pr_agent/git_providers/github_provider.py @@ -428,6 +428,130 @@ def publish_inline_comments(self, comments: list[dict], disable_fallback: bool = except Exception as e: get_logger().error(f"Failed to publish inline code comments fallback, error: {e}") raise e + + def get_review_comment_by_id(self, comment_id: int): + """ + Retrieves a review comment by its ID. + + Args: + comment_id: Review comment ID + + Returns: + Review comment object or None (if not found) + """ + try: + # Using PyGitHub library + # There's no direct way to get PR comment by ID, so we fetch all comments and filter + all_comments = list(self.pr.get_comments()) + for comment in all_comments: + if comment.id == comment_id: + return comment + return None + except Exception as e: + get_logger().warning(f"Failed to get review comment {comment_id}, error: {e}") + return None + + def get_review_id_by_comment_id(self, comment_id: int): + """ + Finds the review ID that a comment belongs to based on its comment ID. + + Args: + comment_id: Review comment ID + + Returns: + Review ID or None (if not found) + """ + try: + comment = self.get_review_comment_by_id(comment_id) + if comment: + return getattr(comment, 'pull_request_review_id', None) + return None + except Exception as e: + get_logger().warning(f"Failed to get review ID for comment {comment_id}, error: {e}") + return None + + def get_review_thread_comments(self, comment_id: int): + """ + Retrieves all comments in the thread that a specific comment belongs to. + + Args: + comment_id: Review comment ID + + Returns: + List of comments in the same thread + """ + try: + # Get comment information + comment = self.get_review_comment_by_id(comment_id) + if not comment: + return [] + + # get all comments + all_comments = list(self.pr.get_comments()) + + # Filter comments in the same thread + thread_comments = [] + in_reply_to_map = {} + + # First build the in_reply_to relationship map + for c in all_comments: + in_reply_to_id = getattr(c, 'in_reply_to_id', None) + if in_reply_to_id: + in_reply_to_map[c.id] = in_reply_to_id + + # Recursively find all ancestor comments (collect comment's ancestors) + def find_ancestors(cid): + ancestors = [] + current = cid + while current in in_reply_to_map: + parent_id = in_reply_to_map[current] + ancestors.append(parent_id) + current = parent_id + return ancestors + + # Recursively find all descendant comments (collect all replies to the comment) + def find_descendants(cid): + descendants = [] + for c in all_comments: + if getattr(c, 'in_reply_to_id', None) == cid: + descendants.append(c.id) + descendants.extend(find_descendants(c.id)) + return descendants + + # Find all descendants of a specific ancestor (including sibling comments) + def find_all_descendants_of_ancestor(ancestor_id): + all_descendants = [] + for c in all_comments: + if getattr(c, 'in_reply_to_id', None) == ancestor_id: + all_descendants.append(c.id) + all_descendants.extend(find_descendants(c.id)) + return all_descendants + + # Collect both ancestor and descendant IDs of the comment + ancestors = find_ancestors(comment_id) + descendants = find_descendants(comment_id) + + # Create thread ID set (self, ancestors, descendants) + thread_ids = set([comment_id] + ancestors + descendants) + + # For each ancestor, include all conversation branches (sibling comments with the same ancestor) + for ancestor_id in ancestors: + sibling_ids = find_all_descendants_of_ancestor(ancestor_id) + thread_ids.update(sibling_ids) + + # Filter to only get comments belonging to the thread + for c in all_comments: + if c.id in thread_ids: + thread_comments.append(c) + + # Sort chronologically (by creation date) + thread_comments.sort(key=lambda c: c.created_at) + + return thread_comments + + except Exception as e: + get_logger().warning(f"Failed to get review thread comments for comment {comment_id}, error: {e}") + return [] def _publish_inline_comments_fallback_with_verification(self, comments: list[dict]): """ diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index 1a58cc0b1b..84893cdd40 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -119,6 +119,7 @@ async_ai_calls=true [pr_questions] # /ask # enable_help_text=false +use_conversation_history=true [pr_code_suggestions] # /improve # diff --git a/pr_agent/settings/pr_line_questions_prompts.toml b/pr_agent/settings/pr_line_questions_prompts.toml index 2d32223dd3..3f1e797045 100644 --- a/pr_agent/settings/pr_line_questions_prompts.toml +++ b/pr_agent/settings/pr_line_questions_prompts.toml @@ -43,6 +43,15 @@ Now focus on the selected lines from the hunk: ====== Note that lines in the diff body are prefixed with a symbol that represents the type of change: '-' for deletions, '+' for additions, and ' ' (a space) for unchanged lines +{%- if conversation_history %} + +Previous discussion on this code: +====== +{{ conversation_history|trim }} +====== +Consider both the previous review comments from authors and reviewers, as well as any previous questions and answers about this code. The "Previous Question" and "Previous AI Answer" show earlier interactions about the same code. Use this context to provide a more informed and consistent answer. +{%- endif %} + A question about the selected lines: ====== diff --git a/pr_agent/tools/pr_line_questions.py b/pr_agent/tools/pr_line_questions.py index a122c53405..77c41079e6 100644 --- a/pr_agent/tools/pr_line_questions.py +++ b/pr_agent/tools/pr_line_questions.py @@ -35,6 +35,7 @@ def __init__(self, pr_url: str, args=None, ai_handler: partial[BaseAiHandler,] = "question": self.question_str, "full_hunk": "", "selected_lines": "", + "conversation_history": "", } self.token_handler = TokenHandler(self.git_provider.pr, self.vars, @@ -42,6 +43,9 @@ def __init__(self, pr_url: str, args=None, ai_handler: partial[BaseAiHandler,] = get_settings().pr_line_questions_prompt.user) self.patches_diff = None self.prediction = None + + # get settings for use conversation history + self.use_conversation_history = get_settings().pr_questions.use_conversation_history def parse_args(self, args): if args and len(args) > 0: @@ -56,6 +60,10 @@ async def run(self): # if get_settings().config.publish_output: # self.git_provider.publish_comment("Preparing answer...", is_temporary=True) + # set conversation history if enabled + if self.use_conversation_history: + self._load_conversation_history() + self.patch_with_lines = "" ask_diff = get_settings().get('ask_diff_hunk', "") line_start = get_settings().get('line_start', '') @@ -92,6 +100,73 @@ async def run(self): self.git_provider.publish_comment(model_answer_sanitized) return "" + + def _load_conversation_history(self): + """generate conversation history from the code review thread""" + try: + comment_id = get_settings().get('comment_id', '') + file_path = get_settings().get('file_name', '') + line_number = get_settings().get('line_end', '') + + # return if no comment id or file path and line number + if not (comment_id or (file_path and line_number)): + return + + # initialize conversation history + conversation_history = [] + + if hasattr(self.git_provider, 'get_review_thread_comments') and comment_id: + try: + # get review thread comments + thread_comments = self.git_provider.get_review_thread_comments(comment_id) + + # current question id (this question is excluded from the context) + current_question_id = comment_id + + # generate conversation history from the comments + for comment in thread_comments: + # skip empty comments + body = getattr(comment, 'body', '') + if not body or not body.strip(): + continue + + # except for current question + if current_question_id and str(comment.id) == str(current_question_id): + continue + + # remove the AI command (/ask etc) from the beginning of the comment (optional) + clean_body = body + if clean_body.startswith('/'): + clean_body = clean_body.split('\n', 1)[-1] if '\n' in clean_body else '' + + if not clean_body.strip(): + continue + + # author info + user = comment.user + author = user.login if hasattr(user, 'login') else 'Unknown' + + # confirm if the author is the current user (AI vs user) + is_ai = 'bot' in author.lower() or '[bot]' in author.lower() + role = 'AI' if is_ai else 'User' + + # append to the conversation history + conversation_history.append(f"{role} ({author}): {clean_body}") + + # transform the conversation history to a string + if conversation_history: + self.vars["conversation_history"] = "\n\n".join(conversation_history) + get_logger().info(f"Loaded {len(conversation_history)} comments from the code review thread") + else: + self.vars["conversation_history"] = "" + + except Exception as e: + get_logger().warning(f"Failed to get review thread comments: {e}") + self.vars["conversation_history"] = "" + + except Exception as e: + get_logger().error(f"Error loading conversation history: {e}") + self.vars["conversation_history"] = "" async def _get_prediction(self, model: str): variables = copy.deepcopy(self.vars) From 8952459f6d3328dd423be64d24b155cffdfb0b45 Mon Sep 17 00:00:00 2001 From: Benedict Lee Date: Thu, 10 Apr 2025 08:48:59 +0900 Subject: [PATCH 081/365] Update pr_agent/tools/pr_line_questions.py Co-authored-by: Prateek <110811408+Prateikx@users.noreply.github.com> --- pr_agent/tools/pr_line_questions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pr_agent/tools/pr_line_questions.py b/pr_agent/tools/pr_line_questions.py index 77c41079e6..aa8dfed121 100644 --- a/pr_agent/tools/pr_line_questions.py +++ b/pr_agent/tools/pr_line_questions.py @@ -131,8 +131,8 @@ def _load_conversation_history(self): continue # except for current question - if current_question_id and str(comment.id) == str(current_question_id): - continue + # except for current question + if current_question_id and comment.id == current_question_id: # remove the AI command (/ask etc) from the beginning of the comment (optional) clean_body = body From e44b371d340143926b29c06ea412a1309ab759a5 Mon Sep 17 00:00:00 2001 From: "Hussam.lawen" Date: Thu, 10 Apr 2025 11:46:10 +0300 Subject: [PATCH 082/365] docs: add Chrome extension options documentation page --- docs/docs/chrome-extension/options.md | 39 +++++++++++++++++++++++++++ docs/mkdocs.yml | 1 + 2 files changed, 40 insertions(+) create mode 100644 docs/docs/chrome-extension/options.md diff --git a/docs/docs/chrome-extension/options.md b/docs/docs/chrome-extension/options.md new file mode 100644 index 0000000000..16129f4c7b --- /dev/null +++ b/docs/docs/chrome-extension/options.md @@ -0,0 +1,39 @@ +## Options and Configurations + +### Accessing the Options Page + +To access the options page for the Qodo Merge Chrome extension: + +1. Find the extension icon in your Chrome toolbar (usually in the top-right corner of your browser) +2. Right-click on the extension icon +3. Select "Options" from the context menu that appears + +Alternatively, you can access the options page directly using this URL: + +[chrome-extension://ephlnjeghhogofkifjloamocljapahnl/options.html](chrome-extension://ephlnjeghhogofkifjloamocljapahnl/options.html) + + + + +### Configuration Options + + + + +#### API Base Host + +For single-tenant customers, you can configure the extension to communicate directly with your company's Qodo Merge server instance. + +To set this up: +- Enter your organization's Qodo Merge API endpoint in the "API Base Host" field +- This endpoint should be provided by your Qodo DevOps Team + +*Note: The extension does not send your code to the server, but only triggers your previously installed Qodo Merge application.* + +#### Interface Options + +You can customize the extension's interface by: +- Toggling the "Show Qodo Merge Toolbar" option +- When disabled, the toolbar will not appear in your browser + +Remember to click "Save Settings" after making any changes. diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index aa1a075896..57818b5a7d 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -56,6 +56,7 @@ nav: - Qodo Merge Chrome Extension: 'chrome-extension/index.md' - Features: 'chrome-extension/features.md' - Data Privacy: 'chrome-extension/data_privacy.md' + - Options: 'chrome-extension/options.md' - FAQ: - FAQ: 'faq/index.md' - AI Docs Search: 'ai_search/index.md' From da59a6dbe8804b9a3fe80a22342b3e281ae605bb Mon Sep 17 00:00:00 2001 From: "Hussam.lawen" Date: Thu, 10 Apr 2025 12:46:06 +0300 Subject: [PATCH 083/365] docs: add Chrome extension options documentation page --- docs/docs/chrome-extension/options.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/chrome-extension/options.md b/docs/docs/chrome-extension/options.md index 16129f4c7b..eca325f189 100644 --- a/docs/docs/chrome-extension/options.md +++ b/docs/docs/chrome-extension/options.md @@ -34,6 +34,6 @@ To set this up: You can customize the extension's interface by: - Toggling the "Show Qodo Merge Toolbar" option -- When disabled, the toolbar will not appear in your browser +- When disabled, the toolbar will not appear in your Github comment bar Remember to click "Save Settings" after making any changes. From 6bf093a6a12eccc7df73be9226d34ee886a53a6b Mon Sep 17 00:00:00 2001 From: Benedict Lee Date: Thu, 10 Apr 2025 19:41:43 +0900 Subject: [PATCH 084/365] refactor: Add GitHub provider check for conversation history Co-authored-by: ofir-frd <85901822+ofir-frd@users.noreply.github.com> --- pr_agent/tools/pr_line_questions.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pr_agent/tools/pr_line_questions.py b/pr_agent/tools/pr_line_questions.py index aa8dfed121..0157a57be2 100644 --- a/pr_agent/tools/pr_line_questions.py +++ b/pr_agent/tools/pr_line_questions.py @@ -61,7 +61,8 @@ async def run(self): # self.git_provider.publish_comment("Preparing answer...", is_temporary=True) # set conversation history if enabled - if self.use_conversation_history: + # currently only supports GitHub provider + if self.use_conversation_history and isinstance(self.git_provider, GithubProvider): self._load_conversation_history() self.patch_with_lines = "" From c5165d917bdf9fbb338816e65239cb83a1b174e1 Mon Sep 17 00:00:00 2001 From: Benedict Lee Date: Thu, 10 Apr 2025 19:59:34 +0900 Subject: [PATCH 085/365] refactor: Validate all required parameters before proceeding Co-authored-by: ofir-frd <85901822+ofir-frd@users.noreply.github.com> --- pr_agent/tools/pr_line_questions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pr_agent/tools/pr_line_questions.py b/pr_agent/tools/pr_line_questions.py index 0157a57be2..a8f310d9bb 100644 --- a/pr_agent/tools/pr_line_questions.py +++ b/pr_agent/tools/pr_line_questions.py @@ -109,8 +109,8 @@ def _load_conversation_history(self): file_path = get_settings().get('file_name', '') line_number = get_settings().get('line_end', '') - # return if no comment id or file path and line number - if not (comment_id or (file_path and line_number)): + # return if any required parameter is missing + if not all([comment_id, file_path, line_number]): return # initialize conversation history From 9c06b6b26610e23bb601c06bfc049ee7d8ee8b74 Mon Sep 17 00:00:00 2001 From: "benedict.lee" Date: Thu, 10 Apr 2025 21:56:37 +0900 Subject: [PATCH 086/365] Apply PR review feedback: Code style and functionality improvements --- pr_agent/git_providers/git_provider.py | 10 +- pr_agent/git_providers/github_provider.py | 124 +++--------------- .../settings/pr_line_questions_prompts.toml | 3 +- pr_agent/tools/pr_line_questions.py | 94 +++++-------- 4 files changed, 53 insertions(+), 178 deletions(-) diff --git a/pr_agent/git_providers/git_provider.py b/pr_agent/git_providers/git_provider.py index 9f469e562a..127fa41ecb 100644 --- a/pr_agent/git_providers/git_provider.py +++ b/pr_agent/git_providers/git_provider.py @@ -285,14 +285,8 @@ def get_issue_comments(self): def get_comment_url(self, comment) -> str: return "" - - def get_review_comment_by_id(self, comment_id: int): - pass - - def get_review_id_by_comment_id(self, comment_id: int): - pass - - def get_review_thread_comments(self, comment_id: int): + + def get_review_thread_comments(self, comment_id: int) -> list[dict]: pass #### labels operations #### diff --git a/pr_agent/git_providers/github_provider.py b/pr_agent/git_providers/github_provider.py index 8f021fb1b3..80581c6147 100644 --- a/pr_agent/git_providers/github_provider.py +++ b/pr_agent/git_providers/github_provider.py @@ -427,130 +427,44 @@ def publish_inline_comments(self, comments: list[dict], disable_fallback: bool = self._publish_inline_comments_fallback_with_verification(comments) except Exception as e: get_logger().error(f"Failed to publish inline code comments fallback, error: {e}") - raise e - - def get_review_comment_by_id(self, comment_id: int): - """ - Retrieves a review comment by its ID. - - Args: - comment_id: Review comment ID - - Returns: - Review comment object or None (if not found) - """ - try: - # Using PyGitHub library - # There's no direct way to get PR comment by ID, so we fetch all comments and filter - all_comments = list(self.pr.get_comments()) - for comment in all_comments: - if comment.id == comment_id: - return comment - return None - except Exception as e: - get_logger().warning(f"Failed to get review comment {comment_id}, error: {e}") - return None - - def get_review_id_by_comment_id(self, comment_id: int): - """ - Finds the review ID that a comment belongs to based on its comment ID. - - Args: - comment_id: Review comment ID - - Returns: - Review ID or None (if not found) - """ - try: - comment = self.get_review_comment_by_id(comment_id) - if comment: - return getattr(comment, 'pull_request_review_id', None) - return None - except Exception as e: - get_logger().warning(f"Failed to get review ID for comment {comment_id}, error: {e}") - return None + raise e - def get_review_thread_comments(self, comment_id: int): + def get_review_thread_comments(self, comment_id: int) -> list[dict]: """ - Retrieves all comments in the thread that a specific comment belongs to. + Retrieves all comments in the same line as the given comment. Args: comment_id: Review comment ID - + Returns: - List of comments in the same thread + List of comments on the same line """ try: - # Get comment information - comment = self.get_review_comment_by_id(comment_id) + # Get the original comment to find its location + comment = self.pr.get_comment(comment_id) if not comment: return [] - - # get all comments - all_comments = list(self.pr.get_comments()) - - # Filter comments in the same thread - thread_comments = [] - in_reply_to_map = {} - - # First build the in_reply_to relationship map - for c in all_comments: - in_reply_to_id = getattr(c, 'in_reply_to_id', None) - if in_reply_to_id: - in_reply_to_map[c.id] = in_reply_to_id - - # Recursively find all ancestor comments (collect comment's ancestors) - def find_ancestors(cid): - ancestors = [] - current = cid - while current in in_reply_to_map: - parent_id = in_reply_to_map[current] - ancestors.append(parent_id) - current = parent_id - return ancestors - - # Recursively find all descendant comments (collect all replies to the comment) - def find_descendants(cid): - descendants = [] - for c in all_comments: - if getattr(c, 'in_reply_to_id', None) == cid: - descendants.append(c.id) - descendants.extend(find_descendants(c.id)) - return descendants - # Find all descendants of a specific ancestor (including sibling comments) - def find_all_descendants_of_ancestor(ancestor_id): - all_descendants = [] - for c in all_comments: - if getattr(c, 'in_reply_to_id', None) == ancestor_id: - all_descendants.append(c.id) - all_descendants.extend(find_descendants(c.id)) - return all_descendants - - # Collect both ancestor and descendant IDs of the comment - ancestors = find_ancestors(comment_id) - descendants = find_descendants(comment_id) + # Extract file path and line number + file_path = comment.path + line_number = comment.raw_data["line"] if "line" in comment.raw_data else comment.raw_data.get("original_line") - # Create thread ID set (self, ancestors, descendants) - thread_ids = set([comment_id] + ancestors + descendants) - - # For each ancestor, include all conversation branches (sibling comments with the same ancestor) - for ancestor_id in ancestors: - sibling_ids = find_all_descendants_of_ancestor(ancestor_id) - thread_ids.update(sibling_ids) + # Get all comments + all_comments = list(self.pr.get_comments()) - # Filter to only get comments belonging to the thread - for c in all_comments: - if c.id in thread_ids: - thread_comments.append(c) + # Filter comments on the same line of the same file + thread_comments = [ + c for c in all_comments + if c.path == file_path and (c.raw_data.get("line") == line_number or c.raw_data.get("original_line") == line_number) + ] - # Sort chronologically (by creation date) + # Sort chronologically thread_comments.sort(key=lambda c: c.created_at) return thread_comments except Exception as e: - get_logger().warning(f"Failed to get review thread comments for comment {comment_id}, error: {e}") + get_logger().warning(f"Failed to get review comments for comment {comment_id}, error: {e}") return [] def _publish_inline_comments_fallback_with_verification(self, comments: list[dict]): diff --git a/pr_agent/settings/pr_line_questions_prompts.toml b/pr_agent/settings/pr_line_questions_prompts.toml index 3f1e797045..853a797528 100644 --- a/pr_agent/settings/pr_line_questions_prompts.toml +++ b/pr_agent/settings/pr_line_questions_prompts.toml @@ -49,10 +49,9 @@ Previous discussion on this code: ====== {{ conversation_history|trim }} ====== -Consider both the previous review comments from authors and reviewers, as well as any previous questions and answers about this code. The "Previous Question" and "Previous AI Answer" show earlier interactions about the same code. Use this context to provide a more informed and consistent answer. +Use this prior discussion context to provide a consistent and informed answer. {%- endif %} - A question about the selected lines: ====== {{ question|trim }} diff --git a/pr_agent/tools/pr_line_questions.py b/pr_agent/tools/pr_line_questions.py index a8f310d9bb..2a97f9f561 100644 --- a/pr_agent/tools/pr_line_questions.py +++ b/pr_agent/tools/pr_line_questions.py @@ -16,7 +16,7 @@ from pr_agent.git_providers.git_provider import get_main_pr_language from pr_agent.log import get_logger from pr_agent.servers.help import HelpMessage - +from pr_agent.git_providers.github_provider import GithubProvider class PR_LineQuestions: def __init__(self, pr_url: str, args=None, ai_handler: partial[BaseAiHandler,] = LiteLLMAIHandler): @@ -43,9 +43,6 @@ def __init__(self, pr_url: str, args=None, ai_handler: partial[BaseAiHandler,] = get_settings().pr_line_questions_prompt.user) self.patches_diff = None self.prediction = None - - # get settings for use conversation history - self.use_conversation_history = get_settings().pr_questions.use_conversation_history def parse_args(self, args): if args and len(args) > 0: @@ -62,7 +59,7 @@ async def run(self): # set conversation history if enabled # currently only supports GitHub provider - if self.use_conversation_history and isinstance(self.git_provider, GithubProvider): + if get_settings().pr_questions.use_conversation_history and isinstance(self.git_provider, GithubProvider): self._load_conversation_history() self.patch_with_lines = "" @@ -104,70 +101,41 @@ async def run(self): def _load_conversation_history(self): """generate conversation history from the code review thread""" + # set conversation history to empty string + self.vars["conversation_history"] = "" + + comment_id = get_settings().get('comment_id', '') + file_path = get_settings().get('file_name', '') + line_number = get_settings().get('line_end', '') + + # early return if any required parameter is missing + if not all([comment_id, file_path, line_number]): + return + try: - comment_id = get_settings().get('comment_id', '') - file_path = get_settings().get('file_name', '') - line_number = get_settings().get('line_end', '') + # retrieve thread comments + thread_comments = self.git_provider.get_review_thread_comments(comment_id) - # return if any required parameter is missing - if not all([comment_id, file_path, line_number]): - return - - # initialize conversation history + # generate conversation history conversation_history = [] + for comment in thread_comments: + body = getattr(comment, 'body', '') + + # skip empty comments, current comment(will be added as a question at prompt) + if not body or not body.strip() or comment_id == comment.id: + continue + + user = comment.user + author = user.login if hasattr(user, 'login') else 'Unknown' + conversation_history.append(f"{author}: {body}") - if hasattr(self.git_provider, 'get_review_thread_comments') and comment_id: - try: - # get review thread comments - thread_comments = self.git_provider.get_review_thread_comments(comment_id) - - # current question id (this question is excluded from the context) - current_question_id = comment_id - - # generate conversation history from the comments - for comment in thread_comments: - # skip empty comments - body = getattr(comment, 'body', '') - if not body or not body.strip(): - continue - - # except for current question - # except for current question - if current_question_id and comment.id == current_question_id: - - # remove the AI command (/ask etc) from the beginning of the comment (optional) - clean_body = body - if clean_body.startswith('/'): - clean_body = clean_body.split('\n', 1)[-1] if '\n' in clean_body else '' - - if not clean_body.strip(): - continue - - # author info - user = comment.user - author = user.login if hasattr(user, 'login') else 'Unknown' - - # confirm if the author is the current user (AI vs user) - is_ai = 'bot' in author.lower() or '[bot]' in author.lower() - role = 'AI' if is_ai else 'User' - - # append to the conversation history - conversation_history.append(f"{role} ({author}): {clean_body}") - - # transform the conversation history to a string - if conversation_history: - self.vars["conversation_history"] = "\n\n".join(conversation_history) - get_logger().info(f"Loaded {len(conversation_history)} comments from the code review thread") - else: - self.vars["conversation_history"] = "" - - except Exception as e: - get_logger().warning(f"Failed to get review thread comments: {e}") - self.vars["conversation_history"] = "" + # transform and save conversation history + if conversation_history: + self.vars["conversation_history"] = "\n\n".join(conversation_history) + get_logger().info(f"Loaded {len(conversation_history)} comments from the code review thread") except Exception as e: - get_logger().error(f"Error loading conversation history: {e}") - self.vars["conversation_history"] = "" + get_logger().error(f"Error processing conversation history, error: {e}") async def _get_prediction(self, model: str): variables = copy.deepcopy(self.vars) From a919c626067f2988d9e257266cf039b47e097c6f Mon Sep 17 00:00:00 2001 From: Hussam Lawen Date: Thu, 10 Apr 2025 18:23:52 +0300 Subject: [PATCH 087/365] Update options.md --- docs/docs/chrome-extension/options.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/docs/chrome-extension/options.md b/docs/docs/chrome-extension/options.md index eca325f189..9316f284a5 100644 --- a/docs/docs/chrome-extension/options.md +++ b/docs/docs/chrome-extension/options.md @@ -25,6 +25,7 @@ Alternatively, you can access the options page directly using this URL: For single-tenant customers, you can configure the extension to communicate directly with your company's Qodo Merge server instance. To set this up: + - Enter your organization's Qodo Merge API endpoint in the "API Base Host" field - This endpoint should be provided by your Qodo DevOps Team @@ -33,6 +34,7 @@ To set this up: #### Interface Options You can customize the extension's interface by: + - Toggling the "Show Qodo Merge Toolbar" option - When disabled, the toolbar will not appear in your Github comment bar From 0ac7028bc61b682a1e2be4ca030cb3976d79315d Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Fri, 11 Apr 2025 00:31:15 +0800 Subject: [PATCH 088/365] Support xAI Grok-3 series models Reference: - https://docs.x.ai/docs/release-notes#april-2025 --- pr_agent/algo/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pr_agent/algo/__init__.py b/pr_agent/algo/__init__.py index 449d89e19c..8189f7e622 100644 --- a/pr_agent/algo/__init__.py +++ b/pr_agent/algo/__init__.py @@ -85,6 +85,10 @@ 'xai/grok-2': 131072, 'xai/grok-2-1212': 131072, 'xai/grok-2-latest': 131072, + 'xai/grok-3-beta': 131072, + 'xai/grok-3-fast-beta': 131072, + 'xai/grok-3-mini-beta': 131072, + 'xai/grok-3-mini-fast-beta': 131072, 'ollama/llama3': 4096, 'watsonx/meta-llama/llama-3-8b-instruct': 4096, "watsonx/meta-llama/llama-3-70b-instruct": 4096, From c0c307503f4088d880a8b80d1701f6b48c8f5a22 Mon Sep 17 00:00:00 2001 From: ChunTing Wu Date: Fri, 11 Apr 2025 14:55:06 +0800 Subject: [PATCH 089/365] Update pr_agent/git_providers/bitbucket_provider.py Co-authored-by: Prateek <110811408+Prateikx@users.noreply.github.com> --- pr_agent/git_providers/bitbucket_provider.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pr_agent/git_providers/bitbucket_provider.py b/pr_agent/git_providers/bitbucket_provider.py index 74de2407fd..3a0d20bed2 100644 --- a/pr_agent/git_providers/bitbucket_provider.py +++ b/pr_agent/git_providers/bitbucket_provider.py @@ -32,18 +32,20 @@ def __init__( s.headers["Content-Type"] = "application/json" try: - auth_type = context.get("bitbucket_auth_type", None) or get_settings().get("BITBUCKET.AUTH_TYPE", "bearer") + self.auth_type = context.get("bitbucket_auth_type", None) or get_settings().get("BITBUCKET.AUTH_TYPE", "bearer") - if auth_type == "basic": + if self.auth_type == "basic": self.basic_token = context.get("bitbucket_basic_token", None) or get_settings().get("BITBUCKET.BASIC_TOKEN", None) if not self.basic_token: raise ValueError("Basic auth requires a token") s.headers["Authorization"] = f"Basic {self.basic_token}" - else: # default to bearer + elif self.auth_type == "bearer": self.bearer_token = context.get("bitbucket_bearer_token", None) or get_settings().get("BITBUCKET.BEARER_TOKEN", None) if not self.bearer_token: raise ValueError("Bearer token is required for bearer auth") s.headers["Authorization"] = f"Bearer {self.bearer_token}" + else: + raise ValueError(f"Unsupported auth_type: {self.auth_type}") except Exception as e: get_logger().exception(f"Failed to initialize Bitbucket authentication: {e}") From 0cbf65dab6e66a35f4a61e36a31e266ae2080592 Mon Sep 17 00:00:00 2001 From: ChunTing Wu Date: Fri, 11 Apr 2025 14:59:27 +0800 Subject: [PATCH 090/365] Update pr_agent/git_providers/bitbucket_provider.py Co-authored-by: Prateek <110811408+Prateikx@users.noreply.github.com> --- pr_agent/git_providers/bitbucket_provider.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pr_agent/git_providers/bitbucket_provider.py b/pr_agent/git_providers/bitbucket_provider.py index 3a0d20bed2..50d84abc9c 100644 --- a/pr_agent/git_providers/bitbucket_provider.py +++ b/pr_agent/git_providers/bitbucket_provider.py @@ -625,14 +625,15 @@ def _prepare_clone_url_with_token(self, repo_url_to_clone: str) -> str | None: get_logger().error(f"repo_url_to_clone: {repo_url_to_clone} is not a valid bitbucket URL.") return None - if hasattr(self, 'basic_token'): + if self.auth_type == "basic": # Basic auth with token clone_url = f"{scheme}x-token-auth:{self.basic_token}@bitbucket.org{base_url}" - elif hasattr(self, 'bearer_token'): + elif self.auth_type == "bearer": # Bearer token clone_url = f"{scheme}x-token-auth:{self.bearer_token}@bitbucket.org{base_url}" else: - get_logger().error("No valid authentication method provided. Returning None") + # This case should ideally not be reached if __init__ validates auth_type + get_logger().error(f"Unsupported or uninitialized auth_type: {getattr(self, 'auth_type', 'N/A')}. Returning None") return None return clone_url From 5f2d4d400e8427a7f8d863692d2e10d7a0ca91b7 Mon Sep 17 00:00:00 2001 From: Chunting Wu Date: Fri, 11 Apr 2025 16:20:28 +0800 Subject: [PATCH 091/365] Extract repeated token retrieval logic into a helper function to reduce code duplication --- pr_agent/git_providers/bitbucket_provider.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/pr_agent/git_providers/bitbucket_provider.py b/pr_agent/git_providers/bitbucket_provider.py index 50d84abc9c..4c27e898b1 100644 --- a/pr_agent/git_providers/bitbucket_provider.py +++ b/pr_agent/git_providers/bitbucket_provider.py @@ -34,15 +34,17 @@ def __init__( try: self.auth_type = context.get("bitbucket_auth_type", None) or get_settings().get("BITBUCKET.AUTH_TYPE", "bearer") + def get_token(token_name, auth_type_name): + token = context.get(f"bitbucket_{token_name}", None) or get_settings().get(f"BITBUCKET.{token_name.upper()}", None) + if not token: + raise ValueError(f"{auth_type_name} auth requires a token") + return token + if self.auth_type == "basic": - self.basic_token = context.get("bitbucket_basic_token", None) or get_settings().get("BITBUCKET.BASIC_TOKEN", None) - if not self.basic_token: - raise ValueError("Basic auth requires a token") + self.basic_token = get_token("basic_token", "Basic") s.headers["Authorization"] = f"Basic {self.basic_token}" elif self.auth_type == "bearer": - self.bearer_token = context.get("bitbucket_bearer_token", None) or get_settings().get("BITBUCKET.BEARER_TOKEN", None) - if not self.bearer_token: - raise ValueError("Bearer token is required for bearer auth") + self.bearer_token = get_token("bearer_token", "Bearer") s.headers["Authorization"] = f"Bearer {self.bearer_token}" else: raise ValueError(f"Unsupported auth_type: {self.auth_type}") From 3c8ad9eac8a2e311b7fe05c074d0325fe6bfeb30 Mon Sep 17 00:00:00 2001 From: Chunting Wu Date: Fri, 11 Apr 2025 16:55:32 +0800 Subject: [PATCH 092/365] Update doc for auth_type and basic_token --- docs/docs/installation/bitbucket.md | 30 +++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/docs/docs/installation/bitbucket.md b/docs/docs/installation/bitbucket.md index a15da9f1ec..507e88ae84 100644 --- a/docs/docs/installation/bitbucket.md +++ b/docs/docs/installation/bitbucket.md @@ -1,33 +1,33 @@ ## Run as a Bitbucket Pipeline - You can use the Bitbucket Pipeline system to run PR-Agent on every pull request open or update. 1. Add the following file in your repository bitbucket-pipelines.yml ```yaml pipelines: - pull-requests: - '**': - - step: - name: PR Agent Review - image: python:3.12 - services: - - docker - script: - - docker run -e CONFIG.GIT_PROVIDER=bitbucket -e OPENAI.KEY=$OPENAI_API_KEY -e BITBUCKET.BEARER_TOKEN=$BITBUCKET_BEARER_TOKEN codiumai/pr-agent:latest --pr_url=https://bitbucket.org/$BITBUCKET_WORKSPACE/$BITBUCKET_REPO_SLUG/pull-requests/$BITBUCKET_PR_ID review + pull-requests: + "**": + - step: + name: PR Agent Review + image: python:3.12 + services: + - docker + script: + - docker run -e CONFIG.GIT_PROVIDER=bitbucket -e OPENAI.KEY=$OPENAI_API_KEY -e BITBUCKET.BEARER_TOKEN=$BITBUCKET_BEARER_TOKEN codiumai/pr-agent:latest --pr_url=https://bitbucket.org/$BITBUCKET_WORKSPACE/$BITBUCKET_REPO_SLUG/pull-requests/$BITBUCKET_PR_ID review ``` 2. Add the following secure variables to your repository under Repository settings > Pipelines > Repository variables. -OPENAI_API_KEY: `` -BITBUCKET_BEARER_TOKEN: `` + OPENAI_API_KEY: `` + BITBUCKET.AUTH_TYPE: `basic` or `bearer` (default is `bearer`) + BITBUCKET.BEARER_TOKEN: `` (required when auth_type is bearer) + BITBUCKET.BASIC_TOKEN: `` (required when auth_type is basic) You can get a Bitbucket token for your repository by following Repository Settings -> Security -> Access Tokens. +For basic auth, you can generate a base64 encoded token from your username:password combination. Note that comments on a PR are not supported in Bitbucket Pipeline. - - ## Bitbucket Server and Data Center Login into your on-prem instance of Bitbucket with your service account username and password. @@ -48,6 +48,7 @@ git_provider="bitbucket_server" ``` and pass the Pull request URL: + ```shell python cli.py --pr_url https://git.onpreminstanceofbitbucket.com/projects/PROJECT/repos/REPO/pull-requests/1 review ``` @@ -55,6 +56,7 @@ python cli.py --pr_url https://git.onpreminstanceofbitbucket.com/projects/PROJEC ### Run it as service To run PR-Agent as webhook, build the docker image: + ``` docker build . -t codiumai/pr-agent:bitbucket_server_webhook --target bitbucket_server_webhook -f docker/Dockerfile docker push codiumai/pr-agent:bitbucket_server_webhook # Push to your Docker repository From 7a32faf64f74be7ab7c0dc0465bd85218884084b Mon Sep 17 00:00:00 2001 From: Chunting Wu Date: Fri, 11 Apr 2025 17:44:46 +0800 Subject: [PATCH 093/365] Fix Bearer backward compatibility logic --- pr_agent/git_providers/bitbucket_provider.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/pr_agent/git_providers/bitbucket_provider.py b/pr_agent/git_providers/bitbucket_provider.py index 4c27e898b1..73db18b942 100644 --- a/pr_agent/git_providers/bitbucket_provider.py +++ b/pr_agent/git_providers/bitbucket_provider.py @@ -31,11 +31,11 @@ def __init__( s = requests.Session() s.headers["Content-Type"] = "application/json" - try: - self.auth_type = context.get("bitbucket_auth_type", None) or get_settings().get("BITBUCKET.AUTH_TYPE", "bearer") + self.auth_type = get_settings().get("BITBUCKET.AUTH_TYPE", "bearer") + try: def get_token(token_name, auth_type_name): - token = context.get(f"bitbucket_{token_name}", None) or get_settings().get(f"BITBUCKET.{token_name.upper()}", None) + token = get_settings().get(f"BITBUCKET.{token_name.upper()}", None) if not token: raise ValueError(f"{auth_type_name} auth requires a token") return token @@ -44,7 +44,13 @@ def get_token(token_name, auth_type_name): self.basic_token = get_token("basic_token", "Basic") s.headers["Authorization"] = f"Basic {self.basic_token}" elif self.auth_type == "bearer": - self.bearer_token = get_token("bearer_token", "Bearer") + try: + self.bearer_token = context.get("bitbucket_bearer_token", None) + except: + self.bearer_token = None + + if not self.bearer_token: + get_token("bearer_token", "Bearer") s.headers["Authorization"] = f"Bearer {self.bearer_token}" else: raise ValueError(f"Unsupported auth_type: {self.auth_type}") From fc6de449ad903cd316eb32430a80062ade5a15bf Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sat, 12 Apr 2025 09:58:17 +0300 Subject: [PATCH 094/365] Add Jira PAT token validation script for troubleshooting ticket retrieval issues --- .../core-abilities/fetching_ticket_context.md | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/docs/docs/core-abilities/fetching_ticket_context.md b/docs/docs/core-abilities/fetching_ticket_context.md index e3204be525..daeb9384f8 100644 --- a/docs/docs/core-abilities/fetching_ticket_context.md +++ b/docs/docs/core-abilities/fetching_ticket_context.md @@ -176,6 +176,50 @@ jira_base_url = "YOUR_JIRA_BASE_URL" # e.g. https://jira.example.com jira_api_token = "YOUR_API_TOKEN" ``` +#### Validating PAT token via Python script + +If you are facing issues retrieving tickets in Qodo Merge with PAT token, you can validate the flow using a Python script. +This following steps will help you check if the token is working correctly, and if you can access the Jira ticket details. + +1) run `pip install jira==3.8.0` + +2) run the following script (after replacing the placeholders with your actual values): + +??? example "Python script to validate PAT token" + + ```python + from jira import JIRA + + + if __name__ == "__main__": + try: + # Jira server URL + server = "https://..." + # Jira PAT token + token_auth = "..." + # Jira ticket code (e.g. "PROJ-123") + ticket_id = "..." + + print("Initializing JiraServerTicketProvider with JIRA server") + # Initialize JIRA client + jira = JIRA( + server=server, + token_auth=token_auth, + timeout=30 + ) + if jira: + print(f"JIRA client initialized successfully") + else: + print("Error initializing JIRA client") + + # Fetch ticket details + ticket = jira.issue(ticket_id) + print(f"Ticket title: {ticket.fields.summary}") + + except Exception as e: + print(f"Error fetching JIRA ticket details: {e}") + ``` + ### How to link a PR to a Jira ticket To integrate with Jira, you can link your PR to a ticket using either of these methods: From 8bdd11646c8b81b3c6cd3331e9d306b0d794a64c Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sat, 12 Apr 2025 10:07:45 +0300 Subject: [PATCH 095/365] Fix token assignment in Bitbucket provider authentication --- pr_agent/git_providers/bitbucket_provider.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/git_providers/bitbucket_provider.py b/pr_agent/git_providers/bitbucket_provider.py index 73db18b942..754cf81bf4 100644 --- a/pr_agent/git_providers/bitbucket_provider.py +++ b/pr_agent/git_providers/bitbucket_provider.py @@ -50,7 +50,7 @@ def get_token(token_name, auth_type_name): self.bearer_token = None if not self.bearer_token: - get_token("bearer_token", "Bearer") + self.bearer_token = get_token("bearer_token", "Bearer") s.headers["Authorization"] = f"Bearer {self.bearer_token}" else: raise ValueError(f"Unsupported auth_type: {self.auth_type}") From c8f519ad70586ad28f6d4b0f316c74a30e9bde22 Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Sun, 13 Apr 2025 00:42:53 +0800 Subject: [PATCH 096/365] Fix Discord link in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a2b62a90c7..07424f934b 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ PR-Agent aims to help efficiently review and handle pull requests, by providing [![Static Badge](https://img.shields.io/badge/Chrome-Extension-violet)](https://chromewebstore.google.com/detail/qodo-merge-ai-powered-cod/ephlnjeghhogofkifjloamocljapahnl) [![Static Badge](https://img.shields.io/badge/Pro-App-blue)](https://github.com/apps/qodo-merge-pro/) [![Static Badge](https://img.shields.io/badge/OpenSource-App-red)](https://github.com/apps/qodo-merge-pro-for-open-source/) -[![Discord](https://badgen.net/badge/icon/discord?icon=discord&label&color=purple)](https://discord.com/channels/1057273017547378788/1126104260430528613) +[![Discord](https://badgen.net/badge/icon/discord?icon=discord&label&color=purple)](https://discord.com/invite/SgSxuQ65GF) GitHub From 84fdc4ca2bf1019c4e85cf9e16244a8661cb8fd4 Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Sun, 13 Apr 2025 00:50:38 +0800 Subject: [PATCH 097/365] Correct the tools guide link in the locally installation doc --- docs/docs/installation/locally.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/installation/locally.md b/docs/docs/installation/locally.md index 5032f608a2..e6a4d78a0c 100644 --- a/docs/docs/installation/locally.md +++ b/docs/docs/installation/locally.md @@ -6,7 +6,7 @@ To run PR-Agent locally, you first need to acquire two keys: ## Using Docker image -A list of the relevant tools can be found in the [tools guide](../tools/ask.md). +A list of the relevant tools can be found in the [tools guide](../tools/). To invoke a tool (for example `review`), you can run PR-Agent directly from the Docker image. Here's how: From 259d67c064d539156cb8c1950d9872fa1769a0ed Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Sun, 13 Apr 2025 01:02:20 +0800 Subject: [PATCH 098/365] Improve Markdown format in model configuration guide Enhance documentation readability by: - Adding TOML language identifiers to enable syntax highlighting - Adjusting spacing and formatting for proper Markdown rendering These improvements make the guide easier to follow. --- docs/docs/usage-guide/changing_a_model.md | 67 +++++++++++++++-------- 1 file changed, 44 insertions(+), 23 deletions(-) diff --git a/docs/docs/usage-guide/changing_a_model.md b/docs/docs/usage-guide/changing_a_model.md index 3a978f82d1..9eec3b6463 100644 --- a/docs/docs/usage-guide/changing_a_model.md +++ b/docs/docs/usage-guide/changing_a_model.md @@ -2,14 +2,15 @@ See [here](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/algo/__init__.py) for a list of available models. To use a different model than the default (o3-mini), you need to edit in the [configuration file](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml#L2) the fields: -``` + +```toml [config] model = "..." fallback_models = ["..."] ``` For models and environments not from OpenAI, you might need to provide additional keys and other parameters. -You can give parameters via a configuration file, or from environment variables. +You can give parameters via a configuration file, or from environment variables. !!! note "Model-specific environment variables" See [litellm documentation](https://litellm.vercel.app/docs/proxy/quick_start#supported-llms) for the environment variables needed per model, as they may vary and change over time. Our documentation per-model may not always be up-to-date with the latest changes. @@ -18,7 +19,8 @@ You can give parameters via a configuration file, or from environment variables. ### Azure To use Azure, set in your `.secrets.toml` (working from CLI), or in the GitHub `Settings > Secrets and variables` (working from GitHub App or GitHub Action): -``` + +```toml [openai] key = "" # your azure api key api_type = "azure" @@ -28,26 +30,29 @@ deployment_id = "" # The deployment name you chose when you deployed the engine ``` and set in your configuration file: -``` + +```toml [config] model="" # the OpenAI model you've deployed on Azure (e.g. gpt-4o) fallback_models=["..."] ``` -Passing custom headers to the underlying LLM Model API can be done by setting extra_headers parameter to litellm. -``` +Passing custom headers to the underlying LLM Model API can be done by setting extra_headers parameter to litellm. + +```toml [litellm] extra_headers='{"projectId": "", ...}') #The value of this setting should be a JSON string representing the desired headers, a ValueError is thrown otherwise. ``` -This enables users to pass authorization tokens or API keys, when routing requests through an API management gateway. +This enables users to pass authorization tokens or API keys, when routing requests through an API management gateway. ### Ollama You can run models locally through either [VLLM](https://docs.litellm.ai/docs/providers/vllm) or [Ollama](https://docs.litellm.ai/docs/providers/ollama) E.g. to use a new model locally via Ollama, set in `.secrets.toml` or in a configuration file: -``` + +```toml [config] model = "ollama/qwen2.5-coder:32b" fallback_models=["ollama/qwen2.5-coder:32b"] @@ -64,7 +69,7 @@ Please note that the `custom_model_max_tokens` setting should be configured in a !!! note "Local models vs commercial models" Qodo Merge is compatible with almost any AI model, but analyzing complex code repositories and pull requests requires a model specifically optimized for code analysis. - + Commercial models such as GPT-4, Claude Sonnet, and Gemini have demonstrated robust capabilities in generating structured output for code analysis tasks with large input. In contrast, most open-source models currently available (as of January 2025) face challenges with these complex tasks. Based on our testing, local open-source models are suitable for experimentation and learning purposes (mainly for the `ask` command), but they are not suitable for production-level code analysis tasks. @@ -74,7 +79,8 @@ Please note that the `custom_model_max_tokens` setting should be configured in a ### Hugging Face To use a new model with Hugging Face Inference Endpoints, for example, set: -``` + +```toml [config] # in configuration.toml model = "huggingface/meta-llama/Llama-2-7b-chat-hf" fallback_models=["huggingface/meta-llama/Llama-2-7b-chat-hf"] @@ -84,39 +90,44 @@ custom_model_max_tokens=... # set the maximal input tokens for the model key = ... # your Hugging Face api key api_base = ... # the base url for your Hugging Face inference endpoint ``` + (you can obtain a Llama2 key from [here](https://replicate.com/replicate/llama-2-70b-chat/api)) ### Replicate To use Llama2 model with Replicate, for example, set: -``` + +```toml [config] # in configuration.toml model = "replicate/llama-2-70b-chat:2c1608e18606fad2812020dc541930f2d0495ce32eee50074220b87300bc16e1" fallback_models=["replicate/llama-2-70b-chat:2c1608e18606fad2812020dc541930f2d0495ce32eee50074220b87300bc16e1"] [replicate] # in .secrets.toml key = ... ``` -(you can obtain a Llama2 key from [here](https://replicate.com/replicate/llama-2-70b-chat/api)) +(you can obtain a Llama2 key from [here](https://replicate.com/replicate/llama-2-70b-chat/api)) Also, review the [AiHandler](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/algo/ai_handler.py) file for instructions on how to set keys for other models. ### Groq To use Llama3 model with Groq, for example, set: -``` + +```toml [config] # in configuration.toml model = "llama3-70b-8192" fallback_models = ["groq/llama3-70b-8192"] [groq] # in .secrets.toml key = ... # your Groq api key ``` + (you can obtain a Groq key from [here](https://console.groq.com/keys)) ### xAI To use xAI's models with PR-Agent, set: -``` + +```toml [config] # in configuration.toml model = "xai/grok-2-latest" fallback_models = ["xai/grok-2-latest"] # or any other model as fallback @@ -131,7 +142,7 @@ You can obtain an xAI API key from [xAI's console](https://console.x.ai/) by cre To use Google's Vertex AI platform and its associated models (chat-bison/codechat-bison) set: -``` +```toml [config] # in configuration.toml model = "vertex_ai/codechat-bison" fallback_models="vertex_ai/codechat-bison" @@ -164,14 +175,15 @@ If you don't want to set the API key in the .secrets.toml file, you can set the To use Anthropic models, set the relevant models in the configuration section of the configuration file: -``` +```toml [config] model="anthropic/claude-3-opus-20240229" fallback_models=["anthropic/claude-3-opus-20240229"] ``` And also set the api key in the .secrets.toml file: -``` + +```toml [anthropic] KEY = "..." ``` @@ -182,7 +194,7 @@ See [litellm](https://docs.litellm.ai/docs/providers/anthropic#usage) documentat To use Amazon Bedrock and its foundational models, add the below configuration: -``` +```toml [config] # in configuration.toml model="bedrock/anthropic.claude-3-5-sonnet-20240620-v1:0" fallback_models=["bedrock/anthropic.claude-3-5-sonnet-20240620-v1:0"] @@ -214,17 +226,18 @@ key = ... (you can obtain a deepseek-chat key from [here](https://platform.deepseek.com)) - ### DeepInfra To use DeepSeek model with DeepInfra, for example, set: -``` + +```toml [config] # in configuration.toml model = "deepinfra/deepseek-ai/DeepSeek-R1-Distill-Llama-70B" fallback_models = ["deepinfra/deepseek-ai/DeepSeek-R1-Distill-Qwen-32B"] [deepinfra] # in .secrets.toml key = ... # your DeepInfra api key ``` + (you can obtain a DeepInfra key from [here](https://deepinfra.com/dash/api_keys)) ### Custom models @@ -232,33 +245,41 @@ key = ... # your DeepInfra api key If the relevant model doesn't appear [here](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/algo/__init__.py), you can still use it as a custom model: 1. Set the model name in the configuration file: -``` + +```toml [config] model="custom_model_name" fallback_models=["custom_model_name"] ``` + 2. Set the maximal tokens for the model: -``` + +```toml [config] custom_model_max_tokens= ... ``` + 3. Go to [litellm documentation](https://litellm.vercel.app/docs/proxy/quick_start#supported-llms), find the model you want to use, and set the relevant environment variables. -4. Most reasoning models do not support chat-style inputs (`system` and `user` messages) or temperature settings. +4. Most reasoning models do not support chat-style inputs (`system` and `user` messages) or temperature settings. To bypass chat templates and temperature controls, set `config.custom_reasoning_model = true` in your configuration file. ## Dedicated parameters ### OpenAI models +```toml [config] reasoning_efffort= = "medium" # "low", "medium", "high" +``` With the OpenAI models that support reasoning effort (eg: o3-mini), you can specify its reasoning effort via `config` section. The default value is `medium`. You can change it to `high` or `low` based on your usage. ### Anthropic models +```toml [config] enable_claude_extended_thinking = false # Set to true to enable extended thinking feature extended_thinking_budget_tokens = 2048 extended_thinking_max_output_tokens = 4096 +``` From 943af0653b8b33529a56ceee800e96ccb1b1bc90 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Mon, 14 Apr 2025 08:29:19 +0300 Subject: [PATCH 099/365] Improve formatting and clarity in Jira PAT token validation documentation --- docs/docs/core-abilities/fetching_ticket_context.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/docs/core-abilities/fetching_ticket_context.md b/docs/docs/core-abilities/fetching_ticket_context.md index daeb9384f8..3a0c9fd0eb 100644 --- a/docs/docs/core-abilities/fetching_ticket_context.md +++ b/docs/docs/core-abilities/fetching_ticket_context.md @@ -179,13 +179,13 @@ jira_api_token = "YOUR_API_TOKEN" #### Validating PAT token via Python script If you are facing issues retrieving tickets in Qodo Merge with PAT token, you can validate the flow using a Python script. -This following steps will help you check if the token is working correctly, and if you can access the Jira ticket details. +This following steps will help you check if the token is working correctly, and if you can access the Jira ticket details: -1) run `pip install jira==3.8.0` +1. run `pip install jira==3.8.0` -2) run the following script (after replacing the placeholders with your actual values): +2. run the following Python script (after replacing the placeholders with your actual values): -??? example "Python script to validate PAT token" +??? example "Script to validate PAT token" ```python from jira import JIRA From 2cb226dbcc8730b0894cf1b4691cdb49af0f259d Mon Sep 17 00:00:00 2001 From: Tal Date: Mon, 14 Apr 2025 20:29:09 +0300 Subject: [PATCH 100/365] Update README.md --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index a2b62a90c7..a0aa707860 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,15 @@ PR-Agent aims to help efficiently review and handle pull requests, by providing ## News and Updates +## Apr 14, 2025 + +GPT-4.1 is out. And its quite good on coding tasks... + +https://openai.com/index/gpt-4-1/ + +image + + ## March 28, 2025 A new version, v0.28, was released. See release notes [here](https://github.com/qodo-ai/pr-agent/releases/tag/v0.28). From 57808075bedfb40b516777d65a32a0ac7b495b1f Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Tue, 15 Apr 2025 01:54:35 +0800 Subject: [PATCH 101/365] Add support of OpenAI GPT-4.1 model family Reference: - https://openai.com/index/gpt-4-1/ - https://platform.openai.com/docs/models/gpt-4.1 --- pr_agent/algo/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pr_agent/algo/__init__.py b/pr_agent/algo/__init__.py index 1925b16755..1e7b9eb40b 100644 --- a/pr_agent/algo/__init__.py +++ b/pr_agent/algo/__init__.py @@ -22,6 +22,12 @@ 'gpt-4o-2024-11-20': 128000, # 128K, but may be limited by config.max_model_tokens 'gpt-4.5-preview': 128000, # 128K, but may be limited by config.max_model_tokens 'gpt-4.5-preview-2025-02-27': 128000, # 128K, but may be limited by config.max_model_tokens + 'gpt-4.1': 1047576, + 'gpt-4.1-2025-04-14': 1047576, + 'gpt-4.1-mini': 1047576, + 'gpt-4.1-mini-2025-04-14': 1047576, + 'gpt-4.1-nano': 1047576, + 'gpt-4.1-nano-2025-04-14': 1047576, 'o1-mini': 128000, # 128K, but may be limited by config.max_model_tokens 'o1-mini-2024-09-12': 128000, # 128K, but may be limited by config.max_model_tokens 'o1-preview': 128000, # 128K, but may be limited by config.max_model_tokens From 08bf9593b2bcad5b32759bae230913dd26b3e36e Mon Sep 17 00:00:00 2001 From: mrT23 Date: Mon, 14 Apr 2025 21:15:19 +0300 Subject: [PATCH 102/365] Fix tokenizer fallback to use o200k_base instead of cl100k_base --- pr_agent/algo/token_handler.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pr_agent/algo/token_handler.py b/pr_agent/algo/token_handler.py index f1393e38f5..60cf2c84af 100644 --- a/pr_agent/algo/token_handler.py +++ b/pr_agent/algo/token_handler.py @@ -19,8 +19,11 @@ def get_token_encoder(cls): with cls._lock: # Lock acquisition to ensure thread safety if cls._encoder_instance is None or model != cls._model: cls._model = model - cls._encoder_instance = encoding_for_model(cls._model) if "gpt" in cls._model else get_encoding( - "cl100k_base") + try: + cls._encoder_instance = encoding_for_model(cls._model) if "gpt" in cls._model else get_encoding( + "o200k_base") + except: + cls._encoder_instance = get_encoding("o200k_base") return cls._encoder_instance From 14dafc4016967ddb988759accb4d96e925045bd3 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Tue, 15 Apr 2025 18:57:09 +0300 Subject: [PATCH 103/365] Update GPT-4o references to GPT-4.1 and upgrade litellm to 1.66.1 --- docs/docs/usage-guide/qodo_merge_models.md | 6 +++--- requirements.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/docs/usage-guide/qodo_merge_models.md b/docs/docs/usage-guide/qodo_merge_models.md index 2c48dd0a8b..519de148b0 100644 --- a/docs/docs/usage-guide/qodo_merge_models.md +++ b/docs/docs/usage-guide/qodo_merge_models.md @@ -8,7 +8,7 @@ The models supported by Qodo Merge are: - `claude-3-7-sonnet` (default) - `o3-mini` -- `gpt-4o` +- `gpt-4.1` - `deepseek/r1` To restrict Qodo Merge to using only `o3-mini`, add this setting: @@ -17,10 +17,10 @@ To restrict Qodo Merge to using only `o3-mini`, add this setting: model="o3-mini" ``` -To restrict Qodo Merge to using only `GPT-4o`, add this setting: +To restrict Qodo Merge to using only `GPT-4.1`, add this setting: ``` [config] -model="gpt-4o" +model="gpt-4.1" ``` To restrict Qodo Merge to using only `deepseek-r1` us-hosted, add this setting: diff --git a/requirements.txt b/requirements.txt index 2625ad6628..32e689f6aa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,7 +13,7 @@ google-cloud-aiplatform==1.38.0 google-generativeai==0.8.3 google-cloud-storage==2.10.0 Jinja2==3.1.2 -litellm==1.61.20 +litellm==1.66.1 loguru==0.7.2 msrest==0.7.1 openai>=1.55.3 From 3d2a2850912f8dcf43a1b8c345ac92bf93948089 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Tue, 15 Apr 2025 19:28:36 +0300 Subject: [PATCH 104/365] Add skip_ci_on_push configuration option for changelog updates --- docs/docs/tools/update_changelog.md | 7 ++++--- pr_agent/settings/configuration.toml | 1 + pr_agent/tools/pr_update_changelog.py | 6 +++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/docs/tools/update_changelog.md b/docs/docs/tools/update_changelog.md index ff78962231..23e620edfe 100644 --- a/docs/docs/tools/update_changelog.md +++ b/docs/docs/tools/update_changelog.md @@ -15,6 +15,7 @@ It can be invoked manually by commenting on any PR: Under the section `pr_update_changelog`, the [configuration file](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml#L50) contains options to customize the 'update changelog' tool: -- `push_changelog_changes`: whether to push the changes to CHANGELOG.md, or just print them. Default is false (print only). -- `extra_instructions`: Optional extra instructions to the tool. For example: "focus on the changes in the file X. Ignore change in ... -- `add_pr_link`: whether the model should try to add a link to the PR in the changelog. Default is true. \ No newline at end of file +- `push_changelog_changes`: whether to push the changes to CHANGELOG.md, or just publish them as a comment. Default is false (publish as comment). +- `extra_instructions`: Optional extra instructions to the tool. For example: "Use the following structure: ..." +- `add_pr_link`: whether the model should try to add a link to the PR in the changelog. Default is true. +- `skip_ci_on_push`: whether the commit message (when `push_changelog_changes` is true) will include the term "[skip ci]", preventing CI tests to be triggered on the changelog commit. Default is true. \ No newline at end of file diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index 1a58cc0b1b..cd3e4aed12 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -182,6 +182,7 @@ class_name = "" # in case there are several methods with the same name in push_changelog_changes=false extra_instructions = "" add_pr_link=true +skip_ci_on_push=true [pr_analyze] # /analyze # enable_help_text=true diff --git a/pr_agent/tools/pr_update_changelog.py b/pr_agent/tools/pr_update_changelog.py index fadf28f19e..5b21785b16 100644 --- a/pr_agent/tools/pr_update_changelog.py +++ b/pr_agent/tools/pr_update_changelog.py @@ -140,11 +140,15 @@ def _prepare_changelog_update(self) -> Tuple[str, str]: return new_file_content, answer def _push_changelog_update(self, new_file_content, answer): + if get_settings().pr_update_changelog.get("skip_ci_on_push", True): + commit_message = "[skip ci] Update CHANGELOG.md" + else: + commit_message = "Update CHANGELOG.md" self.git_provider.create_or_update_pr_file( file_path="CHANGELOG.md", branch=self.git_provider.get_pr_branch(), contents=new_file_content, - message="[skip ci] Update CHANGELOG.md", + message=commit_message, ) sleep(5) # wait for the file to be updated From 73b3e2520c0f53eb640c473fbe1690f2fa858d8e Mon Sep 17 00:00:00 2001 From: mrT23 Date: Tue, 15 Apr 2025 19:40:36 +0300 Subject: [PATCH 105/365] Rename code fine-tuning benchmark to pull request benchmark and update model references --- docs/docs/core-abilities/index.md | 2 +- docs/docs/finetuning_benchmark/index.md | 16 ++++++++++++---- docs/mkdocs.yml | 2 +- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/docs/docs/core-abilities/index.md b/docs/docs/core-abilities/index.md index 0a9507203f..d15b0ec69c 100644 --- a/docs/docs/core-abilities/index.md +++ b/docs/docs/core-abilities/index.md @@ -2,7 +2,7 @@ Qodo Merge utilizes a variety of core abilities to provide a comprehensive and efficient code review experience. These abilities include: - [Auto best practices](https://qodo-merge-docs.qodo.ai/core-abilities/auto_best_practices/) -- [Code fine-tuning benchmark](https://qodo-merge-docs.qodo.ai/finetuning_benchmark/) +- [Pull request benchmark](https://qodo-merge-docs.qodo.ai/finetuning_benchmark/) - [Code validation](https://qodo-merge-docs.qodo.ai/core-abilities/code_validation/) - [Compression strategy](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) - [Dynamic context](https://qodo-merge-docs.qodo.ai/core-abilities/dynamic_context/) diff --git a/docs/docs/finetuning_benchmark/index.md b/docs/docs/finetuning_benchmark/index.md index 31f31c40d1..436db87246 100644 --- a/docs/docs/finetuning_benchmark/index.md +++ b/docs/docs/finetuning_benchmark/index.md @@ -1,10 +1,10 @@ -# Qodo Merge Code Fine-tuning Benchmark +# Qodo Merge Pull Request Benchmark -On coding tasks, the gap between open-source models and top closed-source models such as GPT-4o is significant. +On coding tasks, the gap between open-source models and top closed-source models such as Claude and GPT is significant.
In practice, open-source models are unsuitable for most real-world code tasks, and require further fine-tuning to produce acceptable results. -_Qodo Merge fine-tuning benchmark_ aims to benchmark open-source models on their ability to be fine-tuned for a coding task. +_Qodo Merge pull request benchmark_ aims to benchmark models on their ability to be fine-tuned for a coding task. Specifically, we chose to fine-tune open-source models on the task of analyzing a pull request, and providing useful feedback and code suggestions. Here are the results: @@ -49,7 +49,7 @@ Here are the results: - **The best small model** - For small 7B code-dedicated models, the gaps when fine-tuning are much larger. **CodeQWEN 1.5-7B** is by far the best model for fine-tuning. - **Base vs. instruct** - For the top model (deepseek), we saw small advantage when starting from the instruct version. However, we recommend testing both versions on each specific task, as the base model is generally considered more suitable for fine-tuning. -## The dataset +## Dataset ### Training dataset @@ -91,3 +91,11 @@ why: | actionable suggestions, such as changing variable names and adding comments, which are less critical for immediate code improvement." ``` + +## Comparing Top Closed-Source Models + +Another application of the Pull Request Benchmark is comparing leading closed-source models to determine which performs better at analyzing pull request code. + +The evaluation methodology resembles the approach used for evaluating fine-tuned models: +- We ran each model across 200 diverse pull requests, asking them to generate code suggestions using Qodo Merge's `improve` tool +- A third top model served as judge to determine which response better fulfilled the prompt and would likely be perceived as superior by human users \ No newline at end of file diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 9b1feee36e..4ac00cfb78 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -42,7 +42,7 @@ nav: - Core Abilities: - 'core-abilities/index.md' - Auto best practices: 'core-abilities/auto_best_practices.md' - - Code fine-tuning benchmark: 'finetuning_benchmark/index.md' + - Pull request benchmark: 'finetuning_benchmark/index.md' - Code validation: 'core-abilities/code_validation.md' - Compression strategy: 'core-abilities/compression_strategy.md' - Dynamic context: 'core-abilities/dynamic_context.md' From 0da667d1793d0f57f65b551cf683d38be6735509 Mon Sep 17 00:00:00 2001 From: arpit-at Date: Wed, 16 Apr 2025 11:19:04 +0530 Subject: [PATCH 106/365] support Azure AD authentication for OpenAI services for litellm implemetation --- .../algo/ai_handlers/litellm_ai_handler.py | 35 ++++++++++++++++++- pr_agent/settings/.secrets_template.toml | 7 ++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/pr_agent/algo/ai_handlers/litellm_ai_handler.py b/pr_agent/algo/ai_handlers/litellm_ai_handler.py index 2ca04ea3ee..d2286d3b86 100644 --- a/pr_agent/algo/ai_handlers/litellm_ai_handler.py +++ b/pr_agent/algo/ai_handlers/litellm_ai_handler.py @@ -1,5 +1,5 @@ import os - +from azure.identity import ClientSecretCredential import litellm import openai import requests @@ -31,6 +31,7 @@ def __init__(self): self.azure = False self.api_base = None self.repetition_penalty = None + if get_settings().get("OPENAI.KEY", None): openai.api_key = get_settings().openai.key litellm.openai_key = get_settings().openai.key @@ -97,6 +98,19 @@ def __init__(self): if get_settings().get("DEEPINFRA.KEY", None): os.environ['DEEPINFRA_API_KEY'] = get_settings().get("DEEPINFRA.KEY") + # Check for Azure AD configuration + if get_settings().get("AZURE_AD.CLIENT_ID", None): + self.azure = True + # Generate access token using Azure AD credentials from settings + access_token = self._get_azure_ad_token() + litellm.api_key = access_token + openai.api_key = access_token + + # Set API base from settings + self.api_base = get_settings().azure_ad.api_base + litellm.api_base = self.api_base + openai.api_base = self.api_base + # Models that only use user meessage self.user_message_only_models = USER_MESSAGE_ONLY_MODELS @@ -109,6 +123,25 @@ def __init__(self): # Models that support extended thinking self.claude_extended_thinking_models = CLAUDE_EXTENDED_THINKING_MODELS + def _get_azure_ad_token(self): + """ + Generates an access token using Azure AD credentials from settings. + Returns: + str: The access token + """ + try: + credential = ClientSecretCredential( + tenant_id=get_settings().azure_ad.tenant_id, + client_id=get_settings().azure_ad.client_id, + client_secret=get_settings().azure_ad.client_secret + ) + # Get token for Azure OpenAI service + token = credential.get_token("https://cognitiveservices.azure.com/.default") + return token.token + except Exception as e: + get_logger().error(f"Failed to get Azure AD token: {e}") + raise + def prepare_logs(self, response, system, user, resp, finish_reason): response_log = response.dict().copy() response_log['system'] = system diff --git a/pr_agent/settings/.secrets_template.toml b/pr_agent/settings/.secrets_template.toml index f1bb30d4cf..05f7bc0e4b 100644 --- a/pr_agent/settings/.secrets_template.toml +++ b/pr_agent/settings/.secrets_template.toml @@ -101,3 +101,10 @@ key = "" [deepinfra] key = "" + +[azure_ad] +# Azure AD authentication for OpenAI services +client_id = "" # Your Azure AD application client ID +client_secret = "" # Your Azure AD application client secret +tenant_id = "" # Your Azure AD tenant ID +api_base = "" # Your Azure OpenAI service base URL (e.g., https://openai.xyz.com/) \ No newline at end of file From dc46acb7626d976b46715032dc6ed72b3c1aa348 Mon Sep 17 00:00:00 2001 From: arpit-at Date: Wed, 16 Apr 2025 13:27:52 +0530 Subject: [PATCH 107/365] doc update and minor fix --- docs/docs/usage-guide/changing_a_model.md | 11 +++++++++++ pr_agent/algo/ai_handlers/litellm_ai_handler.py | 1 + 2 files changed, 12 insertions(+) diff --git a/docs/docs/usage-guide/changing_a_model.md b/docs/docs/usage-guide/changing_a_model.md index 9eec3b6463..36480d2144 100644 --- a/docs/docs/usage-guide/changing_a_model.md +++ b/docs/docs/usage-guide/changing_a_model.md @@ -37,6 +37,17 @@ model="" # the OpenAI model you've deployed on Azure (e.g. gpt-4o) fallback_models=["..."] ``` +To use Azure AD (Entra id) based authentication set in your `.secrets.toml` (working from CLI), or in the GitHub `Settings > Secrets and variables` (working from GitHub App or GitHub Action): + +```toml +[azure_ad] +client_id = "" # Your Azure AD application client ID +client_secret = "" # Your Azure AD application client secret +tenant_id = "" # Your Azure AD tenant ID +api_base = "" # Your Azure OpenAI service base URL (e.g., https://openai.xyz.com/) +``` + + Passing custom headers to the underlying LLM Model API can be done by setting extra_headers parameter to litellm. ```toml diff --git a/pr_agent/algo/ai_handlers/litellm_ai_handler.py b/pr_agent/algo/ai_handlers/litellm_ai_handler.py index d2286d3b86..b34b4a0aff 100644 --- a/pr_agent/algo/ai_handlers/litellm_ai_handler.py +++ b/pr_agent/algo/ai_handlers/litellm_ai_handler.py @@ -100,6 +100,7 @@ def __init__(self): # Check for Azure AD configuration if get_settings().get("AZURE_AD.CLIENT_ID", None): + from azure.identity import ClientSecretCredential self.azure = True # Generate access token using Azure AD credentials from settings access_token = self._get_azure_ad_token() From 27a7c1a94f36dad384a7b9e5f650b283b94f7342 Mon Sep 17 00:00:00 2001 From: arpit-at Date: Wed, 16 Apr 2025 13:32:53 +0530 Subject: [PATCH 108/365] doc update and minor fix --- pr_agent/algo/ai_handlers/litellm_ai_handler.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pr_agent/algo/ai_handlers/litellm_ai_handler.py b/pr_agent/algo/ai_handlers/litellm_ai_handler.py index b34b4a0aff..d717c087e2 100644 --- a/pr_agent/algo/ai_handlers/litellm_ai_handler.py +++ b/pr_agent/algo/ai_handlers/litellm_ai_handler.py @@ -1,5 +1,4 @@ import os -from azure.identity import ClientSecretCredential import litellm import openai import requests @@ -100,7 +99,6 @@ def __init__(self): # Check for Azure AD configuration if get_settings().get("AZURE_AD.CLIENT_ID", None): - from azure.identity import ClientSecretCredential self.azure = True # Generate access token using Azure AD credentials from settings access_token = self._get_azure_ad_token() @@ -130,6 +128,7 @@ def _get_azure_ad_token(self): Returns: str: The access token """ + from azure.identity import ClientSecretCredential try: credential = ClientSecretCredential( tenant_id=get_settings().azure_ad.tenant_id, From 7704379e627a573f3894aaf1051468ca2867a02f Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 16 Apr 2025 13:39:52 +0300 Subject: [PATCH 109/365] Add new tool for scanning repository discussions and generating best practices --- README.md | 96 ++++++++++++------------ docs/docs/index.md | 1 + docs/docs/tools/index.md | 1 + docs/docs/tools/scan_repo_discussions.md | 38 ++++++++++ docs/mkdocs.yml | 7 +- 5 files changed, 92 insertions(+), 51 deletions(-) create mode 100644 docs/docs/tools/scan_repo_discussions.md diff --git a/README.md b/README.md index 30794960c1..94941097ad 100644 --- a/README.md +++ b/README.md @@ -52,13 +52,22 @@ PR-Agent aims to help efficiently review and handle pull requests, by providing ## News and Updates +## Apr 16, 2025 + +New tool for Qodo Merge 💎 - `/scan_repo_discussions`. + +image + +Read more about it [here](https://qodo-merge-docs.qodo.ai/tools/scan_repo_discussions/). + + ## Apr 14, 2025 GPT-4.1 is out. And its quite good on coding tasks... https://openai.com/index/gpt-4-1/ -image +image ## March 28, 2025 @@ -71,16 +80,6 @@ This version includes a new tool, [Help Docs](https://qodo-merge-docs.qodo.ai/to ### Feb 28, 2025 A new version, v0.27, was released. See release notes [here](https://github.com/qodo-ai/pr-agent/releases/tag/v0.27). -### Feb 27, 2025 -- Updated the default model to `o3-mini` for all tools. You can still use the `gpt-4o` as the default model by setting the `model` parameter in the configuration file. -- Important updates and bug fixes for Azure DevOps, see [here](https://github.com/qodo-ai/pr-agent/pull/1583) -- Added support for adjusting the [response language](https://qodo-merge-docs.qodo.ai/usage-guide/additional_configurations/#language-settings) of the PR-Agent tools. - - -### December 30, 2024 - -Following feedback from the community, we have addressed two vulnerabilities identified in the open-source PR-Agent project. The [fixes](https://github.com/qodo-ai/pr-agent/pull/1425) are now included in the newly released version (v0.26), available as of today. - ## Overview
@@ -88,43 +87,44 @@ Following feedback from the community, we have addressed two vulnerabilities ide Supported commands per platform: | | | GitHub | GitLab | Bitbucket | Azure DevOps | -|-------|---------------------------------------------------------------------------------------------------------|:--------------------:|:--------------------:|:---------:|:------------:| -| TOOLS | [Review](https://qodo-merge-docs.qodo.ai/tools/review/) | ✅ | ✅ | ✅ | ✅ | -| | [Describe](https://qodo-merge-docs.qodo.ai/tools/describe/) | ✅ | ✅ | ✅ | ✅ | -| | [Improve](https://qodo-merge-docs.qodo.ai/tools/improve/) | ✅ | ✅ | ✅ | ✅ | -| | [Ask](https://qodo-merge-docs.qodo.ai/tools/ask/) | ✅ | ✅ | ✅ | ✅ | -| | ⮑ [Ask on code lines](https://qodo-merge-docs.qodo.ai/tools/ask/#ask-lines) | ✅ | ✅ | | | -| | [Update CHANGELOG](https://qodo-merge-docs.qodo.ai/tools/update_changelog/) | ✅ | ✅ | ✅ | ✅ | -| | [Help Docs](https://qodo-merge-docs.qodo.ai/tools/help_docs/?h=auto#auto-approval) | ✅ | ✅ | ✅ | | -| | [Ticket Context](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/) 💎 | ✅ | ✅ | ✅ | | -| | [Utilizing Best Practices](https://qodo-merge-docs.qodo.ai/tools/improve/#best-practices) 💎 | ✅ | ✅ | ✅ | | -| | [PR Chat](https://qodo-merge-docs.qodo.ai/chrome-extension/features/#pr-chat) 💎 | ✅ | | | | -| | [Suggestion Tracking](https://qodo-merge-docs.qodo.ai/tools/improve/#suggestion-tracking) 💎 | ✅ | ✅ | | | -| | [CI Feedback](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/) 💎 | ✅ | | | | -| | [PR Documentation](https://qodo-merge-docs.qodo.ai/tools/documentation/) 💎 | ✅ | ✅ | | | -| | [Custom Labels](https://qodo-merge-docs.qodo.ai/tools/custom_labels/) 💎 | ✅ | ✅ | | | -| | [Analyze](https://qodo-merge-docs.qodo.ai/tools/analyze/) 💎 | ✅ | ✅ | | | -| | [Similar Code](https://qodo-merge-docs.qodo.ai/tools/similar_code/) 💎 | ✅ | | | | -| | [Custom Prompt](https://qodo-merge-docs.qodo.ai/tools/custom_prompt/) 💎 | ✅ | ✅ | ✅ | | -| | [Test](https://qodo-merge-docs.qodo.ai/tools/test/) 💎 | ✅ | ✅ | | | -| | [Implement](https://qodo-merge-docs.qodo.ai/tools/implement/) 💎 | ✅ | ✅ | ✅ | | -| | [Auto-Approve](https://qodo-merge-docs.qodo.ai/tools/improve/?h=auto#auto-approval) 💎 | ✅ | ✅ | ✅ | | -| | | | | | | -| USAGE | [CLI](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#local-repo-cli) | ✅ | ✅ | ✅ | ✅ | -| | [App / webhook](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#github-app) | ✅ | ✅ | ✅ | ✅ | -| | [Tagging bot](https://github.com/Codium-ai/pr-agent#try-it-now) | ✅ | | | | -| | [Actions](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-action) | ✅ |✅| ✅ |✅| -| | | | | | | -| CORE | [PR compression](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) | ✅ | ✅ | ✅ | ✅ | -| | Adaptive and token-aware file patch fitting | ✅ | ✅ | ✅ | ✅ | -| | [Multiple models support](https://qodo-merge-docs.qodo.ai/usage-guide/changing_a_model/) | ✅ | ✅ | ✅ | ✅ | -| | [Local and global metadata](https://qodo-merge-docs.qodo.ai/core-abilities/metadata/) | ✅ | ✅ | ✅ | ✅ | -| | [Dynamic context](https://qodo-merge-docs.qodo.ai/core-abilities/dynamic_context/) | ✅ | ✅ | ✅ | ✅ | -| | [Self reflection](https://qodo-merge-docs.qodo.ai/core-abilities/self_reflection/) | ✅ | ✅ | ✅ | ✅ | -| | [Static code analysis](https://qodo-merge-docs.qodo.ai/core-abilities/static_code_analysis/) 💎 | ✅ | ✅ | | | -| | [Global and wiki configurations](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/) 💎 | ✅ | ✅ | ✅ | | -| | [PR interactive actions](https://www.qodo.ai/images/pr_agent/pr-actions.mp4) 💎 | ✅ | ✅ | | | -| | [Impact Evaluation](https://qodo-merge-docs.qodo.ai/core-abilities/impact_evaluation/) 💎 | ✅ | ✅ | | | +|-------|---------------------------------------------------------------------------------------------------------|:--------------------:|:--------------------:|:--------:|:------------:| +| TOOLS | [Review](https://qodo-merge-docs.qodo.ai/tools/review/) | ✅ | ✅ | ✅ | ✅ | +| | [Describe](https://qodo-merge-docs.qodo.ai/tools/describe/) | ✅ | ✅ | ✅ | ✅ | +| | [Improve](https://qodo-merge-docs.qodo.ai/tools/improve/) | ✅ | ✅ | ✅ | ✅ | +| | [Ask](https://qodo-merge-docs.qodo.ai/tools/ask/) | ✅ | ✅ | ✅ | ✅ | +| | ⮑ [Ask on code lines](https://qodo-merge-docs.qodo.ai/tools/ask/#ask-lines) | ✅ | ✅ | | | +| | [Update CHANGELOG](https://qodo-merge-docs.qodo.ai/tools/update_changelog/) | ✅ | ✅ | ✅ | ✅ | +| | [Help Docs](https://qodo-merge-docs.qodo.ai/tools/help_docs/?h=auto#auto-approval) | ✅ | ✅ | ✅ | | +| | [Ticket Context](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/) 💎 | ✅ | ✅ | ✅ | | +| | [Utilizing Best Practices](https://qodo-merge-docs.qodo.ai/tools/improve/#best-practices) 💎 | ✅ | ✅ | ✅ | | +| | [PR Chat](https://qodo-merge-docs.qodo.ai/chrome-extension/features/#pr-chat) 💎 | ✅ | | | | +| | [Suggestion Tracking](https://qodo-merge-docs.qodo.ai/tools/improve/#suggestion-tracking) 💎 | ✅ | ✅ | | | +| | [CI Feedback](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/) 💎 | ✅ | | | | +| | [PR Documentation](https://qodo-merge-docs.qodo.ai/tools/documentation/) 💎 | ✅ | ✅ | | | +| | [Custom Labels](https://qodo-merge-docs.qodo.ai/tools/custom_labels/) 💎 | ✅ | ✅ | | | +| | [Analyze](https://qodo-merge-docs.qodo.ai/tools/analyze/) 💎 | ✅ | ✅ | | | +| | [Similar Code](https://qodo-merge-docs.qodo.ai/tools/similar_code/) 💎 | ✅ | | | | +| | [Custom Prompt](https://qodo-merge-docs.qodo.ai/tools/custom_prompt/) 💎 | ✅ | ✅ | ✅ | | +| | [Test](https://qodo-merge-docs.qodo.ai/tools/test/) 💎 | ✅ | ✅ | | | +| | [Implement](https://qodo-merge-docs.qodo.ai/tools/implement/) 💎 | ✅ | ✅ | ✅ | | +| | [Scan Repo Discussions](https://qodo-merge-docs.qodo.ai/tools/scan_repo_discussions/) 💎 | ✅ | | | | +| | [Auto-Approve](https://qodo-merge-docs.qodo.ai/tools/improve/?h=auto#auto-approval) 💎 | ✅ | ✅ | ✅ | | +| | | | | | | +| USAGE | [CLI](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#local-repo-cli) | ✅ | ✅ | ✅ | ✅ | +| | [App / webhook](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#github-app) | ✅ | ✅ | ✅ | ✅ | +| | [Tagging bot](https://github.com/Codium-ai/pr-agent#try-it-now) | ✅ | | | | +| | [Actions](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-action) | ✅ |✅| ✅ |✅| +| | | | | | | +| CORE | [PR compression](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) | ✅ | ✅ | ✅ | ✅ | +| | Adaptive and token-aware file patch fitting | ✅ | ✅ | ✅ | ✅ | +| | [Multiple models support](https://qodo-merge-docs.qodo.ai/usage-guide/changing_a_model/) | ✅ | ✅ | ✅ | ✅ | +| | [Local and global metadata](https://qodo-merge-docs.qodo.ai/core-abilities/metadata/) | ✅ | ✅ | ✅ | ✅ | +| | [Dynamic context](https://qodo-merge-docs.qodo.ai/core-abilities/dynamic_context/) | ✅ | ✅ | ✅ | ✅ | +| | [Self reflection](https://qodo-merge-docs.qodo.ai/core-abilities/self_reflection/) | ✅ | ✅ | ✅ | ✅ | +| | [Static code analysis](https://qodo-merge-docs.qodo.ai/core-abilities/static_code_analysis/) 💎 | ✅ | ✅ | | | +| | [Global and wiki configurations](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/) 💎 | ✅ | ✅ | ✅ | | +| | [PR interactive actions](https://www.qodo.ai/images/pr_agent/pr-actions.mp4) 💎 | ✅ | ✅ | | | +| | [Impact Evaluation](https://qodo-merge-docs.qodo.ai/core-abilities/impact_evaluation/) 💎 | ✅ | ✅ | | | - 💎 means this feature is available only in [Qodo-Merge](https://www.qodo.ai/pricing/) [//]: # (- Support for additional git providers is described in [here](./docs/Full_environments.md)) diff --git a/docs/docs/index.md b/docs/docs/index.md index 5bdc8030f9..938fab7c47 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -48,6 +48,7 @@ PR-Agent and Qodo Merge offers extensive pull request functionalities across var | | [Custom Prompt](https://qodo-merge-docs.qodo.ai/tools/custom_prompt/) 💎 | ✅ | ✅ | ✅ | | | | [Test](https://qodo-merge-docs.qodo.ai/tools/test/) 💎 | ✅ | ✅ | | | | | [Implement](https://qodo-merge-docs.qodo.ai/tools/implement/) 💎 | ✅ | ✅ | ✅ | | +| | [Scan Repo Discussions](https://qodo-merge-docs.qodo.ai/tools/scan_repo_discussions/) 💎 | ✅ | | | | | | [Auto-Approve](https://qodo-merge-docs.qodo.ai/tools/improve/?h=auto#auto-approval) 💎 | ✅ | ✅ | ✅ | | | | | | | | | | USAGE | [CLI](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#local-repo-cli) | ✅ | ✅ | ✅ | ✅ | diff --git a/docs/docs/tools/index.md b/docs/docs/tools/index.md index 419c684076..030d23c274 100644 --- a/docs/docs/tools/index.md +++ b/docs/docs/tools/index.md @@ -19,4 +19,5 @@ Here is a list of Qodo Merge tools, each with a dedicated page that explains how | **💎 [Improve Component (`/improve_component component_name`](./improve_component.md))** | Generates code suggestions for a specific code component that changed in the PR | | **💎 [CI Feedback (`/checks ci_job`](./ci_feedback.md))** | Automatically generates feedback and analysis for a failed CI job | | **💎 [Implement (`/implement`](./implement.md))** | Generates implementation code from review suggestions | +| **💎 [Scan Reop Discussions (`/scan_repo_discussions`](./implement.md))** | Generates `best_practices.md` file based on previous discussions in the repository | Note that the tools marked with 💎 are available only for Qodo Merge users. diff --git a/docs/docs/tools/scan_repo_discussions.md b/docs/docs/tools/scan_repo_discussions.md new file mode 100644 index 0000000000..c7eb23c61b --- /dev/null +++ b/docs/docs/tools/scan_repo_discussions.md @@ -0,0 +1,38 @@ +`Platforms supported: GitHub` + + +## Overview + +The `scan_repo_discussions` tool analyzes code discussions (meaning review comments over code lines) from merged pull requests over the past 12 months. +It processes these discussions alongside other PR metadata to identify recurring patterns in team feedback and code reviews, generating a comprehensive `best_practices.md` document that distills key insights and recommendations. + +This file captures repository-specific patterns derived from your team's actual workflow and discussions, rather than more generic best practices. + +Teams are encouraged to further customize and refine these insights to better align with their specific development priorities and contexts. +This can be done by editing the `best_practices.md` file directly when the PR is created, or iteratively over time to enhance the 'best practices' suggestions provided by Qodo Merge. + +The tool can be invoked manually by commenting on any PR: +``` +/scan_repo_discussions +``` + +As a response, the bot will create a new PR that contains an auto-generated `best_practices.md` file. +Note that the scan can take several minutes to complete, since up to 250 PRs are scanned. + +Invoking the tool: + +![scan1](https://codium.ai/images/pr_agent/scan_repo_discussions_1.png){width=640} + +The PR created by the bot: + +![scan1](https://codium.ai/images/pr_agent/scan_repo_discussions_2.png){width=640} + +The `best_practices.md` file in the PR: + +![scan1](https://codium.ai/images/pr_agent/scan_repo_discussions_3.png){width=640} + +### Configuration options + +- Use `/scan_repo_discussions --scan_repo_discussions.force_scan=true` to force generating a PR with a new `best_practices.md` file, even if it already exists (by default, the bot will not generate a new file if it already exists). +- Use `/scan_repo_discussions --scan_repo_discussions.days_back=X` to specify the number of days back to scan for discussions. The default is 365 days. +- Use `/scan_repo_discussions --scan_repo_discussions.minimal_number_of_prs=X` to specify the minimum number of merged PRs needed to generate the `best_practices.md` file. The default is 50 PRs. \ No newline at end of file diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 4ac00cfb78..a6d1d4f6cf 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -39,6 +39,7 @@ nav: - 💎 CI Feedback: 'tools/ci_feedback.md' - 💎 Similar Code: 'tools/similar_code.md' - 💎 Implement: 'tools/implement.md' + - 💎 Scan Repo Discussions: 'tools/scan_repo_discussions.md' - Core Abilities: - 'core-abilities/index.md' - Auto best practices: 'core-abilities/auto_best_practices.md' @@ -76,8 +77,8 @@ theme: - navigation.top - navigation.tracking - navigation.indexes - - search.suggest - - search.highlight +# - search.suggest +# - search.highlight - content.tabs.link - content.code.annotation - content.code.copy @@ -107,7 +108,7 @@ theme: plugins: - social - - search +# - search - glightbox extra: From a5cb0e48fe8d54383ab89e68d8f7983ea3eead7d Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 16 Apr 2025 13:42:18 +0300 Subject: [PATCH 110/365] Fix link to Scan Repo Discussions tool in index.md --- docs/docs/tools/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/tools/index.md b/docs/docs/tools/index.md index 030d23c274..a447a7feb1 100644 --- a/docs/docs/tools/index.md +++ b/docs/docs/tools/index.md @@ -19,5 +19,5 @@ Here is a list of Qodo Merge tools, each with a dedicated page that explains how | **💎 [Improve Component (`/improve_component component_name`](./improve_component.md))** | Generates code suggestions for a specific code component that changed in the PR | | **💎 [CI Feedback (`/checks ci_job`](./ci_feedback.md))** | Automatically generates feedback and analysis for a failed CI job | | **💎 [Implement (`/implement`](./implement.md))** | Generates implementation code from review suggestions | -| **💎 [Scan Reop Discussions (`/scan_repo_discussions`](./implement.md))** | Generates `best_practices.md` file based on previous discussions in the repository | +| **💎 [Scan Repo Discussions (`/scan_repo_discussions`](./scan_repo_discussions.md))** | Generates `best_practices.md` file based on previous discussions in the repository | Note that the tools marked with 💎 are available only for Qodo Merge users. From 717b03de6e184e95fe5c5ef5e4cc013919c7388d Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 16 Apr 2025 13:43:37 +0300 Subject: [PATCH 111/365] Fix link to Scan Repo Discussions tool in index.md --- docs/mkdocs.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index a6d1d4f6cf..a7d9085d2c 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -82,7 +82,6 @@ theme: - content.tabs.link - content.code.annotation - content.code.copy - - content.tabs.link language: en custom_dir: overrides From bc3ef4763dd77c180094dd38b90fa0352f6e62b0 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 16 Apr 2025 14:17:59 +0300 Subject: [PATCH 112/365] Update scan_repo_discussions.md to include links and customization notes --- docs/docs/tools/scan_repo_discussions.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/docs/docs/tools/scan_repo_discussions.md b/docs/docs/tools/scan_repo_discussions.md index c7eb23c61b..2da46b765c 100644 --- a/docs/docs/tools/scan_repo_discussions.md +++ b/docs/docs/tools/scan_repo_discussions.md @@ -1,15 +1,22 @@ `Platforms supported: GitHub` - ## Overview The `scan_repo_discussions` tool analyzes code discussions (meaning review comments over code lines) from merged pull requests over the past 12 months. -It processes these discussions alongside other PR metadata to identify recurring patterns in team feedback and code reviews, generating a comprehensive `best_practices.md` document that distills key insights and recommendations. +It processes these discussions alongside other PR metadata to identify recurring patterns in team feedback and code reviews, generating a comprehensive [`best_practices.md`](https://github.com/qodo-ai/pr-agent/blob/qodo-merge-best-practices_2025-04-16_1018/best_practices.md) document that distills key insights and recommendations. This file captures repository-specific patterns derived from your team's actual workflow and discussions, rather than more generic best practices. +It will be utilized by Qodo Merge to provide tailored suggestions for improving code quality in future pull requests. + +!!! note "Active repositories are needed" + The tool is designed to work with real-life repositories, as it relies on actual discussions to generate meaningful insights. + At least 50 merged PRs are required to generate the `best_practices.md` file. + +!!! note "Additional customization" + Teams are encouraged to further customize and refine these insights to better align with their specific development priorities and contexts. + This can be done by editing the `best_practices.md` file directly when the PR is created, or iteratively over time to enhance the 'best practices' suggestions provided by Qodo Merge. + -Teams are encouraged to further customize and refine these insights to better align with their specific development priorities and contexts. -This can be done by editing the `best_practices.md` file directly when the PR is created, or iteratively over time to enhance the 'best practices' suggestions provided by Qodo Merge. The tool can be invoked manually by commenting on any PR: ``` @@ -19,7 +26,8 @@ The tool can be invoked manually by commenting on any PR: As a response, the bot will create a new PR that contains an auto-generated `best_practices.md` file. Note that the scan can take several minutes to complete, since up to 250 PRs are scanned. -Invoking the tool: +## Example usage + ![scan1](https://codium.ai/images/pr_agent/scan_repo_discussions_1.png){width=640} From 4e3e963ce510c723b386906e7032603a694b67c1 Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Thu, 17 Apr 2025 02:19:42 +0800 Subject: [PATCH 113/365] Add OpenAI o3 & 4o-mini reasoning models Reference: - https://platform.openai.com/docs/models/o3 - https://platform.openai.com/docs/models/o4-mini - https://openai.com/index/introducing-o3-and-o4-mini/ --- pr_agent/algo/__init__.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/pr_agent/algo/__init__.py b/pr_agent/algo/__init__.py index 1e7b9eb40b..62cd3f61be 100644 --- a/pr_agent/algo/__init__.py +++ b/pr_agent/algo/__init__.py @@ -36,6 +36,10 @@ 'o1': 204800, # 200K, but may be limited by config.max_model_tokens 'o3-mini': 204800, # 200K, but may be limited by config.max_model_tokens 'o3-mini-2025-01-31': 204800, # 200K, but may be limited by config.max_model_tokens + 'o3': 200000, # 200K, but may be limited by config.max_model_tokens + 'o3-2025-04-16': 200000, # 200K, but may be limited by config.max_model_tokens + 'o4-mini': 200000, # 200K, but may be limited by config.max_model_tokens + 'o4-mini-2025-04-16': 200000, # 200K, but may be limited by config.max_model_tokens 'claude-instant-1': 100000, 'claude-2': 100000, 'command-nightly': 4096, @@ -125,12 +129,20 @@ "o1-2024-12-17", "o3-mini", "o3-mini-2025-01-31", - "o1-preview" + "o1-preview", + "o3", + "o3-2025-04-16", + "o4-mini", + "o4-mini-2025-04-16", ] SUPPORT_REASONING_EFFORT_MODELS = [ "o3-mini", - "o3-mini-2025-01-31" + "o3-mini-2025-01-31", + "o3", + "o3-2025-04-16", + "o4-mini", + "o4-mini-2025-04-16", ] CLAUDE_EXTENDED_THINKING_MODELS = [ From e5df079dce0254d72b7dda9961e40d0482c22cb6 Mon Sep 17 00:00:00 2001 From: Nico Hein Date: Wed, 16 Apr 2025 18:01:58 -0400 Subject: [PATCH 114/365] docs: updated bitbucket pipeline docs to make direct use of the pr-agent image without docker in docker --- docs/docs/installation/bitbucket.md | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/docs/docs/installation/bitbucket.md b/docs/docs/installation/bitbucket.md index 507e88ae84..6e6595f86e 100644 --- a/docs/docs/installation/bitbucket.md +++ b/docs/docs/installation/bitbucket.md @@ -6,22 +6,21 @@ You can use the Bitbucket Pipeline system to run PR-Agent on every pull request ```yaml pipelines: - pull-requests: - "**": - - step: - name: PR Agent Review - image: python:3.12 - services: - - docker - script: - - docker run -e CONFIG.GIT_PROVIDER=bitbucket -e OPENAI.KEY=$OPENAI_API_KEY -e BITBUCKET.BEARER_TOKEN=$BITBUCKET_BEARER_TOKEN codiumai/pr-agent:latest --pr_url=https://bitbucket.org/$BITBUCKET_WORKSPACE/$BITBUCKET_REPO_SLUG/pull-requests/$BITBUCKET_PR_ID review + pull-requests: + '**': + - step: + name: PR Agent Review + image: codiumai/pr-agent:latest + script: + - pr-agent --pr_url=https://bitbucket.org/$BITBUCKET_WORKSPACE/$BITBUCKET_REPO_SLUG/pull-requests/$BITBUCKET_PR_ID review ``` 2. Add the following secure variables to your repository under Repository settings > Pipelines > Repository variables. - OPENAI_API_KEY: `` - BITBUCKET.AUTH_TYPE: `basic` or `bearer` (default is `bearer`) - BITBUCKET.BEARER_TOKEN: `` (required when auth_type is bearer) - BITBUCKET.BASIC_TOKEN: `` (required when auth_type is basic) + CONFIG__GIT_PROVIDER: `bitbucket` + OPENAI__KEY: `` + BITBUCKET__AUTH_TYPE: `basic` or `bearer` (default is `bearer`) + BITBUCKET__BEARER_TOKEN: `` (required when auth_type is bearer) + BITBUCKET__BASIC_TOKEN: `` (required when auth_type is basic) You can get a Bitbucket token for your repository by following Repository Settings -> Security -> Access Tokens. For basic auth, you can generate a base64 encoded token from your username:password combination. From 2e18053e02be24d7986191531bef766d9b827e4b Mon Sep 17 00:00:00 2001 From: DongjaJ Date: Thu, 17 Apr 2025 22:57:18 +0900 Subject: [PATCH 115/365] ci: Update actions/checkout to v4 in workflow files --- .github/workflows/build-and-test.yaml | 2 +- .github/workflows/code_coverage.yaml | 3 +-- .github/workflows/e2e_tests.yaml | 5 ++--- .github/workflows/pre-commit.yml | 2 +- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml index 9f56c3e51e..ecd842fe83 100644 --- a/.github/workflows/build-and-test.yaml +++ b/.github/workflows/build-and-test.yaml @@ -14,7 +14,7 @@ jobs: steps: - id: checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - id: dockerx name: Setup Docker Buildx diff --git a/.github/workflows/code_coverage.yaml b/.github/workflows/code_coverage.yaml index de37202467..84d978a645 100644 --- a/.github/workflows/code_coverage.yaml +++ b/.github/workflows/code_coverage.yaml @@ -15,7 +15,7 @@ jobs: steps: - id: checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - id: dockerx name: Setup Docker Buildx @@ -41,7 +41,6 @@ jobs: docker cp test_container:/app/coverage.xml coverage.xml docker rm test_container - - name: Validate coverage report run: | if [ ! -f coverage.xml ]; then diff --git a/.github/workflows/e2e_tests.yaml b/.github/workflows/e2e_tests.yaml index 07602d7f48..9fa7beddab 100644 --- a/.github/workflows/e2e_tests.yaml +++ b/.github/workflows/e2e_tests.yaml @@ -11,7 +11,7 @@ jobs: name: PR-Agent E2E GitHub App Test steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup Docker Buildx uses: docker/setup-buildx-action@v2 @@ -32,14 +32,13 @@ jobs: - id: test1 name: E2E test github app run: | - docker run -e GITHUB.USER_TOKEN=${{ secrets.TOKEN_GITHUB }} --rm codiumai/pr-agent:test pytest -v tests/e2e_tests/test_github_app.py + docker run -e GITHUB.USER_TOKEN=${{ secrets.TOKEN_GITHUB }} --rm codiumai/pr-agent:test pytest -v tests/e2e_tests/test_github_app.py - id: test2 name: E2E gitlab webhook run: | docker run -e gitlab.PERSONAL_ACCESS_TOKEN=${{ secrets.TOKEN_GITLAB }} --rm codiumai/pr-agent:test pytest -v tests/e2e_tests/test_gitlab_webhook.py - - id: test3 name: E2E bitbucket app run: | diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 190ef45008..37d7ee66e1 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -11,7 +11,7 @@ jobs: pre-commit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-python@v5 # SEE https://github.com/pre-commit/action - uses: pre-commit/action@v3.0.1 From 8ad3eb583a246f43f0e0daeca741257b58ebf264 Mon Sep 17 00:00:00 2001 From: DongjaJ Date: Thu, 17 Apr 2025 23:02:48 +0900 Subject: [PATCH 116/365] ci: Upgrade Docker Buildx action to v3 in workflow files --- .github/workflows/build-and-test.yaml | 2 +- .github/workflows/code_coverage.yaml | 2 +- .github/workflows/e2e_tests.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml index ecd842fe83..5e35bb8525 100644 --- a/.github/workflows/build-and-test.yaml +++ b/.github/workflows/build-and-test.yaml @@ -18,7 +18,7 @@ jobs: - id: dockerx name: Setup Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - id: build name: Build dev docker diff --git a/.github/workflows/code_coverage.yaml b/.github/workflows/code_coverage.yaml index 84d978a645..460d08b5c6 100644 --- a/.github/workflows/code_coverage.yaml +++ b/.github/workflows/code_coverage.yaml @@ -19,7 +19,7 @@ jobs: - id: dockerx name: Setup Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - id: build name: Build dev docker diff --git a/.github/workflows/e2e_tests.yaml b/.github/workflows/e2e_tests.yaml index 9fa7beddab..8fa666ec47 100644 --- a/.github/workflows/e2e_tests.yaml +++ b/.github/workflows/e2e_tests.yaml @@ -14,7 +14,7 @@ jobs: uses: actions/checkout@v4 - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - id: build name: Build dev docker From be957cd2ea582ed6546f0f8a9ba0b9a266271697 Mon Sep 17 00:00:00 2001 From: DongjaJ Date: Thu, 17 Apr 2025 23:05:10 +0900 Subject: [PATCH 117/365] ci: Upgrade docker/build-push-action to v6 in workflow files --- .github/workflows/build-and-test.yaml | 2 +- .github/workflows/code_coverage.yaml | 2 +- .github/workflows/e2e_tests.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml index 5e35bb8525..64592b781f 100644 --- a/.github/workflows/build-and-test.yaml +++ b/.github/workflows/build-and-test.yaml @@ -22,7 +22,7 @@ jobs: - id: build name: Build dev docker - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/code_coverage.yaml b/.github/workflows/code_coverage.yaml index 460d08b5c6..6239bb94c2 100644 --- a/.github/workflows/code_coverage.yaml +++ b/.github/workflows/code_coverage.yaml @@ -23,7 +23,7 @@ jobs: - id: build name: Build dev docker - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/e2e_tests.yaml b/.github/workflows/e2e_tests.yaml index 8fa666ec47..375aea54d2 100644 --- a/.github/workflows/e2e_tests.yaml +++ b/.github/workflows/e2e_tests.yaml @@ -18,7 +18,7 @@ jobs: - id: build name: Build dev docker - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile From 6cf0ae0731a5dd8257e05489474e7b87465c4c92 Mon Sep 17 00:00:00 2001 From: DongjaJ Date: Thu, 17 Apr 2025 23:05:47 +0900 Subject: [PATCH 118/365] ci: Upgrade Codecov action to v5 in workflow files --- .github/workflows/code_coverage.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/code_coverage.yaml b/.github/workflows/code_coverage.yaml index 6239bb94c2..44d614ef77 100644 --- a/.github/workflows/code_coverage.yaml +++ b/.github/workflows/code_coverage.yaml @@ -48,6 +48,6 @@ jobs: exit 1 fi - name: Upload coverage to Codecov - uses: codecov/codecov-action@v4.0.1 + uses: codecov/codecov-action@v5 with: token: ${{ secrets.CODECOV_TOKEN }} From 2628f2a997ff74240f141c29f6fe4078f0ef69a0 Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Thu, 17 Apr 2025 23:37:33 +0800 Subject: [PATCH 119/365] Upgrade litellm to v1.66.2 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 32e689f6aa..05747bcd23 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,7 +13,7 @@ google-cloud-aiplatform==1.38.0 google-generativeai==0.8.3 google-cloud-storage==2.10.0 Jinja2==3.1.2 -litellm==1.66.1 +litellm==1.66.2 loguru==0.7.2 msrest==0.7.1 openai>=1.55.3 From 999d03f7aa0c43d9ed984bcc2e124184c8bb5b7f Mon Sep 17 00:00:00 2001 From: mrT23 Date: Thu, 17 Apr 2025 18:51:50 +0300 Subject: [PATCH 120/365] Add Basic Authentication method for Jira Data Center/Server and validation script --- .../core-abilities/fetching_ticket_context.md | 63 ++++++++++++++++++- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/docs/docs/core-abilities/fetching_ticket_context.md b/docs/docs/core-abilities/fetching_ticket_context.md index 3a0c9fd0eb..6418586a25 100644 --- a/docs/docs/core-abilities/fetching_ticket_context.md +++ b/docs/docs/core-abilities/fetching_ticket_context.md @@ -165,7 +165,66 @@ jira_api_email = "YOUR_EMAIL" [//]: # (Personal Access Token (PAT) Authentication) -Currently, JIRA integration for Data Center/Server is available via Personal Access Token (PAT) Authentication method + +#### Using Basic Authentication for Jira Data Center/Server + +You can use your Jira username and password to authenticate with Jira Data Center/Server. + +In your Configuration file/Environment variables/Secrets file, add the following lines: + +```toml +jira_api_email = "your_username" +jira_api_token = "your_password" +``` + +(Note that indeed the 'jira_api_email' field is used for the username, and the 'jira_api_token' field is used for the user password.) + +##### Validating Basic authentication via Python script + +If you are facing issues retrieving tickets in Qodo Merge with Basic auth, you can validate the flow using a Python script. +This following steps will help you check if the basic auth is working correctly, and if you can access the Jira ticket details: + +1. run `pip install jira==3.8.0` + +2. run the following Python script (after replacing the placeholders with your actual values): + +??? example "Script to validate basic auth" + + ```python + from jira import JIRA + + + if __name__ == "__main__": + try: + # Jira server URL + server = "https://..." + # Basic auth + username = "..." + password = "..." + # Jira ticket code (e.g. "PROJ-123") + ticket_id = "..." + + print("Initializing JiraServerTicketProvider with JIRA server") + # Initialize JIRA client + jira = JIRA( + server=server, + basic_auth=(username, password), + timeout=30 + ) + if jira: + print(f"JIRA client initialized successfully") + else: + print("Error initializing JIRA client") + + # Fetch ticket details + ticket = jira.issue(ticket_id) + print(f"Ticket title: {ticket.fields.summary}") + + except Exception as e: + print(f"Error fetching JIRA ticket details: {e}") + ``` + +#### Using a Personal Access Token (PAT) for Jira Data Center/Server 1. Create a [Personal Access Token (PAT)](https://confluence.atlassian.com/enterprise/using-personal-access-tokens-1026032365.html) in your Jira account 2. In your Configuration file/Environment variables/Secrets file, add the following lines: @@ -176,7 +235,7 @@ jira_base_url = "YOUR_JIRA_BASE_URL" # e.g. https://jira.example.com jira_api_token = "YOUR_API_TOKEN" ``` -#### Validating PAT token via Python script +##### Validating PAT token via Python script If you are facing issues retrieving tickets in Qodo Merge with PAT token, you can validate the flow using a Python script. This following steps will help you check if the token is working correctly, and if you can access the Jira ticket details: From f0fc777a4432a4b46e9425fc29de0e97734c6d22 Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Fri, 18 Apr 2025 02:03:47 +0800 Subject: [PATCH 121/365] Improve markdown docs formatting for consistency and readability --- CHANGELOG.md | 7 +- CODE_OF_CONDUCT.md | 1 - CONTRIBUTING.md | 2 +- README.md | 106 +++++++++--------- RELEASE_NOTES.md | 19 +++- SECURITY.md | 6 +- docs/docs/ai_search/index.md | 26 ++--- docs/docs/chrome-extension/features.md | 4 +- docs/docs/chrome-extension/options.md | 2 - .../core-abilities/auto_best_practices.md | 16 ++- docs/docs/core-abilities/code_validation.md | 5 +- .../core-abilities/compression_strategy.md | 17 ++- docs/docs/core-abilities/dynamic_context.md | 8 +- .../core-abilities/fetching_ticket_context.md | 12 +- docs/docs/core-abilities/impact_evaluation.md | 7 +- docs/docs/core-abilities/index.md | 4 + docs/docs/core-abilities/interactivity.md | 12 +- docs/docs/core-abilities/metadata.md | 4 +- .../core-abilities/rag_context_enrichment.md | 10 +- docs/docs/core-abilities/self_reflection.md | 7 +- .../core-abilities/static_code_analysis.md | 8 +- docs/docs/finetuning_benchmark/index.md | 3 +- docs/docs/index.md | 87 +++++++------- docs/docs/installation/azure.md | 14 ++- docs/docs/installation/bitbucket.md | 2 +- docs/docs/installation/github.md | 51 +++++---- docs/docs/installation/gitlab.md | 31 ++--- docs/docs/installation/index.md | 1 + docs/docs/installation/locally.md | 33 +++--- docs/docs/installation/pr_agent.md | 2 +- docs/docs/installation/qodo_merge.md | 5 +- docs/docs/overview/data_privacy.md | 1 - docs/docs/overview/pr_agent_pro.md | 39 ++++--- docs/docs/tools/analyze.md | 2 + docs/docs/tools/ask.md | 5 +- docs/docs/tools/ci_feedback.md | 4 + docs/docs/tools/custom_labels.md | 12 +- docs/docs/tools/custom_prompt.md | 6 +- docs/docs/tools/describe.md | 39 ++++--- docs/docs/tools/documentation.md | 8 +- docs/docs/tools/help.md | 3 + docs/docs/tools/help_docs.md | 4 +- docs/docs/tools/implement.md | 11 +- docs/docs/tools/improve.md | 55 ++++++--- docs/docs/tools/improve_component.md | 4 +- docs/docs/tools/index.md | 37 +++--- docs/docs/tools/review.md | 12 +- docs/docs/tools/scan_repo_discussions.md | 8 +- docs/docs/tools/similar_code.md | 13 ++- docs/docs/tools/similar_issues.md | 9 +- docs/docs/tools/test.md | 6 +- docs/docs/tools/update_changelog.md | 4 +- .../docs/usage-guide/EXAMPLE_BEST_PRACTICE.md | 39 +++---- .../usage-guide/additional_configurations.md | 28 +++-- .../docs/usage-guide/automations_and_usage.md | 28 ++++- docs/docs/usage-guide/changing_a_model.md | 1 - .../docs/usage-guide/configuration_options.md | 17 ++- docs/docs/usage-guide/enabling_a_wiki.md | 8 +- docs/docs/usage-guide/index.md | 25 ++--- docs/docs/usage-guide/introduction.md | 1 - docs/docs/usage-guide/mail_notifications.md | 2 +- docs/docs/usage-guide/qodo_merge_models.md | 5 +- 62 files changed, 546 insertions(+), 402 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c750e4bfc8..ceb01df248 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## 2023-08-03 ### Optimized + - Optimized PR diff processing by introducing caching for diff files, reducing the number of API calls. - Refactored `load_large_diff` function to generate a patch only when necessary. - Fixed a bug in the GitLab provider where the new file was not retrieved correctly. @@ -8,6 +9,7 @@ ## 2023-08-02 ### Enhanced + - Updated several tools in the `pr_agent` package to use commit messages in their functionality. - Commit messages are now retrieved and stored in the `vars` dictionary for each tool. - Added a section to display the commit messages in the prompts of various tools. @@ -15,6 +17,7 @@ ## 2023-08-01 ### Enhanced + - Introduced the ability to retrieve commit messages from pull requests across different git providers. - Implemented commit messages retrieval for GitHub and GitLab providers. - Updated the PR description template to include a section for commit messages if they exist. @@ -22,10 +25,10 @@ - Implemented this feature for both GitHub and GitLab providers. - Added a new configuration option 'use_repo_settings_file' to enable or disable the use of a repo-specific settings file. - ## 2023-07-30 ### Enhanced + - Added the ability to modify any configuration parameter from 'configuration.toml' on-the-fly. - Updated the command line interface and bot commands to accept configuration changes as arguments. - Improved the PR agent to handle additional arguments for each action. @@ -33,6 +36,7 @@ ## 2023-07-28 ### Improved + - Enhanced error handling and logging in the GitLab provider. - Improved handling of inline comments and code suggestions in GitLab. - Fixed a bug where an additional unneeded line was added to code suggestions in GitLab. @@ -40,6 +44,7 @@ ## 2023-07-26 ### Added + - New feature for updating the CHANGELOG.md based on the contents of a PR. - Added support for this feature for the Github provider. - New configuration settings and prompts for the changelog update feature. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index f5ed00cdaf..8f1455ef62 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -42,4 +42,3 @@ with regard to the reporter of an incident. This Code of Conduct is adapted from the [Contributor Covenant](https://contributor-covenant.org), version 1.3.0, available at [contributor-covenant.org/version/1/3/0/](https://contributor-covenant.org/version/1/3/0/) - diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b22d2cd4db..0b638beb12 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ # Contributing to PR-Agent -Thank you for your interest in contributing to the PR-Agent project! +Thank you for your interest in contributing to the PR-Agent project! ## Getting Started diff --git a/README.md b/README.md index 94941097ad..2320a472fe 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,6 @@
- @@ -11,9 +10,9 @@
- [Installation Guide](https://qodo-merge-docs.qodo.ai/installation/) | -[Usage Guide](https://qodo-merge-docs.qodo.ai/usage-guide/) | -[Tools Guide](https://qodo-merge-docs.qodo.ai/tools/) | +[Installation Guide](https://qodo-merge-docs.qodo.ai/installation/) | +[Usage Guide](https://qodo-merge-docs.qodo.ai/usage-guide/) | +[Tools Guide](https://qodo-merge-docs.qodo.ai/tools/) | [Qodo Merge](https://qodo-merge-docs.qodo.ai/overview/pr_agent_pro/) 💎 PR-Agent aims to help efficiently review and handle pull requests, by providing AI feedback and suggestions @@ -39,7 +38,6 @@ PR-Agent aims to help efficiently review and handle pull requests, by providing [//]: # () [//]: # (- See the [Tools Guide](https://qodo-merge-docs.qodo.ai/tools/) for a detailed description of the different tools, and the available configurations for each tool.) - ## Table of Contents - [News and Updates](#news-and-updates) @@ -60,7 +58,6 @@ New tool for Qodo Merge 💎 - `/scan_repo_discussions`. Read more about it [here](https://qodo-merge-docs.qodo.ai/tools/scan_repo_discussions/). - ## Apr 14, 2025 GPT-4.1 is out. And its quite good on coding tasks... @@ -69,8 +66,8 @@ https://openai.com/index/gpt-4-1/ image - ## March 28, 2025 + A new version, v0.28, was released. See release notes [here](https://github.com/qodo-ai/pr-agent/releases/tag/v0.28). This version includes a new tool, [Help Docs](https://qodo-merge-docs.qodo.ai/tools/help_docs/), which can answer free-text questions based on a documentation folder. @@ -78,53 +75,55 @@ This version includes a new tool, [Help Docs](https://qodo-merge-docs.qodo.ai/to `/help_docs` is now being used to provide immediate automatic feedback to any user who [opens an issue](https://github.com/qodo-ai/pr-agent/issues/1608#issue-2897328825) on PR-Agent's open-source project ### Feb 28, 2025 -A new version, v0.27, was released. See release notes [here](https://github.com/qodo-ai/pr-agent/releases/tag/v0.27). +A new version, v0.27, was released. See release notes [here](https://github.com/qodo-ai/pr-agent/releases/tag/v0.27). ## Overview +
Supported commands per platform: -| | | GitHub | GitLab | Bitbucket | Azure DevOps | -|-------|---------------------------------------------------------------------------------------------------------|:--------------------:|:--------------------:|:--------:|:------------:| -| TOOLS | [Review](https://qodo-merge-docs.qodo.ai/tools/review/) | ✅ | ✅ | ✅ | ✅ | -| | [Describe](https://qodo-merge-docs.qodo.ai/tools/describe/) | ✅ | ✅ | ✅ | ✅ | -| | [Improve](https://qodo-merge-docs.qodo.ai/tools/improve/) | ✅ | ✅ | ✅ | ✅ | -| | [Ask](https://qodo-merge-docs.qodo.ai/tools/ask/) | ✅ | ✅ | ✅ | ✅ | -| | ⮑ [Ask on code lines](https://qodo-merge-docs.qodo.ai/tools/ask/#ask-lines) | ✅ | ✅ | | | -| | [Update CHANGELOG](https://qodo-merge-docs.qodo.ai/tools/update_changelog/) | ✅ | ✅ | ✅ | ✅ | -| | [Help Docs](https://qodo-merge-docs.qodo.ai/tools/help_docs/?h=auto#auto-approval) | ✅ | ✅ | ✅ | | -| | [Ticket Context](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/) 💎 | ✅ | ✅ | ✅ | | -| | [Utilizing Best Practices](https://qodo-merge-docs.qodo.ai/tools/improve/#best-practices) 💎 | ✅ | ✅ | ✅ | | -| | [PR Chat](https://qodo-merge-docs.qodo.ai/chrome-extension/features/#pr-chat) 💎 | ✅ | | | | -| | [Suggestion Tracking](https://qodo-merge-docs.qodo.ai/tools/improve/#suggestion-tracking) 💎 | ✅ | ✅ | | | -| | [CI Feedback](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/) 💎 | ✅ | | | | -| | [PR Documentation](https://qodo-merge-docs.qodo.ai/tools/documentation/) 💎 | ✅ | ✅ | | | -| | [Custom Labels](https://qodo-merge-docs.qodo.ai/tools/custom_labels/) 💎 | ✅ | ✅ | | | -| | [Analyze](https://qodo-merge-docs.qodo.ai/tools/analyze/) 💎 | ✅ | ✅ | | | -| | [Similar Code](https://qodo-merge-docs.qodo.ai/tools/similar_code/) 💎 | ✅ | | | | -| | [Custom Prompt](https://qodo-merge-docs.qodo.ai/tools/custom_prompt/) 💎 | ✅ | ✅ | ✅ | | -| | [Test](https://qodo-merge-docs.qodo.ai/tools/test/) 💎 | ✅ | ✅ | | | -| | [Implement](https://qodo-merge-docs.qodo.ai/tools/implement/) 💎 | ✅ | ✅ | ✅ | | -| | [Scan Repo Discussions](https://qodo-merge-docs.qodo.ai/tools/scan_repo_discussions/) 💎 | ✅ | | | | -| | [Auto-Approve](https://qodo-merge-docs.qodo.ai/tools/improve/?h=auto#auto-approval) 💎 | ✅ | ✅ | ✅ | | -| | | | | | | -| USAGE | [CLI](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#local-repo-cli) | ✅ | ✅ | ✅ | ✅ | -| | [App / webhook](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#github-app) | ✅ | ✅ | ✅ | ✅ | -| | [Tagging bot](https://github.com/Codium-ai/pr-agent#try-it-now) | ✅ | | | | -| | [Actions](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-action) | ✅ |✅| ✅ |✅| -| | | | | | | -| CORE | [PR compression](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) | ✅ | ✅ | ✅ | ✅ | -| | Adaptive and token-aware file patch fitting | ✅ | ✅ | ✅ | ✅ | -| | [Multiple models support](https://qodo-merge-docs.qodo.ai/usage-guide/changing_a_model/) | ✅ | ✅ | ✅ | ✅ | -| | [Local and global metadata](https://qodo-merge-docs.qodo.ai/core-abilities/metadata/) | ✅ | ✅ | ✅ | ✅ | -| | [Dynamic context](https://qodo-merge-docs.qodo.ai/core-abilities/dynamic_context/) | ✅ | ✅ | ✅ | ✅ | -| | [Self reflection](https://qodo-merge-docs.qodo.ai/core-abilities/self_reflection/) | ✅ | ✅ | ✅ | ✅ | -| | [Static code analysis](https://qodo-merge-docs.qodo.ai/core-abilities/static_code_analysis/) 💎 | ✅ | ✅ | | | -| | [Global and wiki configurations](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/) 💎 | ✅ | ✅ | ✅ | | -| | [PR interactive actions](https://www.qodo.ai/images/pr_agent/pr-actions.mp4) 💎 | ✅ | ✅ | | | -| | [Impact Evaluation](https://qodo-merge-docs.qodo.ai/core-abilities/impact_evaluation/) 💎 | ✅ | ✅ | | | +| | | GitHub | GitLab | Bitbucket | Azure DevOps | +| ----- | ------------------------------------------------------------------------------------------------------- |:------:|:------:|:---------:|:------------:| +| TOOLS | [Review](https://qodo-merge-docs.qodo.ai/tools/review/) | ✅ | ✅ | ✅ | ✅ | +| | [Describe](https://qodo-merge-docs.qodo.ai/tools/describe/) | ✅ | ✅ | ✅ | ✅ | +| | [Improve](https://qodo-merge-docs.qodo.ai/tools/improve/) | ✅ | ✅ | ✅ | ✅ | +| | [Ask](https://qodo-merge-docs.qodo.ai/tools/ask/) | ✅ | ✅ | ✅ | ✅ | +| | ⮑ [Ask on code lines](https://qodo-merge-docs.qodo.ai/tools/ask/#ask-lines) | ✅ | ✅ | | | +| | [Update CHANGELOG](https://qodo-merge-docs.qodo.ai/tools/update_changelog/) | ✅ | ✅ | ✅ | ✅ | +| | [Help Docs](https://qodo-merge-docs.qodo.ai/tools/help_docs/?h=auto#auto-approval) | ✅ | ✅ | ✅ | | +| | [Ticket Context](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/) 💎 | ✅ | ✅ | ✅ | | +| | [Utilizing Best Practices](https://qodo-merge-docs.qodo.ai/tools/improve/#best-practices) 💎 | ✅ | ✅ | ✅ | | +| | [PR Chat](https://qodo-merge-docs.qodo.ai/chrome-extension/features/#pr-chat) 💎 | ✅ | | | | +| | [Suggestion Tracking](https://qodo-merge-docs.qodo.ai/tools/improve/#suggestion-tracking) 💎 | ✅ | ✅ | | | +| | [CI Feedback](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/) 💎 | ✅ | | | | +| | [PR Documentation](https://qodo-merge-docs.qodo.ai/tools/documentation/) 💎 | ✅ | ✅ | | | +| | [Custom Labels](https://qodo-merge-docs.qodo.ai/tools/custom_labels/) 💎 | ✅ | ✅ | | | +| | [Analyze](https://qodo-merge-docs.qodo.ai/tools/analyze/) 💎 | ✅ | ✅ | | | +| | [Similar Code](https://qodo-merge-docs.qodo.ai/tools/similar_code/) 💎 | ✅ | | | | +| | [Custom Prompt](https://qodo-merge-docs.qodo.ai/tools/custom_prompt/) 💎 | ✅ | ✅ | ✅ | | +| | [Test](https://qodo-merge-docs.qodo.ai/tools/test/) 💎 | ✅ | ✅ | | | +| | [Implement](https://qodo-merge-docs.qodo.ai/tools/implement/) 💎 | ✅ | ✅ | ✅ | | +| | [Scan Repo Discussions](https://qodo-merge-docs.qodo.ai/tools/scan_repo_discussions/) 💎 | ✅ | | | | +| | [Auto-Approve](https://qodo-merge-docs.qodo.ai/tools/improve/?h=auto#auto-approval) 💎 | ✅ | ✅ | ✅ | | +| | | | | | | +| USAGE | [CLI](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#local-repo-cli) | ✅ | ✅ | ✅ | ✅ | +| | [App / webhook](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#github-app) | ✅ | ✅ | ✅ | ✅ | +| | [Tagging bot](https://github.com/Codium-ai/pr-agent#try-it-now) | ✅ | | | | +| | [Actions](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-action) | ✅ | ✅ | ✅ | ✅ | +| | | | | | | +| CORE | [PR compression](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) | ✅ | ✅ | ✅ | ✅ | +| | Adaptive and token-aware file patch fitting | ✅ | ✅ | ✅ | ✅ | +| | [Multiple models support](https://qodo-merge-docs.qodo.ai/usage-guide/changing_a_model/) | ✅ | ✅ | ✅ | ✅ | +| | [Local and global metadata](https://qodo-merge-docs.qodo.ai/core-abilities/metadata/) | ✅ | ✅ | ✅ | ✅ | +| | [Dynamic context](https://qodo-merge-docs.qodo.ai/core-abilities/dynamic_context/) | ✅ | ✅ | ✅ | ✅ | +| | [Self reflection](https://qodo-merge-docs.qodo.ai/core-abilities/self_reflection/) | ✅ | ✅ | ✅ | ✅ | +| | [Static code analysis](https://qodo-merge-docs.qodo.ai/core-abilities/static_code_analysis/) 💎 | ✅ | ✅ | | | +| | [Global and wiki configurations](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/) 💎 | ✅ | ✅ | ✅ | | +| | [PR interactive actions](https://www.qodo.ai/images/pr_agent/pr-actions.mp4) 💎 | ✅ | ✅ | | | +| | [Impact Evaluation](https://qodo-merge-docs.qodo.ai/core-abilities/impact_evaluation/) 💎 | ✅ | ✅ | | | + - 💎 means this feature is available only in [Qodo-Merge](https://www.qodo.ai/pricing/) [//]: # (- Support for additional git providers is described in [here](./docs/Full_environments.md)) @@ -162,6 +161,7 @@ ___ ___ ## Example results +

/describe

@@ -190,40 +190,37 @@ ___

-
-

- ## Try it now Try the Claude Sonnet powered PR-Agent instantly on _your public GitHub repository_. Just mention `@CodiumAI-Agent` and add the desired command in any PR comment. The agent will generate a response based on your command. For example, add a comment to any pull request with the following text: + ``` @CodiumAI-Agent /review ``` + and the agent will respond with a review of your PR. Note that this is a promotional bot, suitable only for initial experimentation. It does not have 'edit' access to your repo, for example, so it cannot update the PR description or add labels (`@CodiumAI-Agent /describe` will publish PR description as a comment). In addition, the bot cannot be used on private repositories, as it does not have access to the files there. - --- - ## Qodo Merge 💎 + [Qodo Merge](https://www.qodo.ai/pricing/) is a hosted version of PR-Agent, provided by Qodo. It is available for a monthly fee, and provides the following benefits: + 1. **Fully managed** - We take care of everything for you - hosting, models, regular updates, and more. Installation is as simple as signing up and adding the Qodo Merge app to your GitHub\GitLab\BitBucket repo. 2. **Improved privacy** - No data will be stored or used to train models. Qodo Merge will employ zero data retention, and will use an OpenAI account with zero data retention. 3. **Improved support** - Qodo Merge users will receive priority support, and will be able to request new features and capabilities. 4. **Extra features** -In addition to the benefits listed above, Qodo Merge will emphasize more customization, and the usage of static code analysis, in addition to LLM logic, to improve results. See [here](https://qodo-merge-docs.qodo.ai/overview/pr_agent_pro/) for a list of features available in Qodo Merge. - - ## How it works The following diagram illustrates PR-Agent tools and their flow: @@ -243,7 +240,6 @@ Here are some advantages of PR-Agent: - Our JSON prompting strategy enables to have **modular, customizable tools**. For example, the '/review' tool categories can be controlled via the [configuration](pr_agent/settings/configuration.toml) file. Adding additional categories is easy and accessible. - We support **multiple git providers** (GitHub, Gitlab, Bitbucket), **multiple ways** to use the tool (CLI, GitHub Action, GitHub App, Docker, ...), and **multiple models** (GPT, Claude, Deepseek, ...) - ## Data privacy ### Self-hosted PR-Agent diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 7ce674d2e6..07a16a8464 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,4 +1,5 @@ ## [Version 0.11] - 2023-12-07 + - codiumai/pr-agent:0.11 - codiumai/pr-agent:0.11-github_app - codiumai/pr-agent:0.11-bitbucket-app @@ -7,16 +8,18 @@ - codiumai/pr-agent:0.11-github_action ### Added::Algo + - New section in `/describe` tool - [PR changes walkthrough](https://github.com/Codium-ai/pr-agent/pull/509) - Improving PR Agent [prompts](https://github.com/Codium-ai/pr-agent/pull/501) - Persistent tools (`/review`, `/describe`) now send an [update message](https://github.com/Codium-ai/pr-agent/pull/499) after finishing - Add Amazon Bedrock [support](https://github.com/Codium-ai/pr-agent/pull/483) ### Fixed -- Update [dependencies](https://github.com/Codium-ai/pr-agent/pull/503) in requirements.txt for Python 3.12 +- Update [dependencies](https://github.com/Codium-ai/pr-agent/pull/503) in requirements.txt for Python 3.12 ## [Version 0.10] - 2023-11-15 + - codiumai/pr-agent:0.10 - codiumai/pr-agent:0.10-github_app - codiumai/pr-agent:0.10-bitbucket-app @@ -25,6 +28,7 @@ - codiumai/pr-agent:0.10-github_action ### Added::Algo + - Review tool now works with [persistent comments](https://github.com/Codium-ai/pr-agent/pull/451) by default - Bitbucket now publishes review suggestions with [code links](https://github.com/Codium-ai/pr-agent/pull/428) - Enabling to limit [max number of tokens](https://github.com/Codium-ai/pr-agent/pull/437/files) @@ -34,11 +38,13 @@ - Decoupled custom labels from [PR type](https://github.com/Codium-ai/pr-agent/pull/431) ### Fixed + - Fixed bug in [parsing quotes](https://github.com/Codium-ai/pr-agent/pull/446) in CLI - Preserve [user-added labels](https://github.com/Codium-ai/pr-agent/pull/433) in pull requests - Bug fixes in GitLab and BitBucket ## [Version 0.9] - 2023-10-29 + - codiumai/pr-agent:0.9 - codiumai/pr-agent:0.9-github_app - codiumai/pr-agent:0.9-bitbucket-app @@ -47,6 +53,7 @@ - codiumai/pr-agent:0.9-github_action ### Added::Algo + - New tool - [generate_labels](https://github.com/Codium-ai/pr-agent/blob/main/docs/GENERATE_CUSTOM_LABELS.md) - New ability to use [customize labels](https://github.com/Codium-ai/pr-agent/blob/main/docs/GENERATE_CUSTOM_LABELS.md#how-to-enable-custom-labels) on the `review` and `describe` tools. - New tool - [add_docs](https://github.com/Codium-ai/pr-agent/blob/main/docs/ADD_DOCUMENTATION.md) @@ -56,14 +63,17 @@ - PR Description default mode is now in [bullet points](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml#L35). ### Added::Documentation + Significant documentation updates (see [Installation Guide](https://github.com/Codium-ai/pr-agent/blob/main/INSTALL.md), [Usage Guide](https://github.com/Codium-ai/pr-agent/blob/main/Usage.md), and [Tools Guide](https://github.com/Codium-ai/pr-agent/blob/main/docs/TOOLS_GUIDE.md)) ### Fixed + - Fixed support for BitBucket pipeline (see [link](https://github.com/Codium-ai/pr-agent/pull/386)) - Fixed a bug in `review -i` tool - Added blacklist for specific file extensions in `add_docs` tool (see [link](https://github.com/Codium-ai/pr-agent/pull/385/)) ## [Version 0.8] - 2023-09-27 + - codiumai/pr-agent:0.8 - codiumai/pr-agent:0.8-github_app - codiumai/pr-agent:0.8-bitbucket-app @@ -72,16 +82,18 @@ Significant documentation updates (see [Installation Guide](https://github.com/C - codiumai/pr-agent:0.8-github_action ### Added::Algo + - GitHub Action: Can control which tools will run automatically when a new PR is created. (see usage guide: https://github.com/Codium-ai/pr-agent/blob/main/Usage.md#working-with-github-action) - Code suggestion tool: Will try to avoid an 'add comments' suggestion (see https://github.com/Codium-ai/pr-agent/pull/327) ### Fixed -- Gitlab: Fixed a bug of improper usage of pr_id +- Gitlab: Fixed a bug of improper usage of pr_id ## [Version 0.7] - 2023-09-20 ### Docker Tags + - codiumai/pr-agent:0.7 - codiumai/pr-agent:0.7-github_app - codiumai/pr-agent:0.7-bitbucket-app @@ -90,14 +102,17 @@ Significant documentation updates (see [Installation Guide](https://github.com/C - codiumai/pr-agent:0.7-github_action ### Added::Algo + - New tool /similar_issue - Currently on GitHub app and CLI: indexes the issues in the repo, find the most similar issues to the target issue. - Describe markers: Empower the /describe tool with a templating capability (see more details in https://github.com/Codium-ai/pr-agent/pull/273). - New feature in the /review tool - added an estimated effort estimation to the review (https://github.com/Codium-ai/pr-agent/pull/306). ### Added::Infrastructure + - Implementation of a GitLab webhook. - Implementation of a BitBucket app. ### Fixed + - Protection against no code suggestions generated. - Resilience to repositories where the languages cannot be automatically detected. diff --git a/SECURITY.md b/SECURITY.md index 907893edbb..5cf4dfba43 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,6 +1,6 @@ # Security Policy -PR-Agent is an open-source tool to help efficiently review and handle pull requests. Qodo Merge is a paid version of PR-Agent, designed for companies and teams that require additional features and capabilities. +PR-Agent is an open-source tool to help efficiently review and handle pull requests. Qodo Merge is a paid version of PR-Agent, designed for companies and teams that require additional features and capabilities. This document describes the security policy of PR-Agent. For Qodo Merge's security policy, see [here](https://qodo-merge-docs.qodo.ai/overview/data_privacy/#qodo-merge). @@ -9,13 +9,13 @@ This document describes the security policy of PR-Agent. For Qodo Merge's securi When using PR-Agent with your OpenAI (or other LLM provider) API key, the security relationship is directly between you and the provider. We do not send your code to Qodo servers. Types of [self-hosted solutions](https://qodo-merge-docs.qodo.ai/installation): + - Locally - GitHub integration - GitLab integration - BitBucket integration - Azure DevOps integration - ## PR-Agent Supported Versions This section outlines which versions of PR-Agent are currently supported with security updates. @@ -25,6 +25,7 @@ This section outlines which versions of PR-Agent are currently supported with se #### Latest Version For the most recent updates, use our latest Docker image which is automatically built nightly: + ```yaml uses: qodo-ai/pr-agent@main ``` @@ -46,6 +47,7 @@ steps: #### Enhanced Security with Docker Digest For maximum security, you can specify the Docker image using its digest: + ```yaml steps: - name: PR Agent action step diff --git a/docs/docs/ai_search/index.md b/docs/docs/ai_search/index.md index 2ef9a8f97a..7f646a04ee 100644 --- a/docs/docs/ai_search/index.md +++ b/docs/docs/ai_search/index.md @@ -4,10 +4,10 @@ Search through our documentation using AI-powered natural language queries.

- @@ -206,13 +206,13 @@ window.addEventListener('load', function() { const resultsContainer = document.getElementById('results'); const spinner = document.getElementById('spinner'); const searchContainer = document.querySelector('.search-container'); - + // Hide spinner spinner.style.display = 'none'; - + // Scroll to search bar searchContainer.scrollIntoView({ behavior: 'smooth', block: 'start' }); - + try { const results = JSON.parse(responseText); @@ -224,7 +224,7 @@ window.addEventListener('load', function() { }); const htmlContent = marked.parse(results.message); - + resultsContainer.className = 'markdown-content'; resultsContainer.innerHTML = htmlContent; @@ -234,7 +234,7 @@ window.addEventListener('load', function() { const offset = 55; // Offset from top in pixels const elementPosition = searchContainer.getBoundingClientRect().top; const offsetPosition = elementPosition + window.pageYOffset - offset; - + window.scrollTo({ top: offsetPosition, behavior: 'smooth' @@ -261,7 +261,7 @@ window.addEventListener('load', function() { spinner.style.display = 'flex'; resultsContainer.innerHTML = ''; - try { + try { const data = { "query": searchTerm }; @@ -299,11 +299,11 @@ window.addEventListener('load', function() { // Add event listeners const searchButton = document.getElementById('searchButton'); const searchInput = document.getElementById('searchInput'); - + if (searchButton) { searchButton.addEventListener('click', performSearch); } - + if (searchInput) { searchInput.addEventListener('keypress', function(e) { if (e.key === 'Enter') { @@ -312,4 +312,4 @@ window.addEventListener('load', function() { }); } }); - \ No newline at end of file + diff --git a/docs/docs/chrome-extension/features.md b/docs/docs/chrome-extension/features.md index 97c17aac60..b00730542c 100644 --- a/docs/docs/chrome-extension/features.md +++ b/docs/docs/chrome-extension/features.md @@ -17,8 +17,8 @@ Qodo Merge constructs a comprehensive context for each pull request, incorporati - ### Toolbar extension + With Qodo Merge Chrome extension, it's [easier than ever](https://www.youtube.com/watch?v=gT5tli7X4H4) to interactively configure and experiment with the different tools and configuration options. For private repositories, after you found the setup that works for you, you can also easily export it as a persistent configuration file, and use it for automatic commands. @@ -37,7 +37,6 @@ For example, you can choose to present only message from Qodo Merge, or filter t - ### Enhanced code suggestions Qodo Merge Chrome extension adds the following capabilities to code suggestions tool's comments: @@ -45,7 +44,6 @@ Qodo Merge Chrome extension adds the following capabilities to code suggestions - Auto-expand the table when you are viewing a code block, to avoid clipping. - Adding a "quote-and-reply" button, that enables to address and comment on a specific suggestion (for example, asking the author to fix the issue) - diff --git a/docs/docs/chrome-extension/options.md b/docs/docs/chrome-extension/options.md index 9316f284a5..1ee93959e2 100644 --- a/docs/docs/chrome-extension/options.md +++ b/docs/docs/chrome-extension/options.md @@ -14,12 +14,10 @@ Alternatively, you can access the options page directly using this URL: - ### Configuration Options - #### API Base Host For single-tenant customers, you can configure the extension to communicate directly with your company's Qodo Merge server instance. diff --git a/docs/docs/core-abilities/auto_best_practices.md b/docs/docs/core-abilities/auto_best_practices.md index 9bc342403b..2ea6cad01d 100644 --- a/docs/docs/core-abilities/auto_best_practices.md +++ b/docs/docs/core-abilities/auto_best_practices.md @@ -1,4 +1,5 @@ # Auto Best Practices 💎 + `Supported Git Platforms: GitHub` ## Overview @@ -9,17 +10,16 @@ ### Finding Code Problems - Exploration Phase -The `improve` tool identifies potential issues, problems and bugs in Pull Request (PR) code changes. -Rather than focusing on minor issues like code style or formatting, the tool intelligently analyzes code to detect meaningful problems. +The `improve` tool identifies potential issues, problems and bugs in Pull Request (PR) code changes. +Rather than focusing on minor issues like code style or formatting, the tool intelligently analyzes code to detect meaningful problems. The analysis intentionally takes a flexible, _exploratory_ approach to identify meaningful potential issues, allowing the tool to surface relevant code suggestions without being constrained by predefined categories. ### Tracking Implemented Suggestions -Qodo Merge features a novel [tracking system](https://qodo-merge-docs.qodo.ai/tools/improve/#suggestion-tracking) that automatically detects when PR authors implement AI-generated code suggestions. +Qodo Merge features a novel [tracking system](https://qodo-merge-docs.qodo.ai/tools/improve/#suggestion-tracking) that automatically detects when PR authors implement AI-generated code suggestions. All accepted suggestions are aggregated in a repository-specific wiki page called [`.pr_agent_accepted_suggestions`](https://github.com/qodo-ai/pr-agent/wiki/.pr_agent_accepted_suggestions) - ### Learning and Applying Auto Best Practices Monthly, Qodo Merge analyzes the collection of accepted suggestions to generate repository-specific best practices, stored in [`.pr_agent_auto_best_practices`](https://github.com/qodo-ai/pr-agent/wiki/.pr_agent_auto_best_practices) wiki file. @@ -33,17 +33,15 @@ This creates a two-phase analysis: By keeping these phases decoupled, the tool remains free to discover new or unseen issues and problems, while also learning from past experiences. - When presenting the suggestions generated by the `improve` tool, Qodo Merge will add a dedicated label for each suggestion generated from the auto best practices - 'Learned best practice': ![Auto best practice suggestion](https://www.qodo.ai/images/pr_agent/auto_best_practices.png){width=684} - ## Auto Best Practices vs Custom Best Practices -Teams and companies can also manually define their own [custom best practices](https://qodo-merge-docs.qodo.ai/tools/improve/#best-practices) in Qodo Merge. +Teams and companies can also manually define their own [custom best practices](https://qodo-merge-docs.qodo.ai/tools/improve/#best-practices) in Qodo Merge. -When custom best practices exist, Qodo Merge will still generate an 'auto best practices' wiki file, though it won't be used by the `improve` tool. +When custom best practices exist, Qodo Merge will still generate an 'auto best practices' wiki file, though it won't be used by the `improve` tool. However, this auto-generated file can still serve two valuable purposes: 1. It can help enhance your custom best practices with additional insights derived from suggestions your team found valuable enough to implement @@ -66,4 +64,4 @@ extra_instructions = "" # Max number of patterns to be detected max_patterns = 5 -``` \ No newline at end of file +``` diff --git a/docs/docs/core-abilities/code_validation.md b/docs/docs/core-abilities/code_validation.md index 30f03f8b11..59e02eba28 100644 --- a/docs/docs/core-abilities/code_validation.md +++ b/docs/docs/core-abilities/code_validation.md @@ -1,4 +1,5 @@ ## Introduction + The Git environment usually represents the final stage before code enters production. Hence, Detecting bugs and issues during the review process is critical. The [`improve`](https://qodo-merge-docs.qodo.ai/tools/improve/) tool provides actionable code suggestions for your pull requests, aiming to help detect and fix bugs and problems. @@ -32,8 +33,8 @@ Read more about this process in the [self-reflection](https://qodo-merge-docs.qo ## Conclusion The validation methods described above enhance the reliability of code suggestions and help PR authors determine which suggestions are safer to apply in the Git environment. -Of course, additional factors should be considered, such as suggestion complexity and potential code impact. +Of course, additional factors should be considered, such as suggestion complexity and potential code impact. Human judgment remains essential. After clicking 'apply', Qodo Merge still presents the 'before' and 'after' code snippets for review, allowing you to assess the changes before finalizing the commit. -![improve](https://codium.ai/images/pr_agent/improve.png){width=512} \ No newline at end of file +![improve](https://codium.ai/images/pr_agent/improve.png){width=512} diff --git a/docs/docs/core-abilities/compression_strategy.md b/docs/docs/core-abilities/compression_strategy.md index 1115c33c8b..ff948b73a1 100644 --- a/docs/docs/core-abilities/compression_strategy.md +++ b/docs/docs/core-abilities/compression_strategy.md @@ -1,5 +1,6 @@ ## Overview - PR Compression Strategy + There are two scenarios: 1. The PR is small enough to fit in a single prompt (including system and user prompt) @@ -8,6 +9,7 @@ There are two scenarios: For both scenarios, we first use the following strategy #### Repo language prioritization strategy + We prioritize the languages of the repo based on the following criteria: 1. Exclude binary files and non code files (e.g. images, pdfs, etc) @@ -15,28 +17,33 @@ We prioritize the languages of the repo based on the following criteria: 3. We sort the PR files by the most common languages in the repo (in descending order): * ```[[file.py, file2.py],[file3.js, file4.jsx],[readme.md]]``` - ### Small PR + In this case, we can fit the entire PR in a single prompt: + 1. Exclude binary files and non code files (e.g. images, pdfs, etc) 2. We Expand the surrounding context of each patch to 3 lines above and below the patch ### Large PR #### Motivation + Pull Requests can be very long and contain a lot of information with varying degree of relevance to the pr-agent. We want to be able to pack as much information as possible in a single LMM prompt, while keeping the information relevant to the pr-agent. #### Compression strategy + We prioritize additions over deletions: - - Combine all deleted files into a single list (`deleted files`) - - File patches are a list of hunks, remove all hunks of type deletion-only from the hunks in the file patch -#### Adaptive and token-aware file patch fitting +* Combine all deleted files into a single list (`deleted files`) +* File patches are a list of hunks, remove all hunks of type deletion-only from the hunks in the file patch + +#### Adaptive and token-aware file patch fitting + We use [tiktoken](https://github.com/openai/tiktoken) to tokenize the patches after the modifications described above, and we use the following strategy to fit the patches into the prompt: 1. Within each language we sort the files by the number of tokens in the file (in descending order): - - ```[[file2.py, file.py],[file4.jsx, file3.js],[readme.md]]``` + * ```[[file2.py, file.py],[file4.jsx, file3.js],[readme.md]]``` 2. Iterate through the patches in the order described above 3. Add the patches to the prompt until the prompt reaches a certain buffer from the max token length 4. If there are still patches left, add the remaining patches as a list called `other modified files` to the prompt until the prompt reaches the max token length (hard stop), skip the rest of the patches. diff --git a/docs/docs/core-abilities/dynamic_context.md b/docs/docs/core-abilities/dynamic_context.md index eff3e60c9c..76ec2d6e25 100644 --- a/docs/docs/core-abilities/dynamic_context.md +++ b/docs/docs/core-abilities/dynamic_context.md @@ -7,7 +7,8 @@ This approach balances providing sufficient context for accurate analysis, while ## Introduction Pull request code changes are retrieved in a unified diff format, showing three lines of context before and after each modified section, with additions marked by '+' and deletions by '-'. -``` + +```diff @@ -12,5 +12,5 @@ def func1(): code line that already existed in the file... code line that already existed in the file... @@ -25,7 +26,6 @@ Pull request code changes are retrieved in a unified diff format, showing three This unified diff format can be challenging for AI models to interpret accurately, as it provides limited context for understanding the full scope of code changes. The presentation of code using '+', '-', and ' ' symbols to indicate additions, deletions, and unchanged lines respectively also differs from the standard code formatting typically used to train AI models. - ## Challenges of expanding the context window While expanding the context window is technically feasible, it presents a more fundamental trade-off: @@ -43,6 +43,7 @@ Pull requests often encompass multiple changes across many files, potentially sp - Increased context expands the token count, increasing processing time and cost, and may prevent the model from processing the entire pull request in a single pass. ## Asymmetric and dynamic context + To address these challenges, Qodo Merge employs an **asymmetric** and **dynamic** context strategy, providing the model with more focused and relevant context information for each code change. **Asymmetric:** @@ -62,7 +63,8 @@ To prevent overwhelming the model with excessive context, we impose a limit on t This balance allows for comprehensive understanding while maintaining efficiency and limiting context token usage. ## Appendix - relevant configuration options -``` + +```toml [config] patch_extension_skip_types =[".md",".txt"] # Skip files with these extensions when trying to extend the context allow_dynamic_context=true # Allow dynamic context extension diff --git a/docs/docs/core-abilities/fetching_ticket_context.md b/docs/docs/core-abilities/fetching_ticket_context.md index 6418586a25..c6424ad1b9 100644 --- a/docs/docs/core-abilities/fetching_ticket_context.md +++ b/docs/docs/core-abilities/fetching_ticket_context.md @@ -1,7 +1,9 @@ # Fetching Ticket Context for PRs + `Supported Git Platforms: GitHub, GitLab, Bitbucket` ## Overview + Qodo Merge streamlines code review workflows by seamlessly connecting with multiple ticket management systems. This integration enriches the review process by automatically surfacing relevant ticket information and context alongside code changes. @@ -27,16 +29,17 @@ Ticket Recognition Requirements: - For Jira tickets, you should follow the instructions in [Jira Integration](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/#jira-integration) in order to authenticate with Jira. ### Describe tool + Qodo Merge will recognize the ticket and use the ticket content (title, description, labels) to provide additional context for the code changes. By understanding the reasoning and intent behind modifications, the LLM can offer more insightful and relevant code analysis. ### Review tool + Similarly to the `describe` tool, the `review` tool will use the ticket content to provide additional context for the code changes. In addition, this feature will evaluate how well a Pull Request (PR) adheres to its original purpose/intent as defined by the associated ticket or issue mentioned in the PR description. Each ticket will be assigned a label (Compliance/Alignment level), Indicates the degree to which the PR fulfills its original purpose, Options: Fully compliant, Partially compliant or Not compliant. - ![Ticket Compliance](https://www.qodo.ai/images/pr_agent/ticket_compliance_review.png){width=768} By default, the tool will automatically validate if the PR complies with the referenced ticket. @@ -63,6 +66,7 @@ Since Qodo Merge is integrated with GitHub, it doesn't require any additional co We support both Jira Cloud and Jira Server/Data Center. ### Jira Cloud + There are two ways to authenticate with Jira Cloud: **1) Jira App Authentication** @@ -101,7 +105,6 @@ jira_api_token = "YOUR_API_TOKEN" jira_api_email = "YOUR_EMAIL" ``` - ### Jira Data Center/Server [//]: # () @@ -163,7 +166,6 @@ jira_api_email = "YOUR_EMAIL" [//]: # (* You will be redirected back to Qodo Merge and you will see a success message.) - [//]: # (Personal Access Token (PAT) Authentication) #### Using Basic Authentication for Jira Data Center/Server @@ -181,7 +183,7 @@ jira_api_token = "your_password" ##### Validating Basic authentication via Python script -If you are facing issues retrieving tickets in Qodo Merge with Basic auth, you can validate the flow using a Python script. +If you are facing issues retrieving tickets in Qodo Merge with Basic auth, you can validate the flow using a Python script. This following steps will help you check if the basic auth is working correctly, and if you can access the Jira ticket details: 1. run `pip install jira==3.8.0` @@ -237,7 +239,7 @@ jira_api_token = "YOUR_API_TOKEN" ##### Validating PAT token via Python script -If you are facing issues retrieving tickets in Qodo Merge with PAT token, you can validate the flow using a Python script. +If you are facing issues retrieving tickets in Qodo Merge with PAT token, you can validate the flow using a Python script. This following steps will help you check if the token is working correctly, and if you can access the Jira ticket details: 1. run `pip install jira==3.8.0` diff --git a/docs/docs/core-abilities/impact_evaluation.md b/docs/docs/core-abilities/impact_evaluation.md index 2790411e64..4f56ea56fa 100644 --- a/docs/docs/core-abilities/impact_evaluation.md +++ b/docs/docs/core-abilities/impact_evaluation.md @@ -3,22 +3,24 @@ Demonstrating the return on investment (ROI) of AI-powered initiatives is crucial for modern organizations. To address this need, Qodo Merge has developed an AI impact measurement tools and metrics, providing advanced analytics to help businesses quantify the tangible benefits of AI adoption in their PR review process. - ## Auto Impact Validator - Real-Time Tracking of Implemented Qodo Merge Suggestions ### How It Works + When a user pushes a new commit to the pull request, Qodo Merge automatically compares the updated code against the previous suggestions, marking them as implemented if the changes address these recommendations, whether directly or indirectly: 1. **Direct Implementation:** The user directly addresses the suggestion as-is in the PR, either by clicking on the "apply code suggestion" checkbox or by making the changes manually. 2. **Indirect Implementation:** Qodo Merge recognizes when a suggestion's intent is fulfilled, even if the exact code changes differ from the original recommendation. It marks these suggestions as implemented, acknowledging that users may achieve the same goal through alternative solutions. ### Real-Time Visual Feedback + Upon confirming that a suggestion was implemented, Qodo Merge automatically adds a ✅ (check mark) to the relevant suggestion, enabling transparent tracking of Qodo Merge's impact analysis. Qodo Merge will also add, inside the relevant suggestions, an explanation of how the new code was impacted by each suggestion. ![Suggestion_checkmark](https://codium.ai/images/pr_agent/auto_suggestion_checkmark.png){width=512} ### Dashboard Metrics + The dashboard provides macro-level insights into the overall impact of Qodo Merge on the pull-request process with key productivity metrics. By offering clear, data-driven evidence of Qodo Merge's impact, it empowers leadership teams to make informed decisions about the tool's effectiveness and ROI. @@ -26,6 +28,7 @@ By offering clear, data-driven evidence of Qodo Merge's impact, it empowers lead Here are key metrics that the dashboard tracks: #### Qodo Merge Impacts per 1K Lines + ![Dashboard](https://codium.ai/images/pr_agent/impacts_per_1k_llines.png){width=512} > Explanation: for every 1K lines of code (additions/edits), Qodo Merge had on average ~X suggestions implemented. @@ -36,9 +39,11 @@ Here are key metrics that the dashboard tracks: 3. **Quantifies Value and ROI:** The metric directly correlates with the value Qodo Merge is providing, showing how frequently it offers improvements relative to the amount of new code being written. This provides a clear, quantifiable way to demonstrate Qodo Merge's return on investment to stakeholders. #### Suggestion Effectiveness Across Categories + ![Impacted_Suggestion_Score](https://codium.ai/images/pr_agent/impact_by_category.png){width=512} > Explanation: This chart illustrates the distribution of implemented suggestions across different categories, enabling teams to better understand Qodo Merge's impact on various aspects of code quality and development practices. #### Suggestion Score Distribution + ![Impacted_Suggestion_Score](https://codium.ai/images/pr_agent/impacted_score_dist.png){width=512} > Explanation: The distribution of the suggestion score for the implemented suggestions, ensuring that higher-scored suggestions truly represent more significant improvements. diff --git a/docs/docs/core-abilities/index.md b/docs/docs/core-abilities/index.md index d15b0ec69c..d06a39aed2 100644 --- a/docs/docs/core-abilities/index.md +++ b/docs/docs/core-abilities/index.md @@ -1,4 +1,5 @@ # Core Abilities + Qodo Merge utilizes a variety of core abilities to provide a comprehensive and efficient code review experience. These abilities include: - [Auto best practices](https://qodo-merge-docs.qodo.ai/core-abilities/auto_best_practices/) @@ -20,13 +21,16 @@ Here are some additional technical blogs from Qodo, that delve deeper into the c These resources provide more comprehensive insights into leveraging LLMs for software development. ### Code Generation and LLMs + - [Effective AI code suggestions: less is more](https://www.qodo.ai/blog/effective-code-suggestions-llms-less-is-more/) - [State-of-the-art Code Generation with AlphaCodium – From Prompt Engineering to Flow Engineering](https://www.qodo.ai/blog/qodoflow-state-of-the-art-code-generation-for-code-contests/) - [RAG for a Codebase with 10k Repos](https://www.qodo.ai/blog/rag-for-large-scale-code-repos/) ### Development Processes + - [Understanding the Challenges and Pain Points of the Pull Request Cycle](https://www.qodo.ai/blog/understanding-the-challenges-and-pain-points-of-the-pull-request-cycle/) - [Introduction to Code Coverage Testing](https://www.qodo.ai/blog/introduction-to-code-coverage-testing/) ### Cost Optimization + - [Reduce Your Costs by 30% When Using GPT for Python Code](https://www.qodo.ai/blog/reduce-your-costs-by-30-when-using-gpt-3-for-python-code/) diff --git a/docs/docs/core-abilities/interactivity.md b/docs/docs/core-abilities/interactivity.md index 0e0dbd7d6a..04297b9edd 100644 --- a/docs/docs/core-abilities/interactivity.md +++ b/docs/docs/core-abilities/interactivity.md @@ -5,7 +5,7 @@ ## Overview Qodo Merge transforms static code reviews into interactive experiences by enabling direct actions from pull request (PR) comments. -Developers can immediately trigger actions and apply changes with simple checkbox clicks. +Developers can immediately trigger actions and apply changes with simple checkbox clicks. This focused workflow maintains context while dramatically reducing the time between PR creation and final merge. The approach eliminates manual steps, provides clear visual indicators, and creates immediate feedback loops all within the same interface. @@ -24,8 +24,7 @@ The [`/improve`](https://qodo-merge-docs.qodo.ai/tools/improve/) command deliver - _**Author self-review**_: Interactive acknowledgment that developers have opened and reviewed collapsed suggestions - -### 2\. Interactive `/analyze` Tool +### 2\. Interactive `/analyze` Tool The [`/analyze`](https://qodo-merge-docs.qodo.ai/tools/analyze/) command provides component-level analysis with interactive options for each identified code component: @@ -35,9 +34,8 @@ The [`/analyze`](https://qodo-merge-docs.qodo.ai/tools/analyze/) command provide - Component-specific actions that trigger only for the selected elements, providing focused assistance +### 3\. Interactive `/help` Tool -### 3\. Interactive `/help` Tool - -The [`/help`](https://qodo-merge-docs.qodo.ai/tools/help/) command not only lists available tools and their descriptions but also enables immediate tool invocation through interactive checkboxes. -When a user checks a tool's checkbox, Qodo Merge instantly triggers that tool without requiring additional commands. +The [`/help`](https://qodo-merge-docs.qodo.ai/tools/help/) command not only lists available tools and their descriptions but also enables immediate tool invocation through interactive checkboxes. +When a user checks a tool's checkbox, Qodo Merge instantly triggers that tool without requiring additional commands. This transforms the standard help menu into an interactive launch pad for all Qodo Merge capabilities, eliminating context switching by keeping developers within their PR workflow. diff --git a/docs/docs/core-abilities/metadata.md b/docs/docs/core-abilities/metadata.md index b886929e9d..00ed3b5268 100644 --- a/docs/docs/core-abilities/metadata.md +++ b/docs/docs/core-abilities/metadata.md @@ -1,4 +1,5 @@ ## Local and global metadata injection with multi-stage analysis + 1\. Qodo Merge initially retrieves for each PR the following data: @@ -23,7 +24,7 @@ This effectively enables multi-stage chain-of-thought analysis, without doing an For example, when generating code suggestions for different files, Qodo Merge can inject the AI-generated ["Changes walkthrough"](https://github.com/Codium-ai/pr-agent/pull/1202#issue-2511546839) file summary in the prompt: -``` +```diff ## File: 'src/file1.py' ### AI-generated file summary: - edited function `func1` that does X @@ -51,6 +52,5 @@ __old hunk__ 3\. The entire PR files that were retrieved are also used to expand and enhance the PR context (see [Dynamic Context](https://qodo-merge-docs.qodo.ai/core-abilities/dynamic_context/)). - 4\. All the metadata described above represents several level of cumulative analysis - ranging from hunk level, to file level, to PR level, to organization level. This comprehensive approach enables Qodo Merge AI models to generate more precise and contextually relevant suggestions and feedback. diff --git a/docs/docs/core-abilities/rag_context_enrichment.md b/docs/docs/core-abilities/rag_context_enrichment.md index 694972191a..a6ba038f23 100644 --- a/docs/docs/core-abilities/rag_context_enrichment.md +++ b/docs/docs/core-abilities/rag_context_enrichment.md @@ -6,7 +6,6 @@ - RAG is available only for Qodo enterprise plan users, with single tenant or on-premises setup. - Database setup and codebase indexing must be completed before proceeding. [Contact support](https://www.qodo.ai/contact/) for more information. - ## Overview ### What is RAG Context Enrichment? @@ -17,13 +16,13 @@ A feature that enhances AI analysis by retrieving and referencing relevant code Using Retrieval-Augmented Generation (RAG), it searches your configured repositories for contextually relevant code segments, enriching pull request (PR) insights and accelerating review accuracy. - ## Getting started ### Configuration options In order to enable the RAG feature, add the following lines to your configuration file: -``` toml + +```toml [rag_arguments] enable_rag=true ``` @@ -45,7 +44,7 @@ enable_rag=true #### 1\. The `/review` Tool -The [`/review`](https://qodo-merge-docs.qodo.ai/tools/review/) tool offers the _Focus area from RAG data_ which contains feedback based on the RAG references analysis. +The [`/review`](https://qodo-merge-docs.qodo.ai/tools/review/) tool offers the _Focus area from RAG data_ which contains feedback based on the RAG references analysis. The complete list of references found relevant to the PR will be shown in the _References_ section, helping developers understand the broader context by exploring the provided references. ![References](https://codium.ai/images/pr_agent/rag_review.png){width=640} @@ -64,15 +63,16 @@ The _References_ section displays the additional repository content consulted to ![References](https://codium.ai/images/pr_agent/rag_ask.png){width=640} - ## Limitations ### Querying the codebase presents significant challenges + - **Search Method**: RAG uses natural language queries to find semantically relevant code sections - **Result Quality**: No guarantee that RAG results will be useful for all queries - **Scope Recommendation**: To reduce noise, focus on the PR repository rather than searching across multiple repositories ### This feature has several requirements and restrictions + - **Codebase**: Must be properly indexed for search functionality - **Security**: Requires secure and private indexed codebase implementation - **Deployment**: Only available for Qodo Merge Enterprise plan using single tenant or on-premises setup diff --git a/docs/docs/core-abilities/self_reflection.md b/docs/docs/core-abilities/self_reflection.md index 0a40912886..2675a89d9e 100644 --- a/docs/docs/core-abilities/self_reflection.md +++ b/docs/docs/core-abilities/self_reflection.md @@ -6,7 +6,6 @@ Configuration options allow users to set a score threshold for further filtering ## Introduction - Efficient Review with Hierarchical Presentation - Given that not all generated code suggestions will be relevant, it is crucial to enable users to review them in a fast and efficient way, allowing quick identification and filtering of non-applicable ones. To achieve this goal, Qodo Merge offers a dedicated hierarchical structure when presenting suggestions to users: @@ -42,9 +41,9 @@ This results in a more refined and valuable set of suggestions for the user, sav ![self_reflection](https://codium.ai/images/pr_agent/self_reflection1.png){width=768} ![self_reflection](https://codium.ai/images/pr_agent/self_reflection2.png){width=768} - ## Appendix - Relevant Configuration Options -``` + +```toml [pr_code_suggestions] -suggestions_score_threshold = 0 # Filter out suggestions with a score below this threshold (0-10) +suggestions_score_threshold = 0 # Filter out suggestions with a score below this threshold (0-10) ``` diff --git a/docs/docs/core-abilities/static_code_analysis.md b/docs/docs/core-abilities/static_code_analysis.md index 2f60da0ef4..10eb265782 100644 --- a/docs/docs/core-abilities/static_code_analysis.md +++ b/docs/docs/core-abilities/static_code_analysis.md @@ -7,14 +7,13 @@ It scans the PR code changes, finds all the code components (methods, functions, !!! note "Language that are currently supported:" Python, Java, C++, JavaScript, TypeScript, C#. - ## Capabilities ### Analyze PR - The [`analyze`](https://qodo-merge-docs.qodo.ai/tools/analyze/) tool enables to interactively generate tests, docs, code suggestions and similar code search for each component that changed in the PR. It can be invoked manually by commenting on any PR: + ``` /analyze ``` @@ -29,9 +28,11 @@ Clicking on each checkbox will trigger the relevant tool for the selected compon The [`test`](https://qodo-merge-docs.qodo.ai/tools/test/) tool generate tests for a selected component, based on the PR code changes. It can be invoked manually by commenting on any PR: + ``` /test component_name ``` + where 'component_name' is the name of a specific component in the PR, Or be triggered interactively by using the `analyze` tool. ![test1](https://codium.ai/images/pr_agent/test1.png){width=768} @@ -40,6 +41,7 @@ where 'component_name' is the name of a specific component in the PR, Or be tri The [`add_docs`](https://qodo-merge-docs.qodo.ai/tools/documentation/) tool scans the PR code changes, and automatically generate docstrings for any code components that changed in the PR. It can be invoked manually by commenting on any PR: + ``` /add_docs component_name ``` @@ -49,8 +51,10 @@ Or be triggered interactively by using the `analyze` tool. ![Docs single component](https://codium.ai/images/pr_agent/docs_single_component.png){width=768} ### Generate Code Suggestions for a Component + The [`improve_component`](https://qodo-merge-docs.qodo.ai/tools/improve_component/) tool generates code suggestions for a specific code component that changed in the PR. It can be invoked manually by commenting on any PR: + ``` /improve_component component_name ``` diff --git a/docs/docs/finetuning_benchmark/index.md b/docs/docs/finetuning_benchmark/index.md index 436db87246..bffaa68d8e 100644 --- a/docs/docs/finetuning_benchmark/index.md +++ b/docs/docs/finetuning_benchmark/index.md @@ -97,5 +97,6 @@ why: | Another application of the Pull Request Benchmark is comparing leading closed-source models to determine which performs better at analyzing pull request code. The evaluation methodology resembles the approach used for evaluating fine-tuned models: + - We ran each model across 200 diverse pull requests, asking them to generate code suggestions using Qodo Merge's `improve` tool -- A third top model served as judge to determine which response better fulfilled the prompt and would likely be perceived as superior by human users \ No newline at end of file +- A third top model served as judge to determine which response better fulfilled the prompt and would likely be perceived as superior by human users diff --git a/docs/docs/index.md b/docs/docs/index.md index 938fab7c47..9e80f42013 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -1,6 +1,6 @@ # Overview -[PR-Agent](https://github.com/Codium-ai/pr-agent) is an open-source tool to help efficiently review and handle pull requests. +[PR-Agent](https://github.com/Codium-ai/pr-agent) is an open-source tool to help efficiently review and handle pull requests. Qodo Merge is a hosted version of PR-Agent, designed for companies and teams that require additional features and capabilities - See the [Installation Guide](./installation/index.md) for instructions on installing and running the tool on different git platforms. @@ -22,77 +22,80 @@ To search the documentation site using natural language: 2) The bot will respond with an [answer](https://github.com/Codium-ai/pr-agent/pull/1241#issuecomment-2365259334) that includes relevant documentation links. - ## Features PR-Agent and Qodo Merge offers extensive pull request functionalities across various git providers: -| | | GitHub | GitLab | Bitbucket | Azure DevOps | -|-------|---------------------------------------------------------------------------------------------------------|:--------------------:|:--------------------:|:---------:|:------------:| -| TOOLS | [Review](https://qodo-merge-docs.qodo.ai/tools/review/) | ✅ | ✅ | ✅ | ✅ | -| | [Describe](https://qodo-merge-docs.qodo.ai/tools/describe/) | ✅ | ✅ | ✅ | ✅ | -| | [Improve](https://qodo-merge-docs.qodo.ai/tools/improve/) | ✅ | ✅ | ✅ | ✅ | -| | [Ask](https://qodo-merge-docs.qodo.ai/tools/ask/) | ✅ | ✅ | ✅ | ✅ | -| | ⮑ [Ask on code lines](https://qodo-merge-docs.qodo.ai/tools/ask/#ask-lines) | ✅ | ✅ | | | -| | [Update CHANGELOG](https://qodo-merge-docs.qodo.ai/tools/update_changelog/) | ✅ | ✅ | ✅ | ✅ | -| | [Help Docs](https://qodo-merge-docs.qodo.ai/tools/help_docs/?h=auto#auto-approval) | ✅ | ✅ | ✅ | | -| | [Ticket Context](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/) 💎 | ✅ | ✅ | ✅ | | -| | [Utilizing Best Practices](https://qodo-merge-docs.qodo.ai/tools/improve/#best-practices) 💎 | ✅ | ✅ | ✅ | | -| | [PR Chat](https://qodo-merge-docs.qodo.ai/chrome-extension/features/#pr-chat) 💎 | ✅ | | | | -| | [Suggestion Tracking](https://qodo-merge-docs.qodo.ai/tools/improve/#suggestion-tracking) 💎 | ✅ | ✅ | | | -| | [CI Feedback](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/) 💎 | ✅ | | | | -| | [PR Documentation](https://qodo-merge-docs.qodo.ai/tools/documentation/) 💎 | ✅ | ✅ | | | -| | [Custom Labels](https://qodo-merge-docs.qodo.ai/tools/custom_labels/) 💎 | ✅ | ✅ | | | -| | [Analyze](https://qodo-merge-docs.qodo.ai/tools/analyze/) 💎 | ✅ | ✅ | | | -| | [Similar Code](https://qodo-merge-docs.qodo.ai/tools/similar_code/) 💎 | ✅ | | | | -| | [Custom Prompt](https://qodo-merge-docs.qodo.ai/tools/custom_prompt/) 💎 | ✅ | ✅ | ✅ | | -| | [Test](https://qodo-merge-docs.qodo.ai/tools/test/) 💎 | ✅ | ✅ | | | -| | [Implement](https://qodo-merge-docs.qodo.ai/tools/implement/) 💎 | ✅ | ✅ | ✅ | | -| | [Scan Repo Discussions](https://qodo-merge-docs.qodo.ai/tools/scan_repo_discussions/) 💎 | ✅ | | | | -| | [Auto-Approve](https://qodo-merge-docs.qodo.ai/tools/improve/?h=auto#auto-approval) 💎 | ✅ | ✅ | ✅ | | -| | | | | | | -| USAGE | [CLI](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#local-repo-cli) | ✅ | ✅ | ✅ | ✅ | -| | [App / webhook](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#github-app) | ✅ | ✅ | ✅ | ✅ | -| | [Tagging bot](https://github.com/Codium-ai/pr-agent#try-it-now) | ✅ | | | | -| | [Actions](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-action) | ✅ |✅| ✅ |✅| -| | | | | | | -| CORE | [PR compression](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) | ✅ | ✅ | ✅ | ✅ | -| | Adaptive and token-aware file patch fitting | ✅ | ✅ | ✅ | ✅ | -| | [Multiple models support](https://qodo-merge-docs.qodo.ai/usage-guide/changing_a_model/) | ✅ | ✅ | ✅ | ✅ | -| | [Local and global metadata](https://qodo-merge-docs.qodo.ai/core-abilities/metadata/) | ✅ | ✅ | ✅ | ✅ | -| | [Dynamic context](https://qodo-merge-docs.qodo.ai/core-abilities/dynamic_context/) | ✅ | ✅ | ✅ | ✅ | -| | [Self reflection](https://qodo-merge-docs.qodo.ai/core-abilities/self_reflection/) | ✅ | ✅ | ✅ | ✅ | -| | [Static code analysis](https://qodo-merge-docs.qodo.ai/core-abilities/static_code_analysis/) 💎 | ✅ | ✅ | | | -| | [Global and wiki configurations](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/) 💎 | ✅ | ✅ | ✅ | | -| | [PR interactive actions](https://www.qodo.ai/images/pr_agent/pr-actions.mp4) 💎 | ✅ | ✅ | | | -| | [Impact Evaluation](https://qodo-merge-docs.qodo.ai/core-abilities/impact_evaluation/) 💎 | ✅ | ✅ | | | +| | | GitHub | GitLab | Bitbucket | Azure DevOps | +| ----- | ------------------------------------------------------------------------------------------------------- |:------:|:------:|:---------:|:------------:| +| TOOLS | [Review](https://qodo-merge-docs.qodo.ai/tools/review/) | ✅ | ✅ | ✅ | ✅ | +| | [Describe](https://qodo-merge-docs.qodo.ai/tools/describe/) | ✅ | ✅ | ✅ | ✅ | +| | [Improve](https://qodo-merge-docs.qodo.ai/tools/improve/) | ✅ | ✅ | ✅ | ✅ | +| | [Ask](https://qodo-merge-docs.qodo.ai/tools/ask/) | ✅ | ✅ | ✅ | ✅ | +| | ⮑ [Ask on code lines](https://qodo-merge-docs.qodo.ai/tools/ask/#ask-lines) | ✅ | ✅ | | | +| | [Update CHANGELOG](https://qodo-merge-docs.qodo.ai/tools/update_changelog/) | ✅ | ✅ | ✅ | ✅ | +| | [Help Docs](https://qodo-merge-docs.qodo.ai/tools/help_docs/?h=auto#auto-approval) | ✅ | ✅ | ✅ | | +| | [Ticket Context](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/) 💎 | ✅ | ✅ | ✅ | | +| | [Utilizing Best Practices](https://qodo-merge-docs.qodo.ai/tools/improve/#best-practices) 💎 | ✅ | ✅ | ✅ | | +| | [PR Chat](https://qodo-merge-docs.qodo.ai/chrome-extension/features/#pr-chat) 💎 | ✅ | | | | +| | [Suggestion Tracking](https://qodo-merge-docs.qodo.ai/tools/improve/#suggestion-tracking) 💎 | ✅ | ✅ | | | +| | [CI Feedback](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/) 💎 | ✅ | | | | +| | [PR Documentation](https://qodo-merge-docs.qodo.ai/tools/documentation/) 💎 | ✅ | ✅ | | | +| | [Custom Labels](https://qodo-merge-docs.qodo.ai/tools/custom_labels/) 💎 | ✅ | ✅ | | | +| | [Analyze](https://qodo-merge-docs.qodo.ai/tools/analyze/) 💎 | ✅ | ✅ | | | +| | [Similar Code](https://qodo-merge-docs.qodo.ai/tools/similar_code/) 💎 | ✅ | | | | +| | [Custom Prompt](https://qodo-merge-docs.qodo.ai/tools/custom_prompt/) 💎 | ✅ | ✅ | ✅ | | +| | [Test](https://qodo-merge-docs.qodo.ai/tools/test/) 💎 | ✅ | ✅ | | | +| | [Implement](https://qodo-merge-docs.qodo.ai/tools/implement/) 💎 | ✅ | ✅ | ✅ | | +| | [Scan Repo Discussions](https://qodo-merge-docs.qodo.ai/tools/scan_repo_discussions/) 💎 | ✅ | | | | +| | [Auto-Approve](https://qodo-merge-docs.qodo.ai/tools/improve/?h=auto#auto-approval) 💎 | ✅ | ✅ | ✅ | | +| | | | | | | +| USAGE | [CLI](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#local-repo-cli) | ✅ | ✅ | ✅ | ✅ | +| | [App / webhook](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#github-app) | ✅ | ✅ | ✅ | ✅ | +| | [Tagging bot](https://github.com/Codium-ai/pr-agent#try-it-now) | ✅ | | | | +| | [Actions](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-action) | ✅ | ✅ | ✅ | ✅ | +| | | | | | | +| CORE | [PR compression](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) | ✅ | ✅ | ✅ | ✅ | +| | Adaptive and token-aware file patch fitting | ✅ | ✅ | ✅ | ✅ | +| | [Multiple models support](https://qodo-merge-docs.qodo.ai/usage-guide/changing_a_model/) | ✅ | ✅ | ✅ | ✅ | +| | [Local and global metadata](https://qodo-merge-docs.qodo.ai/core-abilities/metadata/) | ✅ | ✅ | ✅ | ✅ | +| | [Dynamic context](https://qodo-merge-docs.qodo.ai/core-abilities/dynamic_context/) | ✅ | ✅ | ✅ | ✅ | +| | [Self reflection](https://qodo-merge-docs.qodo.ai/core-abilities/self_reflection/) | ✅ | ✅ | ✅ | ✅ | +| | [Static code analysis](https://qodo-merge-docs.qodo.ai/core-abilities/static_code_analysis/) 💎 | ✅ | ✅ | | | +| | [Global and wiki configurations](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/) 💎 | ✅ | ✅ | ✅ | | +| | [PR interactive actions](https://www.qodo.ai/images/pr_agent/pr-actions.mp4) 💎 | ✅ | ✅ | | | +| | [Impact Evaluation](https://qodo-merge-docs.qodo.ai/core-abilities/impact_evaluation/) 💎 | ✅ | ✅ | | | !!! note "💎 means Qodo Merge only" All along the documentation, 💎 marks a feature available only in [Qodo Merge](https://www.codium.ai/pricing/){:target="_blank"}, and not in the open-source version. - ## Example Results +
#### [/describe](https://github.com/Codium-ai/pr-agent/pull/530) +
![/describe](https://www.codium.ai/images/pr_agent/describe_new_short_main.png){width=512}

#### [/review](https://github.com/Codium-ai/pr-agent/pull/732#issuecomment-1975099151) +
![/review](https://www.codium.ai/images/pr_agent/review_new_short_main.png){width=512}

#### [/improve](https://github.com/Codium-ai/pr-agent/pull/732#issuecomment-1975099159) +
![/improve](https://www.codium.ai/images/pr_agent/improve_new_short_main.png){width=512}

#### [/generate_labels](https://github.com/Codium-ai/pr-agent/pull/530) +
![/generate_labels](https://www.codium.ai/images/pr_agent/geneare_custom_labels_main_short.png){width=300}
diff --git a/docs/docs/installation/azure.md b/docs/docs/installation/azure.md index a4ec632bcc..9586cb5b0b 100644 --- a/docs/docs/installation/azure.md +++ b/docs/docs/installation/azure.md @@ -1,6 +1,8 @@ ## Azure DevOps Pipeline + You can use a pre-built Action Docker image to run PR-Agent as an Azure devops pipeline. add the following file to your repository under `azure-pipelines.yml`: + ```yaml # Opt out of CI triggers trigger: none @@ -49,6 +51,7 @@ stages: openai__key: $(OPENAI_KEY) displayName: 'Run Qodo Merge' ``` + This script will run Qodo Merge on every new merge request, with the `improve`, `review`, and `describe` commands. Note that you need to export the `azure_devops__pat` and `OPENAI_KEY` variables in the Azure DevOps pipeline settings (Pipelines -> Library -> + Variable group): @@ -61,7 +64,8 @@ Make sure to give pipeline permissions to the `pr_agent` variable group. ## Azure DevOps from CLI To use Azure DevOps provider use the following settings in configuration.toml: -``` + +```toml [config] git_provider="azure" ``` @@ -74,7 +78,8 @@ If PAT was chosen, you can assign the value in .secrets.toml. If DefaultAzureCredential was chosen, you can assigned the additional env vars like AZURE_CLIENT_SECRET directly, or use managed identity/az cli (for local development) without any additional configuration. in any case, 'org' value must be assigned in .secrets.toml: -``` + +```toml [azure_devops] org = "https://dev.azure.com/YOUR_ORGANIZATION/" # pat = "YOUR_PAT_TOKEN" needed only if using PAT for authentication @@ -85,11 +90,12 @@ org = "https://dev.azure.com/YOUR_ORGANIZATION/" To trigger from an Azure webhook, you need to manually [add a webhook](https://learn.microsoft.com/en-us/azure/devops/service-hooks/services/webhooks?view=azure-devops). Use the "Pull request created" type to trigger a review, or "Pull request commented on" to trigger any supported comment with / comment on the relevant PR. Note that for the "Pull request commented on" trigger, only API v2.0 is supported. - For webhook security, create a sporadic username/password pair and configure the webhook username and password on both the server and Azure DevOps webhook. These will be sent as basic Auth data by the webhook with each request: -``` + +```toml [azure_devops_server] webhook_username = "" webhook_password = "" ``` + > :warning: **Ensure that the webhook endpoint is only accessible over HTTPS** to mitigate the risk of credential interception when using basic authentication. diff --git a/docs/docs/installation/bitbucket.md b/docs/docs/installation/bitbucket.md index 507e88ae84..c57cd4516b 100644 --- a/docs/docs/installation/bitbucket.md +++ b/docs/docs/installation/bitbucket.md @@ -57,7 +57,7 @@ python cli.py --pr_url https://git.onpreminstanceofbitbucket.com/projects/PROJEC To run PR-Agent as webhook, build the docker image: -``` +```bash docker build . -t codiumai/pr-agent:bitbucket_server_webhook --target bitbucket_server_webhook -f docker/Dockerfile docker push codiumai/pr-agent:bitbucket_server_webhook # Push to your Docker repository ``` diff --git a/docs/docs/installation/github.md b/docs/docs/installation/github.md index 207d6ec84e..018499ee98 100644 --- a/docs/docs/installation/github.md +++ b/docs/docs/installation/github.md @@ -40,6 +40,7 @@ The GITHUB_TOKEN secret is automatically created by GitHub. When you open your next PR, you should see a comment from `github-actions` bot with a review of your PR, and instructions on how to use the rest of the tools. 4) You may configure Qodo Merge by adding environment variables under the env section corresponding to any configurable property in the [configuration](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml) file. Some examples: + ```yaml env: # ... previous environment values @@ -47,9 +48,11 @@ When you open your next PR, you should see a comment from `github-actions` bot w PR_REVIEWER.REQUIRE_TESTS_REVIEW: "false" # Disable tests review PR_CODE_SUGGESTIONS.NUM_CODE_SUGGESTIONS: 6 # Increase number of code suggestions ``` + See detailed usage instructions in the [USAGE GUIDE](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#github-action) ### Using a specific release + !!! tip "" if you want to pin your action to a specific release (v0.23 for example) for stability reasons, use: ```yaml @@ -72,6 +75,7 @@ See detailed usage instructions in the [USAGE GUIDE](https://qodo-merge-docs.qod ``` ### Action for GitHub enterprise server + !!! tip "" To use the action with a GitHub enterprise server, add an environment variable `GITHUB.BASE_URL` with the API URL of your GitHub server. @@ -82,10 +86,10 @@ See detailed usage instructions in the [USAGE GUIDE](https://qodo-merge-docs.qod GITHUB.BASE_URL: "https://github.mycompany.com/api/v3" ``` - --- ## Run as a GitHub App + Allowing you to automate the review process on your private or public repositories. 1) Create a GitHub App from the [Github Developer Portal](https://docs.github.com/en/developers/apps/creating-a-github-app). @@ -102,7 +106,7 @@ Allowing you to automate the review process on your private or public repositori 2) Generate a random secret for your app, and save it for later. For example, you can use: -``` +```bash WEBHOOK_SECRET=$(python -c "import secrets; print(secrets.token_hex(10))") ``` @@ -113,28 +117,29 @@ WEBHOOK_SECRET=$(python -c "import secrets; print(secrets.token_hex(10))") 4) Clone this repository: -``` +```bash git clone https://github.com/Codium-ai/pr-agent.git ``` 5) Copy the secrets template file and fill in the following: -``` +```bash cp pr_agent/settings/.secrets_template.toml pr_agent/settings/.secrets.toml # Edit .secrets.toml file ``` - - Your OpenAI key. - - Copy your app's private key to the private_key field. - - Copy your app's ID to the app_id field. - - Copy your app's webhook secret to the webhook_secret field. - - Set deployment_type to 'app' in [configuration.toml](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml) +- Your OpenAI key. +- Copy your app's private key to the private_key field. +- Copy your app's ID to the app_id field. +- Copy your app's webhook secret to the webhook_secret field. +- Set deployment_type to 'app' in [configuration.toml](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml) > The .secrets.toml file is not copied to the Docker image by default, and is only used for local development. > If you want to use the .secrets.toml file in your Docker image, you can add remove it from the .dockerignore file. > In most production environments, you would inject the secrets file as environment variables or as mounted volumes. > For example, in order to inject a secrets file as a volume in a Kubernetes environment you can update your pod spec to include the following, > assuming you have a secret named `pr-agent-settings` with a key named `.secrets.toml`: + ``` volumes: - name: settings-volume @@ -152,7 +157,7 @@ cp pr_agent/settings/.secrets_template.toml pr_agent/settings/.secrets.toml 6) Build a Docker image for the app and optionally push it to a Docker repository. We'll use Dockerhub as an example: - ``` + ```bash docker build . -t codiumai/pr-agent:github_app --target github_app -f docker/Dockerfile docker push codiumai/pr-agent:github_app # Push to your Docker repository ``` @@ -180,14 +185,19 @@ For example: `GITHUB.WEBHOOK_SECRET` --> `GITHUB__WEBHOOK_SECRET` 1. Follow steps 1-5 from [here](#run-as-a-github-app). 2. Build a docker image that can be used as a lambda function + ```shell docker buildx build --platform=linux/amd64 . -t codiumai/pr-agent:serverless -f docker/Dockerfile.lambda ``` + 3. Push image to ECR + ```shell - docker tag codiumai/pr-agent:serverless .dkr.ecr..amazonaws.com/codiumai/pr-agent:serverless - docker push .dkr.ecr..amazonaws.com/codiumai/pr-agent:serverless + + docker tag codiumai/pr-agent:serverless .dkr.ecr..amazonaws.com/codiumai/pr-agent:serverless + docker push .dkr.ecr..amazonaws.com/codiumai/pr-agent:serverless ``` + 4. Create a lambda function that uses the uploaded image. Set the lambda timeout to be at least 3m. 5. Configure the lambda function to have a Function URL. 6. In the environment variables of the Lambda function, specify `AZURE_DEVOPS_CACHE_DIR` to a writable location such as /tmp. (see [link](https://github.com/Codium-ai/pr-agent/pull/450#issuecomment-1840242269)) @@ -201,28 +211,27 @@ For example: `GITHUB.WEBHOOK_SECRET` --> `GITHUB__WEBHOOK_SECRET` Not all features have been added to CodeCommit yet. As of right now, CodeCommit has been implemented to run the Qodo Merge CLI on the command line, using AWS credentials stored in environment variables. (More features will be added in the future.) The following is a set of instructions to have Qodo Merge do a review of your CodeCommit pull request from the command line: 1. Create an IAM user that you will use to read CodeCommit pull requests and post comments - * Note: That user should have CLI access only, not Console access + - Note: That user should have CLI access only, not Console access 2. Add IAM permissions to that user, to allow access to CodeCommit (see IAM Role example below) 3. Generate an Access Key for your IAM user 4. Set the Access Key and Secret using environment variables (see Access Key example below) 5. Set the `git_provider` value to `codecommit` in the `pr_agent/settings/configuration.toml` settings file 6. Set the `PYTHONPATH` to include your `pr-agent` project directory - * Option A: Add `PYTHONPATH="/PATH/TO/PROJECTS/pr-agent` to your `.env` file - * Option B: Set `PYTHONPATH` and run the CLI in one command, for example: - * `PYTHONPATH="/PATH/TO/PROJECTS/pr-agent python pr_agent/cli.py [--ARGS]` + - Option A: Add `PYTHONPATH="/PATH/TO/PROJECTS/pr-agent` to your `.env` file + - Option B: Set `PYTHONPATH` and run the CLI in one command, for example: + - `PYTHONPATH="/PATH/TO/PROJECTS/pr-agent python pr_agent/cli.py [--ARGS]` --- - #### AWS CodeCommit IAM Role Example Example IAM permissions to that user to allow access to CodeCommit: -* Note: The following is a working example of IAM permissions that has read access to the repositories and write access to allow posting comments -* Note: If you only want pr-agent to review your pull requests, you can tighten the IAM permissions further, however this IAM example will work, and allow the pr-agent to post comments to the PR -* Note: You may want to replace the `"Resource": "*"` with your list of repos, to limit access to only those repos +- Note: The following is a working example of IAM permissions that has read access to the repositories and write access to allow posting comments +- Note: If you only want pr-agent to review your pull requests, you can tighten the IAM permissions further, however this IAM example will work, and allow the pr-agent to post comments to the PR +- Note: You may want to replace the `"Resource": "*"` with your list of repos, to limit access to only those repos -``` +```json { "Version": "2012-10-17", "Statement": [ diff --git a/docs/docs/installation/gitlab.md b/docs/docs/installation/gitlab.md index 998d85831d..bbcf4027de 100644 --- a/docs/docs/installation/gitlab.md +++ b/docs/docs/installation/gitlab.md @@ -1,7 +1,9 @@ ## Run as a GitLab Pipeline + You can use a pre-built Action Docker image to run PR-Agent as a GitLab pipeline. This is a simple way to get started with Qodo Merge without setting up your own server. (1) Add the following file to your repository under `.gitlab-ci.yml`: + ```yaml stages: - pr_agent @@ -26,10 +28,10 @@ pr_agent_job: rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' ``` + This script will run Qodo Merge on every new merge request. You can modify the `rules` section to run Qodo Merge on different events. You can also modify the `script` section to run different Qodo Merge commands, or with different parameters by exporting different environment variables. - (2) Add the following masked variables to your GitLab repository (CI/CD -> Variables): - `GITLAB_PERSONAL_ACCESS_TOKEN`: Your GitLab personal access token. @@ -40,7 +42,6 @@ Note that if your base branches are not protected, don't set the variables as `p > **Note**: The `$CI_SERVER_FQDN` variable is available starting from GitLab version 16.10. If you're using an earlier version, this variable will not be available. However, you can combine `$CI_SERVER_HOST` and `$CI_SERVER_PORT` to achieve the same result. Please ensure you're using a compatible version or adjust your configuration. - ## Run a GitLab webhook server 1. In GitLab create a new user and give it "Reporter" role ("Developer" if using Pro version of the agent) for the intended group or project. @@ -49,41 +50,41 @@ Note that if your base branches are not protected, don't set the variables as `p 3. Generate a random secret for your app, and save it for later (`shared_secret`). For example, you can use: -``` +```bash SHARED_SECRET=$(python -c "import secrets; print(secrets.token_hex(10))") ``` 4. Clone this repository: -``` +```bash git clone https://github.com/qodo-ai/pr-agent.git ``` 5. Prepare variables and secrets. Skip this step if you plan on setting these as environment variables when running the agent: - 1. In the configuration file/variables: +1. In the configuration file/variables: - Set `config.git_provider` to "gitlab" - 2. In the secrets file/variables: +2. In the secrets file/variables: - Set your AI model key in the respective section - In the [gitlab] section, set `personal_access_token` (with token from step 2) and `shared_secret` (with secret from step 3) 6. Build a Docker image for the app and optionally push it to a Docker repository. We'll use Dockerhub as an example: -``` + +```bash docker build . -t gitlab_pr_agent --target gitlab_webhook -f docker/Dockerfile docker push codiumai/pr-agent:gitlab_webhook # Push to your Docker repository ``` 7. Set the environmental variables, the method depends on your docker runtime. Skip this step if you included your secrets/configuration directly in the Docker image. -``` -"CONFIG.GIT_PROVIDER": "gitlab" -"GITLAB.PERSONAL_ACCESS_TOKEN": "" -"GITLAB.SHARED_SECRET": "" -"GITLAB.URL": "https://gitlab.com" -"OPENAI.KEY": "" +```bash +CONFIG__GIT_PROVIDER=gitlab +GITLAB__PERSONAL_ACCESS_TOKEN= +GITLAB__SHARED_SECRET= +GITLAB__URL=https://gitlab.com +OPENAI__KEY= ``` -8. Create a webhook in your GitLab project. Set the URL to ```http[s]:///webhook```, the secret token to the generated secret from step 3, and enable the triggers `push`, `comments` and `merge request events`. +8. Create a webhook in your GitLab project. Set the URL to `http[s]:///webhook`, the secret token to the generated secret from step 3, and enable the triggers `push`, `comments` and `merge request events`. 9. Test your installation by opening a merge request or commenting on a merge request using one of PR Agent's commands. - diff --git a/docs/docs/installation/index.md b/docs/docs/installation/index.md index cf5bf2e4f5..9831078d8c 100644 --- a/docs/docs/installation/index.md +++ b/docs/docs/installation/index.md @@ -11,6 +11,7 @@ There are several ways to use self-hosted PR-Agent: - [Azure DevOps integration](./azure.md) ## Qodo Merge 💎 + Qodo Merge, an app hosted by QodoAI for GitHub\GitLab\BitBucket, is also available.
With Qodo Merge, installation is as simple as adding the Qodo Merge app to your relevant repositories. diff --git a/docs/docs/installation/locally.md b/docs/docs/installation/locally.md index e6a4d78a0c..f2d23dbc84 100644 --- a/docs/docs/installation/locally.md +++ b/docs/docs/installation/locally.md @@ -3,7 +3,6 @@ To run PR-Agent locally, you first need to acquire two keys: 1. An OpenAI key from [here](https://platform.openai.com/api-keys){:target="_blank"}, with access to GPT-4 and o3-mini (or a key for other [language models](https://qodo-merge-docs.qodo.ai/usage-guide/changing_a_model/), if you prefer). 2. A personal access token from your Git platform (GitHub, GitLab, BitBucket) with repo scope. GitHub token, for example, can be issued from [here](https://github.com/settings/tokens){:target="_blank"} - ## Using Docker image A list of the relevant tools can be found in the [tools guide](../tools/). @@ -11,27 +10,33 @@ A list of the relevant tools can be found in the [tools guide](../tools/). To invoke a tool (for example `review`), you can run PR-Agent directly from the Docker image. Here's how: - For GitHub: - ``` + + ```bash docker run --rm -it -e OPENAI.KEY= -e GITHUB.USER_TOKEN= codiumai/pr-agent:latest --pr_url review ``` + If you are using GitHub enterprise server, you need to specify the custom url as variable. For example, if your GitHub server is at `https://github.mycompany.com`, add the following to the command: - ``` + + ```bash -e GITHUB.BASE_URL=https://github.mycompany.com/api/v3 ``` - For GitLab: - ``` + + ```bash docker run --rm -it -e OPENAI.KEY= -e CONFIG.GIT_PROVIDER=gitlab -e GITLAB.PERSONAL_ACCESS_TOKEN= codiumai/pr-agent:latest --pr_url review ``` If you have a dedicated GitLab instance, you need to specify the custom url as variable: - ``` + + ```bash -e GITLAB.URL= ``` - For BitBucket: - ``` + + ```bash docker run --rm -it -e CONFIG.GIT_PROVIDER=bitbucket -e OPENAI.KEY=$OPENAI_API_KEY -e BITBUCKET.BEARER_TOKEN=$BITBUCKET_BEARER_TOKEN codiumai/pr-agent:latest --pr_url= review ``` @@ -46,7 +51,7 @@ The `` refers to a table/section in a configuration file and `= review python3 -m pr_agent.cli --pr_url ask python3 -m pr_agent.cli --pr_url describe @@ -148,6 +152,7 @@ python3 -m pr_agent.cli --issue_url similar_issue ``` [Optional] Add the pr_agent folder to your PYTHONPATH -``` + +```bash export PYTHONPATH=$PYTHONPATH: ``` diff --git a/docs/docs/installation/pr_agent.md b/docs/docs/installation/pr_agent.md index cb6a4092a4..1982b7a120 100644 --- a/docs/docs/installation/pr_agent.md +++ b/docs/docs/installation/pr_agent.md @@ -46,4 +46,4 @@ Configure PR-Agent with Azure DevOps as: - Azure DevOps pipeline job - Local Azure DevOps webhook -[View Azure DevOps Integration Guide →](https://qodo-merge-docs.qodo.ai/installation/azure/) \ No newline at end of file +[View Azure DevOps Integration Guide →](https://qodo-merge-docs.qodo.ai/installation/azure/) diff --git a/docs/docs/installation/qodo_merge.md b/docs/docs/installation/qodo_merge.md index dae48a7907..3bdcba72fa 100644 --- a/docs/docs/installation/qodo_merge.md +++ b/docs/docs/installation/qodo_merge.md @@ -23,7 +23,7 @@ For open-source projects, Qodo Merge is available for free usage. To install Qod ## Install Qodo Merge for Bitbucket -### Bitbucket Cloud +### Bitbucket Cloud Qodo Merge for Bitbucket Cloud is available for installation through the following [link](https://bitbucket.org/site/addons/authorize?addon_key=d6df813252c37258) @@ -33,8 +33,7 @@ Qodo Merge for Bitbucket Cloud is available for installation through the followi To use Qodo Merge application on your private Bitbucket Server, you will need to contact us for starting an [Enterprise](https://www.qodo.ai/pricing/) trial. - -## Install Qodo Merge for GitLab +## Install Qodo Merge for GitLab ### GitLab Cloud diff --git a/docs/docs/overview/data_privacy.md b/docs/docs/overview/data_privacy.md index 4595661f6f..01dea5e73e 100644 --- a/docs/docs/overview/data_privacy.md +++ b/docs/docs/overview/data_privacy.md @@ -10,7 +10,6 @@ - No passive collection of Code and Pull Requests’ data — Qodo Merge will be active only when you invoke it, and it will then extract and analyze only data relevant to the executed command and queried pull request. - ## Qodo Merge Chrome extension - The [Qodo Merge Chrome extension](https://chromewebstore.google.com/detail/pr-agent-chrome-extension/ephlnjeghhogofkifjloamocljapahnl) will not send your code to any external servers. diff --git a/docs/docs/overview/pr_agent_pro.md b/docs/docs/overview/pr_agent_pro.md index 978793ec44..44b4bd7527 100644 --- a/docs/docs/overview/pr_agent_pro.md +++ b/docs/docs/overview/pr_agent_pro.md @@ -17,32 +17,31 @@ Qodo Merge is designed for companies and teams that require additional features Here are some of the additional features and capabilities that Qodo Merge offers: -| Feature | Description | -|----------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [**Model selection**](https://qodo-merge-docs.qodo.ai/usage-guide/PR_agent_pro_models/) | Choose the model that best fits your needs, among top models like `Claude Sonnet` and `o3-mini` -| [**Global and wiki configuration**](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/) | Control configurations for many repositories from a single location;
Edit configuration of a single repo without committing code | -| [**Apply suggestions**](https://qodo-merge-docs.qodo.ai/tools/improve/#overview) | Generate committable code from the relevant suggestions interactively by clicking on a checkbox | -| [**Suggestions impact**](https://qodo-merge-docs.qodo.ai/tools/improve/#assessing-impact) | Automatically mark suggestions that were implemented by the user (either directly in GitHub, or indirectly in the IDE) to enable tracking of the impact of the suggestions | -| [**CI feedback**](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/) | Automatically analyze failed CI checks on GitHub and provide actionable feedback in the PR conversation, helping to resolve issues quickly | -| [**Advanced usage statistics**](https://www.codium.ai/contact/#/) | Qodo Merge offers detailed statistics at user, repository, and company levels, including metrics about Qodo Merge usage, and also general statistics and insights | -| [**Incorporating companies' best practices**](https://qodo-merge-docs.qodo.ai/tools/improve/#best-practices) | Use the companies' best practices as reference to increase the effectiveness and the relevance of the code suggestions | -| [**Interactive triggering**](https://qodo-merge-docs.qodo.ai/tools/analyze/#example-usage) | Interactively apply different tools via the `analyze` command | -| [**Custom labels**](https://qodo-merge-docs.qodo.ai/tools/describe/#handle-custom-labels-from-the-repos-labels-page) | Define custom labels for Qodo Merge to assign to the PR | +| Feature | Description | +| -------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [**Model selection**](https://qodo-merge-docs.qodo.ai/usage-guide/PR_agent_pro_models/) | Choose the model that best fits your needs, among top models like `Claude Sonnet` and `o3-mini` | +| [**Global and wiki configuration**](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/) | Control configurations for many repositories from a single location;
Edit configuration of a single repo without committing code | +| [**Apply suggestions**](https://qodo-merge-docs.qodo.ai/tools/improve/#overview) | Generate committable code from the relevant suggestions interactively by clicking on a checkbox | +| [**Suggestions impact**](https://qodo-merge-docs.qodo.ai/tools/improve/#assessing-impact) | Automatically mark suggestions that were implemented by the user (either directly in GitHub, or indirectly in the IDE) to enable tracking of the impact of the suggestions | +| [**CI feedback**](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/) | Automatically analyze failed CI checks on GitHub and provide actionable feedback in the PR conversation, helping to resolve issues quickly | +| [**Advanced usage statistics**](https://www.codium.ai/contact/#/) | Qodo Merge offers detailed statistics at user, repository, and company levels, including metrics about Qodo Merge usage, and also general statistics and insights | +| [**Incorporating companies' best practices**](https://qodo-merge-docs.qodo.ai/tools/improve/#best-practices) | Use the companies' best practices as reference to increase the effectiveness and the relevance of the code suggestions | +| [**Interactive triggering**](https://qodo-merge-docs.qodo.ai/tools/analyze/#example-usage) | Interactively apply different tools via the `analyze` command | +| [**Custom labels**](https://qodo-merge-docs.qodo.ai/tools/describe/#handle-custom-labels-from-the-repos-labels-page) | Define custom labels for Qodo Merge to assign to the PR | ### Additional tools Here are additional tools that are available only for Qodo Merge users: -| Feature | Description | -|---------------------------------------------------------------------------------------|-------------| -| [**Custom Prompt Suggestions**](https://qodo-merge-docs.qodo.ai/tools/custom_prompt/) | Generate code suggestions based on custom prompts from the user | +| Feature | Description | +| ------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | +| [**Custom Prompt Suggestions**](https://qodo-merge-docs.qodo.ai/tools/custom_prompt/) | Generate code suggestions based on custom prompts from the user | | [**Analyze PR components**](https://qodo-merge-docs.qodo.ai/tools/analyze/) | Identify the components that changed in the PR, and enable to interactively apply different tools to them | -| [**Tests**](https://qodo-merge-docs.qodo.ai/tools/test/) | Generate tests for code components that changed in the PR | -| [**PR documentation**](https://qodo-merge-docs.qodo.ai/tools/documentation/) | Generate docstring for code components that changed in the PR | -| [**Improve Component**](https://qodo-merge-docs.qodo.ai/tools/improve_component/) | Generate code suggestions for code components that changed in the PR | -| [**Similar code search**](https://qodo-merge-docs.qodo.ai/tools/similar_code/) | Search for similar code in the repository, organization, or entire GitHub | -| [**Code implementation**](https://qodo-merge-docs.qodo.ai/tools/implement/) | Generates implementation code from review suggestions | - +| [**Tests**](https://qodo-merge-docs.qodo.ai/tools/test/) | Generate tests for code components that changed in the PR | +| [**PR documentation**](https://qodo-merge-docs.qodo.ai/tools/documentation/) | Generate docstring for code components that changed in the PR | +| [**Improve Component**](https://qodo-merge-docs.qodo.ai/tools/improve_component/) | Generate code suggestions for code components that changed in the PR | +| [**Similar code search**](https://qodo-merge-docs.qodo.ai/tools/similar_code/) | Search for similar code in the repository, organization, or entire GitHub | +| [**Code implementation**](https://qodo-merge-docs.qodo.ai/tools/implement/) | Generates implementation code from review suggestions | ### Supported languages diff --git a/docs/docs/tools/analyze.md b/docs/docs/tools/analyze.md index 189c20af83..217ddec704 100644 --- a/docs/docs/tools/analyze.md +++ b/docs/docs/tools/analyze.md @@ -1,9 +1,11 @@ ## Overview + The `analyze` tool combines advanced static code analysis with LLM capabilities to provide a comprehensive analysis of the PR code changes. The tool scans the PR code changes, finds the code components (methods, functions, classes) that changed, and enables to interactively generate tests, docs, code suggestions and similar code search for each component. It can be invoked manually by commenting on any PR: + ``` /analyze ``` diff --git a/docs/docs/tools/ask.md b/docs/docs/tools/ask.md index 7a5dad6fc8..1562c660ec 100644 --- a/docs/docs/tools/ask.md +++ b/docs/docs/tools/ask.md @@ -2,6 +2,7 @@ The `ask` tool answers questions about the PR, based on the PR code changes. Make sure to be specific and clear in your questions. It can be invoked manually by commenting on any PR: + ``` /ask "..." ``` @@ -15,6 +16,7 @@ It can be invoked manually by commenting on any PR: ## Ask lines You can run `/ask` on specific lines of code in the PR from the PR's diff view. The tool will answer questions based on the code changes in the selected lines. + - Click on the '+' sign next to the line number to select the line. - To select multiple lines, click on the '+' sign of the first line and then hold and drag to select the rest of the lines. - write `/ask "..."` in the comment box and press `Add single comment` button. @@ -28,11 +30,13 @@ Note that the tool does not have "memory" of previous questions, and answers eac You can also ask questions about images that appear in the comment, where the entire PR code will be used as context.
The basic syntax is: + ``` /ask "..." [Image](https://real_link_to_image) ``` + where `https://real_link_to_image` is the direct link to the image. Note that GitHub has a built-in mechanism of pasting images in comments. However, pasted image does not provide a direct link. @@ -55,5 +59,4 @@ To get a direct link to an image, we recommend using the following scheme: ![Ask image5](https://codium.ai/images/pr_agent/ask_images5.png){width=512} - See a full video tutorial [here](https://codium.ai/images/pr_agent/ask_image_video.mov) diff --git a/docs/docs/tools/ci_feedback.md b/docs/docs/tools/ci_feedback.md index 10f024fa4e..af749a1516 100644 --- a/docs/docs/tools/ci_feedback.md +++ b/docs/docs/tools/ci_feedback.md @@ -18,20 +18,24 @@ The tool analyzes the failed checks and provides several feedbacks: ___ In addition to being automatically triggered, the tool can also be invoked manually by commenting on a PR: + ``` /checks "https://github.com/{repo_name}/actions/runs/{run_number}/job/{job_number}" ``` + where `{repo_name}` is the name of the repository, `{run_number}` is the run number of the failed check, and `{job_number}` is the job number of the failed check. ## Disabling the tool from running automatically If you wish to disable the tool from running automatically, you can do so by adding the following configuration to the configuration file: + ``` [checks] enable_auto_checks_feedback = false ``` ## Configuration options + - `enable_auto_checks_feedback` - if set to true, the tool will automatically provide feedback when a check is failed. Default is true. - `excluded_checks_list` - a list of checks to exclude from the feedback, for example: ["check1", "check2"]. Default is an empty list. - `persistent_comment` - if set to true, the tool will overwrite a previous checks comment with the new feedback. Default is true. diff --git a/docs/docs/tools/custom_labels.md b/docs/docs/tools/custom_labels.md index cbc6ba0df6..f22000cee2 100644 --- a/docs/docs/tools/custom_labels.md +++ b/docs/docs/tools/custom_labels.md @@ -1,7 +1,9 @@ ## Overview + The `generate_labels` tool scans the PR code changes, and given a list of labels and their descriptions, it automatically suggests labels that match the PR code changes. It can be invoked manually by commenting on any PR: + ``` /generate_labels ``` @@ -19,21 +21,26 @@ When running the `generate_labels` tool on a PR that includes changes in SQL que Note that in addition to the dedicated tool `generate_labels`, the custom labels will also be used by the `describe` tool. ### How to enable custom labels + There are 3 ways to enable custom labels: #### 1. CLI (local configuration file) + When working from CLI, you need to apply the [configuration changes](#configuration-options) to the [custom_labels file](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/custom_labels.toml): #### 2. Repo configuration file + To enable custom labels, you need to apply the [configuration changes](#configuration-options) to the local `.pr_agent.toml` file in your repository. #### 3. Handle custom labels from the Repo's labels page 💎 + > This feature is available only in Qodo Merge * GitHub : `https://github.com/{owner}/{repo}/labels`, or click on the "Labels" tab in the issues or PRs page. * GitLab : `https://gitlab.com/{owner}/{repo}/-/labels`, or click on "Manage" -> "Labels" on the left menu. b. Add/edit the custom labels. It should be formatted as follows: + * Label name: The name of the custom label. * Description: Start the description of with prefix `pr_agent:`, for example: `pr_agent: Description of when AI should suggest this label`.
The description should be comprehensive and detailed, indicating when to add the desired label. @@ -45,8 +52,9 @@ c. Now the custom labels will be included in the `generate_labels` tool. > This feature is supported in GitHub and GitLab. ## Configuration options - - Change `enable_custom_labels` to True: This will turn off the default labels and enable the custom labels provided in the custom_labels.toml file. - - Add the custom labels. It should be formatted as follows: + +* Change `enable_custom_labels` to True: This will turn off the default labels and enable the custom labels provided in the custom_labels.toml file. +* Add the custom labels. It should be formatted as follows: ``` [config] diff --git a/docs/docs/tools/custom_prompt.md b/docs/docs/tools/custom_prompt.md index 31823c8e25..5022d593e6 100644 --- a/docs/docs/tools/custom_prompt.md +++ b/docs/docs/tools/custom_prompt.md @@ -1,4 +1,5 @@ ## Overview + The `custom_prompt` tool scans the PR code changes, and automatically generates suggestions for improving the PR code. It shares similarities with the `improve` tool, but with one main difference: the `custom_prompt` tool will **only propose suggestions that follow specific guidelines defined by the prompt** in: `pr_custom_prompt.prompt` configuration. @@ -17,7 +18,7 @@ The code suggestions should focus only on the following: With a [configuration file](../usage-guide/automations_and_usage.md#github-app), use the following template: -``` +```toml [pr_custom_prompt] prompt="""\ The suggestions should focus only on the following: @@ -33,7 +34,8 @@ You might benefit from several trial-and-error iterations, until you get the cor ## Example usage Here is an example of a possible prompt, defined in the configuration file: -``` + +```toml [pr_custom_prompt] prompt="""\ The code suggestions should focus only on the following: diff --git a/docs/docs/tools/describe.md b/docs/docs/tools/describe.md index 0980923daf..8dce7646fb 100644 --- a/docs/docs/tools/describe.md +++ b/docs/docs/tools/describe.md @@ -1,7 +1,9 @@ ## Overview + The `describe` tool scans the PR code changes, and generates a description for the PR - title, type, summary, walkthrough and labels. The tool can be triggered automatically every time a new PR is [opened](../usage-guide/automations_and_usage.md#github-app-automatic-tools-when-a-new-pr-is-opened), or it can be invoked manually by commenting on any PR: + ``` /describe ``` @@ -19,6 +21,7 @@ After ~30 seconds, the tool will generate a description for the PR: ![Describe New](https://codium.ai/images/pr_agent/describe_new.png){width=512} If you want to edit [configurations](#configuration-options), add the relevant ones to the command: + ``` /describe --pr_description.some_config1=... --pr_description.some_config2=... ``` @@ -26,6 +29,7 @@ If you want to edit [configurations](#configuration-options), add the relevant o ### Automatic triggering To run the `describe` automatically when a PR is opened, define in a [configuration file](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/#wiki-configuration-file): + ``` [github_app] pr_commands = [ @@ -41,7 +45,6 @@ publish_labels = true - The `pr_commands` lists commands that will be executed automatically when a PR is opened. - The `[pr_description]` section contains the configurations for the `describe` tool you want to edit (if any). - ## Configuration options !!! example "Possible configurations" @@ -97,7 +100,6 @@ publish_labels = true
- ## Inline file summary 💎 This feature enables you to copy the `changes walkthrough` table to the "Files changed" tab, so you can quickly understand the changes in each file while reviewing the code changes (diff view). @@ -120,13 +122,13 @@ If you prefer to have the file summaries appear in the "Files changed" tab on ev **Note**: that this feature is currently available only for GitHub. - ## Markers template To enable markers, set `pr_description.use_description_markers=true`. Markers enable to easily integrate user's content and auto-generated content, with a template-like mechanism. For example, if the PR original description was: + ``` User content... @@ -139,6 +141,7 @@ pr_agent:summary ## PR Walkthrough: pr_agent:walkthrough ``` + The marker `pr_agent:type` will be replaced with the PR type, `pr_agent:summary` will be replaced with the PR summary, and `pr_agent:walkthrough` will be replaced with the PR walkthrough. ![Describe markers before](https://codium.ai/images/pr_agent/describe_markers_before.png){width=512} @@ -147,13 +150,13 @@ becomes ![Describe markers after](https://codium.ai/images/pr_agent/describe_markers_after.png){width=512} - **Configuration params**: - `use_description_markers`: if set to true, the tool will use markers template. It replaces every marker of the form `pr_agent:marker_name` with the relevant content. Default is false. - `include_generated_by_header`: if set to true, the tool will add a dedicated header: 'Generated by PR Agent at ...' to any automatic content. Default is true. ## Custom labels + The default labels of the describe tool are quite generic, since they are meant to be used in any repo: [`Bug fix`, `Tests`, `Enhancement`, `Documentation`, `Other`]. You can define custom labels that are relevant for your repo and use cases. @@ -163,7 +166,9 @@ Make sure to provide proper title, and a detailed and well-phrased description f Each label description should be a **conditional statement**, that indicates if to add the label to the PR or not, according to the PR content. ### Handle custom labels from a configuration file + Example for a custom labels configuration setup in a configuration file: + ``` [config] enable_custom_labels=true @@ -182,26 +187,25 @@ description = "use when a PR primarily contains new tests" You can also control the custom labels that will be suggested by the `describe` tool from the repo's labels page: -* GitHub : go to `https://github.com/{owner}/{repo}/labels` (or click on the "Labels" tab in the issues or PRs page) -* GitLab : go to `https://gitlab.com/{owner}/{repo}/-/labels` (or click on "Manage" -> "Labels" on the left menu) +- GitHub : go to `https://github.com/{owner}/{repo}/labels` (or click on the "Labels" tab in the issues or PRs page) +- GitLab : go to `https://gitlab.com/{owner}/{repo}/-/labels` (or click on "Manage" -> "Labels" on the left menu) Now add/edit the custom labels. they should be formatted as follows: -* Label name: The name of the custom label. -* Description: Start the description of with prefix `pr_agent:`, for example: `pr_agent: Description of when AI should suggest this label`.
+- Label name: The name of the custom label. +- Description: Start the description of with prefix `pr_agent:`, for example: `pr_agent: Description of when AI should suggest this label`.
Examples for custom labels: - - `Main topic:performance` - pr_agent:The main topic of this PR is performance - - `New endpoint` - pr_agent:A new endpoint was added in this PR - - `SQL query` - pr_agent:A new SQL query was added in this PR - - `Dockerfile changes` - pr_agent:The PR contains changes in the Dockerfile - - ... +- `Main topic:performance` - pr_agent:The main topic of this PR is performance +- `New endpoint` - pr_agent:A new endpoint was added in this PR +- `SQL query` - pr_agent:A new SQL query was added in this PR +- `Dockerfile changes` - pr_agent:The PR contains changes in the Dockerfile +- ... The description should be comprehensive and detailed, indicating when to add the desired label. For example: ![Add native custom labels](https://codium.ai/images/pr_agent/add_native_custom_labels.png){width=768} - ## Usage Tips !!! tip "Automation" @@ -211,14 +215,15 @@ The description should be comprehensive and detailed, indicating when to add the ``` 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: ``` pr_commands = ["/describe --pr_description.use_description_markers=true", ...] ``` + the tool will replace every marker of the form `pr_agent:marker_name` in the PR description with the relevant content, where `marker_name` is one of the following: - * `type`: the PR type. + *`type`: the PR type. * `summary`: the PR summary. * `walkthrough`: the PR walkthrough. - - Note that when markers are enabled, if the original PR description does not contain any markers, the tool will not alter the description at all. +- Note that when markers are enabled, if the original PR description does not contain any markers, the tool will not alter the description at all. diff --git a/docs/docs/tools/documentation.md b/docs/docs/tools/documentation.md index b09eda3e85..247d5d6dd1 100644 --- a/docs/docs/tools/documentation.md +++ b/docs/docs/tools/documentation.md @@ -1,7 +1,9 @@ ## Overview + The `add_docs` tool scans the PR code changes, and automatically suggests documentation for any code components that changed in the PR (functions, classes, etc.). It can be invoked manually by commenting on any PR: + ``` /add_docs ``` @@ -19,13 +21,15 @@ The tool will generate documentation for all the components that changed in the ![Docs single component](https://codium.ai/images/pr_agent/docs_single_component.png){width=768} You can state a name of a specific component in the PR to get documentation only for that component: + ``` /add_docs component_name ``` ## Configuration options - - `docs_style`: The exact style of the documentation (for python docstring). you can choose between: `google`, `numpy`, `sphinx`, `restructuredtext`, `plain`. Default is `sphinx`. - - `extra_instructions`: Optional extra instructions to the tool. For example: "focus on the changes in the file X. Ignore change in ...". + +- `docs_style`: The exact style of the documentation (for python docstring). you can choose between: `google`, `numpy`, `sphinx`, `restructuredtext`, `plain`. Default is `sphinx`. +- `extra_instructions`: Optional extra instructions to the tool. For example: "focus on the changes in the file X. Ignore change in ...". !!! note "Notes" - The following languages are currently supported: Python, Java, C++, JavaScript, TypeScript, C#. diff --git a/docs/docs/tools/help.md b/docs/docs/tools/help.md index aaf409f641..a9a458e859 100644 --- a/docs/docs/tools/help.md +++ b/docs/docs/tools/help.md @@ -1,13 +1,16 @@ ## Overview + The `help` tool provides a list of all the available tools and their descriptions. For Qodo Merge users, it also enables to trigger each tool by checking the relevant box. It can be invoked manually by commenting on any PR: + ``` /help ``` ## Example usage + An example [result](https://github.com/Codium-ai/pr-agent/pull/546#issuecomment-1868524805): ![Help 1](https://codium.ai/images/pr_agent/help1.png){width=750} diff --git a/docs/docs/tools/help_docs.md b/docs/docs/tools/help_docs.md index 4ce24a94f3..308edc1d33 100644 --- a/docs/docs/tools/help_docs.md +++ b/docs/docs/tools/help_docs.md @@ -3,9 +3,11 @@ The `help_docs` tool can answer a free-text question based on a git documentation folder. It can be invoked manually by commenting on any PR or Issue: + ``` /help_docs "..." ``` + Or configured to be triggered automatically when a [new issue is opened](#run-as-a-github-action). The tool assumes by default that the documentation is located in the root of the repository, at `/docs` folder. @@ -92,8 +94,8 @@ jobs: 3) Following completion of the remaining steps (such as adding secrets and relevant configurations, such as `repo_url` and `docs_path`) merge this change to your main branch. When a new issue is opened, you should see a comment from `github-actions` bot with an auto response, assuming the question is related to the documentation of the repository. ---- +--- ## Configuration options diff --git a/docs/docs/tools/implement.md b/docs/docs/tools/implement.md index 4151fde75f..9340142503 100644 --- a/docs/docs/tools/implement.md +++ b/docs/docs/tools/implement.md @@ -7,28 +7,31 @@ It leverages LLM technology to transform PR comments and review suggestions into ## Usage Scenarios - ### For Reviewers Reviewers can request code changes by: 1. Selecting the code block to be modified. -2. Adding a comment with the syntax: +2. Adding a comment with the syntax: + ``` /implement ``` ![implement1](https://codium.ai/images/pr_agent/implement1.png){width=640} - ### For PR Authors PR authors can implement suggested changes by replying to a review comment using either:
+ 1. Add specific implementation details as described above + ``` /implement ``` + 2. Use the original review comment as instructions + ``` /implement ``` @@ -38,6 +41,7 @@ PR authors can implement suggested changes by replying to a review comment using ### For Referencing Comments You can reference and implement changes from any comment by: + ``` /implement ``` @@ -46,7 +50,6 @@ You can reference and implement changes from any comment by: Note that the implementation will occur within the review discussion thread. - **Configuration options** - Use `/implement` to implement code change within and based on the review discussion. diff --git a/docs/docs/tools/improve.md b/docs/docs/tools/improve.md index ed361865f7..ceb4dee3e4 100644 --- a/docs/docs/tools/improve.md +++ b/docs/docs/tools/improve.md @@ -1,6 +1,8 @@ ## Overview + The `improve` tool scans the PR code changes, and automatically generates [meaningful](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/pr_code_suggestions_prompts.toml#L41) suggestions for improving the PR code. The tool can be triggered automatically every time a new PR is [opened](../usage-guide/automations_and_usage.md#github-app-automatic-tools-when-a-new-pr-is-opened), or it can be invoked manually by commenting on any PR: + ```toml /improve ``` @@ -20,24 +22,26 @@ The tool can be triggered automatically every time a new PR is [opened](../usage Invoke the tool manually by commenting `/improve` on any PR. The code suggestions by default are presented as a single comment: To edit [configurations](#configuration-options) related to the improve tool, use the following template: + ```toml /improve --pr_code_suggestions.some_config1=... --pr_code_suggestions.some_config2=... ``` For example, you can choose to present all the suggestions as commitable code comments, by running the following command: + ```toml /improve --pr_code_suggestions.commitable_code_suggestions=true ``` ![improve](https://codium.ai/images/pr_agent/improve.png){width=512} - As can be seen, a single table comment has a significantly smaller PR footprint. We recommend this mode for most cases. Also note that collapsible are not supported in _Bitbucket_. Hence, the suggestions can only be presented in Bitbucket as code comments. ### Automatic triggering To run the `improve` automatically when a PR is opened, define in a [configuration file](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/#wiki-configuration-file): + ```toml [github_app] pr_commands = [ @@ -54,6 +58,7 @@ num_code_suggestions_per_chunk = ... - The `[pr_code_suggestions]` section contains the configurations for the `improve` tool you want to edit (if any) ### Assessing Impact + >`💎 feature` Qodo Merge tracks two types of implementations for tracking implemented suggestions: @@ -70,6 +75,7 @@ In post-process, Qodo Merge counts the number of suggestions that were implement ![code_suggestions_asses_impact_stats_2](https://codium.ai/images/pr_agent/code_suggestions_asses_impact_stats_2.png){width=512} ## Suggestion tracking + >`💎 feature. Platforms supported: GitHub, GitLab` Qodo Merge employs a novel detection system to automatically [identify](https://qodo-merge-docs.qodo.ai/core-abilities/impact_evaluation/) AI code suggestions that PR authors have accepted and implemented. @@ -107,6 +113,7 @@ You can use the `extra_instructions` configuration option to give the AI model a Be specific, clear, and concise in the instructions. With extra instructions, you are the prompter. Examples for possible instructions: + ```toml [pr_code_suggestions] extra_instructions="""\ @@ -116,6 +123,7 @@ extra_instructions="""\ ... """ ``` + Use triple quotes to write multi-line instructions. Use bullet points or numbers to make the instructions more readable. ### Best practices @@ -129,6 +137,7 @@ The AI model will use this `best_practices.md` file as a reference, and in case best practice`. Example for a python `best_practices.md` content: + ```markdown ## Project best practices - Make sure that I/O operations are encapsulated in a try-except block @@ -145,10 +154,11 @@ Tips for writing an effective `best_practices.md` file: - Include brief code examples when helpful - Focus on project-specific guidelines, that will result in relevant suggestions you actually want to get - Keep the file relatively short, under 800 lines, since: - - AI models may not process effectively very long documents - - Long files tend to contain generic guidelines already known to AI + - AI models may not process effectively very long documents + - Long files tend to contain generic guidelines already known to AI #### Local and global best practices + By default, Qodo Merge will look for a local `best_practices.md` in the root of the relevant local repo. If you want to enable also a global `best_practices.md` file, set first in the global configuration file: @@ -161,7 +171,8 @@ enable_global_best_practices = true Then, create a `best_practices.md` file in the root of [global](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/#global-configuration-file) configuration repository, `pr-agent-settings`. #### Best practices for multiple languages -For a git organization working with multiple programming languages, you can maintain a centralized global `best_practices.md` file containing language-specific guidelines. + +For a git organization working with multiple programming languages, you can maintain a centralized global `best_practices.md` file containing language-specific guidelines. When reviewing pull requests, Qodo Merge automatically identifies the programming language and applies the relevant best practices from this file. To do this, structure your `best_practices.md` file using the following format: @@ -176,7 +187,8 @@ To do this, structure your `best_practices.md` file using the following format: ``` #### Dedicated label for best practices suggestions -Best practice suggestions are labeled as `Organization best practice` by default. + +Best practice suggestions are labeled as `Organization best practice` by default. To customize this label, modify it in your configuration file: ```toml @@ -186,7 +198,6 @@ organization_name = "..." And the label will be: `{organization_name} best practice`. - #### Example results ![best_practice](https://codium.ai/images/pr_agent/org_best_practice.png){width=512} @@ -201,7 +212,7 @@ And the label will be: `{organization_name} best practice`. 2. **Automatically** generates [best practices page](https://github.com/qodo-ai/pr-agent/wiki/.pr_agent_auto_best_practices) based on what your team consistently values 3. Applies these learned patterns to future code reviews -This creates an automatic feedback loop where the system continuously learns from your team's choices to provide increasingly relevant suggestions. +This creates an automatic feedback loop where the system continuously learns from your team's choices to provide increasingly relevant suggestions. The system maintains two analysis phases: - Open exploration for new issues @@ -227,8 +238,8 @@ extra_instructions = "" max_patterns = 5 ``` - ### Combining 'extra instructions' and 'best practices' + > `💎 feature` The `extra instructions` configuration is more related to the `improve` tool prompt. It can be used, for example, to avoid specific suggestions ("Don't suggest to add try-except block", "Ignore changes in toml files", ...) or to emphasize specific aspects or formats ("Answer in Japanese", "Give only short suggestions", ...) @@ -237,10 +248,10 @@ In contrast, the `best_practices.md` file is a general guideline for the way cod Using a combination of both can help the AI model to provide relevant and tailored suggestions. - ## Usage Tips ### Implementing the proposed code suggestions + Each generated suggestion consists of three key elements: 1. A single-line summary of the proposed change @@ -252,6 +263,7 @@ In addition to mistakes (which may happen, but are rare), sometimes the presente In such cases, we recommend prioritizing the suggestion's detailed description, using the diff snippet primarily as a supporting reference. ### Dual publishing mode + Our recommended approach for presenting code suggestions is through a [table](https://qodo-merge-docs.qodo.ai/tools/improve/#overview) (`--pr_code_suggestions.commitable_code_suggestions=false`). This method significantly reduces the PR footprint and allows for quick and easy digestion of multiple suggestions. @@ -268,9 +280,11 @@ dual_publishing_score_threshold = x Where x represents the minimum score threshold (>=) for suggestions to be presented as commitable PR comments in addition to the table. Default is -1 (disabled). ### Self-review + > `💎 feature` Platforms supported: GitHub, GitLab If you set in a configuration file: + ```toml [pr_code_suggestions] demand_code_suggestions_self_review = true @@ -278,6 +292,7 @@ demand_code_suggestions_self_review = true The `improve` tool will add a checkbox below the suggestions, prompting user to acknowledge that they have reviewed the suggestions. You can set the content of the checkbox text via: + ```toml [pr_code_suggestions] code_suggestions_self_review_text = "... (your text here) ..." @@ -285,7 +300,6 @@ code_suggestions_self_review_text = "... (your text here) ..." ![self_review_1](https://codium.ai/images/pr_agent/self_review_1.png){width=512} - !!! tip "Tip - Reducing visual footprint after self-review 💎" The configuration parameter `pr_code_suggestions.fold_suggestions_on_self_review` (default is True) @@ -293,8 +307,6 @@ code_suggestions_self_review_text = "... (your text here) ..." This reduces the visual footprint of the suggestions, and also indicates to the PR reviewer that the suggestions have been reviewed by the PR author, and don't require further attention. - - !!! tip "Tip - Demanding self-review from the PR author 💎" By setting: @@ -314,12 +326,14 @@ code_suggestions_self_review_text = "... (your text here) ..." To prevent unauthorized approvals, this configuration defaults to false, and cannot be altered through online comments; enabling requires a direct update to the configuration file and a commit to the repository. This ensures that utilizing the feature demands a deliberate documented decision by the repository owner. ### Auto-approval + > `💎 feature. Platforms supported: GitHub, GitLab, Bitbucket` Under specific conditions, Qodo Merge can auto-approve a PR when a specific comment is invoked, or when the PR meets certain criteria. **To ensure safety, the auto-approval feature is disabled by default.** To enable auto-approval features, you need to actively set one or both of the following options in a pre-defined _configuration file_: + ```toml [config] enable_comment_approval = true # For approval via comments @@ -333,20 +347,24 @@ enable_auto_approval = true # For criteria-based auto-approval 1\. **Auto-approval by commenting** To enable auto-approval by commenting, set in the configuration file: + ```toml [config] enable_comment_approval = true ``` After enabling, by commenting on a PR: + ``` /review auto_approve ``` + Qodo Merge will automatically approve the PR, and add a comment with the approval. 2\. **Auto-approval when the PR meets certain criteria** To enable auto-approval based on specific criteria, first, you need to enable the top-level flag: + ```toml [config] enable_auto_approval = true @@ -355,30 +373,38 @@ enable_auto_approval = true There are two criteria that can be set for auto-approval: - **Review effort score** + ```toml [config] enable_auto_approval = true auto_approve_for_low_review_effort = X # X is a number between 1 to 5 ``` + When the [review effort score](https://www.qodo.ai/images/pr_agent/review3.png) is lower or equal to X, the PR will be auto-approved. ___ + - **No code suggestions** + ```toml [config] enable_auto_approval = true auto_approve_for_no_suggestions = true ``` + When no [code suggestion](https://www.qodo.ai/images/pr_agent/code_suggestions_as_comment_closed.png) were found for the PR, the PR will be auto-approved. ### How many code suggestions are generated? + Qodo Merge uses a dynamic strategy to generate code suggestions based on the size of the pull request (PR). Here's how it works: #### 1. Chunking large PRs + - Qodo Merge divides large PRs into 'chunks'. - Each chunk contains up to `pr_code_suggestions.max_context_tokens` tokens (default: 14,000). #### 2. Generating suggestions + - For each chunk, Qodo Merge generates up to `pr_code_suggestions.num_code_suggestions_per_chunk` suggestions (default: 3). This approach has two main benefits: @@ -388,7 +414,6 @@ This approach has two main benefits: Note: Chunking is primarily relevant for large PRs. For most PRs (up to 500 lines of code), Qodo Merge will be able to process the entire code in a single call. - ## Configuration options ??? example "General options" @@ -468,8 +493,8 @@ Note: Chunking is primarily relevant for large PRs. For most PRs (up to 500 line - **AI Limitations:** AI models for code are getting better and better, but they are not flawless. Not all the suggestions will be perfect, and a user should not accept all of them automatically. Critical reading and judgment are required. Mistakes of the AI are rare but can happen, and it is usually quite easy for a human to spot them. - **Purpose of Suggestions:** - - **Self-reflection:** The suggestions aim to enable developers to _self-reflect_ and improve their pull requests. This process can help to identify blind spots, uncover missed edge cases, and enhance code readability and coherency. Even when a specific code suggestion isn't suitable, the underlying issue it highlights often reveals something important that might deserve attention. - - **Bug detection:** The suggestions also alert on any _critical bugs_ that may have been identified during the analysis. This provides an additional safety net to catch potential issues before they make it into production. It's perfectly acceptable to implement only the suggestions you find valuable for your specific context. + - **Self-reflection:** The suggestions aim to enable developers to _self-reflect_ and improve their pull requests. This process can help to identify blind spots, uncover missed edge cases, and enhance code readability and coherency. Even when a specific code suggestion isn't suitable, the underlying issue it highlights often reveals something important that might deserve attention. + - **Bug detection:** The suggestions also alert on any _critical bugs_ that may have been identified during the analysis. This provides an additional safety net to catch potential issues before they make it into production. It's perfectly acceptable to implement only the suggestions you find valuable for your specific context. - **Hierarchy:** Presenting the suggestions in a structured hierarchical table enables the user to _quickly_ understand them, and to decide which ones are relevant and which are not. - **Customization:** To guide the model to suggestions that are more relevant to the specific needs of your project, we recommend to use the [`extra_instructions`](https://qodo-merge-docs.qodo.ai/tools/improve/#extra-instructions-and-best-practices) and [`best practices`](https://qodo-merge-docs.qodo.ai/tools/improve/#best-practices) fields. - **Interactive usage:** The interactive [PR chat](https://qodo-merge-docs.qodo.ai/chrome-extension/) also provides an easy way to get more tailored suggestions and feedback from the AI model. diff --git a/docs/docs/tools/improve_component.md b/docs/docs/tools/improve_component.md index da79c65c05..6b93b621b7 100644 --- a/docs/docs/tools/improve_component.md +++ b/docs/docs/tools/improve_component.md @@ -1,13 +1,14 @@ ## Overview + The `improve_component` tool generates code suggestions for a specific code component that changed in the PR. it can be invoked manually by commenting on any PR: + ``` /improve_component component_name ``` To get a list of the components that changed in the PR and choose the relevant component interactively, use the [`analyze`](./analyze.md) tool. - ## Example usage Invoke the tool manually by commenting `/improve_component` on any PR: @@ -23,6 +24,7 @@ The tool will generate code suggestions for the selected component (if no compon - This tool can also be triggered interactively by using the [`analyze`](./analyze.md) tool. ## Configuration options + - `num_code_suggestions`: number of code suggestions to provide. Default is 4 - `extra_instructions`: Optional extra instructions to the tool. For example: "focus on ...". - `file`: in case there are several components with the same name, you can specify the relevant file. diff --git a/docs/docs/tools/index.md b/docs/docs/tools/index.md index a447a7feb1..e422b856d0 100644 --- a/docs/docs/tools/index.md +++ b/docs/docs/tools/index.md @@ -2,22 +2,23 @@ Here is a list of Qodo Merge tools, each with a dedicated page that explains how to use it: -| Tool | Description | -|------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------| -| **[PR Description (`/describe`](./describe.md))** | Automatically generating PR description - title, type, summary, code walkthrough and labels | -| **[PR Review (`/review`](./review.md))** | Adjustable feedback about the PR, possible issues, security concerns, review effort and more | -| **[Code Suggestions (`/improve`](./improve.md))** | Code suggestions for improving the PR | -| **[Question Answering (`/ask ...`](./ask.md))** | Answering free-text questions about the PR, or on specific code lines | -| **[Update Changelog (`/update_changelog`](./update_changelog.md))** | Automatically updating the CHANGELOG.md file with the PR changes | -| **[Help (`/help`](./help.md))** | Provides a list of all the available tools. Also enables to trigger them interactively (💎) | -| **💎 [Add Documentation (`/add_docs`](./documentation.md))** | Generates documentation to methods/functions/classes that changed in the PR | -| **💎 [Generate Custom Labels (`/generate_labels`](./custom_labels.md))** | Generates custom labels for the PR, based on specific guidelines defined by the user | -| **💎 [Analyze (`/analyze`](./analyze.md))** | Identify code components that changed in the PR, and enables to interactively generate tests, docs, and code suggestions for each component| -| **💎 [Test (`/test`](./test.md))** | generate tests for a selected component, based on the PR code changes | -| **💎 [Custom Prompt (`/custom_prompt`](./custom_prompt.md))** | Automatically generates custom suggestions for improving the PR code, based on specific guidelines defined by the user | -| **💎 [Generate Tests (`/test component_name`](./test.md))** | Automatically generates unit tests for a selected component, based on the PR code changes | -| **💎 [Improve Component (`/improve_component component_name`](./improve_component.md))** | Generates code suggestions for a specific code component that changed in the PR | -| **💎 [CI Feedback (`/checks ci_job`](./ci_feedback.md))** | Automatically generates feedback and analysis for a failed CI job | -| **💎 [Implement (`/implement`](./implement.md))** | Generates implementation code from review suggestions | -| **💎 [Scan Repo Discussions (`/scan_repo_discussions`](./scan_repo_discussions.md))** | Generates `best_practices.md` file based on previous discussions in the repository | +| Tool | Description | +| ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | +| **[PR Description (`/describe`](./describe.md))** | Automatically generating PR description - title, type, summary, code walkthrough and labels | +| **[PR Review (`/review`](./review.md))** | Adjustable feedback about the PR, possible issues, security concerns, review effort and more | +| **[Code Suggestions (`/improve`](./improve.md))** | Code suggestions for improving the PR | +| **[Question Answering (`/ask ...`](./ask.md))** | Answering free-text questions about the PR, or on specific code lines | +| **[Update Changelog (`/update_changelog`](./update_changelog.md))** | Automatically updating the CHANGELOG.md file with the PR changes | +| **[Help (`/help`](./help.md))** | Provides a list of all the available tools. Also enables to trigger them interactively (💎) | +| **💎 [Add Documentation (`/add_docs`](./documentation.md))** | Generates documentation to methods/functions/classes that changed in the PR | +| **💎 [Generate Custom Labels (`/generate_labels`](./custom_labels.md))** | Generates custom labels for the PR, based on specific guidelines defined by the user | +| **💎 [Analyze (`/analyze`](./analyze.md))** | Identify code components that changed in the PR, and enables to interactively generate tests, docs, and code suggestions for each component | +| **💎 [Test (`/test`](./test.md))** | generate tests for a selected component, based on the PR code changes | +| **💎 [Custom Prompt (`/custom_prompt`](./custom_prompt.md))** | Automatically generates custom suggestions for improving the PR code, based on specific guidelines defined by the user | +| **💎 [Generate Tests (`/test component_name`](./test.md))** | Automatically generates unit tests for a selected component, based on the PR code changes | +| **💎 [Improve Component (`/improve_component component_name`](./improve_component.md))** | Generates code suggestions for a specific code component that changed in the PR | +| **💎 [CI Feedback (`/checks ci_job`](./ci_feedback.md))** | Automatically generates feedback and analysis for a failed CI job | +| **💎 [Implement (`/implement`](./implement.md))** | Generates implementation code from review suggestions | +| **💎 [Scan Repo Discussions (`/scan_repo_discussions`](./scan_repo_discussions.md))** | Generates `best_practices.md` file based on previous discussions in the repository | + Note that the tools marked with 💎 are available only for Qodo Merge users. diff --git a/docs/docs/tools/review.md b/docs/docs/tools/review.md index 37dfcc8ec9..e5cc6b93ea 100644 --- a/docs/docs/tools/review.md +++ b/docs/docs/tools/review.md @@ -1,7 +1,9 @@ ## Overview + The `review` tool scans the PR code changes, and generates a list of feedbacks about the PR, aiming to aid the reviewing process.
The tool can be triggered automatically every time a new PR is [opened](../usage-guide/automations_and_usage.md#github-app-automatic-tools-when-a-new-pr-is-opened), or can be invoked manually by commenting on any PR: + ``` /review ``` @@ -10,7 +12,6 @@ Note that the main purpose of the `review` tool is to provide the **PR reviewer* (Read more about the different personas in the PR process and how Qodo Merge aims to assist them in our [blog](https://www.codium.ai/blog/understanding-the-challenges-and-pain-points-of-the-pull-request-cycle/)) - ## Example usage ### Manual triggering @@ -24,6 +25,7 @@ After ~30 seconds, the tool will generate a review for the PR: ![review](https://codium.ai/images/pr_agent/review3.png){width=512} If you want to edit [configurations](#configuration-options), add the relevant ones to the command: + ``` /review --pr_reviewer.some_config1=... --pr_reviewer.some_config2=... ``` @@ -31,6 +33,7 @@ If you want to edit [configurations](#configuration-options), add the relevant o ### Automatic triggering To run the `review` automatically when a PR is opened, define in a [configuration file](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/#wiki-configuration-file): + ``` [github_app] pr_commands = [ @@ -46,7 +49,6 @@ extra_instructions = "..." - The `pr_commands` lists commands that will be executed automatically when a PR is opened. - The `[pr_reviewer]` section contains the configurations for the `review` tool you want to edit (if any). - ## Configuration options !!! example "General options" @@ -114,7 +116,6 @@ extra_instructions = "..." - ## Usage Tips !!! tip "General guidelines" @@ -164,11 +165,6 @@ extra_instructions = "..." ``` Use triple quotes to write multi-line instructions. Use bullet points to make the instructions more readable. - - - !!! tip "Code suggestions" The `review` tool previously included a legacy feature for providing code suggestions (controlled by `--pr_reviewer.num_code_suggestion`). This functionality has been deprecated and replaced by the [`improve`](./improve.md) tool, which offers higher quality and more actionable code suggestions. - - diff --git a/docs/docs/tools/scan_repo_discussions.md b/docs/docs/tools/scan_repo_discussions.md index 2da46b765c..289eb31510 100644 --- a/docs/docs/tools/scan_repo_discussions.md +++ b/docs/docs/tools/scan_repo_discussions.md @@ -5,7 +5,7 @@ The `scan_repo_discussions` tool analyzes code discussions (meaning review comments over code lines) from merged pull requests over the past 12 months. It processes these discussions alongside other PR metadata to identify recurring patterns in team feedback and code reviews, generating a comprehensive [`best_practices.md`](https://github.com/qodo-ai/pr-agent/blob/qodo-merge-best-practices_2025-04-16_1018/best_practices.md) document that distills key insights and recommendations. -This file captures repository-specific patterns derived from your team's actual workflow and discussions, rather than more generic best practices. +This file captures repository-specific patterns derived from your team's actual workflow and discussions, rather than more generic best practices. It will be utilized by Qodo Merge to provide tailored suggestions for improving code quality in future pull requests. !!! note "Active repositories are needed" @@ -16,9 +16,8 @@ It will be utilized by Qodo Merge to provide tailored suggestions for improving Teams are encouraged to further customize and refine these insights to better align with their specific development priorities and contexts. This can be done by editing the `best_practices.md` file directly when the PR is created, or iteratively over time to enhance the 'best practices' suggestions provided by Qodo Merge. - - The tool can be invoked manually by commenting on any PR: + ``` /scan_repo_discussions ``` @@ -28,7 +27,6 @@ Note that the scan can take several minutes to complete, since up to 250 PRs are ## Example usage - ![scan1](https://codium.ai/images/pr_agent/scan_repo_discussions_1.png){width=640} The PR created by the bot: @@ -43,4 +41,4 @@ The `best_practices.md` file in the PR: - Use `/scan_repo_discussions --scan_repo_discussions.force_scan=true` to force generating a PR with a new `best_practices.md` file, even if it already exists (by default, the bot will not generate a new file if it already exists). - Use `/scan_repo_discussions --scan_repo_discussions.days_back=X` to specify the number of days back to scan for discussions. The default is 365 days. -- Use `/scan_repo_discussions --scan_repo_discussions.minimal_number_of_prs=X` to specify the minimum number of merged PRs needed to generate the `best_practices.md` file. The default is 50 PRs. \ No newline at end of file +- Use `/scan_repo_discussions --scan_repo_discussions.minimal_number_of_prs=X` to specify the minimum number of merged PRs needed to generate the `best_practices.md` file. The default is 50 PRs. diff --git a/docs/docs/tools/similar_code.md b/docs/docs/tools/similar_code.md index be8dddfc27..623232f7ee 100644 --- a/docs/docs/tools/similar_code.md +++ b/docs/docs/tools/similar_code.md @@ -1,4 +1,5 @@ ## Overview + The similar code tool retrieves the most similar code components from inside the organization's codebase, or from open-source code. For example: @@ -7,7 +8,6 @@ For example: ![similar code global](https://codium.ai/images/pr_agent/similar_code_global2.png){width=768} - Qodo Merge will examine the code component and will extract the most relevant keywords to search for similar code: - `extracted keywords`: the keywords that were extracted from the code by Qodo Merge. the link will open a search page with the extracted keywords, to allow the user to modify the search if needed. @@ -19,18 +19,20 @@ Search result link example: ![code search result single](https://codium.ai/images/pr_agent/code_search_result_single.png){width=768} - `Organization Search`: ![similar code org](https://codium.ai/images/pr_agent/similar_code_org.png){width=768} - ## How to use + ### Manually + To invoke the `similar code` tool manually, comment on the PR: + ``` /find_similar_component COMPONENT_NAME ``` + Where `COMPONENT_NAME` should be the name of a code component in the PR (class, method, function). If there is a name ambiguity, there are two configurations that will help the tool to find the correct component: @@ -39,15 +41,19 @@ If there is a name ambiguity, there are two configurations that will help the to - `--pr_find_similar_component.class_name`: in case there are several methods with the same name in the same file, you can specify the relevant class name. example: + ``` /find_similar_component COMPONENT_NAME --pr_find_similar_component.file=FILE_NAME ``` ### Automatically (via Analyze table) + It can be invoked automatically from the analyze table, can be accessed by: + ``` /analyze ``` + Choose the components you want to find similar code for, and click on the `similar` checkbox. ![analyze similar](https://codium.ai/images/pr_agent/analyze_similar.png){width=768} @@ -56,7 +62,6 @@ You can search for similar code either within the organization's codebase or glo ![similar code global](https://codium.ai/images/pr_agent/similar_code_global.png){width=768} - ## Configuration options - `search_from_org`: if set to true, the tool will search for similar code in the organization's codebase. Default is false. diff --git a/docs/docs/tools/similar_issues.md b/docs/docs/tools/similar_issues.md index c974507f6b..35c0779e44 100644 --- a/docs/docs/tools/similar_issues.md +++ b/docs/docs/tools/similar_issues.md @@ -1,11 +1,12 @@ ## Overview + The similar issue tool retrieves the most similar issues to the current issue. It can be invoked manually by commenting on any PR: + ``` /similar_issue ``` - ## Example usage ![similar_issue_original_issue](https://codium.ai/images/pr_agent/similar_issue_original_issue.png){width=768} @@ -17,15 +18,18 @@ It can be invoked manually by commenting on any PR: Note that to perform retrieval, the `similar_issue` tool indexes all the repo previous issues (once). ### Selecting a Vector Database + Configure your preferred database by changing the `pr_similar_issue` parameter in `configuration.toml` file. #### Available Options + Choose from the following Vector Databases: 1. LanceDB 2. Pinecone #### Pinecone Configuration + To use Pinecone with the `similar issue` tool, add these credentials to `.secrets.toml` (or set as environment variables): ``` @@ -33,10 +37,11 @@ To use Pinecone with the `similar issue` tool, add these credentials to `.secret api_key = "..." environment = "..." ``` -These parameters can be obtained by registering to [Pinecone](https://app.pinecone.io/?sessionType=signup/). +These parameters can be obtained by registering to [Pinecone](https://app.pinecone.io/?sessionType=signup/). ## How to use + - To invoke the 'similar issue' tool from **CLI**, run: `python3 cli.py --issue_url=... similar_issue` diff --git a/docs/docs/tools/test.md b/docs/docs/tools/test.md index 0d3da4ff6a..04cc33d6c2 100644 --- a/docs/docs/tools/test.md +++ b/docs/docs/tools/test.md @@ -1,9 +1,12 @@ ## Overview + By combining LLM abilities with static code analysis, the `test` tool generate tests for a selected component, based on the PR code changes. It can be invoked manually by commenting on any PR: + ``` /test component_name ``` + where 'component_name' is the name of a specific component in the PR. To get a list of the components that changed in the PR and choose the relevant component interactively, use the [`analyze`](./analyze.md) tool. @@ -14,15 +17,14 @@ The tool will generate tests for the selected component (if no component is stat ![test1](https://codium.ai/images/pr_agent/test1.png){width=768} - (Example taken from [here](https://github.com/Codium-ai/pr-agent/pull/598#issuecomment-1913679429)): !!! note "Notes" - The following languages are currently supported: Python, Java, C++, JavaScript, TypeScript, C#. - This tool can also be triggered interactively by using the [`analyze`](./analyze.md) tool. - ## Configuration options + - `num_tests`: number of tests to generate. Default is 3. - `testing_framework`: the testing framework to use. If not set, for Python it will use `pytest`, for Java it will use `JUnit`, for C++ it will use `Catch2`, and for JavaScript and TypeScript it will use `jest`. - `avoid_mocks`: if set to true, the tool will try to avoid using mocks in the generated tests. Note that even if this option is set to true, the tool might still use mocks if it cannot generate a test without them. Default is true. diff --git a/docs/docs/tools/update_changelog.md b/docs/docs/tools/update_changelog.md index 23e620edfe..9e25fd1444 100644 --- a/docs/docs/tools/update_changelog.md +++ b/docs/docs/tools/update_changelog.md @@ -1,6 +1,8 @@ ## Overview + The `update_changelog` tool automatically updates the CHANGELOG.md file with the PR changes. It can be invoked manually by commenting on any PR: + ``` /update_changelog ``` @@ -18,4 +20,4 @@ Under the section `pr_update_changelog`, the [configuration file](https://github - `push_changelog_changes`: whether to push the changes to CHANGELOG.md, or just publish them as a comment. Default is false (publish as comment). - `extra_instructions`: Optional extra instructions to the tool. For example: "Use the following structure: ..." - `add_pr_link`: whether the model should try to add a link to the PR in the changelog. Default is true. -- `skip_ci_on_push`: whether the commit message (when `push_changelog_changes` is true) will include the term "[skip ci]", preventing CI tests to be triggered on the changelog commit. Default is true. \ No newline at end of file +- `skip_ci_on_push`: whether the commit message (when `push_changelog_changes` is true) will include the term "[skip ci]", preventing CI tests to be triggered on the changelog commit. Default is true. diff --git a/docs/docs/usage-guide/EXAMPLE_BEST_PRACTICE.md b/docs/docs/usage-guide/EXAMPLE_BEST_PRACTICE.md index e9de20d8f1..993c0246db 100644 --- a/docs/docs/usage-guide/EXAMPLE_BEST_PRACTICE.md +++ b/docs/docs/usage-guide/EXAMPLE_BEST_PRACTICE.md @@ -1,7 +1,8 @@ ## Recommend Python Best Practices + This document outlines a series of recommended best practices for Python development. These guidelines aim to improve code quality, maintainability, and readability. -### Imports +### Imports Use `import` statements for packages and modules only, not for individual types, classes, or functions. @@ -9,16 +10,16 @@ Use `import` statements for packages and modules only, not for individual type Reusability mechanism for sharing code from one module to another. -#### Decision +#### Decision -- Use `import x` for importing packages and modules. -- Use `from x import y` where `x` is the package prefix and `y` is the module name with no prefix. -- Use `from x import y as z` in any of the following circumstances: - - Two modules named `y` are to be imported. - - `y` conflicts with a top-level name defined in the current module. - - `y` conflicts with a common parameter name that is part of the public API (e.g., `features`). - - `y` is an inconveniently long name, or too generic in the context of your code -- Use `import y as z` only when `z` is a standard abbreviation (e.g., `import numpy as np`). +- Use `import x` for importing packages and modules. +- Use `from x import y` where `x` is the package prefix and `y` is the module name with no prefix. +- Use `from x import y as z` in any of the following circumstances: + - Two modules named `y` are to be imported. + - `y` conflicts with a top-level name defined in the current module. + - `y` conflicts with a common parameter name that is part of the public API (e.g., `features`). + - `y` is an inconveniently long name, or too generic in the context of your code +- Use `import y as z` only when `z` is a standard abbreviation (e.g., `import numpy as np`). For example the module `sound.effects.echo` may be imported as follows: @@ -35,13 +36,13 @@ Do not use relative names in imports. Even if the module is in the same package, Exemptions from this rule: -- Symbols from the following modules are used to support static analysis and type checking: - - [`typing` module](https://google.github.io/styleguide/pyguide.html#typing-imports) - - [`collections.abc` module](https://google.github.io/styleguide/pyguide.html#typing-imports) - - [`typing_extensions` module](https://github.com/python/typing_extensions/blob/main/README.md) -- Redirects from the [six.moves module](https://six.readthedocs.io/#module-six.moves). +- Symbols from the following modules are used to support static analysis and type checking: + - [`typing` module](https://google.github.io/styleguide/pyguide.html#typing-imports) + - [`collections.abc` module](https://google.github.io/styleguide/pyguide.html#typing-imports) + - [`typing_extensions` module](https://github.com/python/typing_extensions/blob/main/README.md) +- Redirects from the [six.moves module](https://six.readthedocs.io/#module-six.moves). -### Packages +### Packages Import each module using the full pathname location of the module. @@ -85,6 +86,7 @@ No: The directory the main binary is located in should not be assumed to be in `sys.path` despite that happening in some environments. This being the case, code should assume that `import jodie` refers to a third-party or top-level package named `jodie`, not a local `jodie.py`. ### Default Iterators and Operators + Use default iterators and operators for types that support them, like lists, dictionaries, and files. #### Definition @@ -125,7 +127,7 @@ Okay in most cases. You can specify values for variables at the end of a function’s parameter list, e.g., `def foo(a, b=0):`. If `foo` is called with only one argument, `b` is set to 0. If it is called with two arguments, `b` has the value of the second argument. -#### Decision +#### Decision Okay to use with the following caveat: @@ -158,7 +160,6 @@ No: def foo(a, b: Mapping = {}): # Could still get passed to unchecked code. ### True/False Evaluations - Use the “implicit” false if possible, e.g., `if foo:` rather than `if foo != []:` ### Lexical Scoping @@ -175,11 +176,11 @@ def get_adder(summand1: float) -> Callable[[float], float]: return adder ``` + #### Decision Okay to use. - ### Threading Do not rely on the atomicity of built-in types. diff --git a/docs/docs/usage-guide/additional_configurations.md b/docs/docs/usage-guide/additional_configurations.md index 578223a7cc..bbdc753e21 100644 --- a/docs/docs/usage-guide/additional_configurations.md +++ b/docs/docs/usage-guide/additional_configurations.md @@ -1,32 +1,35 @@ ## Show possible configurations + The possible configurations of Qodo Merge are stored in [here](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml){:target="_blank"}. In the [tools](https://qodo-merge-docs.qodo.ai/tools/) page you can find explanations on how to use these configurations for each tool. To print all the available configurations as a comment on your PR, you can use the following command: + ``` /config ``` ![possible_config1](https://codium.ai/images/pr_agent/possible_config1.png){width=512} - To view the **actual** configurations used for a specific tool, after all the user settings are applied, you can add for each tool a `--config.output_relevant_configurations=true` suffix. For example: + ``` /improve --config.output_relevant_configurations=true ``` + Will output an additional field showing the actual configurations used for the `improve` tool. ![possible_config2](https://codium.ai/images/pr_agent/possible_config2.png){width=512} - ## Ignoring files from analysis In some cases, you may want to exclude specific files or directories from the analysis performed by Qodo Merge. This can be useful, for example, when you have files that are generated automatically or files that shouldn't be reviewed, like vendor code. You can ignore files or folders using the following methods: - - `IGNORE.GLOB` - - `IGNORE.REGEX` + +- `IGNORE.GLOB` +- `IGNORE.REGEX` which you can edit to ignore files or folders based on glob or regex patterns. @@ -37,14 +40,15 @@ Let's look at an example where we want to ignore all files with `.py` extension To ignore Python files in a PR with online usage, comment on a PR: `/review --ignore.glob="['*.py']"` - To ignore Python files in all PRs using `glob` pattern, set in a configuration file: + ``` [ignore] glob = ['*.py'] ``` And to ignore Python files in all PRs using `regex` pattern, set in a configuration file: + ``` [regex] regex = ['.*\.py$'] @@ -53,6 +57,7 @@ regex = ['.*\.py$'] ## Extra instructions All Qodo Merge tools have a parameter called `extra_instructions`, that enables to add free-text extra instructions. Example usage: + ``` /update_changelog --pr_update_changelog.extra_instructions="Make sure to update also the version ..." ``` @@ -63,7 +68,7 @@ The default response language for Qodo Merge is **U.S. English**. However, some To configure this, set the `response_language` parameter in the configuration file. This will prompt the model to respond in the specified language. Use a **standard locale code** based on [ISO 3166](https://en.wikipedia.org/wiki/ISO_3166) (country codes) and [ISO 639](https://en.wikipedia.org/wiki/ISO_639) (language codes) to define a language-country pair. See this [comprehensive list of locale codes](https://simplelocalize.io/data/locales/). -Example: +Example: ```toml [config] @@ -72,7 +77,7 @@ response_language: "it-IT" This will set the response language globally for all the commands to Italian. -> **Important:** Note that only dynamic text generated by the AI model is translated to the configured language. Static text such as labels and table headers that are not part of the AI models response will remain in US English. In addition, the model you are using must have good support for the specified language. +> **Important:** Note that only dynamic text generated by the AI model is translated to the configured language. Static text such as labels and table headers that are not part of the AI models response will remain in US English. In addition, the model you are using must have good support for the specified language. [//]: # (## Working with large PRs) @@ -92,11 +97,10 @@ This will set the response language globally for all the commands to Italian. [//]: # (which divides the PR into chunks, and processes each chunk separately. With this mode, regardless of the model, no compression will be done (but for large PRs, multiple model calls may occur)) - - ## Patch Extra Lines By default, around any change in your PR, git patch provides three lines of context above and below the change. + ``` @@ -12,5 +12,5 @@ def func1(): code line that already existed in the file... @@ -110,6 +114,7 @@ By default, around any change in your PR, git patch provides three lines of cont ``` Qodo Merge will try to increase the number of lines of context, via the parameter: + ``` [config] patch_extra_lines_before=3 @@ -136,6 +141,7 @@ The default log level is "DEBUG", which provides detailed output of all operatio Various logging observability tools can be used out-of-the box when using the default LiteLLM AI Handler. Simply configure the LiteLLM callback settings in `configuration.toml` and set environment variables according to the LiteLLM [documentation](https://docs.litellm.ai/docs/). For example, to use [LangSmith](https://www.langchain.com/langsmith) you can add the following to your `configuration.toml` file: + ``` [litellm] enable_callbacks = true @@ -201,7 +207,7 @@ For the configuration above, automatic feedback will only be triggered when the To ignore PRs containing specific labels, you can add the following to your `configuration.toml` file: -``` +``` [config] ignore_pr_labels = ["do-not-merge"] ``` @@ -221,10 +227,10 @@ While this detection is robust, it may not catch all cases, particularly when: - Bot names don't match common patterns To supplement the automatic bot detection, you can manually specify users to ignore. Add the following to your `configuration.toml` file to ignore PRs from specific users: + ``` [config] ignore_pr_authors = ["my-special-bot-user", ...] ``` Where the `ignore_pr_authors` is a list of usernames that you want to ignore. - diff --git a/docs/docs/usage-guide/automations_and_usage.md b/docs/docs/usage-guide/automations_and_usage.md index 25b962e0f8..c13bcd3489 100644 --- a/docs/docs/usage-guide/automations_and_usage.md +++ b/docs/docs/usage-guide/automations_and_usage.md @@ -14,23 +14,26 @@ Examples of invoking the different tools via the CLI: **Notes:** 1. in addition to editing your local configuration file, you can also change any configuration value by adding it to the command line: + ``` python -m pr_agent.cli --pr_url= /review --pr_reviewer.extra_instructions="focus on the file: ..." ``` 2. You can print results locally, without publishing them, by setting in `configuration.toml`: + ``` [config] publish_output=false verbosity_level=2 ``` + This is useful for debugging or experimenting with different tools. 3. **git provider**: The [git_provider](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml#L5) field in a configuration file determines the GIT provider that will be used by Qodo Merge. Currently, the following providers are supported: `github` **(default)**, `gitlab`, `bitbucket`, `azure`, `codecommit`, `local`, and `gerrit`. - ### CLI Health Check + To verify that Qodo Merge has been configured correctly, you can run this health check command from the repository root: ```bash @@ -63,18 +66,17 @@ Commands for invoking the different tools via comments: - **Ask**: `/ask "..."` - **Update Changelog**: `/update_changelog` - To edit a specific configuration value, just add `--config_path=` to any command. For example, if you want to edit the `review` tool configurations, you can run: + ``` /review --pr_reviewer.extra_instructions="..." --pr_reviewer.require_score_review=false ``` -Any configuration value in [configuration file](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml) file can be similarly edited. Comment `/config` to see the list of available configurations. +Any configuration value in [configuration file](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml) file can be similarly edited. Comment `/config` to see the list of available configurations. ## Qodo Merge Automatic Feedback - ### Disabling all automatic feedback To easily disable all automatic feedback from Qodo Merge (GitHub App, GitLab Webhook, BitBucket App, Azure DevOps Webhook), set in a configuration file: @@ -97,6 +99,7 @@ When this parameter is set to `true`, Qodo Merge will not run any automatic tool The [github_app](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml#L220) section defines GitHub app specific configurations. The configuration parameter `pr_commands` defines the list of tools that will be **run automatically** when a new PR is opened: + ```toml [github_app] pr_commands = [ @@ -120,6 +123,7 @@ Every time you run the `describe` tool (including automatic runs) the PR title w You can customize configurations specifically for automated runs by using the `--config_path=` parameter. For instance, to modify the `review` tool settings only for newly opened PRs, use: + ```toml [github_app] pr_commands = [ @@ -135,6 +139,7 @@ In addition to running automatic tools when a PR is opened, the GitHub app can a The configuration toggle `handle_push_trigger` can be used to enable this feature. The configuration parameter `push_commands` defines the list of tools that will be **run automatically** when new code is pushed to the PR. + ```toml [github_app] handle_push_trigger = true @@ -143,12 +148,15 @@ push_commands = [ "/review", ] ``` + This means that when new code is pushed to the PR, the Qodo Merge will run the `describe` and `review` tools, with the specified parameters. ### GitHub Action + `GitHub Action` is a different way to trigger Qodo Merge tools, and uses a different configuration mechanism than `GitHub App`.
You can configure settings for `GitHub Action` by adding environment variables under the env section in `.github/workflows/pr_agent.yml` file. Specifically, start by setting the following environment variables: + ```yaml env: OPENAI_KEY: ${{ secrets.OPENAI_KEY }} # Make sure to add your OpenAI key to your repo secrets @@ -158,6 +166,7 @@ Specifically, start by setting the following environment variables: github_action_config.auto_improve: "true" # enable\disable auto improve github_action_config.pr_actions: '["opened", "reopened", "ready_for_review", "review_requested"]' ``` + `github_action_config.auto_review`, `github_action_config.auto_describe` and `github_action_config.auto_improve` are used to enable/disable automatic tools that run when a new PR is opened. If not set, the default configuration is for all three tools to run automatically when a new PR is opened. @@ -180,6 +189,7 @@ publish_labels = false to prevent Qodo Merge from publishing labels when running the `describe` tool. ### GitLab Webhook + After setting up a GitLab webhook, to control which commands will run automatically when a new MR is opened, you can set the `pr_commands` parameter in the configuration file, similar to the GitHub App: ```toml @@ -194,6 +204,7 @@ pr_commands = [ the GitLab webhook can also respond to new code that is pushed to an open MR. The configuration toggle `handle_push_trigger` can be used to enable this feature. The configuration parameter `push_commands` defines the list of tools that will be **run automatically** when new code is pushed to the MR. + ```toml [gitlab] handle_push_trigger = true @@ -206,11 +217,13 @@ push_commands = [ Note that to use the 'handle_push_trigger' feature, you need to give the gitlab webhook also the "Push events" scope. ### BitBucket App + Similar to GitHub app, when running Qodo Merge from BitBucket App, the default [configuration file](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml) will be initially loaded. By uploading a local `.pr_agent.toml` file to the root of the repo's default branch, you can edit and customize any configuration parameter. Note that you need to upload `.pr_agent.toml` prior to creating a PR, in order for the configuration to take effect. For example, if your local `.pr_agent.toml` file contains: + ```toml [pr_reviewer] extra_instructions = "Answer in japanese" @@ -218,12 +231,10 @@ extra_instructions = "Answer in japanese" Each time you invoke a `/review` tool, it will use the extra instructions you set in the local configuration file. - Note that among other limitations, BitBucket provides relatively low rate-limits for applications (up to 1000 requests per hour), and does not provide an API to track the actual rate-limit usage. If you experience a lack of responses from Qodo Merge, you might want to set: `bitbucket_app.avoid_full_files=true` in your configuration file. This will prevent Qodo Merge from acquiring the full file content, and will only use the diff content. This will reduce the number of requests made to BitBucket, at the cost of small decrease in accuracy, as dynamic context will not be applicable. - #### BitBucket Self-Hosted App automatic tools To control which commands will run automatically when a new PR is opened, you can set the `pr_commands` parameter in the configuration file: @@ -236,10 +247,12 @@ pr_commands = [ "/improve --pr_code_suggestions.commitable_code_suggestions=true --pr_code_suggestions.suggestions_score_threshold=7", ] ``` + Note that we set specifically for bitbucket, we recommend using: `--pr_code_suggestions.suggestions_score_threshold=7` and that is the default value we set for bitbucket. Since this platform only supports inline code suggestions, we want to limit the number of suggestions, and only present a limited number. To enable BitBucket app to respond to each **push** to the PR, set (for example): + ```toml [bitbucket_app] handle_push_trigger = true @@ -252,6 +265,7 @@ push_commands = [ ### Azure DevOps provider To use Azure DevOps provider use the following settings in configuration.toml: + ```toml [config] git_provider="azure" @@ -265,6 +279,7 @@ If PAT was chosen, you can assign the value in .secrets.toml. If DefaultAzureCredential was chosen, you can assigned the additional env vars like AZURE_CLIENT_SECRET directly, or use managed identity/az cli (for local development) without any additional configuration. in any case, 'org' value must be assigned in .secrets.toml: + ``` [azure_devops] org = "https://dev.azure.com/YOUR_ORGANIZATION/" @@ -274,6 +289,7 @@ org = "https://dev.azure.com/YOUR_ORGANIZATION/" #### Azure DevOps Webhook To control which commands will run automatically when a new PR is opened, you can set the `pr_commands` parameter in the configuration file, similar to the GitHub App: + ```toml [azure_devops_server] pr_commands = [ diff --git a/docs/docs/usage-guide/changing_a_model.md b/docs/docs/usage-guide/changing_a_model.md index 36480d2144..24c7b93423 100644 --- a/docs/docs/usage-guide/changing_a_model.md +++ b/docs/docs/usage-guide/changing_a_model.md @@ -47,7 +47,6 @@ tenant_id = "" # Your Azure AD tenant ID api_base = "" # Your Azure OpenAI service base URL (e.g., https://openai.xyz.com/) ``` - Passing custom headers to the underlying LLM Model API can be done by setting extra_headers parameter to litellm. ```toml diff --git a/docs/docs/usage-guide/configuration_options.md b/docs/docs/usage-guide/configuration_options.md index 85ede6ba94..dc0ac8d06f 100644 --- a/docs/docs/usage-guide/configuration_options.md +++ b/docs/docs/usage-guide/configuration_options.md @@ -23,7 +23,6 @@ In terms of precedence, wiki configurations will override local configurations, With Qodo Merge, you can set configurations by creating a page called `.pr_agent.toml` in the [wiki](https://github.com/Codium-ai/pr-agent/wiki/pr_agent.toml) of the repo. The advantage of this method is that it allows to set configurations without needing to commit new content to the repo - just edit the wiki page and **save**. - ![wiki_configuration](https://codium.ai/images/pr_agent/wiki_configuration.png){width=512} Click [here](https://codium.ai/images/pr_agent/wiki_configuration_pr_agent.mp4) to see a short instructional video. We recommend surrounding the configuration content with triple-quotes (or \`\`\`toml), to allow better presentation when displayed in the wiki as markdown. @@ -40,7 +39,6 @@ Qodo Merge will know to remove the surrounding quotes when reading the configura `Platforms supported: GitHub, GitLab, Bitbucket, Azure DevOps` - By uploading a local `.pr_agent.toml` file to the root of the repo's default branch, you can edit and customize any configuration parameter. Note that you need to upload `.pr_agent.toml` prior to creating a PR, in order for the configuration to take effect. For example, if you set in `.pr_agent.toml`: @@ -56,7 +54,6 @@ extra_instructions="""\ Then you can give a list of extra instructions to the `review` tool. - ## Global configuration file 💎 `Platforms supported: GitHub, GitLab, Bitbucket` @@ -71,27 +68,27 @@ For example, in the GitHub organization `Codium-ai`: - The repo [`https://github.com/Codium-ai/pr-agent`](https://github.com/Codium-ai/pr-agent/blob/main/.pr_agent.toml) inherits the global configuration file from `pr-agent-settings`. ### Bitbucket Organization level configuration file 💎 + `Relevant platforms: Bitbucket Data Center` In Bitbucket Data Center, there are two levels where you can define a global configuration file: -* Project-level global configuration: +- Project-level global configuration: Create a repository named `pr-agent-settings` within a specific project. The configuration file in this repository will apply to all repositories under the same project. -* Organization-level global configuration: +- Organization-level global configuration: Create a dedicated project to hold a global configuration file that affects all repositories across all projects in your organization. **Setting up organization-level global configuration:** -1. Create a new project with both the name and key: PR_AGENT_SETTINGS. -2. Inside the PR_AGENT_SETTINGS project, create a repository named pr-agent-settings. -3. In this repository, add a `.pr_agent.toml` configuration file—structured similarly to the global configuration file described above. -4. Optionally, you can add organizational-level [global best practices file](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/#global-configuration-file). +1. Create a new project with both the name and key: PR_AGENT_SETTINGS. +2. Inside the PR_AGENT_SETTINGS project, create a repository named pr-agent-settings. +3. In this repository, add a `.pr_agent.toml` configuration file—structured similarly to the global configuration file described above. +4. Optionally, you can add organizational-level [global best practices file](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/#global-configuration-file). Repositories across your entire Bitbucket organization will inherit the configuration from this file. !!! note "Note" If both organization-level and project-level global settings are defined, the project-level settings will take precedence over the organization-level configuration. Additionally, parameters from a repository’s local .pr_agent.toml file will always override both global settings. - diff --git a/docs/docs/usage-guide/enabling_a_wiki.md b/docs/docs/usage-guide/enabling_a_wiki.md index 3d1c7af1e9..42d6c37607 100644 --- a/docs/docs/usage-guide/enabling_a_wiki.md +++ b/docs/docs/usage-guide/enabling_a_wiki.md @@ -1,6 +1,5 @@ `Supported Git Platforms: GitHub, GitLab, Bitbucket` - For optimal functionality of Qodo Merge, we recommend enabling a wiki for each repository where Qodo Merge is installed. The wiki serves several important purposes: **Key Wiki Features: 💎** @@ -9,7 +8,6 @@ For optimal functionality of Qodo Merge, we recommend enabling a wiki for each r - Track [accepted suggestions](https://qodo-merge-docs.qodo.ai/tools/improve/#suggestion-tracking) - Facilitates learning over time by creating an [auto_best_practices.md](https://qodo-merge-docs.qodo.ai/core-abilities/auto_best_practices) file - **Setup Instructions (GitHub):** To enable a wiki for your repository: @@ -27,6 +25,6 @@ To enable a wiki for your repository: - Your code (and its derivatives, including accepted code suggestions) is yours. Qodo Merge will never store it on external servers. - Repository changes typically require pull requests, which create overhead and are time-consuming. This process is too cumbersome for auto data aggregation, and is not very convenient even for managing frequently updated content like configuration files. - A repository wiki page provides an ideal balance: - - It lives within your repository, making it suitable for code-related documentation - - It enables quick updates without the overhead of pull requests - - It maintains full Git version control, allowing you to track changes over time. + - It lives within your repository, making it suitable for code-related documentation + - It enables quick updates without the overhead of pull requests + - It maintains full Git version control, allowing you to track changes over time. diff --git a/docs/docs/usage-guide/index.md b/docs/docs/usage-guide/index.md index 087dd772b3..75de694403 100644 --- a/docs/docs/usage-guide/index.md +++ b/docs/docs/usage-guide/index.md @@ -3,24 +3,23 @@ This page provides a detailed guide on how to use Qodo Merge. It includes information on how to adjust Qodo Merge configurations, define which tools will run automatically, and other advanced configurations. - - [Introduction](./introduction.md) - [Enabling a Wiki](./enabling_a_wiki) - [Configuration File](./configuration_options.md) - [Usage and Automation](./automations_and_usage.md) - - [Local Repo (CLI)](./automations_and_usage.md#local-repo-cli) - - [Online Usage](./automations_and_usage.md#online-usage) - - [GitHub App](./automations_and_usage.md#github-app) - - [GitHub Action](./automations_and_usage.md#github-action) - - [GitLab Webhook](./automations_and_usage.md#gitlab-webhook) - - [BitBucket App](./automations_and_usage.md#bitbucket-app) - - [Azure DevOps Provider](./automations_and_usage.md#azure-devops-provider) + - [Local Repo (CLI)](./automations_and_usage.md#local-repo-cli) + - [Online Usage](./automations_and_usage.md#online-usage) + - [GitHub App](./automations_and_usage.md#github-app) + - [GitHub Action](./automations_and_usage.md#github-action) + - [GitLab Webhook](./automations_and_usage.md#gitlab-webhook) + - [BitBucket App](./automations_and_usage.md#bitbucket-app) + - [Azure DevOps Provider](./automations_and_usage.md#azure-devops-provider) - [Managing Mail Notifications](./mail_notifications.md) - [Changing a Model](./changing_a_model.md) - [Additional Configurations Walkthrough](./additional_configurations.md) - - [Ignoring files from analysis](./additional_configurations.md#ignoring-files-from-analysis) - - [Extra instructions](./additional_configurations.md#extra-instructions) - - [Working with large PRs](./additional_configurations.md#working-with-large-prs) - - [Changing a model](./additional_configurations.md#changing-a-model) - - [Patch Extra Lines](./additional_configurations.md#patch-extra-lines) + - [Ignoring files from analysis](./additional_configurations.md#ignoring-files-from-analysis) + - [Extra instructions](./additional_configurations.md#extra-instructions) + - [Working with large PRs](./additional_configurations.md#working-with-large-prs) + - [Changing a model](./additional_configurations.md#changing-a-model) + - [Patch Extra Lines](./additional_configurations.md#patch-extra-lines) - [Qodo Merge Models](./qodo_merge_models) diff --git a/docs/docs/usage-guide/introduction.md b/docs/docs/usage-guide/introduction.md index f827056113..11e56b32bb 100644 --- a/docs/docs/usage-guide/introduction.md +++ b/docs/docs/usage-guide/introduction.md @@ -5,7 +5,6 @@ After [installation](https://qodo-merge-docs.qodo.ai/installation/), there are t 2. Online usage - by [commenting](https://github.com/Codium-ai/pr-agent/pull/229#issuecomment-1695021901){:target="_blank"} on a PR 3. Enabling Qodo Merge tools to run automatically when a new PR is opened - Specifically, CLI commands can be issued by invoking a pre-built [docker image](https://qodo-merge-docs.qodo.ai/installation/locally/#using-docker-image), or by invoking a [locally cloned repo](https://qodo-merge-docs.qodo.ai/installation/locally/#run-from-source). For online usage, you will need to setup either a [GitHub App](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-app) or a [GitHub Action](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-action) (GitHub), a [GitLab webhook](https://qodo-merge-docs.qodo.ai/installation/gitlab/#run-a-gitlab-webhook-server) (GitLab), or a [BitBucket App](https://qodo-merge-docs.qodo.ai/installation/bitbucket/#run-using-codiumai-hosted-bitbucket-app) (BitBucket). diff --git a/docs/docs/usage-guide/mail_notifications.md b/docs/docs/usage-guide/mail_notifications.md index a1d97016de..054504f7c8 100644 --- a/docs/docs/usage-guide/mail_notifications.md +++ b/docs/docs/usage-guide/mail_notifications.md @@ -8,10 +8,10 @@ As an alternative, you can filter in your mail provider the notifications specif ![filter_mail_notifications](https://codium.ai/images/pr_agent/filter_mail_notifications.png){width=512} - Another option to reduce the mail overload, yet still receive notifications on Qodo Merge tools, is to disable the help collapsible section in Qodo Merge bot comments. This can done by setting `enable_help_text=false` for the relevant tool in the configuration file. For example, to disable the help text for the `pr_reviewer` tool, set: + ``` [pr_reviewer] enable_help_text = false diff --git a/docs/docs/usage-guide/qodo_merge_models.md b/docs/docs/usage-guide/qodo_merge_models.md index 519de148b0..2adc04f131 100644 --- a/docs/docs/usage-guide/qodo_merge_models.md +++ b/docs/docs/usage-guide/qodo_merge_models.md @@ -12,21 +12,22 @@ The models supported by Qodo Merge are: - `deepseek/r1` To restrict Qodo Merge to using only `o3-mini`, add this setting: + ``` [config] model="o3-mini" ``` To restrict Qodo Merge to using only `GPT-4.1`, add this setting: + ``` [config] model="gpt-4.1" ``` To restrict Qodo Merge to using only `deepseek-r1` us-hosted, add this setting: + ``` [config] model="deepseek/r1" ``` - - From 869a179506fb6906a7a6d84d68aa0b9fc36826b8 Mon Sep 17 00:00:00 2001 From: dst03106 Date: Fri, 18 Apr 2025 12:02:55 +0900 Subject: [PATCH 122/365] feat: add support for Mistral and Codestral models --- docs/docs/usage-guide/changing_a_model.md | 28 +++++++++++++++++++ pr_agent/algo/__init__.py | 14 ++++++++++ .../algo/ai_handlers/litellm_ai_handler.py | 8 ++++++ 3 files changed, 50 insertions(+) diff --git a/docs/docs/usage-guide/changing_a_model.md b/docs/docs/usage-guide/changing_a_model.md index 36480d2144..bb34a2af9c 100644 --- a/docs/docs/usage-guide/changing_a_model.md +++ b/docs/docs/usage-guide/changing_a_model.md @@ -251,6 +251,34 @@ key = ... # your DeepInfra api key (you can obtain a DeepInfra key from [here](https://deepinfra.com/dash/api_keys)) +### Mistral + +To use models like Mistral or Codestral with Mistral, for example, set: + +```toml +[config] # in configuration.toml +model = "mistral/mistral-small-latest" +fallback_models = ["mistral/mistral-medium-latest"] +[mistral] # in .secrets.toml +key = "..." # your Mistral api key +``` + +(you can obtain a Mistral key from [here](https://console.mistral.ai/api-keys)) + +### Codestral + +To use Codestral model with Codestral, for example, set: + +```toml +[config] # in configuration.toml +model = "codestral/codestral-latest" +fallback_models = ["codestral/codestral-2405"] +[codestral] # in .secrets.toml +key = "..." # your Codestral api key +``` + +(you can obtain a Codestral key from [here](https://console.mistral.ai/codestral)) + ### Custom models If the relevant model doesn't appear [here](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/algo/__init__.py), you can still use it as a custom model: diff --git a/pr_agent/algo/__init__.py b/pr_agent/algo/__init__.py index 62cd3f61be..e3abb93cc4 100644 --- a/pr_agent/algo/__init__.py +++ b/pr_agent/algo/__init__.py @@ -112,6 +112,20 @@ "deepinfra/deepseek-ai/DeepSeek-R1-Distill-Qwen-32B": 128000, "deepinfra/deepseek-ai/DeepSeek-R1-Distill-Llama-70B": 128000, "deepinfra/deepseek-ai/DeepSeek-R1": 128000, + "mistral/mistral-small-latest": 8191, + "mistral/mistral-medium-latest": 8191, + "mistral/mistral-large-2407": 128000, + "mistral/mistral-large-latest": 128000, + "mistral/open-mistral-7b": 8191, + "mistral/open-mixtral-8x7b": 8191, + "mistral/open-mixtral-8x22b": 8191, + "mistral/codestral-latest": 8191, + "mistral/open-mistral-nemo": 128000, + "mistral/open-mistral-nemo-2407": 128000, + "mistral/open-codestral-mamba": 256000, + "mistral/codestral-mamba-latest": 256000, + "codestral/codestral-latest": 8191, + "codestral/codestral-2405": 8191, } USER_MESSAGE_ONLY_MODELS = [ diff --git a/pr_agent/algo/ai_handlers/litellm_ai_handler.py b/pr_agent/algo/ai_handlers/litellm_ai_handler.py index d717c087e2..61af7383cc 100644 --- a/pr_agent/algo/ai_handlers/litellm_ai_handler.py +++ b/pr_agent/algo/ai_handlers/litellm_ai_handler.py @@ -97,6 +97,14 @@ def __init__(self): if get_settings().get("DEEPINFRA.KEY", None): os.environ['DEEPINFRA_API_KEY'] = get_settings().get("DEEPINFRA.KEY") + # Support mistral models + if get_settings().get("MISTRAL.KEY", None): + os.environ["MISTRAL_API_KEY"] = get_settings().get("MISTRAL.KEY") + + # Support codestral models + if get_settings().get("CODESTRAL.KEY", None): + os.environ["CODESTRAL_API_KEY"] = get_settings().get("CODESTRAL.KEY") + # Check for Azure AD configuration if get_settings().get("AZURE_AD.CLIENT_ID", None): self.azure = True From fab4717449f353d26414ed4d970bb45b3f3ae8ec Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Fri, 18 Apr 2025 20:27:46 +0800 Subject: [PATCH 123/365] Upgrade litellm to v1.66.3 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 05747bcd23..86a50c849d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,7 +13,7 @@ google-cloud-aiplatform==1.38.0 google-generativeai==0.8.3 google-cloud-storage==2.10.0 Jinja2==3.1.2 -litellm==1.66.2 +litellm==1.66.3 loguru==0.7.2 msrest==0.7.1 openai>=1.55.3 From e6ee2b96e27a64d40699cde567d03f9c12129a78 Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Fri, 18 Apr 2025 21:43:05 +0800 Subject: [PATCH 124/365] Add .venv/ to .dockerignore and .gitignore for virtual env compatibility Include .venv/ alongside venv/ to align with common Python virtual environment practices as recommended in the official documentation. --- .dockerignore | 1 + .gitignore | 1 + 2 files changed, 2 insertions(+) diff --git a/.dockerignore b/.dockerignore index 822a9e6be7..d785fbe944 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,4 @@ +.venv/ venv/ pr_agent/settings/.secrets.toml pics/ diff --git a/.gitignore b/.gitignore index d67e566970..6f39a88995 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ .lsp/ .vscode/ .env +.venv/ venv/ pr_agent/settings/.secrets.toml __pycache__ From 5e98926b5a53eebb1bb04c5e1c791c0c0a5860a7 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Fri, 18 Apr 2025 21:40:39 +0300 Subject: [PATCH 125/365] Update scan_repo_discussions.md to clarify focus on best practices --- docs/docs/tools/scan_repo_discussions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/tools/scan_repo_discussions.md b/docs/docs/tools/scan_repo_discussions.md index 289eb31510..517dd985f2 100644 --- a/docs/docs/tools/scan_repo_discussions.md +++ b/docs/docs/tools/scan_repo_discussions.md @@ -3,7 +3,7 @@ ## Overview The `scan_repo_discussions` tool analyzes code discussions (meaning review comments over code lines) from merged pull requests over the past 12 months. -It processes these discussions alongside other PR metadata to identify recurring patterns in team feedback and code reviews, generating a comprehensive [`best_practices.md`](https://github.com/qodo-ai/pr-agent/blob/qodo-merge-best-practices_2025-04-16_1018/best_practices.md) document that distills key insights and recommendations. +It processes these discussions alongside other PR metadata to identify recurring patterns related to best practices in team feedback and code reviews, generating a comprehensive [`best_practices.md`](https://github.com/qodo-ai/pr-agent/blob/qodo-merge-best-practices_2025-04-16_1018/best_practices.md) document that distills key insights and recommendations. This file captures repository-specific patterns derived from your team's actual workflow and discussions, rather than more generic best practices. It will be utilized by Qodo Merge to provide tailored suggestions for improving code quality in future pull requests. From 2fa44383102f2e1e1c6d49c31f765e414ed4e799 Mon Sep 17 00:00:00 2001 From: Nico Hein Date: Fri, 18 Apr 2025 14:42:51 -0400 Subject: [PATCH 126/365] docs: itemization fix --- docs/docs/installation/bitbucket.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/docs/installation/bitbucket.md b/docs/docs/installation/bitbucket.md index 6e6595f86e..fe9e01f129 100644 --- a/docs/docs/installation/bitbucket.md +++ b/docs/docs/installation/bitbucket.md @@ -16,11 +16,12 @@ pipelines: ``` 2. Add the following secure variables to your repository under Repository settings > Pipelines > Repository variables. - CONFIG__GIT_PROVIDER: `bitbucket` - OPENAI__KEY: `` - BITBUCKET__AUTH_TYPE: `basic` or `bearer` (default is `bearer`) - BITBUCKET__BEARER_TOKEN: `` (required when auth_type is bearer) - BITBUCKET__BASIC_TOKEN: `` (required when auth_type is basic) + + - CONFIG__GIT_PROVIDER: `bitbucket` + - OPENAI__KEY: `` + - BITBUCKET__AUTH_TYPE: `basic` or `bearer` (default is `bearer`) + - BITBUCKET__BEARER_TOKEN: `` (required when auth_type is bearer) + - BITBUCKET__BASIC_TOKEN: `` (required when auth_type is basic) You can get a Bitbucket token for your repository by following Repository Settings -> Security -> Access Tokens. For basic auth, you can generate a base64 encoded token from your username:password combination. From e85438312308b171da3518934f905822a58ee639 Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Sat, 19 Apr 2025 03:51:57 +0800 Subject: [PATCH 127/365] Replace default o3-mini with o4-mini --- docs/docs/chrome-extension/index.md | 2 +- docs/docs/installation/locally.md | 2 +- docs/docs/usage-guide/changing_a_model.md | 4 ++-- pr_agent/settings/configuration.toml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/docs/chrome-extension/index.md b/docs/docs/chrome-extension/index.md index d5af4d8865..a063d2c894 100644 --- a/docs/docs/chrome-extension/index.md +++ b/docs/docs/chrome-extension/index.md @@ -2,7 +2,7 @@ With a single-click installation you will gain access to a context-aware chat on your pull requests code, a toolbar extension with multiple AI feedbacks, Qodo Merge filters, and additional abilities. -The extension is powered by top code models like Claude 3.7 Sonnet and o3-mini. All the extension's features are free to use on public repositories. +The extension is powered by top code models like Claude 3.7 Sonnet and o4-mini. All the extension's features are free to use on public repositories. For private repositories, you will need to install [Qodo Merge](https://github.com/apps/qodo-merge-pro){:target="_blank"} in addition to the extension (Quick GitHub app setup with a 14-day free trial. No credit card needed). For a demonstration of how to install Qodo Merge and use it with the Chrome extension, please refer to the tutorial video at the provided [link](https://codium.ai/images/pr_agent/private_repos.mp4){:target="_blank"}. diff --git a/docs/docs/installation/locally.md b/docs/docs/installation/locally.md index f2d23dbc84..cd981f9614 100644 --- a/docs/docs/installation/locally.md +++ b/docs/docs/installation/locally.md @@ -1,6 +1,6 @@ To run PR-Agent locally, you first need to acquire two keys: -1. An OpenAI key from [here](https://platform.openai.com/api-keys){:target="_blank"}, with access to GPT-4 and o3-mini (or a key for other [language models](https://qodo-merge-docs.qodo.ai/usage-guide/changing_a_model/), if you prefer). +1. An OpenAI key from [here](https://platform.openai.com/api-keys){:target="_blank"}, with access to GPT-4 and o4-mini (or a key for other [language models](https://qodo-merge-docs.qodo.ai/usage-guide/changing_a_model/), if you prefer). 2. A personal access token from your Git platform (GitHub, GitLab, BitBucket) with repo scope. GitHub token, for example, can be issued from [here](https://github.com/settings/tokens){:target="_blank"} ## Using Docker image diff --git a/docs/docs/usage-guide/changing_a_model.md b/docs/docs/usage-guide/changing_a_model.md index d05a73ab54..76a67d2937 100644 --- a/docs/docs/usage-guide/changing_a_model.md +++ b/docs/docs/usage-guide/changing_a_model.md @@ -1,7 +1,7 @@ ## Changing a model in PR-Agent See [here](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/algo/__init__.py) for a list of available models. -To use a different model than the default (o3-mini), you need to edit in the [configuration file](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml#L2) the fields: +To use a different model than the default (o4-mini), you need to edit in the [configuration file](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml#L2) the fields: ```toml [config] @@ -311,7 +311,7 @@ To bypass chat templates and temperature controls, set `config.custom_reasoning_ reasoning_efffort= = "medium" # "low", "medium", "high" ``` -With the OpenAI models that support reasoning effort (eg: o3-mini), you can specify its reasoning effort via `config` section. The default value is `medium`. You can change it to `high` or `low` based on your usage. +With the OpenAI models that support reasoning effort (eg: o4-mini), you can specify its reasoning effort via `config` section. The default value is `medium`. You can change it to `high` or `low` based on your usage. ### Anthropic models diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index cd3e4aed12..edb5296f0b 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -6,7 +6,7 @@ [config] # models -model="o3-mini" +model="o4-mini" fallback_models=["gpt-4o-2024-11-20"] #model_weak="gpt-4o-mini-2024-07-18" # optional, a weaker model to use for some easier tasks # CLI From 4ac0aa56e5892be6b2a3834ccf19616673baf85e Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sat, 19 Apr 2025 09:26:35 +0300 Subject: [PATCH 128/365] Update model references from o3-mini to o4-mini and add Gemini models --- docs/docs/overview/pr_agent_pro.md | 20 +++++++-------- docs/docs/usage-guide/qodo_merge_models.md | 29 +++++++++++++++++----- pr_agent/algo/__init__.py | 2 ++ pr_agent/algo/utils.py | 1 + pr_agent/settings/configuration.toml | 4 +-- 5 files changed, 38 insertions(+), 18 deletions(-) diff --git a/docs/docs/overview/pr_agent_pro.md b/docs/docs/overview/pr_agent_pro.md index 44b4bd7527..02a6081fb7 100644 --- a/docs/docs/overview/pr_agent_pro.md +++ b/docs/docs/overview/pr_agent_pro.md @@ -17,17 +17,17 @@ Qodo Merge is designed for companies and teams that require additional features Here are some of the additional features and capabilities that Qodo Merge offers: -| Feature | Description | -| -------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [**Model selection**](https://qodo-merge-docs.qodo.ai/usage-guide/PR_agent_pro_models/) | Choose the model that best fits your needs, among top models like `Claude Sonnet` and `o3-mini` | -| [**Global and wiki configuration**](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/) | Control configurations for many repositories from a single location;
Edit configuration of a single repo without committing code | -| [**Apply suggestions**](https://qodo-merge-docs.qodo.ai/tools/improve/#overview) | Generate committable code from the relevant suggestions interactively by clicking on a checkbox | +| Feature | Description | +| -------------------------------------------------------------------------------------------------------------------- |--------------------------------------------------------------------------------------------------------------------------------------------------------| +| [**Model selection**](https://qodo-merge-docs.qodo.ai/usage-guide/PR_agent_pro_models/) | Choose the model that best fits your needs, among top models like `Claude Sonnet`, `o4-mini` | +| [**Global and wiki configuration**](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/) | Control configurations for many repositories from a single location;
Edit configuration of a single repo without committing code | +| [**Apply suggestions**](https://qodo-merge-docs.qodo.ai/tools/improve/#overview) | Generate committable code from the relevant suggestions interactively by clicking on a checkbox | | [**Suggestions impact**](https://qodo-merge-docs.qodo.ai/tools/improve/#assessing-impact) | Automatically mark suggestions that were implemented by the user (either directly in GitHub, or indirectly in the IDE) to enable tracking of the impact of the suggestions | -| [**CI feedback**](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/) | Automatically analyze failed CI checks on GitHub and provide actionable feedback in the PR conversation, helping to resolve issues quickly | -| [**Advanced usage statistics**](https://www.codium.ai/contact/#/) | Qodo Merge offers detailed statistics at user, repository, and company levels, including metrics about Qodo Merge usage, and also general statistics and insights | -| [**Incorporating companies' best practices**](https://qodo-merge-docs.qodo.ai/tools/improve/#best-practices) | Use the companies' best practices as reference to increase the effectiveness and the relevance of the code suggestions | -| [**Interactive triggering**](https://qodo-merge-docs.qodo.ai/tools/analyze/#example-usage) | Interactively apply different tools via the `analyze` command | -| [**Custom labels**](https://qodo-merge-docs.qodo.ai/tools/describe/#handle-custom-labels-from-the-repos-labels-page) | Define custom labels for Qodo Merge to assign to the PR | +| [**CI feedback**](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/) | Automatically analyze failed CI checks on GitHub and provide actionable feedback in the PR conversation, helping to resolve issues quickly | +| [**Advanced usage statistics**](https://www.codium.ai/contact/#/) | Qodo Merge offers detailed statistics at user, repository, and company levels, including metrics about Qodo Merge usage, and also general statistics and insights | +| [**Incorporating companies' best practices**](https://qodo-merge-docs.qodo.ai/tools/improve/#best-practices) | Use the companies' best practices as reference to increase the effectiveness and the relevance of the code suggestions | +| [**Interactive triggering**](https://qodo-merge-docs.qodo.ai/tools/analyze/#example-usage) | Interactively apply different tools via the `analyze` command | +| [**Custom labels**](https://qodo-merge-docs.qodo.ai/tools/describe/#handle-custom-labels-from-the-repos-labels-page) | Define custom labels for Qodo Merge to assign to the PR | ### Additional tools diff --git a/docs/docs/usage-guide/qodo_merge_models.md b/docs/docs/usage-guide/qodo_merge_models.md index 2adc04f131..cb062cb9d9 100644 --- a/docs/docs/usage-guide/qodo_merge_models.md +++ b/docs/docs/usage-guide/qodo_merge_models.md @@ -7,27 +7,44 @@ Users can configure Qodo Merge to use a specific model by editing the [configura The models supported by Qodo Merge are: - `claude-3-7-sonnet` (default) -- `o3-mini` +- `o4-mini` - `gpt-4.1` +- `gemini-2.5-pro` +- `gemini-2.5-flash` - `deepseek/r1` -To restrict Qodo Merge to using only `o3-mini`, add this setting: +To restrict Qodo Merge to using only `o4-mini`, add this setting: -``` +```toml [config] -model="o3-mini" +model="o4-mini" ``` To restrict Qodo Merge to using only `GPT-4.1`, add this setting: -``` +```toml [config] model="gpt-4.1" ``` -To restrict Qodo Merge to using only `deepseek-r1` us-hosted, add this setting: +To restrict Qodo Merge to using only `gemini-2.5-pro`, add this setting: + +```toml +[config] +model="gemini-2.5-pro" +``` +To restrict Qodo Merge to using only `gemini-2.5-flash`, add this setting: + +```toml +[config] +model="gemini-2.5-flash" ``` + + +To restrict Qodo Merge to using only `deepseek-r1` us-hosted, add this setting: + +```toml [config] model="deepseek/r1" ``` diff --git a/pr_agent/algo/__init__.py b/pr_agent/algo/__init__.py index e3abb93cc4..8d805baf99 100644 --- a/pr_agent/algo/__init__.py +++ b/pr_agent/algo/__init__.py @@ -57,8 +57,10 @@ 'vertex_ai/claude-3-5-sonnet-v2@20241022': 100000, 'vertex_ai/claude-3-7-sonnet@20250219': 200000, 'vertex_ai/gemini-1.5-pro': 1048576, + 'vertex_ai/gemini-2.5-pro-preview-03-25': 1048576, 'vertex_ai/gemini-1.5-flash': 1048576, 'vertex_ai/gemini-2.0-flash': 1048576, + 'vertex_ai/gemini-2.5-flash-preview-04-17': 1048576, 'vertex_ai/gemma2': 8200, 'gemini/gemini-1.5-pro': 1048576, 'gemini/gemini-1.5-flash': 1048576, diff --git a/pr_agent/algo/utils.py b/pr_agent/algo/utils.py index 4e42332fdd..398df4c342 100644 --- a/pr_agent/algo/utils.py +++ b/pr_agent/algo/utils.py @@ -878,6 +878,7 @@ def get_max_tokens(model): elif settings.config.custom_model_max_tokens > 0: max_tokens_model = settings.config.custom_model_max_tokens else: + get_logger().error(f"Model {model} is not defined in MAX_TOKENS in ./pr_agent/algo/__init__.py and no custom_model_max_tokens is set") raise Exception(f"Ensure {model} is defined in MAX_TOKENS in ./pr_agent/algo/__init__.py or set a positive value for it in config.custom_model_max_tokens") if settings.config.max_model_tokens and settings.config.max_model_tokens > 0: diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index edb5296f0b..02bc7311bd 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -7,8 +7,8 @@ [config] # models model="o4-mini" -fallback_models=["gpt-4o-2024-11-20"] -#model_weak="gpt-4o-mini-2024-07-18" # optional, a weaker model to use for some easier tasks +fallback_models=["gpt-4.1"] +#model_weak="gpt-4o" # optional, a weaker model to use for some easier tasks # CLI git_provider="github" publish_output=true From a434d0af9bb229531d067f96e6d6dd78d489196e Mon Sep 17 00:00:00 2001 From: "benedict.lee" Date: Mon, 21 Apr 2025 16:28:42 +0900 Subject: [PATCH 129/365] Improve comment thread retrieval by using in_reply_to_id instead of line numbers --- pr_agent/git_providers/github_provider.py | 38 +++++++++++++---------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/pr_agent/git_providers/github_provider.py b/pr_agent/git_providers/github_provider.py index 80581c6147..2e3a8bf0fb 100644 --- a/pr_agent/git_providers/github_provider.py +++ b/pr_agent/git_providers/github_provider.py @@ -431,36 +431,40 @@ def publish_inline_comments(self, comments: list[dict], disable_fallback: bool = def get_review_thread_comments(self, comment_id: int) -> list[dict]: """ - Retrieves all comments in the same line as the given comment. + Retrieves all comments in the same thread as the given comment. Args: comment_id: Review comment ID Returns: - List of comments on the same line + List of comments in the same thread """ try: - # Get the original comment to find its location - comment = self.pr.get_comment(comment_id) - if not comment: - return [] - - # Extract file path and line number - file_path = comment.path - line_number = comment.raw_data["line"] if "line" in comment.raw_data else comment.raw_data.get("original_line") - - # Get all comments + # Fetch all comments with a single API call all_comments = list(self.pr.get_comments()) - # Filter comments on the same line of the same file + # Find the target comment by ID + target_comment = next((c for c in all_comments if c.id == comment_id), None) + if not target_comment: + return [] + + # First, identify if this is a reply to another comment + root_comment_id = target_comment.raw_data.get("in_reply_to_id", target_comment.id) + if root_comment_id != target_comment.id: + # If this is a reply, find the root comment + root_comment = next((c for c in all_comments if c.id == root_comment_id), None) + if root_comment: + target_comment = root_comment + + # Build the thread - include the root comment and all replies to it thread_comments = [ - c for c in all_comments - if c.path == file_path and (c.raw_data.get("line") == line_number or c.raw_data.get("original_line") == line_number) + c for c in all_comments if + c.id == target_comment.id or c.raw_data.get("in_reply_to_id") == target_comment.id ] - + # Sort chronologically thread_comments.sort(key=lambda c: c.created_at) - + return thread_comments except Exception as e: From e11c2e1c7f7a001e0e2f94fa46aa6437d57a2b33 Mon Sep 17 00:00:00 2001 From: "benedict.lee" Date: Mon, 21 Apr 2025 16:30:27 +0900 Subject: [PATCH 130/365] Reorganize imports according to Python conventions --- pr_agent/tools/pr_line_questions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/tools/pr_line_questions.py b/pr_agent/tools/pr_line_questions.py index 2a97f9f561..073e03eb28 100644 --- a/pr_agent/tools/pr_line_questions.py +++ b/pr_agent/tools/pr_line_questions.py @@ -14,9 +14,9 @@ from pr_agent.config_loader import get_settings from pr_agent.git_providers import get_git_provider from pr_agent.git_providers.git_provider import get_main_pr_language +from pr_agent.git_providers.github_provider import GithubProvider from pr_agent.log import get_logger from pr_agent.servers.help import HelpMessage -from pr_agent.git_providers.github_provider import GithubProvider class PR_LineQuestions: def __init__(self, pr_url: str, args=None, ai_handler: partial[BaseAiHandler,] = LiteLLMAIHandler): From 8b4bf49f1cc9ca97dc01782af4636e9ec40d0aac Mon Sep 17 00:00:00 2001 From: "benedict.lee" Date: Mon, 21 Apr 2025 16:50:37 +0900 Subject: [PATCH 131/365] Improve conversation history handling and prompts for line questions --- .../settings/pr_line_questions_prompts.toml | 7 +++++- pr_agent/tools/pr_line_questions.py | 22 ++++++++++++------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/pr_agent/settings/pr_line_questions_prompts.toml b/pr_agent/settings/pr_line_questions_prompts.toml index 853a797528..2460831388 100644 --- a/pr_agent/settings/pr_line_questions_prompts.toml +++ b/pr_agent/settings/pr_line_questions_prompts.toml @@ -49,7 +49,12 @@ Previous discussion on this code: ====== {{ conversation_history|trim }} ====== -Use this prior discussion context to provide a consistent and informed answer. + +Consider this conversation history (format: "Username: Message"). When responding: +- Maintain consistency with previous technical explanations +- Address unresolved issues from earlier discussions +- Build upon existing knowledge without contradictions +- Incorporate relevant context while focusing on the current question {%- endif %} A question about the selected lines: diff --git a/pr_agent/tools/pr_line_questions.py b/pr_agent/tools/pr_line_questions.py index 073e03eb28..c5cc234c49 100644 --- a/pr_agent/tools/pr_line_questions.py +++ b/pr_agent/tools/pr_line_questions.py @@ -60,7 +60,8 @@ async def run(self): # set conversation history if enabled # currently only supports GitHub provider if get_settings().pr_questions.use_conversation_history and isinstance(self.git_provider, GithubProvider): - self._load_conversation_history() + conversation_history = self._load_conversation_history() + self.vars["conversation_history"] = conversation_history self.patch_with_lines = "" ask_diff = get_settings().get('ask_diff_hunk', "") @@ -99,18 +100,20 @@ async def run(self): return "" - def _load_conversation_history(self): - """generate conversation history from the code review thread""" - # set conversation history to empty string - self.vars["conversation_history"] = "" + def _load_conversation_history(self) -> str: + """Generate conversation history from the code review thread + Returns: + str: The formatted conversation history + """ comment_id = get_settings().get('comment_id', '') file_path = get_settings().get('file_name', '') line_number = get_settings().get('line_end', '') # early return if any required parameter is missing if not all([comment_id, file_path, line_number]): - return + get_logger().error("Missing required parameters for conversation history") + return "" try: # retrieve thread comments @@ -129,13 +132,16 @@ def _load_conversation_history(self): author = user.login if hasattr(user, 'login') else 'Unknown' conversation_history.append(f"{author}: {body}") - # transform and save conversation history + # transform conversation history to string if conversation_history: - self.vars["conversation_history"] = "\n\n".join(conversation_history) get_logger().info(f"Loaded {len(conversation_history)} comments from the code review thread") + return "\n\n".join(conversation_history) + + return "" except Exception as e: get_logger().error(f"Error processing conversation history, error: {e}") + return "" async def _get_prediction(self, model: str): variables = copy.deepcopy(self.vars) From 9906ec3687dbfc2666731ab45c5308059f428a68 Mon Sep 17 00:00:00 2001 From: "benedict.lee" Date: Mon, 21 Apr 2025 17:14:36 +0900 Subject: [PATCH 132/365] Improve conversation history formatting with numbered comments --- .../settings/pr_line_questions_prompts.toml | 2 +- pr_agent/tools/pr_line_questions.py | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/pr_agent/settings/pr_line_questions_prompts.toml b/pr_agent/settings/pr_line_questions_prompts.toml index 2460831388..93cdab868b 100644 --- a/pr_agent/settings/pr_line_questions_prompts.toml +++ b/pr_agent/settings/pr_line_questions_prompts.toml @@ -50,7 +50,7 @@ Previous discussion on this code: {{ conversation_history|trim }} ====== -Consider this conversation history (format: "Username: Message"). When responding: +Consider this conversation history (format: "N. Username: Message", where numbers indicate the comment order). When responding: - Maintain consistency with previous technical explanations - Address unresolved issues from earlier discussions - Build upon existing knowledge without contradictions diff --git a/pr_agent/tools/pr_line_questions.py b/pr_agent/tools/pr_line_questions.py index c5cc234c49..f373a4a11f 100644 --- a/pr_agent/tools/pr_line_questions.py +++ b/pr_agent/tools/pr_line_questions.py @@ -119,8 +119,8 @@ def _load_conversation_history(self) -> str: # retrieve thread comments thread_comments = self.git_provider.get_review_thread_comments(comment_id) - # generate conversation history - conversation_history = [] + # filter and prepare comments + filtered_comments = [] for comment in thread_comments: body = getattr(comment, 'body', '') @@ -130,12 +130,17 @@ def _load_conversation_history(self) -> str: user = comment.user author = user.login if hasattr(user, 'login') else 'Unknown' - conversation_history.append(f"{author}: {body}") + filtered_comments.append((author, body)) - # transform conversation history to string - if conversation_history: - get_logger().info(f"Loaded {len(conversation_history)} comments from the code review thread") - return "\n\n".join(conversation_history) + # transform conversation history to string using the same pattern as get_commit_messages + if filtered_comments: + comment_count = len(filtered_comments) + get_logger().info(f"Loaded {comment_count} comments from the code review thread") + + # Format as numbered list, similar to get_commit_messages + conversation_history_str = "\n".join([f"{i + 1}. {author}: {body}" + for i, (author, body) in enumerate(filtered_comments)]) + return conversation_history_str return "" From fef0bc6146ffc43fabc56b9c8c67951442ad5a64 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Tue, 22 Apr 2025 08:09:49 +0300 Subject: [PATCH 133/365] Update Qodo Merge documentation with detailed licensing information --- docs/docs/installation/qodo_merge.md | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/docs/docs/installation/qodo_merge.md b/docs/docs/installation/qodo_merge.md index 3bdcba72fa..a5ce0547a2 100644 --- a/docs/docs/installation/qodo_merge.md +++ b/docs/docs/installation/qodo_merge.md @@ -1,9 +1,21 @@ Qodo Merge is a versatile application compatible with GitHub, GitLab, and BitBucket, hosted by QodoAI. See [here](https://qodo-merge-docs.qodo.ai/overview/pr_agent_pro/) for more details about the benefits of using Qodo Merge. -A complimentary two-week trial is provided to all new users (with three additional grace usages). Following the trial period, user licenses (seats) are required for continued access. -To purchase user licenses, please visit our [pricing page](https://www.qodo.ai/pricing/). -Once subscribed, users can seamlessly deploy the application across any of their code repositories. +## Trial Period and Licensing + +### Cloud users with 'Teams' accounts + +A complimentary two-week trial is provided to all new users (with three additional grace usages). When the trial period ends, users will stop receiving feedback from Qodo Merge. + +Following the trial period, user licenses (seats) are required for continued access. Each user requires an individual seat license. +After purchasing seats, the team owner can assign them to specific users through the management portal. + +With an assigned seat, users can seamlessly deploy the application across any of their code repositories. + +### 'Enterprise' users + +For users with 'Enterprise' accounts, please [contact](https://www.qodo.ai/contact/#pricing) us to initiate a trial period, and to discuss pricing and licensing options. + ## Install Qodo Merge for GitHub From 581f35db99e7cc3f6654af14f5d5a6d3a496bb4b Mon Sep 17 00:00:00 2001 From: mrT23 Date: Tue, 22 Apr 2025 08:12:40 +0300 Subject: [PATCH 134/365] Update Qodo Merge documentation with detailed licensing information --- docs/docs/installation/qodo_merge.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/installation/qodo_merge.md b/docs/docs/installation/qodo_merge.md index a5ce0547a2..930127226b 100644 --- a/docs/docs/installation/qodo_merge.md +++ b/docs/docs/installation/qodo_merge.md @@ -3,7 +3,7 @@ See [here](https://qodo-merge-docs.qodo.ai/overview/pr_agent_pro/) for more deta ## Trial Period and Licensing -### Cloud users with 'Teams' accounts +### Cloud users with Teams accounts A complimentary two-week trial is provided to all new users (with three additional grace usages). When the trial period ends, users will stop receiving feedback from Qodo Merge. @@ -12,7 +12,7 @@ After purchasing seats, the team owner can assign them to specific users through With an assigned seat, users can seamlessly deploy the application across any of their code repositories. -### 'Enterprise' users +### Enterprise users For users with 'Enterprise' accounts, please [contact](https://www.qodo.ai/contact/#pricing) us to initiate a trial period, and to discuss pricing and licensing options. From 8a45e41749967b0d9d64d8393fcfe344fe5570d9 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Tue, 22 Apr 2025 08:14:53 +0300 Subject: [PATCH 135/365] Update Qodo Merge documentation with consistent heading capitalization --- docs/docs/installation/qodo_merge.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/docs/installation/qodo_merge.md b/docs/docs/installation/qodo_merge.md index 930127226b..92634cacb0 100644 --- a/docs/docs/installation/qodo_merge.md +++ b/docs/docs/installation/qodo_merge.md @@ -3,7 +3,7 @@ See [here](https://qodo-merge-docs.qodo.ai/overview/pr_agent_pro/) for more deta ## Trial Period and Licensing -### Cloud users with Teams accounts +### Cloud Users with Teams Account A complimentary two-week trial is provided to all new users (with three additional grace usages). When the trial period ends, users will stop receiving feedback from Qodo Merge. @@ -12,9 +12,9 @@ After purchasing seats, the team owner can assign them to specific users through With an assigned seat, users can seamlessly deploy the application across any of their code repositories. -### Enterprise users +### Enterprise Account -For users with 'Enterprise' accounts, please [contact](https://www.qodo.ai/contact/#pricing) us to initiate a trial period, and to discuss pricing and licensing options. +For users who require an Enterprise account, please [contact](https://www.qodo.ai/contact/#pricing) us to initiate a trial period, and to discuss pricing and licensing options. ## Install Qodo Merge for GitHub From 15b2b5767773dcf9d894b75cb73da422e2b5a1ee Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Tue, 22 Apr 2025 08:49:30 +0300 Subject: [PATCH 136/365] Uncomment search plugin in mkdocs configuration --- docs/mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index a7d9085d2c..0021555bdd 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -107,7 +107,7 @@ theme: plugins: - social -# - search + - search - glightbox extra: From fe0d5df145adab840b0225ccbffacd05419ca3b9 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Tue, 22 Apr 2025 10:56:53 +0300 Subject: [PATCH 137/365] Improve documentation formatting and navigation features --- docs/docs/installation/qodo_merge.md | 2 +- docs/docs/usage-guide/index.md | 28 ++++++++++++++-------------- docs/mkdocs.yml | 6 +++--- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/docs/installation/qodo_merge.md b/docs/docs/installation/qodo_merge.md index 92634cacb0..607d3f8aa6 100644 --- a/docs/docs/installation/qodo_merge.md +++ b/docs/docs/installation/qodo_merge.md @@ -14,7 +14,7 @@ With an assigned seat, users can seamlessly deploy the application across any of ### Enterprise Account -For users who require an Enterprise account, please [contact](https://www.qodo.ai/contact/#pricing) us to initiate a trial period, and to discuss pricing and licensing options. +For organizations who require an Enterprise account, please [contact](https://www.qodo.ai/contact/#pricing) us to initiate a trial period, and to discuss pricing and licensing options. ## Install Qodo Merge for GitHub diff --git a/docs/docs/usage-guide/index.md b/docs/docs/usage-guide/index.md index 75de694403..8de093affd 100644 --- a/docs/docs/usage-guide/index.md +++ b/docs/docs/usage-guide/index.md @@ -1,25 +1,25 @@ # Usage guide -This page provides a detailed guide on how to use Qodo Merge. +This section provides a detailed guide on how to use Qodo Merge. It includes information on how to adjust Qodo Merge configurations, define which tools will run automatically, and other advanced configurations. - [Introduction](./introduction.md) - [Enabling a Wiki](./enabling_a_wiki) - [Configuration File](./configuration_options.md) - [Usage and Automation](./automations_and_usage.md) - - [Local Repo (CLI)](./automations_and_usage.md#local-repo-cli) - - [Online Usage](./automations_and_usage.md#online-usage) - - [GitHub App](./automations_and_usage.md#github-app) - - [GitHub Action](./automations_and_usage.md#github-action) - - [GitLab Webhook](./automations_and_usage.md#gitlab-webhook) - - [BitBucket App](./automations_and_usage.md#bitbucket-app) - - [Azure DevOps Provider](./automations_and_usage.md#azure-devops-provider) + - [Local Repo (CLI)](./automations_and_usage.md#local-repo-cli) + - [Online Usage](./automations_and_usage.md#online-usage) + - [GitHub App](./automations_and_usage.md#github-app) + - [GitHub Action](./automations_and_usage.md#github-action) + - [GitLab Webhook](./automations_and_usage.md#gitlab-webhook) + - [BitBucket App](./automations_and_usage.md#bitbucket-app) + - [Azure DevOps Provider](./automations_and_usage.md#azure-devops-provider) - [Managing Mail Notifications](./mail_notifications.md) - [Changing a Model](./changing_a_model.md) -- [Additional Configurations Walkthrough](./additional_configurations.md) - - [Ignoring files from analysis](./additional_configurations.md#ignoring-files-from-analysis) - - [Extra instructions](./additional_configurations.md#extra-instructions) - - [Working with large PRs](./additional_configurations.md#working-with-large-prs) - - [Changing a model](./additional_configurations.md#changing-a-model) - - [Patch Extra Lines](./additional_configurations.md#patch-extra-lines) +- [Additional Configurations](./additional_configurations.md) + - [Ignoring files from analysis](./additional_configurations.md#ignoring-files-from-analysis) + - [Extra instructions](./additional_configurations.md#extra-instructions) + - [Working with large PRs](./additional_configurations.md#working-with-large-prs) + - [Changing a model](./additional_configurations.md#changing-a-model) + - [Patch Extra Lines](./additional_configurations.md#patch-extra-lines) - [Qodo Merge Models](./qodo_merge_models) diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index a7d9085d2c..7b9c967bec 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -77,8 +77,8 @@ theme: - navigation.top - navigation.tracking - navigation.indexes -# - search.suggest -# - search.highlight + - search.suggest + - search.highlight - content.tabs.link - content.code.annotation - content.code.copy @@ -107,7 +107,7 @@ theme: plugins: - social -# - search + - search - glightbox extra: From 1aa0186f5ca51a056bc379e6b9072657b239a364 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Tue, 22 Apr 2025 14:19:54 +0300 Subject: [PATCH 138/365] Improve field descriptions for code suggestion model clarity and brevity --- .../pr_code_suggestions_prompts_not_decoupled.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pr_agent/settings/code_suggestions/pr_code_suggestions_prompts_not_decoupled.toml b/pr_agent/settings/code_suggestions/pr_code_suggestions_prompts_not_decoupled.toml index 3d68b36b0c..405237cfd1 100644 --- a/pr_agent/settings/code_suggestions/pr_code_suggestions_prompts_not_decoupled.toml +++ b/pr_agent/settings/code_suggestions/pr_code_suggestions_prompts_not_decoupled.toml @@ -77,10 +77,10 @@ The output must be a YAML object equivalent to type $PRCodeSuggestions, accordin class CodeSuggestion(BaseModel): relevant_file: str = Field(description="Full path of the relevant file") language: str = Field(description="Programming language used by the relevant file") - existing_code: str = Field(description="A short code snippet from the final state of the PR diff that the suggestion will address. Select only the span of code that will be modified - without surrounding unchanged code. Preserve all indentation, newlines, and original formatting. Use ellipsis (...) for brevity if needed.") + existing_code: str = Field(description="A short code snippet from the final state of the PR diff that the suggestion will address. Select only the span of code that will be modified - without surrounding unchanged code. Preserve all indentation, newlines, and original formatting. Show the code snippet without the '+'/'-'/' ' prefixes. When providing suggestions for long code sections, shorten the presented code with ellipsis (...) for brevity where possible.") suggestion_content: str = Field(description="An actionable suggestion to enhance, improve or fix the new code introduced in the PR. Don't present here actual code snippets, just the suggestion. Be short and concise") improved_code: str = Field(description="A refined code snippet that replaces the 'existing_code' snippet after implementing the suggestion.") - one_sentence_summary: str = Field(description="A concise, single-sentence overview (up to 6 words) of the suggested improvement. Focus on the 'what'. Be general, and avoid method or variable names.") + one_sentence_summary: str = Field(description="A single-sentence overview (up to 6 words) of the suggestion. Focus on the 'what'. Be general, and avoid mentioning method or variable names.") {%- if not focus_only_on_problems %} label: str = Field(description="A single, descriptive label that best characterizes the suggestion type. Possible labels include 'security', 'possible bug', 'possible issue', 'performance', 'enhancement', 'best practice', 'maintainability', 'typo'. Other relevant labels are also acceptable.") {%- else %} From 7d17ed5b7f9bf8b580caf5479a0ef761c98940d3 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 23 Apr 2025 21:15:00 +0300 Subject: [PATCH 139/365] fix linting --- docs/docs/tools/improve.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/tools/improve.md b/docs/docs/tools/improve.md index ceb4dee3e4..522a5fe319 100644 --- a/docs/docs/tools/improve.md +++ b/docs/docs/tools/improve.md @@ -493,8 +493,8 @@ Note: Chunking is primarily relevant for large PRs. For most PRs (up to 500 line - **AI Limitations:** AI models for code are getting better and better, but they are not flawless. Not all the suggestions will be perfect, and a user should not accept all of them automatically. Critical reading and judgment are required. Mistakes of the AI are rare but can happen, and it is usually quite easy for a human to spot them. - **Purpose of Suggestions:** - - **Self-reflection:** The suggestions aim to enable developers to _self-reflect_ and improve their pull requests. This process can help to identify blind spots, uncover missed edge cases, and enhance code readability and coherency. Even when a specific code suggestion isn't suitable, the underlying issue it highlights often reveals something important that might deserve attention. - - **Bug detection:** The suggestions also alert on any _critical bugs_ that may have been identified during the analysis. This provides an additional safety net to catch potential issues before they make it into production. It's perfectly acceptable to implement only the suggestions you find valuable for your specific context. + - **Self-reflection:** The suggestions aim to enable developers to _self-reflect_ and improve their pull requests. This process can help to identify blind spots, uncover missed edge cases, and enhance code readability and coherency. Even when a specific code suggestion isn't suitable, the underlying issue it highlights often reveals something important that might deserve attention. + - **Bug detection:** The suggestions also alert on any _critical bugs_ that may have been identified during the analysis. This provides an additional safety net to catch potential issues before they make it into production. It's perfectly acceptable to implement only the suggestions you find valuable for your specific context. - **Hierarchy:** Presenting the suggestions in a structured hierarchical table enables the user to _quickly_ understand them, and to decide which ones are relevant and which are not. - **Customization:** To guide the model to suggestions that are more relevant to the specific needs of your project, we recommend to use the [`extra_instructions`](https://qodo-merge-docs.qodo.ai/tools/improve/#extra-instructions-and-best-practices) and [`best practices`](https://qodo-merge-docs.qodo.ai/tools/improve/#best-practices) fields. - **Interactive usage:** The interactive [PR chat](https://qodo-merge-docs.qodo.ai/chrome-extension/) also provides an easy way to get more tailored suggestions and feedback from the AI model. From 29d4fe510e930fd04e6feeec546f4189da06420d Mon Sep 17 00:00:00 2001 From: Benedict Lee Date: Thu, 24 Apr 2025 11:21:49 +0900 Subject: [PATCH 140/365] Improve get_review_thread_comments method implementation Co-authored-by: ofir-frd <85901822+ofir-frd@users.noreply.github.com> --- pr_agent/git_providers/github_provider.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/pr_agent/git_providers/github_provider.py b/pr_agent/git_providers/github_provider.py index 2e3a8bf0fb..daa0c84b28 100644 --- a/pr_agent/git_providers/github_provider.py +++ b/pr_agent/git_providers/github_provider.py @@ -448,19 +448,13 @@ def get_review_thread_comments(self, comment_id: int) -> list[dict]: if not target_comment: return [] - # First, identify if this is a reply to another comment + # Get root comment id root_comment_id = target_comment.raw_data.get("in_reply_to_id", target_comment.id) - if root_comment_id != target_comment.id: - # If this is a reply, find the root comment - root_comment = next((c for c in all_comments if c.id == root_comment_id), None) - if root_comment: - target_comment = root_comment - # Build the thread - include the root comment and all replies to it thread_comments = [ - c for c in all_comments if - c.id == target_comment.id or c.raw_data.get("in_reply_to_id") == target_comment.id - ] + c for c in all_comments if + c.id == root_comment_id or c.raw_data.get("in_reply_to_id") == root_comment_id +] # Sort chronologically thread_comments.sort(key=lambda c: c.created_at) From ddb94ec9b40d3cefaf51c40a0b38f4c0d6428483 Mon Sep 17 00:00:00 2001 From: Benedict Lee Date: Thu, 24 Apr 2025 11:22:43 +0900 Subject: [PATCH 141/365] mprove get_review_thread_comments method implementation Co-authored-by: ofir-frd <85901822+ofir-frd@users.noreply.github.com> --- pr_agent/git_providers/github_provider.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pr_agent/git_providers/github_provider.py b/pr_agent/git_providers/github_provider.py index daa0c84b28..a526f594ec 100644 --- a/pr_agent/git_providers/github_provider.py +++ b/pr_agent/git_providers/github_provider.py @@ -456,8 +456,6 @@ def get_review_thread_comments(self, comment_id: int) -> list[dict]: c.id == root_comment_id or c.raw_data.get("in_reply_to_id") == root_comment_id ] - # Sort chronologically - thread_comments.sort(key=lambda c: c.created_at) return thread_comments From c35942c12ba92a03e7fca1c89a0d72d322adffd2 Mon Sep 17 00:00:00 2001 From: Benedict Lee Date: Thu, 24 Apr 2025 11:23:16 +0900 Subject: [PATCH 142/365] mprove get_review_thread_comments method implementation Co-authored-by: ofir-frd <85901822+ofir-frd@users.noreply.github.com> --- pr_agent/git_providers/github_provider.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/git_providers/github_provider.py b/pr_agent/git_providers/github_provider.py index a526f594ec..485d5afeca 100644 --- a/pr_agent/git_providers/github_provider.py +++ b/pr_agent/git_providers/github_provider.py @@ -460,7 +460,7 @@ def get_review_thread_comments(self, comment_id: int) -> list[dict]: return thread_comments except Exception as e: - get_logger().warning(f"Failed to get review comments for comment {comment_id}, error: {e}") + get_logger().exception(f"Failed to get review comments for an inline ask command", artifact={"comment_id": comment_id, "error": e}) return [] def _publish_inline_comments_fallback_with_verification(self, comments: list[dict]): From 6ceea2b134fa0c7782dbe2722c6b06d046916093 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Thu, 24 Apr 2025 08:28:23 +0300 Subject: [PATCH 143/365] Clarify Qodo Merge's exclusive features and bot handling behavior --- docs/docs/overview/pr_agent_pro.md | 2 +- docs/docs/usage-guide/additional_configurations.md | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/docs/overview/pr_agent_pro.md b/docs/docs/overview/pr_agent_pro.md index 02a6081fb7..5ec5ef8ee7 100644 --- a/docs/docs/overview/pr_agent_pro.md +++ b/docs/docs/overview/pr_agent_pro.md @@ -15,7 +15,7 @@ Qodo Merge is designed for companies and teams that require additional features ### Additional features -Here are some of the additional features and capabilities that Qodo Merge offers: +Here are some of the additional features and capabilities that Qodo Merge offers, and are not available in the open-source version of PR-Agent: | Feature | Description | | -------------------------------------------------------------------------------------------------------------------- |--------------------------------------------------------------------------------------------------------------------------------------------------------| diff --git a/docs/docs/usage-guide/additional_configurations.md b/docs/docs/usage-guide/additional_configurations.md index bbdc753e21..d2df16afe9 100644 --- a/docs/docs/usage-guide/additional_configurations.md +++ b/docs/docs/usage-guide/additional_configurations.md @@ -216,7 +216,7 @@ Where the `ignore_pr_labels` is a list of labels that when present in the PR, th ### Ignoring PRs from specific users -Qodo Merge automatically identifies and ignores pull requests created by bots using: +Qodo Merge tries to automatically identifies and ignores pull requests created by bots using: - GitHub's native bot detection system - Name-based pattern matching @@ -234,3 +234,6 @@ ignore_pr_authors = ["my-special-bot-user", ...] ``` Where the `ignore_pr_authors` is a list of usernames that you want to ignore. + +!!! note + There is one specific case where bots will receive an automatic response - when they generated a PR with a _failed test_. In that case, the [`ci_feedback`](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/) tool will be invoked. \ No newline at end of file From dedad94a204ebec181401dd70032a8cb5ebb3cc0 Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Thu, 24 Apr 2025 09:34:51 +0300 Subject: [PATCH 144/365] Fix code indentation in get_review_thread_comments method --- pr_agent/git_providers/github_provider.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pr_agent/git_providers/github_provider.py b/pr_agent/git_providers/github_provider.py index 485d5afeca..4f3a5ec71d 100644 --- a/pr_agent/git_providers/github_provider.py +++ b/pr_agent/git_providers/github_provider.py @@ -452,9 +452,9 @@ def get_review_thread_comments(self, comment_id: int) -> list[dict]: root_comment_id = target_comment.raw_data.get("in_reply_to_id", target_comment.id) # Build the thread - include the root comment and all replies to it thread_comments = [ - c for c in all_comments if - c.id == root_comment_id or c.raw_data.get("in_reply_to_id") == root_comment_id -] + c for c in all_comments if + c.id == root_comment_id or c.raw_data.get("in_reply_to_id") == root_comment_id + ] return thread_comments From 9df0f71d4acfbadc1e9efc82e841f95824d7bf05 Mon Sep 17 00:00:00 2001 From: "yuli.kamakura" Date: Thu, 24 Apr 2025 16:14:02 +0800 Subject: [PATCH 145/365] Update .gitignore and fix license format in pyproject.toml --- .gitignore | 1 + pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 6f39a88995..8da05107fd 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ build/ .DS_Store docs/.cache/ .qodo +poetry.lock diff --git a/pyproject.toml b/pyproject.toml index 71e61ff5f5..996870bb75 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,7 @@ description = "QodoAI PR-Agent aims to help efficiently review and handle pull r readme = "README.md" requires-python = ">=3.12" keywords = ["AI", "Agents", "Pull Request", "Automation", "Code Review"] -license = { name = "Apache 2.0", file = "LICENSE" } +license = "Apache-2.0" classifiers = [ "Intended Audience :: Developers", From 87532cf68c019717fbc9d712660e977950d98fc4 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Thu, 24 Apr 2025 14:59:58 +0300 Subject: [PATCH 146/365] Add documentation about preserving original PR descriptions in Qodo Merge --- docs/docs/tools/describe.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/docs/tools/describe.md b/docs/docs/tools/describe.md index 8dce7646fb..0f2c45a701 100644 --- a/docs/docs/tools/describe.md +++ b/docs/docs/tools/describe.md @@ -45,6 +45,17 @@ publish_labels = true - The `pr_commands` lists commands that will be executed automatically when a PR is opened. - The `[pr_description]` section contains the configurations for the `describe` tool you want to edit (if any). +## Preserving the original user description + +By default, Qodo Merge preserves your original PR description by placing it above the generated content. +This requires including your description during the initial PR creation. +Be aware that if you edit the description while the automated tool is running, a race condition may occur, potentially causing your original description to be lost. + +When updating PR descriptions, the `/describe` tool considers everything above the "PR Type" field as user content and will preserve it. +Everything below this marker is treated as previously auto-generated content and will be replaced. + +![Describe comment](https://codium.ai/images/pr_agent/pr_description_user_description.png){width=512} + ## Configuration options !!! example "Possible configurations" From 4a231f8933e34a9017c1b3bb197672d61ea8683c Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Thu, 24 Apr 2025 15:32:01 +0300 Subject: [PATCH 147/365] Add documentation for chat functionality in code suggestions --- docs/docs/tools/improve.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/docs/tools/improve.md b/docs/docs/tools/improve.md index 522a5fe319..45432e55a8 100644 --- a/docs/docs/tools/improve.md +++ b/docs/docs/tools/improve.md @@ -262,6 +262,19 @@ We advise users to apply critical analysis and judgment when implementing the pr In addition to mistakes (which may happen, but are rare), sometimes the presented code modification may serve more as an _illustrative example_ than a direct applicable solution. In such cases, we recommend prioritizing the suggestion's detailed description, using the diff snippet primarily as a supporting reference. + +### Chat on code suggestions + +If you set the following in your configuration file: + +```toml +[pr_code_suggestions] +enable_chat_in_code_suggestions = true +``` + +The Qodo Merge bot will automatically listen and respond to comments within code suggestion discussions that it has initiated, without requiring explicit tool calls. + + ### Dual publishing mode Our recommended approach for presenting code suggestions is through a [table](https://qodo-merge-docs.qodo.ai/tools/improve/#overview) (`--pr_code_suggestions.commitable_code_suggestions=false`). @@ -427,6 +440,10 @@ Note: Chunking is primarily relevant for large PRs. For most PRs (up to 500 line commitable_code_suggestions If set to true, the tool will display the suggestions as commitable code comments. Default is false. + + enable_chat_in_code_suggestions + If set to true, QM bot will interact with comments made on code changes it has proposed. Default is true. + dual_publishing_score_threshold Minimum score threshold for suggestions to be presented as commitable PR comments in addition to the table. Default is -1 (disabled). From 68737b643242fea4252417a13bee9c1cea460291 Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Thu, 24 Apr 2025 15:39:48 +0300 Subject: [PATCH 148/365] Add platform support indicators for chat on code suggestions feature --- docs/docs/tools/improve.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/docs/tools/improve.md b/docs/docs/tools/improve.md index 45432e55a8..41b23c17a5 100644 --- a/docs/docs/tools/improve.md +++ b/docs/docs/tools/improve.md @@ -265,6 +265,8 @@ In such cases, we recommend prioritizing the suggestion's detailed description, ### Chat on code suggestions +> `💎 feature` Platforms supported: GitHub, GitLab + If you set the following in your configuration file: ```toml From 4ff2bd176b15dcbbe962d8edf0796898874a6164 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Thu, 24 Apr 2025 18:05:48 +0300 Subject: [PATCH 149/365] Fix typo in Qodo Merge bot detection documentation --- docs/docs/usage-guide/additional_configurations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/usage-guide/additional_configurations.md b/docs/docs/usage-guide/additional_configurations.md index d2df16afe9..dfd3850fb4 100644 --- a/docs/docs/usage-guide/additional_configurations.md +++ b/docs/docs/usage-guide/additional_configurations.md @@ -216,7 +216,7 @@ Where the `ignore_pr_labels` is a list of labels that when present in the PR, th ### Ignoring PRs from specific users -Qodo Merge tries to automatically identifies and ignores pull requests created by bots using: +Qodo Merge tries to automatically identify and ignore pull requests created by bots using: - GitHub's native bot detection system - Name-based pattern matching From ff00e303cc7b2e875b2c29d081a9e76966cf4ed0 Mon Sep 17 00:00:00 2001 From: bananana0118 Date: Fri, 25 Apr 2025 14:31:56 +0900 Subject: [PATCH 150/365] docs: fix typo in documentation about config response_language from pr-agent.toml --- .../usage-guide/additional_configurations.md | 59 ++++++++----------- 1 file changed, 26 insertions(+), 33 deletions(-) diff --git a/docs/docs/usage-guide/additional_configurations.md b/docs/docs/usage-guide/additional_configurations.md index dfd3850fb4..662e986bfa 100644 --- a/docs/docs/usage-guide/additional_configurations.md +++ b/docs/docs/usage-guide/additional_configurations.md @@ -1,6 +1,6 @@ ## Show possible configurations -The possible configurations of Qodo Merge are stored in [here](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml){:target="_blank"}. +The possible configurations of Qodo Merge are stored in [here](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml){:target="\_blank"}. In the [tools](https://qodo-merge-docs.qodo.ai/tools/) page you can find explanations on how to use these configurations for each tool. To print all the available configurations as a comment on your PR, you can use the following command: @@ -28,8 +28,8 @@ In some cases, you may want to exclude specific files or directories from the an You can ignore files or folders using the following methods: -- `IGNORE.GLOB` -- `IGNORE.REGEX` +- `IGNORE.GLOB` +- `IGNORE.REGEX` which you can edit to ignore files or folders based on glob or regex patterns. @@ -64,38 +64,31 @@ All Qodo Merge tools have a parameter called `extra_instructions`, that enables ## Language Settings -The default response language for Qodo Merge is **U.S. English**. However, some development teams may prefer to display information in a different language. For example, your team's workflow might improve if PR descriptions and code suggestions are set to your country's native language. +The default response language for Qodo Merge is **U.S. English**. However, some development teams may prefer to display information in a different language. For example, your team's workflow might improve if PR descriptions and code suggestions are set to your country's native language. -To configure this, set the `response_language` parameter in the configuration file. This will prompt the model to respond in the specified language. Use a **standard locale code** based on [ISO 3166](https://en.wikipedia.org/wiki/ISO_3166) (country codes) and [ISO 639](https://en.wikipedia.org/wiki/ISO_639) (language codes) to define a language-country pair. See this [comprehensive list of locale codes](https://simplelocalize.io/data/locales/). +To configure this, set the `response_language` parameter in the configuration file. This will prompt the model to respond in the specified language. Use a **standard locale code** based on [ISO 3166](https://en.wikipedia.org/wiki/ISO_3166) (country codes) and [ISO 639](https://en.wikipedia.org/wiki/ISO_639) (language codes) to define a language-country pair. See this [comprehensive list of locale codes](https://simplelocalize.io/data/locales/). Example: ```toml [config] -response_language: "it-IT" +response_language= "it-IT" ``` This will set the response language globally for all the commands to Italian. > **Important:** Note that only dynamic text generated by the AI model is translated to the configured language. Static text such as labels and table headers that are not part of the AI models response will remain in US English. In addition, the model you are using must have good support for the specified language. -[//]: # (## Working with large PRs) - -[//]: # () -[//]: # (The default mode of CodiumAI is to have a single call per tool, using GPT-4, which has a token limit of 8000 tokens.) - -[//]: # (This mode provides a very good speed-quality-cost tradeoff, and can handle most PRs successfully.) - -[//]: # (When the PR is above the token limit, it employs a [PR Compression strategy](../core-abilities/index.md).) - -[//]: # () -[//]: # (However, for very large PRs, or in case you want to emphasize quality over speed and cost, there are two possible solutions:) - -[//]: # (1) [Use a model](https://qodo-merge-docs.qodo.ai/usage-guide/changing_a_model/) with larger context, like GPT-32K, or claude-100K. This solution will be applicable for all the tools.) - -[//]: # (2) For the `/improve` tool, there is an ['extended' mode](https://qodo-merge-docs.qodo.ai/tools/improve/) (`/improve --extended`),) - -[//]: # (which divides the PR into chunks, and processes each chunk separately. With this mode, regardless of the model, no compression will be done (but for large PRs, multiple model calls may occur)) +[//]: # "## Working with large PRs" +[//]: # +[//]: # "The default mode of CodiumAI is to have a single call per tool, using GPT-4, which has a token limit of 8000 tokens." +[//]: # "This mode provides a very good speed-quality-cost tradeoff, and can handle most PRs successfully." +[//]: # "When the PR is above the token limit, it employs a [PR Compression strategy](../core-abilities/index.md)." +[//]: # +[//]: # "However, for very large PRs, or in case you want to emphasize quality over speed and cost, there are two possible solutions:" +[//]: # "1) [Use a model](https://qodo-merge-docs.qodo.ai/usage-guide/changing_a_model/) with larger context, like GPT-32K, or claude-100K. This solution will be applicable for all the tools." +[//]: # "2) For the `/improve` tool, there is an ['extended' mode](https://qodo-merge-docs.qodo.ai/tools/improve/) (`/improve --extended`)," +[//]: # "which divides the PR into chunks, and processes each chunk separately. With this mode, regardless of the model, no compression will be done (but for large PRs, multiple model calls may occur)" ## Patch Extra Lines @@ -162,11 +155,11 @@ LANGSMITH_BASE_URL= Qodo Merge allows you to automatically ignore certain PRs based on various criteria: -- PRs with specific titles (using regex matching) -- PRs between specific branches (using regex matching) -- PRs not from specific folders -- PRs containing specific labels -- PRs opened by specific users +- PRs with specific titles (using regex matching) +- PRs between specific branches (using regex matching) +- PRs not from specific folders +- PRs containing specific labels +- PRs opened by specific users ### Ignoring PRs with specific titles @@ -218,13 +211,13 @@ Where the `ignore_pr_labels` is a list of labels that when present in the PR, th Qodo Merge tries to automatically identify and ignore pull requests created by bots using: -- GitHub's native bot detection system -- Name-based pattern matching +- GitHub's native bot detection system +- Name-based pattern matching While this detection is robust, it may not catch all cases, particularly when: -- Bots are registered as regular user accounts -- Bot names don't match common patterns +- Bots are registered as regular user accounts +- Bot names don't match common patterns To supplement the automatic bot detection, you can manually specify users to ignore. Add the following to your `configuration.toml` file to ignore PRs from specific users: @@ -236,4 +229,4 @@ ignore_pr_authors = ["my-special-bot-user", ...] Where the `ignore_pr_authors` is a list of usernames that you want to ignore. !!! note - There is one specific case where bots will receive an automatic response - when they generated a PR with a _failed test_. In that case, the [`ci_feedback`](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/) tool will be invoked. \ No newline at end of file +There is one specific case where bots will receive an automatic response - when they generated a PR with a _failed test_. In that case, the [`ci_feedback`](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/) tool will be invoked. From 9549289a719747b8e9d4474aae2b615d65e32961 Mon Sep 17 00:00:00 2001 From: bananana0118 Date: Fri, 25 Apr 2025 14:51:15 +0900 Subject: [PATCH 151/365] docs: fix typo in response_language setting --- .../usage-guide/additional_configurations.md | 57 +++++++++++-------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/docs/docs/usage-guide/additional_configurations.md b/docs/docs/usage-guide/additional_configurations.md index 662e986bfa..5938d71822 100644 --- a/docs/docs/usage-guide/additional_configurations.md +++ b/docs/docs/usage-guide/additional_configurations.md @@ -1,6 +1,6 @@ ## Show possible configurations -The possible configurations of Qodo Merge are stored in [here](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml){:target="\_blank"}. +The possible configurations of Qodo Merge are stored in [here](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml){:target="_blank"}. In the [tools](https://qodo-merge-docs.qodo.ai/tools/) page you can find explanations on how to use these configurations for each tool. To print all the available configurations as a comment on your PR, you can use the following command: @@ -28,8 +28,8 @@ In some cases, you may want to exclude specific files or directories from the an You can ignore files or folders using the following methods: -- `IGNORE.GLOB` -- `IGNORE.REGEX` +- `IGNORE.GLOB` +- `IGNORE.REGEX` which you can edit to ignore files or folders based on glob or regex patterns. @@ -64,9 +64,9 @@ All Qodo Merge tools have a parameter called `extra_instructions`, that enables ## Language Settings -The default response language for Qodo Merge is **U.S. English**. However, some development teams may prefer to display information in a different language. For example, your team's workflow might improve if PR descriptions and code suggestions are set to your country's native language. +The default response language for Qodo Merge is **U.S. English**. However, some development teams may prefer to display information in a different language. For example, your team's workflow might improve if PR descriptions and code suggestions are set to your country's native language. -To configure this, set the `response_language` parameter in the configuration file. This will prompt the model to respond in the specified language. Use a **standard locale code** based on [ISO 3166](https://en.wikipedia.org/wiki/ISO_3166) (country codes) and [ISO 639](https://en.wikipedia.org/wiki/ISO_639) (language codes) to define a language-country pair. See this [comprehensive list of locale codes](https://simplelocalize.io/data/locales/). +To configure this, set the `response_language` parameter in the configuration file. This will prompt the model to respond in the specified language. Use a **standard locale code** based on [ISO 3166](https://en.wikipedia.org/wiki/ISO_3166) (country codes) and [ISO 639](https://en.wikipedia.org/wiki/ISO_639) (language codes) to define a language-country pair. See this [comprehensive list of locale codes](https://simplelocalize.io/data/locales/). Example: @@ -79,16 +79,23 @@ This will set the response language globally for all the commands to Italian. > **Important:** Note that only dynamic text generated by the AI model is translated to the configured language. Static text such as labels and table headers that are not part of the AI models response will remain in US English. In addition, the model you are using must have good support for the specified language. -[//]: # "## Working with large PRs" -[//]: # -[//]: # "The default mode of CodiumAI is to have a single call per tool, using GPT-4, which has a token limit of 8000 tokens." -[//]: # "This mode provides a very good speed-quality-cost tradeoff, and can handle most PRs successfully." -[//]: # "When the PR is above the token limit, it employs a [PR Compression strategy](../core-abilities/index.md)." -[//]: # -[//]: # "However, for very large PRs, or in case you want to emphasize quality over speed and cost, there are two possible solutions:" -[//]: # "1) [Use a model](https://qodo-merge-docs.qodo.ai/usage-guide/changing_a_model/) with larger context, like GPT-32K, or claude-100K. This solution will be applicable for all the tools." -[//]: # "2) For the `/improve` tool, there is an ['extended' mode](https://qodo-merge-docs.qodo.ai/tools/improve/) (`/improve --extended`)," -[//]: # "which divides the PR into chunks, and processes each chunk separately. With this mode, regardless of the model, no compression will be done (but for large PRs, multiple model calls may occur)" +[//]: # (## Working with large PRs) + +[//]: # () +[//]: # (The default mode of CodiumAI is to have a single call per tool, using GPT-4, which has a token limit of 8000 tokens.) + +[//]: # (This mode provides a very good speed-quality-cost tradeoff, and can handle most PRs successfully.) + +[//]: # (When the PR is above the token limit, it employs a [PR Compression strategy](../core-abilities/index.md).) + +[//]: # () +[//]: # (However, for very large PRs, or in case you want to emphasize quality over speed and cost, there are two possible solutions:) + +[//]: # (1) [Use a model](https://qodo-merge-docs.qodo.ai/usage-guide/changing_a_model/) with larger context, like GPT-32K, or claude-100K. This solution will be applicable for all the tools.) + +[//]: # (2) For the `/improve` tool, there is an ['extended' mode](https://qodo-merge-docs.qodo.ai/tools/improve/) (`/improve --extended`),) + +[//]: # (which divides the PR into chunks, and processes each chunk separately. With this mode, regardless of the model, no compression will be done (but for large PRs, multiple model calls may occur)) ## Patch Extra Lines @@ -155,11 +162,11 @@ LANGSMITH_BASE_URL= Qodo Merge allows you to automatically ignore certain PRs based on various criteria: -- PRs with specific titles (using regex matching) -- PRs between specific branches (using regex matching) -- PRs not from specific folders -- PRs containing specific labels -- PRs opened by specific users +- PRs with specific titles (using regex matching) +- PRs between specific branches (using regex matching) +- PRs not from specific folders +- PRs containing specific labels +- PRs opened by specific users ### Ignoring PRs with specific titles @@ -211,13 +218,13 @@ Where the `ignore_pr_labels` is a list of labels that when present in the PR, th Qodo Merge tries to automatically identify and ignore pull requests created by bots using: -- GitHub's native bot detection system -- Name-based pattern matching +- GitHub's native bot detection system +- Name-based pattern matching While this detection is robust, it may not catch all cases, particularly when: -- Bots are registered as regular user accounts -- Bot names don't match common patterns +- Bots are registered as regular user accounts +- Bot names don't match common patterns To supplement the automatic bot detection, you can manually specify users to ignore. Add the following to your `configuration.toml` file to ignore PRs from specific users: @@ -229,4 +236,4 @@ ignore_pr_authors = ["my-special-bot-user", ...] Where the `ignore_pr_authors` is a list of usernames that you want to ignore. !!! note -There is one specific case where bots will receive an automatic response - when they generated a PR with a _failed test_. In that case, the [`ci_feedback`](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/) tool will be invoked. + There is one specific case where bots will receive an automatic response - when they generated a PR with a _failed test_. In that case, the [`ci_feedback`](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/) tool will be invoked. \ No newline at end of file From bb8e606f93989ab76871bf747a399ca1078c3c58 Mon Sep 17 00:00:00 2001 From: bananana0118 Date: Fri, 25 Apr 2025 14:55:31 +0900 Subject: [PATCH 152/365] docs: fix spacing after equals sign in TOML example --- docs/docs/usage-guide/additional_configurations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/usage-guide/additional_configurations.md b/docs/docs/usage-guide/additional_configurations.md index 5938d71822..c345e0c69f 100644 --- a/docs/docs/usage-guide/additional_configurations.md +++ b/docs/docs/usage-guide/additional_configurations.md @@ -72,7 +72,7 @@ Example: ```toml [config] -response_language= "it-IT" +response_language = "it-IT" ``` This will set the response language globally for all the commands to Italian. From 60a887ffe1f49bb8db5039705ff5b0b8462d1acb Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sun, 27 Apr 2025 08:42:28 +0300 Subject: [PATCH 153/365] Improve code suggestion prompts for clarity, accuracy, and evaluation criteria --- ...ode_suggestions_prompts_not_decoupled.toml | 10 ++++----- .../pr_code_suggestions_reflect_prompts.toml | 22 ++++++++++--------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/pr_agent/settings/code_suggestions/pr_code_suggestions_prompts_not_decoupled.toml b/pr_agent/settings/code_suggestions/pr_code_suggestions_prompts_not_decoupled.toml index 405237cfd1..6178ee23c0 100644 --- a/pr_agent/settings/code_suggestions/pr_code_suggestions_prompts_not_decoupled.toml +++ b/pr_agent/settings/code_suggestions/pr_code_suggestions_prompts_not_decoupled.toml @@ -55,11 +55,11 @@ Specific guidelines for generating code suggestions: - DO NOT suggest the following: - change packages version - add missing import statement - - declare undefined variable + - declare undefined variable, add missing imports, etc. - use more specific exception types {%- endif %} -- When mentioning code elements (variables, names, or files) in your response, surround them with backticks (`). For example: "verify that `user_id` is..." -- Note that you will only see partial code segments that were changed (diff hunks in a PR), and not the entire codebase. Avoid suggestions that might duplicate existing functionality or question the existence of code elements like variables, functions, classes, and import statements, that may be defined elsewhere in the codebase. +- When mentioning code elements (variables, names, or files) in your response, surround them with markdown backticks (`). For example: "verify that `user_id` is..." +- Note that you will only see partial code segments that were changed (diff hunks in a PR code), and not the entire codebase. Avoid suggestions that might duplicate existing functionality of the outer codebase. In addition, the absence of a definition, declaration, import, or initialization for any entity in the PR code is NEVER a basis for a suggestion. - Also note that if the code ends at an opening brace or statement that begins a new scope (like 'if', 'for', 'try'), don't treat it as incomplete. Instead, acknowledge the visible scope boundary and analyze only the code shown. {%- if extra_instructions %} @@ -77,8 +77,8 @@ The output must be a YAML object equivalent to type $PRCodeSuggestions, accordin class CodeSuggestion(BaseModel): relevant_file: str = Field(description="Full path of the relevant file") language: str = Field(description="Programming language used by the relevant file") - existing_code: str = Field(description="A short code snippet from the final state of the PR diff that the suggestion will address. Select only the span of code that will be modified - without surrounding unchanged code. Preserve all indentation, newlines, and original formatting. Show the code snippet without the '+'/'-'/' ' prefixes. When providing suggestions for long code sections, shorten the presented code with ellipsis (...) for brevity where possible.") - suggestion_content: str = Field(description="An actionable suggestion to enhance, improve or fix the new code introduced in the PR. Don't present here actual code snippets, just the suggestion. Be short and concise") + existing_code: str = Field(description="A short code snippet, from the final state of the PR diff, that the suggestion will address. Select only the specific span of code that will be modified - without surrounding unchanged code. Preserve all indentation, newlines, and original formatting. Show the code snippet without the '+'/'-'/' ' prefixes. When providing suggestions for long code sections, shorten the presented code with ellipsis (...) for brevity where possible.") + suggestion_content: str = Field(description="An actionable suggestion to enhance, improve or fix the new code introduced in the PR. Use 2-3 short sentences.") improved_code: str = Field(description="A refined code snippet that replaces the 'existing_code' snippet after implementing the suggestion.") one_sentence_summary: str = Field(description="A single-sentence overview (up to 6 words) of the suggestion. Focus on the 'what'. Be general, and avoid mentioning method or variable names.") {%- if not focus_only_on_problems %} diff --git a/pr_agent/settings/code_suggestions/pr_code_suggestions_reflect_prompts.toml b/pr_agent/settings/code_suggestions/pr_code_suggestions_reflect_prompts.toml index f930eb29a3..e3035d127e 100644 --- a/pr_agent/settings/code_suggestions/pr_code_suggestions_reflect_prompts.toml +++ b/pr_agent/settings/code_suggestions/pr_code_suggestions_reflect_prompts.toml @@ -1,26 +1,25 @@ [pr_code_suggestions_reflect_prompt] system="""You are an AI language model specialized in reviewing and evaluating code suggestions for a Pull Request (PR). -Your task is to analyze a PR code diff and evaluate a set of AI-generated code suggestions. These suggestions aim to address potential bugs and problems, and enhance the new code introduced in the PR. +Your task is to analyze a PR code diff and evaluate the correctness and importance set of AI-generated code suggestions. +In addition to evaluating the suggestion correctness and importance, another sub-task you have is to detect the line numbers in the '__new hunk__' of the PR code diff section that correspond to the 'existing_code' snippet. Examine each suggestion meticulously, assessing its quality, relevance, and accuracy within the context of PR. Keep in mind that the suggestions may vary in their correctness, accuracy and impact. Consider the following components of each suggestion: - 1. 'one_sentence_summary' - A brief summary of the suggestion's purpose - 2. 'suggestion_content' - The detailed suggestion content, explaining the proposed modification + 1. 'one_sentence_summary' - A one-liner summary summary of the suggestion's purpose + 2. 'suggestion_content' - The suggestion content, explaining the proposed modification 3. 'existing_code' - a code snippet from a __new hunk__ section in the PR code diff that the suggestion addresses 4. 'improved_code' - a code snippet demonstrating how the 'existing_code' should be after the suggestion is applied Be particularly vigilant for suggestions that: - - Overlook crucial details in the PR + - Overlook crucial details in the PR code - The 'improved_code' section does not accurately reflect the suggested changes, in relation to the 'existing_code' - Contradict or ignore parts of the PR's modifications In such cases, assign the suggestion a score of 0. Evaluate each valid suggestion by scoring its potential impact on the PR's correctness, quality and functionality. -In addition, you should also detect the line numbers in the '__new hunk__' section that correspond to the 'existing_code' snippet. - Key guidelines for evaluation: - Thoroughly examine both the suggestion content and the corresponding PR code diff. Be vigilant for potential errors in each suggestion, ensuring they are logically sound, accurate, and directly derived from the PR code diff. -- Extend your review beyond the specifically mentioned code lines to encompass surrounding context, verifying the suggestions' contextual accuracy. +- Extend your review beyond the specifically mentioned code lines to encompass surrounding PR code context, verifying the suggestions' contextual accuracy. - Validate the 'existing_code' field by confirming it matches or is accurately derived from code lines within a '__new hunk__' section of the PR code diff. - Ensure the 'improved_code' section accurately reflects the 'existing_code' segment after the suggested modification is applied. - Apply a nuanced scoring system: @@ -30,13 +29,16 @@ Key guidelines for evaluation: - Maintain the original order of suggestions in your feedback, corresponding to their input sequence. Additional scoring considerations: -- If the suggestion is not actionable, and only asks the user to verify or ensure a change, reduce its score by 1-2 points. +- If the suggestion only asks the user to verify or ensure a change done in the PR, it should not receive a score above 7 (and may be lower). - Error handling or type checking suggestions should not receive a score above 8 (and may be lower). +- If the 'existing_code' snippet is equal to the 'improved_code' snippet, it should not receive a score above 7 (and may be lower). +- Assume each suggestion is independent and is not influenced by the other suggestions. - Assign a score of 0 to suggestions aiming at: - Adding docstring, type hints, or comments - Remove unused imports or variables - Add missing import statements - Using more specific exception types. + - Questions the definition, declaration, import, or initialization of any entity in the PR code, that might be done in the outer codebase. @@ -87,7 +89,7 @@ class CodeSuggestionFeedback(BaseModel): relevant_lines_start: int = Field(description="The relevant line number, from a '__new hunk__' section, where the suggestion starts (inclusive). Should be derived from the added '__new hunk__' line numbers, and correspond to the first line of the relevant 'existing code' snippet.") relevant_lines_end: int = Field(description="The relevant line number, from a '__new hunk__' section, where the suggestion ends (inclusive). Should be derived from the added '__new hunk__' line numbers, and correspond to the end of the relevant 'existing code' snippet") suggestion_score: int = Field(description="Evaluate the suggestion and assign a score from 0 to 10. Give 0 if the suggestion is wrong. For valid suggestions, score from 1 (lowest impact/importance) to 10 (highest impact/importance).") - why: str = Field(description="Briefly explain the score given in 1-2 sentences, focusing on the suggestion's impact, relevance, and accuracy.") + why: str = Field(description="Briefly explain the score given in 1-2 short sentences, focusing on the suggestion's impact, relevance, and accuracy. When mentioning code elements (variables, names, or files) in your response, surround them with markdown backticks (`).") class PRCodeSuggestionsFeedback(BaseModel): code_suggestions: List[CodeSuggestionFeedback] @@ -118,7 +120,7 @@ user="""You are given a Pull Request (PR) code diff: ====== -Below are {{ num_code_suggestions }} AI-generated code suggestions for enhancing the Pull Request: +Below are {{ num_code_suggestions }} AI-generated code suggestions for the Pull Request: ====== {{ suggestion_str|trim }} ====== From f53bd524c58477a104c66c861a7643776b1751d3 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sun, 27 Apr 2025 08:50:03 +0300 Subject: [PATCH 154/365] Support multiple model types for different reasoning tasks --- docs/docs/tools/improve.md | 6 +++--- pr_agent/algo/pr_processing.py | 6 ++++-- pr_agent/algo/utils.py | 8 +++++--- pr_agent/settings/configuration.toml | 9 +++++---- pr_agent/tools/pr_code_suggestions.py | 15 ++++++++++----- 5 files changed, 27 insertions(+), 17 deletions(-) diff --git a/docs/docs/tools/improve.md b/docs/docs/tools/improve.md index 41b23c17a5..337efbffe8 100644 --- a/docs/docs/tools/improve.md +++ b/docs/docs/tools/improve.md @@ -416,18 +416,18 @@ Qodo Merge uses a dynamic strategy to generate code suggestions based on the siz #### 1. Chunking large PRs - Qodo Merge divides large PRs into 'chunks'. -- Each chunk contains up to `pr_code_suggestions.max_context_tokens` tokens (default: 14,000). +- Each chunk contains up to `pr_code_suggestions.max_context_tokens` tokens (default: 24,000). #### 2. Generating suggestions -- For each chunk, Qodo Merge generates up to `pr_code_suggestions.num_code_suggestions_per_chunk` suggestions (default: 3). +- For each chunk, Qodo Merge generates up to `pr_code_suggestions.num_code_suggestions_per_chunk` suggestions (default: 4). This approach has two main benefits: - Scalability: The number of suggestions scales with the PR size, rather than being fixed. - Quality: By processing smaller chunks, the AI can maintain higher quality suggestions, as larger contexts tend to decrease AI performance. -Note: Chunking is primarily relevant for large PRs. For most PRs (up to 500 lines of code), Qodo Merge will be able to process the entire code in a single call. +Note: Chunking is primarily relevant for large PRs. For most PRs (up to 600 lines of code), Qodo Merge will be able to process the entire code in a single call. ## Configuration options diff --git a/pr_agent/algo/pr_processing.py b/pr_agent/algo/pr_processing.py index 17a973dc03..59ed671801 100644 --- a/pr_agent/algo/pr_processing.py +++ b/pr_agent/algo/pr_processing.py @@ -12,7 +12,7 @@ from pr_agent.algo.language_handler import sort_files_by_main_languages from pr_agent.algo.token_handler import TokenHandler from pr_agent.algo.types import EDIT_TYPE, FilePatchInfo -from pr_agent.algo.utils import ModelType, clip_tokens, get_max_tokens, get_weak_model +from pr_agent.algo.utils import ModelType, clip_tokens, get_max_tokens, get_model from pr_agent.config_loader import get_settings from pr_agent.git_providers.git_provider import GitProvider from pr_agent.log import get_logger @@ -339,7 +339,9 @@ async def retry_with_fallback_models(f: Callable, model_type: ModelType = ModelT def _get_all_models(model_type: ModelType = ModelType.REGULAR) -> List[str]: if model_type == ModelType.WEAK: - model = get_weak_model() + model = get_model('model_weak') + elif model_type == ModelType.REASONING: + model = get_model('model_reasoning') else: model = get_settings().config.model fallback_models = get_settings().config.fallback_models diff --git a/pr_agent/algo/utils.py b/pr_agent/algo/utils.py index 398df4c342..4a3413866f 100644 --- a/pr_agent/algo/utils.py +++ b/pr_agent/algo/utils.py @@ -30,12 +30,13 @@ from pr_agent.log import get_logger -def get_weak_model() -> str: - if get_settings().get("config.model_weak"): +def get_model(model_type: str = "model_weak") -> str: + if model_type == "model_weak" and get_settings().get("config.model_weak"): return get_settings().config.model_weak + elif model_type == "model_reasoning" and get_settings().get("config.model_reasoning"): + return get_settings().config.model_reasoning return get_settings().config.model - class Range(BaseModel): line_start: int # should be 0-indexed line_end: int @@ -45,6 +46,7 @@ class Range(BaseModel): class ModelType(str, Enum): REGULAR = "regular" WEAK = "weak" + REASONING = "reasoning" class PRReviewHeader(str, Enum): REGULAR = "## PR Reviewer Guide" diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index 09548011c3..7952812289 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -6,8 +6,9 @@ [config] # models -model="o4-mini" -fallback_models=["gpt-4.1"] +model_reasoning="o4-mini" +model="gpt-4.1" +fallback_models=["o4-mini"] #model_weak="gpt-4o" # optional, a weaker model to use for some easier tasks # CLI git_provider="github" @@ -123,7 +124,7 @@ use_conversation_history=true [pr_code_suggestions] # /improve # -max_context_tokens=16000 +max_context_tokens=24000 # commitable_code_suggestions = false dual_publishing_score_threshold=-1 # -1 to disable, [0-10] to set the threshold (>=) for publishing a code suggestion both in a table and as commitable @@ -144,7 +145,7 @@ new_score_mechanism_th_high=9 new_score_mechanism_th_medium=7 # params for '/improve --extended' mode auto_extended_mode=true -num_code_suggestions_per_chunk=3 +num_code_suggestions_per_chunk=4 max_number_of_calls = 3 parallel_calls = true diff --git a/pr_agent/tools/pr_code_suggestions.py b/pr_agent/tools/pr_code_suggestions.py index 1d699f390d..67113dc5ef 100644 --- a/pr_agent/tools/pr_code_suggestions.py +++ b/pr_agent/tools/pr_code_suggestions.py @@ -19,7 +19,7 @@ retry_with_fallback_models) from pr_agent.algo.token_handler import TokenHandler from pr_agent.algo.utils import (ModelType, load_yaml, replace_code_tags, - show_relevant_configurations, get_max_tokens, clip_tokens) + show_relevant_configurations, get_max_tokens, clip_tokens, get_model) from pr_agent.config_loader import get_settings from pr_agent.git_providers import (AzureDevopsProvider, GithubProvider, GitLabProvider, get_git_provider, @@ -121,7 +121,7 @@ async def run(self): # if not self.is_extended: # data = await retry_with_fallback_models(self._prepare_prediction, model_type=ModelType.REGULAR) # else: - data = await retry_with_fallback_models(self._prepare_prediction_extended, model_type=ModelType.REGULAR) + data = await retry_with_fallback_models(self.prepare_prediction_main, model_type=ModelType.REGULAR) if not data: data = {"code_suggestions": []} self.data = data @@ -416,9 +416,14 @@ async def _get_prediction(self, model: str, patches_diff: str, patches_diff_no_l data = self._prepare_pr_code_suggestions(response) # self-reflect on suggestions (mandatory, since line numbers are generated now here) - model_reflection = get_settings().config.model + model_reflect_with_reasoning = get_model('model_reasoning') + if model_reflect_with_reasoning == get_settings().config.model and model != get_settings().config.model and model == \ + get_settings().config.fallback_models[0]: + # we are using a fallback model (should not happen on regular conditions) + get_logger().warning(f"Using the same model for self-reflection as the one used for suggestions") + model_reflect_with_reasoning = model response_reflect = await self.self_reflect_on_suggestions(data["code_suggestions"], - patches_diff, model=model_reflection) + patches_diff, model=model_reflect_with_reasoning) if response_reflect: await self.analyze_self_reflection_response(data, response_reflect) else: @@ -675,7 +680,7 @@ def remove_line_numbers(self, patches_diff_list: List[str]) -> List[str]: get_logger().error(f"Error removing line numbers from patches_diff_list, error: {e}") return patches_diff_list - async def _prepare_prediction_extended(self, model: str) -> dict: + async def prepare_prediction_main(self, model: str) -> dict: # get PR diff if get_settings().pr_code_suggestions.decouple_hunks: self.patches_diff_list = get_pr_multi_diffs(self.git_provider, From 3f194e6730160593ac74906807aaf1eb1fe93fb3 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sun, 27 Apr 2025 10:07:56 +0300 Subject: [PATCH 155/365] Improve dynamic context handling in git patch processing --- pr_agent/algo/git_patch_processing.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/pr_agent/algo/git_patch_processing.py b/pr_agent/algo/git_patch_processing.py index b2b80f24ec..f38e1ae24d 100644 --- a/pr_agent/algo/git_patch_processing.py +++ b/pr_agent/algo/git_patch_processing.py @@ -102,20 +102,20 @@ def _calc_context_limits(patch_lines_before): lines_before_original = file_original_lines[extended_start1 - 1:start1 - 1] lines_before_new = file_new_lines[extended_start2 - 1:start2 - 1] found_header = False - if lines_before_original == lines_before_new: # Making sure no changes from a previous hunk - for i, line, in enumerate(lines_before_original): - if section_header in line: + for i, line, in enumerate(lines_before_original): + if section_header in line: + # Update start and size in one line each + extended_start1, extended_start2 = extended_start1 + i, extended_start2 + i + extended_size1, extended_size2 = extended_size1 - i, extended_size2 - i + lines_before_original_dynamic_context = lines_before_original[i:] + lines_before_new_dynamic_context = lines_before_new[i:] + if lines_before_original_dynamic_context == lines_before_new_dynamic_context: + # get_logger().debug(f"found dynamic context match for section header: {section_header}") found_header = True - # Update start and size in one line each - extended_start1, extended_start2 = extended_start1 + i, extended_start2 + i - extended_size1, extended_size2 = extended_size1 - i, extended_size2 - i - # get_logger().debug(f"Found section header in line {i} before the hunk") section_header = '' - break - else: - get_logger().debug(f"Extra lines before hunk are different in original and new file - dynamic context", - artifact={"lines_before_original": lines_before_original, - "lines_before_new": lines_before_new}) + else: + pass # its ok to be here. We cant apply dynamic context if the lines are different if 'old' and 'new' hunks + break if not found_header: # get_logger().debug(f"Section header not found in the extra lines before the hunk") From c951fc9a87b0e0e5ac3d111bf572e354ea1544d6 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sun, 27 Apr 2025 10:46:23 +0300 Subject: [PATCH 156/365] Improve dynamic context handling with partial line matching and adjust model configuration --- pr_agent/algo/git_patch_processing.py | 26 ++++++++++++++++++-------- pr_agent/settings/configuration.toml | 10 +++++----- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/pr_agent/algo/git_patch_processing.py b/pr_agent/algo/git_patch_processing.py index f38e1ae24d..161469e16d 100644 --- a/pr_agent/algo/git_patch_processing.py +++ b/pr_agent/algo/git_patch_processing.py @@ -130,14 +130,24 @@ def _calc_context_limits(patch_lines_before): if file_new_lines: delta_lines_new = [f' {line}' for line in file_new_lines[extended_start2 - 1:start2 - 1]] if delta_lines_original != delta_lines_new: - get_logger().debug(f"Extra lines before hunk are different in original and new file", - artifact={"delta_lines_original": delta_lines_original, - "delta_lines_new": delta_lines_new}) - extended_start1 = start1 - extended_size1 = size1 - extended_start2 = start2 - extended_size2 = size2 - delta_lines_original = [] + found_mini_match = False + for i in range(len(delta_lines_original)): + if delta_lines_original[i:] == delta_lines_new[i:]: + delta_lines_original = delta_lines_original[i:] + delta_lines_new = delta_lines_new[i:] + extended_start1 += i + extended_start2 += i + found_mini_match = True + break + if not found_mini_match: + extended_start1 = start1 + extended_size1 = size1 + extended_start2 = start2 + extended_size2 = size2 + delta_lines_original = [] + # get_logger().debug(f"Extra lines before hunk are different in original and new file", + # artifact={"delta_lines_original": delta_lines_original, + # "delta_lines_new": delta_lines_new}) # logic to remove section header if its in the extra delta lines (in dynamic context, this is also done) if section_header and not allow_dynamic_context: diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index 7952812289..e63b7ea8b7 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -6,9 +6,9 @@ [config] # models -model_reasoning="o4-mini" -model="gpt-4.1" -fallback_models=["o4-mini"] +model="o4-mini" +fallback_models=["gpt-4.1"] +#model_reasoning="o4-mini" # dedictated reasoning model for self-reflection #model_weak="gpt-4o" # optional, a weaker model to use for some easier tasks # CLI git_provider="github" @@ -36,8 +36,8 @@ model_token_count_estimate_factor=0.3 # factor to increase the token count estim # patch extension logic patch_extension_skip_types =[".md",".txt"] allow_dynamic_context=true -max_extra_lines_before_dynamic_context = 8 # will try to include up to 10 extra lines before the hunk in the patch, until we reach an enclosing function or class -patch_extra_lines_before = 3 # Number of extra lines (+3 default ones) to include before each hunk in the patch +max_extra_lines_before_dynamic_context = 10 # will try to include up to 10 extra lines before the hunk in the patch, until we reach an enclosing function or class +patch_extra_lines_before = 5 # Number of extra lines (+3 default ones) to include before each hunk in the patch patch_extra_lines_after = 1 # Number of extra lines (+3 default ones) to include after each hunk in the patch secret_provider="" cli_mode=false From f505c7ad3c56a6f37c087f8345b41feae97d43f5 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sun, 27 Apr 2025 11:00:34 +0300 Subject: [PATCH 157/365] Add multi-model support for different reasoning tasks --- docs/docs/tools/improve.md | 1 + docs/docs/usage-guide/qodo_merge_models.md | 14 +++----------- pr_agent/algo/git_patch_processing.py | 4 +++- pr_agent/algo/pr_processing.py | 2 ++ pr_agent/tools/pr_code_suggestions.py | 5 +++-- 5 files changed, 12 insertions(+), 14 deletions(-) diff --git a/docs/docs/tools/improve.md b/docs/docs/tools/improve.md index 337efbffe8..f190990cdf 100644 --- a/docs/docs/tools/improve.md +++ b/docs/docs/tools/improve.md @@ -516,4 +516,5 @@ Note: Chunking is primarily relevant for large PRs. For most PRs (up to 600 line - **Bug detection:** The suggestions also alert on any _critical bugs_ that may have been identified during the analysis. This provides an additional safety net to catch potential issues before they make it into production. It's perfectly acceptable to implement only the suggestions you find valuable for your specific context. - **Hierarchy:** Presenting the suggestions in a structured hierarchical table enables the user to _quickly_ understand them, and to decide which ones are relevant and which are not. - **Customization:** To guide the model to suggestions that are more relevant to the specific needs of your project, we recommend to use the [`extra_instructions`](https://qodo-merge-docs.qodo.ai/tools/improve/#extra-instructions-and-best-practices) and [`best practices`](https://qodo-merge-docs.qodo.ai/tools/improve/#best-practices) fields. +- **Model Selection:** SaaS users can also [choose](https://qodo-merge-docs.qodo.ai/usage-guide/qodo_merge_models/) between different models. For specific programming languages or use cases, some models may perform better than others. - **Interactive usage:** The interactive [PR chat](https://qodo-merge-docs.qodo.ai/chrome-extension/) also provides an easy way to get more tailored suggestions and feedback from the AI model. diff --git a/docs/docs/usage-guide/qodo_merge_models.md b/docs/docs/usage-guide/qodo_merge_models.md index cb062cb9d9..1ce9fd25c3 100644 --- a/docs/docs/usage-guide/qodo_merge_models.md +++ b/docs/docs/usage-guide/qodo_merge_models.md @@ -1,16 +1,15 @@ -The default model used by Qodo Merge (March 2025) is Claude Sonnet 3.7. +The default models used by Qodo Merge (April 2025) are a combination of Claude Sonnet 3.7 and Gemini 2.5 Pro. ### Selecting a Specific Model -Users can configure Qodo Merge to use a specific model by editing the [configuration](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/) file. +Users can configure Qodo Merge to use only a specific model by editing the [configuration](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/) file. The models supported by Qodo Merge are: -- `claude-3-7-sonnet` (default) +- `claude-3-7-sonnet` - `o4-mini` - `gpt-4.1` - `gemini-2.5-pro` -- `gemini-2.5-flash` - `deepseek/r1` To restrict Qodo Merge to using only `o4-mini`, add this setting: @@ -34,13 +33,6 @@ To restrict Qodo Merge to using only `gemini-2.5-pro`, add this setting: model="gemini-2.5-pro" ``` -To restrict Qodo Merge to using only `gemini-2.5-flash`, add this setting: - -```toml -[config] -model="gemini-2.5-flash" -``` - To restrict Qodo Merge to using only `deepseek-r1` us-hosted, add this setting: diff --git a/pr_agent/algo/git_patch_processing.py b/pr_agent/algo/git_patch_processing.py index 161469e16d..81e05d500d 100644 --- a/pr_agent/algo/git_patch_processing.py +++ b/pr_agent/algo/git_patch_processing.py @@ -102,7 +102,7 @@ def _calc_context_limits(patch_lines_before): lines_before_original = file_original_lines[extended_start1 - 1:start1 - 1] lines_before_new = file_new_lines[extended_start2 - 1:start2 - 1] found_header = False - for i, line, in enumerate(lines_before_original): + for i, line in enumerate(lines_before_original): if section_header in line: # Update start and size in one line each extended_start1, extended_start2 = extended_start1 + i, extended_start2 + i @@ -136,7 +136,9 @@ def _calc_context_limits(patch_lines_before): delta_lines_original = delta_lines_original[i:] delta_lines_new = delta_lines_new[i:] extended_start1 += i + extended_size1 -= i extended_start2 += i + extended_size2 -= i found_mini_match = True break if not found_mini_match: diff --git a/pr_agent/algo/pr_processing.py b/pr_agent/algo/pr_processing.py index 59ed671801..55098b5934 100644 --- a/pr_agent/algo/pr_processing.py +++ b/pr_agent/algo/pr_processing.py @@ -342,6 +342,8 @@ def _get_all_models(model_type: ModelType = ModelType.REGULAR) -> List[str]: model = get_model('model_weak') elif model_type == ModelType.REASONING: model = get_model('model_reasoning') + elif model_type == ModelType.REGULAR: + model = get_settings().config.model else: model = get_settings().config.model fallback_models = get_settings().config.fallback_models diff --git a/pr_agent/tools/pr_code_suggestions.py b/pr_agent/tools/pr_code_suggestions.py index 67113dc5ef..c742aa0647 100644 --- a/pr_agent/tools/pr_code_suggestions.py +++ b/pr_agent/tools/pr_code_suggestions.py @@ -417,8 +417,9 @@ async def _get_prediction(self, model: str, patches_diff: str, patches_diff_no_l # self-reflect on suggestions (mandatory, since line numbers are generated now here) model_reflect_with_reasoning = get_model('model_reasoning') - if model_reflect_with_reasoning == get_settings().config.model and model != get_settings().config.model and model == \ - get_settings().config.fallback_models[0]: + fallbacks = get_settings().config.fallback_models + if model_reflect_with_reasoning == get_settings().config.model and model != get_settings().config.model and fallbacks and model == \ + fallbacks[0]: # we are using a fallback model (should not happen on regular conditions) get_logger().warning(f"Using the same model for self-reflection as the one used for suggestions") model_reflect_with_reasoning = model From 9926a10b00b888260a4972581edd64a0541944fe Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sun, 27 Apr 2025 15:58:14 +0300 Subject: [PATCH 158/365] Add Help Docs, Code Validation, and Auto Best Practices features to platform support tables --- README.md | 6 ++++-- docs/docs/index.md | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2320a472fe..86081b33e7 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ A new version, v0.27, was released. See release notes [here](https://github.com/ Supported commands per platform: | | | GitHub | GitLab | Bitbucket | Azure DevOps | -| ----- | ------------------------------------------------------------------------------------------------------- |:------:|:------:|:---------:|:------------:| +| ----- |---------------------------------------------------------------------------------------------------------|:------:|:------:|:---------:|:------------:| | TOOLS | [Review](https://qodo-merge-docs.qodo.ai/tools/review/) | ✅ | ✅ | ✅ | ✅ | | | [Describe](https://qodo-merge-docs.qodo.ai/tools/describe/) | ✅ | ✅ | ✅ | ✅ | | | [Improve](https://qodo-merge-docs.qodo.ai/tools/improve/) | ✅ | ✅ | ✅ | ✅ | @@ -107,6 +107,7 @@ Supported commands per platform: | | [Implement](https://qodo-merge-docs.qodo.ai/tools/implement/) 💎 | ✅ | ✅ | ✅ | | | | [Scan Repo Discussions](https://qodo-merge-docs.qodo.ai/tools/scan_repo_discussions/) 💎 | ✅ | | | | | | [Auto-Approve](https://qodo-merge-docs.qodo.ai/tools/improve/?h=auto#auto-approval) 💎 | ✅ | ✅ | ✅ | | +| | [Help Docs](https://qodo-merge-docs.qodo.ai/tools/improve/?h=auto#auto-approval) 💎 | ✅ | ✅ | ✅ | | | | | | | | | | USAGE | [CLI](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#local-repo-cli) | ✅ | ✅ | ✅ | ✅ | | | [App / webhook](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#github-app) | ✅ | ✅ | ✅ | ✅ | @@ -123,7 +124,8 @@ Supported commands per platform: | | [Global and wiki configurations](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/) 💎 | ✅ | ✅ | ✅ | | | | [PR interactive actions](https://www.qodo.ai/images/pr_agent/pr-actions.mp4) 💎 | ✅ | ✅ | | | | | [Impact Evaluation](https://qodo-merge-docs.qodo.ai/core-abilities/impact_evaluation/) 💎 | ✅ | ✅ | | | - +| | [Code Validation 💎](https://qodo-merge-docs.qodo.ai/core-abilities/code_validation/) | ✅ | ✅ | ✅ | ✅ | +| | [Auto Best Practices 💎](https://qodo-merge-docs.qodo.ai/core-abilities/auto_best_practices/) | ✅ | ✅ | ✅ | ✅ | - 💎 means this feature is available only in [Qodo-Merge](https://www.qodo.ai/pricing/) [//]: # (- Support for additional git providers is described in [here](./docs/Full_environments.md)) diff --git a/docs/docs/index.md b/docs/docs/index.md index 9e80f42013..a924ca28a5 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -49,6 +49,7 @@ PR-Agent and Qodo Merge offers extensive pull request functionalities across var | | [Implement](https://qodo-merge-docs.qodo.ai/tools/implement/) 💎 | ✅ | ✅ | ✅ | | | | [Scan Repo Discussions](https://qodo-merge-docs.qodo.ai/tools/scan_repo_discussions/) 💎 | ✅ | | | | | | [Auto-Approve](https://qodo-merge-docs.qodo.ai/tools/improve/?h=auto#auto-approval) 💎 | ✅ | ✅ | ✅ | | +| | [Help Docs](https://qodo-merge-docs.qodo.ai/tools/improve/?h=auto#auto-approval) 💎 | ✅ | ✅ | ✅ | | | | | | | | | | USAGE | [CLI](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#local-repo-cli) | ✅ | ✅ | ✅ | ✅ | | | [App / webhook](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#github-app) | ✅ | ✅ | ✅ | ✅ | @@ -65,7 +66,8 @@ PR-Agent and Qodo Merge offers extensive pull request functionalities across var | | [Global and wiki configurations](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/) 💎 | ✅ | ✅ | ✅ | | | | [PR interactive actions](https://www.qodo.ai/images/pr_agent/pr-actions.mp4) 💎 | ✅ | ✅ | | | | | [Impact Evaluation](https://qodo-merge-docs.qodo.ai/core-abilities/impact_evaluation/) 💎 | ✅ | ✅ | | | - +| | [Code Validation 💎](https://qodo-merge-docs.qodo.ai/core-abilities/code_validation/) | ✅ | ✅ | ✅ | ✅ | +| | [Auto Best Practices 💎](https://qodo-merge-docs.qodo.ai/core-abilities/auto_best_practices/) | ✅ | ✅ | ✅ | ✅ | !!! note "💎 means Qodo Merge only" All along the documentation, 💎 marks a feature available only in [Qodo Merge](https://www.codium.ai/pricing/){:target="_blank"}, and not in the open-source version. From cbe03b82fec746077c695cdc04c3a0aaced9e0b7 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sun, 27 Apr 2025 16:15:46 +0300 Subject: [PATCH 159/365] Remove Help Docs entry and update Auto Best Practices platform support --- README.md | 3 +-- docs/docs/index.md | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 86081b33e7..064ff0d927 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,6 @@ Supported commands per platform: | | [Implement](https://qodo-merge-docs.qodo.ai/tools/implement/) 💎 | ✅ | ✅ | ✅ | | | | [Scan Repo Discussions](https://qodo-merge-docs.qodo.ai/tools/scan_repo_discussions/) 💎 | ✅ | | | | | | [Auto-Approve](https://qodo-merge-docs.qodo.ai/tools/improve/?h=auto#auto-approval) 💎 | ✅ | ✅ | ✅ | | -| | [Help Docs](https://qodo-merge-docs.qodo.ai/tools/improve/?h=auto#auto-approval) 💎 | ✅ | ✅ | ✅ | | | | | | | | | | USAGE | [CLI](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#local-repo-cli) | ✅ | ✅ | ✅ | ✅ | | | [App / webhook](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#github-app) | ✅ | ✅ | ✅ | ✅ | @@ -125,7 +124,7 @@ Supported commands per platform: | | [PR interactive actions](https://www.qodo.ai/images/pr_agent/pr-actions.mp4) 💎 | ✅ | ✅ | | | | | [Impact Evaluation](https://qodo-merge-docs.qodo.ai/core-abilities/impact_evaluation/) 💎 | ✅ | ✅ | | | | | [Code Validation 💎](https://qodo-merge-docs.qodo.ai/core-abilities/code_validation/) | ✅ | ✅ | ✅ | ✅ | -| | [Auto Best Practices 💎](https://qodo-merge-docs.qodo.ai/core-abilities/auto_best_practices/) | ✅ | ✅ | ✅ | ✅ | +| | [Auto Best Practices 💎](https://qodo-merge-docs.qodo.ai/core-abilities/auto_best_practices/) | ✅ | | | | - 💎 means this feature is available only in [Qodo-Merge](https://www.qodo.ai/pricing/) [//]: # (- Support for additional git providers is described in [here](./docs/Full_environments.md)) diff --git a/docs/docs/index.md b/docs/docs/index.md index a924ca28a5..8dd7c955da 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -49,7 +49,6 @@ PR-Agent and Qodo Merge offers extensive pull request functionalities across var | | [Implement](https://qodo-merge-docs.qodo.ai/tools/implement/) 💎 | ✅ | ✅ | ✅ | | | | [Scan Repo Discussions](https://qodo-merge-docs.qodo.ai/tools/scan_repo_discussions/) 💎 | ✅ | | | | | | [Auto-Approve](https://qodo-merge-docs.qodo.ai/tools/improve/?h=auto#auto-approval) 💎 | ✅ | ✅ | ✅ | | -| | [Help Docs](https://qodo-merge-docs.qodo.ai/tools/improve/?h=auto#auto-approval) 💎 | ✅ | ✅ | ✅ | | | | | | | | | | USAGE | [CLI](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#local-repo-cli) | ✅ | ✅ | ✅ | ✅ | | | [App / webhook](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#github-app) | ✅ | ✅ | ✅ | ✅ | @@ -67,7 +66,7 @@ PR-Agent and Qodo Merge offers extensive pull request functionalities across var | | [PR interactive actions](https://www.qodo.ai/images/pr_agent/pr-actions.mp4) 💎 | ✅ | ✅ | | | | | [Impact Evaluation](https://qodo-merge-docs.qodo.ai/core-abilities/impact_evaluation/) 💎 | ✅ | ✅ | | | | | [Code Validation 💎](https://qodo-merge-docs.qodo.ai/core-abilities/code_validation/) | ✅ | ✅ | ✅ | ✅ | -| | [Auto Best Practices 💎](https://qodo-merge-docs.qodo.ai/core-abilities/auto_best_practices/) | ✅ | ✅ | ✅ | ✅ | +| | [Auto Best Practices 💎](https://qodo-merge-docs.qodo.ai/core-abilities/auto_best_practices/) | ✅ | | | | !!! note "💎 means Qodo Merge only" All along the documentation, 💎 marks a feature available only in [Qodo Merge](https://www.codium.ai/pricing/){:target="_blank"}, and not in the open-source version. From 1c3629d4c21c9773a50db61a72a522dca70c4842 Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Mon, 28 Apr 2025 08:50:58 +0300 Subject: [PATCH 160/365] Add documentation for chat on code suggestions with example image and update RAG platform support --- docs/docs/core-abilities/rag_context_enrichment.md | 2 +- docs/docs/tools/improve.md | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/docs/core-abilities/rag_context_enrichment.md b/docs/docs/core-abilities/rag_context_enrichment.md index a6ba038f23..a2064c3fa8 100644 --- a/docs/docs/core-abilities/rag_context_enrichment.md +++ b/docs/docs/core-abilities/rag_context_enrichment.md @@ -1,6 +1,6 @@ # RAG Context Enrichment 💎 -`Supported Git Platforms: GitHub` +`Supported Git Platforms: GitHub, Bitbucket Data Center` !!! info "Prerequisites" - RAG is available only for Qodo enterprise plan users, with single tenant or on-premises setup. diff --git a/docs/docs/tools/improve.md b/docs/docs/tools/improve.md index f190990cdf..9fd102da1f 100644 --- a/docs/docs/tools/improve.md +++ b/docs/docs/tools/improve.md @@ -267,7 +267,9 @@ In such cases, we recommend prioritizing the suggestion's detailed description, > `💎 feature` Platforms supported: GitHub, GitLab -If you set the following in your configuration file: + + +Enable interactive code discussions by adding this to your configuration file (default `True`): ```toml [pr_code_suggestions] @@ -275,6 +277,11 @@ enable_chat_in_code_suggestions = true ``` The Qodo Merge bot will automatically listen and respond to comments within code suggestion discussions that it has initiated, without requiring explicit tool calls. +The bot intelligently analyzes your responses to determine if you want to implement the suggestion, ask questions about it, or request help with understanding it. + +For example: + +![Chat on code suggestions ](https://codium.ai/images/pr_agent/improve_chat_on_code_suggestions.png){width=512} ### Dual publishing mode From ab0e3bde492a61cbe81e89bc7eaf07ececc4cbe2 Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Mon, 28 Apr 2025 08:52:06 +0300 Subject: [PATCH 161/365] Remove extra blank lines in improve.md documentation --- docs/docs/tools/improve.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/docs/tools/improve.md b/docs/docs/tools/improve.md index 9fd102da1f..f43bc6c49c 100644 --- a/docs/docs/tools/improve.md +++ b/docs/docs/tools/improve.md @@ -267,8 +267,6 @@ In such cases, we recommend prioritizing the suggestion's detailed description, > `💎 feature` Platforms supported: GitHub, GitLab - - Enable interactive code discussions by adding this to your configuration file (default `True`): ```toml From 4ad43c48e564ac182ec3db2017bc41d38f0addbc Mon Sep 17 00:00:00 2001 From: "yuli.kamakura" Date: Mon, 28 Apr 2025 17:59:09 +0800 Subject: [PATCH 162/365] Fix type hint for get_pr_description method and clean up whitespace --- pr_agent/git_providers/git_provider.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pr_agent/git_providers/git_provider.py b/pr_agent/git_providers/git_provider.py index 127fa41ecb..2895bd5579 100644 --- a/pr_agent/git_providers/git_provider.py +++ b/pr_agent/git_providers/git_provider.py @@ -133,7 +133,7 @@ def get_comment_body_from_comment_id(self, comment_id: int) -> str: def reply_to_comment_from_comment_id(self, comment_id: int, body: str): pass - def get_pr_description(self, full: bool = True, split_changes_walkthrough=False) -> str or tuple: + def get_pr_description(self, full: bool = True, split_changes_walkthrough=False) -> str | tuple: from pr_agent.algo.utils import clip_tokens from pr_agent.config_loader import get_settings max_tokens_description = get_settings().get("CONFIG.MAX_DESCRIPTION_TOKENS", None) @@ -285,7 +285,7 @@ def get_issue_comments(self): def get_comment_url(self, comment) -> str: return "" - + def get_review_thread_comments(self, comment_id: int) -> list[dict]: pass From 26dc3b7f219780db1daf9e5bdbac0df81fc214c4 Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Mon, 28 Apr 2025 13:02:54 +0300 Subject: [PATCH 163/365] Update code suggestion chat documentation with multi-node agent description --- docs/docs/tools/improve.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/tools/improve.md b/docs/docs/tools/improve.md index f43bc6c49c..511d6ab824 100644 --- a/docs/docs/tools/improve.md +++ b/docs/docs/tools/improve.md @@ -274,8 +274,8 @@ Enable interactive code discussions by adding this to your configuration file (d enable_chat_in_code_suggestions = true ``` -The Qodo Merge bot will automatically listen and respond to comments within code suggestion discussions that it has initiated, without requiring explicit tool calls. -The bot intelligently analyzes your responses to determine if you want to implement the suggestion, ask questions about it, or request help with understanding it. +The Qodo Merge implements a multi-node agent to listen and respond to comments within code suggestion discussions that it has initiated, without requiring explicit tool calls. +The agent intelligently analyzes your responses to determine if you want to implement a suggestion, ask a question, or request help. For example: From e69b17cb1a0a08c4020dfd432631a65dbc316fb7 Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Mon, 28 Apr 2025 13:59:44 +0300 Subject: [PATCH 164/365] Update code suggestion chat documentation with orchestrator agent description --- docs/docs/tools/improve.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/tools/improve.md b/docs/docs/tools/improve.md index 511d6ab824..72753b59ab 100644 --- a/docs/docs/tools/improve.md +++ b/docs/docs/tools/improve.md @@ -274,8 +274,8 @@ Enable interactive code discussions by adding this to your configuration file (d enable_chat_in_code_suggestions = true ``` -The Qodo Merge implements a multi-node agent to listen and respond to comments within code suggestion discussions that it has initiated, without requiring explicit tool calls. -The agent intelligently analyzes your responses to determine if you want to implement a suggestion, ask a question, or request help. +The Qodo Merge implements an orchestrator agent to listen and respond to comments within code suggestion discussions that it has initiated, without requiring explicit tool calls. +The orchestrator intelligently analyzes your responses to determine if you want to implement a suggestion, ask a question, or request help, then delegates to the appropriate specialized tool. For example: From 3dc0cac97514aaec233cd49818e7ce37346311a2 Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Tue, 29 Apr 2025 09:12:20 +0300 Subject: [PATCH 165/365] docs: Chat on code suggestions: Add interactive code suggestions documentation with tabbed examples --- docs/docs/tools/improve.md | 26 +++++++++++++++++++++----- docs/mkdocs.yml | 2 ++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/docs/docs/tools/improve.md b/docs/docs/tools/improve.md index 72753b59ab..b2e2fc6b94 100644 --- a/docs/docs/tools/improve.md +++ b/docs/docs/tools/improve.md @@ -267,19 +267,35 @@ In such cases, we recommend prioritizing the suggestion's detailed description, > `💎 feature` Platforms supported: GitHub, GitLab -Enable interactive code discussions by adding this to your configuration file (default `True`): +The Qodo Merge implements an orchestrator agent that enables interactive code discussions, listening and responding to comments without requiring explicit tool calls. +The orchestrator intelligently analyzes your responses to determine if you want to implement a suggestion, ask a question, or request help, then delegates to the appropriate specialized tool. + +#### How it works + +Enable interactive code discussions by adding the following to your configuration file (default is `True`): ```toml [pr_code_suggestions] enable_chat_in_code_suggestions = true ``` -The Qodo Merge implements an orchestrator agent to listen and respond to comments within code suggestion discussions that it has initiated, without requiring explicit tool calls. -The orchestrator intelligently analyzes your responses to determine if you want to implement a suggestion, ask a question, or request help, then delegates to the appropriate specialized tool. +!!! info "Activating Dynamic Responses" + To obtain dynamic responses, the following steps are required: + + 1. Run the `/improve` command (mostly automatic) + 2. Tick the `/improve` recommendation checkboxes (_Apply this suggestion_) to have Qodo Merge generate a new inline code suggestion discussion + 3. The orchestrator agent will then automatically listen and reply to comments within the discussion without requiring additional commands + +#### Explore the available interaction patterns: + +=== "Asking for Details" + ![Chat on code suggestions ask](https://codium.ai/images/pr_agent/improve_chat_on_code_suggestions_ask.png){width=512} -For example: +=== "Implementing Suggestions" + ![Chat on code suggestions implement](https://codium.ai/images/pr_agent/improve_chat_on_code_suggestions_implement.png){width=512} -![Chat on code suggestions ](https://codium.ai/images/pr_agent/improve_chat_on_code_suggestions.png){width=512} +=== "Providing Additional Help" + ![Chat on code suggestions help](https://codium.ai/images/pr_agent/improve_chat_on_code_suggestions_help.png){width=512} ### Dual publishing mode diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 7b9c967bec..cc1b02f149 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -149,6 +149,8 @@ markdown_extensions: - pymdownx.emoji: emoji_index: !!python/name:material.extensions.emoji.twemoji emoji_generator: !!python/name:material.extensions.emoji.to_svg + - pymdownx.tabbed: + alternate_style: true - toc: title: On this page toc_depth: 3 From 7b82b08173b6d9bd48c32bdbba00dd564c355e04 Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Tue, 29 Apr 2025 12:35:10 +0300 Subject: [PATCH 166/365] fix: correct grammar in Qodo Merge orchestrator agent description --- docs/docs/tools/improve.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/tools/improve.md b/docs/docs/tools/improve.md index b2e2fc6b94..34a8af7bdf 100644 --- a/docs/docs/tools/improve.md +++ b/docs/docs/tools/improve.md @@ -267,7 +267,7 @@ In such cases, we recommend prioritizing the suggestion's detailed description, > `💎 feature` Platforms supported: GitHub, GitLab -The Qodo Merge implements an orchestrator agent that enables interactive code discussions, listening and responding to comments without requiring explicit tool calls. +Qodo Merge implements an orchestrator agent that enables interactive code discussions, listening and responding to comments without requiring explicit tool calls. The orchestrator intelligently analyzes your responses to determine if you want to implement a suggestion, ask a question, or request help, then delegates to the appropriate specialized tool. #### How it works From bfca4f25154c62d96df4153ac0652e02256acfed Mon Sep 17 00:00:00 2001 From: Yash-1511 Date: Tue, 29 Apr 2025 20:57:41 +0530 Subject: [PATCH 167/365] docs: update Gemini model path from google_ai_studio to gemini in configuration docs --- docs/docs/usage-guide/changing_a_model.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/usage-guide/changing_a_model.md b/docs/docs/usage-guide/changing_a_model.md index 76a67d2937..a60076b532 100644 --- a/docs/docs/usage-guide/changing_a_model.md +++ b/docs/docs/usage-guide/changing_a_model.md @@ -172,8 +172,8 @@ To use [Google AI Studio](https://aistudio.google.com/) models, set the relevant ```toml [config] # in configuration.toml -model="google_ai_studio/gemini-1.5-flash" -fallback_models=["google_ai_studio/gemini-1.5-flash"] +model="gemini/gemini-1.5-flash" +fallback_models=["gemini/gemini-1.5-flash"] [google_ai_studio] # in .secrets.toml gemini_api_key = "..." From 63f50bc1626dad66ccc2e0fc416d787571e7862d Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Wed, 30 Apr 2025 09:14:45 +0300 Subject: [PATCH 168/365] docs: convert RAG context enrichment applications to tabbed format --- .../core-abilities/rag_context_enrichment.md | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/docs/docs/core-abilities/rag_context_enrichment.md b/docs/docs/core-abilities/rag_context_enrichment.md index a2064c3fa8..aa706b2949 100644 --- a/docs/docs/core-abilities/rag_context_enrichment.md +++ b/docs/docs/core-abilities/rag_context_enrichment.md @@ -42,26 +42,23 @@ enable_rag=true ### Applications -#### 1\. The `/review` Tool +=== "`/review`" + The [`/review`](https://qodo-merge-docs.qodo.ai/tools/review/) tool offers the _Focus area from RAG data_ which contains feedback based on the RAG references analysis. + The complete list of references found relevant to the PR will be shown in the _References_ section, helping developers understand the broader context by exploring the provided references. -The [`/review`](https://qodo-merge-docs.qodo.ai/tools/review/) tool offers the _Focus area from RAG data_ which contains feedback based on the RAG references analysis. -The complete list of references found relevant to the PR will be shown in the _References_ section, helping developers understand the broader context by exploring the provided references. + ![RAGed review tool](https://codium.ai/images/pr_agent/rag_review.png){width=640} -![References](https://codium.ai/images/pr_agent/rag_review.png){width=640} +=== "`/implement`" + The [`/implement`](https://qodo-merge-docs.qodo.ai/tools/implement/) tool utilizes the RAG feature to provide comprehensive context of the repository codebase, allowing it to generate more refined code output. + The _References_ section contains links to the content used to support the code generation. -#### 2\. The `/implement` Tool + ![RAGed implement tool](https://codium.ai/images/pr_agent/rag_implement.png){width=640} -The [`/implement`](https://qodo-merge-docs.qodo.ai/tools/implement/) tool utilizes the RAG feature to provide comprehensive context of the repository codebase, allowing it to generate more refined code output. -The _References_ section contains links to the content used to support the code generation. +=== "`/ask`" + The [`/ask`](https://qodo-merge-docs.qodo.ai/tools/ask/) tool can access broader repository context through the RAG feature when answering questions that go beyond the PR scope alone. + The _References_ section displays the additional repository content consulted to formulate the answer. -![References](https://codium.ai/images/pr_agent/rag_implement.png){width=640} - -#### 3\. The `/ask` Tool - -The [`/ask`](https://qodo-merge-docs.qodo.ai/tools/ask/) tool can access broader repository context through the RAG feature when answering questions that go beyond the PR scope alone. -The _References_ section displays the additional repository content consulted to formulate the answer. - -![References](https://codium.ai/images/pr_agent/rag_ask.png){width=640} + ![RAGed ask tool](https://codium.ai/images/pr_agent/rag_ask.png){width=640} ## Limitations From 75a047f348e5e875f23dcf5a86d58501b8309a4c Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Wed, 30 Apr 2025 10:49:05 +0300 Subject: [PATCH 169/365] docs: add clarification about RAG capability tools list --- docs/docs/core-abilities/rag_context_enrichment.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/docs/core-abilities/rag_context_enrichment.md b/docs/docs/core-abilities/rag_context_enrichment.md index aa706b2949..ed8a197014 100644 --- a/docs/docs/core-abilities/rag_context_enrichment.md +++ b/docs/docs/core-abilities/rag_context_enrichment.md @@ -42,6 +42,8 @@ enable_rag=true ### Applications +The following represents the complete list of tools with RAG capability: + === "`/review`" The [`/review`](https://qodo-merge-docs.qodo.ai/tools/review/) tool offers the _Focus area from RAG data_ which contains feedback based on the RAG references analysis. The complete list of references found relevant to the PR will be shown in the _References_ section, helping developers understand the broader context by exploring the provided references. From 056adc6545fa6fccbfcf06f44950917121be66c6 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 30 Apr 2025 11:14:50 +0300 Subject: [PATCH 170/365] Update documentation for improve tool by removing GitHub link and clarifying text --- docs/docs/tools/improve.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/tools/improve.md b/docs/docs/tools/improve.md index f190990cdf..726f546296 100644 --- a/docs/docs/tools/improve.md +++ b/docs/docs/tools/improve.md @@ -1,6 +1,6 @@ ## Overview -The `improve` tool scans the PR code changes, and automatically generates [meaningful](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/pr_code_suggestions_prompts.toml#L41) suggestions for improving the PR code. +The `improve` tool scans the PR code changes, and automatically generates meaningful suggestions for improving the PR code. The tool can be triggered automatically every time a new PR is [opened](../usage-guide/automations_and_usage.md#github-app-automatic-tools-when-a-new-pr-is-opened), or it can be invoked manually by commenting on any PR: ```toml @@ -21,7 +21,7 @@ The tool can be triggered automatically every time a new PR is [opened](../usage Invoke the tool manually by commenting `/improve` on any PR. The code suggestions by default are presented as a single comment: -To edit [configurations](#configuration-options) related to the improve tool, use the following template: +To edit [configurations](#configuration-options) related to the `improve` tool, use the following template: ```toml /improve --pr_code_suggestions.some_config1=... --pr_code_suggestions.some_config2=... From b09eec265ef7d36c232063f76553efb6b53979ff Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Wed, 30 Apr 2025 13:29:45 +0300 Subject: [PATCH 171/365] docs: clarify that RAG capability is exclusive to specific tools --- docs/docs/core-abilities/rag_context_enrichment.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/core-abilities/rag_context_enrichment.md b/docs/docs/core-abilities/rag_context_enrichment.md index ed8a197014..05ebbaff03 100644 --- a/docs/docs/core-abilities/rag_context_enrichment.md +++ b/docs/docs/core-abilities/rag_context_enrichment.md @@ -42,7 +42,7 @@ enable_rag=true ### Applications -The following represents the complete list of tools with RAG capability: +RAG capability is exclusively available in the following tools: === "`/review`" The [`/review`](https://qodo-merge-docs.qodo.ai/tools/review/) tool offers the _Focus area from RAG data_ which contains feedback based on the RAG references analysis. From 968d2ee39b9693baf1b7fe60f9ea18ac46871dbc Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Wed, 30 Apr 2025 17:00:13 +0300 Subject: [PATCH 172/365] docs: add data privacy and contributing sections to README and update news --- README.md | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 064ff0d927..c18bfa7ca7 100644 --- a/README.md +++ b/README.md @@ -47,9 +47,20 @@ PR-Agent aims to help efficiently review and handle pull requests, by providing - [Qodo Merge](https://qodo-merge-docs.qodo.ai/overview/pr_agent_pro/) - [How it works](#how-it-works) - [Why use PR-Agent?](#why-use-pr-agent) +- [Data privacy](#data-privacy) +- [Contributing](#contributing) +- [Links](#links) ## News and Updates +## Apr 30, 2025 + +A new feature is now available in the `/improve` tool for Qodo Merge 💎 - Chat on code suggestions. + +image + +Read more about it [here](https://qodo-merge-docs.qodo.ai/tools/improve/#chat-on-code-suggestions). + ## Apr 16, 2025 New tool for Qodo Merge 💎 - `/scan_repo_discussions`. @@ -60,7 +71,7 @@ Read more about it [here](https://qodo-merge-docs.qodo.ai/tools/scan_repo_discus ## Apr 14, 2025 -GPT-4.1 is out. And its quite good on coding tasks... +GPT-4.1 is out. And it's quite good on coding tasks... https://openai.com/index/gpt-4-1/ @@ -74,10 +85,6 @@ This version includes a new tool, [Help Docs](https://qodo-merge-docs.qodo.ai/to `/help_docs` is now being used to provide immediate automatic feedback to any user who [opens an issue](https://github.com/qodo-ai/pr-agent/issues/1608#issue-2897328825) on PR-Agent's open-source project -### Feb 28, 2025 - -A new version, v0.27, was released. See release notes [here](https://github.com/qodo-ai/pr-agent/releases/tag/v0.27). - ## Overview
@@ -125,7 +132,7 @@ Supported commands per platform: | | [Impact Evaluation](https://qodo-merge-docs.qodo.ai/core-abilities/impact_evaluation/) 💎 | ✅ | ✅ | | | | | [Code Validation 💎](https://qodo-merge-docs.qodo.ai/core-abilities/code_validation/) | ✅ | ✅ | ✅ | ✅ | | | [Auto Best Practices 💎](https://qodo-merge-docs.qodo.ai/core-abilities/auto_best_practices/) | ✅ | | | | -- 💎 means this feature is available only in [Qodo-Merge](https://www.qodo.ai/pricing/) +- 💎 means this feature is available only in [Qodo Merge](https://www.qodo.ai/pricing/) [//]: # (- Support for additional git providers is described in [here](./docs/Full_environments.md)) ___ @@ -216,10 +223,10 @@ It does not have 'edit' access to your repo, for example, so it cannot update th [Qodo Merge](https://www.qodo.ai/pricing/) is a hosted version of PR-Agent, provided by Qodo. It is available for a monthly fee, and provides the following benefits: -1. **Fully managed** - We take care of everything for you - hosting, models, regular updates, and more. Installation is as simple as signing up and adding the Qodo Merge app to your GitHub\GitLab\BitBucket repo. +1. **Fully managed** - We take care of everything for you - hosting, models, regular updates, and more. Installation is as simple as signing up and adding the Qodo Merge app to your GitHub/GitLab/BitBucket repo. 2. **Improved privacy** - No data will be stored or used to train models. Qodo Merge will employ zero data retention, and will use an OpenAI account with zero data retention. 3. **Improved support** - Qodo Merge users will receive priority support, and will be able to request new features and capabilities. -4. **Extra features** -In addition to the benefits listed above, Qodo Merge will emphasize more customization, and the usage of static code analysis, in addition to LLM logic, to improve results. +4. **Extra features** - In addition to the benefits listed above, Qodo Merge will emphasize more customization, and the usage of static code analysis, in addition to LLM logic, to improve results. See [here](https://qodo-merge-docs.qodo.ai/overview/pr_agent_pro/) for a list of features available in Qodo Merge. ## How it works @@ -239,7 +246,7 @@ Here are some advantages of PR-Agent: - We emphasize **real-life practical usage**. Each tool (review, improve, ask, ...) has a single LLM call, no more. We feel that this is critical for realistic team usage - obtaining an answer quickly (~30 seconds) and affordably. - Our [PR Compression strategy](https://qodo-merge-docs.qodo.ai/core-abilities/#pr-compression-strategy) is a core ability that enables to effectively tackle both short and long PRs. - Our JSON prompting strategy enables to have **modular, customizable tools**. For example, the '/review' tool categories can be controlled via the [configuration](pr_agent/settings/configuration.toml) file. Adding additional categories is easy and accessible. -- We support **multiple git providers** (GitHub, Gitlab, Bitbucket), **multiple ways** to use the tool (CLI, GitHub Action, GitHub App, Docker, ...), and **multiple models** (GPT, Claude, Deepseek, ...) +- We support **multiple git providers** (GitHub, GitLab, BitBucket), **multiple ways** to use the tool (CLI, GitHub Action, GitHub App, Docker, ...), and **multiple models** (GPT, Claude, Deepseek, ...) ## Data privacy @@ -260,6 +267,10 @@ https://openai.com/enterprise-privacy - The [Qodo Merge Chrome extension](https://chromewebstore.google.com/detail/qodo-merge-ai-powered-cod/ephlnjeghhogofkifjloamocljapahnl) serves solely to modify the visual appearance of a GitHub PR screen. It does not transmit any user's repo or pull request code. Code is only sent for processing when a user submits a GitHub comment that activates a PR-Agent tool, in accordance with the standard privacy policy of Qodo-Merge. +## Contributing + +To contribute to the project, get started by reading our [Contributing Guide](https://github.com/qodo-ai/pr-agent/blob/b09eec265ef7d36c232063f76553efb6b53979ff/CONTRIBUTING.md). + ## Links - Discord community: https://discord.gg/kG35uSHDBc From d606672801582d123f5304386219b5bc9bbafc51 Mon Sep 17 00:00:00 2001 From: Mike Davies Date: Wed, 30 Apr 2025 14:09:40 -0700 Subject: [PATCH 173/365] Add ignore_repositories config for PR filtering What Changed? * Added support to ignore PRs/MRs from specific repositories in GitHub, Bitbucket, and GitLab webhook logic * Updated configuration.toml to include ignore_repositories option * Added unit tests for ignore_repositories across all supported providers --- pr_agent/servers/bitbucket_app.py | 16 +++++ pr_agent/servers/github_app.py | 8 +++ pr_agent/servers/gitlab_webhook.py | 8 +++ pr_agent/settings/configuration.toml | 1 + tests/unittest/test_ignore_repositories.py | 79 ++++++++++++++++++++++ 5 files changed, 112 insertions(+) create mode 100644 tests/unittest/test_ignore_repositories.py diff --git a/pr_agent/servers/bitbucket_app.py b/pr_agent/servers/bitbucket_app.py index a641adebc5..5692132810 100644 --- a/pr_agent/servers/bitbucket_app.py +++ b/pr_agent/servers/bitbucket_app.py @@ -127,11 +127,22 @@ def should_process_pr_logic(data) -> bool: source_branch = pr_data.get("source", {}).get("branch", {}).get("name", "") target_branch = pr_data.get("destination", {}).get("branch", {}).get("name", "") sender = _get_username(data) + repo_full_name = pr_data.get("destination", {}).get("repository", {}).get("full_name", "") + + print(f"DEBUG: repo_full_name={repo_full_name}, ignore_repos={get_settings().get('CONFIG.IGNORE_REPOSITORIES', [])}") + # logic to ignore PRs from specific repositories + ignore_repos = get_settings().get("CONFIG.IGNORE_REPOSITORIES", []) + if ignore_repos and repo_full_name: + if repo_full_name in ignore_repos: + print(f"DEBUG: Ignoring due to repo match: {repo_full_name}") + get_logger().info(f"Ignoring PR from repository '{repo_full_name}' due to 'config.ignore_repositories' setting") + return False # logic to ignore PRs from specific users ignore_pr_users = get_settings().get("CONFIG.IGNORE_PR_AUTHORS", []) if ignore_pr_users and sender: if sender in ignore_pr_users: + print(f"DEBUG: Ignoring due to user match: {sender}") get_logger().info(f"Ignoring PR from user '{sender}' due to 'config.ignore_pr_authors' setting") return False @@ -141,6 +152,7 @@ def should_process_pr_logic(data) -> bool: if not isinstance(ignore_pr_title_re, list): ignore_pr_title_re = [ignore_pr_title_re] if ignore_pr_title_re and any(re.search(regex, title) for regex in ignore_pr_title_re): + print(f"DEBUG: Ignoring due to title match: {title}") get_logger().info(f"Ignoring PR with title '{title}' due to config.ignore_pr_title setting") return False @@ -148,15 +160,19 @@ def should_process_pr_logic(data) -> bool: ignore_pr_target_branches = get_settings().get("CONFIG.IGNORE_PR_TARGET_BRANCHES", []) if (ignore_pr_source_branches or ignore_pr_target_branches): if any(re.search(regex, source_branch) for regex in ignore_pr_source_branches): + print(f"DEBUG: Ignoring due to source branch match: {source_branch}") get_logger().info( f"Ignoring PR with source branch '{source_branch}' due to config.ignore_pr_source_branches settings") return False if any(re.search(regex, target_branch) for regex in ignore_pr_target_branches): + print(f"DEBUG: Ignoring due to target branch match: {target_branch}") get_logger().info( f"Ignoring PR with target branch '{target_branch}' due to config.ignore_pr_target_branches settings") return False except Exception as e: + print(f"DEBUG: Exception in should_process_pr_logic: {e}") get_logger().error(f"Failed 'should_process_pr_logic': {e}") + print("DEBUG: Returning True from should_process_pr_logic") return True diff --git a/pr_agent/servers/github_app.py b/pr_agent/servers/github_app.py index 4576bd9d7b..6354b2b983 100644 --- a/pr_agent/servers/github_app.py +++ b/pr_agent/servers/github_app.py @@ -258,6 +258,14 @@ def should_process_pr_logic(body) -> bool: source_branch = pull_request.get("head", {}).get("ref", "") target_branch = pull_request.get("base", {}).get("ref", "") sender = body.get("sender", {}).get("login") + repo_full_name = body.get("repository", {}).get("full_name", "") + + # logic to ignore PRs from specific repositories + ignore_repos = get_settings().get("CONFIG.IGNORE_REPOSITORIES", []) + if ignore_repos and repo_full_name: + if repo_full_name in ignore_repos: + get_logger().info(f"Ignoring PR from repository '{repo_full_name}' due to 'config.ignore_repositories' setting") + return False # logic to ignore PRs from specific users ignore_pr_users = get_settings().get("CONFIG.IGNORE_PR_AUTHORS", []) diff --git a/pr_agent/servers/gitlab_webhook.py b/pr_agent/servers/gitlab_webhook.py index 0aef933dee..7d1787026a 100644 --- a/pr_agent/servers/gitlab_webhook.py +++ b/pr_agent/servers/gitlab_webhook.py @@ -103,6 +103,14 @@ def should_process_pr_logic(data) -> bool: return False title = data['object_attributes'].get('title') sender = data.get("user", {}).get("username", "") + repo_full_name = data.get('project', {}).get('path_with_namespace', "") + + # logic to ignore PRs from specific repositories + ignore_repos = get_settings().get("CONFIG.IGNORE_REPOSITORIES", []) + if ignore_repos and repo_full_name: + if repo_full_name in ignore_repos: + get_logger().info(f"Ignoring MR from repository '{repo_full_name}' due to 'config.ignore_repositories' setting") + return False # logic to ignore PRs from specific users ignore_pr_users = get_settings().get("CONFIG.IGNORE_PR_AUTHORS", []) diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index e63b7ea8b7..cd7ebd160c 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -55,6 +55,7 @@ ignore_pr_target_branches = [] # a list of regular expressions of target branche ignore_pr_source_branches = [] # a list of regular expressions of source branches to ignore from PR agent when an PR is created ignore_pr_labels = [] # labels to ignore from PR agent when an PR is created ignore_pr_authors = [] # authors to ignore from PR agent when an PR is created +ignore_repositories = [] # list of repository full names (e.g. "org/repo") to ignore from PR agent processing # is_auto_command = false # will be auto-set to true if the command is triggered by an automation enable_ai_metadata = false # will enable adding ai metadata diff --git a/tests/unittest/test_ignore_repositories.py b/tests/unittest/test_ignore_repositories.py new file mode 100644 index 0000000000..b8b0200934 --- /dev/null +++ b/tests/unittest/test_ignore_repositories.py @@ -0,0 +1,79 @@ +import pytest +from pr_agent.servers.github_app import should_process_pr_logic as github_should_process_pr_logic +from pr_agent.servers.bitbucket_app import should_process_pr_logic as bitbucket_should_process_pr_logic +from pr_agent.servers.gitlab_webhook import should_process_pr_logic as gitlab_should_process_pr_logic +from pr_agent.config_loader import get_settings + +def make_bitbucket_payload(full_name): + return { + "data": { + "pullrequest": { + "title": "Test PR", + "source": {"branch": {"name": "feature/test"}}, + "destination": { + "branch": {"name": "main"}, + "repository": {"full_name": full_name} + } + }, + "actor": {"username": "user", "type": "user"} + } + } + +def make_github_body(full_name): + return { + "pull_request": {}, + "repository": {"full_name": full_name}, + "sender": {"login": "user"} + } + +def make_gitlab_body(full_name): + return { + "object_attributes": {"title": "Test MR"}, + "project": {"path_with_namespace": full_name} + } + +PROVIDERS = [ + ("github", github_should_process_pr_logic, make_github_body), + ("bitbucket", bitbucket_should_process_pr_logic, make_bitbucket_payload), + ("gitlab", gitlab_should_process_pr_logic, make_gitlab_body), +] + +class TestIgnoreRepositories: + def setup_method(self): + get_settings().set("CONFIG.IGNORE_REPOSITORIES", []) + + @pytest.mark.parametrize("provider_name, provider_func, body_func", PROVIDERS) + def test_should_ignore_matching_repository(self, provider_name, provider_func, body_func): + get_settings().set("CONFIG.IGNORE_REPOSITORIES", ["org/repo-to-ignore"]) + body = { + "pull_request": {}, + "repository": {"full_name": "org/repo-to-ignore"}, + "sender": {"login": "user"} + } + result = provider_func(body_func(body["repository"]["full_name"])) + print(f"DEBUG: Provider={provider_name}, test_should_ignore_matching_repository, result={result}") + assert result is False, f"{provider_name}: PR from ignored repository should be ignored (return False)" + + @pytest.mark.parametrize("provider_name, provider_func, body_func", PROVIDERS) + def test_should_not_ignore_non_matching_repository(self, provider_name, provider_func, body_func): + get_settings().set("CONFIG.IGNORE_REPOSITORIES", ["org/repo-to-ignore"]) + body = { + "pull_request": {}, + "repository": {"full_name": "org/other-repo"}, + "sender": {"login": "user"} + } + result = provider_func(body_func(body["repository"]["full_name"])) + print(f"DEBUG: Provider={provider_name}, test_should_not_ignore_non_matching_repository, result={result}") + assert result is True, f"{provider_name}: PR from non-ignored repository should not be ignored (return True)" + + @pytest.mark.parametrize("provider_name, provider_func, body_func", PROVIDERS) + def test_should_not_ignore_when_config_empty(self, provider_name, provider_func, body_func): + get_settings().set("CONFIG.IGNORE_REPOSITORIES", []) + body = { + "pull_request": {}, + "repository": {"full_name": "org/repo-to-ignore"}, + "sender": {"login": "user"} + } + result = provider_func(body_func(body["repository"]["full_name"])) + print(f"DEBUG: Provider={provider_name}, test_should_not_ignore_when_config_empty, result={result}") + assert result is True, f"{provider_name}: PR should not be ignored if ignore_repositories config is empty" \ No newline at end of file From 897a5c66fae8afb32a884d86b89b16dbcb606cdd Mon Sep 17 00:00:00 2001 From: ofir-frd <85901822+ofir-frd@users.noreply.github.com> Date: Thu, 1 May 2025 08:31:19 +0300 Subject: [PATCH 174/365] Update fallback model --- pr_agent/settings/configuration.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index e63b7ea8b7..1b7935952c 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -7,7 +7,7 @@ [config] # models model="o4-mini" -fallback_models=["gpt-4.1"] +fallback_models=["gpt-4.1-2025-04-14"] #model_reasoning="o4-mini" # dedictated reasoning model for self-reflection #model_weak="gpt-4o" # optional, a weaker model to use for some easier tasks # CLI From 914d053801608e226497dd08d2459b539de78262 Mon Sep 17 00:00:00 2001 From: ofir-frd <85901822+ofir-frd@users.noreply.github.com> Date: Thu, 1 May 2025 08:38:21 +0300 Subject: [PATCH 175/365] improve.md: refine a sub-title --- docs/docs/tools/improve.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/tools/improve.md b/docs/docs/tools/improve.md index c787f420a2..b2203b7af8 100644 --- a/docs/docs/tools/improve.md +++ b/docs/docs/tools/improve.md @@ -270,7 +270,7 @@ In such cases, we recommend prioritizing the suggestion's detailed description, Qodo Merge implements an orchestrator agent that enables interactive code discussions, listening and responding to comments without requiring explicit tool calls. The orchestrator intelligently analyzes your responses to determine if you want to implement a suggestion, ask a question, or request help, then delegates to the appropriate specialized tool. -#### How it works +#### Setup and Activation Enable interactive code discussions by adding the following to your configuration file (default is `True`): From 8edacf08bbbb151d900695d005549f5621ae1a46 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Fri, 2 May 2025 09:13:21 +0300 Subject: [PATCH 176/365] Use slim Docker image and install git and curl dependencies --- docker/Dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index ee9e833475..7f41686b27 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,4 +1,6 @@ -FROM python:3.12.3 AS base +FROM python:3.12.10-slim AS base + +RUN apt update && apt install --no-install-recommends -y git curl && rm -rf /var/lib/apt/lists/* WORKDIR /app ADD pyproject.toml . From 5a3010389d3f164d6dae3e1308c31273221e66a8 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Fri, 2 May 2025 09:16:43 +0300 Subject: [PATCH 177/365] Optimize Docker image by cleaning apt cache after installing dependencies --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 7f41686b27..9e83e37bd1 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.12.10-slim AS base -RUN apt update && apt install --no-install-recommends -y git curl && rm -rf /var/lib/apt/lists/* +RUN apt update && apt install --no-install-recommends -y git curl && apt-get clean && rm -rf /var/lib/apt/lists/* WORKDIR /app ADD pyproject.toml . From f0db5cb43bcc7fdc8b374d078ba8c985acb34b41 Mon Sep 17 00:00:00 2001 From: ofir-frd <85901822+ofir-frd@users.noreply.github.com> Date: Sun, 4 May 2025 16:35:14 +0300 Subject: [PATCH 178/365] revert model fixation --- pr_agent/settings/configuration.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index 1b7935952c..e63b7ea8b7 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -7,7 +7,7 @@ [config] # models model="o4-mini" -fallback_models=["gpt-4.1-2025-04-14"] +fallback_models=["gpt-4.1"] #model_reasoning="o4-mini" # dedictated reasoning model for self-reflection #model_weak="gpt-4o" # optional, a weaker model to use for some easier tasks # CLI From f85587fb1f1c9bccfe98649bf3c1ddb4d6bb95ba Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sun, 4 May 2025 17:47:25 +0300 Subject: [PATCH 179/365] docs: add configuration options for best practices in improve tool --- docs/docs/tools/improve.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/docs/tools/improve.md b/docs/docs/tools/improve.md index b2203b7af8..c473ba7bbc 100644 --- a/docs/docs/tools/improve.md +++ b/docs/docs/tools/improve.md @@ -157,6 +157,13 @@ Tips for writing an effective `best_practices.md` file: - AI models may not process effectively very long documents - Long files tend to contain generic guidelines already known to AI +To control the number of best practices suggestions generated by the `improve` tool, give the following configuration: + +```toml +[best_practices] +num_best_practice_suggestions = 2 +``` + #### Local and global best practices By default, Qodo Merge will look for a local `best_practices.md` in the root of the relevant local repo. @@ -238,6 +245,11 @@ extra_instructions = "" max_patterns = 5 ``` +### Multiple best practices sources + +The `improve` tool will combine best practices from all available sources - global configuration, local configuration, and auto-generated files - to provide you with comprehensive recommendations. + + ### Combining 'extra instructions' and 'best practices' > `💎 feature` From df1d859e54582f2d45850ecb762cb4c3f6cf7e71 Mon Sep 17 00:00:00 2001 From: Thomas De Keulenaer <11250711+twdkeule@users.noreply.github.com> Date: Tue, 6 May 2025 15:01:48 +0200 Subject: [PATCH 180/365] Azure devops: parse PR url starting from the end --- .../git_providers/azuredevops_provider.py | 29 +++++++++---------- tests/unittest/test_azure_devops_parsing.py | 7 +++++ 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/pr_agent/git_providers/azuredevops_provider.py b/pr_agent/git_providers/azuredevops_provider.py index 2e1c57b6af..80bf68c56b 100644 --- a/pr_agent/git_providers/azuredevops_provider.py +++ b/pr_agent/git_providers/azuredevops_provider.py @@ -543,22 +543,21 @@ def remove_reaction(self, issue_comment_id: int, reaction_id: int) -> bool: @staticmethod def _parse_pr_url(pr_url: str) -> Tuple[str, str, int]: parsed_url = urlparse(pr_url) - path_parts = parsed_url.path.strip("/").split("/") - if "pullrequest" not in path_parts: - raise ValueError( - "The provided URL does not appear to be a Azure DevOps PR URL" - ) - if len(path_parts) == 6: # "https://dev.azure.com/organization/project/_git/repo/pullrequest/1" - workspace_slug = path_parts[1] - repo_slug = path_parts[3] - pr_number = int(path_parts[5]) - elif len(path_parts) == 5: # 'https://organization.visualstudio.com/project/_git/repo/pullrequest/1' - workspace_slug = path_parts[0] - repo_slug = path_parts[2] - pr_number = int(path_parts[4]) - else: - raise ValueError("The provided URL does not appear to be a Azure DevOps PR URL") + num_parts = len(path_parts) + if num_parts < 5: + raise ValueError("The provided URL has insufficient path components for an Azure DevOps PR URL") + + # Verify that the second-to-last path component is "pullrequest" + if path_parts[num_parts - 2] != "pullrequest": + raise ValueError("The provided URL does not follow the expected Azure DevOps PR URL format") + + workspace_slug = path_parts[num_parts - 5] + repo_slug = path_parts[num_parts - 3] + try: + pr_number = int(path_parts[num_parts - 1]) + except ValueError as e: + raise ValueError("Cannot parse PR number in the provided URL") from e return workspace_slug, repo_slug, pr_number diff --git a/tests/unittest/test_azure_devops_parsing.py b/tests/unittest/test_azure_devops_parsing.py index 441d4e356e..abe0b1fb24 100644 --- a/tests/unittest/test_azure_devops_parsing.py +++ b/tests/unittest/test_azure_devops_parsing.py @@ -13,3 +13,10 @@ def test_visualstudio_address(self): # workspace_slug, repo_slug, pr_number assert AzureDevopsProvider._parse_pr_url(pr_url) == ("project", "repo", 1) + + def test_self_hosted_address(self): + pr_url = "http://server.be:8080/tfs/department/project/_git/repo/pullrequest/1" + + # workspace_slug, repo_slug, pr_number + assert AzureDevopsProvider._parse_pr_url(pr_url) == ("project", "repo", 1) + From 7a6a28d2b990a67255a8422692ad4f17352c2403 Mon Sep 17 00:00:00 2001 From: "irfan.putra" Date: Wed, 7 May 2025 11:54:07 +0700 Subject: [PATCH 181/365] feat: add openrouter support in litellm --- pr_agent/algo/ai_handlers/litellm_ai_handler.py | 12 ++++++++++++ pr_agent/settings/.secrets_template.toml | 5 ++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/pr_agent/algo/ai_handlers/litellm_ai_handler.py b/pr_agent/algo/ai_handlers/litellm_ai_handler.py index 61af7383cc..ba4630f409 100644 --- a/pr_agent/algo/ai_handlers/litellm_ai_handler.py +++ b/pr_agent/algo/ai_handlers/litellm_ai_handler.py @@ -118,6 +118,18 @@ def __init__(self): litellm.api_base = self.api_base openai.api_base = self.api_base + # Support for Openrouter models + if get_settings().get("OPENROUTER.KEY", None): + openrouter_api_key = get_settings().get("OPENROUTER.KEY", None) + os.environ["OPENROUTER_API_KEY"] = openrouter_api_key + litellm.api_key = openrouter_api_key + openai.api_key = openrouter_api_key + + openrouter_api_base = get_settings().get("OPENROUTER.API_BASE", "https://openrouter.ai/api/v1") + os.environ["OPENROUTER_API_BASE"] = openrouter_api_base + self.api_base = openrouter_api_base + litellm.api_base = openrouter_api_base + # Models that only use user meessage self.user_message_only_models = USER_MESSAGE_ONLY_MODELS diff --git a/pr_agent/settings/.secrets_template.toml b/pr_agent/settings/.secrets_template.toml index 05f7bc0e4b..1022f8fa09 100644 --- a/pr_agent/settings/.secrets_template.toml +++ b/pr_agent/settings/.secrets_template.toml @@ -107,4 +107,7 @@ key = "" client_id = "" # Your Azure AD application client ID client_secret = "" # Your Azure AD application client secret tenant_id = "" # Your Azure AD tenant ID -api_base = "" # Your Azure OpenAI service base URL (e.g., https://openai.xyz.com/) \ No newline at end of file +api_base = "" # Your Azure OpenAI service base URL (e.g., https://openai.xyz.com/) + +[openrouter] +key = "" \ No newline at end of file From 5061fb5a24243878019e8557a2369123f4a51818 Mon Sep 17 00:00:00 2001 From: "irfan.putra" Date: Wed, 7 May 2025 12:04:45 +0700 Subject: [PATCH 182/365] docs: add support for openrouter --- docs/docs/usage-guide/changing_a_model.md | 16 ++++++++++++++++ pr_agent/settings/.secrets_template.toml | 3 ++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/docs/docs/usage-guide/changing_a_model.md b/docs/docs/usage-guide/changing_a_model.md index a60076b532..c0b2bc287b 100644 --- a/docs/docs/usage-guide/changing_a_model.md +++ b/docs/docs/usage-guide/changing_a_model.md @@ -278,6 +278,22 @@ key = "..." # your Codestral api key (you can obtain a Codestral key from [here](https://console.mistral.ai/codestral)) +### Openrouter + +To use model from Openrouter, for example, set: + +```toml +[config] # in configuration.toml +model="openrouter/anthropic/claude-3.7-sonnet" +fallback_models=["openrouter/deepseek/deepseek-chat"] +custom_model_max_tokens=20000 + +[openrouter] # in .secrets.toml or passed an environment variable openrouter__key +key = "..." # your openrouter api key +``` + +(you can obtain an Openrouter API key from [here](https://openrouter.ai/settings/keys)) + ### Custom models If the relevant model doesn't appear [here](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/algo/__init__.py), you can still use it as a custom model: diff --git a/pr_agent/settings/.secrets_template.toml b/pr_agent/settings/.secrets_template.toml index 1022f8fa09..6572677dea 100644 --- a/pr_agent/settings/.secrets_template.toml +++ b/pr_agent/settings/.secrets_template.toml @@ -110,4 +110,5 @@ tenant_id = "" # Your Azure AD tenant ID api_base = "" # Your Azure OpenAI service base URL (e.g., https://openai.xyz.com/) [openrouter] -key = "" \ No newline at end of file +key = "" +api_base = "" \ No newline at end of file From 4a8722acd52e9791b2454f8e65d303abf5372651 Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Wed, 7 May 2025 11:21:59 +0300 Subject: [PATCH 183/365] docs: Add tip about using keywords to direct the agent in code suggestions --- docs/docs/tools/improve.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/docs/tools/improve.md b/docs/docs/tools/improve.md index c473ba7bbc..e5e9ea4dc9 100644 --- a/docs/docs/tools/improve.md +++ b/docs/docs/tools/improve.md @@ -300,6 +300,9 @@ enable_chat_in_code_suggestions = true #### Explore the available interaction patterns: +!!! tip "Tip: Direct the agent with keywords" + Use "implement" or "apply" for code generation. Use "explain", "why", or "how" for information and help. + === "Asking for Details" ![Chat on code suggestions ask](https://codium.ai/images/pr_agent/improve_chat_on_code_suggestions_ask.png){width=512} From a11e97b5f5a60741a6ba680bf0b801c3feb01bea Mon Sep 17 00:00:00 2001 From: Maxnster Date: Wed, 7 May 2025 18:26:12 +0800 Subject: [PATCH 184/365] fix: Prevent duplicate language instructions in extra_instructions --- pr_agent/agent/pr_agent.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/pr_agent/agent/pr_agent.py b/pr_agent/agent/pr_agent.py index 54e249b732..0a42a50ddb 100644 --- a/pr_agent/agent/pr_agent.py +++ b/pr_agent/agent/pr_agent.py @@ -84,10 +84,18 @@ async def handle_request(self, pr_url, request, notify=None) -> bool: if str(type(setting)) == "": if hasattr(setting, 'extra_instructions'): current_extra_instructions = setting.extra_instructions - if current_extra_instructions: - setting.extra_instructions = current_extra_instructions+ f"\n======\n\nIn addition, Your response MUST be written in the language corresponding to local code: {response_language}. This is crucial." - else: - setting.extra_instructions = f"Your response MUST be written in the language corresponding to locale code: '{response_language}'. This is crucial." + + # Define the language-specific instruction and the separator + lang_instruction_text = f"Your response MUST be written in the language corresponding to locale code: '{response_language}'. This is crucial." + separator_text = "\n======\n\nIn addition, " + + # Check if the specific language instruction is already present to avoid duplication + if lang_instruction_text not in str(current_extra_instructions): + if current_extra_instructions: # If there's existing text + setting.extra_instructions = str(current_extra_instructions) + separator_text + lang_instruction_text + else: # If extra_instructions was None or empty + setting.extra_instructions = lang_instruction_text + # If lang_instruction_text is already present, do nothing. action = action.lstrip("/").lower() if action not in command2class: From 38f00386b4fa316eef426747d4bbd64a6e6b8dfb Mon Sep 17 00:00:00 2001 From: Simon Stamm Date: Thu, 8 May 2025 12:50:54 +0200 Subject: [PATCH 185/365] fix(gitlab): trigger when MR changes from draft to ready --- pr_agent/servers/gitlab_webhook.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/pr_agent/servers/gitlab_webhook.py b/pr_agent/servers/gitlab_webhook.py index 0aef933dee..7a753c9e74 100644 --- a/pr_agent/servers/gitlab_webhook.py +++ b/pr_agent/servers/gitlab_webhook.py @@ -86,9 +86,19 @@ def is_draft(data) -> bool: def is_draft_ready(data) -> bool: try: if 'draft' in data.get('changes', {}): - if data['changes']['draft']['previous'] == 'true' and data['changes']['draft']['current'] == 'false': + # Handle both boolean values and string values for compatibility + previous = data['changes']['draft']['previous'] + current = data['changes']['draft']['current'] + + # Convert to boolean if they're strings + if isinstance(previous, str): + previous = previous.lower() == 'true' + if isinstance(current, str): + current = current.lower() == 'true' + + if previous is True and current is False: return True - + # for gitlab server version before 16 elif 'title' in data.get('changes', {}): if 'Draft:' in data['changes']['title']['previous'] and 'Draft:' not in data['changes']['title']['current']: From c357f09967bed5abf37edc5a9146274ec58eeaa8 Mon Sep 17 00:00:00 2001 From: chilln Date: Fri, 9 May 2025 11:12:04 +0900 Subject: [PATCH 186/365] fix:correct typo in logger parameter from 'artifacts' to 'artifact' --- pr_agent/tools/pr_description.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/tools/pr_description.py b/pr_agent/tools/pr_description.py index 0a4f7c5946..3763be040f 100644 --- a/pr_agent/tools/pr_description.py +++ b/pr_agent/tools/pr_description.py @@ -95,7 +95,7 @@ async def run(self): get_logger().info(f"Generating a PR description for pr_id: {self.pr_id}") relevant_configs = {'pr_description': dict(get_settings().pr_description), 'config': dict(get_settings().config)} - get_logger().debug("Relevant configs", artifacts=relevant_configs) + get_logger().debug("Relevant configs", artifact=relevant_configs) if get_settings().config.publish_output and not get_settings().config.get('is_auto_command', False): self.git_provider.publish_comment("Preparing PR description...", is_temporary=True) From b658986a5e73de509b50d22109a1148d058927c1 Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Fri, 9 May 2025 14:24:07 +0300 Subject: [PATCH 187/365] docs: improve readability of improve tool documentation with tabs --- docs/docs/tools/improve.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/docs/tools/improve.md b/docs/docs/tools/improve.md index e5e9ea4dc9..6a5680e9ba 100644 --- a/docs/docs/tools/improve.md +++ b/docs/docs/tools/improve.md @@ -7,9 +7,11 @@ The tool can be triggered automatically every time a new PR is [opened](../usage /improve ``` -![code_suggestions_as_comment_closed.png](https://codium.ai/images/pr_agent/code_suggestions_as_comment_closed.png){width=512} +=== "Suggestions Overview" + ![code_suggestions_as_comment_closed](https://codium.ai/images/pr_agent/code_suggestions_as_comment_closed.png){width=512} -![code_suggestions_as_comment_open.png](https://codium.ai/images/pr_agent/code_suggestions_as_comment_open.png){width=512} +=== "Implementation Details" + ![code_suggestions_as_comment_open](https://codium.ai/images/pr_agent/code_suggestions_as_comment_open.png){width=512} !!! note "The following features are available only for Qodo Merge 💎 users:" - The `Apply this suggestion` checkbox, which interactively converts a suggestion into a committable code comment From 171be6727ab3c561f4a0d7a0fc7614aaffde0244 Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Fri, 9 May 2025 14:53:03 +0300 Subject: [PATCH 188/365] docs: fix typos and improve wording in improve tool documentation --- docs/docs/tools/improve.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/docs/tools/improve.md b/docs/docs/tools/improve.md index 6a5680e9ba..8d0850e687 100644 --- a/docs/docs/tools/improve.md +++ b/docs/docs/tools/improve.md @@ -29,7 +29,7 @@ To edit [configurations](#configuration-options) related to the `improve` tool, /improve --pr_code_suggestions.some_config1=... --pr_code_suggestions.some_config2=... ``` -For example, you can choose to present all the suggestions as commitable code comments, by running the following command: +For example, you can choose to present all the suggestions as committable code comments, by running the following command: ```toml /improve --pr_code_suggestions.commitable_code_suggestions=true @@ -68,13 +68,13 @@ Qodo Merge tracks two types of implementations for tracking implemented suggesti - Direct implementation - when the user directly applies the suggestion by clicking the `Apply` checkbox. - Indirect implementation - when the user implements the suggestion in their IDE environment. In this case, Qodo Merge will utilize, after each commit, a dedicated logic to identify if a suggestion was implemented, and will mark it as implemented. -![code_suggestions_asses_impact](https://codium.ai/images/pr_agent/code_suggestions_asses_impact.png){width=512} +![code_suggestions_assess_impact](https://codium.ai/images/pr_agent/code_suggestions_asses_impact.png){width=512} In post-process, Qodo Merge counts the number of suggestions that were implemented, and provides general statistics and insights about the suggestions' impact on the PR process. -![code_suggestions_asses_impact_stats_1](https://codium.ai/images/pr_agent/code_suggestions_asses_impact_stats_1.png){width=512} +![code_suggestions_assess_impact_stats_1](https://codium.ai/images/pr_agent/code_suggestions_asses_impact_stats_1.png){width=512} -![code_suggestions_asses_impact_stats_2](https://codium.ai/images/pr_agent/code_suggestions_asses_impact_stats_2.png){width=512} +![code_suggestions_assess_impact_stats_2](https://codium.ai/images/pr_agent/code_suggestions_asses_impact_stats_2.png){width=512} ## Suggestion tracking @@ -119,7 +119,7 @@ Examples for possible instructions: ```toml [pr_code_suggestions] extra_instructions="""\ -(1) Answer in japanese +(1) Answer in Japanese (2) Don't suggest to add try-except block (3) Ignore changes in toml files ... @@ -138,7 +138,7 @@ This page can contain a list of best practices, coding standards, and guidelines The AI model will use this `best_practices.md` file as a reference, and in case the PR code violates any of the guidelines, it will create additional suggestions, with a dedicated label: `Organization best practice`. -Example for a python `best_practices.md` content: +Example for a Python `best_practices.md` content: ```markdown ## Project best practices @@ -273,7 +273,7 @@ Each generated suggestion consists of three key elements: 3. A diff snippet showing the recommended code modification (before and after) We advise users to apply critical analysis and judgment when implementing the proposed suggestions. -In addition to mistakes (which may happen, but are rare), sometimes the presented code modification may serve more as an _illustrative example_ than a direct applicable solution. +In addition to mistakes (which may happen, but are rare), sometimes the presented code modification may serve more as an _illustrative example_ than a directly applicable solution. In such cases, we recommend prioritizing the suggestion's detailed description, using the diff snippet primarily as a supporting reference. @@ -320,7 +320,7 @@ enable_chat_in_code_suggestions = true Our recommended approach for presenting code suggestions is through a [table](https://qodo-merge-docs.qodo.ai/tools/improve/#overview) (`--pr_code_suggestions.commitable_code_suggestions=false`). This method significantly reduces the PR footprint and allows for quick and easy digestion of multiple suggestions. -We also offer a complementary **dual publishing mode**. When enabled, suggestions exceeding a certain score threshold are not only displayed in the table, but also presented as commitable PR comments. +We also offer a complementary **dual publishing mode**. When enabled, suggestions exceeding a certain score threshold are not only displayed in the table, but also presented as committable PR comments. This mode helps highlight suggestions deemed more critical. To activate dual publishing mode, use the following setting: @@ -330,7 +330,7 @@ To activate dual publishing mode, use the following setting: dual_publishing_score_threshold = x ``` -Where x represents the minimum score threshold (>=) for suggestions to be presented as commitable PR comments in addition to the table. Default is -1 (disabled). +Where x represents the minimum score threshold (>=) for suggestions to be presented as committable PR comments in addition to the table. Default is -1 (disabled). ### Self-review @@ -478,7 +478,7 @@ Note: Chunking is primarily relevant for large PRs. For most PRs (up to 600 line commitable_code_suggestions - If set to true, the tool will display the suggestions as commitable code comments. Default is false. + If set to true, the tool will display the suggestions as committable code comments. Default is false. enable_chat_in_code_suggestions @@ -486,7 +486,7 @@ Note: Chunking is primarily relevant for large PRs. For most PRs (up to 600 line dual_publishing_score_threshold - Minimum score threshold for suggestions to be presented as commitable PR comments in addition to the table. Default is -1 (disabled). + Minimum score threshold for suggestions to be presented as committable PR comments in addition to the table. Default is -1 (disabled). focus_only_on_problems @@ -553,6 +553,6 @@ Note: Chunking is primarily relevant for large PRs. For most PRs (up to 600 line - **Self-reflection:** The suggestions aim to enable developers to _self-reflect_ and improve their pull requests. This process can help to identify blind spots, uncover missed edge cases, and enhance code readability and coherency. Even when a specific code suggestion isn't suitable, the underlying issue it highlights often reveals something important that might deserve attention. - **Bug detection:** The suggestions also alert on any _critical bugs_ that may have been identified during the analysis. This provides an additional safety net to catch potential issues before they make it into production. It's perfectly acceptable to implement only the suggestions you find valuable for your specific context. - **Hierarchy:** Presenting the suggestions in a structured hierarchical table enables the user to _quickly_ understand them, and to decide which ones are relevant and which are not. -- **Customization:** To guide the model to suggestions that are more relevant to the specific needs of your project, we recommend to use the [`extra_instructions`](https://qodo-merge-docs.qodo.ai/tools/improve/#extra-instructions-and-best-practices) and [`best practices`](https://qodo-merge-docs.qodo.ai/tools/improve/#best-practices) fields. +- **Customization:** To guide the model to suggestions that are more relevant to the specific needs of your project, we recommend using the [`extra_instructions`](https://qodo-merge-docs.qodo.ai/tools/improve/#extra-instructions-and-best-practices) and [`best practices`](https://qodo-merge-docs.qodo.ai/tools/improve/#best-practices) fields. - **Model Selection:** SaaS users can also [choose](https://qodo-merge-docs.qodo.ai/usage-guide/qodo_merge_models/) between different models. For specific programming languages or use cases, some models may perform better than others. - **Interactive usage:** The interactive [PR chat](https://qodo-merge-docs.qodo.ai/chrome-extension/) also provides an easy way to get more tailored suggestions and feedback from the AI model. From 6062c99c47fe3f4242571d4468d35e02cac53489 Mon Sep 17 00:00:00 2001 From: joosomi Date: Fri, 9 May 2025 21:20:04 +0900 Subject: [PATCH 189/365] fix: avoid duplicate header for list types to prevent markdown break --- pr_agent/tools/pr_description.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pr_agent/tools/pr_description.py b/pr_agent/tools/pr_description.py index 0a4f7c5946..6c021c3a6b 100644 --- a/pr_agent/tools/pr_description.py +++ b/pr_agent/tools/pr_description.py @@ -507,10 +507,10 @@ def _prepare_pr_answer_with_markers(self) -> Tuple[str, str, str, List[dict]]: ai_type = self.data.get('type') if ai_type and not re.search(r'', body): if isinstance(ai_type, list): - pr_types = [f"{ai_header}{t}" for t in ai_type] - pr_type = ','.join(pr_types) + pr_type = ', '.join(ai_type) else: - pr_type = f"{ai_header}{ai_type}" + pr_type = ai_type + pr_type = f"{ai_header}{pr_type}" body = body.replace('pr_agent:type', pr_type) ai_summary = self.data.get('description') From 57eaba0e751c2bf82cc1e6ab9018bd489d7c82e0 Mon Sep 17 00:00:00 2001 From: joosomi Date: Fri, 9 May 2025 21:33:42 +0900 Subject: [PATCH 190/365] fix: ensure string conversion for list elements --- pr_agent/tools/pr_description.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/tools/pr_description.py b/pr_agent/tools/pr_description.py index 6c021c3a6b..2e6e3a6a6a 100644 --- a/pr_agent/tools/pr_description.py +++ b/pr_agent/tools/pr_description.py @@ -507,7 +507,7 @@ def _prepare_pr_answer_with_markers(self) -> Tuple[str, str, str, List[dict]]: ai_type = self.data.get('type') if ai_type and not re.search(r'', body): if isinstance(ai_type, list): - pr_type = ', '.join(ai_type) + pr_type = ', '.join(str(t) for t in ai_type) else: pr_type = ai_type pr_type = f"{ai_header}{pr_type}" From 625085a0f8c9cec92ee954000d9f0de35be4de0c Mon Sep 17 00:00:00 2001 From: mrT23 Date: Fri, 9 May 2025 16:16:26 +0300 Subject: [PATCH 191/365] docs: add information about feedback on draft PRs --- docs/docs/usage-guide/automations_and_usage.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/docs/usage-guide/automations_and_usage.md b/docs/docs/usage-guide/automations_and_usage.md index c13bcd3489..9c3e29fdef 100644 --- a/docs/docs/usage-guide/automations_and_usage.md +++ b/docs/docs/usage-guide/automations_and_usage.md @@ -111,6 +111,17 @@ pr_commands = [ This means that when a new PR is opened/reopened or marked as ready for review, Qodo Merge will run the `describe`, `review` and `improve` tools. +**Draft PRs:** + +By default, draft PRs are not considered for automatic tools, but you can change this by setting the `feedback_on_draft_pr` parameter to `true` in the configuration file. + +```toml +[github_app] +feedback_on_draft_pr = true +``` + +**Changing default tool parameters:** + You can override the default tool parameters by using one the three options for a [configuration file](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/): **wiki**, **local**, or **global**. For example, if your configuration file contains: @@ -121,6 +132,9 @@ generate_ai_title = true Every time you run the `describe` tool (including automatic runs) the PR title will be generated by the AI. + +**Parameters for automated runs:** + You can customize configurations specifically for automated runs by using the `--config_path=` parameter. For instance, to modify the `review` tool settings only for newly opened PRs, use: From 6596f782e112b2e1aee4b89c7236f7e841336712 Mon Sep 17 00:00:00 2001 From: isExample Date: Sat, 10 May 2025 16:05:37 +0900 Subject: [PATCH 192/365] docs: clarify that .pr_agent.toml can be updated post PR creation --- docs/docs/usage-guide/configuration_options.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/usage-guide/configuration_options.md b/docs/docs/usage-guide/configuration_options.md index dc0ac8d06f..fba4bb480b 100644 --- a/docs/docs/usage-guide/configuration_options.md +++ b/docs/docs/usage-guide/configuration_options.md @@ -39,7 +39,7 @@ Qodo Merge will know to remove the surrounding quotes when reading the configura `Platforms supported: GitHub, GitLab, Bitbucket, Azure DevOps` -By uploading a local `.pr_agent.toml` file to the root of the repo's default branch, you can edit and customize any configuration parameter. Note that you need to upload `.pr_agent.toml` prior to creating a PR, in order for the configuration to take effect. +By uploading a local `.pr_agent.toml` file to the root of the repo's default branch, you can edit and customize any configuration parameter. Note that you need to upload or update `.pr_agent.toml` before using the PR Agent tools (either at PR creation or via manual trigger) for the configuration to take effect. For example, if you set in `.pr_agent.toml`: From 91b3f2ee807dfc6b6a9cd5656ae550299f2b2cb2 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sun, 11 May 2025 11:12:58 +0300 Subject: [PATCH 193/365] docs: improve improve tool documentation with visual section headers --- docs/docs/tools/improve.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/docs/tools/improve.md b/docs/docs/tools/improve.md index 8d0850e687..5ddb01d9d0 100644 --- a/docs/docs/tools/improve.md +++ b/docs/docs/tools/improve.md @@ -7,12 +7,16 @@ The tool can be triggered automatically every time a new PR is [opened](../usage /improve ``` +## How it looks + === "Suggestions Overview" ![code_suggestions_as_comment_closed](https://codium.ai/images/pr_agent/code_suggestions_as_comment_closed.png){width=512} -=== "Implementation Details" +=== "Selecting a specific suggestion" ![code_suggestions_as_comment_open](https://codium.ai/images/pr_agent/code_suggestions_as_comment_open.png){width=512} +___ + !!! note "The following features are available only for Qodo Merge 💎 users:" - The `Apply this suggestion` checkbox, which interactively converts a suggestion into a committable code comment - The `More` checkbox to generate additional suggestions From 0e102a4ad531c48b460fd846231e6d5a3c234440 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sun, 11 May 2025 11:53:22 +0300 Subject: [PATCH 194/365] docs: add recent updates and future roadmap section to documentation --- docs/docs/recent_updates/index.md | 18 ++++++++++++++++++ docs/mkdocs.yml | 2 ++ 2 files changed, 20 insertions(+) create mode 100644 docs/docs/recent_updates/index.md diff --git a/docs/docs/recent_updates/index.md b/docs/docs/recent_updates/index.md new file mode 100644 index 0000000000..fc085597cc --- /dev/null +++ b/docs/docs/recent_updates/index.md @@ -0,0 +1,18 @@ +# Recent Updates and Future Roadmap + +This page summarizes recent enhancements to Qodo Merge (last three months). + +It also outlines our development roadmap for the upcoming three months. Please note that the roadmap is subject to change, and features may be adjusted, added, or reprioritized. + +=== "Recent Updates" + - **Chat on Suggestions**: Users can now chat with Qodo Merge code suggestions ([Learn more](https://qodo-merge-docs.qodo.ai/tools/improve/#chat-on-code-suggestions)) + - **Scan Repo Discussions**: A new tool that analyzes past code discussions to generate a `best_practices.md` file, distilling key insights and recommendations. ([Learn more](https://qodo-merge-docs.qodo.ai/tools/scan_repo_discussions/)) + - **Enhanced Models**: Qodo Merge now defaults to a combination of top models (Claude Sonnet 3.7 and Gemini 2.5 Pro) and incorporates dedicated code validation logic for improved results. ([Details 1](https://qodo-merge-docs.qodo.ai/usage-guide/qodo_merge_models/), [Details 2](https://qodo-merge-docs.qodo.ai/core-abilities/code_validation/)) + - **Chrome Extension Update**: Qodo Merge Chrome extension now supports single-tenant users. ([Learn more](https://qodo-merge-docs.qodo.ai/chrome-extension/options/#configuration-options/)) + +=== "Future Roadmap" + - **Smart Update**: Upon PR updates, Qodo Merge will offer tailored code suggestions, addressing both the entire PR and the specific incremental changes since the last feedback. + - **CLI Endpoint**: A new Qodo Merge endpoint will accept lists of before/after code changes, execute Qodo Merge commands, and return the results. + - **Simplified Free Tier**: We plan to transition from a two-week free trial to a free tier offering a limited number of suggestions per month per organization. + - **Best Practices Hierarchy**: Introducing support for structured best practices, such as for folders in monorepos or a unified best practice file for a group of repositories. + - **Installation Metrics**: Upon installation, Qodo Merge will analyze past PRs for key metrics (e.g., time to merge, time to first reviewer feedback), enabling pre/post-installation comparison to calculate ROI. \ No newline at end of file diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index cc1b02f149..425b2db703 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -61,6 +61,8 @@ nav: - Options: 'chrome-extension/options.md' - FAQ: - FAQ: 'faq/index.md' + - Recent Updates: + - Recent Updates: 'recent_updates/index.md' - AI Docs Search: 'ai_search/index.md' # - Code Fine-tuning Benchmark: 'finetuning_benchmark/index.md' From 22e4276f1a6ff5918de0e652ec5797147cb602b3 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sun, 11 May 2025 12:33:55 +0300 Subject: [PATCH 195/365] docs: fix typo in scan repo discussions tool name and add help docs tool --- docs/docs/recent_updates/index.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/docs/recent_updates/index.md b/docs/docs/recent_updates/index.md index fc085597cc..4663f26f82 100644 --- a/docs/docs/recent_updates/index.md +++ b/docs/docs/recent_updates/index.md @@ -1,14 +1,17 @@ # Recent Updates and Future Roadmap +`Last updated: 2025-05-11` + This page summarizes recent enhancements to Qodo Merge (last three months). It also outlines our development roadmap for the upcoming three months. Please note that the roadmap is subject to change, and features may be adjusted, added, or reprioritized. === "Recent Updates" - **Chat on Suggestions**: Users can now chat with Qodo Merge code suggestions ([Learn more](https://qodo-merge-docs.qodo.ai/tools/improve/#chat-on-code-suggestions)) - - **Scan Repo Discussions**: A new tool that analyzes past code discussions to generate a `best_practices.md` file, distilling key insights and recommendations. ([Learn more](https://qodo-merge-docs.qodo.ai/tools/scan_repo_discussions/)) + - **Scan Repo Discussions Dool**: A new tool that analyzes past code discussions to generate a `best_practices.md` file, distilling key insights and recommendations. ([Learn more](https://qodo-merge-docs.qodo.ai/tools/scan_repo_discussions/)) - **Enhanced Models**: Qodo Merge now defaults to a combination of top models (Claude Sonnet 3.7 and Gemini 2.5 Pro) and incorporates dedicated code validation logic for improved results. ([Details 1](https://qodo-merge-docs.qodo.ai/usage-guide/qodo_merge_models/), [Details 2](https://qodo-merge-docs.qodo.ai/core-abilities/code_validation/)) - **Chrome Extension Update**: Qodo Merge Chrome extension now supports single-tenant users. ([Learn more](https://qodo-merge-docs.qodo.ai/chrome-extension/options/#configuration-options/)) + - **Help Docs Tool**: The help_docs tool can answer free-text questions based on any git documentation folder. ([Learn more](https://qodo-merge-docs.qodo.ai/tools/help_docs/)) === "Future Roadmap" - **Smart Update**: Upon PR updates, Qodo Merge will offer tailored code suggestions, addressing both the entire PR and the specific incremental changes since the last feedback. From 90445342b396a85106cae46616714179bab598fc Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sun, 11 May 2025 12:35:50 +0300 Subject: [PATCH 196/365] docs: fix typo in scan repo discussions tool name and add help docs tool --- docs/docs/recent_updates/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/recent_updates/index.md b/docs/docs/recent_updates/index.md index 4663f26f82..74a3455e04 100644 --- a/docs/docs/recent_updates/index.md +++ b/docs/docs/recent_updates/index.md @@ -8,7 +8,7 @@ It also outlines our development roadmap for the upcoming three months. Please n === "Recent Updates" - **Chat on Suggestions**: Users can now chat with Qodo Merge code suggestions ([Learn more](https://qodo-merge-docs.qodo.ai/tools/improve/#chat-on-code-suggestions)) - - **Scan Repo Discussions Dool**: A new tool that analyzes past code discussions to generate a `best_practices.md` file, distilling key insights and recommendations. ([Learn more](https://qodo-merge-docs.qodo.ai/tools/scan_repo_discussions/)) + - **Scan Repo Discussions Tool**: A new tool that analyzes past code discussions to generate a `best_practices.md` file, distilling key insights and recommendations. ([Learn more](https://qodo-merge-docs.qodo.ai/tools/scan_repo_discussions/)) - **Enhanced Models**: Qodo Merge now defaults to a combination of top models (Claude Sonnet 3.7 and Gemini 2.5 Pro) and incorporates dedicated code validation logic for improved results. ([Details 1](https://qodo-merge-docs.qodo.ai/usage-guide/qodo_merge_models/), [Details 2](https://qodo-merge-docs.qodo.ai/core-abilities/code_validation/)) - **Chrome Extension Update**: Qodo Merge Chrome extension now supports single-tenant users. ([Learn more](https://qodo-merge-docs.qodo.ai/chrome-extension/options/#configuration-options/)) - **Help Docs Tool**: The help_docs tool can answer free-text questions based on any git documentation folder. ([Learn more](https://qodo-merge-docs.qodo.ai/tools/help_docs/)) From df0d065705d983e7148581b5cbf5f7a67ffd63a3 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sun, 11 May 2025 17:17:58 +0300 Subject: [PATCH 197/365] fix: update Docker base image to slim version and install required dependencies --- Dockerfile.github_action | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Dockerfile.github_action b/Dockerfile.github_action index b95bbdb626..458ac78aa5 100644 --- a/Dockerfile.github_action +++ b/Dockerfile.github_action @@ -1,4 +1,6 @@ -FROM python:3.12 as base +FROM python:3.12.10-slim AS base + +RUN apt update && apt install --no-install-recommends -y git curl && apt-get clean && rm -rf /var/lib/apt/lists/* WORKDIR /app ADD pyproject.toml . From 4210262e27c55c6fd3ba826bdbd2a87fe08e6def Mon Sep 17 00:00:00 2001 From: Tal Date: Sun, 11 May 2025 17:19:58 +0300 Subject: [PATCH 198/365] Update Dockerfile.github_action Co-authored-by: qodo-merge-pro-for-open-source[bot] <189517486+qodo-merge-pro-for-open-source[bot]@users.noreply.github.com> --- Dockerfile.github_action | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.github_action b/Dockerfile.github_action index 458ac78aa5..2d39a75e5a 100644 --- a/Dockerfile.github_action +++ b/Dockerfile.github_action @@ -1,6 +1,6 @@ FROM python:3.12.10-slim AS base -RUN apt update && apt install --no-install-recommends -y git curl && apt-get clean && rm -rf /var/lib/apt/lists/* +RUN apt-get update && apt-get install --no-install-recommends -y git curl && apt-get clean && rm -rf /var/lib/apt/lists/* WORKDIR /app ADD pyproject.toml . From c5bcc21a617b87060e9747189b674f561f0782f1 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sun, 11 May 2025 17:32:07 +0300 Subject: [PATCH 199/365] docs: update timestamp label for clarity on recent updates page --- docs/docs/recent_updates/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/recent_updates/index.md b/docs/docs/recent_updates/index.md index 74a3455e04..dc7ea8d1b8 100644 --- a/docs/docs/recent_updates/index.md +++ b/docs/docs/recent_updates/index.md @@ -1,6 +1,6 @@ # Recent Updates and Future Roadmap -`Last updated: 2025-05-11` +`Page last updated: 2025-05-11` This page summarizes recent enhancements to Qodo Merge (last three months). From bd911b67d8c3207a6f97c0c1916ec62cbbda4d7c Mon Sep 17 00:00:00 2001 From: mrT23 Date: Mon, 12 May 2025 08:11:57 +0300 Subject: [PATCH 200/365] docs: add Gemini-2.5-pro-preview model comparison to benchmark documentation --- docs/docs/finetuning_benchmark/index.md | 259 +++++++++++++++++------- 1 file changed, 184 insertions(+), 75 deletions(-) diff --git a/docs/docs/finetuning_benchmark/index.md b/docs/docs/finetuning_benchmark/index.md index bffaa68d8e..79d7957125 100644 --- a/docs/docs/finetuning_benchmark/index.md +++ b/docs/docs/finetuning_benchmark/index.md @@ -1,102 +1,211 @@ # Qodo Merge Pull Request Benchmark -On coding tasks, the gap between open-source models and top closed-source models such as Claude and GPT is significant. -
-In practice, open-source models are unsuitable for most real-world code tasks, and require further fine-tuning to produce acceptable results. +## Methodology -_Qodo Merge pull request benchmark_ aims to benchmark models on their ability to be fine-tuned for a coding task. -Specifically, we chose to fine-tune open-source models on the task of analyzing a pull request, and providing useful feedback and code suggestions. +... -Here are the results: -
-
+## Gemini-2.5-pro-preview-05-06 -**Model performance:** +### Model 'Gemini-2.5-pro-preview-05-06' vs 'Sonnet 3.7' -| Model name | Model size [B] | Better than gpt-4 rate, after fine-tuning [%] | -|-----------------------------|----------------|----------------------------------------------| -| **DeepSeek 34B-instruct** | **34** | **40.7** | -| DeepSeek 34B-base | 34 | 38.2 | -| Phind-34b | 34 | 38 | -| Granite-34B | 34 | 37.6 | -| Codestral-22B-v0.1 | 22 | 32.7 | -| QWEN-1.5-32B | 32 | 29 | -| | | | -| **CodeQwen1.5-7B** | **7** | **35.4** | -| Llama-3.1-8B-Instruct | 8 | 35.2 | -| Granite-8b-code-instruct | 8 | 34.2 | -| CodeLlama-7b-hf | 7 | 31.8 | -| Gemma-7B | 7 | 27.2 | -| DeepSeek coder-7b-instruct | 7 | 26.8 | -| Llama-3-8B-Instruct | 8 | 26.8 | -| Mistral-7B-v0.1 | 7 | 16.1 | +![Comparison](https://codium.ai/images/qodo_merge_benchmark/sonnet_37_vs_gemini-2.5-pro-preview-05-06_judge_o3.png){width=768} -
+#### Analysis Summary -**Fine-tuning impact:** +Model 'Gemini-2.5-pro-preview-05-06' is the stronger reviewer—more frequently identifies genuine, high-impact bugs and provides well-formed, actionable fixes. Model 'Sonnet 3.7' is safer against false positives and tends to be concise but often misses important defects or offers low-value or incorrect suggestions. -| Model name | Model size [B] | Fine-tuned | Better than gpt-4 rate [%] | -|---------------------------|----------------|------------|----------------------------| -| DeepSeek 34B-instruct | 34 | yes | 40.7 | -| DeepSeek 34B-instruct | 34 | no | 3.6 | +See raw results [here](https://github.com/Codium-ai/pr-agent-settings/blob/main/benchmark/sonnet_37_vs_gemini-2.5-pro-preview-05-06.md) -## Results analysis -- **Fine-tuning is a must** - without fine-tuning, open-source models provide poor results on most real-world code tasks, which include complicated prompt and lengthy context. We clearly see that without fine-tuning, deepseek model was 96.4% of the time inferior to GPT-4, while after fine-tuning, it is better 40.7% of the time. -- **Always start from a code-dedicated model** — When fine-tuning, always start from a code-dedicated model, and not from a general-usage model. The gaps in downstream results are very big. -- **Don't believe the hype** —newer models, or models from big-tech companies (Llama3, Gemma, Mistral), are not always better for fine-tuning. -- **The best large model** - For large 34B code-dedicated models, the gaps when doing proper fine-tuning are small. The current top model is **DeepSeek 34B-instruct** -- **The best small model** - For small 7B code-dedicated models, the gaps when fine-tuning are much larger. **CodeQWEN 1.5-7B** is by far the best model for fine-tuning. -- **Base vs. instruct** - For the top model (deepseek), we saw small advantage when starting from the instruct version. However, we recommend testing both versions on each specific task, as the base model is generally considered more suitable for fine-tuning. +#### Model 'Gemini-2.5-pro-preview-05-06' vs 'Sonnet 3.7' - Detailed Analysis -## Dataset +strengths: -### Training dataset +- higher_accuracy_and_coverage: finds real critical bugs and supplies actionable patches in most examples (better in 78 % of cases). +- guideline_awareness: usually respects new-lines-only scope, ≤3 suggestions, proper YAML, and stays silent when no issues exist. +- detailed_reasoning_and_patches: explanations tie directly to the diff and fixes are concrete, often catching multiple related defects that 'Sonnet 3.7' overlooks. -Our training dataset comprises 25,000 pull requests, aggregated from permissive license repos. For each pull request, we generated responses for the three main tools of Qodo Merge: -[Describe](https://qodo-merge-docs.qodo.ai/tools/describe/), [Review](https://qodo-merge-docs.qodo.ai/tools/improve/) and [Improve](https://qodo-merge-docs.qodo.ai/tools/improve/). +weaknesses: -On the raw data collected, we employed various automatic and manual cleaning techniques to ensure the outputs were of the highest quality, and suitable for instruct-tuning. +- occasional_rule_violations: sometimes proposes new imports, package-version changes, or edits outside the added lines. +- overzealous_suggestions: may add speculative or stylistic fixes that exceed the “critical” scope, or mis-label severity. +- sporadic_technical_slips: a few patches contain minor coding errors, oversized snippets, or duplicate/contradicting advice. -Here are the prompts, and example outputs, used as input-output pairs to fine-tune the models: -| Tool | Prompt | Example output | -|----------|------------------------------------------------------------------------------------------------------------|----------------| -| Describe | [link](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/pr_description_prompts.toml) | [link](https://github.com/Codium-ai/pr-agent/pull/910#issue-2303989601) | -| Review | [link](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/pr_reviewer_prompts.toml) | [link](https://github.com/Codium-ai/pr-agent/pull/910#issuecomment-2118761219) | -| Improve | [link](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/pr_code_suggestions_prompts.toml) | [link](https://github.com/Codium-ai/pr-agent/pull/910#issuecomment-2118761309) | +### Model 'Gemini-2.5-pro-preview-05-06' vs 'GPT-4.1' -### Evaluation dataset -- For each tool, we aggregated 200 additional examples to be used for evaluation. These examples were not used in the training dataset, and were manually selected to represent diverse real-world use-cases. -- For each test example, we generated two responses: one from the fine-tuned model, and one from the best code model in the world, `gpt-4-turbo-2024-04-09`. +[//]: # (On coding tasks, the gap between open-source models and top closed-source models such as Claude and GPT is significant.) -- We used a third LLM to judge which response better answers the prompt, and will likely be perceived by a human as better response. -
+[//]: # (
) -We experimented with three model as judges: `gpt-4-turbo-2024-04-09`, `gpt-4o`, and `claude-3-opus-20240229`. All three produced similar results, with the same ranking order. This strengthens the validity of our testing protocol. -The evaluation prompt can be found [here](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/pr_evaluate_prompt_response.toml) +[//]: # (In practice, open-source models are unsuitable for most real-world code tasks, and require further fine-tuning to produce acceptable results.) -Here is an example of a judge model feedback: +[//]: # () +[//]: # (_Qodo Merge pull request benchmark_ aims to benchmark models on their ability to be fine-tuned for a coding task.) -``` -command: improve -model1_score: 9, -model2_score: 6, -why: | - Response 1 is better because it provides more actionable and specific suggestions that directly - enhance the code's maintainability, performance, and best practices. For example, it suggests - using a variable for reusable widget instances and using named routes for navigation, which - are practical improvements. In contrast, Response 2 focuses more on general advice and less - actionable suggestions, such as changing variable names and adding comments, which are less - critical for immediate code improvement." -``` +[//]: # (Specifically, we chose to fine-tune open-source models on the task of analyzing a pull request, and providing useful feedback and code suggestions.) -## Comparing Top Closed-Source Models +[//]: # () +[//]: # (Here are the results:) -Another application of the Pull Request Benchmark is comparing leading closed-source models to determine which performs better at analyzing pull request code. +[//]: # (
) -The evaluation methodology resembles the approach used for evaluating fine-tuned models: +[//]: # (
) -- We ran each model across 200 diverse pull requests, asking them to generate code suggestions using Qodo Merge's `improve` tool -- A third top model served as judge to determine which response better fulfilled the prompt and would likely be perceived as superior by human users +[//]: # () +[//]: # (**Model performance:**) + +[//]: # () +[//]: # (| Model name | Model size [B] | Better than gpt-4 rate, after fine-tuning [%] |) + +[//]: # (|-----------------------------|----------------|----------------------------------------------|) + +[//]: # (| **DeepSeek 34B-instruct** | **34** | **40.7** |) + +[//]: # (| DeepSeek 34B-base | 34 | 38.2 |) + +[//]: # (| Phind-34b | 34 | 38 |) + +[//]: # (| Granite-34B | 34 | 37.6 |) + +[//]: # (| Codestral-22B-v0.1 | 22 | 32.7 |) + +[//]: # (| QWEN-1.5-32B | 32 | 29 |) + +[//]: # (| | | |) + +[//]: # (| **CodeQwen1.5-7B** | **7** | **35.4** |) + +[//]: # (| Llama-3.1-8B-Instruct | 8 | 35.2 |) + +[//]: # (| Granite-8b-code-instruct | 8 | 34.2 |) + +[//]: # (| CodeLlama-7b-hf | 7 | 31.8 |) + +[//]: # (| Gemma-7B | 7 | 27.2 |) + +[//]: # (| DeepSeek coder-7b-instruct | 7 | 26.8 |) + +[//]: # (| Llama-3-8B-Instruct | 8 | 26.8 |) + +[//]: # (| Mistral-7B-v0.1 | 7 | 16.1 |) + +[//]: # () +[//]: # (
) + +[//]: # () +[//]: # (**Fine-tuning impact:**) + +[//]: # () +[//]: # (| Model name | Model size [B] | Fine-tuned | Better than gpt-4 rate [%] |) + +[//]: # (|---------------------------|----------------|------------|----------------------------|) + +[//]: # (| DeepSeek 34B-instruct | 34 | yes | 40.7 |) + +[//]: # (| DeepSeek 34B-instruct | 34 | no | 3.6 |) + +[//]: # () +[//]: # (## Results analysis) + +[//]: # () +[//]: # (- **Fine-tuning is a must** - without fine-tuning, open-source models provide poor results on most real-world code tasks, which include complicated prompt and lengthy context. We clearly see that without fine-tuning, deepseek model was 96.4% of the time inferior to GPT-4, while after fine-tuning, it is better 40.7% of the time.) + +[//]: # (- **Always start from a code-dedicated model** — When fine-tuning, always start from a code-dedicated model, and not from a general-usage model. The gaps in downstream results are very big.) + +[//]: # (- **Don't believe the hype** —newer models, or models from big-tech companies (Llama3, Gemma, Mistral), are not always better for fine-tuning.) + +[//]: # (- **The best large model** - For large 34B code-dedicated models, the gaps when doing proper fine-tuning are small. The current top model is **DeepSeek 34B-instruct**) + +[//]: # (- **The best small model** - For small 7B code-dedicated models, the gaps when fine-tuning are much larger. **CodeQWEN 1.5-7B** is by far the best model for fine-tuning.) + +[//]: # (- **Base vs. instruct** - For the top model (deepseek), we saw small advantage when starting from the instruct version. However, we recommend testing both versions on each specific task, as the base model is generally considered more suitable for fine-tuning.) + +[//]: # () +[//]: # (## Dataset) + +[//]: # () +[//]: # (### Training dataset) + +[//]: # () +[//]: # (Our training dataset comprises 25,000 pull requests, aggregated from permissive license repos. For each pull request, we generated responses for the three main tools of Qodo Merge:) + +[//]: # ([Describe](https://qodo-merge-docs.qodo.ai/tools/describe/), [Review](https://qodo-merge-docs.qodo.ai/tools/improve/) and [Improve](https://qodo-merge-docs.qodo.ai/tools/improve/).) + +[//]: # () +[//]: # (On the raw data collected, we employed various automatic and manual cleaning techniques to ensure the outputs were of the highest quality, and suitable for instruct-tuning.) + +[//]: # () +[//]: # (Here are the prompts, and example outputs, used as input-output pairs to fine-tune the models:) + +[//]: # () +[//]: # (| Tool | Prompt | Example output |) + +[//]: # (|----------|------------------------------------------------------------------------------------------------------------|----------------|) + +[//]: # (| Describe | [link](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/pr_description_prompts.toml) | [link](https://github.com/Codium-ai/pr-agent/pull/910#issue-2303989601) |) + +[//]: # (| Review | [link](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/pr_reviewer_prompts.toml) | [link](https://github.com/Codium-ai/pr-agent/pull/910#issuecomment-2118761219) |) + +[//]: # (| Improve | [link](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/pr_code_suggestions_prompts.toml) | [link](https://github.com/Codium-ai/pr-agent/pull/910#issuecomment-2118761309) |) + +[//]: # () +[//]: # (### Evaluation dataset) + +[//]: # () +[//]: # (- For each tool, we aggregated 200 additional examples to be used for evaluation. These examples were not used in the training dataset, and were manually selected to represent diverse real-world use-cases.) + +[//]: # (- For each test example, we generated two responses: one from the fine-tuned model, and one from the best code model in the world, `gpt-4-turbo-2024-04-09`.) + +[//]: # () +[//]: # (- We used a third LLM to judge which response better answers the prompt, and will likely be perceived by a human as better response.) + +[//]: # (
) + +[//]: # () +[//]: # (We experimented with three model as judges: `gpt-4-turbo-2024-04-09`, `gpt-4o`, and `claude-3-opus-20240229`. All three produced similar results, with the same ranking order. This strengthens the validity of our testing protocol.) + +[//]: # (The evaluation prompt can be found [here](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/pr_evaluate_prompt_response.toml)) + +[//]: # () +[//]: # (Here is an example of a judge model feedback:) + +[//]: # () +[//]: # (```) + +[//]: # (command: improve) + +[//]: # (model1_score: 9,) + +[//]: # (model2_score: 6,) + +[//]: # (why: |) + +[//]: # ( Response 1 is better because it provides more actionable and specific suggestions that directly) + +[//]: # ( enhance the code's maintainability, performance, and best practices. For example, it suggests) + +[//]: # ( using a variable for reusable widget instances and using named routes for navigation, which) + +[//]: # ( are practical improvements. In contrast, Response 2 focuses more on general advice and less) + +[//]: # ( actionable suggestions, such as changing variable names and adding comments, which are less) + +[//]: # ( critical for immediate code improvement.") + +[//]: # (```) + +[//]: # () +[//]: # (## Comparing Top Closed-Source Models) + +[//]: # () +[//]: # (Another application of the Pull Request Benchmark is comparing leading closed-source models to determine which performs better at analyzing pull request code.) + +[//]: # () +[//]: # (The evaluation methodology resembles the approach used for evaluating fine-tuned models:) + +[//]: # () +[//]: # (- We ran each model across 200 diverse pull requests, asking them to generate code suggestions using Qodo Merge's `improve` tool) + +[//]: # (- A third top model served as judge to determine which response better fulfilled the prompt and would likely be perceived as superior by human users) From 212c72eb7dab197c48fac75e1eb2d385dc27326d Mon Sep 17 00:00:00 2001 From: Thomas De Keulenaer <11250711+twdkeule@users.noreply.github.com> Date: Wed, 7 May 2025 16:02:54 +0200 Subject: [PATCH 201/365] Changelog prompt: fix markdown link --- pr_agent/settings/pr_update_changelog_prompts.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/settings/pr_update_changelog_prompts.toml b/pr_agent/settings/pr_update_changelog_prompts.toml index 6825da669f..ecf610b4b1 100644 --- a/pr_agent/settings/pr_update_changelog_prompts.toml +++ b/pr_agent/settings/pr_update_changelog_prompts.toml @@ -6,7 +6,7 @@ Your task is to add a brief summary of this PR's changes to CHANGELOG.md file of - Be general, and avoid specific details, files, etc. The output should be minimal, no more than 3-4 short lines. - Write only the new content to be added to CHANGELOG.md, without any introduction or summary. The content should appear as if it's a natural part of the existing file. {%- if pr_link %} -- If relevant, convert the changelog main header into a clickable link using the PR URL '{{ pr_link }}'. Format: header [*][pr_link] +- If relevant, convert the changelog main header into a clickable link using the PR URL '{{ pr_link }}'. Format: header [*](pr_link) {%- endif %} From e516d66c1c4a6eb93c31d149b7a9004a5d708de8 Mon Sep 17 00:00:00 2001 From: Thomas De Keulenaer <11250711+twdkeule@users.noreply.github.com> Date: Fri, 9 May 2025 11:58:24 +0200 Subject: [PATCH 202/365] Azure: return Comment object when creating comment --- .../git_providers/azuredevops_provider.py | 33 ++++++++----------- pr_agent/git_providers/git_provider.py | 9 +++-- 2 files changed, 18 insertions(+), 24 deletions(-) diff --git a/pr_agent/git_providers/azuredevops_provider.py b/pr_agent/git_providers/azuredevops_provider.py index 80bf68c56b..7524896c33 100644 --- a/pr_agent/git_providers/azuredevops_provider.py +++ b/pr_agent/git_providers/azuredevops_provider.py @@ -18,14 +18,10 @@ MAX_PR_DESCRIPTION_AZURE_LENGTH = 4000-1 try: - # noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences from azure.devops.connection import Connection # noinspection PyUnresolvedReferences - from azure.devops.v7_1.git.models import (Comment, CommentThread, - GitPullRequest, - GitPullRequestIterationChanges, - GitVersionDescriptor) + from azure.devops.released.git import (Comment, CommentThread, GitPullRequest, GitVersionDescriptor, GitClient) # noinspection PyUnresolvedReferences from azure.identity import DefaultAzureCredential from msrest.authentication import BasicAuthentication @@ -121,31 +117,29 @@ def publish_code_suggestions(self, code_suggestions: list) -> bool: get_logger().warning(f"Azure failed to publish code suggestion, error: {e}") return True - - def get_pr_description_full(self) -> str: return self.pr.description - def edit_comment(self, comment, body: str): + def edit_comment(self, comment: Comment, body: str): try: self.azure_devops_client.update_comment( repository_id=self.repo_slug, pull_request_id=self.pr_num, - thread_id=comment["thread_id"], - comment_id=comment["comment_id"], + thread_id=comment.thread_id, + comment_id=comment.id, comment=Comment(content=body), project=self.workspace_slug, ) except Exception as e: get_logger().exception(f"Failed to edit comment, error: {e}") - def remove_comment(self, comment): + def remove_comment(self, comment: Comment): try: self.azure_devops_client.delete_comment( repository_id=self.repo_slug, pull_request_id=self.pr_num, - thread_id=comment["thread_id"], - comment_id=comment["comment_id"], + thread_id=comment.thread_id, + comment_id=comment.id, project=self.workspace_slug, ) except Exception as e: @@ -378,7 +372,7 @@ def get_diff_files(self) -> list[FilePatchInfo]: get_logger().exception(f"Failed to get diff files, error: {e}") return [] - def publish_comment(self, pr_comment: str, is_temporary: bool = False, thread_context=None): + def publish_comment(self, pr_comment: str, is_temporary: bool = False, thread_context=None) -> Comment: if is_temporary and not get_settings().config.publish_output_progress: get_logger().debug(f"Skipping publish_comment for temporary comment: {pr_comment}") return None @@ -390,10 +384,11 @@ def publish_comment(self, pr_comment: str, is_temporary: bool = False, thread_co repository_id=self.repo_slug, pull_request_id=self.pr_num, ) - response = {"thread_id": thread_response.id, "comment_id": thread_response.comments[0].id} + created_comment = thread_response.comments[0] + created_comment.thread_id = thread_response.id if is_temporary: - self.temp_comments.append(response) - return response + self.temp_comments.append(created_comment) + return created_comment def publish_description(self, pr_title: str, pr_body: str): if len(pr_body) > MAX_PR_DESCRIPTION_AZURE_LENGTH: @@ -522,7 +517,7 @@ def get_pr_branch(self): def get_user_id(self): return 0 - def get_issue_comments(self): + def get_issue_comments(self) -> list[Comment]: threads = self.azure_devops_client.get_threads(repository_id=self.repo_slug, pull_request_id=self.pr_num, project=self.workspace_slug) threads.reverse() comment_list = [] @@ -562,7 +557,7 @@ def _parse_pr_url(pr_url: str) -> Tuple[str, str, int]: return workspace_slug, repo_slug, pr_number @staticmethod - def _get_azure_devops_client(): + def _get_azure_devops_client() -> GitClient: org = get_settings().azure_devops.get("org", None) pat = get_settings().azure_devops.get("pat", None) diff --git a/pr_agent/git_providers/git_provider.py b/pr_agent/git_providers/git_provider.py index 2895bd5579..dfb5b22412 100644 --- a/pr_agent/git_providers/git_provider.py +++ b/pr_agent/git_providers/git_provider.py @@ -228,7 +228,7 @@ def publish_persistent_comment(self, pr_comment: str, update_header: bool = True, name='review', final_update_message=True): - self.publish_comment(pr_comment) + return self.publish_comment(pr_comment) def publish_persistent_comment_full(self, pr_comment: str, initial_header: str, @@ -250,14 +250,13 @@ def publish_persistent_comment_full(self, pr_comment: str, # response = self.mr.notes.update(comment.id, {'body': pr_comment_updated}) self.edit_comment(comment, pr_comment_updated) if final_update_message: - self.publish_comment( + return self.publish_comment( f"**[Persistent {name}]({comment_url})** updated to latest commit {latest_commit_url}") - return + return comment except Exception as e: get_logger().exception(f"Failed to update persistent review, error: {e}") pass - self.publish_comment(pr_comment) - + return self.publish_comment(pr_comment) @abstractmethod def publish_inline_comment(self, body: str, relevant_file: str, relevant_line_in_file: str, original_suggestion=None): From c924affebc3e85e15d64dcd47c8c1591ff7f9ec1 Mon Sep 17 00:00:00 2001 From: Thomas De Keulenaer <11250711+twdkeule@users.noreply.github.com> Date: Wed, 7 May 2025 14:46:08 +0200 Subject: [PATCH 203/365] Azure devops provider: add persistent comment --- .../git_providers/azuredevops_provider.py | 22 ++++++++++++++----- pr_agent/tools/pr_code_suggestions.py | 8 ------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/pr_agent/git_providers/azuredevops_provider.py b/pr_agent/git_providers/azuredevops_provider.py index 7524896c33..4dd61e5b83 100644 --- a/pr_agent/git_providers/azuredevops_provider.py +++ b/pr_agent/git_providers/azuredevops_provider.py @@ -170,10 +170,6 @@ def get_pr_labels(self, update=False): return [] def is_supported(self, capability: str) -> bool: - if capability in [ - "get_issue_comments", - ]: - return False return True def set_pr(self, pr_url: str): @@ -390,6 +386,13 @@ def publish_comment(self, pr_comment: str, is_temporary: bool = False, thread_co self.temp_comments.append(created_comment) return created_comment + def publish_persistent_comment(self, pr_comment: str, + initial_header: str, + update_header: bool = True, + name='review', + final_update_message=True): + return self.publish_persistent_comment_full(pr_comment, initial_header, update_header, name, final_update_message) + def publish_description(self, pr_title: str, pr_body: str): if len(pr_body) > MAX_PR_DESCRIPTION_AZURE_LENGTH: @@ -433,7 +436,6 @@ def remove_initial_comment(self): def publish_inline_comment(self, body: str, relevant_file: str, relevant_line_in_file: str, original_suggestion=None): self.publish_inline_comments([self.create_inline_comment(body, relevant_file, relevant_line_in_file)]) - 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(), @@ -617,3 +619,13 @@ def publish_file_comments(self, file_comments: list) -> bool: def get_line_link(self, relevant_file: str, relevant_line_start: int, relevant_line_end: int = None) -> str: return self.pr_url+f"?_a=files&path={relevant_file}" + + def get_comment_url(self, comment) -> str: + return self.pr_url + "?discussionId=" + str(comment.thread_id) + + def get_latest_commit_url(self) -> str: + commits = self.azure_devops_client.get_pull_request_commits(self.repo_slug, self.pr_num, self.workspace_slug) + last = commits[0] + url = self.azure_devops_client.normalized_url + "/" + self.workspace_slug + "/_git/" + self.repo_slug + "/commit/" + last.commit_id + return url + \ No newline at end of file diff --git a/pr_agent/tools/pr_code_suggestions.py b/pr_agent/tools/pr_code_suggestions.py index c742aa0647..0dea2e70ad 100644 --- a/pr_agent/tools/pr_code_suggestions.py +++ b/pr_agent/tools/pr_code_suggestions.py @@ -267,14 +267,6 @@ def _extract_link(comment_text: str): up_to_commit_txt = f" up to commit {match.group(0)[4:-3].strip()}" return up_to_commit_txt - if isinstance(git_provider, AzureDevopsProvider): # get_latest_commit_url is not supported yet - if progress_response: - git_provider.edit_comment(progress_response, pr_comment) - new_comment = progress_response - else: - new_comment = git_provider.publish_comment(pr_comment) - return new_comment - history_header = f"#### Previous suggestions\n" last_commit_num = git_provider.get_latest_commit_url().split('/')[-1][:7] if only_fold: # A user clicked on the 'self-review' checkbox From 3a07b55d0c0dcb3ae2299caee04139f2493620a8 Mon Sep 17 00:00:00 2001 From: Thomas De Keulenaer <11250711+twdkeule@users.noreply.github.com> Date: Wed, 7 May 2025 16:38:11 +0200 Subject: [PATCH 204/365] Azure: dont start threads as active because they block the pull request --- pr_agent/git_providers/azuredevops_provider.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/git_providers/azuredevops_provider.py b/pr_agent/git_providers/azuredevops_provider.py index 4dd61e5b83..39e971ecd4 100644 --- a/pr_agent/git_providers/azuredevops_provider.py +++ b/pr_agent/git_providers/azuredevops_provider.py @@ -373,7 +373,7 @@ def publish_comment(self, pr_comment: str, is_temporary: bool = False, thread_co get_logger().debug(f"Skipping publish_comment for temporary comment: {pr_comment}") return None comment = Comment(content=pr_comment) - thread = CommentThread(comments=[comment], thread_context=thread_context, status=1) + thread = CommentThread(comments=[comment], thread_context=thread_context, status="closed") thread_response = self.azure_devops_client.create_thread( comment_thread=thread, project=self.workspace_slug, From 67272700a6bf0b6ad932edc04bde687a0a96f6cc Mon Sep 17 00:00:00 2001 From: Thomas De Keulenaer <11250711+twdkeule@users.noreply.github.com> Date: Fri, 9 May 2025 09:07:16 +0200 Subject: [PATCH 205/365] Azure: handle line comments --- .../git_providers/azuredevops_provider.py | 27 +++++++++++++ .../servers/azuredevops_server_webhook.py | 40 +++++++++++-------- 2 files changed, 51 insertions(+), 16 deletions(-) diff --git a/pr_agent/git_providers/azuredevops_provider.py b/pr_agent/git_providers/azuredevops_provider.py index 39e971ecd4..426953aec4 100644 --- a/pr_agent/git_providers/azuredevops_provider.py +++ b/pr_agent/git_providers/azuredevops_provider.py @@ -117,6 +117,10 @@ def publish_code_suggestions(self, code_suggestions: list) -> bool: get_logger().warning(f"Azure failed to publish code suggestion, error: {e}") return True + def reply_to_comment_from_comment_id(self, comment_id: int, body: str, is_temporary: bool = False) -> Comment: + # comment_id is actually thread_id + return self.reply_to_thread(comment_id, body, is_temporary) + def get_pr_description_full(self) -> str: return self.pr.description @@ -537,6 +541,29 @@ def add_eyes_reaction(self, issue_comment_id: int, disable_eyes: bool = False) - def remove_reaction(self, issue_comment_id: int, reaction_id: int) -> bool: return True + def set_like(self, thread_id: int, comment_id: int, create: bool = True): + if create: + self.azure_devops_client.create_like(self.repo_slug, self.pr_num, thread_id, comment_id, project=self.workspace_slug) + else: + self.azure_devops_client.delete_like(self.repo_slug, self.pr_num, thread_id, comment_id, project=self.workspace_slug) + + def set_thread_status(self, thread_id: int, status: str): + try: + self.azure_devops_client.update_thread(CommentThread(status=status), self.repo_slug, self.pr_num, thread_id, self.workspace_slug) + except Exception as e: + get_logger().exception(f"Failed to set thread status, error: {e}") + + def reply_to_thread(self, thread_id: int, body: str, is_temporary: bool = False) -> Comment: + try: + comment = Comment(content=body) + response = self.azure_devops_client.create_comment(comment, self.repo_slug, self.pr_num, thread_id, self.workspace_slug) + response.thread_id = thread_id + if is_temporary: + self.temp_comments.append(response) + return response + except Exception as e: + get_logger().exception(f"Failed to reply to thread, error: {e}") + @staticmethod def _parse_pr_url(pr_url: str) -> Tuple[str, str, int]: parsed_url = urlparse(pr_url) diff --git a/pr_agent/servers/azuredevops_server_webhook.py b/pr_agent/servers/azuredevops_server_webhook.py index bb97b83962..3a03250bad 100644 --- a/pr_agent/servers/azuredevops_server_webhook.py +++ b/pr_agent/servers/azuredevops_server_webhook.py @@ -22,6 +22,7 @@ from pr_agent.agent.pr_agent import PRAgent, command2class from pr_agent.algo.utils import update_settings_from_args from pr_agent.config_loader import get_settings +from pr_agent.git_providers import get_git_provider_with_context from pr_agent.git_providers.utils import apply_repo_settings from pr_agent.log import LoggingFormat, get_logger, setup_logger @@ -33,14 +34,18 @@ WEBHOOK_USERNAME = azure_devops_server.get("webhook_username") WEBHOOK_PASSWORD = azure_devops_server.get("webhook_password") -async def handle_request_comment( url: str, body: str, log_context: dict -): +async def handle_request_comment(url: str, body: str, thread_id: int, comment_id: int, log_context: dict): log_context["action"] = body log_context["api_url"] = url - try: with get_logger().contextualize(**log_context): - await PRAgent().handle_request(url, body) + agent = PRAgent() + provider = get_git_provider_with_context(pr_url=url) + handled = await agent.handle_request(url, body, notify=lambda: provider.reply_to_thread(thread_id, "On it! ⏳", True)) + # mark command comment as closed + if handled: + provider.set_thread_status(thread_id, "closed") + provider.remove_initial_comment() except Exception as e: get_logger().exception(f"Failed to handle webhook", artifact={"url": url, "body": body}, error=str(e)) @@ -83,7 +88,6 @@ async def _perform_commands_azure(commands_conf: str, agent: PRAgent, api_url: s async def handle_request_azure(data, log_context): - actions = [] if data["eventType"] == "git.pullrequest.created": # API V1 (latest) pr_url = unquote(data["resource"]["_links"]["web"]["href"].replace("_apis/git/repositories", "_git")) @@ -95,11 +99,16 @@ async def handle_request_azure(data, log_context): content=jsonable_encoder({"message": "webhook triggered successfully"}) ) elif data["eventType"] == "ms.vss-code.git-pullrequest-comment-event" and "content" in data["resource"]["comment"]: - if available_commands_rgx.match(data["resource"]["comment"]["content"]): + comment = data["resource"]["comment"] + if available_commands_rgx.match(comment["content"]): if(data["resourceVersion"] == "2.0"): repo = data["resource"]["pullRequest"]["repository"]["webUrl"] pr_url = unquote(f'{repo}/pullrequest/{data["resource"]["pullRequest"]["pullRequestId"]}') - actions = [data["resource"]["comment"]["content"]] + action = comment["content"] + thread_url = comment["_links"]["threads"]["href"] + thread_id = int(thread_url.split("/")[-1]) + comment_id = int(comment["id"]) + pass else: # API V1 not supported as it does not contain the PR URL return JSONResponse( @@ -119,15 +128,14 @@ async def handle_request_azure(data, log_context): log_context["event"] = data["eventType"] log_context["api_url"] = pr_url - for action in actions: - try: - await handle_request_comment(pr_url, action, log_context) - except Exception as e: - get_logger().error("Azure DevOps Trigger failed. Error:" + str(e)) - return JSONResponse( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - content=json.dumps({"message": "Internal server error"}), - ) + try: + await handle_request_comment(pr_url, action, thread_id, comment_id, log_context) + except Exception as e: + get_logger().error("Azure DevOps Trigger failed. Error:" + str(e)) + return JSONResponse( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + content=json.dumps({"message": "Internal server error"}), + ) return JSONResponse( status_code=status.HTTP_202_ACCEPTED, content=jsonable_encoder({"message": "webhook triggered successfully"}) ) From d6a7c897626ba18ab3dccd0a53a5d4c81e991668 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Mon, 12 May 2025 09:53:59 +0300 Subject: [PATCH 206/365] docs: add Gemini-2.5-pro-preview vs GPT-4.1 benchmark comparison --- docs/docs/finetuning_benchmark/index.md | 28 ++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/docs/docs/finetuning_benchmark/index.md b/docs/docs/finetuning_benchmark/index.md index 79d7957125..85c312009a 100644 --- a/docs/docs/finetuning_benchmark/index.md +++ b/docs/docs/finetuning_benchmark/index.md @@ -6,7 +6,30 @@ ## Gemini-2.5-pro-preview-05-06 -### Model 'Gemini-2.5-pro-preview-05-06' vs 'Sonnet 3.7' +### Gemini-2.5-pro-preview-05-06 vs GPT-4.1 + +![Comparison](https://codium.ai/images/qodo_merge_benchmark/gpt-4.1_vs_gemini-2.5-pro-preview-05-06_judge_o3.png){width=768} + +#### Analysis Summary + +Model 'Gemini-2.5-pro-preview-05-06' is generally more useful thanks to wider and more accurate bug detection and concrete patches, but it sacrifices compliance discipline and sometimes oversteps the task rules. Model 'GPT-4.1' is safer and highly rule-abiding, yet often too timid—missing many genuine issues and providing limited insight. An ideal reviewer would combine 'GPT-4.1’ restraint with 'Gemini-2.5-pro-preview-05-06' thoroughness. + +#### Gemini-2.5-pro-preview-05-06 vs GPT-4.1 - Detailed Analysis + +strengths: + +- better_bug_coverage: Detects and explains more critical issues, winning in ~70 % of comparisons and achieving a higher average score. +- actionable_fixes: Supplies clear code snippets, correct language labels, and often multiple coherent suggestions per diff. +- deeper_reasoning: Shows stronger grasp of logic, edge cases, and cross-file implications, leading to broader, high-impact reviews. + +weaknesses: + +- guideline_violations: More prone to over-eager advice—non-critical tweaks, touching unchanged code, suggesting new imports, or minor format errors. +- occasional_overreach: Some fixes are speculative or risky, potentially introducing new bugs. +- redundant_or_duplicate: At times repeats the same point or exceeds the required brevity. + + +### Gemini-2.5-pro-preview-05-06 vs Sonnet 3.7 ![Comparison](https://codium.ai/images/qodo_merge_benchmark/sonnet_37_vs_gemini-2.5-pro-preview-05-06_judge_o3.png){width=768} @@ -17,7 +40,7 @@ Model 'Gemini-2.5-pro-preview-05-06' is the stronger reviewer—more frequently See raw results [here](https://github.com/Codium-ai/pr-agent-settings/blob/main/benchmark/sonnet_37_vs_gemini-2.5-pro-preview-05-06.md) -#### Model 'Gemini-2.5-pro-preview-05-06' vs 'Sonnet 3.7' - Detailed Analysis +#### Gemini-2.5-pro-preview-05-06 vs Sonnet 3.7 - Detailed Analysis strengths: @@ -32,7 +55,6 @@ weaknesses: - sporadic_technical_slips: a few patches contain minor coding errors, oversized snippets, or duplicate/contradicting advice. -### Model 'Gemini-2.5-pro-preview-05-06' vs 'GPT-4.1' [//]: # (On coding tasks, the gap between open-source models and top closed-source models such as Claude and GPT is significant.) From 24a90cab8e91475c3dce65aade65c73892b2e611 Mon Sep 17 00:00:00 2001 From: Thomas De Keulenaer <11250711+twdkeule@users.noreply.github.com> Date: Fri, 9 May 2025 12:13:42 +0200 Subject: [PATCH 207/365] Azure: handle inline /ask --- .../git_providers/azuredevops_provider.py | 9 ++++++- .../servers/azuredevops_server_webhook.py | 25 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/pr_agent/git_providers/azuredevops_provider.py b/pr_agent/git_providers/azuredevops_provider.py index 426953aec4..d77efc270b 100644 --- a/pr_agent/git_providers/azuredevops_provider.py +++ b/pr_agent/git_providers/azuredevops_provider.py @@ -21,7 +21,7 @@ # noinspection PyUnresolvedReferences from azure.devops.connection import Connection # noinspection PyUnresolvedReferences - from azure.devops.released.git import (Comment, CommentThread, GitPullRequest, GitVersionDescriptor, GitClient) + from azure.devops.released.git import (Comment, CommentThread, GitPullRequest, GitVersionDescriptor, GitClient, CommentThreadContext) # noinspection PyUnresolvedReferences from azure.identity import DefaultAzureCredential from msrest.authentication import BasicAuthentication @@ -564,6 +564,13 @@ def reply_to_thread(self, thread_id: int, body: str, is_temporary: bool = False) except Exception as e: get_logger().exception(f"Failed to reply to thread, error: {e}") + def get_thread_context(self, thread_id: int) -> CommentThreadContext: + try: + thread = self.azure_devops_client.get_pull_request_thread(self.repo_slug, self.pr_num, thread_id, self.workspace_slug) + return thread.thread_context + except Exception as e: + get_logger().exception(f"Failed to set thread status, error: {e}") + @staticmethod def _parse_pr_url(pr_url: str) -> Tuple[str, str, int]: parsed_url = urlparse(pr_url) diff --git a/pr_agent/servers/azuredevops_server_webhook.py b/pr_agent/servers/azuredevops_server_webhook.py index 3a03250bad..4553338578 100644 --- a/pr_agent/servers/azuredevops_server_webhook.py +++ b/pr_agent/servers/azuredevops_server_webhook.py @@ -23,6 +23,7 @@ from pr_agent.algo.utils import update_settings_from_args from pr_agent.config_loader import get_settings from pr_agent.git_providers import get_git_provider_with_context +from pr_agent.git_providers.azuredevops_provider import AzureDevopsProvider from pr_agent.git_providers.utils import apply_repo_settings from pr_agent.log import LoggingFormat, get_logger, setup_logger @@ -41,6 +42,7 @@ async def handle_request_comment(url: str, body: str, thread_id: int, comment_id with get_logger().contextualize(**log_context): agent = PRAgent() provider = get_git_provider_with_context(pr_url=url) + body = handle_line_comment(body, thread_id, provider) handled = await agent.handle_request(url, body, notify=lambda: provider.reply_to_thread(thread_id, "On it! ⏳", True)) # mark command comment as closed if handled: @@ -49,6 +51,29 @@ async def handle_request_comment(url: str, body: str, thread_id: int, comment_id except Exception as e: get_logger().exception(f"Failed to handle webhook", artifact={"url": url, "body": body}, error=str(e)) +def handle_line_comment(body: str, thread_id: int, provider: AzureDevopsProvider): + body = body.strip() + if not body.startswith('/ask '): + return body + thread_context = provider.get_thread_context(thread_id) + if not thread_context: + return body + + path = thread_context.file_path + if thread_context.left_file_end or thread_context.left_file_start: + start_line = thread_context.left_file_start.line + end_line = thread_context.left_file_end.line + side = "left" + elif thread_context.right_file_end or thread_context.right_file_start: + start_line = thread_context.right_file_start.line + end_line = thread_context.right_file_end.line + side = "right" + else: + get_logger().info("No line range found in thread context", artifact={"thread_context": thread_context}) + return body + + question = body[5:].lstrip() # remove 4 chars: '/ask ' + return f"/ask_line --line_start={start_line} --line_end={end_line} --side={side} --file_name={path} --comment_id={thread_id} {question}" # currently only basic auth is supported with azure webhooks # for this reason, https must be enabled to ensure the credentials are not sent in clear text From 954d61e5dc232d9151a9820901b4eebff68ed949 Mon Sep 17 00:00:00 2001 From: Thomas De Keulenaer <11250711+twdkeule@users.noreply.github.com> Date: Fri, 9 May 2025 13:12:47 +0200 Subject: [PATCH 208/365] Azure: refactor publish_code_suggestions() to use azure classes --- .../git_providers/azuredevops_provider.py | 43 ++++--------------- 1 file changed, 8 insertions(+), 35 deletions(-) diff --git a/pr_agent/git_providers/azuredevops_provider.py b/pr_agent/git_providers/azuredevops_provider.py index d77efc270b..35165bdd55 100644 --- a/pr_agent/git_providers/azuredevops_provider.py +++ b/pr_agent/git_providers/azuredevops_provider.py @@ -21,7 +21,7 @@ # noinspection PyUnresolvedReferences from azure.devops.connection import Connection # noinspection PyUnresolvedReferences - from azure.devops.released.git import (Comment, CommentThread, GitPullRequest, GitVersionDescriptor, GitClient, CommentThreadContext) + from azure.devops.released.git import (Comment, CommentThread, GitPullRequest, GitVersionDescriptor, GitClient, CommentThreadContext, CommentPosition) # noinspection PyUnresolvedReferences from azure.identity import DefaultAzureCredential from msrest.authentication import BasicAuthentication @@ -73,40 +73,13 @@ def publish_code_suggestions(self, code_suggestions: list) -> bool: f"relevant_lines_start is {relevant_lines_start}") continue - if relevant_lines_end > relevant_lines_start: - post_parameters = { - "body": body, - "path": relevant_file, - "line": relevant_lines_end, - "start_line": relevant_lines_start, - "start_side": "RIGHT", - } - else: # API is different for single line comments - post_parameters = { - "body": body, - "path": relevant_file, - "line": relevant_lines_start, - "side": "RIGHT", - } - post_parameters_list.append(post_parameters) - if not post_parameters_list: - return False - - for post_parameters in post_parameters_list: + thread_context = CommentThreadContext( + file_path=relevant_file, + right_file_start=CommentPosition(offset=1, line=relevant_lines_start), + right_file_end=CommentPosition(offset=1, line=relevant_lines_end)) + comment = Comment(content=body, comment_type=1) + thread = CommentThread(comments=[comment], thread_context=thread_context) try: - comment = Comment(content=post_parameters["body"], comment_type=1) - thread = CommentThread(comments=[comment], - thread_context={ - "filePath": post_parameters["path"], - "rightFileStart": { - "line": post_parameters["start_line"], - "offset": 1, - }, - "rightFileEnd": { - "line": post_parameters["line"], - "offset": 1, - }, - }) self.azure_devops_client.create_thread( comment_thread=thread, project=self.workspace_slug, @@ -114,7 +87,7 @@ def publish_code_suggestions(self, code_suggestions: list) -> bool: pull_request_id=self.pr_num ) except Exception as e: - get_logger().warning(f"Azure failed to publish code suggestion, error: {e}") + get_logger().error(f"Azure failed to publish code suggestion, error: {e}", suggestion=suggestion) return True def reply_to_comment_from_comment_id(self, comment_id: int, body: str, is_temporary: bool = False) -> Comment: From db0c213d72347e7dec8476b313985225f8d78e98 Mon Sep 17 00:00:00 2001 From: Thomas De Keulenaer <11250711+twdkeule@users.noreply.github.com> Date: Fri, 9 May 2025 13:13:29 +0200 Subject: [PATCH 209/365] AzureDevops webhook: allow disabling BasicAuth Azure webhooks do not allow BasicAuth without HTTPS --- .../servers/azuredevops_server_webhook.py | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/pr_agent/servers/azuredevops_server_webhook.py b/pr_agent/servers/azuredevops_server_webhook.py index 4553338578..8eacbf664c 100644 --- a/pr_agent/servers/azuredevops_server_webhook.py +++ b/pr_agent/servers/azuredevops_server_webhook.py @@ -28,12 +28,12 @@ from pr_agent.log import LoggingFormat, get_logger, setup_logger setup_logger(fmt=LoggingFormat.JSON, level=get_settings().get("CONFIG.LOG_LEVEL", "DEBUG")) -security = HTTPBasic() +security = HTTPBasic(auto_error=False) router = APIRouter() available_commands_rgx = re.compile(r"^\/(" + "|".join(command2class.keys()) + r")\s*") azure_devops_server = get_settings().get("azure_devops_server") -WEBHOOK_USERNAME = azure_devops_server.get("webhook_username") -WEBHOOK_PASSWORD = azure_devops_server.get("webhook_password") +WEBHOOK_USERNAME = azure_devops_server.get("webhook_username", None) +WEBHOOK_PASSWORD = azure_devops_server.get("webhook_password", None) async def handle_request_comment(url: str, body: str, thread_id: int, comment_id: int, log_context: dict): log_context["action"] = body @@ -78,14 +78,17 @@ def handle_line_comment(body: str, thread_id: int, provider: AzureDevopsProvider # currently only basic auth is supported with azure webhooks # for this reason, https must be enabled to ensure the credentials are not sent in clear text def authorize(credentials: HTTPBasicCredentials = Depends(security)): - is_user_ok = secrets.compare_digest(credentials.username, WEBHOOK_USERNAME) - is_pass_ok = secrets.compare_digest(credentials.password, WEBHOOK_PASSWORD) - if not (is_user_ok and is_pass_ok): - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail='Incorrect username or password.', - headers={'WWW-Authenticate': 'Basic'}, - ) + if WEBHOOK_USERNAME is None or WEBHOOK_PASSWORD is None: + return + + is_user_ok = secrets.compare_digest(credentials.username, WEBHOOK_USERNAME) + is_pass_ok = secrets.compare_digest(credentials.password, WEBHOOK_PASSWORD) + if not (is_user_ok and is_pass_ok): + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail='Incorrect username or password.', + headers={'WWW-Authenticate': 'Basic'}, + ) async def _perform_commands_azure(commands_conf: str, agent: PRAgent, api_url: str, log_context: dict): From d6aaf8a7097cee2e2c6095fc203798c43f0a99f7 Mon Sep 17 00:00:00 2001 From: ofir-frd <85901822+ofir-frd@users.noreply.github.com> Date: Mon, 12 May 2025 11:13:01 +0300 Subject: [PATCH 210/365] docs: Update "Apply this suggestion" to "Apply / Chat" --- docs/docs/tools/improve.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/tools/improve.md b/docs/docs/tools/improve.md index 5ddb01d9d0..aa0c051f69 100644 --- a/docs/docs/tools/improve.md +++ b/docs/docs/tools/improve.md @@ -18,7 +18,7 @@ The tool can be triggered automatically every time a new PR is [opened](../usage ___ !!! note "The following features are available only for Qodo Merge 💎 users:" - - The `Apply this suggestion` checkbox, which interactively converts a suggestion into a committable code comment + - The `Apply / Chat` checkbox, which interactively converts a suggestion into a committable code comment - The `More` checkbox to generate additional suggestions ## Example usage From b334bcd25099c5a782ab13751710631339cdd53c Mon Sep 17 00:00:00 2001 From: "Hussam.lawen" Date: Mon, 12 May 2025 16:54:28 +0300 Subject: [PATCH 211/365] docs: enhance improve.md with manual suggestions for Bitbucket and GitLab --- docs/docs/tools/improve.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/docs/tools/improve.md b/docs/docs/tools/improve.md index aa0c051f69..2777a6d5b9 100644 --- a/docs/docs/tools/improve.md +++ b/docs/docs/tools/improve.md @@ -20,6 +20,7 @@ ___ !!! note "The following features are available only for Qodo Merge 💎 users:" - The `Apply / Chat` checkbox, which interactively converts a suggestion into a committable code comment - The `More` checkbox to generate additional suggestions + - On Bitbucket (Cloud & Data Center) and GitLab Server (v16 and earlier), you can invoke [More Suggestions manually](#manual-more-suggestions) ## Example usage @@ -44,6 +45,13 @@ For example, you can choose to present all the suggestions as committable code c As can be seen, a single table comment has a significantly smaller PR footprint. We recommend this mode for most cases. Also note that collapsible are not supported in _Bitbucket_. Hence, the suggestions can only be presented in Bitbucket as code comments. +#### Manual more suggestions +To generate more suggestions (distinct from the ones already generated), for git-providers that don't support interactive checkbox option, you can manually run: + +``` +/improve --more_suggestions=true +``` + ### Automatic triggering To run the `improve` automatically when a PR is opened, define in a [configuration file](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/#wiki-configuration-file): From 489a16a3e63cdef9dedccbf6317e2a093c33fe7f Mon Sep 17 00:00:00 2001 From: mrT23 Date: Tue, 13 May 2025 08:05:36 +0300 Subject: [PATCH 212/365] docs: reorganize documentation structure and move PR benchmark section --- docs/docs/core-abilities/index.md | 1 - docs/docs/{finetuning_benchmark => pr_benchmark}/index.md | 0 docs/docs/usage-guide/index.md | 1 + docs/mkdocs.yml | 6 +++--- 4 files changed, 4 insertions(+), 4 deletions(-) rename docs/docs/{finetuning_benchmark => pr_benchmark}/index.md (100%) diff --git a/docs/docs/core-abilities/index.md b/docs/docs/core-abilities/index.md index d06a39aed2..9af26e2eee 100644 --- a/docs/docs/core-abilities/index.md +++ b/docs/docs/core-abilities/index.md @@ -3,7 +3,6 @@ Qodo Merge utilizes a variety of core abilities to provide a comprehensive and efficient code review experience. These abilities include: - [Auto best practices](https://qodo-merge-docs.qodo.ai/core-abilities/auto_best_practices/) -- [Pull request benchmark](https://qodo-merge-docs.qodo.ai/finetuning_benchmark/) - [Code validation](https://qodo-merge-docs.qodo.ai/core-abilities/code_validation/) - [Compression strategy](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) - [Dynamic context](https://qodo-merge-docs.qodo.ai/core-abilities/dynamic_context/) diff --git a/docs/docs/finetuning_benchmark/index.md b/docs/docs/pr_benchmark/index.md similarity index 100% rename from docs/docs/finetuning_benchmark/index.md rename to docs/docs/pr_benchmark/index.md diff --git a/docs/docs/usage-guide/index.md b/docs/docs/usage-guide/index.md index 8de093affd..34a66dd506 100644 --- a/docs/docs/usage-guide/index.md +++ b/docs/docs/usage-guide/index.md @@ -22,4 +22,5 @@ It includes information on how to adjust Qodo Merge configurations, define which - [Working with large PRs](./additional_configurations.md#working-with-large-prs) - [Changing a model](./additional_configurations.md#changing-a-model) - [Patch Extra Lines](./additional_configurations.md#patch-extra-lines) +- [FAQ](https://qodo-merge-docs.qodo.ai/faq/) - [Qodo Merge Models](./qodo_merge_models) diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 425b2db703..e8dd439002 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -20,6 +20,7 @@ nav: - Managing Mail Notifications: 'usage-guide/mail_notifications.md' - Changing a Model: 'usage-guide/changing_a_model.md' - Additional Configurations: 'usage-guide/additional_configurations.md' + - Frequently Asked Questions: 'faq/index.md' - 💎 Qodo Merge Models: 'usage-guide/qodo_merge_models.md' - Tools: - 'tools/index.md' @@ -43,7 +44,6 @@ nav: - Core Abilities: - 'core-abilities/index.md' - Auto best practices: 'core-abilities/auto_best_practices.md' - - Pull request benchmark: 'finetuning_benchmark/index.md' - Code validation: 'core-abilities/code_validation.md' - Compression strategy: 'core-abilities/compression_strategy.md' - Dynamic context: 'core-abilities/dynamic_context.md' @@ -59,8 +59,8 @@ nav: - Features: 'chrome-extension/features.md' - Data Privacy: 'chrome-extension/data_privacy.md' - Options: 'chrome-extension/options.md' - - FAQ: - - FAQ: 'faq/index.md' + - PR Benchmark: + - FAQ: 'pr_benchmark/index.md' - Recent Updates: - Recent Updates: 'recent_updates/index.md' - AI Docs Search: 'ai_search/index.md' From 25530a8b2c805b07809aa5df18ed822035c2a506 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Tue, 13 May 2025 08:39:19 +0300 Subject: [PATCH 213/365] docs: add benchmark methodology and improve model comparison formatting --- docs/docs/pr_benchmark/index.md | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/docs/docs/pr_benchmark/index.md b/docs/docs/pr_benchmark/index.md index 85c312009a..5e25a46955 100644 --- a/docs/docs/pr_benchmark/index.md +++ b/docs/docs/pr_benchmark/index.md @@ -2,9 +2,23 @@ ## Methodology -... +Qodo Merge PR Benchmark evaluates and compares the performance of two Large Language Models (LLMs) in analyzing pull request code and providing meaningful code suggestions. +Our diverse dataset comprises of 400 pull requests from over 100 repositories, spanning various programming languages and frameworks to reflect real-world scenarios. -## Gemini-2.5-pro-preview-05-06 +- For each pull request, two distinct LLMs process the same prompt using the Qodo Merge `improve` tool, each generating two sets of responses. The prompt for response generation can be found [here](https://github.com/qodo-ai/pr-agent/blob/main/pr_agent/settings/code_suggestions/pr_code_suggestions_prompts_not_decoupled.toml). + +- Subsequently, a high-performing third model (an AI judge) evaluates the responses from the initial two models to determine the superior one. We utilize OpenAI's `o3` model as the judge, though other models have yielded consistent results. The prompt for this comparative judgment is available [here](https://github.com/Codium-ai/pr-agent-settings/tree/main/benchmark). + +- We aggregate comparison outcomes across all the pull requests, calculating the win rate for each model. We also analyze the qualitative feedback (the "why" explanations from the judge) to identify each model's comparative strengths and weaknesses. +This approach provides not just a quantitative score but also a detailed analysis of each model's strengths and weaknesses. + +- The final output is a "Model Card", comparing the evaluated model against others. To ensure full transparency and enable community scrutiny, we also share the raw code suggestions generated by each model, and the judge's specific feedback. + +Note that this benchmark focuses on quality: the ability of an LLM to process complex pull request with multiple files and nuanced task to produce high-quality code suggestions. +Other factors like speed, cost, and availability, while also relevant for model selection, are outside this benchmark's scope. + + +## Gemini-2.5-pro-preview-05-06 - Model Card ### Gemini-2.5-pro-preview-05-06 vs GPT-4.1 @@ -14,15 +28,15 @@ Model 'Gemini-2.5-pro-preview-05-06' is generally more useful thanks to wider and more accurate bug detection and concrete patches, but it sacrifices compliance discipline and sometimes oversteps the task rules. Model 'GPT-4.1' is safer and highly rule-abiding, yet often too timid—missing many genuine issues and providing limited insight. An ideal reviewer would combine 'GPT-4.1’ restraint with 'Gemini-2.5-pro-preview-05-06' thoroughness. -#### Gemini-2.5-pro-preview-05-06 vs GPT-4.1 - Detailed Analysis +#### Detailed Analysis -strengths: +Gemini-2.5-pro-preview-05-06 vs GPT-4.1 strengths: - better_bug_coverage: Detects and explains more critical issues, winning in ~70 % of comparisons and achieving a higher average score. - actionable_fixes: Supplies clear code snippets, correct language labels, and often multiple coherent suggestions per diff. - deeper_reasoning: Shows stronger grasp of logic, edge cases, and cross-file implications, leading to broader, high-impact reviews. -weaknesses: +Gemini-2.5-pro-preview-05-06 vs GPT-4.1 weaknesses: - guideline_violations: More prone to over-eager advice—non-critical tweaks, touching unchanged code, suggesting new imports, or minor format errors. - occasional_overreach: Some fixes are speculative or risky, potentially introducing new bugs. @@ -40,15 +54,15 @@ Model 'Gemini-2.5-pro-preview-05-06' is the stronger reviewer—more frequently See raw results [here](https://github.com/Codium-ai/pr-agent-settings/blob/main/benchmark/sonnet_37_vs_gemini-2.5-pro-preview-05-06.md) -#### Gemini-2.5-pro-preview-05-06 vs Sonnet 3.7 - Detailed Analysis +#### Detailed Analysis -strengths: +Gemini-2.5-pro-preview-05-06 vs Sonnet 3.7 strengths: - higher_accuracy_and_coverage: finds real critical bugs and supplies actionable patches in most examples (better in 78 % of cases). - guideline_awareness: usually respects new-lines-only scope, ≤3 suggestions, proper YAML, and stays silent when no issues exist. - detailed_reasoning_and_patches: explanations tie directly to the diff and fixes are concrete, often catching multiple related defects that 'Sonnet 3.7' overlooks. -weaknesses: +Gemini-2.5-pro-preview-05-06 vs Sonnet 3.7 weaknesses: - occasional_rule_violations: sometimes proposes new imports, package-version changes, or edits outside the added lines. - overzealous_suggestions: may add speculative or stylistic fixes that exceed the “critical” scope, or mis-label severity. From e7317ce99f95e2a743cc5867318ab67ee9ecf8d9 Mon Sep 17 00:00:00 2001 From: dst03106 Date: Tue, 13 May 2025 14:46:50 +0900 Subject: [PATCH 214/365] Add timeout to asyncio.wait --- pr_agent/cli.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pr_agent/cli.py b/pr_agent/cli.py index ef1614378a..13058971df 100644 --- a/pr_agent/cli.py +++ b/pr_agent/cli.py @@ -86,7 +86,9 @@ async def inner(): if get_settings().litellm.get("enable_callbacks", False): # There may be additional events on the event queue from the run above. If there are give them time to complete. get_logger().debug("Waiting for event queue to complete") - await asyncio.wait([task for task in asyncio.all_tasks() if task is not asyncio.current_task()]) + tasks = [task for task in asyncio.all_tasks() if task is not asyncio.current_task()] + if tasks: + await asyncio.wait(tasks, timeout=30) return result From 3ec5bc12b7fbf5644533c29be8bf3e8ad764f3c5 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Tue, 13 May 2025 08:53:03 +0300 Subject: [PATCH 215/365] s --- docs/docs/pr_benchmark/index.md | 206 ++++++++++---------------------- 1 file changed, 61 insertions(+), 145 deletions(-) diff --git a/docs/docs/pr_benchmark/index.md b/docs/docs/pr_benchmark/index.md index 5e25a46955..2624e1b2a8 100644 --- a/docs/docs/pr_benchmark/index.md +++ b/docs/docs/pr_benchmark/index.md @@ -17,6 +17,15 @@ This approach provides not just a quantitative score but also a detailed analysi Note that this benchmark focuses on quality: the ability of an LLM to process complex pull request with multiple files and nuanced task to produce high-quality code suggestions. Other factors like speed, cost, and availability, while also relevant for model selection, are outside this benchmark's scope. +## TL;DR + +Here's a summary of the win rates based on the benchmark: + +| Model A | Model B | Model A Win Rate | Model B Win Rate | +|-------------------------------|-------------------------------|------------------|------------------| +| Gemini-2.5-pro-preview-05-06 | GPT-4.1 | 70.4% | 29.6% | +| Gemini-2.5-pro-preview-05-06 | Sonnet 3.7 | 78.1% | 21.9% | +| GPT-4.1 | Sonnet 3.7 | 61.0% | 39.0% | ## Gemini-2.5-pro-preview-05-06 - Model Card @@ -68,180 +77,87 @@ Gemini-2.5-pro-preview-05-06 vs Sonnet 3.7 weaknesses: - overzealous_suggestions: may add speculative or stylistic fixes that exceed the “critical” scope, or mis-label severity. - sporadic_technical_slips: a few patches contain minor coding errors, oversized snippets, or duplicate/contradicting advice. +## GPT-4.1 - Model Card +### GPT-4.1 vs Sonnet 3.7 +![Comparison](https://codium.ai/images/qodo_merge_benchmark/gpt-4.1_vs_sonnet_3.7_judge_o3.png){width=768} -[//]: # (On coding tasks, the gap between open-source models and top closed-source models such as Claude and GPT is significant.) - -[//]: # (
) - -[//]: # (In practice, open-source models are unsuitable for most real-world code tasks, and require further fine-tuning to produce acceptable results.) - -[//]: # () -[//]: # (_Qodo Merge pull request benchmark_ aims to benchmark models on their ability to be fine-tuned for a coding task.) - -[//]: # (Specifically, we chose to fine-tune open-source models on the task of analyzing a pull request, and providing useful feedback and code suggestions.) - -[//]: # () -[//]: # (Here are the results:) - -[//]: # (
) - -[//]: # (
) - -[//]: # () -[//]: # (**Model performance:**) - -[//]: # () -[//]: # (| Model name | Model size [B] | Better than gpt-4 rate, after fine-tuning [%] |) - -[//]: # (|-----------------------------|----------------|----------------------------------------------|) - -[//]: # (| **DeepSeek 34B-instruct** | **34** | **40.7** |) - -[//]: # (| DeepSeek 34B-base | 34 | 38.2 |) - -[//]: # (| Phind-34b | 34 | 38 |) - -[//]: # (| Granite-34B | 34 | 37.6 |) - -[//]: # (| Codestral-22B-v0.1 | 22 | 32.7 |) - -[//]: # (| QWEN-1.5-32B | 32 | 29 |) - -[//]: # (| | | |) - -[//]: # (| **CodeQwen1.5-7B** | **7** | **35.4** |) - -[//]: # (| Llama-3.1-8B-Instruct | 8 | 35.2 |) - -[//]: # (| Granite-8b-code-instruct | 8 | 34.2 |) - -[//]: # (| CodeLlama-7b-hf | 7 | 31.8 |) - -[//]: # (| Gemma-7B | 7 | 27.2 |) - -[//]: # (| DeepSeek coder-7b-instruct | 7 | 26.8 |) - -[//]: # (| Llama-3-8B-Instruct | 8 | 26.8 |) - -[//]: # (| Mistral-7B-v0.1 | 7 | 16.1 |) - -[//]: # () -[//]: # (
) - -[//]: # () -[//]: # (**Fine-tuning impact:**) - -[//]: # () -[//]: # (| Model name | Model size [B] | Fine-tuned | Better than gpt-4 rate [%] |) - -[//]: # (|---------------------------|----------------|------------|----------------------------|) - -[//]: # (| DeepSeek 34B-instruct | 34 | yes | 40.7 |) - -[//]: # (| DeepSeek 34B-instruct | 34 | no | 3.6 |) - -[//]: # () -[//]: # (## Results analysis) - -[//]: # () -[//]: # (- **Fine-tuning is a must** - without fine-tuning, open-source models provide poor results on most real-world code tasks, which include complicated prompt and lengthy context. We clearly see that without fine-tuning, deepseek model was 96.4% of the time inferior to GPT-4, while after fine-tuning, it is better 40.7% of the time.) - -[//]: # (- **Always start from a code-dedicated model** — When fine-tuning, always start from a code-dedicated model, and not from a general-usage model. The gaps in downstream results are very big.) - -[//]: # (- **Don't believe the hype** —newer models, or models from big-tech companies (Llama3, Gemma, Mistral), are not always better for fine-tuning.) - -[//]: # (- **The best large model** - For large 34B code-dedicated models, the gaps when doing proper fine-tuning are small. The current top model is **DeepSeek 34B-instruct**) - -[//]: # (- **The best small model** - For small 7B code-dedicated models, the gaps when fine-tuning are much larger. **CodeQWEN 1.5-7B** is by far the best model for fine-tuning.) - -[//]: # (- **Base vs. instruct** - For the top model (deepseek), we saw small advantage when starting from the instruct version. However, we recommend testing both versions on each specific task, as the base model is generally considered more suitable for fine-tuning.) - -[//]: # () -[//]: # (## Dataset) - -[//]: # () -[//]: # (### Training dataset) - -[//]: # () -[//]: # (Our training dataset comprises 25,000 pull requests, aggregated from permissive license repos. For each pull request, we generated responses for the three main tools of Qodo Merge:) - -[//]: # ([Describe](https://qodo-merge-docs.qodo.ai/tools/describe/), [Review](https://qodo-merge-docs.qodo.ai/tools/improve/) and [Improve](https://qodo-merge-docs.qodo.ai/tools/improve/).) - -[//]: # () -[//]: # (On the raw data collected, we employed various automatic and manual cleaning techniques to ensure the outputs were of the highest quality, and suitable for instruct-tuning.) - -[//]: # () -[//]: # (Here are the prompts, and example outputs, used as input-output pairs to fine-tune the models:) - -[//]: # () -[//]: # (| Tool | Prompt | Example output |) +#### Analysis Summary -[//]: # (|----------|------------------------------------------------------------------------------------------------------------|----------------|) +Model 'GPT-4.1' is safer and more compliant, preferring silence over speculation, which yields fewer rule breaches and false positives but misses some real bugs. +Model 'Sonnet 3.7' is more adventurous and often uncovers important issues that 'GPT-4.1' ignores, yet its aggressive style leads to frequent guideline violations and a higher proportion of incorrect or non-critical advice. -[//]: # (| Describe | [link](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/pr_description_prompts.toml) | [link](https://github.com/Codium-ai/pr-agent/pull/910#issue-2303989601) |) +See raw results [here](https://github.com/Codium-ai/pr-agent-settings/blob/main/benchmark/gpt-4.1_vs_sonnet_3.7_judge_o3.md) -[//]: # (| Review | [link](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/pr_reviewer_prompts.toml) | [link](https://github.com/Codium-ai/pr-agent/pull/910#issuecomment-2118761219) |) -[//]: # (| Improve | [link](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/pr_code_suggestions_prompts.toml) | [link](https://github.com/Codium-ai/pr-agent/pull/910#issuecomment-2118761309) |) +#### Detailed Analysis -[//]: # () -[//]: # (### Evaluation dataset) +Model 'GPT-4.1' vs 'Sonnet 3.7' +strengths: +- Strong guideline adherence: usually stays strictly on `+` lines, avoids non-critical or stylistic advice, and rarely suggests forbidden imports; often outputs an empty list when no real bug exists. +- Lower false-positive rate: suggestions are more accurate and seldom introduce new bugs; fixes compile more reliably. +- Good schema discipline: YAML is almost always well-formed and fields are populated correctly. -[//]: # () -[//]: # (- For each tool, we aggregated 200 additional examples to be used for evaluation. These examples were not used in the training dataset, and were manually selected to represent diverse real-world use-cases.) +weaknesses: +- Misses bugs: often returns an empty list even when a clear critical issue is present, so coverage is narrower. +- Sparse feedback: when it does comment, it tends to give fewer suggestions and sometimes lacks depth or completeness. +- Occasional metadata/slip-ups (wrong language tags, overly broad code spans), though less harmful than Sonnet 3.7 errors. -[//]: # (- For each test example, we generated two responses: one from the fine-tuned model, and one from the best code model in the world, `gpt-4-turbo-2024-04-09`.) +### GPT-4.1 vs Gemini-2.5-pro-preview-05-06 -[//]: # () -[//]: # (- We used a third LLM to judge which response better answers the prompt, and will likely be perceived by a human as better response.) +![Comparison](https://codium.ai/images/qodo_merge_benchmark/gpt-4.1_vs_gemini-2.5-pro-preview-05-06_judge_o3.png){width=768} -[//]: # (
) +#### Analysis Summary -[//]: # () -[//]: # (We experimented with three model as judges: `gpt-4-turbo-2024-04-09`, `gpt-4o`, and `claude-3-opus-20240229`. All three produced similar results, with the same ranking order. This strengthens the validity of our testing protocol.) +Model 'Gemini-2.5-pro-preview-05-06' is generally more useful thanks to wider and more accurate bug detection and concrete patches, but it sacrifices compliance discipline and sometimes oversteps the task rules. Model 'GPT-4.1' is safer and highly rule-abiding, yet often too timid—missing many genuine issues and providing limited insight. An ideal reviewer would combine 'GPT-4.1’ restraint with 'Gemini-2.5-pro-preview-05-06' thoroughness. -[//]: # (The evaluation prompt can be found [here](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/pr_evaluate_prompt_response.toml)) +#### Detailed Analysis -[//]: # () -[//]: # (Here is an example of a judge model feedback:) +GPT-4.1 strengths: +- strict_compliance: Usually sticks to the “critical bugs only / new ‘+’ lines only” rule, so outputs rarely violate task constraints. +- low_risk: Conservative behaviour avoids harmful or speculative fixes; safer when no obvious issue exists. +- concise_formatting: Tends to produce minimal, correctly-structured YAML without extra noise. -[//]: # () -[//]: # (```) +GPT-4.1 weaknesses: +- under_detection: Frequently returns an empty list even when real bugs are present, missing ~70 % of the time. +- shallow_analysis: When it does suggest fixes, coverage is narrow and technical depth is limited, sometimes with wrong language tags or minor format slips. +- occasional_inaccuracy: A few suggestions are unfounded or duplicate, and rare guideline breaches (e.g., import advice) still occur. -[//]: # (command: improve) -[//]: # (model1_score: 9,) +## Sonnet 3.7 - Model Card -[//]: # (model2_score: 6,) +### Sonnet 3.7 vs GPT-4.1 -[//]: # (why: |) +![Comparison](https://codium.ai/images/qodo_merge_benchmark/gpt-4.1_vs_sonnet_3.7_judge_o3.png){width=768} -[//]: # ( Response 1 is better because it provides more actionable and specific suggestions that directly) +#### Analysis Summary -[//]: # ( enhance the code's maintainability, performance, and best practices. For example, it suggests) +Model 'GPT-4.1' is safer and more compliant, preferring silence over speculation, which yields fewer rule breaches and false positives but misses some real bugs. +Model 'Sonnet 3.7' is more adventurous and often uncovers important issues that 'GPT-4.1' ignores, yet its aggressive style leads to frequent guideline violations and a higher proportion of incorrect or non-critical advice. -[//]: # ( using a variable for reusable widget instances and using named routes for navigation, which) +See raw results [here](https://github.com/Codium-ai/pr-agent-settings/blob/main/benchmark/gpt-4.1_vs_sonnet_3.7_judge_o3.md) -[//]: # ( are practical improvements. In contrast, Response 2 focuses more on general advice and less) +#### Detailed Analysis -[//]: # ( actionable suggestions, such as changing variable names and adding comments, which are less) +Model 'Sonnet 3.7' vs 'GPT-4.1' +'Sonnet 3.7' strengths: +- Better bug discovery breadth: more willing to dive into logic and spot critical problems that 'GPT-4.1' overlooks; often supplies multiple, detailed fixes. +- Richer explanations & patches: gives fuller context and, when correct, proposes more functional or user-friendly solutions. +- Generally correct language/context tagging and targeted code snippets. -[//]: # ( critical for immediate code improvement.") +'Sonnet 3.7' weaknesses: +- Guideline violations: frequently flags non-critical issues, edits untouched code, or recommends adding imports, breaching task rules. +- Higher error rate: suggestions are more speculative and sometimes introduce new defects or duplicate work already done. +- Occasional schema or formatting mistakes (missing list value, duplicated suggestions), reducing reliability. -[//]: # (```) -[//]: # () -[//]: # (## Comparing Top Closed-Source Models) +### Sonnet 3.7 vs Gemini-2.5-pro-preview-05-06 -[//]: # () -[//]: # (Another application of the Pull Request Benchmark is comparing leading closed-source models to determine which performs better at analyzing pull request code.) +![Comparison](https://codium.ai/images/qodo_merge_benchmark/sonnet_37_vs_gemini-2.5-pro-preview-05-06_judge_o3.png){width=768} -[//]: # () -[//]: # (The evaluation methodology resembles the approach used for evaluating fine-tuned models:) +#### Analysis Summary -[//]: # () -[//]: # (- We ran each model across 200 diverse pull requests, asking them to generate code suggestions using Qodo Merge's `improve` tool) +Model 'Gemini-2.5-pro-preview-05-06' is the stronger reviewer—more frequently identifies genuine, high-impact bugs and provides well-formed, actionable fixes. Model 'Sonnet 3.7' is safer against false positives and tends to be concise but often misses important defects or offers low-value or incorrect suggestions. -[//]: # (- A third top model served as judge to determine which response better fulfilled the prompt and would likely be perceived as superior by human users) +See raw results [here](https://github.com/Codium-ai/pr-agent-settings/blob/main/benchmark/sonnet_37_vs_gemini-2.5-pro-preview-05-06.md) From cbfbfa662d210866ac7c89b1c5418332c1a49bd9 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Tue, 13 May 2025 09:05:07 +0300 Subject: [PATCH 216/365] docs: enhance benchmark table with colored win rates and improve comparison headings --- docs/docs/pr_benchmark/index.md | 49 +++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/docs/docs/pr_benchmark/index.md b/docs/docs/pr_benchmark/index.md index 2624e1b2a8..37d2d022f8 100644 --- a/docs/docs/pr_benchmark/index.md +++ b/docs/docs/pr_benchmark/index.md @@ -21,15 +21,42 @@ Other factors like speed, cost, and availability, while also relevant for model Here's a summary of the win rates based on the benchmark: -| Model A | Model B | Model A Win Rate | Model B Win Rate | -|-------------------------------|-------------------------------|------------------|------------------| -| Gemini-2.5-pro-preview-05-06 | GPT-4.1 | 70.4% | 29.6% | -| Gemini-2.5-pro-preview-05-06 | Sonnet 3.7 | 78.1% | 21.9% | -| GPT-4.1 | Sonnet 3.7 | 61.0% | 39.0% | +[//]: # (| Model A | Model B | Model A Win Rate | Model B Win Rate |) + +[//]: # (|:-------------------------------|:-------------------------------|:----------------:|:----------------:|) + +[//]: # (| Gemini-2.5-pro-preview-05-06 | GPT-4.1 | 70.4% | 29.6% |) + +[//]: # (| Gemini-2.5-pro-preview-05-06 | Sonnet 3.7 | 78.1% | 21.9% |) + +[//]: # (| GPT-4.1 | Sonnet 3.7 | 61.0% | 39.0% |) + + + + + + + + + + + + + + + + + + + + + + +
Model AModel BModel A Win Rate Model B Win Rate
Gemini-2.5-pro-preview-05-06GPT-4.170.4% 29.6%
Gemini-2.5-pro-preview-05-06Sonnet 3.778.1% 21.9%
GPT-4.1Sonnet 3.761.0% 39.0%
## Gemini-2.5-pro-preview-05-06 - Model Card -### Gemini-2.5-pro-preview-05-06 vs GPT-4.1 +### Comparison against GPT-4.1 ![Comparison](https://codium.ai/images/qodo_merge_benchmark/gpt-4.1_vs_gemini-2.5-pro-preview-05-06_judge_o3.png){width=768} @@ -52,7 +79,7 @@ Gemini-2.5-pro-preview-05-06 vs GPT-4.1 weaknesses: - redundant_or_duplicate: At times repeats the same point or exceeds the required brevity. -### Gemini-2.5-pro-preview-05-06 vs Sonnet 3.7 +### Comparison against Sonnet 3.7 ![Comparison](https://codium.ai/images/qodo_merge_benchmark/sonnet_37_vs_gemini-2.5-pro-preview-05-06_judge_o3.png){width=768} @@ -79,7 +106,7 @@ Gemini-2.5-pro-preview-05-06 vs Sonnet 3.7 weaknesses: ## GPT-4.1 - Model Card -### GPT-4.1 vs Sonnet 3.7 +### Comparison against Sonnet 3.7 ![Comparison](https://codium.ai/images/qodo_merge_benchmark/gpt-4.1_vs_sonnet_3.7_judge_o3.png){width=768} @@ -104,7 +131,7 @@ weaknesses: - Sparse feedback: when it does comment, it tends to give fewer suggestions and sometimes lacks depth or completeness. - Occasional metadata/slip-ups (wrong language tags, overly broad code spans), though less harmful than Sonnet 3.7 errors. -### GPT-4.1 vs Gemini-2.5-pro-preview-05-06 +### Comparison against Gemini-2.5-pro-preview-05-06 ![Comparison](https://codium.ai/images/qodo_merge_benchmark/gpt-4.1_vs_gemini-2.5-pro-preview-05-06_judge_o3.png){width=768} @@ -127,7 +154,7 @@ GPT-4.1 weaknesses: ## Sonnet 3.7 - Model Card -### Sonnet 3.7 vs GPT-4.1 +### Comparison against GPT-4.1 ![Comparison](https://codium.ai/images/qodo_merge_benchmark/gpt-4.1_vs_sonnet_3.7_judge_o3.png){width=768} @@ -152,7 +179,7 @@ Model 'Sonnet 3.7' vs 'GPT-4.1' - Occasional schema or formatting mistakes (missing list value, duplicated suggestions), reducing reliability. -### Sonnet 3.7 vs Gemini-2.5-pro-preview-05-06 +### Comparison against Gemini-2.5-pro-preview-05-06 ![Comparison](https://codium.ai/images/qodo_merge_benchmark/sonnet_37_vs_gemini-2.5-pro-preview-05-06_judge_o3.png){width=768} From f0fa27535c3373e1668e66931a286a397e1b25f9 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Tue, 13 May 2025 09:07:46 +0300 Subject: [PATCH 217/365] docs: improve model comparison headings in benchmark documentation --- docs/docs/pr_benchmark/index.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/docs/docs/pr_benchmark/index.md b/docs/docs/pr_benchmark/index.md index 37d2d022f8..7c2c709652 100644 --- a/docs/docs/pr_benchmark/index.md +++ b/docs/docs/pr_benchmark/index.md @@ -66,13 +66,13 @@ Model 'Gemini-2.5-pro-preview-05-06' is generally more useful thanks to wider an #### Detailed Analysis -Gemini-2.5-pro-preview-05-06 vs GPT-4.1 strengths: +Gemini-2.5-pro-preview-05-06 strengths: - better_bug_coverage: Detects and explains more critical issues, winning in ~70 % of comparisons and achieving a higher average score. - actionable_fixes: Supplies clear code snippets, correct language labels, and often multiple coherent suggestions per diff. - deeper_reasoning: Shows stronger grasp of logic, edge cases, and cross-file implications, leading to broader, high-impact reviews. -Gemini-2.5-pro-preview-05-06 vs GPT-4.1 weaknesses: +Gemini-2.5-pro-preview-05-06 weaknesses: - guideline_violations: More prone to over-eager advice—non-critical tweaks, touching unchanged code, suggesting new imports, or minor format errors. - occasional_overreach: Some fixes are speculative or risky, potentially introducing new bugs. @@ -92,13 +92,13 @@ See raw results [here](https://github.com/Codium-ai/pr-agent-settings/blob/main/ #### Detailed Analysis -Gemini-2.5-pro-preview-05-06 vs Sonnet 3.7 strengths: +Gemini-2.5-pro-preview-05-06 strengths: - higher_accuracy_and_coverage: finds real critical bugs and supplies actionable patches in most examples (better in 78 % of cases). - guideline_awareness: usually respects new-lines-only scope, ≤3 suggestions, proper YAML, and stays silent when no issues exist. - detailed_reasoning_and_patches: explanations tie directly to the diff and fixes are concrete, often catching multiple related defects that 'Sonnet 3.7' overlooks. -Gemini-2.5-pro-preview-05-06 vs Sonnet 3.7 weaknesses: +Gemini-2.5-pro-preview-05-06 weaknesses: - occasional_rule_violations: sometimes proposes new imports, package-version changes, or edits outside the added lines. - overzealous_suggestions: may add speculative or stylistic fixes that exceed the “critical” scope, or mis-label severity. @@ -120,13 +120,12 @@ See raw results [here](https://github.com/Codium-ai/pr-agent-settings/blob/main/ #### Detailed Analysis -Model 'GPT-4.1' vs 'Sonnet 3.7' -strengths: +GPT-4.1 strengths: - Strong guideline adherence: usually stays strictly on `+` lines, avoids non-critical or stylistic advice, and rarely suggests forbidden imports; often outputs an empty list when no real bug exists. - Lower false-positive rate: suggestions are more accurate and seldom introduce new bugs; fixes compile more reliably. - Good schema discipline: YAML is almost always well-formed and fields are populated correctly. -weaknesses: +GPT-4.1 weaknesses: - Misses bugs: often returns an empty list even when a clear critical issue is present, so coverage is narrower. - Sparse feedback: when it does comment, it tends to give fewer suggestions and sometimes lacks depth or completeness. - Occasional metadata/slip-ups (wrong language tags, overly broad code spans), though less harmful than Sonnet 3.7 errors. @@ -167,7 +166,6 @@ See raw results [here](https://github.com/Codium-ai/pr-agent-settings/blob/main/ #### Detailed Analysis -Model 'Sonnet 3.7' vs 'GPT-4.1' 'Sonnet 3.7' strengths: - Better bug discovery breadth: more willing to dive into logic and spot critical problems that 'GPT-4.1' overlooks; often supplies multiple, detailed fixes. - Richer explanations & patches: gives fuller context and, when correct, proposes more functional or user-friendly solutions. From 87f4783fa0677d15ddacc13b3ad3a42c9811a2cb Mon Sep 17 00:00:00 2001 From: Tal Date: Tue, 13 May 2025 09:11:06 +0300 Subject: [PATCH 218/365] Update docs/mkdocs.yml Co-authored-by: qodo-merge-pro-for-open-source[bot] <189517486+qodo-merge-pro-for-open-source[bot]@users.noreply.github.com> --- docs/mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index e8dd439002..8525fdee3e 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -60,7 +60,7 @@ nav: - Data Privacy: 'chrome-extension/data_privacy.md' - Options: 'chrome-extension/options.md' - PR Benchmark: - - FAQ: 'pr_benchmark/index.md' + - PR Benchmark: 'pr_benchmark/index.md' - Recent Updates: - Recent Updates: 'recent_updates/index.md' - AI Docs Search: 'ai_search/index.md' From 90c46bb2ddeb15a939f5631efb13ee92b74afde5 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Tue, 13 May 2025 11:31:28 +0300 Subject: [PATCH 219/365] docs: update link to "Changing a model" documentation page --- docs/docs/usage-guide/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/usage-guide/index.md b/docs/docs/usage-guide/index.md index 8de093affd..f918e5e95e 100644 --- a/docs/docs/usage-guide/index.md +++ b/docs/docs/usage-guide/index.md @@ -20,6 +20,6 @@ It includes information on how to adjust Qodo Merge configurations, define which - [Ignoring files from analysis](./additional_configurations.md#ignoring-files-from-analysis) - [Extra instructions](./additional_configurations.md#extra-instructions) - [Working with large PRs](./additional_configurations.md#working-with-large-prs) - - [Changing a model](./additional_configurations.md#changing-a-model) + - [Changing a model](https://qodo-merge-docs.qodo.ai/usage-guide/changing_a_model/) - [Patch Extra Lines](./additional_configurations.md#patch-extra-lines) - [Qodo Merge Models](./qodo_merge_models) From d86d1ef3dc072523dbac29ef62e5e8c26db1d14e Mon Sep 17 00:00:00 2001 From: Yunhui Chae <53514545+dst03106@users.noreply.github.com> Date: Tue, 13 May 2025 17:34:30 +0900 Subject: [PATCH 220/365] Log a warning when there are pending asyncio tasks Co-authored-by: qodo-merge-pro-for-open-source[bot] <189517486+qodo-merge-pro-for-open-source[bot]@users.noreply.github.com> --- pr_agent/cli.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pr_agent/cli.py b/pr_agent/cli.py index 13058971df..64ea519913 100644 --- a/pr_agent/cli.py +++ b/pr_agent/cli.py @@ -88,7 +88,9 @@ async def inner(): get_logger().debug("Waiting for event queue to complete") tasks = [task for task in asyncio.all_tasks() if task is not asyncio.current_task()] if tasks: - await asyncio.wait(tasks, timeout=30) + done, pending = await asyncio.wait(tasks, timeout=30) + if pending: + get_logger().warning(f"{len(pending)} callback tasks did not complete within timeout") return result From e2af22d2a00504d2f67de2cf3fe45276c336114d Mon Sep 17 00:00:00 2001 From: dst03106 Date: Tue, 13 May 2025 17:37:09 +0900 Subject: [PATCH 221/365] Log a warning for pending asyncio tasks with coroutine details --- pr_agent/cli.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pr_agent/cli.py b/pr_agent/cli.py index 64ea519913..a741a0b832 100644 --- a/pr_agent/cli.py +++ b/pr_agent/cli.py @@ -88,9 +88,11 @@ async def inner(): get_logger().debug("Waiting for event queue to complete") tasks = [task for task in asyncio.all_tasks() if task is not asyncio.current_task()] if tasks: - done, pending = await asyncio.wait(tasks, timeout=30) + _, pending = await asyncio.wait(tasks, timeout=30) if pending: - get_logger().warning(f"{len(pending)} callback tasks did not complete within timeout") + get_logger().warning( + f"{len(pending)} callback tasks({[task.get_coro() for task in pending]}) did not complete within timeout" + ) return result From 3ddd53d4fee33cf1f506593d3b1ba775cb4db4d3 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Tue, 13 May 2025 11:48:01 +0300 Subject: [PATCH 222/365] docs: add link to example model card in benchmark documentation --- docs/docs/pr_benchmark/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/pr_benchmark/index.md b/docs/docs/pr_benchmark/index.md index 7c2c709652..53851e3608 100644 --- a/docs/docs/pr_benchmark/index.md +++ b/docs/docs/pr_benchmark/index.md @@ -12,7 +12,7 @@ Our diverse dataset comprises of 400 pull requests from over 100 repositories, s - We aggregate comparison outcomes across all the pull requests, calculating the win rate for each model. We also analyze the qualitative feedback (the "why" explanations from the judge) to identify each model's comparative strengths and weaknesses. This approach provides not just a quantitative score but also a detailed analysis of each model's strengths and weaknesses. -- The final output is a "Model Card", comparing the evaluated model against others. To ensure full transparency and enable community scrutiny, we also share the raw code suggestions generated by each model, and the judge's specific feedback. +- For each model we build a "Model Card", comparing it against others. To ensure full transparency and enable community scrutiny, we also share the raw code suggestions generated by each model, and the judge's specific feedback. See example for the full output [here](https://github.com/Codium-ai/pr-agent-settings/blob/main/benchmark/sonnet_37_vs_gemini-2.5-pro-preview-05-06.md) Note that this benchmark focuses on quality: the ability of an LLM to process complex pull request with multiple files and nuanced task to produce high-quality code suggestions. Other factors like speed, cost, and availability, while also relevant for model selection, are outside this benchmark's scope. From 72bcb0ec4cb6bdaa03a91ea38f3ca70ade2bf26f Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 14 May 2025 07:35:09 +0300 Subject: [PATCH 223/365] docs: add Gemini-2.5-flash-preview benchmark comparisons to PR benchmark table --- docs/docs/pr_benchmark/index.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/docs/pr_benchmark/index.md b/docs/docs/pr_benchmark/index.md index 53851e3608..b26fd85074 100644 --- a/docs/docs/pr_benchmark/index.md +++ b/docs/docs/pr_benchmark/index.md @@ -47,6 +47,18 @@ Here's a summary of the win rates based on the benchmark: Gemini-2.5-pro-preview-05-06 Sonnet 3.7 78.1% 21.9% + + Gemini-2.5-pro-preview-05-06 + Gemini-2.5-flash-preview-04-17 + 73.0% 27.0% + + Gemini-2.5-flash-preview-04-17 + GPT-4.1 + 54.6% 45.4% + + Gemini-2.5-flash-preview-04-17 + Sonnet 3.7 + 60.6% 39.4% GPT-4.1 Sonnet 3.7 @@ -54,6 +66,7 @@ Here's a summary of the win rates based on the benchmark: + ## Gemini-2.5-pro-preview-05-06 - Model Card ### Comparison against GPT-4.1 From e55fd64bda9cd7b274bb5a82f7c53871994865b9 Mon Sep 17 00:00:00 2001 From: Sangmin Park Date: Thu, 15 May 2025 18:41:39 +0900 Subject: [PATCH 224/365] Remove unnecessary nested try-except block for cleaner code. Streamlined the import statement to remove an unused reference to `get_git_provider`. --- pr_agent/git_providers/utils.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/pr_agent/git_providers/utils.py b/pr_agent/git_providers/utils.py index 1b4232dfb3..51da46c926 100644 --- a/pr_agent/git_providers/utils.py +++ b/pr_agent/git_providers/utils.py @@ -6,8 +6,7 @@ from starlette_context import context from pr_agent.config_loader import get_settings -from pr_agent.git_providers import (get_git_provider, - get_git_provider_with_context) +from pr_agent.git_providers import get_git_provider_with_context from pr_agent.log import get_logger @@ -17,11 +16,7 @@ def apply_repo_settings(pr_url): if get_settings().config.use_repo_settings_file: repo_settings_file = None try: - try: - repo_settings = context.get("repo_settings", None) - except Exception: - repo_settings = None - pass + repo_settings = context.get("repo_settings", None) if repo_settings is None: # None is different from "", which is a valid value repo_settings = git_provider.get_repo_settings() try: From c79b655864e1f1a61205a5152a82ac61c971b4b1 Mon Sep 17 00:00:00 2001 From: Sangmin Park Date: Thu, 15 May 2025 18:42:08 +0900 Subject: [PATCH 225/365] Fix typo in method parameter name --- pr_agent/identity_providers/identity_provider.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/identity_providers/identity_provider.py b/pr_agent/identity_providers/identity_provider.py index 58e5f6c63d..0fdb9a3767 100644 --- a/pr_agent/identity_providers/identity_provider.py +++ b/pr_agent/identity_providers/identity_provider.py @@ -10,7 +10,7 @@ class Eligibility(Enum): class IdentityProvider(ABC): @abstractmethod - def verify_eligibility(self, git_provider, git_provier_id, pr_url): + def verify_eligibility(self, git_provider, git_provider_id, pr_url): pass @abstractmethod From a3c9fbbf2cebc6143697a3bfcda59b1c225010e2 Mon Sep 17 00:00:00 2001 From: Sangmin Park Date: Thu, 15 May 2025 19:40:40 +0900 Subject: [PATCH 226/365] revert try except --- pr_agent/git_providers/utils.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pr_agent/git_providers/utils.py b/pr_agent/git_providers/utils.py index 51da46c926..0cfbe11627 100644 --- a/pr_agent/git_providers/utils.py +++ b/pr_agent/git_providers/utils.py @@ -16,7 +16,11 @@ def apply_repo_settings(pr_url): if get_settings().config.use_repo_settings_file: repo_settings_file = None try: - repo_settings = context.get("repo_settings", None) + try: + repo_settings = context.get("repo_settings", None) + except Exception: + repo_settings = None + pass if repo_settings is None: # None is different from "", which is a valid value repo_settings = git_provider.get_repo_settings() try: From 250870a3dae7865dba31dac942956b657377c1bd Mon Sep 17 00:00:00 2001 From: Dennis Paul Date: Thu, 15 May 2025 16:05:05 +0200 Subject: [PATCH 227/365] enable usage of openai like apis --- docs/docs/usage-guide/changing_a_model.md | 17 +++++++++++++++++ pr_agent/algo/ai_handlers/litellm_ai_handler.py | 1 + 2 files changed, 18 insertions(+) diff --git a/docs/docs/usage-guide/changing_a_model.md b/docs/docs/usage-guide/changing_a_model.md index c0b2bc287b..bfeb7c3a55 100644 --- a/docs/docs/usage-guide/changing_a_model.md +++ b/docs/docs/usage-guide/changing_a_model.md @@ -16,6 +16,23 @@ You can give parameters via a configuration file, or from environment variables. See [litellm documentation](https://litellm.vercel.app/docs/proxy/quick_start#supported-llms) for the environment variables needed per model, as they may vary and change over time. Our documentation per-model may not always be up-to-date with the latest changes. Failing to set the needed keys of a specific model will usually result in litellm not identifying the model type, and failing to utilize it. +### OpenAI like API +To use an OpenAI like API, set the following in your `.secrets.toml` file: + +```toml +[openai] +api_base = "https://api.openai.com/v1" +api_key = "sk-..." +``` + +or use the environment variables (make sure to use double underscores `__`): + +```bash +OPENAI__API_BASE=https://api.openai.com/v1 +OPENAI__KEY=sk-... +``` + + ### Azure To use Azure, set in your `.secrets.toml` (working from CLI), or in the GitHub `Settings > Secrets and variables` (working from GitHub App or GitHub Action): diff --git a/pr_agent/algo/ai_handlers/litellm_ai_handler.py b/pr_agent/algo/ai_handlers/litellm_ai_handler.py index ba4630f409..8804165801 100644 --- a/pr_agent/algo/ai_handlers/litellm_ai_handler.py +++ b/pr_agent/algo/ai_handlers/litellm_ai_handler.py @@ -59,6 +59,7 @@ def __init__(self): litellm.api_version = get_settings().openai.api_version if get_settings().get("OPENAI.API_BASE", None): litellm.api_base = get_settings().openai.api_base + self.api_base = get_settings().openai.api_base if get_settings().get("ANTHROPIC.KEY", None): litellm.anthropic_key = get_settings().anthropic.key if get_settings().get("COHERE.KEY", None): From 05ab5f699faf2c211602dcf925bd68bb778bbf16 Mon Sep 17 00:00:00 2001 From: kkan9ma Date: Fri, 16 May 2025 17:51:22 +0900 Subject: [PATCH 228/365] Improve token calculation logic based on model type - Rename calc_tokens to get_token_count_by_model_type for clearer intent - Separate model type detection logic to improve maintainability --- pr_agent/algo/token_handler.py | 46 +++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/pr_agent/algo/token_handler.py b/pr_agent/algo/token_handler.py index 60cf2c84af..0c8851e8cd 100644 --- a/pr_agent/algo/token_handler.py +++ b/pr_agent/algo/token_handler.py @@ -107,25 +107,37 @@ def calc_claude_tokens(self, patch): get_logger().error( f"Error in Anthropic token counting: {e}") return MaxTokens - def estimate_token_count_for_non_anth_claude_models(self, model, default_encoder_estimate): - from math import ceil - import re - - model_is_from_o_series = re.match(r"^o[1-9](-mini|-preview)?$", model) - if ('gpt' in get_settings().config.model.lower() or model_is_from_o_series) and get_settings(use_context=False).get('openai.key'): - return default_encoder_estimate - #else: Model is not an OpenAI one - therefore, cannot provide an accurate token count and instead, return a higher number as best effort. + def is_openai_model(self, model_name): + from re import match - elbow_factor = 1 + get_settings().get('config.model_token_count_estimate_factor', 0) - get_logger().warning(f"{model}'s expected token count cannot be accurately estimated. Using {elbow_factor} of encoder output as best effort estimate") - return ceil(elbow_factor * default_encoder_estimate) + return 'gpt' in model_name or match(r"^o[1-9](-mini|-preview)?$", model_name) + + def apply_estimation_factor(self, model_name, default_estimate): + from math import ceil - def count_tokens(self, patch: str, force_accurate=False) -> int: + factor = 1 + get_settings().get('config.model_token_count_estimate_factor', 0) + get_logger().warning(f"{model_name}'s token count cannot be accurately estimated. Using factor of {factor}") + + return ceil(factor * default_estimate) + + def get_token_count_by_model_type(self, patch: str, default_estimate: int) -> int: + model_name = get_settings().config.model.lower() + + if 'claude' in model_name and get_settings(use_context=False).get('anthropic.key'): + return self.calc_claude_tokens(patch) + + if self.is_openai_model(model_name) and get_settings(use_context=False).get('openai.key'): + return default_estimate + + return self.apply_estimation_factor(model_name, default_estimate) + + def count_tokens(self, patch: str, force_accurate: bool = False) -> int: """ Counts the number of tokens in a given patch string. Args: - patch: The patch string. + - force_accurate: If True, uses a more precise calculation method. Returns: The number of tokens in the patch string. @@ -135,11 +147,5 @@ def count_tokens(self, patch: str, force_accurate=False) -> int: #If an estimate is enough (for example, in cases where the maximal allowed tokens is way below the known limits), return it. if not force_accurate: return encoder_estimate - - #else, force_accurate==True: User requested providing an accurate estimation: - model = get_settings().config.model.lower() - if 'claude' in model and get_settings(use_context=False).get('anthropic.key'): - return self.calc_claude_tokens(patch) # API call to Anthropic for accurate token counting for Claude models - - #else: Non Anthropic provided model: - return self.estimate_token_count_for_non_anth_claude_models(model, encoder_estimate) + else: + return self.get_token_count_by_model_type(patch, encoder_estimate=encoder_estimate) From 9ef0c451bf684444472c5ad4d780c0372be29f4a Mon Sep 17 00:00:00 2001 From: Pinyoo Thotaboot Date: Fri, 16 May 2025 16:30:10 +0700 Subject: [PATCH 229/365] Create provider module for --- pr_agent/git_providers/gitea_provider.py | 982 +++++++++++++++++++++++ 1 file changed, 982 insertions(+) create mode 100644 pr_agent/git_providers/gitea_provider.py diff --git a/pr_agent/git_providers/gitea_provider.py b/pr_agent/git_providers/gitea_provider.py new file mode 100644 index 0000000000..9f2715564b --- /dev/null +++ b/pr_agent/git_providers/gitea_provider.py @@ -0,0 +1,982 @@ +import hashlib +import json +from typing import Any, Dict, List, Optional, Set, Tuple +from urllib.parse import urlparse + +import giteapy +from giteapy.rest import ApiException + +from pr_agent.algo.file_filter import filter_ignored +from pr_agent.algo.language_handler import is_valid_file +from pr_agent.algo.types import EDIT_TYPE +from pr_agent.algo.utils import (clip_tokens, + find_line_number_of_relevant_line_in_file) +from pr_agent.config_loader import get_settings +from pr_agent.git_providers.git_provider import (MAX_FILES_ALLOWED_FULL, + FilePatchInfo, GitProvider, + IncrementalPR) +from pr_agent.log import get_logger + + +class GiteaProvider(GitProvider): + def __init__(self, url: Optional[str] = None): + super().__init__() + self.logger = get_logger() + + if not url: + self.logger.error("PR URL not provided.") + raise ValueError("PR URL not provided.") + + self.base_url = get_settings().get("GITEA.URL", "https://gitea.com").rstrip("/") + self.pr_url = "" + self.issue_url = "" + + gitea_access_token = get_settings().get("GITEA.PERSONAL_ACCESS_TOKEN", None) + if not gitea_access_token: + self.logger.error("Gitea access token not found in settings.") + raise ValueError("Gitea access token not found in settings.") + + self.repo_settings = get_settings().get("GITEA.REPO_SETTING", None) + configuration = giteapy.Configuration() + configuration.host = "{}/api/v1".format(self.base_url) + configuration.api_key['Authorization'] = f'token {gitea_access_token}' + + client = giteapy.ApiClient(configuration) + self.repo_api = RepoApi(client) + self.owner = None + self.repo = None + self.pr_number = None + self.issue_number = None + self.max_comment_chars = 65000 + self.enabled_pr = False + self.enabled_issue = False + self.temp_comments = [] + self.pr = None + self.git_files = [] + self.file_contents = {} + self.file_diffs = {} + self.sha = None + self.diff_files = [] + self.incremental = IncrementalPR(False) + self.comments_list = [] + self.unreviewed_files_set = dict() + + if "pulls" in url: + self.pr_url = url + self.__set_repo_and_owner_from_pr() + self.enabled_pr = True + self.pr = self.repo_api.get_pull_request( + owner=self.owner, + repo=self.repo, + pr_number=self.pr_number + ) + self.git_files = self.repo_api.get_change_file_pull_request( + owner=self.owner, + repo=self.repo, + pr_number=self.pr_number + ) + # Optional ignore with user custom + self.git_files = filter_ignored(self.git_files, platform="gitea") + + self.sha = self.pr.head.sha if self.pr.head.sha else "" + self.__add_file_content() + self.__add_file_diff() + self.pr_commits = self.repo_api.list_all_commits( + owner=self.owner, + repo=self.repo + ) + self.last_commit = self.pr_commits[-1] + self.base_sha = self.pr.base.sha if self.pr.base.sha else "" + self.base_ref = self.pr.base.ref if self.pr.base.ref else "" + elif "issues" in url: + self.issue_url = url + self.__set_repo_and_owner_from_issue() + self.enabled_issue = True + else: + self.pr_commits = None + + def __add_file_content(self): + for file in self.git_files: + file_path = file.get("filename") + # Ignore file from default settings + if not is_valid_file(file_path): + continue + + if file_path: + try: + content = self.repo_api.get_file_content( + owner=self.owner, + repo=self.repo, + commit_sha=self.sha, + filepath=file_path + ) + self.file_contents[file_path] = content + except ApiException as e: + self.logger.error(f"Error getting file content for {file_path}: {str(e)}") + + def __add_file_diff(self): + try: + diff_contents = self.repo_api.get_pull_request_diff( + owner=self.owner, + repo=self.repo, + pr_number=self.pr_number + ) + + lines = diff_contents.splitlines() + current_file = None + current_patch = [] + file_patches = {} + for line in lines: + if line.startswith('diff --git'): + if current_file and current_patch: + file_patches[current_file] = '\n'.join(current_patch) + current_patch = [] + current_file = line.split(' b/')[-1] + elif line.startswith('@@'): + current_patch = [line] + elif current_patch: + current_patch.append(line) + + if current_file and current_patch: + file_patches[current_file] = '\n'.join(current_patch) + + self.file_diffs = file_patches + except Exception as e: + self.logger.error(f"Error getting diff content: {str(e)}") + + def _parse_pr_url(self, pr_url: str) -> Tuple[str, str, int]: + parsed_url = urlparse(pr_url) + + if parsed_url.path.startswith('/api/v1'): + parsed_url = urlparse(pr_url.replace("/api/v1", "")) + + path_parts = parsed_url.path.strip('/').split('/') + if len(path_parts) < 4 or path_parts[2] != 'pulls': + raise ValueError("The provided URL does not appear to be a Gitea PR URL") + + try: + pr_number = int(path_parts[3]) + except ValueError as e: + raise ValueError("Unable to convert PR number to integer") from e + + owner = path_parts[0] + repo = path_parts[1] + + return owner, repo, pr_number + + def _parse_issue_url(self, issue_url: str) -> Tuple[str, str, int]: + parsed_url = urlparse(issue_url) + + if parsed_url.path.startswith('/api/v1'): + parsed_url = urlparse(issue_url.replace("/api/v1", "")) + + path_parts = parsed_url.path.strip('/').split('/') + if len(path_parts) < 4 or path_parts[2] != 'issues': + raise ValueError("The provided URL does not appear to be a Gitea issue URL") + + try: + issue_number = int(path_parts[3]) + except ValueError as e: + raise ValueError("Unable to convert issue number to integer") from e + + owner = path_parts[0] + repo = path_parts[1] + + return owner, repo, issue_number + + def __set_repo_and_owner_from_pr(self): + """Extract owner and repo from the PR URL""" + try: + owner, repo, pr_number = self._parse_pr_url(self.pr_url) + self.owner = owner + self.repo = repo + self.pr_number = pr_number + self.logger.info(f"Owner: {self.owner}, Repo: {self.repo}, PR Number: {self.pr_number}") + except ValueError as e: + self.logger.error(f"Error parsing PR URL: {str(e)}") + except Exception as e: + self.logger.error(f"Unexpected error: {str(e)}") + + def __set_repo_and_owner_from_issue(self): + """Extract owner and repo from the issue URL""" + try: + owner, repo, issue_number = self._parse_issue_url(self.issue_url) + self.owner = owner + self.repo = repo + self.issue_number = issue_number + self.logger.info(f"Owner: {self.owner}, Repo: {self.repo}, Issue Number: {self.issue_number}") + except ValueError as e: + self.logger.error(f"Error parsing issue URL: {str(e)}") + except Exception as e: + self.logger.error(f"Unexpected error: {str(e)}") + + def get_pr_url(self) -> str: + return self.pr_url + + def get_issue_url(self) -> str: + return self.issue_url + + def publish_comment(self, comment: str,is_temporary: bool = False) -> None: + """Publish a comment to the pull request""" + if is_temporary and not get_settings().config.publish_output_progress: + get_logger().debug(f"Skipping publish_comment for temporary comment") + return None + + if self.enabled_issue: + index = self.issue_number + elif self.enabled_pr: + index = self.pr_number + else: + self.logger.error("Neither PR nor issue URL provided.") + return None + + comment = self.limit_output_characters(comment, self.max_comment_chars) + reponse = self.repo_api.create_comment( + owner=self.owner, + repo=self.repo, + index=index, + comment=comment + ) + + if not reponse: + self.logger.error("Failed to publish comment") + return None + + if is_temporary: + self.temp_comments.append(comment) + + self.comments_list.append({ + "is_temporary": is_temporary, + "comment": comment, + "comment_id": reponse.id if isinstance(reponse, tuple) else reponse.id + }) + self.logger.info("Comment published") + + def edit_comment(self, comment, body : str): + body = self.limit_output_characters(body, self.max_comment_chars) + try: + self.repo_api.edit_comment( + owner=self.owner, + repo=self.repo, + comment_id=comment.get("comment_id") if isinstance(comment, dict) else comment.id, + comment=body + ) + except ApiException as e: + self.logger.error(f"Error editing comment: {e}") + return None + except Exception as e: + self.logger.error(f"Unexpected error: {e}") + return None + + + def publish_inline_comment(self,body: str, relevant_file: str, relevant_line_in_file: str, original_suggestion=None): + """Publish an inline comment on a specific line""" + body = self.limit_output_characters(body, self.max_comment_chars) + position, absolute_position = find_line_number_of_relevant_line_in_file(self.diff_files, + relevant_file.strip('`'), + relevant_line_in_file, + ) + if position == -1: + get_logger().info(f"Could not find position for {relevant_file} {relevant_line_in_file}") + subject_type = "FILE" + else: + subject_type = "LINE" + + path = relevant_file.strip() + payload = dict(body=body, path=path, old_position=position,new_position = absolute_position) if subject_type == "LINE" else {} + self.publish_inline_comments([payload]) + + + def publish_inline_comments(self, comments: List[Dict[str, Any]],body : str = "Inline comment") -> None: + response = self.repo_api.create_inline_comment( + owner=self.owner, + repo=self.repo, + pr_number=self.pr_number if self.enabled_pr else self.issue_number, + body=body, + commit_id=self.last_commit.sha if self.last_commit else "", + comments=comments + ) + + if not response: + self.logger.error("Failed to publish inline comment") + return None + + self.logger.info("Inline comment published") + + def publish_code_suggestions(self, suggestions: List[Dict[str, Any]]): + """Publish code suggestions""" + for suggestion in suggestions: + body = suggestion.get("body","") + if not body: + self.logger.error("No body provided for the suggestion") + continue + + path = suggestion.get("relevant_file","") + new_position = suggestion.get("relevant_lines_start",0) + old_position = suggestion.get("relevant_lines_start",0) if "original_suggestion" not in suggestion else suggestion["original_suggestion"].get("relevant_lines_start",0) + title_body = suggestion["original_suggestion"].get("suggestion_content","") if "original_suggestion" in suggestion else "" + payload = dict(body=body, path=path, old_position=old_position,new_position = new_position) + if title_body: + title_body = f"**Suggestion:** {title_body}" + self.publish_inline_comments([payload],title_body) + else: + self.publish_inline_comments([payload]) + + def add_eyes_reaction(self, issue_comment_id: int, disable_eyes: bool = False) -> Optional[int]: + """Add eyes reaction to a comment""" + try: + if disable_eyes: + return None + + comments = self.repo_api.list_all_comments( + owner=self.owner, + repo=self.repo, + index=self.pr_number if self.enabled_pr else self.issue_number + ) + + comment_ids = [comment.id for comment in comments] + if issue_comment_id not in comment_ids: + self.logger.error(f"Comment ID {issue_comment_id} not found. Available IDs: {comment_ids}") + return None + + response = self.repo_api.add_reaction_comment( + owner=self.owner, + repo=self.repo, + comment_id=issue_comment_id, + reaction="eyes" + ) + + if not response: + self.logger.error("Failed to add eyes reaction") + return None + + return response[0].id if isinstance(response, tuple) else response.id + + except ApiException as e: + self.logger.error(f"Error adding eyes reaction: {e}") + return None + except Exception as e: + self.logger.error(f"Unexpected error: {e}") + return None + + def remove_reaction(self, comment_id: int) -> None: + """Remove reaction from a comment""" + try: + response = self.repo_api.remove_reaction_comment( + owner=self.owner, + repo=self.repo, + comment_id=comment_id + ) + if not response: + self.logger.error("Failed to remove reaction") + except ApiException as e: + self.logger.error(f"Error removing reaction: {e}") + except Exception as e: + self.logger.error(f"Unexpected error: {e}") + + def get_commit_messages(self)-> str: + """Get commit messages for the PR""" + max_tokens = get_settings().get("CONFIG.MAX_COMMITS_TOKENS", None) + pr_commits = self.repo_api.get_pr_commits( + owner=self.owner, + repo=self.repo, + pr_number=self.pr_number + ) + + if not pr_commits: + self.logger.error("Failed to get commit messages") + return "" + + try: + commit_messages = [commit["commit"]["message"] for commit in pr_commits if commit] + + if not commit_messages: + self.logger.error("No commit messages found") + return "" + + commit_message = "".join(commit_messages) + if max_tokens: + commit_message = clip_tokens(commit_message, max_tokens) + + return commit_message + except Exception as e: + self.logger.error(f"Error processing commit messages: {str(e)}") + return "" + + def _get_file_content_from_base(self, filename: str) -> str: + return self.repo_api.get_file_content( + owner=self.owner, + repo=self.base_ref, + commit_sha=self.base_sha, + filepath=filename + ) + + def _get_file_content_from_latest_commit(self, filename: str) -> str: + return self.repo_api.get_file_content( + owner=self.owner, + repo=self.base_ref, + commit_sha=self.last_commit.sha, + filepath=filename + ) + + def get_diff_files(self) -> List[FilePatchInfo]: + """Get files that were modified in the PR""" + if self.diff_files: + return self.diff_files + + invalid_files_names = [] + counter_valid = 0 + diff_files = [] + for file in self.git_files: + filename = file.get("filename") + if not filename: + continue + + if not is_valid_file(filename): + invalid_files_names.append(filename) + continue + + counter_valid += 1 + avoid_load = False + patch = self.file_diffs.get(filename,"") + head_file = "" + base_file = "" + + if counter_valid >= MAX_FILES_ALLOWED_FULL and patch and not self.incremental.is_incremental: + avoid_load = True + if counter_valid == MAX_FILES_ALLOWED_FULL: + self.logger.info("Too many files in PR, will avoid loading full content for rest of files") + + if avoid_load: + head_file = "" + else: + # Get file content from this pr + head_file = self.file_contents.get(filename,"") + + if self.incremental.is_incremental and self.unreviewed_files_set: + base_file = self._get_file_content_from_latest_commit(filename) + self.unreviewed_files_set[filename] = patch + else: + if avoid_load: + base_file = "" + else: + base_file = self._get_file_content_from_base(filename) + + num_plus_lines = file.get("additions",0) + num_minus_lines = file.get("deletions",0) + status = file.get("status","") + + if status == 'added': + edit_type = EDIT_TYPE.ADDED + elif status == 'removed': + edit_type = EDIT_TYPE.DELETED + elif status == 'renamed': + edit_type = EDIT_TYPE.RENAMED + elif status == 'modified': + edit_type = EDIT_TYPE.MODIFIED + else: + self.logger.error(f"Unknown edit type: {status}") + edit_type = EDIT_TYPE.UNKNOWN + + file_patch_info = FilePatchInfo( + base_file=base_file, + head_file=head_file, + patch=patch, + filename=filename, + num_minus_lines=num_minus_lines, + num_plus_lines=num_plus_lines, + edit_type=edit_type + ) + diff_files.append(file_patch_info) + + if invalid_files_names: + self.logger.info(f"Filtered out files with invalid extensions: {invalid_files_names}") + + self.diff_files = diff_files + return diff_files + + def get_line_link(self, relevant_file, relevant_line_start, relevant_line_end = None) -> str: + if relevant_line_start == -1: + link = f"{self.base_url}/{self.owner}/{self.repo}/src/branch/{self.get_pr_branch()}/{relevant_file}" + elif relevant_line_end: + link = f"{self.base_url}/{self.owner}/{self.repo}/src/branch/{self.get_pr_branch()}/{relevant_file}#L{relevant_line_start}-L{relevant_line_end}" + else: + link = f"{self.base_url}/{self.owner}/{self.repo}/src/branch/{self.get_pr_branch()}/{relevant_file}#L{relevant_line_start}" + + self.logger.info(f"Generated link: {link}") + return link + + def get_files(self) -> List[Dict[str, Any]]: + """Get all files in the PR""" + return [file.get("filename","") for file in self.git_files] + + def get_num_of_files(self) -> int: + """Get number of files changed in the PR""" + return len(self.git_files) + + def get_issue_comments(self) -> List[Dict[str, Any]]: + """Get all comments in the PR""" + index = self.issue_number if self.enabled_issue else self.pr_number + comments = self.repo_api.list_all_comments( + owner=self.owner, + repo=self.repo, + index=index + ) + if not comments: + self.logger.error("Failed to get comments") + return [] + + return comments + + def get_languages(self) -> Set[str]: + """Get programming languages used in the repository""" + languages = self.repo_api.get_languages( + owner=self.owner, + repo=self.repo + ) + + return languages + + def get_pr_branch(self) -> str: + """Get the branch name of the PR""" + if not self.pr: + self.logger.error("Failed to get PR branch") + return "" + + return self.pr.head.ref if self.pr.head.ref else "" + + def get_pr_description_full(self) -> str: + """Get full PR description with metadata""" + if not self.pr: + self.logger.error("Failed to get PR description") + return "" + + return self.pr.body if self.pr.body else "" + + def get_pr_labels(self,update=False) -> List[str]: + """Get labels assigned to the PR""" + if not update: + if not self.pr.labels: + self.logger.error("Failed to get PR labels") + return [] + return [label.name for label in self.pr.labels] + + labels = self.repo_api.get_issue_labels( + owner=self.owner, + repo=self.repo, + issue_number=self.pr_number + ) + if not labels: + self.logger.error("Failed to get PR labels") + return [] + + return [label.name for label in labels] + + def get_repo_settings(self) -> str: + """Get repository settings""" + if not self.repo_settings: + self.logger.error("Repository settings not found") + return "" + + response = self.repo_api.get_file_content( + owner=self.owner, + repo=self.repo, + commit_sha=self.sha, + filepath=self.repo_settings + ) + if not response: + self.logger.error("Failed to get repository settings") + return "" + + return response + + def get_user_id(self) -> str: + """Get the ID of the authenticated user""" + return f"{self.pr.user.id}" if self.pr else "" + + def is_supported(self, capability) -> bool: + """Check if the provider is supported""" + return True + + def publish_description(self, pr_title: str, pr_body: str) -> None: + """Publish PR description""" + response = self.repo_api.edit_pull_request( + owner=self.owner, + repo=self.repo, + pr_number=self.pr_number if self.enabled_pr else self.issue_number, + title=pr_title, + body=pr_body + ) + + if not response: + self.logger.error("Failed to publish PR description") + return None + + self.logger.info("PR description published successfully") + if self.enabled_pr: + self.pr = self.repo_api.get_pull_request( + owner=self.owner, + repo=self.repo, + pr_number=self.pr_number + ) + + def publish_labels(self, labels: List[int]) -> None: + """Publish labels to the PR""" + if not labels: + self.logger.error("No labels provided to publish") + return None + + response = self.repo_api.add_labels( + owner=self.owner, + repo=self.repo, + issue_number=self.pr_number if self.enabled_pr else self.issue_number, + labels=labels + ) + + if response: + self.logger.info("Labels added successfully") + + def remove_comment(self, comment) -> None: + """Remove a specific comment""" + if not comment: + return + + try: + comment_id = comment.get("comment_id") + if not comment_id: + self.logger.error("Comment ID not found") + return None + self.repo_api.remove_comment( + owner=self.owner, + repo=self.repo, + comment_id=comment_id + ) + + if self.comments_list: + self.comments_list.remove(comment) + + self.logger.info(f"Comment removed successfully: {comment}") + except ApiException as e: + self.logger.error(f"Error removing comment: {e}") + raise e + + def remove_initial_comment(self) -> None: + """Remove the initial comment""" + for comment in self.comments_list: + try: + self.remove_comment(comment) + except Exception as e: + self.logger.error(f"Error removing comment: {e}") + continue + self.logger.info(f"Removed initial comment: {comment.get('comment_id')}") + + +class RepoApi(giteapy.RepositoryApi): + def __init__(self, client: giteapy.ApiClient): + self.repository = giteapy.RepositoryApi(client) + self.issue = giteapy.IssueApi(client) + self.logger = get_logger() + super().__init__(client) + + def create_inline_comment(self, owner: str, repo: str, pr_number: int, body : str ,commit_id : str, comments: List[Dict[str, Any]]) -> None: + body = { + "body": body, + "comments": comments, + "commit_id": commit_id, + } + return self.api_client.call_api( + '/repos/{owner}/{repo}/pulls/{pr_number}/reviews', + 'POST', + path_params={'owner': owner, 'repo': repo, 'pr_number': pr_number}, + body=body, + response_type='Repository', + auth_settings=['AuthorizationHeaderToken'] + ) + + def create_comment(self, owner: str, repo: str, index: int, comment: str): + body = { + "body": comment + } + return self.issue.issue_create_comment( + owner=owner, + repo=repo, + index=index, + body=body + ) + + def edit_comment(self, owner: str, repo: str, comment_id: int, comment: str): + body = { + "body": comment + } + return self.issue.issue_edit_comment( + owner=owner, + repo=repo, + id=comment_id, + body=body + ) + + def remove_comment(self, owner: str, repo: str, comment_id: int): + return self.issue.issue_delete_comment( + owner=owner, + repo=repo, + id=comment_id + ) + + def list_all_comments(self, owner: str, repo: str, index: int): + return self.issue.issue_get_comments( + owner=owner, + repo=repo, + index=index + ) + + def get_pull_request_diff(self, owner: str, repo: str, pr_number: int) -> str: + """Get the diff content of a pull request using direct API call""" + try: + token = self.api_client.configuration.api_key.get('Authorization', '').replace('token ', '') + url = f'/repos/{owner}/{repo}/pulls/{pr_number}.diff' + if token: + url = f'{url}?token={token}' + + response = self.api_client.call_api( + url, + 'GET', + path_params={}, + response_type=None, + _return_http_data_only=False, + _preload_content=False + ) + + if hasattr(response, 'data'): + raw_data = response.data.read() + return raw_data.decode('utf-8') + elif isinstance(response, tuple): + raw_data = response[0].read() + return raw_data.decode('utf-8') + else: + self.logger.error("Unexpected response format") + return "" + + except ApiException as e: + self.logger.error(f"Error getting diff: {str(e)}") + raise e + except Exception as e: + self.logger.error(f"Unexpected error: {str(e)}") + raise e + + def get_pull_request(self, owner: str, repo: str, pr_number: int): + """Get pull request details including description""" + return self.repository.repo_get_pull_request( + owner=owner, + repo=repo, + index=pr_number + ) + + def edit_pull_request(self, owner: str, repo: str, pr_number: int,title : str, body: str): + """Edit pull request description""" + body = { + "body": body, + "title" : title + } + return self.repository.repo_edit_pull_request( + owner=owner, + repo=repo, + index=pr_number, + body=body + ) + + def get_change_file_pull_request(self, owner: str, repo: str, pr_number: int): + """Get changed files in the pull request""" + try: + token = self.api_client.configuration.api_key.get('Authorization', '').replace('token ', '') + url = f'/repos/{owner}/{repo}/pulls/{pr_number}/files' + if token: + url = f'{url}?token={token}' + + response = self.api_client.call_api( + url, + 'GET', + path_params={}, + response_type=None, + _return_http_data_only=False, + _preload_content=False + ) + + if hasattr(response, 'data'): + raw_data = response.data.read() + diff_content = raw_data.decode('utf-8') + return json.loads(diff_content) if isinstance(diff_content, str) else diff_content + elif isinstance(response, tuple): + raw_data = response[0].read() + diff_content = raw_data.decode('utf-8') + return json.loads(diff_content) if isinstance(diff_content, str) else diff_content + + return [] + + except ApiException as e: + self.logger.error(f"Error getting changed files: {e}") + return [] + except Exception as e: + self.logger.error(f"Unexpected error: {e}") + return [] + + def get_languages(self, owner: str, repo: str): + """Get programming languages used in the repository""" + try: + token = self.api_client.configuration.api_key.get('Authorization', '').replace('token ', '') + url = f'/repos/{owner}/{repo}/languages' + if token: + url = f'{url}?token={token}' + + response = self.api_client.call_api( + url, + 'GET', + path_params={}, + response_type=None, + _return_http_data_only=False, + _preload_content=False + ) + + if hasattr(response, 'data'): + raw_data = response.data.read() + return json.loads(raw_data.decode('utf-8')) + elif isinstance(response, tuple): + raw_data = response[0].read() + return json.loads(raw_data.decode('utf-8')) + + return {} + + except ApiException as e: + self.logger.error(f"Error getting languages: {e}") + return {} + except Exception as e: + self.logger.error(f"Unexpected error: {e}") + return {} + + def get_file_content(self, owner: str, repo: str, commit_sha: str, filepath: str) -> str: + """Get raw file content from a specific commit""" + + try: + token = self.api_client.configuration.api_key.get('Authorization', '').replace('token ', '') + url = f'/repos/{owner}/{repo}/raw/{filepath}' + if token: + url = f'{url}?token={token}&ref={commit_sha}' + + response = self.api_client.call_api( + url, + 'GET', + path_params={}, + response_type=None, + _return_http_data_only=False, + _preload_content=False + ) + + if hasattr(response, 'data'): + raw_data = response.data.read() + return raw_data.decode('utf-8') + elif isinstance(response, tuple): + raw_data = response[0].read() + return raw_data.decode('utf-8') + + return "" + + except ApiException as e: + self.logger.error(f"Error getting file: {filepath}, content: {e}") + return "" + except Exception as e: + self.logger.error(f"Unexpected error: {e}") + return "" + + def get_issue_labels(self, owner: str, repo: str, issue_number: int): + """Get labels assigned to the issue""" + return self.issue.issue_get_labels( + owner=owner, + repo=repo, + index=issue_number + ) + + def list_all_commits(self, owner: str, repo: str): + return self.repository.repo_get_all_commits( + owner=owner, + repo=repo + ) + + def add_reviewer(self, owner: str, repo: str, pr_number: int, reviewers: List[str]): + body = { + "reviewers": reviewers + } + return self.api_client.call_api( + '/repos/{owner}/{repo}/pulls/{pr_number}/requested_reviewers', + 'POST', + path_params={'owner': owner, 'repo': repo, 'pr_number': pr_number}, + body=body, + response_type='Repository', + auth_settings=['AuthorizationHeaderToken'] + ) + + def add_reaction_comment(self, owner: str, repo: str, comment_id: int, reaction: str): + body = { + "content": reaction + } + return self.api_client.call_api( + '/repos/{owner}/{repo}/issues/comments/{id}/reactions', + 'POST', + path_params={'owner': owner, 'repo': repo, 'id': comment_id}, + body=body, + response_type='Repository', + auth_settings=['AuthorizationHeaderToken'] + ) + + def remove_reaction_comment(self, owner: str, repo: str, comment_id: int): + return self.api_client.call_api( + '/repos/{owner}/{repo}/issues/comments/{id}/reactions', + 'DELETE', + path_params={'owner': owner, 'repo': repo, 'id': comment_id}, + response_type='Repository', + auth_settings=['AuthorizationHeaderToken'] + ) + + def add_labels(self, owner: str, repo: str, issue_number: int, labels: List[int]): + body = { + "labels": labels + } + return self.issue.issue_add_label( + owner=owner, + repo=repo, + index=issue_number, + body=body + ) + + def get_pr_commits(self, owner: str, repo: str, pr_number: int): + """Get all commits in a pull request""" + try: + token = self.api_client.configuration.api_key.get('Authorization', '').replace('token ', '') + url = f'/repos/{owner}/{repo}/pulls/{pr_number}/commits' + if token: + url = f'{url}?token={token}' + + response = self.api_client.call_api( + url, + 'GET', + path_params={}, + response_type=None, + _return_http_data_only=False, + _preload_content=False + ) + + if hasattr(response, 'data'): + raw_data = response.data.read() + commits_data = json.loads(raw_data.decode('utf-8')) + return commits_data + elif isinstance(response, tuple): + raw_data = response[0].read() + commits_data = json.loads(raw_data.decode('utf-8')) + return commits_data + + return [] + + except ApiException as e: + self.logger.error(f"Error getting PR commits: {e}") + return [] + except Exception as e: + self.logger.error(f"Unexpected error: {e}") + return [] From cf2b95b7663346fa0492d6d43512b32b4db1488c Mon Sep 17 00:00:00 2001 From: Pinyoo Thotaboot Date: Fri, 16 May 2025 16:30:50 +0700 Subject: [PATCH 230/365] Create webhook server implement for --- pr_agent/servers/gitea_app.py | 120 ++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 pr_agent/servers/gitea_app.py diff --git a/pr_agent/servers/gitea_app.py b/pr_agent/servers/gitea_app.py new file mode 100644 index 0000000000..4df8b84ca8 --- /dev/null +++ b/pr_agent/servers/gitea_app.py @@ -0,0 +1,120 @@ +import asyncio +import copy +import os +from typing import Any, Dict + +from fastapi import APIRouter, FastAPI, HTTPException, Request, Response +from starlette.background import BackgroundTasks +from starlette.middleware import Middleware +from starlette_context import context +from starlette_context.middleware import RawContextMiddleware + +from pr_agent.agent.pr_agent import PRAgent +from pr_agent.config_loader import get_settings, global_settings +from pr_agent.log import LoggingFormat, get_logger, setup_logger +from pr_agent.servers.utils import verify_signature + +# Setup logging and router +setup_logger(fmt=LoggingFormat.JSON, level=get_settings().get("CONFIG.LOG_LEVEL", "DEBUG")) +router = APIRouter() + +@router.post("/api/v1/gitea_webhooks") +async def handle_gitea_webhooks(background_tasks: BackgroundTasks, request: Request, response: Response): + """Handle incoming Gitea webhook requests""" + get_logger().debug("Received a Gitea webhook") + + body = await get_body(request) + + # Set context for the request + context["settings"] = copy.deepcopy(global_settings) + context["git_provider"] = {} + + # Handle the webhook in background + background_tasks.add_task(handle_request, body, event=request.headers.get("X-Gitea-Event", None)) + return {} + +async def get_body(request: Request): + """Parse and verify webhook request body""" + try: + body = await request.json() + except Exception as e: + get_logger().error("Error parsing request body", artifact={'error': e}) + raise HTTPException(status_code=400, detail="Error parsing request body") from e + + + # Verify webhook signature + webhook_secret = getattr(get_settings().gitea, 'webhook_secret', None) + if webhook_secret: + body_bytes = await request.body() + signature_header = request.headers.get('x-gitea-signature', None) + verify_signature(body_bytes, webhook_secret, f"sha256={signature_header}") + + return body + +async def handle_request(body: Dict[str, Any], event: str): + """Process Gitea webhook events""" + action = body.get("action") + if not action: + get_logger().debug("No action found in request body") + return {} + + agent = PRAgent() + + # Handle different event types + if event == "pull_request": + if action in ["opened", "reopened", "synchronized"]: + await handle_pr_event(body, event, action, agent) + elif event == "issue_comment": + if action == "created": + await handle_comment_event(body, event, action, agent) + + return {} + +async def handle_pr_event(body: Dict[str, Any], event: str, action: str, agent: PRAgent): + """Handle pull request events""" + pr = body.get("pull_request", {}) + if not pr: + return + + api_url = pr.get("url") + if not api_url: + return + + # Handle PR based on action + if action in ["opened", "reopened"]: + commands = get_settings().get("gitea.pr_commands", []) + for command in commands: + await agent.handle_request(api_url, command) + elif action == "synchronized": + # Handle push to PR + await agent.handle_request(api_url, "/review --incremental") + +async def handle_comment_event(body: Dict[str, Any], event: str, action: str, agent: PRAgent): + """Handle comment events""" + comment = body.get("comment", {}) + if not comment: + return + + comment_body = comment.get("body", "") + if not comment_body or not comment_body.startswith("/"): + return + + pr_url = body.get("pull_request", {}).get("url") + if not pr_url: + return + + await agent.handle_request(pr_url, comment_body) + +# FastAPI app setup +middleware = [Middleware(RawContextMiddleware)] +app = FastAPI(middleware=middleware) +app.include_router(router) + +def start(): + """Start the Gitea webhook server""" + port = int(os.environ.get("PORT", "3000")) + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=port) + +if __name__ == "__main__": + start() From 2d7636543c5981bdbbe85112390c568f64719886 Mon Sep 17 00:00:00 2001 From: Pinyoo Thotaboot Date: Fri, 16 May 2025 16:31:49 +0700 Subject: [PATCH 231/365] Implement provider --- pr_agent/algo/file_filter.py | 3 +++ pr_agent/git_providers/__init__.py | 2 ++ 2 files changed, 5 insertions(+) diff --git a/pr_agent/algo/file_filter.py b/pr_agent/algo/file_filter.py index 5c575eefc5..79bb4d8e67 100644 --- a/pr_agent/algo/file_filter.py +++ b/pr_agent/algo/file_filter.py @@ -58,6 +58,9 @@ def filter_ignored(files, platform = 'github'): files = files_o elif platform == 'azure': files = [f for f in files if not r.match(f)] + elif platform == 'gitea': + files = [f for f in files if not r.match(f.get("filename", ""))] + except Exception as e: print(f"Could not filter file list: {e}") diff --git a/pr_agent/git_providers/__init__.py b/pr_agent/git_providers/__init__.py index 16547d90c4..8ee2db0803 100644 --- a/pr_agent/git_providers/__init__.py +++ b/pr_agent/git_providers/__init__.py @@ -8,6 +8,7 @@ from pr_agent.git_providers.codecommit_provider import CodeCommitProvider from pr_agent.git_providers.gerrit_provider import GerritProvider from pr_agent.git_providers.git_provider import GitProvider +from pr_agent.git_providers.gitea_provider import GiteaProvider from pr_agent.git_providers.github_provider import GithubProvider from pr_agent.git_providers.gitlab_provider import GitLabProvider from pr_agent.git_providers.local_git_provider import LocalGitProvider @@ -21,6 +22,7 @@ 'codecommit': CodeCommitProvider, 'local': LocalGitProvider, 'gerrit': GerritProvider, + 'gitea': GiteaProvider } From fab8573c4d81530fca33507d5ddc53d617a665a7 Mon Sep 17 00:00:00 2001 From: Pinyoo Thotaboot Date: Fri, 16 May 2025 16:33:36 +0700 Subject: [PATCH 232/365] Set default configuration --- pr_agent/settings/configuration.toml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index e63b7ea8b7..50dcbdb941 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -277,6 +277,14 @@ push_commands = [ "/review", ] +[gitea_app] +handle_push_trigger = true +pr_commands = [ + "/describe --pr_description.final_update_message=false", + "/review", + "/improve", +] + [bitbucket_app] pr_commands = [ "/describe --pr_description.final_update_message=false", From a692a700274fda7ca6b4f84e63f7b33ffbbe5aff Mon Sep 17 00:00:00 2001 From: Pinyoo Thotaboot Date: Fri, 16 May 2025 16:34:11 +0700 Subject: [PATCH 233/365] Implement for docker --- docker/Dockerfile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docker/Dockerfile b/docker/Dockerfile index 9e83e37bd1..ce609e4843 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -33,6 +33,11 @@ FROM base AS azure_devops_webhook ADD pr_agent pr_agent CMD ["python", "pr_agent/servers/azuredevops_server_webhook.py"] +FROM base AS gitea_app +ADD pr_agent pr_agent +CMD ["python", "-m", "gunicorn", "-k", "uvicorn.workers.UvicornWorker", "-c", "pr_agent/servers/gunicorn_config.py","pr_agent.servers.gitea_app:app"] + + FROM base AS test ADD requirements-dev.txt . RUN pip install --no-cache-dir -r requirements-dev.txt && rm requirements-dev.txt From 8b1abbcc2c41004fca988b3872e73985695de256 Mon Sep 17 00:00:00 2001 From: Pinyoo Thotaboot Date: Fri, 16 May 2025 16:34:53 +0700 Subject: [PATCH 234/365] Add lib dependency --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 86a50c849d..1038af6928 100644 --- a/requirements.txt +++ b/requirements.txt @@ -31,6 +31,7 @@ gunicorn==22.0.0 pytest-cov==5.0.0 pydantic==2.8.2 html2text==2024.2.26 +giteapy==1.0.8 # Uncomment the following lines to enable the 'similar issue' tool # pinecone-client # pinecone-datasets @ git+https://github.com/mrT23/pinecone-datasets.git@main From 99740156825c212cdaaf8674f45a6bedb9a797dc Mon Sep 17 00:00:00 2001 From: mrT23 Date: Fri, 16 May 2025 16:32:45 +0300 Subject: [PATCH 235/365] Add Gemini-2.5-pro-preview-05-06 model and update litellm dependency --- pr_agent/algo/__init__.py | 2 ++ requirements.txt | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pr_agent/algo/__init__.py b/pr_agent/algo/__init__.py index 8d805baf99..e38bd713fa 100644 --- a/pr_agent/algo/__init__.py +++ b/pr_agent/algo/__init__.py @@ -58,6 +58,7 @@ 'vertex_ai/claude-3-7-sonnet@20250219': 200000, 'vertex_ai/gemini-1.5-pro': 1048576, 'vertex_ai/gemini-2.5-pro-preview-03-25': 1048576, + 'vertex_ai/gemini-2.5-pro-preview-05-06': 1048576, 'vertex_ai/gemini-1.5-flash': 1048576, 'vertex_ai/gemini-2.0-flash': 1048576, 'vertex_ai/gemini-2.5-flash-preview-04-17': 1048576, @@ -66,6 +67,7 @@ 'gemini/gemini-1.5-flash': 1048576, 'gemini/gemini-2.0-flash': 1048576, 'gemini/gemini-2.5-pro-preview-03-25': 1048576, + 'gemini/gemini-2.5-pro-preview-05-06': 1048576, 'codechat-bison': 6144, 'codechat-bison-32k': 32000, 'anthropic.claude-instant-v1': 100000, diff --git a/requirements.txt b/requirements.txt index 86a50c849d..95145ec98c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,7 +13,7 @@ google-cloud-aiplatform==1.38.0 google-generativeai==0.8.3 google-cloud-storage==2.10.0 Jinja2==3.1.2 -litellm==1.66.3 +litellm==1.69.3 loguru==0.7.2 msrest==0.7.1 openai>=1.55.3 From 42557feb9711d106f664df89e4d6993407c225c3 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Fri, 16 May 2025 17:20:54 +0300 Subject: [PATCH 236/365] Enhance repository filtering with regex pattern matching for ignore_repositories --- .../usage-guide/additional_configurations.md | 17 +++++++++++++++-- pr_agent/servers/bitbucket_app.py | 11 ++--------- pr_agent/servers/github_app.py | 2 +- pr_agent/servers/gitlab_webhook.py | 2 +- pr_agent/settings/configuration.toml | 2 +- 5 files changed, 20 insertions(+), 14 deletions(-) diff --git a/docs/docs/usage-guide/additional_configurations.md b/docs/docs/usage-guide/additional_configurations.md index c345e0c69f..eb46bbaa1e 100644 --- a/docs/docs/usage-guide/additional_configurations.md +++ b/docs/docs/usage-guide/additional_configurations.md @@ -164,6 +164,7 @@ Qodo Merge allows you to automatically ignore certain PRs based on various crite - PRs with specific titles (using regex matching) - PRs between specific branches (using regex matching) +- PRs from specific repositories (using regex matching) - PRs not from specific folders - PRs containing specific labels - PRs opened by specific users @@ -172,7 +173,7 @@ Qodo Merge allows you to automatically ignore certain PRs based on various crite To ignore PRs with a specific title such as "[Bump]: ...", you can add the following to your `configuration.toml` file: -``` +```toml [config] ignore_pr_title = ["\\[Bump\\]"] ``` @@ -183,7 +184,7 @@ Where the `ignore_pr_title` is a list of regex patterns to match the PR title yo To ignore PRs from specific source or target branches, you can add the following to your `configuration.toml` file: -``` +```toml [config] ignore_pr_source_branches = ['develop', 'main', 'master', 'stage'] ignore_pr_target_branches = ["qa"] @@ -192,6 +193,18 @@ ignore_pr_target_branches = ["qa"] Where the `ignore_pr_source_branches` and `ignore_pr_target_branches` are lists of regex patterns to match the source and target branches you want to ignore. They are not mutually exclusive, you can use them together or separately. +### Ignoring PRs from specific repositories + +To ignore PRs from specific repositories, you can add the following to your `configuration.toml` file: + +```toml +[config] +ignore_repositories = ["my-org/my-repo1", "my-org/my-repo2"] +``` + +Where the `ignore_repositories` is a list of regex patterns to match the repositories you want to ignore. This is useful when you have multiple repositories and want to exclude certain ones from analysis. + + ### Ignoring PRs not from specific folders To allow only specific folders (often needed in large monorepos), set: diff --git a/pr_agent/servers/bitbucket_app.py b/pr_agent/servers/bitbucket_app.py index 5692132810..c55a335e29 100644 --- a/pr_agent/servers/bitbucket_app.py +++ b/pr_agent/servers/bitbucket_app.py @@ -129,12 +129,10 @@ def should_process_pr_logic(data) -> bool: sender = _get_username(data) repo_full_name = pr_data.get("destination", {}).get("repository", {}).get("full_name", "") - print(f"DEBUG: repo_full_name={repo_full_name}, ignore_repos={get_settings().get('CONFIG.IGNORE_REPOSITORIES', [])}") # logic to ignore PRs from specific repositories ignore_repos = get_settings().get("CONFIG.IGNORE_REPOSITORIES", []) - if ignore_repos and repo_full_name: - if repo_full_name in ignore_repos: - print(f"DEBUG: Ignoring due to repo match: {repo_full_name}") + if repo_full_name and ignore_repos: + if any(re.search(regex, repo_full_name) for regex in ignore_repos): get_logger().info(f"Ignoring PR from repository '{repo_full_name}' due to 'config.ignore_repositories' setting") return False @@ -142,7 +140,6 @@ def should_process_pr_logic(data) -> bool: ignore_pr_users = get_settings().get("CONFIG.IGNORE_PR_AUTHORS", []) if ignore_pr_users and sender: if sender in ignore_pr_users: - print(f"DEBUG: Ignoring due to user match: {sender}") get_logger().info(f"Ignoring PR from user '{sender}' due to 'config.ignore_pr_authors' setting") return False @@ -152,7 +149,6 @@ def should_process_pr_logic(data) -> bool: if not isinstance(ignore_pr_title_re, list): ignore_pr_title_re = [ignore_pr_title_re] if ignore_pr_title_re and any(re.search(regex, title) for regex in ignore_pr_title_re): - print(f"DEBUG: Ignoring due to title match: {title}") get_logger().info(f"Ignoring PR with title '{title}' due to config.ignore_pr_title setting") return False @@ -160,17 +156,14 @@ def should_process_pr_logic(data) -> bool: ignore_pr_target_branches = get_settings().get("CONFIG.IGNORE_PR_TARGET_BRANCHES", []) if (ignore_pr_source_branches or ignore_pr_target_branches): if any(re.search(regex, source_branch) for regex in ignore_pr_source_branches): - print(f"DEBUG: Ignoring due to source branch match: {source_branch}") get_logger().info( f"Ignoring PR with source branch '{source_branch}' due to config.ignore_pr_source_branches settings") return False if any(re.search(regex, target_branch) for regex in ignore_pr_target_branches): - print(f"DEBUG: Ignoring due to target branch match: {target_branch}") get_logger().info( f"Ignoring PR with target branch '{target_branch}' due to config.ignore_pr_target_branches settings") return False except Exception as e: - print(f"DEBUG: Exception in should_process_pr_logic: {e}") get_logger().error(f"Failed 'should_process_pr_logic': {e}") print("DEBUG: Returning True from should_process_pr_logic") return True diff --git a/pr_agent/servers/github_app.py b/pr_agent/servers/github_app.py index 6354b2b983..36db3c6929 100644 --- a/pr_agent/servers/github_app.py +++ b/pr_agent/servers/github_app.py @@ -263,7 +263,7 @@ def should_process_pr_logic(body) -> bool: # logic to ignore PRs from specific repositories ignore_repos = get_settings().get("CONFIG.IGNORE_REPOSITORIES", []) if ignore_repos and repo_full_name: - if repo_full_name in ignore_repos: + if any(re.search(regex, repo_full_name) for regex in ignore_repos): get_logger().info(f"Ignoring PR from repository '{repo_full_name}' due to 'config.ignore_repositories' setting") return False diff --git a/pr_agent/servers/gitlab_webhook.py b/pr_agent/servers/gitlab_webhook.py index 7d1787026a..9036f15d4e 100644 --- a/pr_agent/servers/gitlab_webhook.py +++ b/pr_agent/servers/gitlab_webhook.py @@ -108,7 +108,7 @@ def should_process_pr_logic(data) -> bool: # logic to ignore PRs from specific repositories ignore_repos = get_settings().get("CONFIG.IGNORE_REPOSITORIES", []) if ignore_repos and repo_full_name: - if repo_full_name in ignore_repos: + if any(re.search(regex, repo_full_name) for regex in ignore_repos): get_logger().info(f"Ignoring MR from repository '{repo_full_name}' due to 'config.ignore_repositories' setting") return False diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index cd7ebd160c..ce097c9578 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -55,7 +55,7 @@ ignore_pr_target_branches = [] # a list of regular expressions of target branche ignore_pr_source_branches = [] # a list of regular expressions of source branches to ignore from PR agent when an PR is created ignore_pr_labels = [] # labels to ignore from PR agent when an PR is created ignore_pr_authors = [] # authors to ignore from PR agent when an PR is created -ignore_repositories = [] # list of repository full names (e.g. "org/repo") to ignore from PR agent processing +ignore_repositories = [] # a list of regular expressions of repository full names (e.g. "org/repo") to ignore from PR agent processing # is_auto_command = false # will be auto-set to true if the command is triggered by an automation enable_ai_metadata = false # will enable adding ai metadata From f47da75e6fe553b0ca429eeb79934a004fbeee17 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Fri, 16 May 2025 17:23:27 +0300 Subject: [PATCH 237/365] Remove debug print statement from should_process_pr_logic function --- pr_agent/servers/bitbucket_app.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pr_agent/servers/bitbucket_app.py b/pr_agent/servers/bitbucket_app.py index c55a335e29..0d75d14309 100644 --- a/pr_agent/servers/bitbucket_app.py +++ b/pr_agent/servers/bitbucket_app.py @@ -165,7 +165,6 @@ def should_process_pr_logic(data) -> bool: return False except Exception as e: get_logger().error(f"Failed 'should_process_pr_logic': {e}") - print("DEBUG: Returning True from should_process_pr_logic") return True From 52ce74a31a5f83960e7f67ab019a473d9951d9f1 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Fri, 16 May 2025 17:25:10 +0300 Subject: [PATCH 238/365] Remove debug print statements from repository filtering tests --- tests/unittest/test_ignore_repositories.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/unittest/test_ignore_repositories.py b/tests/unittest/test_ignore_repositories.py index b8b0200934..e05447d53f 100644 --- a/tests/unittest/test_ignore_repositories.py +++ b/tests/unittest/test_ignore_repositories.py @@ -51,7 +51,7 @@ def test_should_ignore_matching_repository(self, provider_name, provider_func, b "sender": {"login": "user"} } result = provider_func(body_func(body["repository"]["full_name"])) - print(f"DEBUG: Provider={provider_name}, test_should_ignore_matching_repository, result={result}") + # print(f"DEBUG: Provider={provider_name}, test_should_ignore_matching_repository, result={result}") assert result is False, f"{provider_name}: PR from ignored repository should be ignored (return False)" @pytest.mark.parametrize("provider_name, provider_func, body_func", PROVIDERS) @@ -63,7 +63,7 @@ def test_should_not_ignore_non_matching_repository(self, provider_name, provider "sender": {"login": "user"} } result = provider_func(body_func(body["repository"]["full_name"])) - print(f"DEBUG: Provider={provider_name}, test_should_not_ignore_non_matching_repository, result={result}") + # print(f"DEBUG: Provider={provider_name}, test_should_not_ignore_non_matching_repository, result={result}") assert result is True, f"{provider_name}: PR from non-ignored repository should not be ignored (return True)" @pytest.mark.parametrize("provider_name, provider_func, body_func", PROVIDERS) @@ -75,5 +75,5 @@ def test_should_not_ignore_when_config_empty(self, provider_name, provider_func, "sender": {"login": "user"} } result = provider_func(body_func(body["repository"]["full_name"])) - print(f"DEBUG: Provider={provider_name}, test_should_not_ignore_when_config_empty, result={result}") + # print(f"DEBUG: Provider={provider_name}, test_should_not_ignore_when_config_empty, result={result}") assert result is True, f"{provider_name}: PR should not be ignored if ignore_repositories config is empty" \ No newline at end of file From f857ea1f220b20ce6462d29acb8678278cc5597c Mon Sep 17 00:00:00 2001 From: Nikolay Gribanov Date: Fri, 16 May 2025 19:34:03 +0300 Subject: [PATCH 239/365] #1657 add gitea/forgejo support --- pr_agent/git_providers/__init__.py | 2 + pr_agent/git_providers/gitea_provider.py | 243 +++++++++++++++++++++++ tests/e2e_tests/test_gitea_app.py | 186 +++++++++++++++++ tests/unittest/test_gitea_provider.py | 126 ++++++++++++ 4 files changed, 557 insertions(+) create mode 100644 pr_agent/git_providers/gitea_provider.py create mode 100644 tests/e2e_tests/test_gitea_app.py create mode 100644 tests/unittest/test_gitea_provider.py diff --git a/pr_agent/git_providers/__init__.py b/pr_agent/git_providers/__init__.py index 16547d90c4..51c6f62427 100644 --- a/pr_agent/git_providers/__init__.py +++ b/pr_agent/git_providers/__init__.py @@ -11,6 +11,7 @@ from pr_agent.git_providers.github_provider import GithubProvider from pr_agent.git_providers.gitlab_provider import GitLabProvider from pr_agent.git_providers.local_git_provider import LocalGitProvider +from pr_agent.git_providers.gitea_provider import GiteaProvider _GIT_PROVIDERS = { 'github': GithubProvider, @@ -21,6 +22,7 @@ 'codecommit': CodeCommitProvider, 'local': LocalGitProvider, 'gerrit': GerritProvider, + 'gitea': GiteaProvider, } diff --git a/pr_agent/git_providers/gitea_provider.py b/pr_agent/git_providers/gitea_provider.py new file mode 100644 index 0000000000..8fd12ff4f2 --- /dev/null +++ b/pr_agent/git_providers/gitea_provider.py @@ -0,0 +1,243 @@ +from typing import Optional, Tuple, List, Dict +from urllib.parse import urlparse +import requests +from pr_agent.git_providers.git_provider import GitProvider +from pr_agent.config_loader import get_settings +from pr_agent.log import get_logger +from pr_agent.algo.types import EDIT_TYPE, FilePatchInfo + + +class GiteaProvider(GitProvider): + """ + Implements GitProvider for Gitea/Forgejo API v1. + """ + + def __init__(self, pr_url: Optional[str] = None, incremental: Optional[bool] = False): + self.gitea_url = get_settings().get("GITEA.URL", None) + self.gitea_token = get_settings().get("GITEA.TOKEN", None) + if not self.gitea_url: + raise ValueError("GITEA.URL is not set in the config file") + if not self.gitea_token: + raise ValueError("GITEA.TOKEN is not set in the config file") + self.headers = { + 'Authorization': f'token {self.gitea_token}', + 'Content-Type': 'application/json', + 'Accept': 'application/json' + } + self.owner = None + self.repo = None + self.pr_num = None + self.pr = None + self.pr_url = pr_url + self.incremental = incremental + if pr_url: + self.set_pr(pr_url) + + @staticmethod + def _parse_pr_url(pr_url: str) -> Tuple[str, str, str]: + """ + Parse Gitea PR URL to (owner, repo, pr_number) + """ + parsed_url = urlparse(pr_url) + path_parts = parsed_url.path.strip('/').split('/') + if len(path_parts) < 4 or path_parts[2] != 'pulls': + raise ValueError(f"Invalid PR URL format: {pr_url}") + return path_parts[0], path_parts[1], path_parts[3] + + def set_pr(self, pr_url: str): + self.owner, self.repo, self.pr_num = self._parse_pr_url(pr_url) + self.pr = self._get_pr() + + def _get_pr(self): + url = f"{self.gitea_url}/api/v1/repos/{self.owner}/{self.repo}/pulls/{self.pr_num}" + response = requests.get(url, headers=self.headers) + response.raise_for_status() + return response.json() + + def is_supported(self, capability: str) -> bool: + # Gitea/Forgejo supports most capabilities + return True + + def get_files(self) -> List[str]: + url = f"{self.gitea_url}/api/v1/repos/{self.owner}/{self.repo}/pulls/{self.pr_num}/files" + response = requests.get(url, headers=self.headers) + response.raise_for_status() + return [file['filename'] for file in response.json()] + + def get_diff_files(self) -> List[FilePatchInfo]: + url = f"{self.gitea_url}/api/v1/repos/{self.owner}/{self.repo}/pulls/{self.pr_num}/files" + response = requests.get(url, headers=self.headers) + response.raise_for_status() + + diff_files = [] + for file in response.json(): + edit_type = EDIT_TYPE.MODIFIED + if file.get('status') == 'added': + edit_type = EDIT_TYPE.ADDED + elif file.get('status') == 'deleted': + edit_type = EDIT_TYPE.DELETED + elif file.get('status') == 'renamed': + edit_type = EDIT_TYPE.RENAMED + + diff_files.append( + FilePatchInfo( + file.get('previous_filename', ''), + file.get('filename', ''), + file.get('patch', ''), + file['filename'], + edit_type=edit_type, + old_filename=file.get('previous_filename') + ) + ) + return diff_files + + def publish_description(self, pr_title: str, pr_body: str): + url = f"{self.gitea_url}/api/v1/repos/{self.owner}/{self.repo}/pulls/{self.pr_num}" + data = {'title': pr_title, 'body': pr_body} + response = requests.patch(url, headers=self.headers, json=data) + response.raise_for_status() + + def publish_comment(self, pr_comment: str, is_temporary: bool = False): + url = f"{self.gitea_url}/api/v1/repos/{self.owner}/{self.repo}/issues/{self.pr_num}/comments" + data = {'body': pr_comment} + response = requests.post(url, headers=self.headers, json=data) + response.raise_for_status() + + def publish_inline_comment(self, body: str, relevant_file: str, relevant_line_in_file: str, + original_suggestion=None): + url = f"{self.gitea_url}/api/v1/repos/{self.owner}/{self.repo}/pulls/{self.pr_num}/comments" + data = { + 'body': body, + 'path': relevant_file, + 'line': int(relevant_line_in_file) + } + response = requests.post(url, headers=self.headers, json=data) + response.raise_for_status() + + def publish_inline_comments(self, comments: list[dict]): + for comment in comments: + self.publish_inline_comment( + comment['body'], + comment['relevant_file'], + comment['relevant_line_in_file'], + comment.get('original_suggestion') + ) + + def publish_code_suggestions(self, code_suggestions: list) -> bool: + for suggestion in code_suggestions: + self.publish_inline_comment( + suggestion['body'], + suggestion['relevant_file'], + suggestion['relevant_line_in_file'], + suggestion.get('original_suggestion') + ) + return True + + def publish_labels(self, labels): + url = f"{self.gitea_url}/api/v1/repos/{self.owner}/{self.repo}/issues/{self.pr_num}/labels" + data = {'labels': labels} + response = requests.post(url, headers=self.headers, json=data) + response.raise_for_status() + + def get_pr_labels(self, update=False): + url = f"{self.gitea_url}/api/v1/repos/{self.owner}/{self.repo}/issues/{self.pr_num}/labels" + response = requests.get(url, headers=self.headers) + response.raise_for_status() + return [label['name'] for label in response.json()] + + def get_issue_comments(self): + url = f"{self.gitea_url}/api/v1/repos/{self.owner}/{self.repo}/issues/{self.pr_num}/comments" + response = requests.get(url, headers=self.headers) + response.raise_for_status() + return response.json() + + def remove_initial_comment(self): + # Implementation depends on how you track the initial comment + pass + + def remove_comment(self, comment): + url = f"{self.gitea_url}/api/v1/repos/{self.owner}/{self.repo}/issues/comments/{comment['id']}" + response = requests.delete(url, headers=self.headers) + response.raise_for_status() + + def add_eyes_reaction(self, issue_comment_id: int, disable_eyes: bool = False) -> Optional[int]: + if disable_eyes: + return None + url = f"{self.gitea_url}/api/v1/repos/{self.owner}/{self.repo}/issues/comments/{issue_comment_id}/reactions" + data = {'content': 'eyes'} + response = requests.post(url, headers=self.headers, json=data) + response.raise_for_status() + return response.json()['id'] + + def remove_reaction(self, issue_comment_id: int, reaction_id: int) -> bool: + url = f"{self.gitea_url}/api/v1/repos/{self.owner}/{self.repo}/issues/comments/{issue_comment_id}/reactions/{reaction_id}" + response = requests.delete(url, headers=self.headers) + return response.status_code == 204 + + def get_commit_messages(self): + url = f"{self.gitea_url}/api/v1/repos/{self.owner}/{self.repo}/pulls/{self.pr_num}/commits" + response = requests.get(url, headers=self.headers) + response.raise_for_status() + return [commit['commit']['message'] for commit in response.json()] + + def get_pr_branch(self): + return self.pr['head']['ref'] + + def get_user_id(self): + return self.pr['user']['id'] + + def get_pr_description_full(self) -> str: + return self.pr['body'] or '' + + def get_git_repo_url(self, issues_or_pr_url: str) -> str: + try: + parsed_url = urlparse(issues_or_pr_url) + path_parts = parsed_url.path.strip('/').split('/') + if len(path_parts) < 2: + raise ValueError(f"Invalid URL format: {issues_or_pr_url}") + return f"{parsed_url.scheme}://{parsed_url.netloc}/{path_parts[0]}/{path_parts[1]}.git" + except Exception as e: + get_logger().exception(f"Failed to get git repo URL from: {issues_or_pr_url}") + return "" + + def get_canonical_url_parts(self, repo_git_url: str, desired_branch: str) -> Tuple[str, str]: + try: + parsed_url = urlparse(repo_git_url) + path_parts = parsed_url.path.strip('/').split('/') + if len(path_parts) < 2: + raise ValueError(f"Invalid git repo URL format: {repo_git_url}") + + repo_name = path_parts[1] + if repo_name.endswith('.git'): + repo_name = repo_name[:-4] + + prefix = f"{parsed_url.scheme}://{parsed_url.netloc}/{path_parts[0]}/{repo_name}/src/branch/{desired_branch}" + suffix = "" + return prefix, suffix + except Exception as e: + get_logger().exception(f"Failed to get canonical URL parts from: {repo_git_url}") + return ("", "") + + def get_languages(self) -> Dict[str, float]: + """ + Get the languages used in the repository and their percentages. + Returns a dictionary mapping language names to their percentage of use. + """ + if not self.owner or not self.repo: + return {} + url = f"{self.gitea_url}/api/v1/repos/{self.owner}/{self.repo}/languages" + response = requests.get(url, headers=self.headers) + response.raise_for_status() + return response.json() + + def get_repo_settings(self) -> Dict: + """ + Get repository settings and configuration. + Returns a dictionary containing repository settings. + """ + if not self.owner or not self.repo: + return {} + url = f"{self.gitea_url}/api/v1/repos/{self.owner}/{self.repo}" + response = requests.get(url, headers=self.headers) + response.raise_for_status() + return response.json() diff --git a/tests/e2e_tests/test_gitea_app.py b/tests/e2e_tests/test_gitea_app.py new file mode 100644 index 0000000000..7114f527d8 --- /dev/null +++ b/tests/e2e_tests/test_gitea_app.py @@ -0,0 +1,186 @@ +import os +import time +import requests +from datetime import datetime + +from pr_agent.config_loader import get_settings +from pr_agent.log import get_logger, setup_logger +from tests.e2e_tests.e2e_utils import (FILE_PATH, + IMPROVE_START_WITH_REGEX_PATTERN, + NEW_FILE_CONTENT, NUM_MINUTES, + PR_HEADER_START_WITH, REVIEW_START_WITH) + +log_level = os.environ.get("LOG_LEVEL", "INFO") +setup_logger(log_level) +logger = get_logger() + +def test_e2e_run_gitea_app(): + repo_name = 'pr-agent-tests' + owner = 'codiumai' + base_branch = "main" + new_branch = f"gitea_app_e2e_test-{datetime.now().strftime('%Y-%m-%d-%H-%M-%S')}" + get_settings().config.git_provider = "gitea" + + headers = None + pr_number = None + + try: + gitea_url = get_settings().get("GITEA.URL", None) + gitea_token = get_settings().get("GITEA.TOKEN", None) + + if not gitea_url: + logger.error("GITEA.URL is not set in the configuration") + logger.info("Please set GITEA.URL in .env file or environment variables") + assert False, "GITEA.URL is not set in the configuration" + + if not gitea_token: + logger.error("GITEA.TOKEN is not set in the configuration") + logger.info("Please set GITEA.TOKEN in .env file or environment variables") + assert False, "GITEA.TOKEN is not set in the configuration" + + headers = { + 'Authorization': f'token {gitea_token}', + 'Content-Type': 'application/json', + 'Accept': 'application/json' + } + + logger.info(f"Creating a new branch {new_branch} from {base_branch}") + + response = requests.get( + f"{gitea_url}/api/v1/repos/{owner}/{repo_name}/branches/{base_branch}", + headers=headers + ) + response.raise_for_status() + base_branch_data = response.json() + base_commit_sha = base_branch_data['commit']['id'] + + branch_data = { + 'ref': f"refs/heads/{new_branch}", + 'sha': base_commit_sha + } + response = requests.post( + f"{gitea_url}/api/v1/repos/{owner}/{repo_name}/git/refs", + headers=headers, + json=branch_data + ) + response.raise_for_status() + + logger.info(f"Updating file {FILE_PATH} in branch {new_branch}") + + import base64 + file_content_encoded = base64.b64encode(NEW_FILE_CONTENT.encode()).decode() + + try: + response = requests.get( + f"{gitea_url}/api/v1/repos/{owner}/{repo_name}/contents/{FILE_PATH}?ref={new_branch}", + headers=headers + ) + response.raise_for_status() + existing_file = response.json() + file_sha = existing_file.get('sha') + + file_data = { + 'message': 'Update cli_pip.py', + 'content': file_content_encoded, + 'sha': file_sha, + 'branch': new_branch + } + except: + file_data = { + 'message': 'Add cli_pip.py', + 'content': file_content_encoded, + 'branch': new_branch + } + + response = requests.put( + f"{gitea_url}/api/v1/repos/{owner}/{repo_name}/contents/{FILE_PATH}", + headers=headers, + json=file_data + ) + response.raise_for_status() + + logger.info(f"Creating a pull request from {new_branch} to {base_branch}") + pr_data = { + 'title': f'Test PR from {new_branch}', + 'body': 'update cli_pip.py', + 'head': new_branch, + 'base': base_branch + } + response = requests.post( + f"{gitea_url}/api/v1/repos/{owner}/{repo_name}/pulls", + headers=headers, + json=pr_data + ) + response.raise_for_status() + pr = response.json() + pr_number = pr['number'] + + for i in range(NUM_MINUTES): + logger.info(f"Waiting for the PR to get all the tool results...") + time.sleep(60) + + response = requests.get( + f"{gitea_url}/api/v1/repos/{owner}/{repo_name}/issues/{pr_number}/comments", + headers=headers + ) + response.raise_for_status() + comments = response.json() + + if len(comments) >= 5: # заголовок, 3 предложения, 1 ревью + valid_review = False + for comment in comments: + if comment['body'].startswith('## PR Reviewer Guide 🔍'): + valid_review = True + break + if valid_review: + break + else: + logger.error("REVIEW feedback is invalid") + raise Exception("REVIEW feedback is invalid") + else: + logger.info(f"Waiting for the PR to get all the tool results. {i + 1} minute(s) passed") + else: + assert False, f"After {NUM_MINUTES} minutes, the PR did not get all the tool results" + + logger.info(f"Cleaning up: closing PR and deleting branch {new_branch}") + + close_data = {'state': 'closed'} + response = requests.patch( + f"{gitea_url}/api/v1/repos/{owner}/{repo_name}/pulls/{pr_number}", + headers=headers, + json=close_data + ) + response.raise_for_status() + + # Удаляем ветку + response = requests.delete( + f"{gitea_url}/api/v1/repos/{owner}/{repo_name}/git/refs/heads/{new_branch}", + headers=headers + ) + response.raise_for_status() + + logger.info(f"Succeeded in running e2e test for Gitea app on the PR") + except Exception as e: + logger.error(f"Failed to run e2e test for Gitea app: {e}") + raise + finally: + try: + if headers is None or gitea_url is None: + return + + if pr_number is not None: + requests.patch( + f"{gitea_url}/api/v1/repos/{owner}/{repo_name}/pulls/{pr_number}", + headers=headers, + json={'state': 'closed'} + ) + + requests.delete( + f"{gitea_url}/api/v1/repos/{owner}/{repo_name}/git/refs/heads/{new_branch}", + headers=headers + ) + except Exception as cleanup_error: + logger.error(f"Failed to clean up after test: {cleanup_error}") + +if __name__ == '__main__': + test_e2e_run_gitea_app() \ No newline at end of file diff --git a/tests/unittest/test_gitea_provider.py b/tests/unittest/test_gitea_provider.py new file mode 100644 index 0000000000..d88de0e041 --- /dev/null +++ b/tests/unittest/test_gitea_provider.py @@ -0,0 +1,126 @@ +from unittest.mock import MagicMock, patch + +import pytest + +from pr_agent.algo.types import EDIT_TYPE +from pr_agent.git_providers.gitea_provider import GiteaProvider + + +class TestGiteaProvider: + """Unit-tests for GiteaProvider following project style (explicit object construction, minimal patching).""" + + def _provider(self): + """Create provider instance with patched settings and avoid real HTTP calls.""" + with patch('pr_agent.git_providers.gitea_provider.get_settings') as mock_get_settings, \ + patch('requests.get') as mock_get: + settings = MagicMock() + settings.get.side_effect = lambda k, d=None: { + 'GITEA.URL': 'https://gitea.example.com', + 'GITEA.TOKEN': 'test-token' + }.get(k, d) + mock_get_settings.return_value = settings + # Stub the PR fetch triggered during provider initialization + pr_resp = MagicMock() + pr_resp.json.return_value = { + 'title': 'stub', + 'body': 'stub', + 'head': {'ref': 'main'}, + 'user': {'id': 1} + } + pr_resp.raise_for_status = MagicMock() + mock_get.return_value = pr_resp + return GiteaProvider('https://gitea.example.com/owner/repo/pulls/123') + + # ---------------- URL parsing ---------------- + def test_parse_pr_url_valid(self): + owner, repo, pr_num = GiteaProvider._parse_pr_url('https://gitea.example.com/owner/repo/pulls/123') + assert (owner, repo, pr_num) == ('owner', 'repo', '123') + + def test_parse_pr_url_invalid(self): + with pytest.raises(ValueError): + GiteaProvider._parse_pr_url('https://gitea.example.com/owner/repo') + + # ---------------- simple getters ---------------- + def test_get_files(self): + provider = self._provider() + mock_resp = MagicMock() + mock_resp.json.return_value = [{'filename': 'a.txt'}, {'filename': 'b.txt'}] + mock_resp.raise_for_status = MagicMock() + with patch('requests.get', return_value=mock_resp) as mock_get: + assert provider.get_files() == ['a.txt', 'b.txt'] + mock_get.assert_called_once() + + def test_get_diff_files(self): + provider = self._provider() + mock_resp = MagicMock() + mock_resp.json.return_value = [ + {'filename': 'f1', 'previous_filename': 'old_f1', 'status': 'renamed', 'patch': ''}, + {'filename': 'f2', 'status': 'added', 'patch': ''}, + {'filename': 'f3', 'status': 'deleted', 'patch': ''}, + {'filename': 'f4', 'status': 'modified', 'patch': ''} + ] + mock_resp.raise_for_status = MagicMock() + with patch('requests.get', return_value=mock_resp): + res = provider.get_diff_files() + assert [f.edit_type for f in res] == [EDIT_TYPE.RENAMED, EDIT_TYPE.ADDED, EDIT_TYPE.DELETED, + EDIT_TYPE.MODIFIED] + + # ---------------- publishing methods ---------------- + def test_publish_description(self): + provider = self._provider() + mock_resp = MagicMock(); + mock_resp.raise_for_status = MagicMock() + with patch('requests.patch', return_value=mock_resp) as mock_patch: + provider.publish_description('t', 'b'); + mock_patch.assert_called_once() + + def test_publish_comment(self): + provider = self._provider() + mock_resp = MagicMock(); + mock_resp.raise_for_status = MagicMock() + with patch('requests.post', return_value=mock_resp) as mock_post: + provider.publish_comment('c'); + mock_post.assert_called_once() + + def test_publish_inline_comment(self): + provider = self._provider() + mock_resp = MagicMock(); + mock_resp.raise_for_status = MagicMock() + with patch('requests.post', return_value=mock_resp) as mock_post: + provider.publish_inline_comment('body', 'file', '10'); + mock_post.assert_called_once() + + # ---------------- labels & reactions ---------------- + def test_get_pr_labels(self): + provider = self._provider() + mock_resp = MagicMock(); + mock_resp.raise_for_status = MagicMock(); + mock_resp.json.return_value = [{'name': 'l1'}] + with patch('requests.get', return_value=mock_resp): + assert provider.get_pr_labels() == ['l1'] + + def test_add_eyes_reaction(self): + provider = self._provider() + mock_resp = MagicMock(); + mock_resp.raise_for_status = MagicMock(); + mock_resp.json.return_value = {'id': 7} + with patch('requests.post', return_value=mock_resp): + assert provider.add_eyes_reaction(1) == 7 + + # ---------------- commit messages & url helpers ---------------- + def test_get_commit_messages(self): + provider = self._provider() + mock_resp = MagicMock(); + mock_resp.raise_for_status = MagicMock() + mock_resp.json.return_value = [ + {'commit': {'message': 'm1'}}, {'commit': {'message': 'm2'}}] + with patch('requests.get', return_value=mock_resp): + assert provider.get_commit_messages() == ['m1', 'm2'] + + def test_git_url_helpers(self): + provider = self._provider() + issues_url = 'https://gitea.example.com/owner/repo/pulls/3' + assert provider.get_git_repo_url(issues_url) == 'https://gitea.example.com/owner/repo.git' + prefix, suffix = provider.get_canonical_url_parts('https://gitea.example.com/owner/repo.git', 'dev') + assert prefix == 'https://gitea.example.com/owner/repo/src/branch/dev' + assert suffix == '' From 12b1fe23dadf7a7e3b024d4a4d96e611cbd6747b Mon Sep 17 00:00:00 2001 From: Nikolay Gribanov Date: Fri, 16 May 2025 20:12:40 +0300 Subject: [PATCH 240/365] #1657 review fix --- pr_agent/git_providers/gitea_provider.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/pr_agent/git_providers/gitea_provider.py b/pr_agent/git_providers/gitea_provider.py index 8fd12ff4f2..b6983f818f 100644 --- a/pr_agent/git_providers/gitea_provider.py +++ b/pr_agent/git_providers/gitea_provider.py @@ -105,11 +105,17 @@ def publish_comment(self, pr_comment: str, is_temporary: bool = False): def publish_inline_comment(self, body: str, relevant_file: str, relevant_line_in_file: str, original_suggestion=None): - url = f"{self.gitea_url}/api/v1/repos/{self.owner}/{self.repo}/pulls/{self.pr_num}/comments" + url = f"{self.gitea_url}/api/v1/repos/{self.owner}/{self.repo}/pulls/{self.pr_num}/reviews" + data = { - 'body': body, - 'path': relevant_file, - 'line': int(relevant_line_in_file) + 'event': 'COMMENT', + 'body': original_suggestion or '', + 'commit_id': self.pr.get('head', {}).get('sha', ''), + 'comments': [{ + 'body': body, + 'path': relevant_file, + 'line': int(relevant_line_in_file) + }] } response = requests.post(url, headers=self.headers, json=data) response.raise_for_status() From d67d07acc7314bf94bd506710e8bfee3b4aeb3ab Mon Sep 17 00:00:00 2001 From: Nikolay Gribanov Date: Fri, 16 May 2025 20:27:01 +0300 Subject: [PATCH 241/365] #1657 review fix 2 --- pr_agent/git_providers/gitea_provider.py | 35 +++++++++++++++--------- tests/e2e_tests/test_gitea_app.py | 3 +- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/pr_agent/git_providers/gitea_provider.py b/pr_agent/git_providers/gitea_provider.py index b6983f818f..1d6715583b 100644 --- a/pr_agent/git_providers/gitea_provider.py +++ b/pr_agent/git_providers/gitea_provider.py @@ -122,22 +122,31 @@ def publish_inline_comment(self, body: str, relevant_file: str, relevant_line_in def publish_inline_comments(self, comments: list[dict]): for comment in comments: - self.publish_inline_comment( - comment['body'], - comment['relevant_file'], - comment['relevant_line_in_file'], - comment.get('original_suggestion') - ) + try: + self.publish_inline_comment( + comment['body'], + comment['relevant_file'], + comment['relevant_line_in_file'], + comment.get('original_suggestion') + ) + except Exception as e: + get_logger().error(f"Failed to publish inline comment on {comment.get('relevant_file')}: {e}") def publish_code_suggestions(self, code_suggestions: list) -> bool: + overall_success = True for suggestion in code_suggestions: - self.publish_inline_comment( - suggestion['body'], - suggestion['relevant_file'], - suggestion['relevant_line_in_file'], - suggestion.get('original_suggestion') - ) - return True + try: + self.publish_inline_comment( + suggestion['body'], + suggestion['relevant_file'], + suggestion['relevant_line_in_file'], + suggestion.get('original_suggestion') + ) + except Exception as e: + overall_success = False + get_logger().error( + f"Failed to publish code suggestion on {suggestion.get('relevant_file')}: {e}") + return overall_success def publish_labels(self, labels): url = f"{self.gitea_url}/api/v1/repos/{self.owner}/{self.repo}/issues/{self.pr_num}/labels" diff --git a/tests/e2e_tests/test_gitea_app.py b/tests/e2e_tests/test_gitea_app.py index 7114f527d8..3a20997560 100644 --- a/tests/e2e_tests/test_gitea_app.py +++ b/tests/e2e_tests/test_gitea_app.py @@ -126,7 +126,7 @@ def test_e2e_run_gitea_app(): response.raise_for_status() comments = response.json() - if len(comments) >= 5: # заголовок, 3 предложения, 1 ревью + if len(comments) >= 5: valid_review = False for comment in comments: if comment['body'].startswith('## PR Reviewer Guide 🔍'): @@ -152,7 +152,6 @@ def test_e2e_run_gitea_app(): ) response.raise_for_status() - # Удаляем ветку response = requests.delete( f"{gitea_url}/api/v1/repos/{owner}/{repo_name}/git/refs/heads/{new_branch}", headers=headers From db5138dc428575c0c2245f2f04b58921b2ecc825 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sat, 17 May 2025 20:38:05 +0300 Subject: [PATCH 242/365] Improve YAML parsing with additional fallback strategies for AI predictions --- pr_agent/algo/utils.py | 69 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 61 insertions(+), 8 deletions(-) diff --git a/pr_agent/algo/utils.py b/pr_agent/algo/utils.py index 4a3413866f..780c7953bf 100644 --- a/pr_agent/algo/utils.py +++ b/pr_agent/algo/utils.py @@ -731,8 +731,9 @@ def try_fix_yaml(response_text: str, response_text_original="") -> dict: response_text_lines = response_text.split('\n') - keys_yaml = ['relevant line:', 'suggestion content:', 'relevant file:', 'existing code:', 'improved code:'] + keys_yaml = ['relevant line:', 'suggestion content:', 'relevant file:', 'existing code:', 'improved code:', 'label:'] keys_yaml = keys_yaml + keys_fix_yaml + # first fallback - try to convert 'relevant line: ...' to relevant line: |-\n ...' response_text_lines_copy = response_text_lines.copy() for i in range(0, len(response_text_lines_copy)): @@ -747,8 +748,29 @@ def try_fix_yaml(response_text: str, except: pass - # second fallback - try to extract only range from first ```yaml to ```` - snippet_pattern = r'```(yaml)?[\s\S]*?```' + # 1.5 fallback - try to convert '|' to '|2'. Will solve cases of indent decreasing during the code + response_text_copy = copy.deepcopy(response_text) + response_text_copy = response_text_copy.replace('|\n', '|2\n') + try: + data = yaml.safe_load(response_text_copy) + get_logger().info(f"Successfully parsed AI prediction after replacing | with |2") + return data + except: + # if it fails, we can try to add spaces to the lines that are not indented properly, and contain '}'. + response_text_lines_copy = response_text_copy.split('\n') + for i in range(0, len(response_text_lines_copy)): + initial_space = len(response_text_lines_copy[i]) - len(response_text_lines_copy[i].lstrip()) + if initial_space == 2 and '|2' not in response_text_lines_copy[i] and '}' in response_text_lines_copy[i]: + response_text_lines_copy[i] = ' ' + response_text_lines_copy[i].lstrip() + try: + data = yaml.safe_load('\n'.join(response_text_lines_copy)) + get_logger().info(f"Successfully parsed AI prediction after replacing | with |2 and adding spaces") + return data + except: + pass + + # second fallback - try to extract only range from first ```yaml to the last ``` + snippet_pattern = r'```yaml([\s\S]*?)```(?=\s*$|")' snippet = re.search(snippet_pattern, '\n'.join(response_text_lines_copy)) if not snippet: snippet = re.search(snippet_pattern, response_text_original) # before we removed the "```" @@ -803,16 +825,47 @@ def try_fix_yaml(response_text: str, except: pass - # sixth fallback - try to remove last lines - for i in range(1, len(response_text_lines)): - response_text_lines_tmp = '\n'.join(response_text_lines[:-i]) + # sixth fallback - replace tabs with spaces + if '\t' in response_text: + response_text_copy = copy.deepcopy(response_text) + response_text_copy = response_text_copy.replace('\t', ' ') try: - data = yaml.safe_load(response_text_lines_tmp) - get_logger().info(f"Successfully parsed AI prediction after removing {i} lines") + data = yaml.safe_load(response_text_copy) + get_logger().info(f"Successfully parsed AI prediction after replacing tabs with spaces") return data except: pass + # seventh fallback - add indent for sections of code blocks + response_text_copy = copy.deepcopy(response_text) + response_text_copy_lines = response_text_copy.split('\n') + start_line = -1 + for i, line in enumerate(response_text_copy_lines): + if 'existing_code:' in line or 'improved_code:' in line: + start_line = i + elif line.endswith(': |') or line.endswith(': |-') or line.endswith(': |2') or line.endswith(':'): + start_line = -1 + elif start_line != -1: + response_text_copy_lines[i] = ' ' + line + response_text_copy = '\n'.join(response_text_copy_lines) + try: + data = yaml.safe_load(response_text_copy) + get_logger().info(f"Successfully parsed AI prediction after adding indent for sections of code blocks") + return data + except: + pass + + # # sixth fallback - try to remove last lines + # for i in range(1, len(response_text_lines)): + # response_text_lines_tmp = '\n'.join(response_text_lines[:-i]) + # try: + # data = yaml.safe_load(response_text_lines_tmp) + # get_logger().info(f"Successfully parsed AI prediction after removing {i} lines") + # return data + # except: + # pass + + def set_custom_labels(variables, git_provider=None): if not get_settings().config.enable_custom_labels: From b4ae07bf82de97c11ba8e0502ce98e29e8101af3 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sat, 17 May 2025 20:58:32 +0300 Subject: [PATCH 243/365] Remove test case for YAML parsing that removes last line --- tests/unittest/test_try_fix_yaml.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/unittest/test_try_fix_yaml.py b/tests/unittest/test_try_fix_yaml.py index 336c4af5ea..826d731237 100644 --- a/tests/unittest/test_try_fix_yaml.py +++ b/tests/unittest/test_try_fix_yaml.py @@ -32,11 +32,6 @@ def test_extract_snippet(self): expected_output = {'name': 'John Smith', 'age': 35} assert try_fix_yaml(review_text) == expected_output - # The function removes the last line(s) of the YAML string and successfully parses the YAML string. - def test_remove_last_line(self): - review_text = "key: value\nextra invalid line\n" - expected_output = {"key": "value"} - assert try_fix_yaml(review_text) == expected_output # The YAML string is empty. def test_empty_yaml_fixed(self): From 14fb98aa779cbf8d2a456754143adf68fc4a4d1e Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sat, 17 May 2025 21:03:29 +0300 Subject: [PATCH 244/365] docs: update README and recent updates page with v0.29 release information --- README.md | 33 ++++++------------------------- docs/docs/recent_updates/index.md | 2 +- 2 files changed, 7 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index c18bfa7ca7..104395c743 100644 --- a/README.md +++ b/README.md @@ -27,17 +27,6 @@ PR-Agent aims to help efficiently review and handle pull requests, by providing
-[//]: # (### [Documentation](https://qodo-merge-docs.qodo.ai/)) - -[//]: # () -[//]: # (- See the [Installation Guide](https://qodo-merge-docs.qodo.ai/installation/) for instructions on installing PR-Agent on different platforms.) - -[//]: # () -[//]: # (- See the [Usage Guide](https://qodo-merge-docs.qodo.ai/usage-guide/) for instructions on running PR-Agent tools via different interfaces, such as CLI, PR Comments, or by automatically triggering them when a new PR is opened.) - -[//]: # () -[//]: # (- See the [Tools Guide](https://qodo-merge-docs.qodo.ai/tools/) for a detailed description of the different tools, and the available configurations for each tool.) - ## Table of Contents - [News and Updates](#news-and-updates) @@ -53,6 +42,12 @@ PR-Agent aims to help efficiently review and handle pull requests, by providing ## News and Updates +## May 17, 2025 + +- v0.29 was [released](https://github.com/qodo-ai/pr-agent/releases) +- Qodo Merge Pull Request Benchmark was [released](https://qodo-merge-docs.qodo.ai/pr_benchmark/), which evaluates and compares the performance of Large Language Models (LLMs) in analyzing pull request code and providing meaningful code suggestions +- 'Recent Updates and Future Roadmap' Page was added to the [Qodo Merge Docs](https://qodo-merge-docs.qodo.ai/recent_updates/) + ## Apr 30, 2025 A new feature is now available in the `/improve` tool for Qodo Merge 💎 - Chat on code suggestions. @@ -69,22 +64,6 @@ New tool for Qodo Merge 💎 - `/scan_repo_discussions`. Read more about it [here](https://qodo-merge-docs.qodo.ai/tools/scan_repo_discussions/). -## Apr 14, 2025 - -GPT-4.1 is out. And it's quite good on coding tasks... - -https://openai.com/index/gpt-4-1/ - -image - -## March 28, 2025 - -A new version, v0.28, was released. See release notes [here](https://github.com/qodo-ai/pr-agent/releases/tag/v0.28). - -This version includes a new tool, [Help Docs](https://qodo-merge-docs.qodo.ai/tools/help_docs/), which can answer free-text questions based on a documentation folder. - -`/help_docs` is now being used to provide immediate automatic feedback to any user who [opens an issue](https://github.com/qodo-ai/pr-agent/issues/1608#issue-2897328825) on PR-Agent's open-source project - ## Overview
diff --git a/docs/docs/recent_updates/index.md b/docs/docs/recent_updates/index.md index dc7ea8d1b8..158fb5ca97 100644 --- a/docs/docs/recent_updates/index.md +++ b/docs/docs/recent_updates/index.md @@ -7,11 +7,11 @@ This page summarizes recent enhancements to Qodo Merge (last three months). It also outlines our development roadmap for the upcoming three months. Please note that the roadmap is subject to change, and features may be adjusted, added, or reprioritized. === "Recent Updates" + - **Qodo Merge Pull Request Benchmark** - evaluating the performance of LLMs in analyzing pull request code ([Learn more](https://qodo-merge-docs.qodo.ai/pr_benchmark/)) - **Chat on Suggestions**: Users can now chat with Qodo Merge code suggestions ([Learn more](https://qodo-merge-docs.qodo.ai/tools/improve/#chat-on-code-suggestions)) - **Scan Repo Discussions Tool**: A new tool that analyzes past code discussions to generate a `best_practices.md` file, distilling key insights and recommendations. ([Learn more](https://qodo-merge-docs.qodo.ai/tools/scan_repo_discussions/)) - **Enhanced Models**: Qodo Merge now defaults to a combination of top models (Claude Sonnet 3.7 and Gemini 2.5 Pro) and incorporates dedicated code validation logic for improved results. ([Details 1](https://qodo-merge-docs.qodo.ai/usage-guide/qodo_merge_models/), [Details 2](https://qodo-merge-docs.qodo.ai/core-abilities/code_validation/)) - **Chrome Extension Update**: Qodo Merge Chrome extension now supports single-tenant users. ([Learn more](https://qodo-merge-docs.qodo.ai/chrome-extension/options/#configuration-options/)) - - **Help Docs Tool**: The help_docs tool can answer free-text questions based on any git documentation folder. ([Learn more](https://qodo-merge-docs.qodo.ai/tools/help_docs/)) === "Future Roadmap" - **Smart Update**: Upon PR updates, Qodo Merge will offer tailored code suggestions, addressing both the entire PR and the specific incremental changes since the last feedback. From 411245155f860e4d647b04bebd24aa6eb2b9ced7 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sat, 17 May 2025 21:05:22 +0300 Subject: [PATCH 245/365] docs: update README and Dockerfile with v0.29 release information --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 104395c743..21c2309a71 100644 --- a/README.md +++ b/README.md @@ -45,8 +45,8 @@ PR-Agent aims to help efficiently review and handle pull requests, by providing ## May 17, 2025 - v0.29 was [released](https://github.com/qodo-ai/pr-agent/releases) -- Qodo Merge Pull Request Benchmark was [released](https://qodo-merge-docs.qodo.ai/pr_benchmark/), which evaluates and compares the performance of Large Language Models (LLMs) in analyzing pull request code and providing meaningful code suggestions -- 'Recent Updates and Future Roadmap' Page was added to the [Qodo Merge Docs](https://qodo-merge-docs.qodo.ai/recent_updates/) +- `Qodo Merge Pull Request Benchmark` was [released](https://qodo-merge-docs.qodo.ai/pr_benchmark/). This benchmark evaluates and compares the performance of LLMs in analyzing pull request code. +- `Recent Updates and Future Roadmap` page was added to the [Qodo Merge Docs](https://qodo-merge-docs.qodo.ai/recent_updates/) ## Apr 30, 2025 From 74b4488c7ea74bbce417dc855b82724edf4bb05e Mon Sep 17 00:00:00 2001 From: wonjongin Date: Mon, 19 May 2025 16:44:22 +0900 Subject: [PATCH 246/365] fix/wonjongin-test_language_handler - test_edge_case_languages_with_no_extensions code modification - Since the existing code did not meet the requirements of the test, test is conducted on the assumption of a separate file name that does not exist in the extension --- tests/unittest/test_language_handler.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/unittest/test_language_handler.py b/tests/unittest/test_language_handler.py index f76a15ea33..8895a28667 100644 --- a/tests/unittest/test_language_handler.py +++ b/tests/unittest/test_language_handler.py @@ -79,13 +79,14 @@ def test_edge_case_languages_with_no_extensions(self): files = [ type('', (object,), {'filename': 'file1.py'})(), type('', (object,), {'filename': 'file2.java'})(), - type('', (object,), {'filename': 'file3.cpp'})() + type('', (object,), {'filename': 'file3.cpp'})(), + type('', (object,), {'filename': 'file3.test'})() ] expected_output = [ {'language': 'Python', 'files': [files[0]]}, {'language': 'Java', 'files': [files[1]]}, {'language': 'C++', 'files': [files[2]]}, - {'language': 'Other', 'files': []} + {'language': 'Other', 'files': [files[3]]} ] assert sort_files_by_main_languages(languages, files) == expected_output From 2dbcb3e5dc91f4d2841f036e41d06b7d75ddabdd Mon Sep 17 00:00:00 2001 From: mrT23 Date: Mon, 19 May 2025 11:01:14 +0300 Subject: [PATCH 247/365] docs: improve configuration options documentation with better formatting and links --- docs/docs/usage-guide/configuration_options.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/docs/usage-guide/configuration_options.md b/docs/docs/usage-guide/configuration_options.md index fba4bb480b..ab499dca3a 100644 --- a/docs/docs/usage-guide/configuration_options.md +++ b/docs/docs/usage-guide/configuration_options.md @@ -1,13 +1,13 @@ -The different tools and sub-tools used by Qodo Merge are adjustable via the **[configuration file](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml)**. +The different tools and sub-tools used by Qodo Merge are adjustable via a configuration file. In addition to general configuration options, each tool has its own configurations. For example, the `review` tool will use parameters from the [pr_reviewer](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml#L16) section in the configuration file. See the [Tools Guide](https://qodo-merge-docs.qodo.ai/tools/) for a detailed description of the different tools and their configurations. -There are three ways to set persistent configurations: +There are three main ways to set persistent configurations: -1. Wiki configuration page 💎 -2. Local configuration file -3. Global configuration file 💎 +1. [Wiki](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/#wiki-configuration-file)configuration page 💎 +2. [Local](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/#local-configuration-file) configuration file +3. [Global](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/#global-configuration-file) configuration file 💎 In terms of precedence, wiki configurations will override local configurations, and local configurations will override global configurations. From 31620a82c04b0b40ea55b398e6ad5bf83f18401c Mon Sep 17 00:00:00 2001 From: jaexxin Date: Mon, 19 May 2025 17:34:48 +0900 Subject: [PATCH 248/365] docs: document how to auto-trigger /add_docs via pr_commands --- docs/docs/tools/documentation.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/docs/tools/documentation.md b/docs/docs/tools/documentation.md index 247d5d6dd1..293cabb028 100644 --- a/docs/docs/tools/documentation.md +++ b/docs/docs/tools/documentation.md @@ -26,6 +26,25 @@ You can state a name of a specific component in the PR to get documentation only /add_docs component_name ``` +## Automatic triggering + +To automatically run the `add_docs` tool when a pull request is opened, configure your `.pr_agent.yaml` or `configuration.toml` as follows: + +```toml +[github_app] +pr_commands = [ + "/describe", + "/review", + "/improve", + "/add_docs" +] +``` + +!!! note + This behavior is **opt-in**; by default `/add_docs` only runs on manual invocation. + + + ## Configuration options - `docs_style`: The exact style of the documentation (for python docstring). you can choose between: `google`, `numpy`, `sphinx`, `restructuredtext`, `plain`. Default is `sphinx`. From e692dee66addca469bb0a721cf02e9ea325f7e06 Mon Sep 17 00:00:00 2001 From: jaexxin Date: Mon, 19 May 2025 17:39:55 +0900 Subject: [PATCH 249/365] docs: document how to auto-trigger /add_docs via pr_commands --- docs/docs/tools/documentation.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/docs/tools/documentation.md b/docs/docs/tools/documentation.md index 293cabb028..307c813101 100644 --- a/docs/docs/tools/documentation.md +++ b/docs/docs/tools/documentation.md @@ -43,8 +43,6 @@ pr_commands = [ !!! note This behavior is **opt-in**; by default `/add_docs` only runs on manual invocation. - - ## Configuration options - `docs_style`: The exact style of the documentation (for python docstring). you can choose between: `google`, `numpy`, `sphinx`, `restructuredtext`, `plain`. Default is `sphinx`. From 3f4fac1232697ab96ee3c0c9cb630c6a86eb67cb Mon Sep 17 00:00:00 2001 From: jaexxin Date: Mon, 19 May 2025 21:17:59 +0900 Subject: [PATCH 250/365] docs: document how to auto-trigger /add_docs via pr_commands --- docs/docs/tools/documentation.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/docs/tools/documentation.md b/docs/docs/tools/documentation.md index 307c813101..ff6a099c97 100644 --- a/docs/docs/tools/documentation.md +++ b/docs/docs/tools/documentation.md @@ -26,6 +26,10 @@ You can state a name of a specific component in the PR to get documentation only /add_docs component_name ``` +## Manual triggering + +Comment `/add_docs` on a PR to invoke it manually. + ## Automatic triggering To automatically run the `add_docs` tool when a pull request is opened, configure your `.pr_agent.yaml` or `configuration.toml` as follows: @@ -33,15 +37,16 @@ To automatically run the `add_docs` tool when a pull request is opened, configur ```toml [github_app] pr_commands = [ - "/describe", - "/review", - "/improve", - "/add_docs" + "/add_docs", + ... ] ``` +The `pr_commands` list defines commands that run automatically when a PR is opened. +This applies only if the Qodo Merge GitHub App is installed and active for the repository. + !!! note - This behavior is **opt-in**; by default `/add_docs` only runs on manual invocation. +By default, /add_docs is not triggered automatically. You must explicitly include it in pr_commands to enable this behavior. ## Configuration options From 5e40b3962a6dc926c1e5bb49d04ebdc18e70c406 Mon Sep 17 00:00:00 2001 From: jaexxin Date: Mon, 19 May 2025 22:22:22 +0900 Subject: [PATCH 251/365] docs: document how to auto-trigger /add_docs via pr_commands --- docs/docs/tools/documentation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/tools/documentation.md b/docs/docs/tools/documentation.md index ff6a099c97..63dac8c369 100644 --- a/docs/docs/tools/documentation.md +++ b/docs/docs/tools/documentation.md @@ -43,7 +43,7 @@ pr_commands = [ ``` The `pr_commands` list defines commands that run automatically when a PR is opened. -This applies only if the Qodo Merge GitHub App is installed and active for the repository. +Since this is under the [github_app] section, it only applies when using the Qodo Merge GitHub App in GitHub environments. !!! note By default, /add_docs is not triggered automatically. You must explicitly include it in pr_commands to enable this behavior. From 5a8ce252f7397d4cfa5d13533e11fdbee616dcf5 Mon Sep 17 00:00:00 2001 From: Guber Tamas Date: Mon, 19 May 2025 16:16:23 +0200 Subject: [PATCH 252/365] Adding num_max_findings configuration parameter with a default value of 3, to have the possibility to extend it --- docs/docs/tools/review.md | 4 ++++ pr_agent/settings/configuration.toml | 1 + pr_agent/settings/pr_reviewer_prompts.toml | 2 +- pr_agent/tools/pr_reviewer.py | 1 + 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/docs/tools/review.md b/docs/docs/tools/review.md index e5cc6b93ea..3245418eb1 100644 --- a/docs/docs/tools/review.md +++ b/docs/docs/tools/review.md @@ -70,6 +70,10 @@ extra_instructions = "..." enable_help_text If set to true, the tool will display a help text in the comment. Default is true. + + num_max_findings + Number of maximum returned finding. Default is 3. + !!! example "Enable\\disable specific sub-sections" diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index ce097c9578..c64379312a 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -81,6 +81,7 @@ require_ticket_analysis_review=true # general options persistent_comment=true extra_instructions = "" +num_max_findings = 3 final_update_message = true # review labels enable_review_labels_security=true diff --git a/pr_agent/settings/pr_reviewer_prompts.toml b/pr_agent/settings/pr_reviewer_prompts.toml index a322b2b918..d4c0a52320 100644 --- a/pr_agent/settings/pr_reviewer_prompts.toml +++ b/pr_agent/settings/pr_reviewer_prompts.toml @@ -98,7 +98,7 @@ class Review(BaseModel): {%- if question_str %} insights_from_user_answers: str = Field(description="shortly summarize the insights you gained from the user's answers to the questions") {%- endif %} - key_issues_to_review: List[KeyIssuesComponentLink] = Field("A short and diverse list (0-3 issues) of high-priority bugs, problems or performance concerns introduced in the PR code, which the PR reviewer should further focus on and validate during the review process.") + key_issues_to_review: List[KeyIssuesComponentLink] = Field("A short and diverse list (0-{{ num_max_findings }} issues) of high-priority bugs, problems or performance concerns introduced in the PR code, which the PR reviewer should further focus on and validate during the review process.") {%- if require_security_review %} 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' (without explaining why) 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 %} diff --git a/pr_agent/tools/pr_reviewer.py b/pr_agent/tools/pr_reviewer.py index ff48819f08..39780e4ccf 100644 --- a/pr_agent/tools/pr_reviewer.py +++ b/pr_agent/tools/pr_reviewer.py @@ -81,6 +81,7 @@ def __init__(self, pr_url: str, is_answer: bool = False, is_auto: bool = False, "language": self.main_language, "diff": "", # empty diff for initial calculation "num_pr_files": self.git_provider.get_num_of_files(), + "num_max_findings": get_settings().pr_reviewer.num_max_findings, "require_score": get_settings().pr_reviewer.require_score_review, "require_tests": get_settings().pr_reviewer.require_tests_review, "require_estimate_effort_to_review": get_settings().pr_reviewer.require_estimate_effort_to_review, From c97b49c373a3f9c297f43484a1ae67f2921dc69b Mon Sep 17 00:00:00 2001 From: Guber Tamas Date: Mon, 19 May 2025 16:19:41 +0200 Subject: [PATCH 253/365] typo fix --- docs/docs/tools/review.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/tools/review.md b/docs/docs/tools/review.md index 3245418eb1..d330947cdb 100644 --- a/docs/docs/tools/review.md +++ b/docs/docs/tools/review.md @@ -72,7 +72,7 @@ extra_instructions = "..." num_max_findings - Number of maximum returned finding. Default is 3. + Number of maximum returned findings. Default is 3. From a315779713f9e26ab43e1f9d595112e214e8c9b2 Mon Sep 17 00:00:00 2001 From: jaexxin Date: Tue, 20 May 2025 00:23:44 +0900 Subject: [PATCH 254/365] docs: document how to auto-trigger /add_docs via pr_commands --- docs/docs/tools/documentation.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/docs/tools/documentation.md b/docs/docs/tools/documentation.md index 63dac8c369..47222f510a 100644 --- a/docs/docs/tools/documentation.md +++ b/docs/docs/tools/documentation.md @@ -32,7 +32,8 @@ Comment `/add_docs` on a PR to invoke it manually. ## Automatic triggering -To automatically run the `add_docs` tool when a pull request is opened, configure your `.pr_agent.yaml` or `configuration.toml` as follows: +To automatically run the `add_docs` tool when a pull request is opened, define in a [configuration file](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/): + ```toml [github_app] From 95df26c97390d4296c443e79c406bbba7504fee1 Mon Sep 17 00:00:00 2001 From: Seohyun Lee Date: Tue, 20 May 2025 02:08:41 +0900 Subject: [PATCH 255/365] docs: Correct typos in ignore section configuration --- docs/docs/usage-guide/additional_configurations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/usage-guide/additional_configurations.md b/docs/docs/usage-guide/additional_configurations.md index eb46bbaa1e..9f9202f6a5 100644 --- a/docs/docs/usage-guide/additional_configurations.md +++ b/docs/docs/usage-guide/additional_configurations.md @@ -50,7 +50,7 @@ glob = ['*.py'] And to ignore Python files in all PRs using `regex` pattern, set in a configuration file: ``` -[regex] +[ignore] regex = ['.*\.py$'] ``` From 2a42d009afcfd60c33f3533314bc383dcb86b7a2 Mon Sep 17 00:00:00 2001 From: Seohyun Lee Date: Tue, 20 May 2025 02:27:14 +0900 Subject: [PATCH 256/365] docs: Fix broken Markdown code block in Installation documentation --- docs/docs/installation/github.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/docs/installation/github.md b/docs/docs/installation/github.md index 018499ee98..3eeace4fa1 100644 --- a/docs/docs/installation/github.md +++ b/docs/docs/installation/github.md @@ -193,9 +193,8 @@ For example: `GITHUB.WEBHOOK_SECRET` --> `GITHUB__WEBHOOK_SECRET` 3. Push image to ECR ```shell - - docker tag codiumai/pr-agent:serverless .dkr.ecr..amazonaws.com/codiumai/pr-agent:serverless - docker push .dkr.ecr..amazonaws.com/codiumai/pr-agent:serverless + docker tag codiumai/pr-agent:serverless .dkr.ecr..amazonaws.com/codiumai/pr-agent:serverless + docker push .dkr.ecr..amazonaws.com/codiumai/pr-agent:serverless ``` 4. Create a lambda function that uses the uploaded image. Set the lambda timeout to be at least 3m. From f5a069d6b4a41e77ceead860d3d151ab476dd648 Mon Sep 17 00:00:00 2001 From: Seohyun Lee Date: Tue, 20 May 2025 02:28:43 +0900 Subject: [PATCH 257/365] refactor: Align comment formatting with others --- pr_agent/tools/pr_reviewer.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pr_agent/tools/pr_reviewer.py b/pr_agent/tools/pr_reviewer.py index ff48819f08..ac57d85da0 100644 --- a/pr_agent/tools/pr_reviewer.py +++ b/pr_agent/tools/pr_reviewer.py @@ -316,7 +316,9 @@ def _remove_previous_review_comment(self, comment): get_logger().exception(f"Failed to remove previous review comment, error: {e}") def _can_run_incremental_review(self) -> bool: - """Checks if we can run incremental review according the various configurations and previous review""" + """ + Checks if we can run incremental review according the various configurations and previous review. + """ # checking if running is auto mode but there are no new commits if self.is_auto and not self.incremental.first_new_commit_sha: get_logger().info(f"Incremental review is enabled for {self.pr_url} but there are no new commits") From 94aa8e8638eae3b181ae0db0d991942cbbc2bdf8 Mon Sep 17 00:00:00 2001 From: Seohyun Lee Date: Tue, 20 May 2025 02:30:17 +0900 Subject: [PATCH 258/365] refactor: Correct case typo in log --- pr_agent/tools/pr_description.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pr_agent/tools/pr_description.py b/pr_agent/tools/pr_description.py index 6ab13ee159..df82db6727 100644 --- a/pr_agent/tools/pr_description.py +++ b/pr_agent/tools/pr_description.py @@ -199,7 +199,7 @@ async def run(self): async def _prepare_prediction(self, model: str) -> None: if get_settings().pr_description.use_description_markers and 'pr_agent:' not in self.user_description: - get_logger().info("Markers were enabled, but user description does not contain markers. skipping AI prediction") + get_logger().info("Markers were enabled, but user description does not contain markers. Skipping AI prediction") return None large_pr_handling = get_settings().pr_description.enable_large_pr_handling and "pr_description_only_files_prompts" in get_settings() @@ -707,7 +707,7 @@ def process_pr_files_prediction(self, pr_body, value): pr_body += """""" except Exception as e: - get_logger().error(f"Error processing pr files to markdown {self.pr_id}: {str(e)}") + get_logger().error(f"Error processing PR files to markdown {self.pr_id}: {str(e)}") pass return pr_body, pr_comments From 4679dce3afb49753d0c78f74b2373ccd25bf4787 Mon Sep 17 00:00:00 2001 From: Seohyun Lee Date: Tue, 20 May 2025 02:31:48 +0900 Subject: [PATCH 259/365] refactor: Correct case typo of PR in log --- pr_agent/git_providers/azuredevops_provider.py | 2 +- pr_agent/git_providers/github_provider.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pr_agent/git_providers/azuredevops_provider.py b/pr_agent/git_providers/azuredevops_provider.py index 35165bdd55..d71a029f37 100644 --- a/pr_agent/git_providers/azuredevops_provider.py +++ b/pr_agent/git_providers/azuredevops_provider.py @@ -618,7 +618,7 @@ def get_pr_id(self): return pr_id except Exception as e: if get_settings().config.verbosity_level >= 2: - get_logger().info(f"Failed to get pr id, error: {e}") + get_logger().info(f"Failed to get PR id, error: {e}") return "" def publish_file_comments(self, file_comments: list) -> bool: diff --git a/pr_agent/git_providers/github_provider.py b/pr_agent/git_providers/github_provider.py index 4f3a5ec71d..fa52b7dc05 100644 --- a/pr_agent/git_providers/github_provider.py +++ b/pr_agent/git_providers/github_provider.py @@ -96,7 +96,7 @@ def _get_owner_and_repo_path(self, given_url: str) -> str: parsed_url = urlparse(given_url) repo_path = (parsed_url.path.split('.git')[0])[1:] # //.git -> / if not repo_path: - get_logger().error(f"url is neither an issues url nor a pr url nor a valid git url: {given_url}. Returning empty result.") + get_logger().error(f"url is neither an issues url nor a PR url nor a valid git url: {given_url}. Returning empty result.") return "" return repo_path except Exception as e: From 03fa5b7d9264d9923e1134419a4d8d641d3cd21b Mon Sep 17 00:00:00 2001 From: Seohyun Lee Date: Tue, 20 May 2025 03:00:54 +0900 Subject: [PATCH 260/365] docs: Modify the review effort label text in the review document to an updated format --- docs/docs/tools/review.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/tools/review.md b/docs/docs/tools/review.md index e5cc6b93ea..2489a724d4 100644 --- a/docs/docs/tools/review.md +++ b/docs/docs/tools/review.md @@ -112,7 +112,7 @@ extra_instructions = "..." enable_review_labels_effort - If set to true, the tool will publish a 'Review effort [1-5]: x' label. Default is true. + If set to true, the tool will publish a 'Review effort x/5' label (1–5 scale). Default is true. @@ -141,7 +141,7 @@ extra_instructions = "..." The `review` tool can auto-generate two specific types of labels for a PR: - a `possible security issue` label that detects if a possible [security issue](https://github.com/Codium-ai/pr-agent/blob/tr/user_description/pr_agent/settings/pr_reviewer_prompts.toml#L136) exists in the PR code (`enable_review_labels_security` flag) - - a `Review effort [1-5]: x` label, where x is the estimated effort to review the PR (`enable_review_labels_effort` flag) + - a `Review effort x/5` label, where x is the estimated effort to review the PR on a 1–5 scale (`enable_review_labels_effort` flag) Both modes are useful, and we recommended to enable them. From 25b807f71c20559a640c00129276abc3c74158d6 Mon Sep 17 00:00:00 2001 From: Seohyun Lee Date: Tue, 20 May 2025 03:11:21 +0900 Subject: [PATCH 261/365] docs: Add missing spacebar --- docs/docs/usage-guide/configuration_options.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/usage-guide/configuration_options.md b/docs/docs/usage-guide/configuration_options.md index ab499dca3a..b9b3d5d929 100644 --- a/docs/docs/usage-guide/configuration_options.md +++ b/docs/docs/usage-guide/configuration_options.md @@ -5,7 +5,7 @@ See the [Tools Guide](https://qodo-merge-docs.qodo.ai/tools/) for a detailed des There are three main ways to set persistent configurations: -1. [Wiki](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/#wiki-configuration-file)configuration page 💎 +1. [Wiki](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/#wiki-configuration-file) configuration page 💎 2. [Local](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/#local-configuration-file) configuration file 3. [Global](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/#global-configuration-file) configuration file 💎 From 8aa89ff8e66f4a9f2de1bbc675228635ed07f6a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Walson=20Low=20=EF=BC=88=E5=88=98=E7=BB=B4=E6=98=87?= =?UTF-8?q?=EF=BC=89?= Date: Tue, 20 May 2025 10:27:13 +0800 Subject: [PATCH 262/365] docs: include [aws] in .secrets.template --- docs/docs/usage-guide/changing_a_model.md | 2 +- pr_agent/settings/.secrets_template.toml | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/docs/usage-guide/changing_a_model.md b/docs/docs/usage-guide/changing_a_model.md index bfeb7c3a55..e7f9e16ae5 100644 --- a/docs/docs/usage-guide/changing_a_model.md +++ b/docs/docs/usage-guide/changing_a_model.md @@ -226,7 +226,7 @@ To use Amazon Bedrock and its foundational models, add the below configuration: model="bedrock/anthropic.claude-3-5-sonnet-20240620-v1:0" fallback_models=["bedrock/anthropic.claude-3-5-sonnet-20240620-v1:0"] -[aws] +[aws] # in .secrets.toml AWS_ACCESS_KEY_ID="..." AWS_SECRET_ACCESS_KEY="..." AWS_REGION_NAME="..." diff --git a/pr_agent/settings/.secrets_template.toml b/pr_agent/settings/.secrets_template.toml index 6572677dea..17c5e8eecc 100644 --- a/pr_agent/settings/.secrets_template.toml +++ b/pr_agent/settings/.secrets_template.toml @@ -111,4 +111,9 @@ api_base = "" # Your Azure OpenAI service base URL (e.g., https://openai.xyz.co [openrouter] key = "" -api_base = "" \ No newline at end of file +api_base = "" + +[aws] +AWS_ACCESS_KEY_ID = "" +AWS_SECRET_ACCESS_KEY = "" +AWS_REGION_NAME = "" \ No newline at end of file From 81fa22e4df6c8fd4ce3da0466bd181019bb6d108 Mon Sep 17 00:00:00 2001 From: kkan9ma Date: Tue, 20 May 2025 13:47:15 +0900 Subject: [PATCH 263/365] Add model name validation --- pr_agent/algo/token_handler.py | 44 ++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/pr_agent/algo/token_handler.py b/pr_agent/algo/token_handler.py index 0c8851e8cd..2781fc5c11 100644 --- a/pr_agent/algo/token_handler.py +++ b/pr_agent/algo/token_handler.py @@ -1,4 +1,6 @@ from threading import Lock +from math import ceil +import re from jinja2 import Environment, StrictUndefined from tiktoken import encoding_for_model, get_encoding @@ -7,6 +9,16 @@ from pr_agent.log import get_logger +class ModelTypeValidator: + @staticmethod + def is_openai_model(model_name: str) -> bool: + return 'gpt' in model_name or re.match(r"^o[1-9](-mini|-preview)?$", model_name) + + @staticmethod + def is_claude_model(model_name: str) -> bool: + return 'claude' in model_name + + class TokenEncoder: _encoder_instance = None _model = None @@ -51,6 +63,9 @@ def __init__(self, pr=None, vars: dict = {}, system="", user=""): - user: The user string. """ self.encoder = TokenEncoder.get_token_encoder() + self.settings = get_settings() + self.model_validator = ModelTypeValidator() + if pr is not None: self.prompt_tokens = self._get_system_user_tokens(pr, self.encoder, vars, system, user) @@ -79,19 +94,20 @@ def _get_system_user_tokens(self, pr, encoder, vars: dict, system, user): get_logger().error(f"Error in _get_system_user_tokens: {e}") return 0 - def calc_claude_tokens(self, patch): + def calc_claude_tokens(self, patch: str) -> int: try: import anthropic from pr_agent.algo import MAX_TOKENS - client = anthropic.Anthropic(api_key=get_settings(use_context=False).get('anthropic.key')) - MaxTokens = MAX_TOKENS[get_settings().config.model] + + client = anthropic.Anthropic(api_key=self.settings.get('anthropic.key')) + max_tokens = MAX_TOKENS[self.settings.config.model] # Check if the content size is too large (9MB limit) if len(patch.encode('utf-8')) > 9_000_000: get_logger().warning( "Content too large for Anthropic token counting API, falling back to local tokenizer" ) - return MaxTokens + return max_tokens response = client.messages.count_tokens( model="claude-3-7-sonnet-20250219", @@ -104,29 +120,21 @@ def calc_claude_tokens(self, patch): return response.input_tokens except Exception as e: - get_logger().error( f"Error in Anthropic token counting: {e}") - return MaxTokens + get_logger().error(f"Error in Anthropic token counting: {e}") + return max_tokens - def is_openai_model(self, model_name): - from re import match - - return 'gpt' in model_name or match(r"^o[1-9](-mini|-preview)?$", model_name) - - def apply_estimation_factor(self, model_name, default_estimate): - from math import ceil - - factor = 1 + get_settings().get('config.model_token_count_estimate_factor', 0) + def apply_estimation_factor(self, model_name: str, default_estimate: int) -> int: + factor = 1 + self.settings.get('config.model_token_count_estimate_factor', 0) get_logger().warning(f"{model_name}'s token count cannot be accurately estimated. Using factor of {factor}") - return ceil(factor * default_estimate) def get_token_count_by_model_type(self, patch: str, default_estimate: int) -> int: model_name = get_settings().config.model.lower() - if 'claude' in model_name and get_settings(use_context=False).get('anthropic.key'): + if self.model_validator.is_claude_model(model_name) and get_settings(use_context=False).get('anthropic.key'): return self.calc_claude_tokens(patch) - if self.is_openai_model(model_name) and get_settings(use_context=False).get('openai.key'): + if self.model_validator.is_openai_model(model_name) and get_settings(use_context=False).get('openai.key'): return default_estimate return self.apply_estimation_factor(model_name, default_estimate) From e72bb28c4e9cad740f9240b25a5adb7339943044 Mon Sep 17 00:00:00 2001 From: kkan9ma Date: Tue, 20 May 2025 13:50:30 +0900 Subject: [PATCH 264/365] Replace get_settings() with self.settings --- pr_agent/algo/token_handler.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pr_agent/algo/token_handler.py b/pr_agent/algo/token_handler.py index 2781fc5c11..99dbc635e6 100644 --- a/pr_agent/algo/token_handler.py +++ b/pr_agent/algo/token_handler.py @@ -129,12 +129,12 @@ def apply_estimation_factor(self, model_name: str, default_estimate: int) -> int return ceil(factor * default_estimate) def get_token_count_by_model_type(self, patch: str, default_estimate: int) -> int: - model_name = get_settings().config.model.lower() + model_name = self.settings.config.model.lower() - if self.model_validator.is_claude_model(model_name) and get_settings(use_context=False).get('anthropic.key'): + if self.model_validator.is_claude_model(model_name) and self.settings.get('anthropic.key'): return self.calc_claude_tokens(patch) - if self.model_validator.is_openai_model(model_name) and get_settings(use_context=False).get('openai.key'): + if self.model_validator.is_openai_model(model_name) and self.settings.get('openai.key'): return default_estimate return self.apply_estimation_factor(model_name, default_estimate) From f198e6fa097a9ad27a08da7b8f9e14dbe7be5a9b Mon Sep 17 00:00:00 2001 From: kkan9ma Date: Tue, 20 May 2025 14:12:24 +0900 Subject: [PATCH 265/365] Add constants and improve token calculation logic --- pr_agent/algo/token_handler.py | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/pr_agent/algo/token_handler.py b/pr_agent/algo/token_handler.py index 99dbc635e6..8bd3f1159f 100644 --- a/pr_agent/algo/token_handler.py +++ b/pr_agent/algo/token_handler.py @@ -52,6 +52,10 @@ class TokenHandler: method. """ + # Constants + CLAUDE_MODEL = "claude-3-7-sonnet-20250219" + CLAUDE_MAX_CONTENT_SIZE = 9_000_000 # Maximum allowed content size (9MB) for Claude API + def __init__(self, pr=None, vars: dict = {}, system="", user=""): """ Initializes the TokenHandler object. @@ -102,15 +106,14 @@ def calc_claude_tokens(self, patch: str) -> int: client = anthropic.Anthropic(api_key=self.settings.get('anthropic.key')) max_tokens = MAX_TOKENS[self.settings.config.model] - # Check if the content size is too large (9MB limit) - if len(patch.encode('utf-8')) > 9_000_000: + if len(patch.encode('utf-8')) > self.CLAUDE_MAX_CONTENT_SIZE: get_logger().warning( "Content too large for Anthropic token counting API, falling back to local tokenizer" ) return max_tokens response = client.messages.count_tokens( - model="claude-3-7-sonnet-20250219", + model=self.CLAUDE_MODEL, system="system", messages=[{ "role": "user", @@ -126,9 +129,20 @@ def calc_claude_tokens(self, patch: str) -> int: def apply_estimation_factor(self, model_name: str, default_estimate: int) -> int: factor = 1 + self.settings.get('config.model_token_count_estimate_factor', 0) get_logger().warning(f"{model_name}'s token count cannot be accurately estimated. Using factor of {factor}") + return ceil(factor * default_estimate) def get_token_count_by_model_type(self, patch: str, default_estimate: int) -> int: + """ + Get token count based on model type. + + Args: + patch: The text to count tokens for. + default_estimate: The default token count estimate. + + Returns: + int: The calculated token count. + """ model_name = self.settings.config.model.lower() if self.model_validator.is_claude_model(model_name) and self.settings.get('anthropic.key'): @@ -152,8 +166,8 @@ def count_tokens(self, patch: str, force_accurate: bool = False) -> int: """ encoder_estimate = len(self.encoder.encode(patch, disallowed_special=())) - #If an estimate is enough (for example, in cases where the maximal allowed tokens is way below the known limits), return it. - if not force_accurate: - return encoder_estimate - else: + if force_accurate: return self.get_token_count_by_model_type(patch, encoder_estimate=encoder_estimate) + + # If an estimate is enough (for example, in cases where the maximal allowed tokens is way below the known limits), return it. + return encoder_estimate From 97f2b6f7360117355b46da6436e7f88ecfbd16c8 Mon Sep 17 00:00:00 2001 From: kkan9ma Date: Tue, 20 May 2025 15:29:27 +0900 Subject: [PATCH 266/365] Fix TypeError --- pr_agent/algo/token_handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/algo/token_handler.py b/pr_agent/algo/token_handler.py index 8bd3f1159f..0a1aeb8933 100644 --- a/pr_agent/algo/token_handler.py +++ b/pr_agent/algo/token_handler.py @@ -167,7 +167,7 @@ def count_tokens(self, patch: str, force_accurate: bool = False) -> int: encoder_estimate = len(self.encoder.encode(patch, disallowed_special=())) if force_accurate: - return self.get_token_count_by_model_type(patch, encoder_estimate=encoder_estimate) + return self.get_token_count_by_model_type(patch, encoder_estimate) # If an estimate is enough (for example, in cases where the maximal allowed tokens is way below the known limits), return it. return encoder_estimate From 1b74942919f63ab8cafec68a3b17470426650bcf Mon Sep 17 00:00:00 2001 From: Pinyoo Thotaboot Date: Tue, 20 May 2025 15:18:07 +0700 Subject: [PATCH 267/365] Set default configuration of Gitea --- pr_agent/settings/.secrets_template.toml | 5 +++++ pr_agent/settings/configuration.toml | 1 + 2 files changed, 6 insertions(+) diff --git a/pr_agent/settings/.secrets_template.toml b/pr_agent/settings/.secrets_template.toml index 6572677dea..9590a84c3a 100644 --- a/pr_agent/settings/.secrets_template.toml +++ b/pr_agent/settings/.secrets_template.toml @@ -68,6 +68,11 @@ webhook_secret = "" # Optional, may be commented out. personal_access_token = "" shared_secret = "" # webhook secret +[gitea] +# Gitea personal access token +personal_access_token="" +webhook_secret="" # webhook secret + [bitbucket] # For Bitbucket authentication auth_type = "bearer" # "bearer" or "basic" diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index 50dcbdb941..421ecff4de 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -278,6 +278,7 @@ push_commands = [ ] [gitea_app] +url = "https://gitea.com" handle_push_trigger = true pr_commands = [ "/describe --pr_description.final_update_message=false", From 2d619564f259fdacc7d51775c1cd3862b113c4cd Mon Sep 17 00:00:00 2001 From: Pinyoo Thotaboot Date: Tue, 20 May 2025 15:51:50 +0700 Subject: [PATCH 268/365] Update README for Gitea --- README.md | 82 +++++++++++++++++++++++++++---------------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index c18bfa7ca7..67fa57a740 100644 --- a/README.md +++ b/README.md @@ -91,47 +91,47 @@ This version includes a new tool, [Help Docs](https://qodo-merge-docs.qodo.ai/to Supported commands per platform: -| | | GitHub | GitLab | Bitbucket | Azure DevOps | -| ----- |---------------------------------------------------------------------------------------------------------|:------:|:------:|:---------:|:------------:| -| TOOLS | [Review](https://qodo-merge-docs.qodo.ai/tools/review/) | ✅ | ✅ | ✅ | ✅ | -| | [Describe](https://qodo-merge-docs.qodo.ai/tools/describe/) | ✅ | ✅ | ✅ | ✅ | -| | [Improve](https://qodo-merge-docs.qodo.ai/tools/improve/) | ✅ | ✅ | ✅ | ✅ | -| | [Ask](https://qodo-merge-docs.qodo.ai/tools/ask/) | ✅ | ✅ | ✅ | ✅ | -| | ⮑ [Ask on code lines](https://qodo-merge-docs.qodo.ai/tools/ask/#ask-lines) | ✅ | ✅ | | | -| | [Update CHANGELOG](https://qodo-merge-docs.qodo.ai/tools/update_changelog/) | ✅ | ✅ | ✅ | ✅ | -| | [Help Docs](https://qodo-merge-docs.qodo.ai/tools/help_docs/?h=auto#auto-approval) | ✅ | ✅ | ✅ | | -| | [Ticket Context](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/) 💎 | ✅ | ✅ | ✅ | | -| | [Utilizing Best Practices](https://qodo-merge-docs.qodo.ai/tools/improve/#best-practices) 💎 | ✅ | ✅ | ✅ | | -| | [PR Chat](https://qodo-merge-docs.qodo.ai/chrome-extension/features/#pr-chat) 💎 | ✅ | | | | -| | [Suggestion Tracking](https://qodo-merge-docs.qodo.ai/tools/improve/#suggestion-tracking) 💎 | ✅ | ✅ | | | -| | [CI Feedback](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/) 💎 | ✅ | | | | -| | [PR Documentation](https://qodo-merge-docs.qodo.ai/tools/documentation/) 💎 | ✅ | ✅ | | | -| | [Custom Labels](https://qodo-merge-docs.qodo.ai/tools/custom_labels/) 💎 | ✅ | ✅ | | | -| | [Analyze](https://qodo-merge-docs.qodo.ai/tools/analyze/) 💎 | ✅ | ✅ | | | -| | [Similar Code](https://qodo-merge-docs.qodo.ai/tools/similar_code/) 💎 | ✅ | | | | -| | [Custom Prompt](https://qodo-merge-docs.qodo.ai/tools/custom_prompt/) 💎 | ✅ | ✅ | ✅ | | -| | [Test](https://qodo-merge-docs.qodo.ai/tools/test/) 💎 | ✅ | ✅ | | | -| | [Implement](https://qodo-merge-docs.qodo.ai/tools/implement/) 💎 | ✅ | ✅ | ✅ | | -| | [Scan Repo Discussions](https://qodo-merge-docs.qodo.ai/tools/scan_repo_discussions/) 💎 | ✅ | | | | -| | [Auto-Approve](https://qodo-merge-docs.qodo.ai/tools/improve/?h=auto#auto-approval) 💎 | ✅ | ✅ | ✅ | | -| | | | | | | -| USAGE | [CLI](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#local-repo-cli) | ✅ | ✅ | ✅ | ✅ | -| | [App / webhook](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#github-app) | ✅ | ✅ | ✅ | ✅ | -| | [Tagging bot](https://github.com/Codium-ai/pr-agent#try-it-now) | ✅ | | | | -| | [Actions](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-action) | ✅ | ✅ | ✅ | ✅ | -| | | | | | | -| CORE | [PR compression](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) | ✅ | ✅ | ✅ | ✅ | -| | Adaptive and token-aware file patch fitting | ✅ | ✅ | ✅ | ✅ | -| | [Multiple models support](https://qodo-merge-docs.qodo.ai/usage-guide/changing_a_model/) | ✅ | ✅ | ✅ | ✅ | -| | [Local and global metadata](https://qodo-merge-docs.qodo.ai/core-abilities/metadata/) | ✅ | ✅ | ✅ | ✅ | -| | [Dynamic context](https://qodo-merge-docs.qodo.ai/core-abilities/dynamic_context/) | ✅ | ✅ | ✅ | ✅ | -| | [Self reflection](https://qodo-merge-docs.qodo.ai/core-abilities/self_reflection/) | ✅ | ✅ | ✅ | ✅ | -| | [Static code analysis](https://qodo-merge-docs.qodo.ai/core-abilities/static_code_analysis/) 💎 | ✅ | ✅ | | | -| | [Global and wiki configurations](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/) 💎 | ✅ | ✅ | ✅ | | -| | [PR interactive actions](https://www.qodo.ai/images/pr_agent/pr-actions.mp4) 💎 | ✅ | ✅ | | | -| | [Impact Evaluation](https://qodo-merge-docs.qodo.ai/core-abilities/impact_evaluation/) 💎 | ✅ | ✅ | | | -| | [Code Validation 💎](https://qodo-merge-docs.qodo.ai/core-abilities/code_validation/) | ✅ | ✅ | ✅ | ✅ | -| | [Auto Best Practices 💎](https://qodo-merge-docs.qodo.ai/core-abilities/auto_best_practices/) | ✅ | | | | +| | | GitHub | GitLab | Bitbucket | Azure DevOps | Gitea | +| ----- |---------------------------------------------------------------------------------------------------------|:------:|:------:|:---------:|:------------:|:-----:| +| TOOLS | [Review](https://qodo-merge-docs.qodo.ai/tools/review/) | ✅ | ✅ | ✅ | ✅ | ✅ | +| | [Describe](https://qodo-merge-docs.qodo.ai/tools/describe/) | ✅ | ✅ | ✅ | ✅ | ✅ | +| | [Improve](https://qodo-merge-docs.qodo.ai/tools/improve/) | ✅ | ✅ | ✅ | ✅ | ✅ | +| | [Ask](https://qodo-merge-docs.qodo.ai/tools/ask/) | ✅ | ✅ | ✅ | ✅ | | +| | ⮑ [Ask on code lines](https://qodo-merge-docs.qodo.ai/tools/ask/#ask-lines) | ✅ | ✅ | | | | +| | [Update CHANGELOG](https://qodo-merge-docs.qodo.ai/tools/update_changelog/) | ✅ | ✅ | ✅ | ✅ | | +| | [Help Docs](https://qodo-merge-docs.qodo.ai/tools/help_docs/?h=auto#auto-approval) | ✅ | ✅ | ✅ | | | +| | [Ticket Context](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/) 💎 | ✅ | ✅ | ✅ | | | +| | [Utilizing Best Practices](https://qodo-merge-docs.qodo.ai/tools/improve/#best-practices) 💎 | ✅ | ✅ | ✅ | | | +| | [PR Chat](https://qodo-merge-docs.qodo.ai/chrome-extension/features/#pr-chat) 💎 | ✅ | | | | | +| | [Suggestion Tracking](https://qodo-merge-docs.qodo.ai/tools/improve/#suggestion-tracking) 💎 | ✅ | ✅ | | | | +| | [CI Feedback](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/) 💎 | ✅ | | | | | +| | [PR Documentation](https://qodo-merge-docs.qodo.ai/tools/documentation/) 💎 | ✅ | ✅ | | | | +| | [Custom Labels](https://qodo-merge-docs.qodo.ai/tools/custom_labels/) 💎 | ✅ | ✅ | | | | +| | [Analyze](https://qodo-merge-docs.qodo.ai/tools/analyze/) 💎 | ✅ | ✅ | | | | +| | [Similar Code](https://qodo-merge-docs.qodo.ai/tools/similar_code/) 💎 | ✅ | | | | | +| | [Custom Prompt](https://qodo-merge-docs.qodo.ai/tools/custom_prompt/) 💎 | ✅ | ✅ | ✅ | | | +| | [Test](https://qodo-merge-docs.qodo.ai/tools/test/) 💎 | ✅ | ✅ | | | | +| | [Implement](https://qodo-merge-docs.qodo.ai/tools/implement/) 💎 | ✅ | ✅ | ✅ | | | +| | [Scan Repo Discussions](https://qodo-merge-docs.qodo.ai/tools/scan_repo_discussions/) 💎 | ✅ | | | | | +| | [Auto-Approve](https://qodo-merge-docs.qodo.ai/tools/improve/?h=auto#auto-approval) 💎 | ✅ | ✅ | ✅ | | | +| | | | | | | | +| USAGE | [CLI](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#local-repo-cli) | ✅ | ✅ | ✅ | ✅ | ✅ | +| | [App / webhook](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#github-app) | ✅ | ✅ | ✅ | ✅ | ✅ | +| | [Tagging bot](https://github.com/Codium-ai/pr-agent#try-it-now) | ✅ | | | | | +| | [Actions](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-action) | ✅ | ✅ | ✅ | ✅ | | +| | | | | | | | +| CORE | [PR compression](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) | ✅ | ✅ | ✅ | ✅ | | +| | Adaptive and token-aware file patch fitting | ✅ | ✅ | ✅ | ✅ | | +| | [Multiple models support](https://qodo-merge-docs.qodo.ai/usage-guide/changing_a_model/) | ✅ | ✅ | ✅ | ✅ | | +| | [Local and global metadata](https://qodo-merge-docs.qodo.ai/core-abilities/metadata/) | ✅ | ✅ | ✅ | ✅ | | +| | [Dynamic context](https://qodo-merge-docs.qodo.ai/core-abilities/dynamic_context/) | ✅ | ✅ | ✅ | ✅ | | +| | [Self reflection](https://qodo-merge-docs.qodo.ai/core-abilities/self_reflection/) | ✅ | ✅ | ✅ | ✅ | | +| | [Static code analysis](https://qodo-merge-docs.qodo.ai/core-abilities/static_code_analysis/) 💎 | ✅ | ✅ | | | | +| | [Global and wiki configurations](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/) 💎 | ✅ | ✅ | ✅ | | | +| | [PR interactive actions](https://www.qodo.ai/images/pr_agent/pr-actions.mp4) 💎 | ✅ | ✅ | | | | +| | [Impact Evaluation](https://qodo-merge-docs.qodo.ai/core-abilities/impact_evaluation/) 💎 | ✅ | ✅ | | | | +| | [Code Validation 💎](https://qodo-merge-docs.qodo.ai/core-abilities/code_validation/) | ✅ | ✅ | ✅ | ✅ | | +| | [Auto Best Practices 💎](https://qodo-merge-docs.qodo.ai/core-abilities/auto_best_practices/) | ✅ | | | | | - 💎 means this feature is available only in [Qodo Merge](https://www.qodo.ai/pricing/) [//]: # (- Support for additional git providers is described in [here](./docs/Full_environments.md)) From 9069c37a05c6f65f4392690b2a7e03ab7cc64255 Mon Sep 17 00:00:00 2001 From: sharoneyal Date: Tue, 20 May 2025 12:12:06 +0300 Subject: [PATCH 269/365] Add incremental update to documentation (#1796) --- .../docs/core-abilities/incremental_update.md | 33 +++++++++++++++++++ docs/docs/core-abilities/index.md | 1 + docs/docs/index.md | 1 + docs/mkdocs.yml | 1 + 4 files changed, 36 insertions(+) create mode 100644 docs/docs/core-abilities/incremental_update.md diff --git a/docs/docs/core-abilities/incremental_update.md b/docs/docs/core-abilities/incremental_update.md new file mode 100644 index 0000000000..3813fb73d6 --- /dev/null +++ b/docs/docs/core-abilities/incremental_update.md @@ -0,0 +1,33 @@ +# Incremental Update 💎 + +`Supported Git Platforms: GitHub` + +## Overview +The Incremental Update feature helps users focus on feedback for their newest changes, making large PRs more manageable. + +### How it works + +=== "Update Option on Subsequent Commits" + ![code_suggestions_update](https://www.qodo.ai/images/pr_agent/inc_update_before.png){width=512} + +=== "Generation of Incremental Update" + ![code_suggestions_inc_update_result](https://www.qodo.ai/images/pr_agent/inc_update_shown.png){width=512} + +___ + +Whenever new commits are pushed following a recent code suggestions report for this PR, an Update button appears (as seen above). + +Once the user clicks on the button: + +- The `improve` tool identifies the new changes (the "delta") +- Provides suggestions on these recent changes +- Combines these suggestions with the overall PR feedback, prioritizing delta-related comments +- Marks delta-related comments with a textual indication followed by an asterisk (*) with a link to this page, so they can easily be identified + +### Benefits for Developers + +- Focus on what matters: See feedback on newest code first +- Clearer organization: Comments on recent changes are clearly marked +- Better workflow: Address feedback more systematically, starting with recent changes + + diff --git a/docs/docs/core-abilities/index.md b/docs/docs/core-abilities/index.md index 9af26e2eee..b97260ee78 100644 --- a/docs/docs/core-abilities/index.md +++ b/docs/docs/core-abilities/index.md @@ -8,6 +8,7 @@ Qodo Merge utilizes a variety of core abilities to provide a comprehensive and e - [Dynamic context](https://qodo-merge-docs.qodo.ai/core-abilities/dynamic_context/) - [Fetching ticket context](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/) - [Impact evaluation](https://qodo-merge-docs.qodo.ai/core-abilities/impact_evaluation/) +- [Incremental Update](https://qodo-merge-docs.qodo.ai/core-abilities/incremental_update/) - [Interactivity](https://qodo-merge-docs.qodo.ai/core-abilities/interactivity/) - [Local and global metadata](https://qodo-merge-docs.qodo.ai/core-abilities/metadata/) - [RAG context enrichment](https://qodo-merge-docs.qodo.ai/core-abilities/rag_context_enrichment/) diff --git a/docs/docs/index.md b/docs/docs/index.md index 8dd7c955da..79a06e5d42 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -67,6 +67,7 @@ PR-Agent and Qodo Merge offers extensive pull request functionalities across var | | [Impact Evaluation](https://qodo-merge-docs.qodo.ai/core-abilities/impact_evaluation/) 💎 | ✅ | ✅ | | | | | [Code Validation 💎](https://qodo-merge-docs.qodo.ai/core-abilities/code_validation/) | ✅ | ✅ | ✅ | ✅ | | | [Auto Best Practices 💎](https://qodo-merge-docs.qodo.ai/core-abilities/auto_best_practices/) | ✅ | | | | +| | [Incremental Update 💎](https://qodo-merge-docs.qodo.ai/core-abilities/incremental_update/) | ✅ | | | | !!! note "💎 means Qodo Merge only" All along the documentation, 💎 marks a feature available only in [Qodo Merge](https://www.codium.ai/pricing/){:target="_blank"}, and not in the open-source version. diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 8525fdee3e..a25c081ba1 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -49,6 +49,7 @@ nav: - Dynamic context: 'core-abilities/dynamic_context.md' - Fetching ticket context: 'core-abilities/fetching_ticket_context.md' - Impact evaluation: 'core-abilities/impact_evaluation.md' + - Incremental Update: 'core-abilities/incremental_update.md' - Interactivity: 'core-abilities/interactivity.md' - Local and global metadata: 'core-abilities/metadata.md' - RAG context enrichment: 'core-abilities/rag_context_enrichment.md' From 6405284461f6a57617a52cd80ab0461752d8fe0d Mon Sep 17 00:00:00 2001 From: Kangmoon Seo <100016044+KangmoonSeo@users.noreply.github.com> Date: Tue, 20 May 2025 18:22:33 +0900 Subject: [PATCH 270/365] fix: reorder exception handling to enable proper retry behavior --- pr_agent/algo/ai_handlers/litellm_ai_handler.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pr_agent/algo/ai_handlers/litellm_ai_handler.py b/pr_agent/algo/ai_handlers/litellm_ai_handler.py index 8804165801..8d727b8b15 100644 --- a/pr_agent/algo/ai_handlers/litellm_ai_handler.py +++ b/pr_agent/algo/ai_handlers/litellm_ai_handler.py @@ -371,12 +371,12 @@ async def chat_completion(self, model: str, system: str, user: str, temperature: get_logger().info(f"\nUser prompt:\n{user}") response = await acompletion(**kwargs) - except (openai.APIError, openai.APITimeoutError) as e: - get_logger().warning(f"Error during LLM inference: {e}") - raise except (openai.RateLimitError) as e: get_logger().error(f"Rate limit error during LLM inference: {e}") raise + except (openai.APIError, openai.APITimeoutError) as e: + get_logger().warning(f"Error during LLM inference: {e}") + raise except (Exception) as e: get_logger().warning(f"Unknown error during LLM inference: {e}") raise openai.APIError from e From bd68a0de559611bfd37dc51653f6f3d9921aeb3b Mon Sep 17 00:00:00 2001 From: Pinyoo Thotaboot Date: Tue, 20 May 2025 16:46:32 +0700 Subject: [PATCH 271/365] Update Gitea documents --- docs/docs/installation/gitea.md | 46 +++++++++++++++++++ docs/docs/installation/index.md | 1 + docs/docs/installation/locally.md | 15 +++++- docs/docs/installation/pr_agent.md | 8 ++++ .../docs/usage-guide/automations_and_usage.md | 15 +++++- docs/docs/usage-guide/index.md | 1 + docs/docs/usage-guide/introduction.md | 2 +- 7 files changed, 85 insertions(+), 3 deletions(-) create mode 100644 docs/docs/installation/gitea.md diff --git a/docs/docs/installation/gitea.md b/docs/docs/installation/gitea.md new file mode 100644 index 0000000000..476497f777 --- /dev/null +++ b/docs/docs/installation/gitea.md @@ -0,0 +1,46 @@ +## Run a Gitea webhook server + +1. In Gitea create a new user and give it "Reporter" role ("Developer" if using Pro version of the agent) for the intended group or project. + +2. For the user from step 1. generate a `personal_access_token` with `api` access. + +3. Generate a random secret for your app, and save it for later (`webhook_secret`). For example, you can use: + +```bash +WEBHOOK_SECRET=$(python -c "import secrets; print(secrets.token_hex(10))") +``` + +4. Clone this repository: + +```bash +git clone https://github.com/qodo-ai/pr-agent.git +``` + +5. Prepare variables and secrets. Skip this step if you plan on setting these as environment variables when running the agent: +1. In the configuration file/variables: + - Set `config.git_provider` to "gitea" + +2. In the secrets file/variables: + - Set your AI model key in the respective section + - In the [Gitea] section, set `personal_access_token` (with token from step 2) and `webhook_secret` (with secret from step 3) + +6. Build a Docker image for the app and optionally push it to a Docker repository. We'll use Dockerhub as an example: + +```bash +docker build -f /docker/Dockerfile -t pr-agent:gitea_app --target gitea_app . +docker push codiumai/pr-agent:gitea_webhook # Push to your Docker repository +``` + +7. Set the environmental variables, the method depends on your docker runtime. Skip this step if you included your secrets/configuration directly in the Docker image. + +```bash +CONFIG__GIT_PROVIDER=gitea +GITEA__PERSONAL_ACCESS_TOKEN= +GITEA__WEBHOOK_SECRET= +GITEA__URL=https://gitea.com # Or self host +OPENAI__KEY= +``` + +8. Create a webhook in your Gitea project. Set the URL to `http[s]:///api/v1/gitea_webhooks`, the secret token to the generated secret from step 3, and enable the triggers `push`, `comments` and `merge request events`. + +9. Test your installation by opening a merge request or commenting on a merge request using one of PR Agent's commands. diff --git a/docs/docs/installation/index.md b/docs/docs/installation/index.md index 9831078d8c..cc593debb0 100644 --- a/docs/docs/installation/index.md +++ b/docs/docs/installation/index.md @@ -9,6 +9,7 @@ There are several ways to use self-hosted PR-Agent: - [GitLab integration](./gitlab.md) - [BitBucket integration](./bitbucket.md) - [Azure DevOps integration](./azure.md) +- [Gitea integration](./gitea.md) ## Qodo Merge 💎 diff --git a/docs/docs/installation/locally.md b/docs/docs/installation/locally.md index cd981f9614..9ceb077b2d 100644 --- a/docs/docs/installation/locally.md +++ b/docs/docs/installation/locally.md @@ -1,7 +1,7 @@ To run PR-Agent locally, you first need to acquire two keys: 1. An OpenAI key from [here](https://platform.openai.com/api-keys){:target="_blank"}, with access to GPT-4 and o4-mini (or a key for other [language models](https://qodo-merge-docs.qodo.ai/usage-guide/changing_a_model/), if you prefer). -2. A personal access token from your Git platform (GitHub, GitLab, BitBucket) with repo scope. GitHub token, for example, can be issued from [here](https://github.com/settings/tokens){:target="_blank"} +2. A personal access token from your Git platform (GitHub, GitLab, BitBucket,Gitea) with repo scope. GitHub token, for example, can be issued from [here](https://github.com/settings/tokens){:target="_blank"} ## Using Docker image @@ -40,6 +40,19 @@ To invoke a tool (for example `review`), you can run PR-Agent directly from the docker run --rm -it -e CONFIG.GIT_PROVIDER=bitbucket -e OPENAI.KEY=$OPENAI_API_KEY -e BITBUCKET.BEARER_TOKEN=$BITBUCKET_BEARER_TOKEN codiumai/pr-agent:latest --pr_url= review ``` +- For Gitea: + + ```bash + docker run --rm -it -e OPENAI.KEY= -e CONFIG.GIT_PROVIDER=gitea -e GITEA.PERSONAL_ACCESS_TOKEN= codiumai/pr-agent:latest --pr_url review + ``` + + If you have a dedicated Gitea instance, you need to specify the custom url as variable: + + ```bash + -e GITEA.URL= + ``` + + For other git providers, update `CONFIG.GIT_PROVIDER` accordingly and check the [`pr_agent/settings/.secrets_template.toml`](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/.secrets_template.toml) file for environment variables expected names and values. ### Utilizing environment variables diff --git a/docs/docs/installation/pr_agent.md b/docs/docs/installation/pr_agent.md index 1982b7a120..9a0e3f299c 100644 --- a/docs/docs/installation/pr_agent.md +++ b/docs/docs/installation/pr_agent.md @@ -47,3 +47,11 @@ Configure PR-Agent with Azure DevOps as: - Local Azure DevOps webhook [View Azure DevOps Integration Guide →](https://qodo-merge-docs.qodo.ai/installation/azure/) + +## 🔷 Gitea Integration + +Deploy PR-Agent on Gitea as: + +- Local Gitea webhook server + +[View Gitea Integration Guide →](https://qodo-merge-docs.qodo.ai/installation/gitea/) diff --git a/docs/docs/usage-guide/automations_and_usage.md b/docs/docs/usage-guide/automations_and_usage.md index 9c3e29fdef..0a634e7725 100644 --- a/docs/docs/usage-guide/automations_and_usage.md +++ b/docs/docs/usage-guide/automations_and_usage.md @@ -30,7 +30,7 @@ verbosity_level=2 This is useful for debugging or experimenting with different tools. 3. **git provider**: The [git_provider](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml#L5) field in a configuration file determines the GIT provider that will be used by Qodo Merge. Currently, the following providers are supported: -`github` **(default)**, `gitlab`, `bitbucket`, `azure`, `codecommit`, `local`, and `gerrit`. +`github` **(default)**, `gitlab`, `bitbucket`, `azure`, `codecommit`, `local`,`gitea`, and `gerrit`. ### CLI Health Check @@ -312,3 +312,16 @@ pr_commands = [ "/improve", ] ``` + +### Gitea Webhook + +After setting up a Gitea webhook, to control which commands will run automatically when a new MR is opened, you can set the `pr_commands` parameter in the configuration file, similar to the GitHub App: + +```toml +[gitea] +pr_commands = [ + "/describe", + "/review", + "/improve", +] +``` diff --git a/docs/docs/usage-guide/index.md b/docs/docs/usage-guide/index.md index dba5a56974..79df0be618 100644 --- a/docs/docs/usage-guide/index.md +++ b/docs/docs/usage-guide/index.md @@ -12,6 +12,7 @@ It includes information on how to adjust Qodo Merge configurations, define which - [GitHub App](./automations_and_usage.md#github-app) - [GitHub Action](./automations_and_usage.md#github-action) - [GitLab Webhook](./automations_and_usage.md#gitlab-webhook) + - [Gitea Webhook](./automations_and_usage.md#gitea-webhook) - [BitBucket App](./automations_and_usage.md#bitbucket-app) - [Azure DevOps Provider](./automations_and_usage.md#azure-devops-provider) - [Managing Mail Notifications](./mail_notifications.md) diff --git a/docs/docs/usage-guide/introduction.md b/docs/docs/usage-guide/introduction.md index 11e56b32bb..74838c1ce9 100644 --- a/docs/docs/usage-guide/introduction.md +++ b/docs/docs/usage-guide/introduction.md @@ -7,5 +7,5 @@ After [installation](https://qodo-merge-docs.qodo.ai/installation/), there are t Specifically, CLI commands can be issued by invoking a pre-built [docker image](https://qodo-merge-docs.qodo.ai/installation/locally/#using-docker-image), or by invoking a [locally cloned repo](https://qodo-merge-docs.qodo.ai/installation/locally/#run-from-source). -For online usage, you will need to setup either a [GitHub App](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-app) or a [GitHub Action](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-action) (GitHub), a [GitLab webhook](https://qodo-merge-docs.qodo.ai/installation/gitlab/#run-a-gitlab-webhook-server) (GitLab), or a [BitBucket App](https://qodo-merge-docs.qodo.ai/installation/bitbucket/#run-using-codiumai-hosted-bitbucket-app) (BitBucket). +For online usage, you will need to setup either a [GitHub App](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-app) or a [GitHub Action](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-action) (GitHub), a [GitLab webhook](https://qodo-merge-docs.qodo.ai/installation/gitlab/#run-a-gitlab-webhook-server) (GitLab), or a [BitBucket App](https://qodo-merge-docs.qodo.ai/installation/bitbucket/#run-using-codiumai-hosted-bitbucket-app) (BitBucket) or a [Gitea webhook](https://qodo-merge-docs.qodo.ai/installation/gitea/#run-a-gitea-webhook-server) (Gitea). These platforms also enable to run Qodo Merge specific tools automatically when a new PR is opened, or on each push to a branch. From b686a707a43bb8f1cdc2618616062c4ca898137e Mon Sep 17 00:00:00 2001 From: Pinyoo Thotaboot Date: Tue, 20 May 2025 16:54:20 +0700 Subject: [PATCH 272/365] Not implement online --- docs/docs/installation/pr_agent.md | 8 -------- docs/docs/usage-guide/introduction.md | 2 +- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/docs/docs/installation/pr_agent.md b/docs/docs/installation/pr_agent.md index 9a0e3f299c..1982b7a120 100644 --- a/docs/docs/installation/pr_agent.md +++ b/docs/docs/installation/pr_agent.md @@ -47,11 +47,3 @@ Configure PR-Agent with Azure DevOps as: - Local Azure DevOps webhook [View Azure DevOps Integration Guide →](https://qodo-merge-docs.qodo.ai/installation/azure/) - -## 🔷 Gitea Integration - -Deploy PR-Agent on Gitea as: - -- Local Gitea webhook server - -[View Gitea Integration Guide →](https://qodo-merge-docs.qodo.ai/installation/gitea/) diff --git a/docs/docs/usage-guide/introduction.md b/docs/docs/usage-guide/introduction.md index 74838c1ce9..11e56b32bb 100644 --- a/docs/docs/usage-guide/introduction.md +++ b/docs/docs/usage-guide/introduction.md @@ -7,5 +7,5 @@ After [installation](https://qodo-merge-docs.qodo.ai/installation/), there are t Specifically, CLI commands can be issued by invoking a pre-built [docker image](https://qodo-merge-docs.qodo.ai/installation/locally/#using-docker-image), or by invoking a [locally cloned repo](https://qodo-merge-docs.qodo.ai/installation/locally/#run-from-source). -For online usage, you will need to setup either a [GitHub App](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-app) or a [GitHub Action](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-action) (GitHub), a [GitLab webhook](https://qodo-merge-docs.qodo.ai/installation/gitlab/#run-a-gitlab-webhook-server) (GitLab), or a [BitBucket App](https://qodo-merge-docs.qodo.ai/installation/bitbucket/#run-using-codiumai-hosted-bitbucket-app) (BitBucket) or a [Gitea webhook](https://qodo-merge-docs.qodo.ai/installation/gitea/#run-a-gitea-webhook-server) (Gitea). +For online usage, you will need to setup either a [GitHub App](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-app) or a [GitHub Action](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-action) (GitHub), a [GitLab webhook](https://qodo-merge-docs.qodo.ai/installation/gitlab/#run-a-gitlab-webhook-server) (GitLab), or a [BitBucket App](https://qodo-merge-docs.qodo.ai/installation/bitbucket/#run-using-codiumai-hosted-bitbucket-app) (BitBucket). These platforms also enable to run Qodo Merge specific tools automatically when a new PR is opened, or on each push to a branch. From 3bced45248cc0db1e9d6832f09daa4ddedd45e89 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 21 May 2025 07:53:31 +0300 Subject: [PATCH 273/365] docs: improve configuration options documentation with clearer formatting and links --- .../docs/usage-guide/configuration_options.md | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/docs/docs/usage-guide/configuration_options.md b/docs/docs/usage-guide/configuration_options.md index ab499dca3a..f3b910311c 100644 --- a/docs/docs/usage-guide/configuration_options.md +++ b/docs/docs/usage-guide/configuration_options.md @@ -1,20 +1,22 @@ -The different tools and sub-tools used by Qodo Merge are adjustable via a configuration file. - -In addition to general configuration options, each tool has its own configurations. For example, the `review` tool will use parameters from the [pr_reviewer](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml#L16) section in the configuration file. -See the [Tools Guide](https://qodo-merge-docs.qodo.ai/tools/) for a detailed description of the different tools and their configurations. - +The different tools and sub-tools used by Qodo Merge are adjustable via a Git configuration file. There are three main ways to set persistent configurations: -1. [Wiki](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/#wiki-configuration-file)configuration page 💎 +1. [Wiki](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/#wiki-configuration-file) configuration page 💎 2. [Local](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/#local-configuration-file) configuration file 3. [Global](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/#global-configuration-file) configuration file 💎 In terms of precedence, wiki configurations will override local configurations, and local configurations will override global configurations. -!!! tip "Tip1: edit only what you need" + +For a list of all possible configurations, see the [configuration options](https://github.com/qodo-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml/) page. +In addition to general configuration options, each tool has its own configurations. For example, the `review` tool will use parameters from the [pr_reviewer](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml#L16) section in the configuration file. + +!!! tip "Tip1: Edit only what you need" Your configuration file should be minimal, and edit only the relevant values. Don't copy the entire configuration options, since it can lead to legacy problems when something changes. -!!! tip "Tip2: show relevant configurations" - If you set `config.output_relevant_configurations=true`, each tool will also output in a collapsible section its relevant configurations. This can be useful for debugging, or getting to know the configurations better. +!!! tip "Tip2: Show relevant configurations" + If you set `config.output_relevant_configurations` to True, each tool will also output in a collapsible section its relevant configurations. This can be useful for debugging, or getting to know the configurations better. + + ## Wiki configuration file 💎 From 648829b770773c4db8b7dd671acb2b6c57310d6e Mon Sep 17 00:00:00 2001 From: kkan9ma Date: Wed, 21 May 2025 17:51:03 +0900 Subject: [PATCH 274/365] Rename method --- pr_agent/algo/token_handler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pr_agent/algo/token_handler.py b/pr_agent/algo/token_handler.py index 0a1aeb8933..b8eaac8916 100644 --- a/pr_agent/algo/token_handler.py +++ b/pr_agent/algo/token_handler.py @@ -15,7 +15,7 @@ def is_openai_model(model_name: str) -> bool: return 'gpt' in model_name or re.match(r"^o[1-9](-mini|-preview)?$", model_name) @staticmethod - def is_claude_model(model_name: str) -> bool: + def is_anthropic_model(model_name: str) -> bool: return 'claude' in model_name @@ -145,7 +145,7 @@ def get_token_count_by_model_type(self, patch: str, default_estimate: int) -> in """ model_name = self.settings.config.model.lower() - if self.model_validator.is_claude_model(model_name) and self.settings.get('anthropic.key'): + if self.model_validator.is_anthropic_model(model_name) and self.settings.get('anthropic.key'): return self.calc_claude_tokens(patch) if self.model_validator.is_openai_model(model_name) and self.settings.get('openai.key'): From c3ea048b718510f04637027f0f3ef6a504209a94 Mon Sep 17 00:00:00 2001 From: kkan9ma Date: Wed, 21 May 2025 17:52:51 +0900 Subject: [PATCH 275/365] Restore original return logic for force_accurate condition --- pr_agent/algo/token_handler.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pr_agent/algo/token_handler.py b/pr_agent/algo/token_handler.py index b8eaac8916..5a29da36c3 100644 --- a/pr_agent/algo/token_handler.py +++ b/pr_agent/algo/token_handler.py @@ -166,8 +166,8 @@ def count_tokens(self, patch: str, force_accurate: bool = False) -> int: """ encoder_estimate = len(self.encoder.encode(patch, disallowed_special=())) - if force_accurate: - return self.get_token_count_by_model_type(patch, encoder_estimate) - # If an estimate is enough (for example, in cases where the maximal allowed tokens is way below the known limits), return it. - return encoder_estimate + if not force_accurate: + return encoder_estimate + + return self.get_token_count_by_model_type(patch, encoder_estimate) From df0355d827b0f83f50031bb4133e8acc4929e1d8 Mon Sep 17 00:00:00 2001 From: kkan9ma Date: Wed, 21 May 2025 18:07:47 +0900 Subject: [PATCH 276/365] Remove member variable for restroring get_settings() --- pr_agent/algo/token_handler.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/pr_agent/algo/token_handler.py b/pr_agent/algo/token_handler.py index 5a29da36c3..da239cfbad 100644 --- a/pr_agent/algo/token_handler.py +++ b/pr_agent/algo/token_handler.py @@ -67,7 +67,6 @@ def __init__(self, pr=None, vars: dict = {}, system="", user=""): - user: The user string. """ self.encoder = TokenEncoder.get_token_encoder() - self.settings = get_settings() self.model_validator = ModelTypeValidator() if pr is not None: @@ -103,8 +102,8 @@ def calc_claude_tokens(self, patch: str) -> int: import anthropic from pr_agent.algo import MAX_TOKENS - client = anthropic.Anthropic(api_key=self.settings.get('anthropic.key')) - max_tokens = MAX_TOKENS[self.settings.config.model] + client = anthropic.Anthropic(api_key=get_settings().get('anthropic.key')) + max_tokens = MAX_TOKENS[get_settings().config.model] if len(patch.encode('utf-8')) > self.CLAUDE_MAX_CONTENT_SIZE: get_logger().warning( @@ -127,7 +126,7 @@ def calc_claude_tokens(self, patch: str) -> int: return max_tokens def apply_estimation_factor(self, model_name: str, default_estimate: int) -> int: - factor = 1 + self.settings.get('config.model_token_count_estimate_factor', 0) + factor = 1 + get_settings().get('config.model_token_count_estimate_factor', 0) get_logger().warning(f"{model_name}'s token count cannot be accurately estimated. Using factor of {factor}") return ceil(factor * default_estimate) @@ -143,12 +142,12 @@ def get_token_count_by_model_type(self, patch: str, default_estimate: int) -> in Returns: int: The calculated token count. """ - model_name = self.settings.config.model.lower() + model_name = get_settings().config.model.lower() - if self.model_validator.is_anthropic_model(model_name) and self.settings.get('anthropic.key'): return self.calc_claude_tokens(patch) + if self.model_validator.is_anthropic_model(model_name) and get_settings(use_context=False).get('anthropic.key'): - if self.model_validator.is_openai_model(model_name) and self.settings.get('openai.key'): + if self.model_validator.is_openai_model(model_name) and get_settings(use_context=False).get('openai.key'): return default_estimate return self.apply_estimation_factor(model_name, default_estimate) From ead7491ca91e24ab185b33c2867dd3f4a8baad0a Mon Sep 17 00:00:00 2001 From: kkan9ma Date: Wed, 21 May 2025 18:08:48 +0900 Subject: [PATCH 277/365] Apply convention for marking private --- pr_agent/algo/token_handler.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pr_agent/algo/token_handler.py b/pr_agent/algo/token_handler.py index da239cfbad..e45d611f3f 100644 --- a/pr_agent/algo/token_handler.py +++ b/pr_agent/algo/token_handler.py @@ -97,7 +97,7 @@ def _get_system_user_tokens(self, pr, encoder, vars: dict, system, user): get_logger().error(f"Error in _get_system_user_tokens: {e}") return 0 - def calc_claude_tokens(self, patch: str) -> int: + def _calc_claude_tokens(self, patch: str) -> int: try: import anthropic from pr_agent.algo import MAX_TOKENS @@ -125,13 +125,13 @@ def calc_claude_tokens(self, patch: str) -> int: get_logger().error(f"Error in Anthropic token counting: {e}") return max_tokens - def apply_estimation_factor(self, model_name: str, default_estimate: int) -> int: + def _apply_estimation_factor(self, model_name: str, default_estimate: int) -> int: factor = 1 + get_settings().get('config.model_token_count_estimate_factor', 0) get_logger().warning(f"{model_name}'s token count cannot be accurately estimated. Using factor of {factor}") return ceil(factor * default_estimate) - def get_token_count_by_model_type(self, patch: str, default_estimate: int) -> int: + def _get_token_count_by_model_type(self, patch: str, default_estimate: int) -> int: """ Get token count based on model type. @@ -144,13 +144,13 @@ def get_token_count_by_model_type(self, patch: str, default_estimate: int) -> in """ model_name = get_settings().config.model.lower() - return self.calc_claude_tokens(patch) if self.model_validator.is_anthropic_model(model_name) and get_settings(use_context=False).get('anthropic.key'): + return self._calc_claude_tokens(patch) if self.model_validator.is_openai_model(model_name) and get_settings(use_context=False).get('openai.key'): return default_estimate - return self.apply_estimation_factor(model_name, default_estimate) + return self._apply_estimation_factor(model_name, default_estimate) def count_tokens(self, patch: str, force_accurate: bool = False) -> int: """ @@ -169,4 +169,4 @@ def count_tokens(self, patch: str, force_accurate: bool = False) -> int: if not force_accurate: return encoder_estimate - return self.get_token_count_by_model_type(patch, encoder_estimate) + return self._get_token_count_by_model_type(patch, encoder_estimate) From cc686ef26d961bd515b731c447f98cd23a240633 Mon Sep 17 00:00:00 2001 From: kkan9ma Date: Thu, 22 May 2025 13:12:04 +0900 Subject: [PATCH 278/365] Reorder model check: OpenAI before Anthropic OpenAI is the default in most cases, so checking it first skips unnecessary Anthropic logic. --- pr_agent/algo/token_handler.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pr_agent/algo/token_handler.py b/pr_agent/algo/token_handler.py index e45d611f3f..67a6eed974 100644 --- a/pr_agent/algo/token_handler.py +++ b/pr_agent/algo/token_handler.py @@ -144,11 +144,11 @@ def _get_token_count_by_model_type(self, patch: str, default_estimate: int) -> i """ model_name = get_settings().config.model.lower() - if self.model_validator.is_anthropic_model(model_name) and get_settings(use_context=False).get('anthropic.key'): - return self._calc_claude_tokens(patch) - if self.model_validator.is_openai_model(model_name) and get_settings(use_context=False).get('openai.key'): return default_estimate + + if self.model_validator.is_anthropic_model(model_name) and get_settings(use_context=False).get('anthropic.key'): + return self._calc_claude_tokens(patch) return self._apply_estimation_factor(model_name, default_estimate) From facfb5f46b1be200b7dd5cfe87d3c745fa0cd85e Mon Sep 17 00:00:00 2001 From: kkan9ma Date: Thu, 22 May 2025 13:32:20 +0900 Subject: [PATCH 279/365] Add missing code: use_context=False --- pr_agent/algo/token_handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/algo/token_handler.py b/pr_agent/algo/token_handler.py index 67a6eed974..998391b170 100644 --- a/pr_agent/algo/token_handler.py +++ b/pr_agent/algo/token_handler.py @@ -102,7 +102,7 @@ def _calc_claude_tokens(self, patch: str) -> int: import anthropic from pr_agent.algo import MAX_TOKENS - client = anthropic.Anthropic(api_key=get_settings().get('anthropic.key')) + client = anthropic.Anthropic(api_key=get_settings(use_context=False).get('anthropic.key')) max_tokens = MAX_TOKENS[get_settings().config.model] if len(patch.encode('utf-8')) > self.CLAUDE_MAX_CONTENT_SIZE: From 466ec4ce90ab2dfbc584a3461c45340a82ddd54e Mon Sep 17 00:00:00 2001 From: Kangmoon Seo <100016044+KangmoonSeo@users.noreply.github.com> Date: Thu, 22 May 2025 15:04:16 +0900 Subject: [PATCH 280/365] fix: exclude RateLimitError from retry logic --- .../algo/ai_handlers/langchain_ai_handler.py | 22 +++++++++++------ .../algo/ai_handlers/litellm_ai_handler.py | 12 +++++----- .../algo/ai_handlers/openai_ai_handler.py | 24 ++++++++++--------- 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/pr_agent/algo/ai_handlers/langchain_ai_handler.py b/pr_agent/algo/ai_handlers/langchain_ai_handler.py index d4ea0aa5dc..4d708fcbb9 100644 --- a/pr_agent/algo/ai_handlers/langchain_ai_handler.py +++ b/pr_agent/algo/ai_handlers/langchain_ai_handler.py @@ -6,8 +6,8 @@ import functools -from openai import APIError, RateLimitError, Timeout -from retry import retry +import openai +from tenacity import retry, retry_if_exception_type, retry_if_not_exception_type, stop_after_attempt from pr_agent.algo.ai_handlers.base_ai_handler import BaseAiHandler from pr_agent.config_loader import get_settings @@ -36,8 +36,10 @@ def deployment_id(self): """ return get_settings().get("OPENAI.DEPLOYMENT_ID", None) - @retry(exceptions=(APIError, Timeout, AttributeError, RateLimitError), - tries=OPENAI_RETRIES, delay=2, backoff=2, jitter=(1, 3)) + @retry( + retry=retry_if_exception_type(openai.APIError) & retry_if_not_exception_type(openai.RateLimitError), + stop=stop_after_attempt(OPENAI_RETRIES), + ) async def chat_completion(self, model: str, system: str, user: str, temperature: float = 0.2): try: messages = [SystemMessage(content=system), HumanMessage(content=user)] @@ -47,9 +49,15 @@ async def chat_completion(self, model: str, system: str, user: str, temperature: finish_reason = "completed" return resp.content, finish_reason - except (Exception) as e: - get_logger().error("Unknown error during OpenAI inference: ", e) - raise e + except openai.RateLimitError as e: + get_logger().error(f"Rate limit error during LLM inference: {e}") + raise + except openai.APIError as e: + get_logger().warning(f"Error during LLM inference: {e}") + raise + except Exception as e: + get_logger().warning(f"Unknown error during LLM inference: {e}") + raise openai.APIError from e def _create_chat(self, deployment_id=None): try: diff --git a/pr_agent/algo/ai_handlers/litellm_ai_handler.py b/pr_agent/algo/ai_handlers/litellm_ai_handler.py index 8d727b8b15..f20b03f8e7 100644 --- a/pr_agent/algo/ai_handlers/litellm_ai_handler.py +++ b/pr_agent/algo/ai_handlers/litellm_ai_handler.py @@ -3,7 +3,7 @@ import openai import requests from litellm import acompletion -from tenacity import retry, retry_if_exception_type, stop_after_attempt +from tenacity import retry, retry_if_exception_type, retry_if_not_exception_type, stop_after_attempt from pr_agent.algo import CLAUDE_EXTENDED_THINKING_MODELS, NO_SUPPORT_TEMPERATURE_MODELS, SUPPORT_REASONING_EFFORT_MODELS, USER_MESSAGE_ONLY_MODELS from pr_agent.algo.ai_handlers.base_ai_handler import BaseAiHandler @@ -274,8 +274,8 @@ def deployment_id(self): return get_settings().get("OPENAI.DEPLOYMENT_ID", None) @retry( - retry=retry_if_exception_type((openai.APIError, openai.APIConnectionError, openai.APITimeoutError)), # No retry on RateLimitError - stop=stop_after_attempt(OPENAI_RETRIES) + retry=retry_if_exception_type(openai.APIError) & retry_if_not_exception_type(openai.RateLimitError), + stop=stop_after_attempt(OPENAI_RETRIES), ) async def chat_completion(self, model: str, system: str, user: str, temperature: float = 0.2, img_path: str = None): try: @@ -371,13 +371,13 @@ async def chat_completion(self, model: str, system: str, user: str, temperature: get_logger().info(f"\nUser prompt:\n{user}") response = await acompletion(**kwargs) - except (openai.RateLimitError) as e: + except openai.RateLimitError as e: get_logger().error(f"Rate limit error during LLM inference: {e}") raise - except (openai.APIError, openai.APITimeoutError) as e: + except openai.APIError as e: get_logger().warning(f"Error during LLM inference: {e}") raise - except (Exception) as e: + except Exception as e: get_logger().warning(f"Unknown error during LLM inference: {e}") raise openai.APIError from e if response is None or len(response["choices"]) == 0: diff --git a/pr_agent/algo/ai_handlers/openai_ai_handler.py b/pr_agent/algo/ai_handlers/openai_ai_handler.py index f74444a1ca..253282b0f5 100644 --- a/pr_agent/algo/ai_handlers/openai_ai_handler.py +++ b/pr_agent/algo/ai_handlers/openai_ai_handler.py @@ -1,8 +1,8 @@ from os import environ from pr_agent.algo.ai_handlers.base_ai_handler import BaseAiHandler import openai -from openai import APIError, AsyncOpenAI, RateLimitError, Timeout -from retry import retry +from openai import AsyncOpenAI +from tenacity import retry, retry_if_exception_type, retry_if_not_exception_type, stop_after_attempt from pr_agent.algo.ai_handlers.base_ai_handler import BaseAiHandler from pr_agent.config_loader import get_settings @@ -38,8 +38,10 @@ def deployment_id(self): """ return get_settings().get("OPENAI.DEPLOYMENT_ID", None) - @retry(exceptions=(APIError, Timeout, AttributeError, RateLimitError), - tries=OPENAI_RETRIES, delay=2, backoff=2, jitter=(1, 3)) + @retry( + retry=retry_if_exception_type(openai.APIError) & retry_if_not_exception_type(openai.RateLimitError), + stop=stop_after_attempt(OPENAI_RETRIES), + ) async def chat_completion(self, model: str, system: str, user: str, temperature: float = 0.2): try: get_logger().info("System: ", system) @@ -57,12 +59,12 @@ async def chat_completion(self, model: str, system: str, user: str, temperature: get_logger().info("AI response", response=resp, messages=messages, finish_reason=finish_reason, model=model, usage=usage) return resp, finish_reason - except (APIError, Timeout) as e: - get_logger().error("Error during OpenAI inference: ", e) + except openai.RateLimitError as e: + get_logger().error(f"Rate limit error during LLM inference: {e}") raise - except (RateLimitError) as e: - get_logger().error("Rate limit error during OpenAI inference: ", e) - raise - except (Exception) as e: - get_logger().error("Unknown error during OpenAI inference: ", e) + except openai.APIError as e: + get_logger().warning(f"Error during LLM inference: {e}") raise + except Exception as e: + get_logger().warning(f"Unknown error during LLM inference: {e}") + raise openai.APIError from e From 70428ebb218cd7881b0eae6d77757475371c79c2 Mon Sep 17 00:00:00 2001 From: Tal Date: Thu, 22 May 2025 10:01:52 +0300 Subject: [PATCH 281/365] Update LICENSE --- LICENSE | 863 +++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 661 insertions(+), 202 deletions(-) diff --git a/LICENSE b/LICENSE index a5fcacc9a8..0ad25db4bd 100644 --- a/LICENSE +++ b/LICENSE @@ -1,202 +1,661 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [2023] [Codium ltd] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. From e79c34e0392f848a4a581d8bf0cecfdd3875026c Mon Sep 17 00:00:00 2001 From: jwsong98 Date: Thu, 22 May 2025 02:24:50 +0900 Subject: [PATCH 282/365] test: add test case for YAML wrapped with braces --- tests/unittest/test_try_fix_yaml.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/unittest/test_try_fix_yaml.py b/tests/unittest/test_try_fix_yaml.py index 826d731237..4233f463d2 100644 --- a/tests/unittest/test_try_fix_yaml.py +++ b/tests/unittest/test_try_fix_yaml.py @@ -83,3 +83,22 @@ def test_with_initial_yaml(self): ''' expected_output = {'code_suggestions': [{'relevant_file': 'src/index.ts\n', 'label': 'best practice\n'}, {'relevant_file': 'src/index2.ts\n', 'label': 'enhancment'}]} assert try_fix_yaml(review_text, first_key='code_suggestions', last_key='label') == expected_output + + + def test_with_brackets_yaml_content(self): + review_text = '''\ +{ +code_suggestions: +- relevant_file: | + src/index.ts + label: | + best practice + +- relevant_file: | + src/index2.ts + label: | + enhancement +} +''' + expected_output = {'code_suggestions': [{'relevant_file': 'src/index.ts\n', 'label': 'best practice\n'}, {'relevant_file': 'src/index2.ts\n', 'label': 'enhancement'}]} + assert try_fix_yaml(review_text, first_key='code_suggestions', last_key='label') == expected_output \ No newline at end of file From 1c6958069a8ec11404f118687f9334ff3e856cf3 Mon Sep 17 00:00:00 2001 From: wonjongin Date: Thu, 22 May 2025 16:32:04 +0900 Subject: [PATCH 283/365] test/wonjongin-text_get_max_tokens code generate - There is no test code for get_max_tokens of utils, so the unit test code related to the part corresponding to each test branch is added --- tests/unittest/test_get_max_tokens.py | 67 +++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 tests/unittest/test_get_max_tokens.py diff --git a/tests/unittest/test_get_max_tokens.py b/tests/unittest/test_get_max_tokens.py new file mode 100644 index 0000000000..fe2f668547 --- /dev/null +++ b/tests/unittest/test_get_max_tokens.py @@ -0,0 +1,67 @@ +import pytest +from pr_agent.algo.utils import get_max_tokens, MAX_TOKENS +import pr_agent.algo.utils as utils + +class TestGetMaxTokens: + + # Test if the file is in MAX_TOKENS + def test_model_max_tokens(self, monkeypatch): + fake_settings = type('', (), { + 'config': type('', (), { + 'custom_model_max_tokens': 0, + 'max_model_tokens': 0 + })() + })() + + monkeypatch.setattr(utils, "get_settings", lambda: fake_settings) + + model = "gpt-3.5-turbo" + expected = MAX_TOKENS[model] + + assert get_max_tokens(model) == expected + + # Test situations where the model is not registered and exists as a custom model + def test_model_has_custom(self, monkeypatch): + fake_settings = type('', (), { + 'config': type('', (), { + 'custom_model_max_tokens': 5000, + 'max_model_tokens': 0 # 제한 없음 + })() + })() + + monkeypatch.setattr(utils, "get_settings", lambda: fake_settings) + + model = "custom-model" + expected = 5000 + + assert get_max_tokens(model) == expected + + def test_model_not_max_tokens_and_not_has_custom(self, monkeypatch): + fake_settings = type('', (), { + 'config': type('', (), { + 'custom_model_max_tokens': 0, + 'max_model_tokens': 0 + })() + })() + + monkeypatch.setattr(utils, "get_settings", lambda: fake_settings) + + model = "custom-model" + + with pytest.raises(Exception): + get_max_tokens(model) + + def test_model_max_tokens_with__limit(self, monkeypatch): + fake_settings = type('', (), { + 'config': type('', (), { + 'custom_model_max_tokens': 0, + 'max_model_tokens': 10000 + })() + })() + + monkeypatch.setattr(utils, "get_settings", lambda: fake_settings) + + model = "gpt-3.5-turbo" # this model setting is 160000 + expected = 10000 + + assert get_max_tokens(model) == expected From 069f36fc1f814f3f88d9c45a9c8f47f892b54d9c Mon Sep 17 00:00:00 2001 From: jwsong98 Date: Thu, 22 May 2025 02:26:08 +0900 Subject: [PATCH 284/365] test: add test case for YAML with tab indentation in block scalar --- tests/unittest/test_try_fix_yaml.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tests/unittest/test_try_fix_yaml.py b/tests/unittest/test_try_fix_yaml.py index 4233f463d2..94df8d14ff 100644 --- a/tests/unittest/test_try_fix_yaml.py +++ b/tests/unittest/test_try_fix_yaml.py @@ -101,4 +101,20 @@ def test_with_brackets_yaml_content(self): } ''' expected_output = {'code_suggestions': [{'relevant_file': 'src/index.ts\n', 'label': 'best practice\n'}, {'relevant_file': 'src/index2.ts\n', 'label': 'enhancement'}]} - assert try_fix_yaml(review_text, first_key='code_suggestions', last_key='label') == expected_output \ No newline at end of file + assert try_fix_yaml(review_text, first_key='code_suggestions', last_key='label') == expected_output + + def test_tab_indent_yaml(self): + review_text = '''\ +code_suggestions: +- relevant_file: | + src/index.ts + label: | +\tbest practice + +- relevant_file: | + src/index2.ts + label: | + enhancement +''' + expected_output = {'code_suggestions': [{'relevant_file': 'src/index.ts\n', 'label': 'best practice\n'}, {'relevant_file': 'src/index2.ts\n', 'label': 'enhancement\n'}]} + assert try_fix_yaml(review_text, first_key='code_suggestions', last_key='label') == expected_output From 20e69c35306f637ad2c96de66e740cf6cab2a11e Mon Sep 17 00:00:00 2001 From: jwsong98 Date: Thu, 22 May 2025 02:27:41 +0900 Subject: [PATCH 285/365] test: add test case for YAML block scalar with leading plus signs in code --- tests/unittest/test_try_fix_yaml.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/unittest/test_try_fix_yaml.py b/tests/unittest/test_try_fix_yaml.py index 94df8d14ff..2d64b75520 100644 --- a/tests/unittest/test_try_fix_yaml.py +++ b/tests/unittest/test_try_fix_yaml.py @@ -118,3 +118,24 @@ def test_tab_indent_yaml(self): ''' expected_output = {'code_suggestions': [{'relevant_file': 'src/index.ts\n', 'label': 'best practice\n'}, {'relevant_file': 'src/index2.ts\n', 'label': 'enhancement\n'}]} assert try_fix_yaml(review_text, first_key='code_suggestions', last_key='label') == expected_output + + + def test_leading_plus_mark_code(self): + review_text = '''\ +code_suggestions: +- relevant_file: | + src/index.ts + label: | + best practice + existing_code: | ++ var router = createBrowserRouter([ + improved_code: | ++ const router = createBrowserRouter([ +''' + expected_output = {'code_suggestions': [{ + 'relevant_file': 'src/index.ts\n', + 'label': 'best practice\n', + 'existing_code': 'var router = createBrowserRouter([\n', + 'improved_code': 'const router = createBrowserRouter([\n' + }]} + assert try_fix_yaml(review_text, first_key='code_suggestions', last_key='improved_code') == expected_output From f10c38940686f78611656ac6e1bd03e76291837f Mon Sep 17 00:00:00 2001 From: jwsong98 Date: Thu, 22 May 2025 02:29:05 +0900 Subject: [PATCH 286/365] test: add test cases for YAML block scalar with inconsistent and insufficient indentation --- tests/unittest/test_try_fix_yaml.py | 79 +++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/tests/unittest/test_try_fix_yaml.py b/tests/unittest/test_try_fix_yaml.py index 2d64b75520..09bdbff886 100644 --- a/tests/unittest/test_try_fix_yaml.py +++ b/tests/unittest/test_try_fix_yaml.py @@ -139,3 +139,82 @@ def test_leading_plus_mark_code(self): 'improved_code': 'const router = createBrowserRouter([\n' }]} assert try_fix_yaml(review_text, first_key='code_suggestions', last_key='improved_code') == expected_output + + + def test_inconsistent_indentation_in_block_scalar_yaml(self): + """ + This test case represents a situation where the AI outputs the opening '{' with 5 spaces + (resulting in an inferred indent level of 5), while the closing '}' is output with only 4 spaces. + This inconsistency makes it impossible for the YAML parser to automatically determine the correct + indent level, causing a parsing failure. + + The root cause may be the LLM miscounting spaces or misunderstanding the active block scalar context + while generating YAML output. + """ + + review_text = '''\ +code_suggestions: +- relevant_file: | + tsconfig.json + existing_code: | + { + "key1": "value1", + "key2": { + "subkey": "value" + } + } +''' + expected_json = '''\ + { + "key1": "value1", + "key2": { + "subkey": "value" + } +} +''' + expected_output = { + 'code_suggestions': [{ + 'relevant_file': 'tsconfig.json\n', + 'existing_code': expected_json + }] + } + assert try_fix_yaml(review_text, first_key='code_suggestions', last_key='existing_code') == expected_output + + + def test_inconsistent_and_insufficient_indentation_in_block_scalar_yaml(self): + """ + This test case reproduces a YAML parsing failure where the block scalar content + generated by the AI includes inconsistent and insufficient indentation levels. + + The root cause may be the LLM miscounting spaces or misunderstanding the active block scalar context + while generating YAML output. + """ + + review_text = '''\ +code_suggestions: +- relevant_file: | + tsconfig.json + existing_code: | + { + "key1": "value1", + "key2": { + "subkey": "value" + } + } +''' + expected_json = '''\ +{ + "key1": "value1", + "key2": { + "subkey": "value" + } +} +''' + expected_output = { + 'code_suggestions': [{ + 'relevant_file': 'tsconfig.json\n', + 'existing_code': expected_json + }] + } + assert try_fix_yaml(review_text, first_key='code_suggestions', last_key='existing_code') == expected_output + From 684a438167fd92402bb7968a6ddd2d53256b636e Mon Sep 17 00:00:00 2001 From: jwsong98 Date: Thu, 22 May 2025 16:18:45 +0900 Subject: [PATCH 287/365] test: add test case for fixing incorrect indentation in YAML code block scalar --- tests/unittest/test_try_fix_yaml.py | 33 +++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/unittest/test_try_fix_yaml.py b/tests/unittest/test_try_fix_yaml.py index 09bdbff886..b23e9dd32a 100644 --- a/tests/unittest/test_try_fix_yaml.py +++ b/tests/unittest/test_try_fix_yaml.py @@ -218,3 +218,36 @@ def test_inconsistent_and_insufficient_indentation_in_block_scalar_yaml(self): } assert try_fix_yaml(review_text, first_key='code_suggestions', last_key='existing_code') == expected_output + + def test_wrong_indentation_code_block_scalar(self): + review_text = '''\ +code_suggestions: +- relevant_file: | + a.c + existing_code: | + int sum(int a, int b) { + return a + b; + } + + int sub(int a, int b) { + return a - b; + } +''' + expected_code_block = '''\ +int sum(int a, int b) { + return a + b; +} + +int sub(int a, int b) { + return a - b; +} +''' + expected_output = { + "code_suggestions": [ + { + "relevant_file": "a.c\n", + "existing_code": expected_code_block + } + ] + } + assert try_fix_yaml(review_text, first_key='code_suggestions', last_key='existing_code') == expected_output From f6a9d3c2ccea6cd7eee881a6be42136c64fa57f5 Mon Sep 17 00:00:00 2001 From: jwsong98 Date: Thu, 22 May 2025 16:55:52 +0900 Subject: [PATCH 288/365] fix:typo in test_with_initial_yaml, test_no_initial_yaml --- tests/unittest/test_try_fix_yaml.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/unittest/test_try_fix_yaml.py b/tests/unittest/test_try_fix_yaml.py index b23e9dd32a..98773c819f 100644 --- a/tests/unittest/test_try_fix_yaml.py +++ b/tests/unittest/test_try_fix_yaml.py @@ -53,12 +53,12 @@ def test_no_initial_yaml(self): - relevant_file: | src/index2.ts label: | - enhancment + enhancement ``` We can further improve the code by using the `const` keyword instead of `var` in the `src/index.ts` file. ''' - expected_output = {'code_suggestions': [{'relevant_file': 'src/index.ts\n', 'label': 'best practice\n'}, {'relevant_file': 'src/index2.ts\n', 'label': 'enhancment'}]} + expected_output = {'code_suggestions': [{'relevant_file': 'src/index.ts\n', 'label': 'best practice\n'}, {'relevant_file': 'src/index2.ts\n', 'label': 'enhancement'}]} assert try_fix_yaml(review_text, first_key='code_suggestions', last_key='label') == expected_output @@ -76,12 +76,12 @@ def test_with_initial_yaml(self): - relevant_file: | src/index2.ts label: | - enhancment + enhancement ``` We can further improve the code by using the `const` keyword instead of `var` in the `src/index.ts` file. ''' - expected_output = {'code_suggestions': [{'relevant_file': 'src/index.ts\n', 'label': 'best practice\n'}, {'relevant_file': 'src/index2.ts\n', 'label': 'enhancment'}]} + expected_output = {'code_suggestions': [{'relevant_file': 'src/index.ts\n', 'label': 'best practice\n'}, {'relevant_file': 'src/index2.ts\n', 'label': 'enhancement'}]} assert try_fix_yaml(review_text, first_key='code_suggestions', last_key='label') == expected_output From 48c29c9ffa0c9329b56568ecbf45044eb6ad576b Mon Sep 17 00:00:00 2001 From: Pinyoo Thotaboot Date: Thu, 22 May 2025 14:59:29 +0700 Subject: [PATCH 289/365] Add null check --- pr_agent/git_providers/gitea_provider.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pr_agent/git_providers/gitea_provider.py b/pr_agent/git_providers/gitea_provider.py index 9f2715564b..5eef4362d4 100644 --- a/pr_agent/git_providers/gitea_provider.py +++ b/pr_agent/git_providers/gitea_provider.py @@ -542,6 +542,10 @@ def get_pr_branch(self) -> str: if not self.pr: self.logger.error("Failed to get PR branch") return "" + + if not self.pr.head: + self.logger.error("PR head not found") + return "" return self.pr.head.ref if self.pr.head.ref else "" From 000f0ba93ebd123338fc7ec67387839cbefbbb05 Mon Sep 17 00:00:00 2001 From: Pinyoo Thotaboot Date: Thu, 22 May 2025 15:01:08 +0700 Subject: [PATCH 290/365] Fixed ensure SHA --- pr_agent/git_providers/gitea_provider.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/git_providers/gitea_provider.py b/pr_agent/git_providers/gitea_provider.py index 5eef4362d4..91100cab97 100644 --- a/pr_agent/git_providers/gitea_provider.py +++ b/pr_agent/git_providers/gitea_provider.py @@ -102,7 +102,7 @@ def __add_file_content(self): if not is_valid_file(file_path): continue - if file_path: + if file_path and self.sha: try: content = self.repo_api.get_file_content( owner=self.owner, From 0f893bc4926cf0296c7e29c7153b67ecb321346c Mon Sep 17 00:00:00 2001 From: Pinyoo Thotaboot Date: Thu, 22 May 2025 15:03:15 +0700 Subject: [PATCH 291/365] Fixed webhook security concern --- pr_agent/servers/gitea_app.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pr_agent/servers/gitea_app.py b/pr_agent/servers/gitea_app.py index 4df8b84ca8..85399f2cc7 100644 --- a/pr_agent/servers/gitea_app.py +++ b/pr_agent/servers/gitea_app.py @@ -47,6 +47,10 @@ async def get_body(request: Request): if webhook_secret: body_bytes = await request.body() signature_header = request.headers.get('x-gitea-signature', None) + if not signature_header: + get_logger().error("Missing signature header") + raise HTTPException(status_code=400, detail="Missing signature header") + verify_signature(body_bytes, webhook_secret, f"sha256={signature_header}") return body From 162cc9d833612085da746f3fa94631187fee6fb2 Mon Sep 17 00:00:00 2001 From: Pinyoo Thotaboot Date: Thu, 22 May 2025 15:06:35 +0700 Subject: [PATCH 292/365] Fixed error propagation --- pr_agent/git_providers/gitea_provider.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pr_agent/git_providers/gitea_provider.py b/pr_agent/git_providers/gitea_provider.py index 91100cab97..4ac2e63b96 100644 --- a/pr_agent/git_providers/gitea_provider.py +++ b/pr_agent/git_providers/gitea_provider.py @@ -757,8 +757,9 @@ def get_pull_request_diff(self, owner: str, repo: str, pr_number: int) -> str: raw_data = response[0].read() return raw_data.decode('utf-8') else: - self.logger.error("Unexpected response format") - return "" + error_msg = f"Unexpected response format received from API: {type(response)}" + self.logger.error(error_msg) + return RuntimeError(error_msg) except ApiException as e: self.logger.error(f"Error getting diff: {str(e)}") From 10703a9098ca2f285f6545f09025381ce426e125 Mon Sep 17 00:00:00 2001 From: Hiroyuki Otomo Date: Fri, 23 May 2025 14:16:44 +0900 Subject: [PATCH 293/365] feat: add support for Claude 4 --- pr_agent/algo/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pr_agent/algo/__init__.py b/pr_agent/algo/__init__.py index e38bd713fa..f9a055f1b7 100644 --- a/pr_agent/algo/__init__.py +++ b/pr_agent/algo/__init__.py @@ -53,9 +53,11 @@ 'vertex_ai/claude-3-5-haiku@20241022': 100000, 'vertex_ai/claude-3-sonnet@20240229': 100000, 'vertex_ai/claude-3-opus@20240229': 100000, + 'vertex_ai/claude-opus-4@20250514': 200000, 'vertex_ai/claude-3-5-sonnet@20240620': 100000, 'vertex_ai/claude-3-5-sonnet-v2@20241022': 100000, 'vertex_ai/claude-3-7-sonnet@20250219': 200000, + 'vertex_ai/claude-sonnet-4@20250514': 200000, 'vertex_ai/gemini-1.5-pro': 1048576, 'vertex_ai/gemini-2.5-pro-preview-03-25': 1048576, 'vertex_ai/gemini-2.5-pro-preview-05-06': 1048576, @@ -74,20 +76,24 @@ 'anthropic.claude-v1': 100000, 'anthropic.claude-v2': 100000, 'anthropic/claude-3-opus-20240229': 100000, + 'anthropic/claude-opus-4-20250514': 200000, 'anthropic/claude-3-5-sonnet-20240620': 100000, 'anthropic/claude-3-5-sonnet-20241022': 100000, 'anthropic/claude-3-7-sonnet-20250219': 200000, + 'anthropic/claude-sonnet-4-20250514': 200000, 'claude-3-7-sonnet-20250219': 200000, 'anthropic/claude-3-5-haiku-20241022': 100000, 'bedrock/anthropic.claude-instant-v1': 100000, 'bedrock/anthropic.claude-v2': 100000, 'bedrock/anthropic.claude-v2:1': 100000, 'bedrock/anthropic.claude-3-sonnet-20240229-v1:0': 100000, + 'bedrock/anthropic.claude-opus-4-20250514-v1:0': 100000, 'bedrock/anthropic.claude-3-haiku-20240307-v1:0': 100000, 'bedrock/anthropic.claude-3-5-haiku-20241022-v1:0': 100000, 'bedrock/anthropic.claude-3-5-sonnet-20240620-v1:0': 100000, 'bedrock/anthropic.claude-3-5-sonnet-20241022-v2:0': 100000, 'bedrock/anthropic.claude-3-7-sonnet-20250219-v1:0': 200000, + 'bedrock/anthropic.claude-sonnet-4-20250514-v1:0': 200000, "bedrock/us.anthropic.claude-3-5-sonnet-20241022-v2:0": 100000, "bedrock/us.anthropic.claude-3-7-sonnet-20250219-v1:0": 200000, 'claude-3-5-sonnet': 100000, From c10be827a158a6a2f4dcec90036fa0d9f2309889 Mon Sep 17 00:00:00 2001 From: Hiroyuki Otomo Date: Fri, 23 May 2025 14:23:51 +0900 Subject: [PATCH 294/365] chore: update the version of anthropic sdk --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 95145ec98c..864a3d48ea 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ aiohttp==3.9.5 -anthropic>=0.48 +anthropic>=0.52.0 #anthropic[vertex]==0.47.1 atlassian-python-api==3.41.4 azure-devops==7.1.0b3 From c4358d1ca0d6fc7f0040aa2e989e6a708128e28f Mon Sep 17 00:00:00 2001 From: Hiroyuki Otomo Date: Fri, 23 May 2025 19:18:09 +0900 Subject: [PATCH 295/365] chore: update the version of litellm to 1.70.4 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 864a3d48ea..d1587f25f2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,7 +13,7 @@ google-cloud-aiplatform==1.38.0 google-generativeai==0.8.3 google-cloud-storage==2.10.0 Jinja2==3.1.2 -litellm==1.69.3 +litellm==1.70.4 loguru==0.7.2 msrest==0.7.1 openai>=1.55.3 From 1f836e405d4a7a1313a94d7bf3cb7b1aeb48be5a Mon Sep 17 00:00:00 2001 From: Hiroyuki Otomo Date: Sat, 24 May 2025 09:45:27 +0900 Subject: [PATCH 296/365] fix: reflect comments --- pr_agent/algo/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pr_agent/algo/__init__.py b/pr_agent/algo/__init__.py index f9a055f1b7..dbda2f431d 100644 --- a/pr_agent/algo/__init__.py +++ b/pr_agent/algo/__init__.py @@ -87,15 +87,17 @@ 'bedrock/anthropic.claude-v2': 100000, 'bedrock/anthropic.claude-v2:1': 100000, 'bedrock/anthropic.claude-3-sonnet-20240229-v1:0': 100000, - 'bedrock/anthropic.claude-opus-4-20250514-v1:0': 100000, + 'bedrock/anthropic.claude-opus-4-20250514-v1:0': 200000, 'bedrock/anthropic.claude-3-haiku-20240307-v1:0': 100000, 'bedrock/anthropic.claude-3-5-haiku-20241022-v1:0': 100000, 'bedrock/anthropic.claude-3-5-sonnet-20240620-v1:0': 100000, 'bedrock/anthropic.claude-3-5-sonnet-20241022-v2:0': 100000, 'bedrock/anthropic.claude-3-7-sonnet-20250219-v1:0': 200000, 'bedrock/anthropic.claude-sonnet-4-20250514-v1:0': 200000, + "bedrock/us.anthropic.claude-opus-4-20250514-v1:0": 200000, "bedrock/us.anthropic.claude-3-5-sonnet-20241022-v2:0": 100000, "bedrock/us.anthropic.claude-3-7-sonnet-20250219-v1:0": 200000, + "bedrock/us.anthropic.claude-sonnet-4-20250514-v1:0": 200000, 'claude-3-5-sonnet': 100000, 'groq/meta-llama/llama-4-scout-17b-16e-instruct': 131072, 'groq/meta-llama/llama-4-maverick-17b-128e-instruct': 131072, From 1bc0d488d5ee5afafb4c8e5ed601ada9922e4b78 Mon Sep 17 00:00:00 2001 From: TaskerJang Date: Thu, 22 May 2025 14:33:36 +0900 Subject: [PATCH 297/365] test: add comprehensive unit tests for clip_tokens function - Add 21 test cases covering edge cases and parameter combinations - Include tests for Unicode, special characters, and long text processing - Verify exception handling and backward compatibility --- tests/unittest/test_clip_tokens.py | 301 ++++++++++++++++++++++++++++- 1 file changed, 295 insertions(+), 6 deletions(-) diff --git a/tests/unittest/test_clip_tokens.py b/tests/unittest/test_clip_tokens.py index 79de629400..f791b92fe5 100644 --- a/tests/unittest/test_clip_tokens.py +++ b/tests/unittest/test_clip_tokens.py @@ -1,13 +1,302 @@ - -# Generated by CodiumAI - import pytest - +from unittest.mock import patch, MagicMock from pr_agent.algo.utils import clip_tokens +from pr_agent.algo.token_handler import TokenEncoder class TestClipTokens: - def test_clip(self): + """Comprehensive test suite for the clip_tokens function.""" + + def test_empty_input_text(self): + """Test that empty input returns empty string.""" + assert clip_tokens("", 10) == "" + assert clip_tokens(None, 10) == None + + def test_text_under_token_limit(self): + """Test that text under the token limit is returned unchanged.""" + text = "Short text" + max_tokens = 100 + result = clip_tokens(text, max_tokens) + assert result == text + + def test_text_exactly_at_token_limit(self): + """Test text that is exactly at the token limit.""" + text = "This is exactly at the limit" + # Mock the token encoder to return exact limit + with patch.object(TokenEncoder, 'get_token_encoder') as mock_encoder: + mock_tokenizer = MagicMock() + mock_tokenizer.encode.return_value = [1] * 10 # Exactly 10 tokens + mock_encoder.return_value = mock_tokenizer + + result = clip_tokens(text, 10) + assert result == text + + def test_text_over_token_limit_with_three_dots(self): + """Test text over token limit with three dots addition.""" + text = "This is a longer text that should be clipped when it exceeds the token limit" + max_tokens = 5 + + with patch.object(TokenEncoder, 'get_token_encoder') as mock_encoder: + mock_tokenizer = MagicMock() + mock_tokenizer.encode.return_value = [1] * 20 # 20 tokens + mock_encoder.return_value = mock_tokenizer + + result = clip_tokens(text, max_tokens) + assert result.endswith("\n...(truncated)") + assert len(result) < len(text) + + def test_text_over_token_limit_without_three_dots(self): + """Test text over token limit without three dots addition.""" + text = "This is a longer text that should be clipped" + max_tokens = 5 + + with patch.object(TokenEncoder, 'get_token_encoder') as mock_encoder: + mock_tokenizer = MagicMock() + mock_tokenizer.encode.return_value = [1] * 20 # 20 tokens + mock_encoder.return_value = mock_tokenizer + + result = clip_tokens(text, max_tokens, add_three_dots=False) + assert not result.endswith("\n...(truncated)") + assert len(result) < len(text) + + def test_negative_max_tokens(self): + """Test that negative max_tokens returns empty string.""" + text = "Some text" + result = clip_tokens(text, -1) + assert result == "" + + result = clip_tokens(text, -100) + assert result == "" + + def test_zero_max_tokens(self): + """Test that zero max_tokens returns empty string.""" + text = "Some text" + result = clip_tokens(text, 0) + assert result == "" + + def test_delete_last_line_functionality(self): + """Test the delete_last_line parameter functionality.""" + text = "Line 1\nLine 2\nLine 3\nLine 4" + max_tokens = 5 + + with patch.object(TokenEncoder, 'get_token_encoder') as mock_encoder: + mock_tokenizer = MagicMock() + mock_tokenizer.encode.return_value = [1] * 20 # 20 tokens + mock_encoder.return_value = mock_tokenizer + + # Without delete_last_line + result_normal = clip_tokens(text, max_tokens, delete_last_line=False) + + # With delete_last_line + result_deleted = clip_tokens(text, max_tokens, delete_last_line=True) + + # The result with delete_last_line should be shorter or equal + assert len(result_deleted) <= len(result_normal) + + def test_pre_computed_num_input_tokens(self): + """Test using pre-computed num_input_tokens parameter.""" + text = "This is a test text" + max_tokens = 10 + num_input_tokens = 15 + + # Should not call the encoder when num_input_tokens is provided + with patch.object(TokenEncoder, 'get_token_encoder') as mock_encoder: + mock_encoder.return_value = None # Should not be called + + result = clip_tokens(text, max_tokens, num_input_tokens=num_input_tokens) + assert result.endswith("\n...(truncated)") + mock_encoder.assert_not_called() + + def test_pre_computed_tokens_under_limit(self): + """Test pre-computed tokens under the limit.""" + text = "Short text" + max_tokens = 20 + num_input_tokens = 5 + + with patch.object(TokenEncoder, 'get_token_encoder') as mock_encoder: + mock_encoder.return_value = None # Should not be called + + result = clip_tokens(text, max_tokens, num_input_tokens=num_input_tokens) + assert result == text + mock_encoder.assert_not_called() + + def test_special_characters_and_unicode(self): + """Test text with special characters and Unicode content.""" + text = "Special chars: @#$%^&*()_+ áéíóú 中문 🚀 emoji" + max_tokens = 5 + + with patch.object(TokenEncoder, 'get_token_encoder') as mock_encoder: + mock_tokenizer = MagicMock() + mock_tokenizer.encode.return_value = [1] * 20 # 20 tokens + mock_encoder.return_value = mock_tokenizer + + result = clip_tokens(text, max_tokens) + assert isinstance(result, str) + assert len(result) < len(text) + + def test_multiline_text_handling(self): + """Test handling of multiline text.""" + text = "Line 1\nLine 2\nLine 3\nLine 4\nLine 5" + max_tokens = 5 + + with patch.object(TokenEncoder, 'get_token_encoder') as mock_encoder: + mock_tokenizer = MagicMock() + mock_tokenizer.encode.return_value = [1] * 20 # 20 tokens + mock_encoder.return_value = mock_tokenizer + + result = clip_tokens(text, max_tokens) + assert isinstance(result, str) + + def test_very_long_text(self): + """Test with very long text.""" + text = "A" * 10000 # Very long text + max_tokens = 10 + + with patch.object(TokenEncoder, 'get_token_encoder') as mock_encoder: + mock_tokenizer = MagicMock() + mock_tokenizer.encode.return_value = [1] * 5000 # Many tokens + mock_encoder.return_value = mock_tokenizer + + result = clip_tokens(text, max_tokens) + assert len(result) < len(text) + assert result.endswith("\n...(truncated)") + + def test_encoder_exception_handling(self): + """Test handling of encoder exceptions.""" + text = "Test text" + max_tokens = 10 + + with patch.object(TokenEncoder, 'get_token_encoder') as mock_encoder: + mock_encoder.side_effect = Exception("Encoder error") + + # Should return original text when encoder fails + result = clip_tokens(text, max_tokens) + assert result == text + + def test_zero_division_scenario(self): + """Test scenario that could lead to division by zero.""" + text = "Test" + max_tokens = 10 + + with patch.object(TokenEncoder, 'get_token_encoder') as mock_encoder: + mock_tokenizer = MagicMock() + mock_tokenizer.encode.return_value = [] # Empty tokens (could cause division by zero) + mock_encoder.return_value = mock_tokenizer + + result = clip_tokens(text, max_tokens) + # Should handle gracefully and return original text + assert result == text + + def test_various_edge_cases(self): + """Test various edge cases.""" + # Single character + assert clip_tokens("A", 1000) == "A" + + # Only whitespace + text = " \n \t " + with patch.object(TokenEncoder, 'get_token_encoder') as mock_encoder: + mock_tokenizer = MagicMock() + mock_tokenizer.encode.return_value = [1] * 10 + mock_encoder.return_value = mock_tokenizer + + result = clip_tokens(text, 5) + assert isinstance(result, str) + + # Text with only newlines + text = "\n\n\n\n" + with patch.object(TokenEncoder, 'get_token_encoder') as mock_encoder: + mock_tokenizer = MagicMock() + mock_tokenizer.encode.return_value = [1] * 10 + mock_encoder.return_value = mock_tokenizer + + result = clip_tokens(text, 2, delete_last_line=True) + assert isinstance(result, str) + + def test_parameter_combinations(self): + """Test different parameter combinations.""" + text = "Multi\nline\ntext\nfor\ntesting" + max_tokens = 5 + + with patch.object(TokenEncoder, 'get_token_encoder') as mock_encoder: + mock_tokenizer = MagicMock() + mock_tokenizer.encode.return_value = [1] * 20 + mock_encoder.return_value = mock_tokenizer + + # Test all combinations + combinations = [ + (True, True), # add_three_dots=True, delete_last_line=True + (True, False), # add_three_dots=True, delete_last_line=False + (False, True), # add_three_dots=False, delete_last_line=True + (False, False), # add_three_dots=False, delete_last_line=False + ] + + for add_dots, delete_line in combinations: + result = clip_tokens(text, max_tokens, + add_three_dots=add_dots, + delete_last_line=delete_line) + assert isinstance(result, str) + if add_dots and len(result) > 0: + assert result.endswith("\n...(truncated)") or result == text + + def test_num_output_chars_zero_scenario(self): + """Test scenario where num_output_chars becomes zero or negative.""" + text = "Short" + max_tokens = 1 + + with patch.object(TokenEncoder, 'get_token_encoder') as mock_encoder: + mock_tokenizer = MagicMock() + mock_tokenizer.encode.return_value = [1] * 1000 # Many tokens for short text + mock_encoder.return_value = mock_tokenizer + + result = clip_tokens(text, max_tokens) + # When num_output_chars is 0 or negative, should return empty string + assert result == "" + + def test_logging_on_exception(self): + """Test that exceptions are properly logged.""" + text = "Test text" + max_tokens = 10 + + # Patch the logger at the module level where it's imported + with patch('pr_agent.algo.utils.get_logger') as mock_logger: + mock_log_instance = MagicMock() + mock_logger.return_value = mock_log_instance + + with patch.object(TokenEncoder, 'get_token_encoder') as mock_encoder: + mock_encoder.side_effect = Exception("Test exception") + + result = clip_tokens(text, max_tokens) + + # Should log the warning + mock_log_instance.warning.assert_called_once() + # Should return original text + assert result == text + + def test_factor_safety_calculation(self): + """Test that the 0.9 factor (10% reduction) works correctly.""" + text = "Test text that should be reduced by 10 percent for safety" + max_tokens = 10 + + with patch.object(TokenEncoder, 'get_token_encoder') as mock_encoder: + mock_tokenizer = MagicMock() + mock_tokenizer.encode.return_value = [1] * 20 # 20 tokens + mock_encoder.return_value = mock_tokenizer + + result = clip_tokens(text, max_tokens) + + # The result should be shorter due to the 0.9 factor + # Characters per token = len(text) / 20 + # Expected chars = int(0.9 * (len(text) / 20) * 10) + expected_chars = int(0.9 * (len(text) / 20) * 10) + + # Result should be around expected_chars length (plus truncation text) + if result.endswith("\n...(truncated)"): + actual_content = result[:-len("\n...(truncated)")] + assert len(actual_content) <= expected_chars + 5 # Some tolerance + + # Test the original basic functionality to ensure backward compatibility + def test_clip_original_functionality(self): + """Test original functionality from the existing test.""" text = "line1\nline2\nline3\nline4\nline5\nline6" max_tokens = 25 result = clip_tokens(text, max_tokens) @@ -16,4 +305,4 @@ def test_clip(self): max_tokens = 10 result = clip_tokens(text, max_tokens) expected_results = 'line1\nline2\nline3\n\n...(truncated)' - assert result == expected_results + assert result == expected_results \ No newline at end of file From e2586cb64ad2b5328d8bddc4b15c0eeadca840f4 Mon Sep 17 00:00:00 2001 From: TaskerJang Date: Thu, 22 May 2025 14:33:45 +0900 Subject: [PATCH 298/365] docs: improve clip_tokens function docstring and add examples --- pr_agent/algo/utils.py | 60 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/pr_agent/algo/utils.py b/pr_agent/algo/utils.py index 780c7953bf..3e3103ad6e 100644 --- a/pr_agent/algo/utils.py +++ b/pr_agent/algo/utils.py @@ -945,12 +945,66 @@ def clip_tokens(text: str, max_tokens: int, add_three_dots=True, num_input_token """ Clip the number of tokens in a string to a maximum number of tokens. + This function limits text to a specified token count by calculating the approximate + character-to-token ratio and truncating the text accordingly. A safety factor of 0.9 + (10% reduction) is applied to ensure the result stays within the token limit. + Args: - text (str): The string to clip. + text (str): The string to clip. If empty or None, returns the input unchanged. max_tokens (int): The maximum number of tokens allowed in the string. - add_three_dots (bool, optional): A boolean indicating whether to add three dots at the end of the clipped + If negative, returns an empty string. + add_three_dots (bool, optional): Whether to add "\\n...(truncated)" at the end + of the clipped text to indicate truncation. + Defaults to True. + num_input_tokens (int, optional): Pre-computed number of tokens in the input text. + If provided, skips token encoding step for efficiency. + If None, tokens will be counted using TokenEncoder. + Defaults to None. + delete_last_line (bool, optional): Whether to remove the last line from the + clipped content before adding truncation indicator. + Useful for ensuring clean breaks at line boundaries. + Defaults to False. + Returns: - str: The clipped string. + str: The clipped string. Returns original text if: + - Text is empty/None + - Token count is within limit + - An error occurs during processing + + Returns empty string if max_tokens <= 0. + + Examples: + Basic usage: + >>> text = "This is a sample text that might be too long" + >>> result = clip_tokens(text, max_tokens=10) + >>> print(result) + This is a sample... + (truncated) + + Without truncation indicator: + >>> result = clip_tokens(text, max_tokens=10, add_three_dots=False) + >>> print(result) + This is a sample + + With pre-computed token count: + >>> result = clip_tokens(text, max_tokens=5, num_input_tokens=15) + >>> print(result) + This... + (truncated) + + With line deletion: + >>> multiline_text = "Line 1\\nLine 2\\nLine 3" + >>> result = clip_tokens(multiline_text, max_tokens=3, delete_last_line=True) + >>> print(result) + Line 1 + Line 2 + ... + (truncated) + + Notes: + The function uses a safety factor of 0.9 (10% reduction) to ensure the + result stays within the token limit, as character-to-token ratios can vary. + If token encoding fails, the original text is returned with a warning logged. """ if not text: return text From 95c94b80a271d87a007acbc72ecfc6157c4636aa Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Sat, 24 May 2025 14:22:55 +0800 Subject: [PATCH 299/365] Add Grok-3 non-beta model IDs --- pr_agent/algo/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pr_agent/algo/__init__.py b/pr_agent/algo/__init__.py index e38bd713fa..e659e54014 100644 --- a/pr_agent/algo/__init__.py +++ b/pr_agent/algo/__init__.py @@ -102,9 +102,13 @@ 'xai/grok-2': 131072, 'xai/grok-2-1212': 131072, 'xai/grok-2-latest': 131072, + 'xai/grok-3': 131072, 'xai/grok-3-beta': 131072, + 'xai/grok-3-fast': 131072, 'xai/grok-3-fast-beta': 131072, + 'xai/grok-3-mini': 131072, 'xai/grok-3-mini-beta': 131072, + 'xai/grok-3-mini-fast': 131072, 'xai/grok-3-mini-fast-beta': 131072, 'ollama/llama3': 4096, 'watsonx/meta-llama/llama-3-8b-instruct': 4096, From 453f8e19f3e020d18bf34c1b4b974d44fafd7f19 Mon Sep 17 00:00:00 2001 From: Tal Date: Sat, 24 May 2025 16:12:37 +0300 Subject: [PATCH 300/365] Update docs/docs/usage-guide/changing_a_model.md Co-authored-by: qodo-merge-pro-for-open-source[bot] <189517486+qodo-merge-pro-for-open-source[bot]@users.noreply.github.com> --- docs/docs/usage-guide/changing_a_model.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/usage-guide/changing_a_model.md b/docs/docs/usage-guide/changing_a_model.md index e7f9e16ae5..bfeb7c3a55 100644 --- a/docs/docs/usage-guide/changing_a_model.md +++ b/docs/docs/usage-guide/changing_a_model.md @@ -226,7 +226,7 @@ To use Amazon Bedrock and its foundational models, add the below configuration: model="bedrock/anthropic.claude-3-5-sonnet-20240620-v1:0" fallback_models=["bedrock/anthropic.claude-3-5-sonnet-20240620-v1:0"] -[aws] # in .secrets.toml +[aws] AWS_ACCESS_KEY_ID="..." AWS_SECRET_ACCESS_KEY="..." AWS_REGION_NAME="..." From 16b9ccd0259a8d3a32db2366516b431aa5b20153 Mon Sep 17 00:00:00 2001 From: soprue Date: Sat, 24 May 2025 23:43:16 +0900 Subject: [PATCH 301/365] feat: conditionally include diagram in output example --- pr_agent/settings/pr_description_prompts.toml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pr_agent/settings/pr_description_prompts.toml b/pr_agent/settings/pr_description_prompts.toml index 73ec8459ed..d37839d547 100644 --- a/pr_agent/settings/pr_description_prompts.toml +++ b/pr_agent/settings/pr_description_prompts.toml @@ -60,6 +60,14 @@ type: - ... description: | ... +{%- if add_diagram %} +diagram: | + sequenceDiagram + participant A as ComponentA + participant B as ComponentB + A->>B: functionCall() + B-->>A: response +{%- endif %} title: | ... {%- if enable_semantic_files_types %} @@ -141,6 +149,14 @@ type: - ... description: | ... +{%- if add_diagram %} +diagram: | + sequenceDiagram + participant A as ComponentA + participant B as ComponentB + A->>B: functionCall() + B-->>A: response +{%- endif %} title: | ... {%- if enable_semantic_files_types %} From 4047e71268c040b1d4456deccfaf5f433121dff1 Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Sat, 24 May 2025 23:52:18 +0800 Subject: [PATCH 302/365] Complete Gemini 2.5 Flash model info --- pr_agent/algo/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pr_agent/algo/__init__.py b/pr_agent/algo/__init__.py index 23c9795b7c..a2b35e717e 100644 --- a/pr_agent/algo/__init__.py +++ b/pr_agent/algo/__init__.py @@ -64,10 +64,13 @@ 'vertex_ai/gemini-1.5-flash': 1048576, 'vertex_ai/gemini-2.0-flash': 1048576, 'vertex_ai/gemini-2.5-flash-preview-04-17': 1048576, + 'vertex_ai/gemini-2.5-flash-preview-05-20': 1048576, 'vertex_ai/gemma2': 8200, 'gemini/gemini-1.5-pro': 1048576, 'gemini/gemini-1.5-flash': 1048576, 'gemini/gemini-2.0-flash': 1048576, + 'gemini/gemini-2.5-flash-preview-04-17': 1048576, + 'gemini/gemini-2.5-flash-preview-05-20': 1048576, 'gemini/gemini-2.5-pro-preview-03-25': 1048576, 'gemini/gemini-2.5-pro-preview-05-06': 1048576, 'codechat-bison': 6144, From f5bb508736dfada4b1ef12608b3a8339f4a8bab7 Mon Sep 17 00:00:00 2001 From: TaskerJang Date: Sun, 25 May 2025 07:55:18 +0900 Subject: [PATCH 303/365] fix: use identity check for None comparison in clip_tokens tests - Replace `== None` with `is None` in test_empty_input_text method - Follow Python best practice for None comparisons as recommended in code review - Address feedback from PR #1816 review comment Co-authored-by: mrT23 --- tests/unittest/test_clip_tokens.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unittest/test_clip_tokens.py b/tests/unittest/test_clip_tokens.py index f791b92fe5..a42ef9294d 100644 --- a/tests/unittest/test_clip_tokens.py +++ b/tests/unittest/test_clip_tokens.py @@ -10,7 +10,7 @@ class TestClipTokens: def test_empty_input_text(self): """Test that empty input returns empty string.""" assert clip_tokens("", 10) == "" - assert clip_tokens(None, 10) == None + assert clip_tokens(None, 10) is None def test_text_under_token_limit(self): """Test that text under the token limit is returned unchanged.""" From d62cbb2fc4058e998c745f76c33b76e6717d9fe7 Mon Sep 17 00:00:00 2001 From: yujindonut Date: Sun, 25 May 2025 10:28:50 +0900 Subject: [PATCH 304/365] feat: add add_diagram flag in configuration.toml --- pr_agent/settings/configuration.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index c64379312a..15c99a3276 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -103,6 +103,7 @@ enable_pr_type=true final_update_message = true enable_help_text=false enable_help_comment=true +add_diagram=false # describe as comment publish_description_as_comment=false publish_description_as_comment_persistent=true From 5a0affd6cba57b8e08bbc90dc39719de7d214b33 Mon Sep 17 00:00:00 2001 From: ssunbear Date: Sun, 25 May 2025 11:08:52 +0900 Subject: [PATCH 305/365] feat: add add_diagram configuration option to PR description --- pr_agent/tools/pr_description.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pr_agent/tools/pr_description.py b/pr_agent/tools/pr_description.py index df82db6727..bc8983f473 100644 --- a/pr_agent/tools/pr_description.py +++ b/pr_agent/tools/pr_description.py @@ -73,6 +73,7 @@ def __init__(self, pr_url: str, args: list = None, "related_tickets": "", "include_file_summary_changes": len(self.git_provider.get_diff_files()) <= self.COLLAPSIBLE_FILE_LIST_THRESHOLD, 'duplicate_prompt_examples': get_settings().config.get('duplicate_prompt_examples', False), + "add_diagram": get_settings().config.get('pr_description.add_diagram', True), } self.user_description = self.git_provider.get_user_description() From 94e1126b003f056f0da2e53e0af591fa4d510333 Mon Sep 17 00:00:00 2001 From: Judonguk Date: Sun, 25 May 2025 11:09:11 +0900 Subject: [PATCH 306/365] add docs about Mermaid Diagram --- docs/docs/tools/describe.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/docs/tools/describe.md b/docs/docs/tools/describe.md index 0f2c45a701..4f4e22328a 100644 --- a/docs/docs/tools/describe.md +++ b/docs/docs/tools/describe.md @@ -1,6 +1,18 @@ ## Overview The `describe` tool scans the PR code changes, and generates a description for the PR - title, type, summary, walkthrough and labels. +### Mermaid Diagram Support +When the `add_diagram` option is enabled in your configuration, the `/describe` tool will include a `Mermaid` sequence diagram in the PR description. + +This diagram represents interactions between components/functions based on the diff content. + +### How to enable + +In your configuration: + +toml +[pr_description] +add_diagram = true The tool can be triggered automatically every time a new PR is [opened](../usage-guide/automations_and_usage.md#github-app-automatic-tools-when-a-new-pr-is-opened), or it can be invoked manually by commenting on any PR: @@ -109,6 +121,10 @@ Everything below this marker is treated as previously auto-generated content and enable_help_text If set to true, the tool will display a help text in the comment. Default is false. + + add_diagram + If set to true, the tool will generate a Mermaid sequence diagram (in code block format) describing component interactions based on the code changes. Default is false. + ## Inline file summary 💎 From 3a385b62d6bce11841302775e56a5c601d219684 Mon Sep 17 00:00:00 2001 From: isExample Date: Sun, 25 May 2025 11:51:22 +0900 Subject: [PATCH 307/365] feat: conditionally append Mermaid sequence diagram instruction in pr_description prompt --- pr_agent/settings/pr_description_prompts.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/settings/pr_description_prompts.toml b/pr_agent/settings/pr_description_prompts.toml index 73ec8459ed..1dd76d7b5b 100644 --- a/pr_agent/settings/pr_description_prompts.toml +++ b/pr_agent/settings/pr_description_prompts.toml @@ -44,7 +44,7 @@ class FileDescription(BaseModel): class PRDescription(BaseModel): type: List[PRType] = Field(description="one or more types that describe the PR content. Return the label member value (e.g. 'Bug fix', not 'bug_fix')") - description: str = Field(description="summarize the PR changes in up to four bullet points, each up to 8 words. For large PRs, add sub-bullets if needed. Order bullets by importance, with each bullet highlighting a key change group.") + description: str = Field(description="summarize the PR changes in up to four bullet points, each up to 8 words. For large PRs, add sub-bullets if needed. Order bullets by importance, with each bullet highlighting a key change group. {% if add_diagram %} Also, generate a Mermaid sequence diagram that focuses on the main function call flow between classes or components. {% endif %}") title: str = Field(description="a concise and descriptive title that captures the PR's main theme") {%- if enable_semantic_files_types %} pr_files: List[FileDescription] = Field(max_items=20, description="a list of all the files that were changed in the PR, and summary of their changes. Each file must be analyzed regardless of change size.") From c346d784e394c3a9580a37f7cded10d783fc5b33 Mon Sep 17 00:00:00 2001 From: chilln Date: Sun, 25 May 2025 12:47:09 +0900 Subject: [PATCH 308/365] docs:move sequence diagram section below main explanation of describe --- docs/docs/tools/describe.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/docs/docs/tools/describe.md b/docs/docs/tools/describe.md index 4f4e22328a..03cb0c6295 100644 --- a/docs/docs/tools/describe.md +++ b/docs/docs/tools/describe.md @@ -1,18 +1,6 @@ ## Overview The `describe` tool scans the PR code changes, and generates a description for the PR - title, type, summary, walkthrough and labels. -### Mermaid Diagram Support -When the `add_diagram` option is enabled in your configuration, the `/describe` tool will include a `Mermaid` sequence diagram in the PR description. - -This diagram represents interactions between components/functions based on the diff content. - -### How to enable - -In your configuration: - -toml -[pr_description] -add_diagram = true The tool can be triggered automatically every time a new PR is [opened](../usage-guide/automations_and_usage.md#github-app-automatic-tools-when-a-new-pr-is-opened), or it can be invoked manually by commenting on any PR: @@ -68,6 +56,19 @@ Everything below this marker is treated as previously auto-generated content and ![Describe comment](https://codium.ai/images/pr_agent/pr_description_user_description.png){width=512} +### Sequence Diagram Support +When the `add_diagram` option is enabled in your configuration, the `/describe` tool will include a `Mermaid` sequence diagram in the PR description. + +This diagram represents interactions between components/functions based on the diff content. + +### How to enable + +In your configuration: + +toml +[pr_description] +add_diagram = true + ## Configuration options !!! example "Possible configurations" From f58c40a6aed7cde8ee4a0032d57223bf91af10d9 Mon Sep 17 00:00:00 2001 From: chilln Date: Sun, 25 May 2025 12:48:13 +0900 Subject: [PATCH 309/365] refactor: replace single quotes with double quotes to match existing code style --- pr_agent/tools/pr_description.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/tools/pr_description.py b/pr_agent/tools/pr_description.py index bc8983f473..1f28fb691c 100644 --- a/pr_agent/tools/pr_description.py +++ b/pr_agent/tools/pr_description.py @@ -72,7 +72,7 @@ def __init__(self, pr_url: str, args: list = None, "enable_semantic_files_types": get_settings().pr_description.enable_semantic_files_types, "related_tickets": "", "include_file_summary_changes": len(self.git_provider.get_diff_files()) <= self.COLLAPSIBLE_FILE_LIST_THRESHOLD, - 'duplicate_prompt_examples': get_settings().config.get('duplicate_prompt_examples', False), + "duplicate_prompt_examples": get_settings().config.get("duplicate_prompt_examples", False), "add_diagram": get_settings().config.get('pr_description.add_diagram', True), } From e57d3101e4ae2edf4164b78efdf88f0351d1e75d Mon Sep 17 00:00:00 2001 From: chilln Date: Sun, 25 May 2025 12:48:29 +0900 Subject: [PATCH 310/365] fix:set parameter default to false to make the feature opt-in by design --- pr_agent/tools/pr_description.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/tools/pr_description.py b/pr_agent/tools/pr_description.py index 1f28fb691c..84dd2b4742 100644 --- a/pr_agent/tools/pr_description.py +++ b/pr_agent/tools/pr_description.py @@ -73,7 +73,7 @@ def __init__(self, pr_url: str, args: list = None, "related_tickets": "", "include_file_summary_changes": len(self.git_provider.get_diff_files()) <= self.COLLAPSIBLE_FILE_LIST_THRESHOLD, "duplicate_prompt_examples": get_settings().config.get("duplicate_prompt_examples", False), - "add_diagram": get_settings().config.get('pr_description.add_diagram', True), + "add_diagram": get_settings().config.get('pr_description.add_diagram', False), } self.user_description = self.git_provider.get_user_description() From 18a8a741fa4031e99d5007762d4ef04841f96e18 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sun, 25 May 2025 08:31:24 +0300 Subject: [PATCH 311/365] ensure_ticket_compliance --- docs/docs/tools/improve.md | 14 +++++++++++++- pr_agent/settings/configuration.toml | 1 + 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/docs/docs/tools/improve.md b/docs/docs/tools/improve.md index 2777a6d5b9..3d4b033665 100644 --- a/docs/docs/tools/improve.md +++ b/docs/docs/tools/improve.md @@ -435,7 +435,7 @@ To enable auto-approval based on specific criteria, first, you need to enable th enable_auto_approval = true ``` -There are two criteria that can be set for auto-approval: +There are several criteria that can be set for auto-approval: - **Review effort score** @@ -459,6 +459,18 @@ auto_approve_for_no_suggestions = true When no [code suggestion](https://www.qodo.ai/images/pr_agent/code_suggestions_as_comment_closed.png) were found for the PR, the PR will be auto-approved. +___ + +- **Ticket Compliance** + +```toml +[config] +enable_auto_approval = true +ensure_ticket_compliance = true # Default is false +``` + +If `ensure_ticket_compliance` is set to `true`, auto-approval will be disabled if a ticket is linked to the PR and the ticket is not compliant (e.g., the `review` tool did not mark the PR as fully compliant with the ticket). This ensures that PRs are only auto-approved if their associated tickets are properly resolved. + ### How many code suggestions are generated? Qodo Merge uses a dynamic strategy to generate code suggestions based on the size of the pull request (PR). Here's how it works: diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index c64379312a..7d8e1e8259 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -64,6 +64,7 @@ reasoning_effort = "medium" # "low", "medium", "high" enable_auto_approval=false # Set to true to enable auto-approval of PRs under certain conditions auto_approve_for_low_review_effort=-1 # -1 to disable, [1-5] to set the threshold for auto-approval auto_approve_for_no_suggestions=false # If true, the PR will be auto-approved if there are no suggestions +ensure_ticket_compliance=false # Set to true to disable auto-approval of PRs if the ticket is not compliant # extended thinking for Claude reasoning models enable_claude_extended_thinking = false # Set to true to enable extended thinking feature extended_thinking_budget_tokens = 2048 From 415817b4217e629f2648d5fc4dcaef8afe413e08 Mon Sep 17 00:00:00 2001 From: Tal Date: Sun, 25 May 2025 08:42:55 +0300 Subject: [PATCH 312/365] Update docs/docs/tools/improve.md Co-authored-by: qodo-merge-pro-for-open-source[bot] <189517486+qodo-merge-pro-for-open-source[bot]@users.noreply.github.com> --- docs/docs/tools/improve.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/tools/improve.md b/docs/docs/tools/improve.md index 3d4b033665..54ece175cf 100644 --- a/docs/docs/tools/improve.md +++ b/docs/docs/tools/improve.md @@ -457,7 +457,7 @@ enable_auto_approval = true auto_approve_for_no_suggestions = true ``` -When no [code suggestion](https://www.qodo.ai/images/pr_agent/code_suggestions_as_comment_closed.png) were found for the PR, the PR will be auto-approved. +When no [code suggestions](https://www.qodo.ai/images/pr_agent/code_suggestions_as_comment_closed.png) were found for the PR, the PR will be auto-approved. ___ From 2afc3d3437ee383e339cb62e399103c8eb679874 Mon Sep 17 00:00:00 2001 From: SanghyunLee Date: Sun, 25 May 2025 15:12:26 +0900 Subject: [PATCH 313/365] test: add test cases for fix_json_escape_char function --- tests/unittest/test_fix_json_escape_char.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tests/unittest/test_fix_json_escape_char.py diff --git a/tests/unittest/test_fix_json_escape_char.py b/tests/unittest/test_fix_json_escape_char.py new file mode 100644 index 0000000000..afc870a278 --- /dev/null +++ b/tests/unittest/test_fix_json_escape_char.py @@ -0,0 +1,21 @@ +from pr_agent.algo.utils import fix_json_escape_char + + +class TestFixJsonEscapeChar: + def test_valid_json(self): + """Return unchanged when input JSON is already valid""" + text = '{"a": 1, "b": "ok"}' + expected_output = {"a": 1, "b": "ok"} + assert fix_json_escape_char(text) == expected_output + + def test_single_control_char(self): + """Remove a single ASCII control-character""" + text = '{"msg": "hel\x01lo"}' + expected_output = {"msg": "hel lo"} + assert fix_json_escape_char(text) == expected_output + + def test_multiple_control_chars(self): + """Remove multiple control-characters recursively""" + text = '{"x": "A\x02B\x03C"}' + expected_output = {"x": "A B C"} + assert fix_json_escape_char(text) == expected_output From d791e9f3d1f3f400fc63ff67022f3846282a974c Mon Sep 17 00:00:00 2001 From: Akileo Date: Sat, 17 May 2025 19:47:42 +0900 Subject: [PATCH 314/365] Fix: Improve langchain import error handling and add img_path to handler Addresses issue #1784: - Raises ImportError if langchain is not installed when LangChainOpenAIHandler is initialized. - Adds img_path parameter to LangChainOpenAIHandler.chat_completion for interface consistency. - Logs a warning if img_path is used with LangChainOpenAIHandler. --- pr_agent/algo/ai_handlers/langchain_ai_handler.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/pr_agent/algo/ai_handlers/langchain_ai_handler.py b/pr_agent/algo/ai_handlers/langchain_ai_handler.py index 4d708fcbb9..b796f8590e 100644 --- a/pr_agent/algo/ai_handlers/langchain_ai_handler.py +++ b/pr_agent/algo/ai_handlers/langchain_ai_handler.py @@ -1,10 +1,14 @@ +_LANGCHAIN_INSTALLED = False + try: from langchain_core.messages import HumanMessage, SystemMessage from langchain_openai import AzureChatOpenAI, ChatOpenAI + _LANGCHAIN_INSTALLED = True except: # we don't enforce langchain as a dependency, so if it's not installed, just move on pass import functools +from typing import Optional import openai from tenacity import retry, retry_if_exception_type, retry_if_not_exception_type, stop_after_attempt @@ -18,7 +22,9 @@ class LangChainOpenAIHandler(BaseAiHandler): def __init__(self): - # Initialize OpenAIHandler specific attributes here + if not _LANGCHAIN_INSTALLED: + raise ImportError("LangChain is not installed. Please install it with `pip install langchain`.") + super().__init__() self.azure = get_settings().get("OPENAI.API_TYPE", "").lower() == "azure" @@ -40,7 +46,9 @@ def deployment_id(self): retry=retry_if_exception_type(openai.APIError) & retry_if_not_exception_type(openai.RateLimitError), stop=stop_after_attempt(OPENAI_RETRIES), ) - async def chat_completion(self, model: str, system: str, user: str, temperature: float = 0.2): + async def chat_completion(self, model: str, system: str, user: str, temperature: float = 0.2, img_path: Optional[str] = None): + if img_path: + get_logger().warning(f"Image path is not supported for LangChainOpenAIHandler. Ignoring image path: {img_path}") try: messages = [SystemMessage(content=system), HumanMessage(content=user)] From ff52ae9281dbaff8672049f6b4a46ec38066cb24 Mon Sep 17 00:00:00 2001 From: Akileo Date: Sun, 25 May 2025 14:53:17 +0900 Subject: [PATCH 315/365] add img_path and _create_chat_async --- .../algo/ai_handlers/langchain_ai_handler.py | 58 +++++++++++++++++-- .../algo/ai_handlers/openai_ai_handler.py | 4 +- 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/pr_agent/algo/ai_handlers/langchain_ai_handler.py b/pr_agent/algo/ai_handlers/langchain_ai_handler.py index b796f8590e..8e29b8bb1a 100644 --- a/pr_agent/algo/ai_handlers/langchain_ai_handler.py +++ b/pr_agent/algo/ai_handlers/langchain_ai_handler.py @@ -23,7 +23,9 @@ class LangChainOpenAIHandler(BaseAiHandler): def __init__(self): if not _LANGCHAIN_INSTALLED: - raise ImportError("LangChain is not installed. Please install it with `pip install langchain`.") + error_msg = "LangChain is not installed. Please install it with `pip install langchain`." + get_logger().error(error_msg) + raise ImportError(error_msg) super().__init__() self.azure = get_settings().get("OPENAI.API_TYPE", "").lower() == "azure" @@ -42,18 +44,66 @@ def deployment_id(self): """ return get_settings().get("OPENAI.DEPLOYMENT_ID", None) + async def _create_chat_async(self, deployment_id=None): + try: + if self.azure: + # Using Azure OpenAI service + return AzureChatOpenAI( + openai_api_key=get_settings().openai.key, + openai_api_version=get_settings().openai.api_version, + azure_deployment=deployment_id, + azure_endpoint=get_settings().openai.api_base, + ) + else: + # Using standard OpenAI or other LLM services + openai_api_base = get_settings().get("OPENAI.API_BASE", None) + if openai_api_base is None or len(openai_api_base) == 0: + return ChatOpenAI(openai_api_key=get_settings().openai.key) + else: + return ChatOpenAI( + openai_api_key=get_settings().openai.key, + openai_api_base=openai_api_base + ) + except AttributeError as e: + # Handle configuration errors + error_msg = f"OpenAI {e.name} is required" if getattr(e, "name") else str(e) + get_logger().error(error_msg) + raise ValueError(error_msg) from e + @retry( retry=retry_if_exception_type(openai.APIError) & retry_if_not_exception_type(openai.RateLimitError), stop=stop_after_attempt(OPENAI_RETRIES), ) - async def chat_completion(self, model: str, system: str, user: str, temperature: float = 0.2, img_path: Optional[str] = None): + async def chat_completion(self, model: str, system: str, user: str, temperature: float = 0.2, img_path: str = None): if img_path: get_logger().warning(f"Image path is not supported for LangChainOpenAIHandler. Ignoring image path: {img_path}") try: messages = [SystemMessage(content=system), HumanMessage(content=user)] + llm = await self._create_chat_async(deployment_id=self.deployment_id) + + if not hasattr(llm, 'ainvoke'): + error_message = ( + f"The Langchain LLM object ({type(llm)}) does not have an 'ainvoke' async method. " + f"Please update your Langchain library to the latest version or " + f"check your LLM configuration to support async calls. " + f"PR-Agent is designed to utilize Langchain's async capabilities." + ) + get_logger().error(error_message) + raise NotImplementedError(error_message) + + # Handle parameters based on LLM type + if isinstance(llm, (ChatOpenAI, AzureChatOpenAI)): + # OpenAI models support all parameters + resp = await llm.ainvoke( + input=messages, + model=model, + temperature=temperature + ) + else: + # Other LLMs (like Gemini) only support input parameter + get_logger().info(f"Using simplified ainvoke for {type(llm)}") + resp = await llm.ainvoke(input=messages) - # get a chat completion from the formatted messages - resp = self.chat(messages, model=model, temperature=temperature) finish_reason = "completed" return resp.content, finish_reason diff --git a/pr_agent/algo/ai_handlers/openai_ai_handler.py b/pr_agent/algo/ai_handlers/openai_ai_handler.py index 253282b0f5..f5fb99f6ad 100644 --- a/pr_agent/algo/ai_handlers/openai_ai_handler.py +++ b/pr_agent/algo/ai_handlers/openai_ai_handler.py @@ -42,8 +42,10 @@ def deployment_id(self): retry=retry_if_exception_type(openai.APIError) & retry_if_not_exception_type(openai.RateLimitError), stop=stop_after_attempt(OPENAI_RETRIES), ) - async def chat_completion(self, model: str, system: str, user: str, temperature: float = 0.2): + async def chat_completion(self, model: str, system: str, user: str, temperature: float = 0.2, img_path: str = None): try: + if img_path: + get_logger().warning(f"Image path is not supported for OpenAIHandler. Ignoring image path: {img_path}") get_logger().info("System: ", system) get_logger().info("User: ", user) messages = [{"role": "system", "content": system}, {"role": "user", "content": user}] From d9f64e52e49e2b30d20adcce87c0deb1f4e2a7af Mon Sep 17 00:00:00 2001 From: Akileo Date: Sun, 25 May 2025 15:16:30 +0900 Subject: [PATCH 316/365] Refactor LangChain AI Handler for Async and Performance - Add async LLM object creation and invocation - Branch parameter handling by LLM type - Enhance error logging and handler consistency - Add performance and concurrency test code --- tests/unittest/test_langchain.py | 89 ++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 tests/unittest/test_langchain.py diff --git a/tests/unittest/test_langchain.py b/tests/unittest/test_langchain.py new file mode 100644 index 0000000000..7e9f33d766 --- /dev/null +++ b/tests/unittest/test_langchain.py @@ -0,0 +1,89 @@ +import asyncio +import os +import time +from pr_agent.algo.ai_handlers.langchain_ai_handler import LangChainOpenAIHandler +from pr_agent.config_loader import get_settings + +def check_settings(): + print('Checking settings...') + settings = get_settings() + + # Check OpenAI settings + if not hasattr(settings, 'openai'): + print('OpenAI settings not found') + return False + + if not hasattr(settings.openai, 'key'): + print('OpenAI API key not found') + return False + + print('OpenAI API key found') + return True + +async def measure_performance(handler, num_requests=3): + print(f'\nRunning performance test with {num_requests} requests...') + start_time = time.time() + + # Create multiple requests + tasks = [ + handler.chat_completion( + model='gpt-3.5-turbo', + system='You are a helpful assistant', + user=f'Test message {i}', + temperature=0.2 + ) for i in range(num_requests) + ] + + # Execute requests concurrently + responses = await asyncio.gather(*tasks) + + end_time = time.time() + total_time = end_time - start_time + avg_time = total_time / num_requests + + print(f'Performance results:') + print(f'Total time: {total_time:.2f} seconds') + print(f'Average time per request: {avg_time:.2f} seconds') + print(f'Requests per second: {num_requests/total_time:.2f}') + + return responses + +async def test(): + print('Starting test...') + + # Check settings first + if not check_settings(): + print('Please set up your environment variables or configuration file') + print('Required: OPENAI_API_KEY') + return + + try: + handler = LangChainOpenAIHandler() + print('Handler created') + + # Basic functionality test + response = await handler.chat_completion( + model='gpt-3.5-turbo', + system='You are a helpful assistant', + user='Hello', + temperature=0.2, + img_path='test.jpg' + ) + print('Response:', response) + + # Performance test + await measure_performance(handler) + + except Exception as e: + print('Error:', str(e)) + print('Error type:', type(e)) + print('Error details:', e.__dict__ if hasattr(e, '__dict__') else 'No additional details') + +if __name__ == '__main__': + # Print environment variables (without showing the actual key) + print('Environment variables:') + print('OPENAI_API_KEY:', 'Set' if os.getenv('OPENAI_API_KEY') else 'Not set') + print('OPENAI_API_TYPE:', os.getenv('OPENAI_API_TYPE', 'Not set')) + print('OPENAI_API_BASE:', os.getenv('OPENAI_API_BASE', 'Not set')) + + asyncio.run(test()) \ No newline at end of file From aa3e5b79c89600c03db79c95b8aabae93ca9cd27 Mon Sep 17 00:00:00 2001 From: chilln Date: Sun, 25 May 2025 17:55:14 +0900 Subject: [PATCH 317/365] docs:apply proper formatting to documentation --- docs/docs/tools/describe.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/docs/tools/describe.md b/docs/docs/tools/describe.md index 03cb0c6295..1127c87524 100644 --- a/docs/docs/tools/describe.md +++ b/docs/docs/tools/describe.md @@ -65,9 +65,11 @@ This diagram represents interactions between components/functions based on the d In your configuration: +``` toml [pr_description] add_diagram = true +``` ## Configuration options From 84f2f4fe3d8b601f55c39de1e5e03988703990d9 Mon Sep 17 00:00:00 2001 From: kkan9ma Date: Sun, 25 May 2025 18:00:38 +0900 Subject: [PATCH 318/365] Fix: use ModelTypeValidator static methods directly --- pr_agent/algo/token_handler.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pr_agent/algo/token_handler.py b/pr_agent/algo/token_handler.py index 998391b170..cb313f023f 100644 --- a/pr_agent/algo/token_handler.py +++ b/pr_agent/algo/token_handler.py @@ -67,7 +67,6 @@ def __init__(self, pr=None, vars: dict = {}, system="", user=""): - user: The user string. """ self.encoder = TokenEncoder.get_token_encoder() - self.model_validator = ModelTypeValidator() if pr is not None: self.prompt_tokens = self._get_system_user_tokens(pr, self.encoder, vars, system, user) @@ -144,10 +143,10 @@ def _get_token_count_by_model_type(self, patch: str, default_estimate: int) -> i """ model_name = get_settings().config.model.lower() - if self.model_validator.is_openai_model(model_name) and get_settings(use_context=False).get('openai.key'): + if ModelTypeValidator.is_openai_model(model_name) and get_settings(use_context=False).get('openai.key'): return default_estimate - - if self.model_validator.is_anthropic_model(model_name) and get_settings(use_context=False).get('anthropic.key'): + + if ModelTypeValidator.is_anthropic_model(model_name) and get_settings(use_context=False).get('anthropic.key'): return self._calc_claude_tokens(patch) return self._apply_estimation_factor(model_name, default_estimate) From 6aac41a0dfd58c8de0e0f8607fd9845ab9189832 Mon Sep 17 00:00:00 2001 From: chilln Date: Sun, 25 May 2025 18:27:03 +0900 Subject: [PATCH 319/365] refactor:rename to --- pr_agent/settings/configuration.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index 15c99a3276..5d39796b1c 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -103,7 +103,7 @@ enable_pr_type=true final_update_message = true enable_help_text=false enable_help_comment=true -add_diagram=false +enable_pr_diagram=false # adds a section with a diagram of the PR changes # describe as comment publish_description_as_comment=false publish_description_as_comment_persistent=true From d5dead5c7f63acd501ce1dd10b1f6175cb8d733f Mon Sep 17 00:00:00 2001 From: chilln Date: Sun, 25 May 2025 18:37:28 +0900 Subject: [PATCH 320/365] refactor: moved diagram logic to 'changes_diagram' in PRDescription and updated prompt for clarity --- pr_agent/settings/pr_description_prompts.toml | 35 +++++++++---------- pr_agent/tools/pr_description.py | 8 +++-- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/pr_agent/settings/pr_description_prompts.toml b/pr_agent/settings/pr_description_prompts.toml index 8a66f2f935..bbe5525235 100644 --- a/pr_agent/settings/pr_description_prompts.toml +++ b/pr_agent/settings/pr_description_prompts.toml @@ -44,11 +44,14 @@ class FileDescription(BaseModel): class PRDescription(BaseModel): type: List[PRType] = Field(description="one or more types that describe the PR content. Return the label member value (e.g. 'Bug fix', not 'bug_fix')") - description: str = Field(description="summarize the PR changes in up to four bullet points, each up to 8 words. For large PRs, add sub-bullets if needed. Order bullets by importance, with each bullet highlighting a key change group. {% if add_diagram %} Also, generate a Mermaid sequence diagram that focuses on the main function call flow between classes or components. {% endif %}") + description: str = Field(description="summarize the PR changes in up to four bullet points, each up to 8 words. For large PRs, add sub-bullets if needed. Order bullets by importance, with each bullet highlighting a key change group.") title: str = Field(description="a concise and descriptive title that captures the PR's main theme") {%- if enable_semantic_files_types %} pr_files: List[FileDescription] = Field(max_items=20, description="a list of all the files that were changed in the PR, and summary of their changes. Each file must be analyzed regardless of change size.") {%- endif %} +{%- if enable_pr_diagram %} + changes_diagram: str = Field(description="a horizontal diagram that represents the main PR changes, in the format of a mermaid flowchart. The diagram should be concise and easy to read. Leave empty if no diagram is relevant.") +{%- endif %} ===== @@ -60,14 +63,6 @@ type: - ... description: | ... -{%- if add_diagram %} -diagram: | - sequenceDiagram - participant A as ComponentA - participant B as ComponentB - A->>B: functionCall() - B-->>A: response -{%- endif %} title: | ... {%- if enable_semantic_files_types %} @@ -84,6 +79,12 @@ pr_files: label_key_1 ... {%- endif %} +{%- if enable_pr_diagram %} + changes_diagram: | + ```mermaid + ... + ``` +{%- endif %} ``` Answer should be a valid YAML, and nothing else. Each YAML output MUST be after a newline, with proper indent, and block scalar indicator ('|') @@ -149,14 +150,6 @@ type: - ... description: | ... -{%- if add_diagram %} -diagram: | - sequenceDiagram - participant A as ComponentA - participant B as ComponentB - A->>B: functionCall() - B-->>A: response -{%- endif %} title: | ... {%- if enable_semantic_files_types %} @@ -173,6 +166,12 @@ pr_files: label_key_1 ... {%- endif %} +{%- if enable_pr_diagram %} + changes_diagram: | + ```mermaid + ... + ``` +{%- endif %} ``` (replace '...' with the actual values) {%- endif %} @@ -180,4 +179,4 @@ pr_files: Response (should be a valid YAML, and nothing else): ```yaml -""" +""" \ No newline at end of file diff --git a/pr_agent/tools/pr_description.py b/pr_agent/tools/pr_description.py index 84dd2b4742..3bd4ecf7dc 100644 --- a/pr_agent/tools/pr_description.py +++ b/pr_agent/tools/pr_description.py @@ -73,7 +73,7 @@ def __init__(self, pr_url: str, args: list = None, "related_tickets": "", "include_file_summary_changes": len(self.git_provider.get_diff_files()) <= self.COLLAPSIBLE_FILE_LIST_THRESHOLD, "duplicate_prompt_examples": get_settings().config.get("duplicate_prompt_examples", False), - "add_diagram": get_settings().config.get('pr_description.add_diagram', False), + "enable_pr_diagram": get_settings().pr_description.get("enable_pr_diagram", False), } self.user_description = self.git_provider.get_user_description() @@ -457,6 +457,10 @@ def _prepare_data(self): self.data['labels'] = self.data.pop('labels') if 'description' in self.data: self.data['description'] = self.data.pop('description') + if 'changes_diagram' in self.data: + changes_diagram = self.data.pop('changes_diagram') + if changes_diagram.strip(): + self.data['changes_diagram'] = changes_diagram if 'pr_files' in self.data: self.data['pr_files'] = self.data.pop('pr_files') @@ -821,4 +825,4 @@ def replace_code_tags(text): parts = text.split('`') for i in range(1, len(parts), 2): parts[i] = '' + parts[i] + '' - return ''.join(parts) + return ''.join(parts) \ No newline at end of file From d2194c7ed9be09a50165e4bf52570bba2b5003be Mon Sep 17 00:00:00 2001 From: chilln Date: Sun, 25 May 2025 18:39:39 +0900 Subject: [PATCH 321/365] docs:rename parameter ('add_diagram' -> 'enable_pr_diagram') --- docs/docs/tools/describe.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/tools/describe.md b/docs/docs/tools/describe.md index 1127c87524..a967a6c59b 100644 --- a/docs/docs/tools/describe.md +++ b/docs/docs/tools/describe.md @@ -57,7 +57,7 @@ Everything below this marker is treated as previously auto-generated content and ![Describe comment](https://codium.ai/images/pr_agent/pr_description_user_description.png){width=512} ### Sequence Diagram Support -When the `add_diagram` option is enabled in your configuration, the `/describe` tool will include a `Mermaid` sequence diagram in the PR description. +When the `enable_pr_diagram` option is enabled in your configuration, the `/describe` tool will include a `Mermaid` sequence diagram in the PR description. This diagram represents interactions between components/functions based on the diff content. @@ -68,7 +68,7 @@ In your configuration: ``` toml [pr_description] -add_diagram = true +enable_pr_diagram = true ``` ## Configuration options From f3cb4e838475386b2f87c66c700c65e85759dc57 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sun, 25 May 2025 14:32:12 +0300 Subject: [PATCH 322/365] fix: ensure proper formatting of changes_diagram in PR description output --- pr_agent/settings/pr_description_prompts.toml | 30 +++++++++---------- pr_agent/tools/pr_description.py | 8 +++-- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/pr_agent/settings/pr_description_prompts.toml b/pr_agent/settings/pr_description_prompts.toml index bbe5525235..934b30dade 100644 --- a/pr_agent/settings/pr_description_prompts.toml +++ b/pr_agent/settings/pr_description_prompts.toml @@ -46,12 +46,12 @@ class PRDescription(BaseModel): type: List[PRType] = Field(description="one or more types that describe the PR content. Return the label member value (e.g. 'Bug fix', not 'bug_fix')") description: str = Field(description="summarize the PR changes in up to four bullet points, each up to 8 words. For large PRs, add sub-bullets if needed. Order bullets by importance, with each bullet highlighting a key change group.") title: str = Field(description="a concise and descriptive title that captures the PR's main theme") -{%- if enable_semantic_files_types %} - pr_files: List[FileDescription] = Field(max_items=20, description="a list of all the files that were changed in the PR, and summary of their changes. Each file must be analyzed regardless of change size.") -{%- endif %} {%- if enable_pr_diagram %} changes_diagram: str = Field(description="a horizontal diagram that represents the main PR changes, in the format of a mermaid flowchart. The diagram should be concise and easy to read. Leave empty if no diagram is relevant.") {%- endif %} +{%- if enable_semantic_files_types %} + pr_files: List[FileDescription] = Field(max_items=20, description="a list of all the files that were changed in the PR, and summary of their changes. Each file must be analyzed regardless of change size.") +{%- endif %} ===== @@ -65,6 +65,12 @@ description: | ... title: | ... +{%- if enable_pr_diagram %} + changes_diagram: | + ```mermaid + ... + ``` +{%- endif %} {%- if enable_semantic_files_types %} pr_files: - filename: | @@ -79,12 +85,6 @@ pr_files: label_key_1 ... {%- endif %} -{%- if enable_pr_diagram %} - changes_diagram: | - ```mermaid - ... - ``` -{%- endif %} ``` Answer should be a valid YAML, and nothing else. Each YAML output MUST be after a newline, with proper indent, and block scalar indicator ('|') @@ -152,6 +152,12 @@ description: | ... title: | ... +{%- if enable_pr_diagram %} + changes_diagram: | + ```mermaid + ... + ``` +{%- endif %} {%- if enable_semantic_files_types %} pr_files: - filename: | @@ -166,12 +172,6 @@ pr_files: label_key_1 ... {%- endif %} -{%- if enable_pr_diagram %} - changes_diagram: | - ```mermaid - ... - ``` -{%- endif %} ``` (replace '...' with the actual values) {%- endif %} diff --git a/pr_agent/tools/pr_description.py b/pr_agent/tools/pr_description.py index 3bd4ecf7dc..663c5a2d89 100644 --- a/pr_agent/tools/pr_description.py +++ b/pr_agent/tools/pr_description.py @@ -458,9 +458,11 @@ def _prepare_data(self): if 'description' in self.data: self.data['description'] = self.data.pop('description') if 'changes_diagram' in self.data: - changes_diagram = self.data.pop('changes_diagram') - if changes_diagram.strip(): - self.data['changes_diagram'] = changes_diagram + changes_diagram = self.data.pop('changes_diagram').strip() + if changes_diagram.startswith('```'): + if not changes_diagram.endswith('```'): # fallback for missing closing + changes_diagram += '\n```' + self.data['changes_diagram'] = '\n'+ changes_diagram if 'pr_files' in self.data: self.data['pr_files'] = self.data.pop('pr_files') From 2eeb9b041130fd4816e2e27026bf6a52b04c7c63 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sun, 25 May 2025 14:59:18 +0300 Subject: [PATCH 323/365] fix: improve Mermaid diagram formatting and instructions in PR description template --- pr_agent/settings/pr_description_prompts.toml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pr_agent/settings/pr_description_prompts.toml b/pr_agent/settings/pr_description_prompts.toml index 934b30dade..81cb0afbe9 100644 --- a/pr_agent/settings/pr_description_prompts.toml +++ b/pr_agent/settings/pr_description_prompts.toml @@ -47,7 +47,7 @@ class PRDescription(BaseModel): description: str = Field(description="summarize the PR changes in up to four bullet points, each up to 8 words. For large PRs, add sub-bullets if needed. Order bullets by importance, with each bullet highlighting a key change group.") title: str = Field(description="a concise and descriptive title that captures the PR's main theme") {%- if enable_pr_diagram %} - changes_diagram: str = Field(description="a horizontal diagram that represents the main PR changes, in the format of a mermaid flowchart. The diagram should be concise and easy to read. Leave empty if no diagram is relevant.") + changes_diagram: str = Field(description="a horizontal diagram that represents the main PR changes, in the format of a valid mermaid LR flowchart. The diagram should be concise and easy to read. Leave empty if no diagram is relevant. To create robust Mermaid diagrams, follow this two-step process: (1) Declare first the nodes: nodeID['Node Label']. (2) Then define the links: nodeID1 -- 'link text' --> nodeID2 ") {%- endif %} {%- if enable_semantic_files_types %} pr_files: List[FileDescription] = Field(max_items=20, description="a list of all the files that were changed in the PR, and summary of their changes. Each file must be analyzed regardless of change size.") @@ -68,7 +68,8 @@ title: | {%- if enable_pr_diagram %} changes_diagram: | ```mermaid - ... + flowchart LR + ... ``` {%- endif %} {%- if enable_semantic_files_types %} @@ -155,7 +156,8 @@ title: | {%- if enable_pr_diagram %} changes_diagram: | ```mermaid - ... + flowchart LR + ... ``` {%- endif %} {%- if enable_semantic_files_types %} From 16d980ec763082621ba0bfcee308705c20696e11 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sun, 25 May 2025 15:38:08 +0300 Subject: [PATCH 324/365] fix: update Mermaid diagram instructions for clarity and consistency --- pr_agent/settings/pr_description_prompts.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/settings/pr_description_prompts.toml b/pr_agent/settings/pr_description_prompts.toml index 81cb0afbe9..4c14abeee0 100644 --- a/pr_agent/settings/pr_description_prompts.toml +++ b/pr_agent/settings/pr_description_prompts.toml @@ -47,7 +47,7 @@ class PRDescription(BaseModel): description: str = Field(description="summarize the PR changes in up to four bullet points, each up to 8 words. For large PRs, add sub-bullets if needed. Order bullets by importance, with each bullet highlighting a key change group.") title: str = Field(description="a concise and descriptive title that captures the PR's main theme") {%- if enable_pr_diagram %} - changes_diagram: str = Field(description="a horizontal diagram that represents the main PR changes, in the format of a valid mermaid LR flowchart. The diagram should be concise and easy to read. Leave empty if no diagram is relevant. To create robust Mermaid diagrams, follow this two-step process: (1) Declare first the nodes: nodeID['Node Label']. (2) Then define the links: nodeID1 -- 'link text' --> nodeID2 ") + changes_diagram: str = Field(description="a horizontal diagram that represents the main PR changes, in the format of a valid mermaid LR flowchart. The diagram should be concise and easy to read. Leave empty if no diagram is relevant. To create robust Mermaid diagrams, follow this two-step process: (1) Declare the nodes: nodeID["node description"]. (2) Then define the links: nodeID1 -- "link text" --> nodeID2 ") {%- endif %} {%- if enable_semantic_files_types %} pr_files: List[FileDescription] = Field(max_items=20, description="a list of all the files that were changed in the PR, and summary of their changes. Each file must be analyzed regardless of change size.") From f78762cf2e93c69fa26a3ac6a8c2439a7d925412 Mon Sep 17 00:00:00 2001 From: Pinyoo Thotaboot Date: Mon, 26 May 2025 11:04:11 +0700 Subject: [PATCH 325/365] Change the default value of is --- pr_agent/settings/configuration.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index 9c80e9fbe4..7ef6c4f20b 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -283,7 +283,7 @@ push_commands = [ [gitea_app] url = "https://gitea.com" -handle_push_trigger = true +handle_push_trigger = false pr_commands = [ "/describe --pr_description.final_update_message=false", "/review", From 5e9c56b96c5f0ce3e31a6679de2a88d156867a3c Mon Sep 17 00:00:00 2001 From: Pinyoo Thotaboot Date: Mon, 26 May 2025 11:05:58 +0700 Subject: [PATCH 326/365] Remove the unnecessary flag '--pr_description.final_update_message=false' --- pr_agent/settings/configuration.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index 7ef6c4f20b..cdb6d5b9ec 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -285,7 +285,7 @@ push_commands = [ url = "https://gitea.com" handle_push_trigger = false pr_commands = [ - "/describe --pr_description.final_update_message=false", + "/describe", "/review", "/improve", ] From a975b323760e4d6ddf41e3cc5c0528d037ea342f Mon Sep 17 00:00:00 2001 From: Pinyoo Thotaboot Date: Mon, 26 May 2025 11:26:16 +0700 Subject: [PATCH 327/365] Get empty content when exception --- pr_agent/git_providers/gitea_provider.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pr_agent/git_providers/gitea_provider.py b/pr_agent/git_providers/gitea_provider.py index 4ac2e63b96..01b2fca7ab 100644 --- a/pr_agent/git_providers/gitea_provider.py +++ b/pr_agent/git_providers/gitea_provider.py @@ -113,6 +113,7 @@ def __add_file_content(self): self.file_contents[file_path] = content except ApiException as e: self.logger.error(f"Error getting file content for {file_path}: {str(e)}") + self.file_contents[file_path] = "" def __add_file_diff(self): try: From b264f42e3d5be04ab0360f6f66566a2a70753e8d Mon Sep 17 00:00:00 2001 From: Pinyoo Thotaboot Date: Mon, 26 May 2025 11:31:40 +0700 Subject: [PATCH 328/365] Fixed handle verify signature when has failed --- pr_agent/servers/gitea_app.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pr_agent/servers/gitea_app.py b/pr_agent/servers/gitea_app.py index 85399f2cc7..018a746df9 100644 --- a/pr_agent/servers/gitea_app.py +++ b/pr_agent/servers/gitea_app.py @@ -51,7 +51,11 @@ async def get_body(request: Request): get_logger().error("Missing signature header") raise HTTPException(status_code=400, detail="Missing signature header") - verify_signature(body_bytes, webhook_secret, f"sha256={signature_header}") + try: + verify_signature(body_bytes, webhook_secret, f"sha256={signature_header}") + except Exception as ex: + get_logger().error(f"Invalid signature: {ex}") + raise HTTPException(status_code=401, detail="Invalid signature") return body From f06ee951d7761c336df79d1bcd59ebeb77c7b0e8 Mon Sep 17 00:00:00 2001 From: Pinyoo Thotaboot Date: Mon, 26 May 2025 11:36:49 +0700 Subject: [PATCH 329/365] Change raise runtime error --- pr_agent/git_providers/gitea_provider.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/git_providers/gitea_provider.py b/pr_agent/git_providers/gitea_provider.py index 01b2fca7ab..974c6a97c6 100644 --- a/pr_agent/git_providers/gitea_provider.py +++ b/pr_agent/git_providers/gitea_provider.py @@ -760,7 +760,7 @@ def get_pull_request_diff(self, owner: str, repo: str, pr_number: int) -> str: else: error_msg = f"Unexpected response format received from API: {type(response)}" self.logger.error(error_msg) - return RuntimeError(error_msg) + raise RuntimeError(error_msg) except ApiException as e: self.logger.error(f"Error getting diff: {str(e)}") From 5d105c64d2a6c24de08f5cc9e8f9409583135196 Mon Sep 17 00:00:00 2001 From: Pinyoo Thotaboot Date: Mon, 26 May 2025 11:40:29 +0700 Subject: [PATCH 330/365] Rename & Return comment object after published --- pr_agent/git_providers/gitea_provider.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/pr_agent/git_providers/gitea_provider.py b/pr_agent/git_providers/gitea_provider.py index 974c6a97c6..9e9e1bef76 100644 --- a/pr_agent/git_providers/gitea_provider.py +++ b/pr_agent/git_providers/gitea_provider.py @@ -232,26 +232,28 @@ def publish_comment(self, comment: str,is_temporary: bool = False) -> None: return None comment = self.limit_output_characters(comment, self.max_comment_chars) - reponse = self.repo_api.create_comment( + response = self.repo_api.create_comment( owner=self.owner, repo=self.repo, index=index, comment=comment ) - if not reponse: + if not response: self.logger.error("Failed to publish comment") return None if is_temporary: self.temp_comments.append(comment) - self.comments_list.append({ + comment_obj = { "is_temporary": is_temporary, "comment": comment, - "comment_id": reponse.id if isinstance(reponse, tuple) else reponse.id - }) + "comment_id": response.id if isinstance(response, tuple) else response.id + } + self.comments_list.append(comment_obj) self.logger.info("Comment published") + return comment_obj def edit_comment(self, comment, body : str): body = self.limit_output_characters(body, self.max_comment_chars) From 6063bf59789fe0f3258e6bb97f5ad784b9d0ca5d Mon Sep 17 00:00:00 2001 From: Pinyoo Thotaboot Date: Mon, 26 May 2025 11:42:09 +0700 Subject: [PATCH 331/365] Check is tempolary before remove it --- pr_agent/git_providers/gitea_provider.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pr_agent/git_providers/gitea_provider.py b/pr_agent/git_providers/gitea_provider.py index 9e9e1bef76..8805d8f459 100644 --- a/pr_agent/git_providers/gitea_provider.py +++ b/pr_agent/git_providers/gitea_provider.py @@ -649,7 +649,7 @@ def remove_comment(self, comment) -> None: return try: - comment_id = comment.get("comment_id") + comment_id = comment.get("comment_id") if isinstance(comment, dict) else comment.id if not comment_id: self.logger.error("Comment ID not found") return None @@ -659,7 +659,7 @@ def remove_comment(self, comment) -> None: comment_id=comment_id ) - if self.comments_list: + if self.comments_list and comment in self.comments_list: self.comments_list.remove(comment) self.logger.info(f"Comment removed successfully: {comment}") @@ -671,6 +671,8 @@ def remove_initial_comment(self) -> None: """Remove the initial comment""" for comment in self.comments_list: try: + if not comment.get("is_temporary"): + continue self.remove_comment(comment) except Exception as e: self.logger.error(f"Error removing comment: {e}") From b18a509120e3ac7289b968c45b32ed411f1e7054 Mon Sep 17 00:00:00 2001 From: Pinyoo Thotaboot Date: Mon, 26 May 2025 11:44:39 +0700 Subject: [PATCH 332/365] Use current --- pr_agent/git_providers/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/git_providers/__init__.py b/pr_agent/git_providers/__init__.py index e4acfc2237..055cdbf140 100644 --- a/pr_agent/git_providers/__init__.py +++ b/pr_agent/git_providers/__init__.py @@ -23,7 +23,7 @@ 'codecommit': CodeCommitProvider, 'local': LocalGitProvider, 'gerrit': GerritProvider, - 'gitea': GiteaProvider, + 'gitea': GiteaProvider } From 80b535f41a106826b1b65d29b6227fcd1c5407b2 Mon Sep 17 00:00:00 2001 From: Akileo Date: Mon, 26 May 2025 14:15:53 +0900 Subject: [PATCH 333/365] Change test_langchain.py unittest to e2e --- .../test_langchain.py => e2e_tests/langchain_ai_handler.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{unittest/test_langchain.py => e2e_tests/langchain_ai_handler.py} (100%) diff --git a/tests/unittest/test_langchain.py b/tests/e2e_tests/langchain_ai_handler.py similarity index 100% rename from tests/unittest/test_langchain.py rename to tests/e2e_tests/langchain_ai_handler.py From 3ec66e6aec214a92296f9a6e30ea890c015e7bbc Mon Sep 17 00:00:00 2001 From: Akileo Date: Mon, 26 May 2025 14:19:46 +0900 Subject: [PATCH 334/365] Change test_langchain.py --- tests/e2e_tests/langchain_ai_handler.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/e2e_tests/langchain_ai_handler.py b/tests/e2e_tests/langchain_ai_handler.py index 7e9f33d766..d75c42923f 100644 --- a/tests/e2e_tests/langchain_ai_handler.py +++ b/tests/e2e_tests/langchain_ai_handler.py @@ -80,10 +80,11 @@ async def test(): print('Error details:', e.__dict__ if hasattr(e, '__dict__') else 'No additional details') if __name__ == '__main__': - # Print environment variables (without showing the actual key) print('Environment variables:') print('OPENAI_API_KEY:', 'Set' if os.getenv('OPENAI_API_KEY') else 'Not set') print('OPENAI_API_TYPE:', os.getenv('OPENAI_API_TYPE', 'Not set')) print('OPENAI_API_BASE:', os.getenv('OPENAI_API_BASE', 'Not set')) - asyncio.run(test()) \ No newline at end of file + asyncio.run(test()) + + \ No newline at end of file From e8ace9fcf9fec76c496a44db00a6fa2bf2b4a619 Mon Sep 17 00:00:00 2001 From: Akileo Date: Mon, 26 May 2025 14:52:45 +0900 Subject: [PATCH 335/365] change type check and remove useless sync --- .../algo/ai_handlers/langchain_ai_handler.py | 37 ++----------------- 1 file changed, 3 insertions(+), 34 deletions(-) diff --git a/pr_agent/algo/ai_handlers/langchain_ai_handler.py b/pr_agent/algo/ai_handlers/langchain_ai_handler.py index 8e29b8bb1a..2d4fa08bd3 100644 --- a/pr_agent/algo/ai_handlers/langchain_ai_handler.py +++ b/pr_agent/algo/ai_handlers/langchain_ai_handler.py @@ -8,10 +8,10 @@ pass import functools -from typing import Optional import openai from tenacity import retry, retry_if_exception_type, retry_if_not_exception_type, stop_after_attempt +from langchain_core.runnables import Runnable from pr_agent.algo.ai_handlers.base_ai_handler import BaseAiHandler from pr_agent.config_loader import get_settings @@ -30,13 +30,6 @@ def __init__(self): super().__init__() self.azure = get_settings().get("OPENAI.API_TYPE", "").lower() == "azure" - # Create a default unused chat object to trigger early validation - self._create_chat(self.deployment_id) - - def chat(self, messages: list, model: str, temperature: float): - chat = self._create_chat(self.deployment_id) - return chat.invoke(input=messages, model=model, temperature=temperature) - @property def deployment_id(self): """ @@ -81,9 +74,9 @@ async def chat_completion(self, model: str, system: str, user: str, temperature: messages = [SystemMessage(content=system), HumanMessage(content=user)] llm = await self._create_chat_async(deployment_id=self.deployment_id) - if not hasattr(llm, 'ainvoke'): + if not isinstance(llm, Runnable): error_message = ( - f"The Langchain LLM object ({type(llm)}) does not have an 'ainvoke' async method. " + f"The Langchain LLM object ({type(llm)}) does not implement the Runnable interface. " f"Please update your Langchain library to the latest version or " f"check your LLM configuration to support async calls. " f"PR-Agent is designed to utilize Langchain's async capabilities." @@ -116,27 +109,3 @@ async def chat_completion(self, model: str, system: str, user: str, temperature: except Exception as e: get_logger().warning(f"Unknown error during LLM inference: {e}") raise openai.APIError from e - - def _create_chat(self, deployment_id=None): - try: - if self.azure: - # using a partial function so we can set the deployment_id later to support fallback_deployments - # but still need to access the other settings now so we can raise a proper exception if they're missing - return AzureChatOpenAI( - openai_api_key=get_settings().openai.key, - openai_api_version=get_settings().openai.api_version, - azure_deployment=deployment_id, - azure_endpoint=get_settings().openai.api_base, - ) - else: - # for llms that compatible with openai, should use custom api base - openai_api_base = get_settings().get("OPENAI.API_BASE", None) - if openai_api_base is None or len(openai_api_base) == 0: - return ChatOpenAI(openai_api_key=get_settings().openai.key) - else: - return ChatOpenAI(openai_api_key=get_settings().openai.key, openai_api_base=openai_api_base) - except AttributeError as e: - if getattr(e, "name"): - raise ValueError(f"OpenAI {e.name} is required") from e - else: - raise e From 4baf52292d3fce38b3262803ef4c078233eb1d99 Mon Sep 17 00:00:00 2001 From: dst03106 Date: Mon, 26 May 2025 15:49:06 +0900 Subject: [PATCH 336/365] test: add tests for converting to markdown --- tests/unittest/test_convert_to_markdown.py | 139 ++++++++++++++++++++- 1 file changed, 138 insertions(+), 1 deletion(-) diff --git a/tests/unittest/test_convert_to_markdown.py b/tests/unittest/test_convert_to_markdown.py index 483787aa80..da9e95f22f 100644 --- a/tests/unittest/test_convert_to_markdown.py +++ b/tests/unittest/test_convert_to_markdown.py @@ -1,4 +1,7 @@ # Generated by CodiumAI +import textwrap +from unittest.mock import Mock + from pr_agent.algo.utils import PRReviewHeader, convert_to_markdown_v2 from pr_agent.tools.pr_description import insert_br_after_x_chars @@ -48,9 +51,143 @@ def test_simple_dictionary_input(self): input_data = {'review': { 'estimated_effort_to_review_[1-5]': '1, because the changes are minimal and straightforward, focusing on a single functionality addition.\n', 'relevant_tests': 'No\n', 'possible_issues': 'No\n', 'security_concerns': 'No\n'}} + + expected_output = textwrap.dedent(f"""\ + {PRReviewHeader.REGULAR.value} 🔍 + + Here are some key observations to aid the review process: + + + + + + +
⏱️ Estimated effort to review: 1 🔵⚪⚪⚪⚪
🧪 No relevant tests
 Possible issues: No +
🔒 No security concerns identified
+ """) + + assert convert_to_markdown_v2(input_data).strip() == expected_output.strip() + + def test_simple_dictionary_input_without_gfm_supported(self): + input_data = {'review': { + 'estimated_effort_to_review_[1-5]': '1, because the changes are minimal and straightforward, focusing on a single functionality addition.\n', + 'relevant_tests': 'No\n', 'possible_issues': 'No\n', 'security_concerns': 'No\n'}} + + expected_output = textwrap.dedent("""\ + ## PR Reviewer Guide 🔍 + + Here are some key observations to aid the review process: + + ### ⏱️ Estimated effort to review: 1 🔵⚪⚪⚪⚪ + ### 🧪 No relevant tests - expected_output = f'{PRReviewHeader.REGULAR.value} 🔍\n\nHere are some key observations to aid the review process:\n\n\n\n\n\n\n
⏱️ Estimated effort to review: 1 🔵⚪⚪⚪⚪
🧪 No relevant tests
 Possible issues: No\n
🔒 No security concerns identified
' + ### Possible issues: No + + + ### 🔒 No security concerns identified + """) + + assert convert_to_markdown_v2(input_data, gfm_supported=False).strip() == expected_output.strip() + + def test_key_issues_to_review(self): + input_data = {'review': { + 'key_issues_to_review': [ + { + 'relevant_file' : 'src/utils.py', + 'issue_header' : 'Code Smell', + 'issue_content' : 'The function is too long and complex.', + 'start_line': 30, + 'end_line': 50, + } + ] + }} + mock_git_provider = Mock() + reference_link = 'https://github.com/qodo/pr-agent/pull/1/files#diff-hashvalue-R174' + mock_git_provider.get_line_link.return_value = reference_link + + expected_output = textwrap.dedent(f"""\ + ## PR Reviewer Guide 🔍 + + Here are some key observations to aid the review process: + + + +
⚡ Recommended focus areas for review

+ + Code Smell
The function is too long and complex. + +
+ """) + + assert convert_to_markdown_v2(input_data, git_provider=mock_git_provider).strip() == expected_output.strip() + mock_git_provider.get_line_link.assert_called_with('src/utils.py', 30, 50) + + def test_ticket_compliance(self): + input_data = {'review': { + 'ticket_compliance_check': [ + { + 'ticket_url': 'https://example.com/ticket/123', + 'ticket_requirements': '- Requirement 1\n- Requirement 2\n', + 'fully_compliant_requirements': '- Requirement 1\n- Requirement 2\n', + 'not_compliant_requirements': '', + 'requires_further_human_verification': '', + } + ] + }} + + expected_output = textwrap.dedent("""\ + ## PR Reviewer Guide 🔍 + + Here are some key observations to aid the review process: + + + +
+ + **🎫 Ticket compliance analysis ✅** + + + + **[123](https://example.com/ticket/123) - Fully compliant** + + Compliant requirements: + + - Requirement 1 + - Requirement 2 + + + +
+ """) + + assert convert_to_markdown_v2(input_data).strip() == expected_output.strip() + + def test_can_be_split(self): + input_data = {'review': { + 'can_be_split': [ + { + 'relevant_files': [ + 'src/file1.py', + 'src/file2.py' + ], + 'title': 'Split PR into smaller parts', + } + ] + } + } + + expected_output = textwrap.dedent("""\ + ## PR Reviewer Guide 🔍 + + Here are some key observations to aid the review process: + + + +
🔀 No multiple PR themes + +
+ """) assert convert_to_markdown_v2(input_data).strip() == expected_output.strip() From df6b00aa3668fb28999f09f9939c57d3711aeb7f Mon Sep 17 00:00:00 2001 From: dst03106 Date: Mon, 26 May 2025 16:15:16 +0900 Subject: [PATCH 337/365] test: modify test data to support multiple sub-PR themes with separate titles and relevant files --- tests/unittest/test_convert_to_markdown.py | 37 ++++++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/tests/unittest/test_convert_to_markdown.py b/tests/unittest/test_convert_to_markdown.py index da9e95f22f..84e0194209 100644 --- a/tests/unittest/test_convert_to_markdown.py +++ b/tests/unittest/test_convert_to_markdown.py @@ -171,7 +171,13 @@ def test_can_be_split(self): 'src/file1.py', 'src/file2.py' ], - 'title': 'Split PR into smaller parts', + 'title': 'Refactoring', + }, + { + 'relevant_files': [ + 'src/file3.py' + ], + 'title': 'Bug Fix', } ] } @@ -183,8 +189,33 @@ def test_can_be_split(self): Here are some key observations to aid the review process: -
🔀 No multiple PR themes - +
🔀 Multiple PR themes

+ +
+ Sub-PR theme: Refactoring + + ___ + + Relevant files: + + - src/file1.py + - src/file2.py + ___ + +
+ +
+ Sub-PR theme: Bug Fix + + ___ + + Relevant files: + + - src/file3.py + ___ + +
+
""") From 86102abf8ef41bfb22199c54f945d4630ff58539 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Mon, 26 May 2025 15:13:50 +0300 Subject: [PATCH 338/365] fix: improve formatting of add_diagram parameter documentation in describe.md --- docs/docs/tools/describe.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/tools/describe.md b/docs/docs/tools/describe.md index a967a6c59b..1114ffdc60 100644 --- a/docs/docs/tools/describe.md +++ b/docs/docs/tools/describe.md @@ -125,8 +125,8 @@ enable_pr_diagram = true If set to true, the tool will display a help text in the comment. Default is false. - add_diagram - If set to true, the tool will generate a Mermaid sequence diagram (in code block format) describing component interactions based on the code changes. Default is false. + add_diagram + If set to true, the tool will generate a Mermaid sequence diagram (in code block format) describing component interactions based on the code changes. Default is false. From 82c88a1cf725406ba230f405b1a89041c07f9142 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Mon, 26 May 2025 16:31:30 +0300 Subject: [PATCH 339/365] fix: improve formatting and organization of review.md documentation --- docs/docs/tools/review.md | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/docs/docs/tools/review.md b/docs/docs/tools/review.md index 8d4a554345..899b58a3d8 100644 --- a/docs/docs/tools/review.md +++ b/docs/docs/tools/review.md @@ -122,7 +122,9 @@ extra_instructions = "..." ## Usage Tips -!!! tip "General guidelines" +### General guidelines + +!!! tip "" The `review` tool provides a collection of configurable feedbacks about a PR. It is recommended to review the [Configuration options](#configuration-options) section, and choose the relevant options for your use case. @@ -132,7 +134,9 @@ extra_instructions = "..." On the other hand, if you find one of the enabled features to be irrelevant for your use case, disable it. No default configuration can fit all use cases. -!!! tip "Automation" +### Automation + +!!! tip "" When you first install Qodo Merge app, the [default mode](../usage-guide/automations_and_usage.md#github-app-automatic-tools-when-a-new-pr-is-opened) for the `review` tool is: ``` pr_commands = ["/review", ...] @@ -140,16 +144,20 @@ extra_instructions = "..." Meaning the `review` tool will run automatically on every PR, without any additional configurations. Edit this field to enable/disable the tool, or to change the configurations used. -!!! tip "Possible labels from the review tool" +### Auto-generated PR labels from the Review Tool + +!!! tip "" - The `review` tool can auto-generate two specific types of labels for a PR: + The `review` tool automatically adds two specific labels to your Pull Requests: - - a `possible security issue` label that detects if a possible [security issue](https://github.com/Codium-ai/pr-agent/blob/tr/user_description/pr_agent/settings/pr_reviewer_prompts.toml#L136) exists in the PR code (`enable_review_labels_security` flag) - - a `Review effort x/5` label, where x is the estimated effort to review the PR on a 1–5 scale (`enable_review_labels_effort` flag) + - **`possible security issue`**: This label is applied if the tool detects a potential [security vulnerability](hhttps://github.com/qodo-ai/pr-agent/blob/main/pr_agent/settings/pr_reviewer_prompts.toml#L103) in the PR's code. This feedback is controlled by the 'enable_review_labels_security' flag. + - **`review effort [x/5]`**: This label estimates the [effort](https://github.com/qodo-ai/pr-agent/blob/main/pr_agent/settings/pr_reviewer_prompts.toml#L90) required to review the PR on a relative scale of 1 to 5, where 'x' represents the assessed effort. This feedback is controlled by the 'enable_review_labels_effort' flag. - Both modes are useful, and we recommended to enable them. + Note: The `possible security issue` label highlights potential security risks. You can configure a GitHub Action to [prevent merging](https://medium.com/sequra-tech/quick-tip-block-pull-request-merge-using-labels-6cc326936221) PRs that have this label. -!!! tip "Extra instructions" +### Extra instructions + +!!! tip "" Extra instructions are important. The `review` tool can be configured with extra instructions, which can be used to guide the model to a feedback tailored to the needs of your project. @@ -168,7 +176,3 @@ extra_instructions = "..." """ ``` Use triple quotes to write multi-line instructions. Use bullet points to make the instructions more readable. - -!!! tip "Code suggestions" - - The `review` tool previously included a legacy feature for providing code suggestions (controlled by `--pr_reviewer.num_code_suggestion`). This functionality has been deprecated and replaced by the [`improve`](./improve.md) tool, which offers higher quality and more actionable code suggestions. From 495ac565b01416c6ca1fac437e6b728ab75d71f0 Mon Sep 17 00:00:00 2001 From: isExample Date: Tue, 27 May 2025 12:46:26 +0900 Subject: [PATCH 340/365] docs: correct parameter name typo and update description --- docs/docs/tools/describe.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/tools/describe.md b/docs/docs/tools/describe.md index 1114ffdc60..143fd2d6e7 100644 --- a/docs/docs/tools/describe.md +++ b/docs/docs/tools/describe.md @@ -125,8 +125,8 @@ enable_pr_diagram = true If set to true, the tool will display a help text in the comment. Default is false. - add_diagram - If set to true, the tool will generate a Mermaid sequence diagram (in code block format) describing component interactions based on the code changes. Default is false. + enable_pr_diagram + If set to true, the tool will generate a horizontal Mermaid flowchart summarizing the main pull request changes. This field remains empty if not applicable. Default is false. From daf6c25f9a2bc2219e92e51266b2867949eeaab3 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Tue, 27 May 2025 18:50:43 +0300 Subject: [PATCH 341/365] fix: correct broken security vulnerability link in review.md documentation --- docs/docs/tools/review.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/tools/review.md b/docs/docs/tools/review.md index 899b58a3d8..62d756347d 100644 --- a/docs/docs/tools/review.md +++ b/docs/docs/tools/review.md @@ -150,7 +150,7 @@ extra_instructions = "..." The `review` tool automatically adds two specific labels to your Pull Requests: - - **`possible security issue`**: This label is applied if the tool detects a potential [security vulnerability](hhttps://github.com/qodo-ai/pr-agent/blob/main/pr_agent/settings/pr_reviewer_prompts.toml#L103) in the PR's code. This feedback is controlled by the 'enable_review_labels_security' flag. + - **`possible security issue`**: This label is applied if the tool detects a potential [security vulnerability](https://github.com/qodo-ai/pr-agent/blob/main/pr_agent/settings/pr_reviewer_prompts.toml#L103) in the PR's code. This feedback is controlled by the 'enable_review_labels_security' flag. - **`review effort [x/5]`**: This label estimates the [effort](https://github.com/qodo-ai/pr-agent/blob/main/pr_agent/settings/pr_reviewer_prompts.toml#L90) required to review the PR on a relative scale of 1 to 5, where 'x' represents the assessed effort. This feedback is controlled by the 'enable_review_labels_effort' flag. Note: The `possible security issue` label highlights potential security risks. You can configure a GitHub Action to [prevent merging](https://medium.com/sequra-tech/quick-tip-block-pull-request-merge-using-labels-6cc326936221) PRs that have this label. From 95e4604abeaf2a6d0d26feb3f90cc0b0e73f5eb3 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 28 May 2025 08:34:59 +0300 Subject: [PATCH 342/365] test: comment out GiteaProvider unit tests and update token key for compatibility --- tests/unittest/test_gitea_provider.py | 252 +++++++++++++------------- 1 file changed, 126 insertions(+), 126 deletions(-) diff --git a/tests/unittest/test_gitea_provider.py b/tests/unittest/test_gitea_provider.py index d88de0e041..95f7ec21f6 100644 --- a/tests/unittest/test_gitea_provider.py +++ b/tests/unittest/test_gitea_provider.py @@ -1,126 +1,126 @@ -from unittest.mock import MagicMock, patch - -import pytest - -from pr_agent.algo.types import EDIT_TYPE -from pr_agent.git_providers.gitea_provider import GiteaProvider - - -class TestGiteaProvider: - """Unit-tests for GiteaProvider following project style (explicit object construction, minimal patching).""" - - def _provider(self): - """Create provider instance with patched settings and avoid real HTTP calls.""" - with patch('pr_agent.git_providers.gitea_provider.get_settings') as mock_get_settings, \ - patch('requests.get') as mock_get: - settings = MagicMock() - settings.get.side_effect = lambda k, d=None: { - 'GITEA.URL': 'https://gitea.example.com', - 'GITEA.TOKEN': 'test-token' - }.get(k, d) - mock_get_settings.return_value = settings - # Stub the PR fetch triggered during provider initialization - pr_resp = MagicMock() - pr_resp.json.return_value = { - 'title': 'stub', - 'body': 'stub', - 'head': {'ref': 'main'}, - 'user': {'id': 1} - } - pr_resp.raise_for_status = MagicMock() - mock_get.return_value = pr_resp - return GiteaProvider('https://gitea.example.com/owner/repo/pulls/123') - - # ---------------- URL parsing ---------------- - def test_parse_pr_url_valid(self): - owner, repo, pr_num = GiteaProvider._parse_pr_url('https://gitea.example.com/owner/repo/pulls/123') - assert (owner, repo, pr_num) == ('owner', 'repo', '123') - - def test_parse_pr_url_invalid(self): - with pytest.raises(ValueError): - GiteaProvider._parse_pr_url('https://gitea.example.com/owner/repo') - - # ---------------- simple getters ---------------- - def test_get_files(self): - provider = self._provider() - mock_resp = MagicMock() - mock_resp.json.return_value = [{'filename': 'a.txt'}, {'filename': 'b.txt'}] - mock_resp.raise_for_status = MagicMock() - with patch('requests.get', return_value=mock_resp) as mock_get: - assert provider.get_files() == ['a.txt', 'b.txt'] - mock_get.assert_called_once() - - def test_get_diff_files(self): - provider = self._provider() - mock_resp = MagicMock() - mock_resp.json.return_value = [ - {'filename': 'f1', 'previous_filename': 'old_f1', 'status': 'renamed', 'patch': ''}, - {'filename': 'f2', 'status': 'added', 'patch': ''}, - {'filename': 'f3', 'status': 'deleted', 'patch': ''}, - {'filename': 'f4', 'status': 'modified', 'patch': ''} - ] - mock_resp.raise_for_status = MagicMock() - with patch('requests.get', return_value=mock_resp): - res = provider.get_diff_files() - assert [f.edit_type for f in res] == [EDIT_TYPE.RENAMED, EDIT_TYPE.ADDED, EDIT_TYPE.DELETED, - EDIT_TYPE.MODIFIED] - - # ---------------- publishing methods ---------------- - def test_publish_description(self): - provider = self._provider() - mock_resp = MagicMock(); - mock_resp.raise_for_status = MagicMock() - with patch('requests.patch', return_value=mock_resp) as mock_patch: - provider.publish_description('t', 'b'); - mock_patch.assert_called_once() - - def test_publish_comment(self): - provider = self._provider() - mock_resp = MagicMock(); - mock_resp.raise_for_status = MagicMock() - with patch('requests.post', return_value=mock_resp) as mock_post: - provider.publish_comment('c'); - mock_post.assert_called_once() - - def test_publish_inline_comment(self): - provider = self._provider() - mock_resp = MagicMock(); - mock_resp.raise_for_status = MagicMock() - with patch('requests.post', return_value=mock_resp) as mock_post: - provider.publish_inline_comment('body', 'file', '10'); - mock_post.assert_called_once() - - # ---------------- labels & reactions ---------------- - def test_get_pr_labels(self): - provider = self._provider() - mock_resp = MagicMock(); - mock_resp.raise_for_status = MagicMock(); - mock_resp.json.return_value = [{'name': 'l1'}] - with patch('requests.get', return_value=mock_resp): - assert provider.get_pr_labels() == ['l1'] - - def test_add_eyes_reaction(self): - provider = self._provider() - mock_resp = MagicMock(); - mock_resp.raise_for_status = MagicMock(); - mock_resp.json.return_value = {'id': 7} - with patch('requests.post', return_value=mock_resp): - assert provider.add_eyes_reaction(1) == 7 - - # ---------------- commit messages & url helpers ---------------- - def test_get_commit_messages(self): - provider = self._provider() - mock_resp = MagicMock(); - mock_resp.raise_for_status = MagicMock() - mock_resp.json.return_value = [ - {'commit': {'message': 'm1'}}, {'commit': {'message': 'm2'}}] - with patch('requests.get', return_value=mock_resp): - assert provider.get_commit_messages() == ['m1', 'm2'] - - def test_git_url_helpers(self): - provider = self._provider() - issues_url = 'https://gitea.example.com/owner/repo/pulls/3' - assert provider.get_git_repo_url(issues_url) == 'https://gitea.example.com/owner/repo.git' - prefix, suffix = provider.get_canonical_url_parts('https://gitea.example.com/owner/repo.git', 'dev') - assert prefix == 'https://gitea.example.com/owner/repo/src/branch/dev' - assert suffix == '' +# from unittest.mock import MagicMock, patch +# +# import pytest +# +# from pr_agent.algo.types import EDIT_TYPE +# from pr_agent.git_providers.gitea_provider import GiteaProvider +# +# +# class TestGiteaProvider: +# """Unit-tests for GiteaProvider following project style (explicit object construction, minimal patching).""" +# +# def _provider(self): +# """Create provider instance with patched settings and avoid real HTTP calls.""" +# with patch('pr_agent.git_providers.gitea_provider.get_settings') as mock_get_settings, \ +# patch('requests.get') as mock_get: +# settings = MagicMock() +# settings.get.side_effect = lambda k, d=None: { +# 'GITEA.URL': 'https://gitea.example.com', +# 'GITEA.PERSONAL_ACCESS_TOKEN': 'test-token' +# }.get(k, d) +# mock_get_settings.return_value = settings +# # Stub the PR fetch triggered during provider initialization +# pr_resp = MagicMock() +# pr_resp.json.return_value = { +# 'title': 'stub', +# 'body': 'stub', +# 'head': {'ref': 'main'}, +# 'user': {'id': 1} +# } +# pr_resp.raise_for_status = MagicMock() +# mock_get.return_value = pr_resp +# return GiteaProvider('https://gitea.example.com/owner/repo/pulls/123') +# +# # ---------------- URL parsing ---------------- +# def test_parse_pr_url_valid(self): +# owner, repo, pr_num = self._provider()._parse_pr_url('https://gitea.example.com/owner/repo/pulls/123') +# assert (owner, repo, pr_num) == ('owner', 'repo', '123') +# +# def test_parse_pr_url_invalid(self): +# with pytest.raises(ValueError): +# GiteaProvider._parse_pr_url('https://gitea.example.com/owner/repo') +# +# # ---------------- simple getters ---------------- +# def test_get_files(self): +# provider = self._provider() +# mock_resp = MagicMock() +# mock_resp.json.return_value = [{'filename': 'a.txt'}, {'filename': 'b.txt'}] +# mock_resp.raise_for_status = MagicMock() +# with patch('requests.get', return_value=mock_resp) as mock_get: +# assert provider.get_files() == ['a.txt', 'b.txt'] +# mock_get.assert_called_once() +# +# def test_get_diff_files(self): +# provider = self._provider() +# mock_resp = MagicMock() +# mock_resp.json.return_value = [ +# {'filename': 'f1', 'previous_filename': 'old_f1', 'status': 'renamed', 'patch': ''}, +# {'filename': 'f2', 'status': 'added', 'patch': ''}, +# {'filename': 'f3', 'status': 'deleted', 'patch': ''}, +# {'filename': 'f4', 'status': 'modified', 'patch': ''} +# ] +# mock_resp.raise_for_status = MagicMock() +# with patch('requests.get', return_value=mock_resp): +# res = provider.get_diff_files() +# assert [f.edit_type for f in res] == [EDIT_TYPE.RENAMED, EDIT_TYPE.ADDED, EDIT_TYPE.DELETED, +# EDIT_TYPE.MODIFIED] +# +# # ---------------- publishing methods ---------------- +# def test_publish_description(self): +# provider = self._provider() +# mock_resp = MagicMock(); +# mock_resp.raise_for_status = MagicMock() +# with patch('requests.patch', return_value=mock_resp) as mock_patch: +# provider.publish_description('t', 'b'); +# mock_patch.assert_called_once() +# +# def test_publish_comment(self): +# provider = self._provider() +# mock_resp = MagicMock(); +# mock_resp.raise_for_status = MagicMock() +# with patch('requests.post', return_value=mock_resp) as mock_post: +# provider.publish_comment('c'); +# mock_post.assert_called_once() +# +# def test_publish_inline_comment(self): +# provider = self._provider() +# mock_resp = MagicMock(); +# mock_resp.raise_for_status = MagicMock() +# with patch('requests.post', return_value=mock_resp) as mock_post: +# provider.publish_inline_comment('body', 'file', '10'); +# mock_post.assert_called_once() +# +# # ---------------- labels & reactions ---------------- +# def test_get_pr_labels(self): +# provider = self._provider() +# mock_resp = MagicMock(); +# mock_resp.raise_for_status = MagicMock(); +# mock_resp.json.return_value = [{'name': 'l1'}] +# with patch('requests.get', return_value=mock_resp): +# assert provider.get_pr_labels() == ['l1'] +# +# def test_add_eyes_reaction(self): +# provider = self._provider() +# mock_resp = MagicMock(); +# mock_resp.raise_for_status = MagicMock(); +# mock_resp.json.return_value = {'id': 7} +# with patch('requests.post', return_value=mock_resp): +# assert provider.add_eyes_reaction(1) == 7 +# +# # ---------------- commit messages & url helpers ---------------- +# def test_get_commit_messages(self): +# provider = self._provider() +# mock_resp = MagicMock(); +# mock_resp.raise_for_status = MagicMock() +# mock_resp.json.return_value = [ +# {'commit': {'message': 'm1'}}, {'commit': {'message': 'm2'}}] +# with patch('requests.get', return_value=mock_resp): +# assert provider.get_commit_messages() == ['m1', 'm2'] +# +# def test_git_url_helpers(self): +# provider = self._provider() +# issues_url = 'https://gitea.example.com/owner/repo/pulls/3' +# assert provider.get_git_repo_url(issues_url) == 'https://gitea.example.com/owner/repo.git' +# prefix, suffix = provider.get_canonical_url_parts('https://gitea.example.com/owner/repo.git', 'dev') +# assert prefix == 'https://gitea.example.com/owner/repo/src/branch/dev' +# assert suffix == '' From fd1a27c2ac3f13363c65e1322513c34abd5bb84e Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Wed, 28 May 2025 09:12:13 +0300 Subject: [PATCH 343/365] docs: add dedicated "Chat on code suggestions" core ability page and update related documentation - Introduced a new core abilities page detailing the "Chat on code suggestions" feature, including setup, activation, and usage patterns. - Removed the corresponding section from the improve tool documentation to avoid duplication. - Updated the core abilities index and mkdocs navigation to include the new page. - Improved formatting in the implement tool documentation for clarity and consistency. --- .../chat_on_code_suggestions.md | 55 +++++++++++++++++++ docs/docs/core-abilities/index.md | 1 + docs/docs/tools/implement.md | 54 +++++++++--------- docs/docs/tools/improve.md | 39 ------------- docs/mkdocs.yml | 1 + 5 files changed, 84 insertions(+), 66 deletions(-) create mode 100644 docs/docs/core-abilities/chat_on_code_suggestions.md diff --git a/docs/docs/core-abilities/chat_on_code_suggestions.md b/docs/docs/core-abilities/chat_on_code_suggestions.md new file mode 100644 index 0000000000..086c70ee17 --- /dev/null +++ b/docs/docs/core-abilities/chat_on_code_suggestions.md @@ -0,0 +1,55 @@ +# Chat on code suggestions 💎 + +`Supported Git Platforms: GitHub, GitLab` + +## Overview + +Qodo Merge implements an orchestrator agent that enables interactive code discussions, listening and responding to comments without requiring explicit tool calls. +The orchestrator intelligently analyzes your responses to determine if you want to implement a suggestion, ask a question, or request help, then delegates to the appropriate specialized tool. + +To minimize unnecessary notifications and maintain focused discussions, the orchestrator agent will only respond to comments made directly within the inline code suggestion discussions it has created (`/improve`) or within discussions initiated by the `/implement` command. + +## Getting Started + +### Setup + +Enable interactive code discussions by adding the following to your configuration file (default is `True`): + +```toml +[pr_code_suggestions] +enable_chat_in_code_suggestions = true +``` + + +### Activation + +#### `/improve` + +To obtain dynamic responses, the following steps are required: + +1. Run the `/improve` command (mostly automatic) +2. Check the `/improve` recommendation checkboxes (_Apply this suggestion_) to have Qodo Merge generate a new inline code suggestion discussion +3. The orchestrator agent will then automatically listen to and reply to comments within the discussion without requiring additional commands + +#### `/implement` + +To obtain dynamic responses, the following steps are required: + +1. Select code lines in the PR diff and run the `/implement` command +2. Wait for Qodo Merge to generate a new inline code suggestion +3. The orchestrator agent will then automatically listen to and reply to comments within the discussion without requiring additional commands + + +## Explore the available interaction patterns + +!!! tip "Tip: Direct the agent with keywords" + Use "implement" or "apply" for code generation. Use "explain", "why", or "how" for information and help. + +=== "Asking for Details" + ![Chat on code suggestions ask](https://codium.ai/images/pr_agent/improve_chat_on_code_suggestions_ask.png){width=512} + +=== "Implementing Suggestions" + ![Chat on code suggestions implement](https://codium.ai/images/pr_agent/improve_chat_on_code_suggestions_implement.png){width=512} + +=== "Providing Additional Help" + ![Chat on code suggestions help](https://codium.ai/images/pr_agent/improve_chat_on_code_suggestions_help.png){width=512} diff --git a/docs/docs/core-abilities/index.md b/docs/docs/core-abilities/index.md index b97260ee78..8e07f24fc0 100644 --- a/docs/docs/core-abilities/index.md +++ b/docs/docs/core-abilities/index.md @@ -3,6 +3,7 @@ Qodo Merge utilizes a variety of core abilities to provide a comprehensive and efficient code review experience. These abilities include: - [Auto best practices](https://qodo-merge-docs.qodo.ai/core-abilities/auto_best_practices/) +- [Chat on code suggestions](https://qodo-merge-docs.qodo.ai/core-abilities/chat_on_code_suggestions/) - [Code validation](https://qodo-merge-docs.qodo.ai/core-abilities/code_validation/) - [Compression strategy](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) - [Dynamic context](https://qodo-merge-docs.qodo.ai/core-abilities/dynamic_context/) diff --git a/docs/docs/tools/implement.md b/docs/docs/tools/implement.md index 9340142503..83ccd10136 100644 --- a/docs/docs/tools/implement.md +++ b/docs/docs/tools/implement.md @@ -7,50 +7,50 @@ It leverages LLM technology to transform PR comments and review suggestions into ## Usage Scenarios -### For Reviewers +=== "For Reviewers" -Reviewers can request code changes by: + Reviewers can request code changes by: -1. Selecting the code block to be modified. -2. Adding a comment with the syntax: + 1. Selecting the code block to be modified. + 2. Adding a comment with the syntax: -``` -/implement -``` + ``` + /implement + ``` -![implement1](https://codium.ai/images/pr_agent/implement1.png){width=640} + ![implement1](https://codium.ai/images/pr_agent/implement1.png){width=640} -### For PR Authors +=== "For PR Authors" -PR authors can implement suggested changes by replying to a review comment using either:
+ PR authors can implement suggested changes by replying to a review comment using either: -1. Add specific implementation details as described above + 1. Add specific implementation details as described above -``` -/implement -``` + ``` + /implement + ``` -2. Use the original review comment as instructions + 2. Use the original review comment as instructions -``` -/implement -``` + ``` + /implement + ``` -![implement2](https://codium.ai/images/pr_agent/implement2.png){width=640} + ![implement2](https://codium.ai/images/pr_agent/implement2.png){width=640} -### For Referencing Comments +=== "For Referencing Comments" -You can reference and implement changes from any comment by: + You can reference and implement changes from any comment by: -``` -/implement -``` + ``` + /implement + ``` -![implement3](https://codium.ai/images/pr_agent/implement3.png){width=640} + ![implement3](https://codium.ai/images/pr_agent/implement3.png){width=640} -Note that the implementation will occur within the review discussion thread. + Note that the implementation will occur within the review discussion thread. -**Configuration options** +## Configuration options - Use `/implement` to implement code change within and based on the review discussion. - Use `/implement ` inside a review discussion to implement specific instructions. diff --git a/docs/docs/tools/improve.md b/docs/docs/tools/improve.md index 54ece175cf..2ca0c74cf1 100644 --- a/docs/docs/tools/improve.md +++ b/docs/docs/tools/improve.md @@ -288,45 +288,6 @@ We advise users to apply critical analysis and judgment when implementing the pr In addition to mistakes (which may happen, but are rare), sometimes the presented code modification may serve more as an _illustrative example_ than a directly applicable solution. In such cases, we recommend prioritizing the suggestion's detailed description, using the diff snippet primarily as a supporting reference. - -### Chat on code suggestions - -> `💎 feature` Platforms supported: GitHub, GitLab - -Qodo Merge implements an orchestrator agent that enables interactive code discussions, listening and responding to comments without requiring explicit tool calls. -The orchestrator intelligently analyzes your responses to determine if you want to implement a suggestion, ask a question, or request help, then delegates to the appropriate specialized tool. - -#### Setup and Activation - -Enable interactive code discussions by adding the following to your configuration file (default is `True`): - -```toml -[pr_code_suggestions] -enable_chat_in_code_suggestions = true -``` - -!!! info "Activating Dynamic Responses" - To obtain dynamic responses, the following steps are required: - - 1. Run the `/improve` command (mostly automatic) - 2. Tick the `/improve` recommendation checkboxes (_Apply this suggestion_) to have Qodo Merge generate a new inline code suggestion discussion - 3. The orchestrator agent will then automatically listen and reply to comments within the discussion without requiring additional commands - -#### Explore the available interaction patterns: - -!!! tip "Tip: Direct the agent with keywords" - Use "implement" or "apply" for code generation. Use "explain", "why", or "how" for information and help. - -=== "Asking for Details" - ![Chat on code suggestions ask](https://codium.ai/images/pr_agent/improve_chat_on_code_suggestions_ask.png){width=512} - -=== "Implementing Suggestions" - ![Chat on code suggestions implement](https://codium.ai/images/pr_agent/improve_chat_on_code_suggestions_implement.png){width=512} - -=== "Providing Additional Help" - ![Chat on code suggestions help](https://codium.ai/images/pr_agent/improve_chat_on_code_suggestions_help.png){width=512} - - ### Dual publishing mode Our recommended approach for presenting code suggestions is through a [table](https://qodo-merge-docs.qodo.ai/tools/improve/#overview) (`--pr_code_suggestions.commitable_code_suggestions=false`). diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index a25c081ba1..740488ad6a 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -44,6 +44,7 @@ nav: - Core Abilities: - 'core-abilities/index.md' - Auto best practices: 'core-abilities/auto_best_practices.md' + - Chat on code suggestions: 'core-abilities/chat_on_code_suggestions.md' - Code validation: 'core-abilities/code_validation.md' - Compression strategy: 'core-abilities/compression_strategy.md' - Dynamic context: 'core-abilities/dynamic_context.md' From 2b614330ec6c4a139c3606f4d45ed3a2b7378cf9 Mon Sep 17 00:00:00 2001 From: ofir-frd <85901822+ofir-frd@users.noreply.github.com> Date: Wed, 28 May 2025 18:52:09 +0300 Subject: [PATCH 344/365] docs: move Installation Metrics feature from Future Plans to Recent Updates --- docs/docs/recent_updates/index.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/docs/recent_updates/index.md b/docs/docs/recent_updates/index.md index 158fb5ca97..85f21c0782 100644 --- a/docs/docs/recent_updates/index.md +++ b/docs/docs/recent_updates/index.md @@ -12,10 +12,11 @@ It also outlines our development roadmap for the upcoming three months. Please n - **Scan Repo Discussions Tool**: A new tool that analyzes past code discussions to generate a `best_practices.md` file, distilling key insights and recommendations. ([Learn more](https://qodo-merge-docs.qodo.ai/tools/scan_repo_discussions/)) - **Enhanced Models**: Qodo Merge now defaults to a combination of top models (Claude Sonnet 3.7 and Gemini 2.5 Pro) and incorporates dedicated code validation logic for improved results. ([Details 1](https://qodo-merge-docs.qodo.ai/usage-guide/qodo_merge_models/), [Details 2](https://qodo-merge-docs.qodo.ai/core-abilities/code_validation/)) - **Chrome Extension Update**: Qodo Merge Chrome extension now supports single-tenant users. ([Learn more](https://qodo-merge-docs.qodo.ai/chrome-extension/options/#configuration-options/)) + - **Installation Metrics**: Upon installation, Qodo Merge analyzes past PRs for key metrics (e.g., time to merge, time to first reviewer feedback), enabling pre/post-installation comparison to calculate ROI. + === "Future Roadmap" - **Smart Update**: Upon PR updates, Qodo Merge will offer tailored code suggestions, addressing both the entire PR and the specific incremental changes since the last feedback. - **CLI Endpoint**: A new Qodo Merge endpoint will accept lists of before/after code changes, execute Qodo Merge commands, and return the results. - **Simplified Free Tier**: We plan to transition from a two-week free trial to a free tier offering a limited number of suggestions per month per organization. - **Best Practices Hierarchy**: Introducing support for structured best practices, such as for folders in monorepos or a unified best practice file for a group of repositories. - - **Installation Metrics**: Upon installation, Qodo Merge will analyze past PRs for key metrics (e.g., time to merge, time to first reviewer feedback), enabling pre/post-installation comparison to calculate ROI. \ No newline at end of file From b4cef661e6128a9fa6be6755f2463ca13a873ae2 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 28 May 2025 19:35:31 +0300 Subject: [PATCH 345/365] docs: enhance review.md with ticket compliance labels and merge blocking guidance --- docs/docs/tools/review.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/docs/tools/review.md b/docs/docs/tools/review.md index 62d756347d..56029c7ff1 100644 --- a/docs/docs/tools/review.md +++ b/docs/docs/tools/review.md @@ -152,8 +152,16 @@ extra_instructions = "..." - **`possible security issue`**: This label is applied if the tool detects a potential [security vulnerability](https://github.com/qodo-ai/pr-agent/blob/main/pr_agent/settings/pr_reviewer_prompts.toml#L103) in the PR's code. This feedback is controlled by the 'enable_review_labels_security' flag. - **`review effort [x/5]`**: This label estimates the [effort](https://github.com/qodo-ai/pr-agent/blob/main/pr_agent/settings/pr_reviewer_prompts.toml#L90) required to review the PR on a relative scale of 1 to 5, where 'x' represents the assessed effort. This feedback is controlled by the 'enable_review_labels_effort' flag. + - **`ticket compliance`**: Adds a label indicating code compliance level ("Fully compliant" | "PR Code Verified" | "Partially compliant" | "Not compliant") to any GitHub/Jira/Linea ticket linked in the PR. Controlled by the 'require_ticket_labels' flag (default: false). If 'require_no_ticket_labels' is also enabled, PRs without ticket links will receive a "No ticket found" label. - Note: The `possible security issue` label highlights potential security risks. You can configure a GitHub Action to [prevent merging](https://medium.com/sequra-tech/quick-tip-block-pull-request-merge-using-labels-6cc326936221) PRs that have this label. + +### Blocking PRs from merging based on the generated labels + +!!! tip "" + + You can configure a CI/CD Action to prevent merging PRs with specific labels. For example, implement a dedicated [GitHub Action](https://medium.com/sequra-tech/quick-tip-block-pull-request-merge-using-labels-6cc326936221). + This approach helps ensure PRs with potential security issues or ticket compliance problems will not be merged without further review. + Since AI may make mistakes or lack complete context, use this feature judiciously. For flexibility, users with appropriate permissions can remove generated labels when necessary. Any label removal will be documented in the PR discussion, clearly indicating it was a deliberate action by an authorized user to override the AI blocking the merge. ### Extra instructions From e9ce3ae8699c9da6a879f6249a9c0f5946f804f8 Mon Sep 17 00:00:00 2001 From: Tal Date: Wed, 28 May 2025 19:37:23 +0300 Subject: [PATCH 346/365] Update docs/docs/tools/review.md Co-authored-by: qodo-merge-pro-for-open-source[bot] <189517486+qodo-merge-pro-for-open-source[bot]@users.noreply.github.com> --- docs/docs/tools/review.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/tools/review.md b/docs/docs/tools/review.md index 56029c7ff1..67b5f024e6 100644 --- a/docs/docs/tools/review.md +++ b/docs/docs/tools/review.md @@ -161,7 +161,7 @@ extra_instructions = "..." You can configure a CI/CD Action to prevent merging PRs with specific labels. For example, implement a dedicated [GitHub Action](https://medium.com/sequra-tech/quick-tip-block-pull-request-merge-using-labels-6cc326936221). This approach helps ensure PRs with potential security issues or ticket compliance problems will not be merged without further review. - Since AI may make mistakes or lack complete context, use this feature judiciously. For flexibility, users with appropriate permissions can remove generated labels when necessary. Any label removal will be documented in the PR discussion, clearly indicating it was a deliberate action by an authorized user to override the AI blocking the merge. + Since AI may make mistakes or lack complete context, use this feature judiciously. For flexibility, users with appropriate permissions can remove generated labels when necessary. When a label is removed, this action will be automatically documented in the PR discussion, clearly indicating it was a deliberate override by an authorized user to allow the merge. ### Extra instructions From a17100e51259464a4d729d03fe9b7449b87b9d89 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 28 May 2025 19:42:27 +0300 Subject: [PATCH 347/365] docs: improve clarity and formatting of review tool labels documentation --- docs/docs/tools/review.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/docs/tools/review.md b/docs/docs/tools/review.md index 67b5f024e6..b94d6394f8 100644 --- a/docs/docs/tools/review.md +++ b/docs/docs/tools/review.md @@ -144,14 +144,14 @@ extra_instructions = "..." Meaning the `review` tool will run automatically on every PR, without any additional configurations. Edit this field to enable/disable the tool, or to change the configurations used. -### Auto-generated PR labels from the Review Tool +### Auto-generated PR labels by the Review Tool !!! tip "" - The `review` tool automatically adds two specific labels to your Pull Requests: + The `review` can tool automatically add labels to your Pull Requests: - - **`possible security issue`**: This label is applied if the tool detects a potential [security vulnerability](https://github.com/qodo-ai/pr-agent/blob/main/pr_agent/settings/pr_reviewer_prompts.toml#L103) in the PR's code. This feedback is controlled by the 'enable_review_labels_security' flag. - - **`review effort [x/5]`**: This label estimates the [effort](https://github.com/qodo-ai/pr-agent/blob/main/pr_agent/settings/pr_reviewer_prompts.toml#L90) required to review the PR on a relative scale of 1 to 5, where 'x' represents the assessed effort. This feedback is controlled by the 'enable_review_labels_effort' flag. + - **`possible security issue`**: This label is applied if the tool detects a potential [security vulnerability](https://github.com/qodo-ai/pr-agent/blob/main/pr_agent/settings/pr_reviewer_prompts.toml#L103) in the PR's code. This feedback is controlled by the 'enable_review_labels_security' flag (default is true). + - **`review effort [x/5]`**: This label estimates the [effort](https://github.com/qodo-ai/pr-agent/blob/main/pr_agent/settings/pr_reviewer_prompts.toml#L90) required to review the PR on a relative scale of 1 to 5, where 'x' represents the assessed effort. This feedback is controlled by the 'enable_review_labels_effort' flag (default is true). - **`ticket compliance`**: Adds a label indicating code compliance level ("Fully compliant" | "PR Code Verified" | "Partially compliant" | "Not compliant") to any GitHub/Jira/Linea ticket linked in the PR. Controlled by the 'require_ticket_labels' flag (default: false). If 'require_no_ticket_labels' is also enabled, PRs without ticket links will receive a "No ticket found" label. @@ -160,7 +160,9 @@ extra_instructions = "..." !!! tip "" You can configure a CI/CD Action to prevent merging PRs with specific labels. For example, implement a dedicated [GitHub Action](https://medium.com/sequra-tech/quick-tip-block-pull-request-merge-using-labels-6cc326936221). + This approach helps ensure PRs with potential security issues or ticket compliance problems will not be merged without further review. + Since AI may make mistakes or lack complete context, use this feature judiciously. For flexibility, users with appropriate permissions can remove generated labels when necessary. When a label is removed, this action will be automatically documented in the PR discussion, clearly indicating it was a deliberate override by an authorized user to allow the merge. ### Extra instructions From 8143f4b35b130b164b60c97f1fbb94779fa0b77b Mon Sep 17 00:00:00 2001 From: "Hussam.lawen" Date: Wed, 28 May 2025 22:23:24 +0300 Subject: [PATCH 348/365] docs: add Linear integration to ticket context fetching documentation --- .../core-abilities/fetching_ticket_context.md | 53 +++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/docs/docs/core-abilities/fetching_ticket_context.md b/docs/docs/core-abilities/fetching_ticket_context.md index c6424ad1b9..db6e72e1ca 100644 --- a/docs/docs/core-abilities/fetching_ticket_context.md +++ b/docs/docs/core-abilities/fetching_ticket_context.md @@ -11,6 +11,7 @@ This integration enriches the review process by automatically surfacing relevant - GitHub - Jira (💎) +- Linear (💎) **Ticket data fetched:** @@ -75,13 +76,17 @@ The recommended way to authenticate with Jira Cloud is to install the Qodo Merge Installation steps: -1. Click [here](https://auth.atlassian.com/authorize?audience=api.atlassian.com&client_id=8krKmA4gMD8mM8z24aRCgPCSepZNP1xf&scope=read%3Ajira-work%20offline_access&redirect_uri=https%3A%2F%2Fregister.jira.pr-agent.codium.ai&state=qodomerge&response_type=code&prompt=consent) to install the Qodo Merge app in your Jira Cloud instance, click the `accept` button.
+1. Go to the [Qodo Merge integrations page](https://app.qodo.ai/qodo-merge/integrations) + +2. Click on the Connect **Jira Cloud** button to connect the Jira Cloud app + +3. Click the `accept` button.
![Jira Cloud App Installation](https://www.qodo.ai/images/pr_agent/jira_app_installation1.png){width=384} -2. After installing the app, you will be redirected to the Qodo Merge registration page. and you will see a success message.
+4. After installing the app, you will be redirected to the Qodo Merge registration page. and you will see a success message.
![Jira Cloud App success message](https://www.qodo.ai/images/pr_agent/jira_app_success.png){width=384} -3. Now Qodo Merge will be able to fetch Jira ticket context for your PRs. +5. Now Qodo Merge will be able to fetch Jira ticket context for your PRs. **2) Email/Token Authentication** @@ -300,3 +305,45 @@ Name your branch with the ticket ID as a prefix (e.g., `ISSUE-123-feature-descri [jira] jira_base_url = "https://.atlassian.net" ``` + +## Linear Integration 💎 + +### Linear App Authentication + +The recommended way to authenticate with Linear is to connect the Linear app through the Qodo Merge portal. + +Installation steps: + +1. Go to the [Qodo Merge integrations page](https://app.qodo.ai/qodo-merge/integrations) + +2. Navigate to the **Integrations** tab + +3. Click on the **Linear** button to connect the Linear app + +4. Follow the authentication flow to authorize Qodo Merge to access your Linear workspace + +5. Once connected, Qodo Merge will be able to fetch Linear ticket context for your PRs + +### How to link a PR to a Linear ticket + +Qodo Merge will automatically detect Linear tickets using either of these methods: + +**Method 1: Description Reference:** + +Include a ticket reference in your PR description using either: +- The complete Linear ticket URL: `https://linear.app/[ORG_ID]/issue/[TICKET_ID]` +- The shortened ticket ID: `[TICKET_ID]` (e.g., `ABC-123`) - requires linear_base_url configuration (see below). + +**Method 2: Branch Name Detection:** + +Name your branch with the ticket ID as a prefix (e.g., `ABC-123-feature-description` or `feature/ABC-123/feature-description`). + +!!! note "Linear Base URL" + For shortened ticket IDs or branch detection (method 2), you must configure the Linear base URL in your configuration file under the [linear] section: + + ```toml + [linear] + linear_base_url = "https://linear.app/[ORG_ID]" + ``` + + Replace `[ORG_ID]` with your Linear organization identifier. \ No newline at end of file From 2e75fa31bd550469444212202316842df8ec6c01 Mon Sep 17 00:00:00 2001 From: "Hussam.lawen" Date: Thu, 29 May 2025 08:29:41 +0300 Subject: [PATCH 349/365] docs: add hyperlinks to ticket system integrations in fetching_ticket_context.md --- docs/docs/core-abilities/fetching_ticket_context.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/docs/core-abilities/fetching_ticket_context.md b/docs/docs/core-abilities/fetching_ticket_context.md index db6e72e1ca..05e19c00df 100644 --- a/docs/docs/core-abilities/fetching_ticket_context.md +++ b/docs/docs/core-abilities/fetching_ticket_context.md @@ -9,9 +9,9 @@ This integration enriches the review process by automatically surfacing relevant **Ticket systems supported**: -- GitHub -- Jira (💎) -- Linear (💎) +- [GitHub](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/#github-issues-integration) +- [Jira (💎)](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/#jira-integration) +- [Linear (💎)](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/#linear-integration) **Ticket data fetched:** From c57f8aff9bfb83f0236f98227b1d1fecdb10da7d Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Fri, 30 May 2025 13:35:58 +0300 Subject: [PATCH 350/365] docs: add Help Docs to the Tools list and reorganize tools documentation in alphabetical order --- docs/docs/tools/index.md | 15 ++++++++------- docs/mkdocs.yml | 36 ++++++++++++++++++------------------ 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/docs/docs/tools/index.md b/docs/docs/tools/index.md index e422b856d0..36e3caef70 100644 --- a/docs/docs/tools/index.md +++ b/docs/docs/tools/index.md @@ -3,22 +3,23 @@ Here is a list of Qodo Merge tools, each with a dedicated page that explains how to use it: | Tool | Description | -| ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | +|------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------| | **[PR Description (`/describe`](./describe.md))** | Automatically generating PR description - title, type, summary, code walkthrough and labels | | **[PR Review (`/review`](./review.md))** | Adjustable feedback about the PR, possible issues, security concerns, review effort and more | | **[Code Suggestions (`/improve`](./improve.md))** | Code suggestions for improving the PR | | **[Question Answering (`/ask ...`](./ask.md))** | Answering free-text questions about the PR, or on specific code lines | -| **[Update Changelog (`/update_changelog`](./update_changelog.md))** | Automatically updating the CHANGELOG.md file with the PR changes | | **[Help (`/help`](./help.md))** | Provides a list of all the available tools. Also enables to trigger them interactively (💎) | +| **[Help Docs (`/help_docs`](./help_docs.md))** | Answer a free-text question based on a git documentation folder. | +| **[Update Changelog (`/update_changelog`](./update_changelog.md))** | Automatically updating the CHANGELOG.md file with the PR changes | | **💎 [Add Documentation (`/add_docs`](./documentation.md))** | Generates documentation to methods/functions/classes that changed in the PR | -| **💎 [Generate Custom Labels (`/generate_labels`](./custom_labels.md))** | Generates custom labels for the PR, based on specific guidelines defined by the user | | **💎 [Analyze (`/analyze`](./analyze.md))** | Identify code components that changed in the PR, and enables to interactively generate tests, docs, and code suggestions for each component | -| **💎 [Test (`/test`](./test.md))** | generate tests for a selected component, based on the PR code changes | +| **💎 [CI Feedback (`/checks ci_job`](./ci_feedback.md))** | Automatically generates feedback and analysis for a failed CI job | | **💎 [Custom Prompt (`/custom_prompt`](./custom_prompt.md))** | Automatically generates custom suggestions for improving the PR code, based on specific guidelines defined by the user | +| **💎 [Generate Custom Labels (`/generate_labels`](./custom_labels.md))** | Generates custom labels for the PR, based on specific guidelines defined by the user | | **💎 [Generate Tests (`/test component_name`](./test.md))** | Automatically generates unit tests for a selected component, based on the PR code changes | -| **💎 [Improve Component (`/improve_component component_name`](./improve_component.md))** | Generates code suggestions for a specific code component that changed in the PR | -| **💎 [CI Feedback (`/checks ci_job`](./ci_feedback.md))** | Automatically generates feedback and analysis for a failed CI job | | **💎 [Implement (`/implement`](./implement.md))** | Generates implementation code from review suggestions | +| **💎 [Improve Component (`/improve_component component_name`](./improve_component.md))** | Generates code suggestions for a specific code component that changed in the PR | | **💎 [Scan Repo Discussions (`/scan_repo_discussions`](./scan_repo_discussions.md))** | Generates `best_practices.md` file based on previous discussions in the repository | +| **💎 [Test (`/test`](./test.md))** | generate tests for a selected component, based on the PR code changes | -Note that the tools marked with 💎 are available only for Qodo Merge users. +Note that the tools marked with 💎 are available only for Qodo Merge users. \ No newline at end of file diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 740488ad6a..c45e4616c6 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -23,24 +23,24 @@ nav: - Frequently Asked Questions: 'faq/index.md' - 💎 Qodo Merge Models: 'usage-guide/qodo_merge_models.md' - Tools: - - 'tools/index.md' - - Describe: 'tools/describe.md' - - Review: 'tools/review.md' - - Improve: 'tools/improve.md' - - Ask: 'tools/ask.md' - - Update Changelog: 'tools/update_changelog.md' - - Help Docs: 'tools/help_docs.md' - - Help: 'tools/help.md' - - 💎 Analyze: 'tools/analyze.md' - - 💎 Test: 'tools/test.md' - - 💎 Improve Component: 'tools/improve_component.md' - - 💎 Documentation: 'tools/documentation.md' - - 💎 Custom Labels: 'tools/custom_labels.md' - - 💎 Custom Prompt: 'tools/custom_prompt.md' - - 💎 CI Feedback: 'tools/ci_feedback.md' - - 💎 Similar Code: 'tools/similar_code.md' - - 💎 Implement: 'tools/implement.md' - - 💎 Scan Repo Discussions: 'tools/scan_repo_discussions.md' + - 'tools/index.md' + - Describe: 'tools/describe.md' + - Review: 'tools/review.md' + - Improve: 'tools/improve.md' + - Ask: 'tools/ask.md' + - Help: 'tools/help.md' + - Help Docs: 'tools/help_docs.md' + - Update Changelog: 'tools/update_changelog.md' + - 💎 Analyze: 'tools/analyze.md' + - 💎 CI Feedback: 'tools/ci_feedback.md' + - 💎 Custom Labels: 'tools/custom_labels.md' + - 💎 Custom Prompt: 'tools/custom_prompt.md' + - 💎 Documentation: 'tools/documentation.md' + - 💎 Implement: 'tools/implement.md' + - 💎 Improve Component: 'tools/improve_component.md' + - 💎 Scan Repo Discussions: 'tools/scan_repo_discussions.md' + - 💎 Similar Code: 'tools/similar_code.md' + - 💎 Test: 'tools/test.md' - Core Abilities: - 'core-abilities/index.md' - Auto best practices: 'core-abilities/auto_best_practices.md' From e7f85cf858324920c905f6c91b457017b63fa047 Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Fri, 30 May 2025 13:58:48 +0300 Subject: [PATCH 351/365] docs: reorganize tools documentation in alphabetical order and fix inconsistencies --- docs/docs/tools/index.md | 3 +-- docs/mkdocs.yml | 9 ++++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/docs/docs/tools/index.md b/docs/docs/tools/index.md index 36e3caef70..d44a988b95 100644 --- a/docs/docs/tools/index.md +++ b/docs/docs/tools/index.md @@ -16,10 +16,9 @@ Here is a list of Qodo Merge tools, each with a dedicated page that explains how | **💎 [CI Feedback (`/checks ci_job`](./ci_feedback.md))** | Automatically generates feedback and analysis for a failed CI job | | **💎 [Custom Prompt (`/custom_prompt`](./custom_prompt.md))** | Automatically generates custom suggestions for improving the PR code, based on specific guidelines defined by the user | | **💎 [Generate Custom Labels (`/generate_labels`](./custom_labels.md))** | Generates custom labels for the PR, based on specific guidelines defined by the user | -| **💎 [Generate Tests (`/test component_name`](./test.md))** | Automatically generates unit tests for a selected component, based on the PR code changes | +| **💎 [Generate Tests (`/test`](./test.md))** | Automatically generates unit tests for a selected component, based on the PR code changes | | **💎 [Implement (`/implement`](./implement.md))** | Generates implementation code from review suggestions | | **💎 [Improve Component (`/improve_component component_name`](./improve_component.md))** | Generates code suggestions for a specific code component that changed in the PR | | **💎 [Scan Repo Discussions (`/scan_repo_discussions`](./scan_repo_discussions.md))** | Generates `best_practices.md` file based on previous discussions in the repository | -| **💎 [Test (`/test`](./test.md))** | generate tests for a selected component, based on the PR code changes | Note that the tools marked with 💎 are available only for Qodo Merge users. \ No newline at end of file diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index c45e4616c6..d8e09a7f51 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -31,16 +31,15 @@ nav: - Help: 'tools/help.md' - Help Docs: 'tools/help_docs.md' - Update Changelog: 'tools/update_changelog.md' + - 💎 Add Documentation: 'tools/documentation.md' - 💎 Analyze: 'tools/analyze.md' - 💎 CI Feedback: 'tools/ci_feedback.md' - - 💎 Custom Labels: 'tools/custom_labels.md' - 💎 Custom Prompt: 'tools/custom_prompt.md' - - 💎 Documentation: 'tools/documentation.md' + - 💎 Generate Labels: 'tools/custom_labels.md' + - 💎 Generate Tests: 'tools/test.md' - 💎 Implement: 'tools/implement.md' - - 💎 Improve Component: 'tools/improve_component.md' + - 💎 Improve Components: 'tools/improve_component.md' - 💎 Scan Repo Discussions: 'tools/scan_repo_discussions.md' - - 💎 Similar Code: 'tools/similar_code.md' - - 💎 Test: 'tools/test.md' - Core Abilities: - 'core-abilities/index.md' - Auto best practices: 'core-abilities/auto_best_practices.md' From d857132d1d9073e6a9aececd9841292ea4e911fd Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Fri, 30 May 2025 14:04:45 +0300 Subject: [PATCH 352/365] docs: add Similar Code tool to documentation index and navigation --- docs/docs/tools/index.md | 1 + docs/mkdocs.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/docs/tools/index.md b/docs/docs/tools/index.md index d44a988b95..e50e078548 100644 --- a/docs/docs/tools/index.md +++ b/docs/docs/tools/index.md @@ -20,5 +20,6 @@ Here is a list of Qodo Merge tools, each with a dedicated page that explains how | **💎 [Implement (`/implement`](./implement.md))** | Generates implementation code from review suggestions | | **💎 [Improve Component (`/improve_component component_name`](./improve_component.md))** | Generates code suggestions for a specific code component that changed in the PR | | **💎 [Scan Repo Discussions (`/scan_repo_discussions`](./scan_repo_discussions.md))** | Generates `best_practices.md` file based on previous discussions in the repository | +| **💎 [Similar Code (`/similar_code`](./similar_code.md))** | Retrieves the most similar code components from inside the organization's codebase, or from open-source code. | Note that the tools marked with 💎 are available only for Qodo Merge users. \ No newline at end of file diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index d8e09a7f51..6210ed1c2d 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -40,6 +40,7 @@ nav: - 💎 Implement: 'tools/implement.md' - 💎 Improve Components: 'tools/improve_component.md' - 💎 Scan Repo Discussions: 'tools/scan_repo_discussions.md' + - 💎 Similar Code: 'tools/similar_code.md' - Core Abilities: - 'core-abilities/index.md' - Auto best practices: 'core-abilities/auto_best_practices.md' From 403efcae226a6c3c4914a4cc466e5d8b7ca51b90 Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Fri, 30 May 2025 14:16:09 +0300 Subject: [PATCH 353/365] docs: reorganize tools list in alphabetical order in README.md --- README.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 8fcfb0d012..e16e771f14 100644 --- a/README.md +++ b/README.md @@ -72,27 +72,27 @@ Supported commands per platform: | | | GitHub | GitLab | Bitbucket | Azure DevOps | Gitea | | ----- |---------------------------------------------------------------------------------------------------------|:------:|:------:|:---------:|:------------:|:-----:| -| TOOLS | [Review](https://qodo-merge-docs.qodo.ai/tools/review/) | ✅ | ✅ | ✅ | ✅ | ✅ | -| | [Describe](https://qodo-merge-docs.qodo.ai/tools/describe/) | ✅ | ✅ | ✅ | ✅ | ✅ | +| TOOLS | [Describe](https://qodo-merge-docs.qodo.ai/tools/describe/) | ✅ | ✅ | ✅ | ✅ | ✅ | +| | [Review](https://qodo-merge-docs.qodo.ai/tools/review/) | ✅ | ✅ | ✅ | ✅ | ✅ | | | [Improve](https://qodo-merge-docs.qodo.ai/tools/improve/) | ✅ | ✅ | ✅ | ✅ | ✅ | | | [Ask](https://qodo-merge-docs.qodo.ai/tools/ask/) | ✅ | ✅ | ✅ | ✅ | | | | ⮑ [Ask on code lines](https://qodo-merge-docs.qodo.ai/tools/ask/#ask-lines) | ✅ | ✅ | | | | -| | [Update CHANGELOG](https://qodo-merge-docs.qodo.ai/tools/update_changelog/) | ✅ | ✅ | ✅ | ✅ | | | | [Help Docs](https://qodo-merge-docs.qodo.ai/tools/help_docs/?h=auto#auto-approval) | ✅ | ✅ | ✅ | | | -| | [Ticket Context](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/) 💎 | ✅ | ✅ | ✅ | | | -| | [Utilizing Best Practices](https://qodo-merge-docs.qodo.ai/tools/improve/#best-practices) 💎 | ✅ | ✅ | ✅ | | | -| | [PR Chat](https://qodo-merge-docs.qodo.ai/chrome-extension/features/#pr-chat) 💎 | ✅ | | | | | -| | [Suggestion Tracking](https://qodo-merge-docs.qodo.ai/tools/improve/#suggestion-tracking) 💎 | ✅ | ✅ | | | | -| | [CI Feedback](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/) 💎 | ✅ | | | | | +| | [Update CHANGELOG](https://qodo-merge-docs.qodo.ai/tools/update_changelog/) | ✅ | ✅ | ✅ | ✅ | | | | [PR Documentation](https://qodo-merge-docs.qodo.ai/tools/documentation/) 💎 | ✅ | ✅ | | | | -| | [Custom Labels](https://qodo-merge-docs.qodo.ai/tools/custom_labels/) 💎 | ✅ | ✅ | | | | | | [Analyze](https://qodo-merge-docs.qodo.ai/tools/analyze/) 💎 | ✅ | ✅ | | | | -| | [Similar Code](https://qodo-merge-docs.qodo.ai/tools/similar_code/) 💎 | ✅ | | | | | +| | [Auto-Approve](https://qodo-merge-docs.qodo.ai/tools/improve/?h=auto#auto-approval) 💎 | ✅ | ✅ | ✅ | | | +| | [CI Feedback](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/) 💎 | ✅ | | | | | | | [Custom Prompt](https://qodo-merge-docs.qodo.ai/tools/custom_prompt/) 💎 | ✅ | ✅ | ✅ | | | +| | [Custom Labels](https://qodo-merge-docs.qodo.ai/tools/custom_labels/) 💎 | ✅ | ✅ | | | | | | [Test](https://qodo-merge-docs.qodo.ai/tools/test/) 💎 | ✅ | ✅ | | | | | | [Implement](https://qodo-merge-docs.qodo.ai/tools/implement/) 💎 | ✅ | ✅ | ✅ | | | | | [Scan Repo Discussions](https://qodo-merge-docs.qodo.ai/tools/scan_repo_discussions/) 💎 | ✅ | | | | | -| | [Auto-Approve](https://qodo-merge-docs.qodo.ai/tools/improve/?h=auto#auto-approval) 💎 | ✅ | ✅ | ✅ | | | +| | [Similar Code](https://qodo-merge-docs.qodo.ai/tools/similar_code/) 💎 | ✅ | | | | | +| | [Ticket Context](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/) 💎 | ✅ | ✅ | ✅ | | | +| | [Utilizing Best Practices](https://qodo-merge-docs.qodo.ai/tools/improve/#best-practices) 💎 | ✅ | ✅ | ✅ | | | +| | [PR Chat](https://qodo-merge-docs.qodo.ai/chrome-extension/features/#pr-chat) 💎 | ✅ | | | | | +| | [Suggestion Tracking](https://qodo-merge-docs.qodo.ai/tools/improve/#suggestion-tracking) 💎 | ✅ | ✅ | | | | | | | | | | | | | USAGE | [CLI](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#local-repo-cli) | ✅ | ✅ | ✅ | ✅ | ✅ | | | [App / webhook](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#github-app) | ✅ | ✅ | ✅ | ✅ | ✅ | From 8baf6dba935a797d4a39124796d728a0f2d05b67 Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Fri, 30 May 2025 14:27:04 +0300 Subject: [PATCH 354/365] docs: reorganize core abilities in alphabetical order in README.md --- README.md | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index e16e771f14..432ff4b751 100644 --- a/README.md +++ b/README.md @@ -99,18 +99,23 @@ Supported commands per platform: | | [Tagging bot](https://github.com/Codium-ai/pr-agent#try-it-now) | ✅ | | | | | | | [Actions](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-action) | ✅ | ✅ | ✅ | ✅ | | | | | | | | | | -| CORE | [PR compression](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) | ✅ | ✅ | ✅ | ✅ | | -| | Adaptive and token-aware file patch fitting | ✅ | ✅ | ✅ | ✅ | | -| | [Multiple models support](https://qodo-merge-docs.qodo.ai/usage-guide/changing_a_model/) | ✅ | ✅ | ✅ | ✅ | | -| | [Local and global metadata](https://qodo-merge-docs.qodo.ai/core-abilities/metadata/) | ✅ | ✅ | ✅ | ✅ | | +| CORE | [Adaptive and token-aware file patch fitting](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) | ✅ | ✅ | ✅ | ✅ | | +| | [Auto Best Practices 💎](https://qodo-merge-docs.qodo.ai/core-abilities/auto_best_practices/) | ✅ | | | | | +| | [Chat on code suggestions](https://qodo-merge-docs.qodo.ai/core-abilities/chat_on_code_suggestions/) | ✅ | ✅ | | | | +| | [Code Validation 💎](https://qodo-merge-docs.qodo.ai/core-abilities/code_validation/) | ✅ | ✅ | ✅ | ✅ | | | | [Dynamic context](https://qodo-merge-docs.qodo.ai/core-abilities/dynamic_context/) | ✅ | ✅ | ✅ | ✅ | | -| | [Self reflection](https://qodo-merge-docs.qodo.ai/core-abilities/self_reflection/) | ✅ | ✅ | ✅ | ✅ | | -| | [Static code analysis](https://qodo-merge-docs.qodo.ai/core-abilities/static_code_analysis/) 💎 | ✅ | ✅ | | | | +| | [Fetching ticket context](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/) | ✅ | ✅ | ✅ | | | | | [Global and wiki configurations](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/) 💎 | ✅ | ✅ | ✅ | | | -| | [PR interactive actions](https://www.qodo.ai/images/pr_agent/pr-actions.mp4) 💎 | ✅ | ✅ | | | | | | [Impact Evaluation](https://qodo-merge-docs.qodo.ai/core-abilities/impact_evaluation/) 💎 | ✅ | ✅ | | | | -| | [Code Validation 💎](https://qodo-merge-docs.qodo.ai/core-abilities/code_validation/) | ✅ | ✅ | ✅ | ✅ | | -| | [Auto Best Practices 💎](https://qodo-merge-docs.qodo.ai/core-abilities/auto_best_practices/) | ✅ | | | | | +| | [Incremental Update](https://qodo-merge-docs.qodo.ai/core-abilities/incremental_update/) | ✅ | | | | | +| | [Interactivity](https://qodo-merge-docs.qodo.ai/core-abilities/interactivity/) | ✅ | ✅ | | | | +| | [Local and global metadata](https://qodo-merge-docs.qodo.ai/core-abilities/metadata/) | ✅ | ✅ | ✅ | ✅ | | +| | [Multiple models support](https://qodo-merge-docs.qodo.ai/usage-guide/changing_a_model/) | ✅ | ✅ | ✅ | ✅ | | +| | [PR compression](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) | ✅ | ✅ | ✅ | ✅ | | +| | [PR interactive actions](https://www.qodo.ai/images/pr_agent/pr-actions.mp4) 💎 | ✅ | ✅ | | | | +| | [RAG context enrichment](https://qodo-merge-docs.qodo.ai/core-abilities/rag_context_enrichment/) | ✅ | | ✅ | | | +| | [Self reflection](https://qodo-merge-docs.qodo.ai/core-abilities/self_reflection/) | ✅ | ✅ | ✅ | ✅ | | +| | [Static code analysis](https://qodo-merge-docs.qodo.ai/core-abilities/static_code_analysis/) 💎 | ✅ | ✅ | | | | - 💎 means this feature is available only in [Qodo Merge](https://www.qodo.ai/pricing/) [//]: # (- Support for additional git providers is described in [here](./docs/Full_environments.md)) From 9c1f5ad49761048ad53e6dc4a210219b2e5cecfc Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Fri, 30 May 2025 14:31:46 +0300 Subject: [PATCH 355/365] docs: rename "Test" to "Generate Tests" and improve tool name consistency in README --- README.md | 92 +++++++++++++++++++++++++++---------------------------- 1 file changed, 45 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 432ff4b751..e778719b5d 100644 --- a/README.md +++ b/README.md @@ -70,52 +70,52 @@ Read more about it [here](https://qodo-merge-docs.qodo.ai/tools/scan_repo_discus Supported commands per platform: -| | | GitHub | GitLab | Bitbucket | Azure DevOps | Gitea | -| ----- |---------------------------------------------------------------------------------------------------------|:------:|:------:|:---------:|:------------:|:-----:| -| TOOLS | [Describe](https://qodo-merge-docs.qodo.ai/tools/describe/) | ✅ | ✅ | ✅ | ✅ | ✅ | -| | [Review](https://qodo-merge-docs.qodo.ai/tools/review/) | ✅ | ✅ | ✅ | ✅ | ✅ | -| | [Improve](https://qodo-merge-docs.qodo.ai/tools/improve/) | ✅ | ✅ | ✅ | ✅ | ✅ | -| | [Ask](https://qodo-merge-docs.qodo.ai/tools/ask/) | ✅ | ✅ | ✅ | ✅ | | -| | ⮑ [Ask on code lines](https://qodo-merge-docs.qodo.ai/tools/ask/#ask-lines) | ✅ | ✅ | | | | -| | [Help Docs](https://qodo-merge-docs.qodo.ai/tools/help_docs/?h=auto#auto-approval) | ✅ | ✅ | ✅ | | | -| | [Update CHANGELOG](https://qodo-merge-docs.qodo.ai/tools/update_changelog/) | ✅ | ✅ | ✅ | ✅ | | -| | [PR Documentation](https://qodo-merge-docs.qodo.ai/tools/documentation/) 💎 | ✅ | ✅ | | | | -| | [Analyze](https://qodo-merge-docs.qodo.ai/tools/analyze/) 💎 | ✅ | ✅ | | | | -| | [Auto-Approve](https://qodo-merge-docs.qodo.ai/tools/improve/?h=auto#auto-approval) 💎 | ✅ | ✅ | ✅ | | | -| | [CI Feedback](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/) 💎 | ✅ | | | | | -| | [Custom Prompt](https://qodo-merge-docs.qodo.ai/tools/custom_prompt/) 💎 | ✅ | ✅ | ✅ | | | -| | [Custom Labels](https://qodo-merge-docs.qodo.ai/tools/custom_labels/) 💎 | ✅ | ✅ | | | | -| | [Test](https://qodo-merge-docs.qodo.ai/tools/test/) 💎 | ✅ | ✅ | | | | -| | [Implement](https://qodo-merge-docs.qodo.ai/tools/implement/) 💎 | ✅ | ✅ | ✅ | | | -| | [Scan Repo Discussions](https://qodo-merge-docs.qodo.ai/tools/scan_repo_discussions/) 💎 | ✅ | | | | | -| | [Similar Code](https://qodo-merge-docs.qodo.ai/tools/similar_code/) 💎 | ✅ | | | | | -| | [Ticket Context](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/) 💎 | ✅ | ✅ | ✅ | | | -| | [Utilizing Best Practices](https://qodo-merge-docs.qodo.ai/tools/improve/#best-practices) 💎 | ✅ | ✅ | ✅ | | | -| | [PR Chat](https://qodo-merge-docs.qodo.ai/chrome-extension/features/#pr-chat) 💎 | ✅ | | | | | -| | [Suggestion Tracking](https://qodo-merge-docs.qodo.ai/tools/improve/#suggestion-tracking) 💎 | ✅ | ✅ | | | | -| | | | | | | | -| USAGE | [CLI](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#local-repo-cli) | ✅ | ✅ | ✅ | ✅ | ✅ | -| | [App / webhook](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#github-app) | ✅ | ✅ | ✅ | ✅ | ✅ | -| | [Tagging bot](https://github.com/Codium-ai/pr-agent#try-it-now) | ✅ | | | | | -| | [Actions](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-action) | ✅ | ✅ | ✅ | ✅ | | -| | | | | | | | -| CORE | [Adaptive and token-aware file patch fitting](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) | ✅ | ✅ | ✅ | ✅ | | -| | [Auto Best Practices 💎](https://qodo-merge-docs.qodo.ai/core-abilities/auto_best_practices/) | ✅ | | | | | +| | | GitHub | GitLab | Bitbucket | Azure DevOps | Gitea | +| ----- |---------------------------------------------------------------------------------------------------------------------|:------:|:------:|:---------:|:------------:|:-----:| +| TOOLS | [Describe](https://qodo-merge-docs.qodo.ai/tools/describe/) | ✅ | ✅ | ✅ | ✅ | ✅ | +| | [Review](https://qodo-merge-docs.qodo.ai/tools/review/) | ✅ | ✅ | ✅ | ✅ | ✅ | +| | [Improve](https://qodo-merge-docs.qodo.ai/tools/improve/) | ✅ | ✅ | ✅ | ✅ | ✅ | +| | [Ask](https://qodo-merge-docs.qodo.ai/tools/ask/) | ✅ | ✅ | ✅ | ✅ | | +| | ⮑ [Ask on code lines](https://qodo-merge-docs.qodo.ai/tools/ask/#ask-lines) | ✅ | ✅ | | | | +| | [Help Docs](https://qodo-merge-docs.qodo.ai/tools/help_docs/?h=auto#auto-approval) | ✅ | ✅ | ✅ | | | +| | [Update CHANGELOG](https://qodo-merge-docs.qodo.ai/tools/update_changelog/) | ✅ | ✅ | ✅ | ✅ | | +| | [PR Documentation](https://qodo-merge-docs.qodo.ai/tools/documentation/) 💎 | ✅ | ✅ | | | | +| | [Analyze](https://qodo-merge-docs.qodo.ai/tools/analyze/) 💎 | ✅ | ✅ | | | | +| | [Auto-Approve](https://qodo-merge-docs.qodo.ai/tools/improve/?h=auto#auto-approval) 💎 | ✅ | ✅ | ✅ | | | +| | [CI Feedback](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/) 💎 | ✅ | | | | | +| | [Custom Prompt](https://qodo-merge-docs.qodo.ai/tools/custom_prompt/) 💎 | ✅ | ✅ | ✅ | | | +| | [Generate Custom Labels](https://qodo-merge-docs.qodo.ai/tools/custom_labels/) 💎 | ✅ | ✅ | | | | +| | [Generate Tests](https://qodo-merge-docs.qodo.ai/tools/test/) 💎 | ✅ | ✅ | | | | +| | [Implement](https://qodo-merge-docs.qodo.ai/tools/implement/) 💎 | ✅ | ✅ | ✅ | | | +| | [Scan Repo Discussions](https://qodo-merge-docs.qodo.ai/tools/scan_repo_discussions/) 💎 | ✅ | | | | | +| | [Similar Code](https://qodo-merge-docs.qodo.ai/tools/similar_code/) 💎 | ✅ | | | | | +| | [Ticket Context](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/) 💎 | ✅ | ✅ | ✅ | | | +| | [Utilizing Best Practices](https://qodo-merge-docs.qodo.ai/tools/improve/#best-practices) 💎 | ✅ | ✅ | ✅ | | | +| | [PR Chat](https://qodo-merge-docs.qodo.ai/chrome-extension/features/#pr-chat) 💎 | ✅ | | | | | +| | [Suggestion Tracking](https://qodo-merge-docs.qodo.ai/tools/improve/#suggestion-tracking) 💎 | ✅ | ✅ | | | | +| | | | | | | | +| USAGE | [CLI](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#local-repo-cli) | ✅ | ✅ | ✅ | ✅ | ✅ | +| | [App / webhook](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#github-app) | ✅ | ✅ | ✅ | ✅ | ✅ | +| | [Tagging bot](https://github.com/Codium-ai/pr-agent#try-it-now) | ✅ | | | | | +| | [Actions](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-action) | ✅ | ✅ | ✅ | ✅ | | +| | | | | | | | +| CORE | [Adaptive and token-aware file patch fitting](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) | ✅ | ✅ | ✅ | ✅ | | +| | [Auto Best Practices 💎](https://qodo-merge-docs.qodo.ai/core-abilities/auto_best_practices/) | ✅ | | | | | | | [Chat on code suggestions](https://qodo-merge-docs.qodo.ai/core-abilities/chat_on_code_suggestions/) | ✅ | ✅ | | | | -| | [Code Validation 💎](https://qodo-merge-docs.qodo.ai/core-abilities/code_validation/) | ✅ | ✅ | ✅ | ✅ | | -| | [Dynamic context](https://qodo-merge-docs.qodo.ai/core-abilities/dynamic_context/) | ✅ | ✅ | ✅ | ✅ | | -| | [Fetching ticket context](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/) | ✅ | ✅ | ✅ | | | -| | [Global and wiki configurations](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/) 💎 | ✅ | ✅ | ✅ | | | -| | [Impact Evaluation](https://qodo-merge-docs.qodo.ai/core-abilities/impact_evaluation/) 💎 | ✅ | ✅ | | | | -| | [Incremental Update](https://qodo-merge-docs.qodo.ai/core-abilities/incremental_update/) | ✅ | | | | | -| | [Interactivity](https://qodo-merge-docs.qodo.ai/core-abilities/interactivity/) | ✅ | ✅ | | | | -| | [Local and global metadata](https://qodo-merge-docs.qodo.ai/core-abilities/metadata/) | ✅ | ✅ | ✅ | ✅ | | -| | [Multiple models support](https://qodo-merge-docs.qodo.ai/usage-guide/changing_a_model/) | ✅ | ✅ | ✅ | ✅ | | -| | [PR compression](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) | ✅ | ✅ | ✅ | ✅ | | -| | [PR interactive actions](https://www.qodo.ai/images/pr_agent/pr-actions.mp4) 💎 | ✅ | ✅ | | | | -| | [RAG context enrichment](https://qodo-merge-docs.qodo.ai/core-abilities/rag_context_enrichment/) | ✅ | | ✅ | | | -| | [Self reflection](https://qodo-merge-docs.qodo.ai/core-abilities/self_reflection/) | ✅ | ✅ | ✅ | ✅ | | -| | [Static code analysis](https://qodo-merge-docs.qodo.ai/core-abilities/static_code_analysis/) 💎 | ✅ | ✅ | | | | +| | [Code Validation 💎](https://qodo-merge-docs.qodo.ai/core-abilities/code_validation/) | ✅ | ✅ | ✅ | ✅ | | +| | [Dynamic context](https://qodo-merge-docs.qodo.ai/core-abilities/dynamic_context/) | ✅ | ✅ | ✅ | ✅ | | +| | [Fetching ticket context](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/) | ✅ | ✅ | ✅ | | | +| | [Global and wiki configurations](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/) 💎 | ✅ | ✅ | ✅ | | | +| | [Impact Evaluation](https://qodo-merge-docs.qodo.ai/core-abilities/impact_evaluation/) 💎 | ✅ | ✅ | | | | +| | [Incremental Update](https://qodo-merge-docs.qodo.ai/core-abilities/incremental_update/) | ✅ | | | | | +| | [Interactivity](https://qodo-merge-docs.qodo.ai/core-abilities/interactivity/) | ✅ | ✅ | | | | +| | [Local and global metadata](https://qodo-merge-docs.qodo.ai/core-abilities/metadata/) | ✅ | ✅ | ✅ | ✅ | | +| | [Multiple models support](https://qodo-merge-docs.qodo.ai/usage-guide/changing_a_model/) | ✅ | ✅ | ✅ | ✅ | | +| | [PR compression](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) | ✅ | ✅ | ✅ | ✅ | | +| | [PR interactive actions](https://www.qodo.ai/images/pr_agent/pr-actions.mp4) 💎 | ✅ | ✅ | | | | +| | [RAG context enrichment](https://qodo-merge-docs.qodo.ai/core-abilities/rag_context_enrichment/) | ✅ | | ✅ | | | +| | [Self reflection](https://qodo-merge-docs.qodo.ai/core-abilities/self_reflection/) | ✅ | ✅ | ✅ | ✅ | | +| | [Static code analysis](https://qodo-merge-docs.qodo.ai/core-abilities/static_code_analysis/) 💎 | ✅ | ✅ | | | | - 💎 means this feature is available only in [Qodo Merge](https://www.qodo.ai/pricing/) [//]: # (- Support for additional git providers is described in [here](./docs/Full_environments.md)) @@ -139,8 +139,6 @@ ___ \ ‣ **Analyze 💎 ([`/analyze`](https://qodo-merge-docs.qodo.ai/tools/analyze/))**: Identify code components that changed in the PR, and enables to interactively generate tests, docs, and code suggestions for each component. \ -‣ **Test 💎 ([`/test`](https://qodo-merge-docs.qodo.ai/tools/test/))**: Generate tests for a selected component, based on the PR code changes. -\ ‣ **Custom Prompt 💎 ([`/custom_prompt`](https://qodo-merge-docs.qodo.ai/tools/custom_prompt/))**: Automatically generates custom suggestions for improving the PR code, based on specific guidelines defined by the user. \ ‣ **Generate Tests 💎 ([`/test component_name`](https://qodo-merge-docs.qodo.ai/tools/test/))**: Generates unit tests for a selected component, based on the PR code changes. From ac8aa9c2efc34f5c40743a5816c15989ef9e0c29 Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Fri, 30 May 2025 14:36:28 +0300 Subject: [PATCH 356/365] docs: reorganize tools list with consistent formatting and command names in README --- README.md | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index e778719b5d..d1d8cb7dc0 100644 --- a/README.md +++ b/README.md @@ -121,33 +121,39 @@ Supported commands per platform: [//]: # (- Support for additional git providers is described in [here](./docs/Full_environments.md)) ___ -‣ **Auto Description ([`/describe`](https://qodo-merge-docs.qodo.ai/tools/describe/))**: Automatically generating PR description - title, type, summary, code walkthrough and labels. +‣ **PR Description ([`/describe`](https://qodo-merge-docs.qodo.ai/tools/describe/))**: Automatically generating PR description - title, type, summary, code walkthrough and labels. \ -‣ **Auto Review ([`/review`](https://qodo-merge-docs.qodo.ai/tools/review/))**: Adjustable feedback about the PR, possible issues, security concerns, review effort and more. +‣ **PR Review ([`/review`](https://qodo-merge-docs.qodo.ai/tools/review/))**: Adjustable feedback about the PR, possible issues, security concerns, review effort and more. \ ‣ **Code Suggestions ([`/improve`](https://qodo-merge-docs.qodo.ai/tools/improve/))**: Code suggestions for improving the PR. \ -‣ **Question Answering ([`/ask ...`](https://qodo-merge-docs.qodo.ai/tools/ask/))**: Answering free-text questions about the PR. +‣ **Question Answering ([`/ask ...`](https://qodo-merge-docs.qodo.ai/tools/ask/))**: Answering free-text questions about the PR, or on specific code lines. +\ +‣ **Help ([`/help`](https://qodo-merge-docs.qodo.ai/tools/help/))**: Provides a list of all the available tools. Also enables to trigger them interactively (💎). +\ +‣ **Help Docs ([`/help_docs`](https://qodo-merge-docs.qodo.ai/tools/help_docs/))**: Answer a free-text question based on a git documentation folder. \ ‣ **Update Changelog ([`/update_changelog`](https://qodo-merge-docs.qodo.ai/tools/update_changelog/))**: Automatically updating the CHANGELOG.md file with the PR changes. \ -‣ **Help Docs ([`/help_docs`](https://qodo-merge-docs.qodo.ai/tools/help_docs/))**: Answers a question on any repository by utilizing given documentation. +‣ **💎 Add Documentation ([`/add_docs`](https://qodo-merge-docs.qodo.ai/tools/documentation/))**: Generates documentation to methods/functions/classes that changed in the PR. +\ +‣ **💎 Analyze ([`/analyze`](https://qodo-merge-docs.qodo.ai/tools/analyze/))**: Identify code components that changed in the PR, and enables to interactively generate tests, docs, and code suggestions for each component. \ -‣ **Add Documentation 💎 ([`/add_docs`](https://qodo-merge-docs.qodo.ai/tools/documentation/))**: Generates documentation to methods/functions/classes that changed in the PR. +‣ **💎 CI Feedback ([`/checks ci_job`](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/))**: Automatically generates feedback and analysis for a failed CI job. \ -‣ **Generate Custom Labels 💎 ([`/generate_labels`](https://qodo-merge-docs.qodo.ai/tools/custom_labels/))**: Generates custom labels for the PR, based on specific guidelines defined by the user. +‣ **💎 Custom Prompt ([`/custom_prompt`](https://qodo-merge-docs.qodo.ai/tools/custom_prompt/))**: Automatically generates custom suggestions for improving the PR code, based on specific guidelines defined by the user. \ -‣ **Analyze 💎 ([`/analyze`](https://qodo-merge-docs.qodo.ai/tools/analyze/))**: Identify code components that changed in the PR, and enables to interactively generate tests, docs, and code suggestions for each component. +‣ **💎 Generate Custom Labels ([`/generate_labels`](https://qodo-merge-docs.qodo.ai/tools/custom_labels/))**: Generates custom labels for the PR, based on specific guidelines defined by the user. \ -‣ **Custom Prompt 💎 ([`/custom_prompt`](https://qodo-merge-docs.qodo.ai/tools/custom_prompt/))**: Automatically generates custom suggestions for improving the PR code, based on specific guidelines defined by the user. +‣ **💎 Generate Tests ([`/test`](https://qodo-merge-docs.qodo.ai/tools/test/))**: Automatically generates unit tests for a selected component, based on the PR code changes. \ -‣ **Generate Tests 💎 ([`/test component_name`](https://qodo-merge-docs.qodo.ai/tools/test/))**: Generates unit tests for a selected component, based on the PR code changes. +‣ **💎 Implement ([`/implement`](https://qodo-merge-docs.qodo.ai/tools/implement/))**: Generates implementation code from review suggestions. \ -‣ **CI Feedback 💎 ([`/checks ci_job`](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/))**: Automatically generates feedback and analysis for a failed CI job. +‣ **💎 Improve Component ([`/improve_component component_name`](https://qodo-merge-docs.qodo.ai/tools/improve_component/))**: Generates code suggestions for a specific code component that changed in the PR. \ -‣ **Similar Code 💎 ([`/find_similar_component`](https://qodo-merge-docs.qodo.ai/tools/similar_code/))**: Retrieves the most similar code components from inside the organization's codebase, or from open-source code. +‣ **💎 Scan Repo Discussions ([`/scan_repo_discussions`](https://qodo-merge-docs.qodo.ai/tools/scan_repo_discussions/))**: Generates best_practices.md file based on previous discussions in the repository. \ -‣ **Implement 💎 ([`/implement`](https://qodo-merge-docs.qodo.ai/tools/implement/))**: Generates implementation code from review suggestions. +‣ **💎 Similar Code ([`/similar_code`](https://qodo-merge-docs.qodo.ai/tools/similar_code/))**: Retrieves the most similar code components from inside the organization's codebase, or from open-source code. ___ ## Example results From cc06da3b7fcee26251102576ea0a69a2fe4a8cce Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Fri, 30 May 2025 15:32:01 +0300 Subject: [PATCH 357/365] docs: update README with improved navigation, Discord link, and table formatting --- README.md | 164 +++++++++++++++++++++++------------------------------- 1 file changed, 69 insertions(+), 95 deletions(-) diff --git a/README.md b/README.md index d1d8cb7dc0..ed43f1b082 100644 --- a/README.md +++ b/README.md @@ -21,24 +21,33 @@ PR-Agent aims to help efficiently review and handle pull requests, by providing [![Static Badge](https://img.shields.io/badge/Chrome-Extension-violet)](https://chromewebstore.google.com/detail/qodo-merge-ai-powered-cod/ephlnjeghhogofkifjloamocljapahnl) [![Static Badge](https://img.shields.io/badge/Pro-App-blue)](https://github.com/apps/qodo-merge-pro/) [![Static Badge](https://img.shields.io/badge/OpenSource-App-red)](https://github.com/apps/qodo-merge-pro-for-open-source/) -[![Discord](https://badgen.net/badge/icon/discord?icon=discord&label&color=purple)](https://discord.com/invite/SgSxuQ65GF) +[![Discord](https://badgen.net/badge/icon/discord?icon=discord&label&color=purple)](https://discord.gg/kG35uSHDBc) GitHub
-## Table of Contents +## Getting Started + +### Try it now +Test PR-Agent on any public GitHub repository by commenting `@CodiumAI-Agent /review` + +### GitHub Action +Add automated PR reviews to your repository with a simple workflow file using [GitHub Action setup guide](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-action) + +#### Other Platforms +- [GitLab webhook setup](https://qodo-merge-docs.qodo.ai/installation/gitlab/) +- [BitBucket app installation](https://qodo-merge-docs.qodo.ai/installation/bitbucket/) +- [Azure DevOps setup](https://qodo-merge-docs.qodo.ai/installation/azure/) + +### CLI Usage +Run PR-Agent locally on your repository via command line: [Local CLI setup guide](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#local-repo-cli) + +### [Qodo Merge 💎](https://github.com/qodo-ai/pr-agent?tab=readme-ov-file#qodo-merge-) +Zero-setup hosted solution with advanced features and priority support +- [Intro and Installation guide](https://qodo-merge-docs.qodo.ai/installation/qodo_merge/) +- [Plans & Pricing](https://www.qodo.ai/pricing/) -- [News and Updates](#news-and-updates) -- [Overview](#overview) -- [Example results](#example-results) -- [Try it now](#try-it-now) -- [Qodo Merge](https://qodo-merge-docs.qodo.ai/overview/pr_agent_pro/) -- [How it works](#how-it-works) -- [Why use PR-Agent?](#why-use-pr-agent) -- [Data privacy](#data-privacy) -- [Contributing](#contributing) -- [Links](#links) ## News and Updates @@ -70,93 +79,58 @@ Read more about it [here](https://qodo-merge-docs.qodo.ai/tools/scan_repo_discus Supported commands per platform: -| | | GitHub | GitLab | Bitbucket | Azure DevOps | Gitea | -| ----- |---------------------------------------------------------------------------------------------------------------------|:------:|:------:|:---------:|:------------:|:-----:| -| TOOLS | [Describe](https://qodo-merge-docs.qodo.ai/tools/describe/) | ✅ | ✅ | ✅ | ✅ | ✅ | -| | [Review](https://qodo-merge-docs.qodo.ai/tools/review/) | ✅ | ✅ | ✅ | ✅ | ✅ | -| | [Improve](https://qodo-merge-docs.qodo.ai/tools/improve/) | ✅ | ✅ | ✅ | ✅ | ✅ | -| | [Ask](https://qodo-merge-docs.qodo.ai/tools/ask/) | ✅ | ✅ | ✅ | ✅ | | -| | ⮑ [Ask on code lines](https://qodo-merge-docs.qodo.ai/tools/ask/#ask-lines) | ✅ | ✅ | | | | -| | [Help Docs](https://qodo-merge-docs.qodo.ai/tools/help_docs/?h=auto#auto-approval) | ✅ | ✅ | ✅ | | | -| | [Update CHANGELOG](https://qodo-merge-docs.qodo.ai/tools/update_changelog/) | ✅ | ✅ | ✅ | ✅ | | -| | [PR Documentation](https://qodo-merge-docs.qodo.ai/tools/documentation/) 💎 | ✅ | ✅ | | | | -| | [Analyze](https://qodo-merge-docs.qodo.ai/tools/analyze/) 💎 | ✅ | ✅ | | | | -| | [Auto-Approve](https://qodo-merge-docs.qodo.ai/tools/improve/?h=auto#auto-approval) 💎 | ✅ | ✅ | ✅ | | | -| | [CI Feedback](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/) 💎 | ✅ | | | | | -| | [Custom Prompt](https://qodo-merge-docs.qodo.ai/tools/custom_prompt/) 💎 | ✅ | ✅ | ✅ | | | -| | [Generate Custom Labels](https://qodo-merge-docs.qodo.ai/tools/custom_labels/) 💎 | ✅ | ✅ | | | | -| | [Generate Tests](https://qodo-merge-docs.qodo.ai/tools/test/) 💎 | ✅ | ✅ | | | | -| | [Implement](https://qodo-merge-docs.qodo.ai/tools/implement/) 💎 | ✅ | ✅ | ✅ | | | -| | [Scan Repo Discussions](https://qodo-merge-docs.qodo.ai/tools/scan_repo_discussions/) 💎 | ✅ | | | | | -| | [Similar Code](https://qodo-merge-docs.qodo.ai/tools/similar_code/) 💎 | ✅ | | | | | -| | [Ticket Context](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/) 💎 | ✅ | ✅ | ✅ | | | -| | [Utilizing Best Practices](https://qodo-merge-docs.qodo.ai/tools/improve/#best-practices) 💎 | ✅ | ✅ | ✅ | | | -| | [PR Chat](https://qodo-merge-docs.qodo.ai/chrome-extension/features/#pr-chat) 💎 | ✅ | | | | | -| | [Suggestion Tracking](https://qodo-merge-docs.qodo.ai/tools/improve/#suggestion-tracking) 💎 | ✅ | ✅ | | | | -| | | | | | | | -| USAGE | [CLI](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#local-repo-cli) | ✅ | ✅ | ✅ | ✅ | ✅ | -| | [App / webhook](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#github-app) | ✅ | ✅ | ✅ | ✅ | ✅ | -| | [Tagging bot](https://github.com/Codium-ai/pr-agent#try-it-now) | ✅ | | | | | -| | [Actions](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-action) | ✅ | ✅ | ✅ | ✅ | | -| | | | | | | | -| CORE | [Adaptive and token-aware file patch fitting](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) | ✅ | ✅ | ✅ | ✅ | | -| | [Auto Best Practices 💎](https://qodo-merge-docs.qodo.ai/core-abilities/auto_best_practices/) | ✅ | | | | | -| | [Chat on code suggestions](https://qodo-merge-docs.qodo.ai/core-abilities/chat_on_code_suggestions/) | ✅ | ✅ | | | | -| | [Code Validation 💎](https://qodo-merge-docs.qodo.ai/core-abilities/code_validation/) | ✅ | ✅ | ✅ | ✅ | | -| | [Dynamic context](https://qodo-merge-docs.qodo.ai/core-abilities/dynamic_context/) | ✅ | ✅ | ✅ | ✅ | | -| | [Fetching ticket context](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/) | ✅ | ✅ | ✅ | | | -| | [Global and wiki configurations](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/) 💎 | ✅ | ✅ | ✅ | | | -| | [Impact Evaluation](https://qodo-merge-docs.qodo.ai/core-abilities/impact_evaluation/) 💎 | ✅ | ✅ | | | | -| | [Incremental Update](https://qodo-merge-docs.qodo.ai/core-abilities/incremental_update/) | ✅ | | | | | -| | [Interactivity](https://qodo-merge-docs.qodo.ai/core-abilities/interactivity/) | ✅ | ✅ | | | | -| | [Local and global metadata](https://qodo-merge-docs.qodo.ai/core-abilities/metadata/) | ✅ | ✅ | ✅ | ✅ | | -| | [Multiple models support](https://qodo-merge-docs.qodo.ai/usage-guide/changing_a_model/) | ✅ | ✅ | ✅ | ✅ | | -| | [PR compression](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) | ✅ | ✅ | ✅ | ✅ | | -| | [PR interactive actions](https://www.qodo.ai/images/pr_agent/pr-actions.mp4) 💎 | ✅ | ✅ | | | | -| | [RAG context enrichment](https://qodo-merge-docs.qodo.ai/core-abilities/rag_context_enrichment/) | ✅ | | ✅ | | | -| | [Self reflection](https://qodo-merge-docs.qodo.ai/core-abilities/self_reflection/) | ✅ | ✅ | ✅ | ✅ | | -| | [Static code analysis](https://qodo-merge-docs.qodo.ai/core-abilities/static_code_analysis/) 💎 | ✅ | ✅ | | | | +| | | GitHub | GitLab | Bitbucket | Azure DevOps | Gitea | +|---------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------|:------:|:------:|:---------:|:------------:|:-----:| +| [TOOLS](https://qodo-merge-docs.qodo.ai/tools/) | [Describe](https://qodo-merge-docs.qodo.ai/tools/describe/) | ✅ | ✅ | ✅ | ✅ | ✅ | +| | [Review](https://qodo-merge-docs.qodo.ai/tools/review/) | ✅ | ✅ | ✅ | ✅ | ✅ | +| | [Improve](https://qodo-merge-docs.qodo.ai/tools/improve/) | ✅ | ✅ | ✅ | ✅ | ✅ | +| | [Ask](https://qodo-merge-docs.qodo.ai/tools/ask/) | ✅ | ✅ | ✅ | ✅ | | +| | ⮑ [Ask on code lines](https://qodo-merge-docs.qodo.ai/tools/ask/#ask-lines) | ✅ | ✅ | | | | +| | [Help Docs](https://qodo-merge-docs.qodo.ai/tools/help_docs/?h=auto#auto-approval) | ✅ | ✅ | ✅ | | | +| | [Update CHANGELOG](https://qodo-merge-docs.qodo.ai/tools/update_changelog/) | ✅ | ✅ | ✅ | ✅ | | +| | [PR Documentation](https://qodo-merge-docs.qodo.ai/tools/documentation/) 💎 | ✅ | ✅ | | | | +| | [Analyze](https://qodo-merge-docs.qodo.ai/tools/analyze/) 💎 | ✅ | ✅ | | | | +| | [Auto-Approve](https://qodo-merge-docs.qodo.ai/tools/improve/?h=auto#auto-approval) 💎 | ✅ | ✅ | ✅ | | | +| | [CI Feedback](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/) 💎 | ✅ | | | | | +| | [Custom Prompt](https://qodo-merge-docs.qodo.ai/tools/custom_prompt/) 💎 | ✅ | ✅ | ✅ | | | +| | [Generate Custom Labels](https://qodo-merge-docs.qodo.ai/tools/custom_labels/) 💎 | ✅ | ✅ | | | | +| | [Generate Tests](https://qodo-merge-docs.qodo.ai/tools/test/) 💎 | ✅ | ✅ | | | | +| | [Implement](https://qodo-merge-docs.qodo.ai/tools/implement/) 💎 | ✅ | ✅ | ✅ | | | +| | [Scan Repo Discussions](https://qodo-merge-docs.qodo.ai/tools/scan_repo_discussions/) 💎 | ✅ | | | | | +| | [Similar Code](https://qodo-merge-docs.qodo.ai/tools/similar_code/) 💎 | ✅ | | | | | +| | [Ticket Context](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/) 💎 | ✅ | ✅ | ✅ | | | +| | [Utilizing Best Practices](https://qodo-merge-docs.qodo.ai/tools/improve/#best-practices) 💎 | ✅ | ✅ | ✅ | | | +| | [PR Chat](https://qodo-merge-docs.qodo.ai/chrome-extension/features/#pr-chat) 💎 | ✅ | | | | | +| | [Suggestion Tracking](https://qodo-merge-docs.qodo.ai/tools/improve/#suggestion-tracking) 💎 | ✅ | ✅ | | | | +| | | | | | | | +| [USAGE](https://qodo-merge-docs.qodo.ai/usage-guide/) | [CLI](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#local-repo-cli) | ✅ | ✅ | ✅ | ✅ | ✅ | +| | [App / webhook](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#github-app) | ✅ | ✅ | ✅ | ✅ | ✅ | +| | [Tagging bot](https://github.com/Codium-ai/pr-agent#try-it-now) | ✅ | | | | | +| | [Actions](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-action) | ✅ | ✅ | ✅ | ✅ | | +| | | | | | | | +| [CORE](https://qodo-merge-docs.qodo.ai/core-abilities/) | [Adaptive and token-aware file patch fitting](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) | ✅ | ✅ | ✅ | ✅ | | +| | [Auto Best Practices 💎](https://qodo-merge-docs.qodo.ai/core-abilities/auto_best_practices/) | ✅ | | | | | +| | [Chat on code suggestions](https://qodo-merge-docs.qodo.ai/core-abilities/chat_on_code_suggestions/) | ✅ | ✅ | | | | +| | [Code Validation 💎](https://qodo-merge-docs.qodo.ai/core-abilities/code_validation/) | ✅ | ✅ | ✅ | ✅ | | +| | [Dynamic context](https://qodo-merge-docs.qodo.ai/core-abilities/dynamic_context/) | ✅ | ✅ | ✅ | ✅ | | +| | [Fetching ticket context](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/) | ✅ | ✅ | ✅ | | | +| | [Global and wiki configurations](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/) 💎 | ✅ | ✅ | ✅ | | | +| | [Impact Evaluation](https://qodo-merge-docs.qodo.ai/core-abilities/impact_evaluation/) 💎 | ✅ | ✅ | | | | +| | [Incremental Update](https://qodo-merge-docs.qodo.ai/core-abilities/incremental_update/) | ✅ | | | | | +| | [Interactivity](https://qodo-merge-docs.qodo.ai/core-abilities/interactivity/) | ✅ | ✅ | | | | +| | [Local and global metadata](https://qodo-merge-docs.qodo.ai/core-abilities/metadata/) | ✅ | ✅ | ✅ | ✅ | | +| | [Multiple models support](https://qodo-merge-docs.qodo.ai/usage-guide/changing_a_model/) | ✅ | ✅ | ✅ | ✅ | | +| | [PR compression](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) | ✅ | ✅ | ✅ | ✅ | | +| | [PR interactive actions](https://www.qodo.ai/images/pr_agent/pr-actions.mp4) 💎 | ✅ | ✅ | | | | +| | [RAG context enrichment](https://qodo-merge-docs.qodo.ai/core-abilities/rag_context_enrichment/) | ✅ | | ✅ | | | +| | [Self reflection](https://qodo-merge-docs.qodo.ai/core-abilities/self_reflection/) | ✅ | ✅ | ✅ | ✅ | | +| | [Static code analysis](https://qodo-merge-docs.qodo.ai/core-abilities/static_code_analysis/) 💎 | ✅ | ✅ | | | | - 💎 means this feature is available only in [Qodo Merge](https://www.qodo.ai/pricing/) [//]: # (- Support for additional git providers is described in [here](./docs/Full_environments.md)) ___ -‣ **PR Description ([`/describe`](https://qodo-merge-docs.qodo.ai/tools/describe/))**: Automatically generating PR description - title, type, summary, code walkthrough and labels. -\ -‣ **PR Review ([`/review`](https://qodo-merge-docs.qodo.ai/tools/review/))**: Adjustable feedback about the PR, possible issues, security concerns, review effort and more. -\ -‣ **Code Suggestions ([`/improve`](https://qodo-merge-docs.qodo.ai/tools/improve/))**: Code suggestions for improving the PR. -\ -‣ **Question Answering ([`/ask ...`](https://qodo-merge-docs.qodo.ai/tools/ask/))**: Answering free-text questions about the PR, or on specific code lines. -\ -‣ **Help ([`/help`](https://qodo-merge-docs.qodo.ai/tools/help/))**: Provides a list of all the available tools. Also enables to trigger them interactively (💎). -\ -‣ **Help Docs ([`/help_docs`](https://qodo-merge-docs.qodo.ai/tools/help_docs/))**: Answer a free-text question based on a git documentation folder. -\ -‣ **Update Changelog ([`/update_changelog`](https://qodo-merge-docs.qodo.ai/tools/update_changelog/))**: Automatically updating the CHANGELOG.md file with the PR changes. -\ -‣ **💎 Add Documentation ([`/add_docs`](https://qodo-merge-docs.qodo.ai/tools/documentation/))**: Generates documentation to methods/functions/classes that changed in the PR. -\ -‣ **💎 Analyze ([`/analyze`](https://qodo-merge-docs.qodo.ai/tools/analyze/))**: Identify code components that changed in the PR, and enables to interactively generate tests, docs, and code suggestions for each component. -\ -‣ **💎 CI Feedback ([`/checks ci_job`](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/))**: Automatically generates feedback and analysis for a failed CI job. -\ -‣ **💎 Custom Prompt ([`/custom_prompt`](https://qodo-merge-docs.qodo.ai/tools/custom_prompt/))**: Automatically generates custom suggestions for improving the PR code, based on specific guidelines defined by the user. -\ -‣ **💎 Generate Custom Labels ([`/generate_labels`](https://qodo-merge-docs.qodo.ai/tools/custom_labels/))**: Generates custom labels for the PR, based on specific guidelines defined by the user. -\ -‣ **💎 Generate Tests ([`/test`](https://qodo-merge-docs.qodo.ai/tools/test/))**: Automatically generates unit tests for a selected component, based on the PR code changes. -\ -‣ **💎 Implement ([`/implement`](https://qodo-merge-docs.qodo.ai/tools/implement/))**: Generates implementation code from review suggestions. -\ -‣ **💎 Improve Component ([`/improve_component component_name`](https://qodo-merge-docs.qodo.ai/tools/improve_component/))**: Generates code suggestions for a specific code component that changed in the PR. -\ -‣ **💎 Scan Repo Discussions ([`/scan_repo_discussions`](https://qodo-merge-docs.qodo.ai/tools/scan_repo_discussions/))**: Generates best_practices.md file based on previous discussions in the repository. -\ -‣ **💎 Similar Code ([`/similar_code`](https://qodo-merge-docs.qodo.ai/tools/similar_code/))**: Retrieves the most similar code components from inside the organization's codebase, or from open-source code. -___ - -## Example results +## See it in Action

/describe

@@ -233,7 +207,7 @@ Here are some advantages of PR-Agent: - We emphasize **real-life practical usage**. Each tool (review, improve, ask, ...) has a single LLM call, no more. We feel that this is critical for realistic team usage - obtaining an answer quickly (~30 seconds) and affordably. - Our [PR Compression strategy](https://qodo-merge-docs.qodo.ai/core-abilities/#pr-compression-strategy) is a core ability that enables to effectively tackle both short and long PRs. -- Our JSON prompting strategy enables to have **modular, customizable tools**. For example, the '/review' tool categories can be controlled via the [configuration](pr_agent/settings/configuration.toml) file. Adding additional categories is easy and accessible. +- Our JSON prompting strategy enables us to have **modular, customizable tools**. For example, the '/review' tool categories can be controlled via the [configuration](pr_agent/settings/configuration.toml) file. Adding additional categories is easy and accessible. - We support **multiple git providers** (GitHub, GitLab, BitBucket), **multiple ways** to use the tool (CLI, GitHub Action, GitHub App, Docker, ...), and **multiple models** (GPT, Claude, Deepseek, ...) ## Data privacy From d5080a35f6ecbf55d0c426f2848ae2e957c5febc Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Fri, 30 May 2025 15:39:24 +0300 Subject: [PATCH 358/365] docs: update Discord invitation link in README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ed43f1b082..4a387e9c61 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ PR-Agent aims to help efficiently review and handle pull requests, by providing [![Static Badge](https://img.shields.io/badge/Chrome-Extension-violet)](https://chromewebstore.google.com/detail/qodo-merge-ai-powered-cod/ephlnjeghhogofkifjloamocljapahnl) [![Static Badge](https://img.shields.io/badge/Pro-App-blue)](https://github.com/apps/qodo-merge-pro/) [![Static Badge](https://img.shields.io/badge/OpenSource-App-red)](https://github.com/apps/qodo-merge-pro-for-open-source/) -[![Discord](https://badgen.net/badge/icon/discord?icon=discord&label&color=purple)](https://discord.gg/kG35uSHDBc) +[![Discord](https://badgen.net/badge/icon/discord?icon=discord&label&color=purple)](https://discord.com/invite/SgSxuQ65GF) GitHub @@ -235,7 +235,7 @@ To contribute to the project, get started by reading our [Contributing Guide](ht ## Links -- Discord community: https://discord.gg/kG35uSHDBc +- Discord community: https://discord.com/invite/SgSxuQ65GF - Qodo site: https://www.qodo.ai/ - Blog: https://www.qodo.ai/blog/ - Troubleshooting: https://www.qodo.ai/blog/technical-faq-and-troubleshooting/ From 76172bd3ec945380114511673b1936ac31e1381c Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Sun, 1 Jun 2025 09:59:47 +0300 Subject: [PATCH 359/365] docs: add table of contents to README for improved navigation --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 4a387e9c61..aa7eac11c2 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,20 @@ PR-Agent aims to help efficiently review and handle pull requests, by providing
+## Table of Contents + +- [Getting Started](#getting-started) +- [News and Updates](#news-and-updates) +- [Overview](#overview) +- [Example results](#example-results) +- [Try it now](#try-it-now) +- [Qodo Merge](https://qodo-merge-docs.qodo.ai/overview/pr_agent_pro/) +- [How it works](#how-it-works) +- [Why use PR-Agent?](#why-use-pr-agent) +- [Data privacy](#data-privacy) +- [Contributing](#contributing) +- [Links](#links) + ## Getting Started ### Try it now From 27479d87b7c8ee444d6eace61f2291bb611dc3c3 Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Sun, 1 Jun 2025 10:09:37 +0300 Subject: [PATCH 360/365] docs: standardize capitalization in README table of contents and headings --- README.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index aa7eac11c2..d9abe17557 100644 --- a/README.md +++ b/README.md @@ -32,18 +32,18 @@ PR-Agent aims to help efficiently review and handle pull requests, by providing - [Getting Started](#getting-started) - [News and Updates](#news-and-updates) - [Overview](#overview) -- [Example results](#example-results) -- [Try it now](#try-it-now) -- [Qodo Merge](https://qodo-merge-docs.qodo.ai/overview/pr_agent_pro/) -- [How it works](#how-it-works) -- [Why use PR-Agent?](#why-use-pr-agent) -- [Data privacy](#data-privacy) +- [See It in Action](#see-it-in-action) +- [Try It Now](#try-it-now) +- [Qodo Merge 💎](#qodo-merge-) +- [How It Works](#how-it-works) +- [Why Use PR-Agent?](#why-use-pr-agent) +- [Data Privacy](#data-privacy) - [Contributing](#contributing) - [Links](#links) ## Getting Started -### Try it now +### Try it now Test PR-Agent on any public GitHub repository by commenting `@CodiumAI-Agent /review` ### GitHub Action @@ -57,7 +57,7 @@ Add automated PR reviews to your repository with a simple workflow file using [G ### CLI Usage Run PR-Agent locally on your repository via command line: [Local CLI setup guide](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#local-repo-cli) -### [Qodo Merge 💎](https://github.com/qodo-ai/pr-agent?tab=readme-ov-file#qodo-merge-) +### Qodo Merge 💎 Zero-setup hosted solution with advanced features and priority support - [Intro and Installation guide](https://qodo-merge-docs.qodo.ai/installation/qodo_merge/) - [Plans & Pricing](https://www.qodo.ai/pricing/) @@ -144,7 +144,7 @@ Supported commands per platform: [//]: # (- Support for additional git providers is described in [here](./docs/Full_environments.md)) ___ -## See it in Action +## See It in Action

/describe

@@ -179,7 +179,7 @@ ___
-## Try it now +## Try It Now Try the Claude Sonnet powered PR-Agent instantly on _your public GitHub repository_. Just mention `@CodiumAI-Agent` and add the desired command in any PR comment. The agent will generate a response based on your command. For example, add a comment to any pull request with the following text: @@ -205,7 +205,7 @@ It does not have 'edit' access to your repo, for example, so it cannot update th 4. **Extra features** - In addition to the benefits listed above, Qodo Merge will emphasize more customization, and the usage of static code analysis, in addition to LLM logic, to improve results. See [here](https://qodo-merge-docs.qodo.ai/overview/pr_agent_pro/) for a list of features available in Qodo Merge. -## How it works +## How It Works The following diagram illustrates PR-Agent tools and their flow: @@ -213,7 +213,7 @@ The following diagram illustrates PR-Agent tools and their flow: Check out the [PR Compression strategy](https://qodo-merge-docs.qodo.ai/core-abilities/#pr-compression-strategy) page for more details on how we convert a code diff to a manageable LLM prompt -## Why use PR-Agent? +## Why Use PR-Agent? A reasonable question that can be asked is: `"Why use PR-Agent? What makes it stand out from existing tools?"` @@ -224,7 +224,7 @@ Here are some advantages of PR-Agent: - Our JSON prompting strategy enables us to have **modular, customizable tools**. For example, the '/review' tool categories can be controlled via the [configuration](pr_agent/settings/configuration.toml) file. Adding additional categories is easy and accessible. - We support **multiple git providers** (GitHub, GitLab, BitBucket), **multiple ways** to use the tool (CLI, GitHub Action, GitHub App, Docker, ...), and **multiple models** (GPT, Claude, Deepseek, ...) -## Data privacy +## Data Privacy ### Self-hosted PR-Agent From 8be2e43a0f5bbebea502dd0e5ae97027a542e857 Mon Sep 17 00:00:00 2001 From: Tal Date: Sun, 1 Jun 2025 11:15:42 +0300 Subject: [PATCH 361/365] Update README.md Co-authored-by: qodo-merge-pro-for-open-source[bot] <189517486+qodo-merge-pro-for-open-source[bot]@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d9abe17557..fcdb7949b6 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ PR-Agent aims to help efficiently review and handle pull requests, by providing ## Getting Started ### Try it now -Test PR-Agent on any public GitHub repository by commenting `@CodiumAI-Agent /review` +Test PR-Agent on any public GitHub repository by commenting `@CodiumAI-Agent /improve` ### GitHub Action Add automated PR reviews to your repository with a simple workflow file using [GitHub Action setup guide](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-action) From 0dba39566f10f64bbba225e36f72616fd1761bc4 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sun, 1 Jun 2025 11:40:17 +0300 Subject: [PATCH 362/365] docs: fix indentation in Linear base URL note in ticket context documentation --- .../core-abilities/fetching_ticket_context.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/docs/core-abilities/fetching_ticket_context.md b/docs/docs/core-abilities/fetching_ticket_context.md index 05e19c00df..73096040fb 100644 --- a/docs/docs/core-abilities/fetching_ticket_context.md +++ b/docs/docs/core-abilities/fetching_ticket_context.md @@ -339,11 +339,12 @@ Include a ticket reference in your PR description using either: Name your branch with the ticket ID as a prefix (e.g., `ABC-123-feature-description` or `feature/ABC-123/feature-description`). !!! note "Linear Base URL" - For shortened ticket IDs or branch detection (method 2), you must configure the Linear base URL in your configuration file under the [linear] section: - ```toml - [linear] - linear_base_url = "https://linear.app/[ORG_ID]" - ``` - - Replace `[ORG_ID]` with your Linear organization identifier. \ No newline at end of file + For shortened ticket IDs or branch detection (method 2), you must configure the Linear base URL in your configuration file under the [linear] section: + + ```toml + [linear] + linear_base_url = "https://linear.app/[ORG_ID]" + ``` + + Replace `[ORG_ID]` with your Linear organization identifier. \ No newline at end of file From 75d24791a4553c88e792fb9b8671480505e41019 Mon Sep 17 00:00:00 2001 From: ofir-frd Date: Sun, 1 Jun 2025 11:48:30 +0300 Subject: [PATCH 363/365] docs: improve section headings in README for better clarity --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fcdb7949b6..304297b073 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ PR-Agent aims to help efficiently review and handle pull requests, by providing ## Getting Started -### Try it now +### Try it Instantly Test PR-Agent on any public GitHub repository by commenting `@CodiumAI-Agent /improve` ### GitHub Action @@ -57,7 +57,7 @@ Add automated PR reviews to your repository with a simple workflow file using [G ### CLI Usage Run PR-Agent locally on your repository via command line: [Local CLI setup guide](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#local-repo-cli) -### Qodo Merge 💎 +### Discover Qodo Merge 💎 Zero-setup hosted solution with advanced features and priority support - [Intro and Installation guide](https://qodo-merge-docs.qodo.ai/installation/qodo_merge/) - [Plans & Pricing](https://www.qodo.ai/pricing/) From 9b06f46563506213582dfbe34ceb934bf2ab0365 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sun, 1 Jun 2025 12:20:21 +0300 Subject: [PATCH 364/365] docs: update recent updates page with new features and roadmap changes --- docs/docs/recent_updates/index.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/docs/recent_updates/index.md b/docs/docs/recent_updates/index.md index 85f21c0782..354f3ec4ed 100644 --- a/docs/docs/recent_updates/index.md +++ b/docs/docs/recent_updates/index.md @@ -1,22 +1,23 @@ # Recent Updates and Future Roadmap -`Page last updated: 2025-05-11` +`Page last updated: 2025-06-01` This page summarizes recent enhancements to Qodo Merge (last three months). It also outlines our development roadmap for the upcoming three months. Please note that the roadmap is subject to change, and features may be adjusted, added, or reprioritized. === "Recent Updates" + - **CLI Endpoint**: A new Qodo Merge endpoint will accept lists of before/after code changes, execute Qodo Merge commands, and return the results. Currently available for enterprise customers. Contact [Qodo](https://www.qodo.ai/contact/) for more information. + - **Linear tickets support**: Qodo Merge now supports Linear tickets, enabling users to link PRs to Linear issues and receive feedback on ticket compliance ([Learn more](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/#linear-integration)) + - **Smart Update**: Upon PR updates, Qodo Merge will offer tailored code suggestions, addressing both the entire PR and the specific incremental changes since the last feedback ([Learn more](https://qodo-merge-docs.qodo.ai/core-abilities/incremental_update//)) - **Qodo Merge Pull Request Benchmark** - evaluating the performance of LLMs in analyzing pull request code ([Learn more](https://qodo-merge-docs.qodo.ai/pr_benchmark/)) - **Chat on Suggestions**: Users can now chat with Qodo Merge code suggestions ([Learn more](https://qodo-merge-docs.qodo.ai/tools/improve/#chat-on-code-suggestions)) - **Scan Repo Discussions Tool**: A new tool that analyzes past code discussions to generate a `best_practices.md` file, distilling key insights and recommendations. ([Learn more](https://qodo-merge-docs.qodo.ai/tools/scan_repo_discussions/)) - - **Enhanced Models**: Qodo Merge now defaults to a combination of top models (Claude Sonnet 3.7 and Gemini 2.5 Pro) and incorporates dedicated code validation logic for improved results. ([Details 1](https://qodo-merge-docs.qodo.ai/usage-guide/qodo_merge_models/), [Details 2](https://qodo-merge-docs.qodo.ai/core-abilities/code_validation/)) - - **Chrome Extension Update**: Qodo Merge Chrome extension now supports single-tenant users. ([Learn more](https://qodo-merge-docs.qodo.ai/chrome-extension/options/#configuration-options/)) - - **Installation Metrics**: Upon installation, Qodo Merge analyzes past PRs for key metrics (e.g., time to merge, time to first reviewer feedback), enabling pre/post-installation comparison to calculate ROI. === "Future Roadmap" - - **Smart Update**: Upon PR updates, Qodo Merge will offer tailored code suggestions, addressing both the entire PR and the specific incremental changes since the last feedback. - - **CLI Endpoint**: A new Qodo Merge endpoint will accept lists of before/after code changes, execute Qodo Merge commands, and return the results. - **Simplified Free Tier**: We plan to transition from a two-week free trial to a free tier offering a limited number of suggestions per month per organization. - **Best Practices Hierarchy**: Introducing support for structured best practices, such as for folders in monorepos or a unified best practice file for a group of repositories. + - **Enhanced `review` tool**: Enhancing the `review` tool validate compliance across multiple categories including security, tickets, and custom best practices. + - **Smarter context retrieval**: Leverage AST and LSP analysis to gather relevant context from across the entire repository. + - **Enhanced portal experience**: Improved user experience in the Qodo Merge portal with new options and capabilities. From 5856a9e548810409c9d7f683151355f8296635bf Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sun, 1 Jun 2025 12:24:25 +0300 Subject: [PATCH 365/365] docs: refine descriptions in recent updates page for clarity and consistency --- docs/docs/recent_updates/index.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/docs/recent_updates/index.md b/docs/docs/recent_updates/index.md index 354f3ec4ed..84b362f0dc 100644 --- a/docs/docs/recent_updates/index.md +++ b/docs/docs/recent_updates/index.md @@ -7,11 +7,11 @@ This page summarizes recent enhancements to Qodo Merge (last three months). It also outlines our development roadmap for the upcoming three months. Please note that the roadmap is subject to change, and features may be adjusted, added, or reprioritized. === "Recent Updates" - - **CLI Endpoint**: A new Qodo Merge endpoint will accept lists of before/after code changes, execute Qodo Merge commands, and return the results. Currently available for enterprise customers. Contact [Qodo](https://www.qodo.ai/contact/) for more information. - - **Linear tickets support**: Qodo Merge now supports Linear tickets, enabling users to link PRs to Linear issues and receive feedback on ticket compliance ([Learn more](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/#linear-integration)) + - **CLI Endpoint**: A new Qodo Merge endpoint that accepts a lists of before/after code changes, executes Qodo Merge commands, and return the results. Currently available for enterprise customers. Contact [Qodo](https://www.qodo.ai/contact/) for more information. + - **Linear tickets support**: Qodo Merge now supports Linear tickets. ([Learn more](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/#linear-integration)) - **Smart Update**: Upon PR updates, Qodo Merge will offer tailored code suggestions, addressing both the entire PR and the specific incremental changes since the last feedback ([Learn more](https://qodo-merge-docs.qodo.ai/core-abilities/incremental_update//)) - **Qodo Merge Pull Request Benchmark** - evaluating the performance of LLMs in analyzing pull request code ([Learn more](https://qodo-merge-docs.qodo.ai/pr_benchmark/)) - - **Chat on Suggestions**: Users can now chat with Qodo Merge code suggestions ([Learn more](https://qodo-merge-docs.qodo.ai/tools/improve/#chat-on-code-suggestions)) + - **Chat on Suggestions**: Users can now chat with code suggestions ([Learn more](https://qodo-merge-docs.qodo.ai/tools/improve/#chat-on-code-suggestions)) - **Scan Repo Discussions Tool**: A new tool that analyzes past code discussions to generate a `best_practices.md` file, distilling key insights and recommendations. ([Learn more](https://qodo-merge-docs.qodo.ai/tools/scan_repo_discussions/))