diff --git a/Dockerfile.bitbucket_pipeline b/Dockerfile.bitbucket_pipeline index b7560001..889ee120 100644 --- a/Dockerfile.bitbucket_pipeline +++ b/Dockerfile.bitbucket_pipeline @@ -1,5 +1,12 @@ FROM python:3.10 as base +ENV OPENAI_API_KEY=${OPENAI_API_KEY} \ + BITBUCKET_BEARER_TOKEN=${BITBUCKET_BEARER_TOKEN} \ + BITBUCKET_PR_ID=${BITBUCKET_PR_ID} \ + BITBUCKET_REPO_SLUG=${BITBUCKET_REPO_SLUG} \ + BITBUCKET_WORKSPACE=${BITBUCKET_WORKSPACE} + + WORKDIR /app ADD pyproject.toml . ADD requirements.txt . diff --git a/INSTALL.md b/INSTALL.md index 4593dccf..f4247ce2 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -89,10 +89,10 @@ chmod 600 pr_agent/settings/.secrets.toml ``` export PYTHONPATH=[$PYTHONPATH:] -python pr_agent/cli.py --pr_url /review -python pr_agent/cli.py --pr_url /ask -python pr_agent/cli.py --pr_url /describe -python pr_agent/cli.py --pr_url /improve +python3 -m pr_agent.cli --pr_url /review +python3 -m pr_agent.cli --pr_url /ask +python3 -m pr_agent.cli --pr_url /describe +python3 -m pr_agent.cli --pr_url /improve ``` --- @@ -156,7 +156,7 @@ The GITHUB_TOKEN secret is automatically created by GitHub. 3. Merge this change to your main branch. 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 PR-Agent by adding environment variables under the env section corresponding to any configurable property in the [configuration](./Usage.md) file. Some examples: +4. You may configure PR-Agent by adding environment variables under the env section corresponding to any configurable property in the [configuration](pr_agent/settings/configuration.toml) file. Some examples: ```yaml env: # ... previous environment values @@ -379,21 +379,23 @@ You can use our pre-build Bitbucket-Pipeline docker image to run as Bitbucket-Pi 1. Add the following file in your repository bitbucket_pipelines.yml - pipelines: +```yaml + pipelines: pull-requests: - '**': + '**': - step: name: PR Agent Pipeline caches: - - pip - image: python:3.8 # Use an appropriate Python image with pip + - pip + image: python:3.8 services: - - docker + - docker script: - - git clone https://github.com/Codium-ai/pr-agent.git - - cd pr-agent - - pip install docker-compose - - docker-compose up --build + - git clone https://github.com/Codium-ai/pr-agent.git + - cd pr-agent + - docker build -t bitbucket_runner:latest -f Dockerfile.bitbucket_pipeline . + - docker run -e OPENAI_API_KEY=$OPENAI_API_KEY -e BITBUCKET_BEARER_TOKEN=$BITBUCKET_BEARER_TOKEN -e BITBUCKET_PR_ID=$BITBUCKET_PR_ID -e BITBUCKET_REPO_SLUG=$BITBUCKET_REPO_SLUG -e BITBUCKET_WORKSPACE=$BITBUCKET_WORKSPACE bitbucket_runner:latest +``` 2. Add the following secret to your repository under Repository settings > Pipelines > Repository variables. OPENAI_API_KEY: diff --git a/PR_COMPRESSION.md b/PR_COMPRESSION.md index 8e3e5fd7..ef053efe 100644 --- a/PR_COMPRESSION.md +++ b/PR_COMPRESSION.md @@ -1,4 +1,4 @@ -# Git Patch Logic +# PR Compression Strategy There are two scenarios: 1. The PR is small enough to fit in a single prompt (including system and user prompt) 2. The PR is too large to fit in a single prompt (including system and user prompt) @@ -16,7 +16,7 @@ We prioritize the languages of the repo based on the following criteria: ## 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 6 lines above and below the patch +2. We Expand the surrounding context of each patch to 3 lines above and below the patch ## Large PR ### Motivation @@ -25,7 +25,7 @@ We want to be able to pack as much information as possible in a single LMM promp -#### PR compression strategy +#### 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 diff --git a/README.md b/README.md index af0b5af2..6fd1f9f6 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ Making pull requests less painful with an AI agent [![GitHub license](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://github.com/Codium-ai/pr-agent/blob/main/LICENSE) [![Discord](https://badgen.net/badge/icon/discord?icon=discord&label&color=purple)](https://discord.com/channels/1057273017547378788/1126104260430528613) +[![Twitter](https://img.shields.io/twitter/follow/codiumai)](https://twitter.com/codiumai) GitHub @@ -17,22 +18,25 @@ Making pull requests less painful with an AI agent CodiumAI `PR-Agent` is an open-source tool aiming to help developers review pull requests faster and more efficiently. It automatically analyzes the pull request and can provide several types of commands: -‣ **Auto Description (`/describe`)**: Automatically generating [PR description](https://github.com/Codium-ai/pr-agent/pull/229#issue-1860711415) - title, type, summary, code walkthrough and labels. +‣ **Auto Description ([`/describe`](./docs/DESCRIBE.md))**: Automatically generating PR description - title, type, summary, code walkthrough and labels. \ -‣ **Auto Review (`/review`)**: [Adjustable feedback](https://github.com/Codium-ai/pr-agent/pull/229#issuecomment-1695022908) about the PR main theme, type, relevant tests, security issues, score, and various suggestions for the PR content. +‣ **Auto Review ([`/review`](./docs/REVIEW.md))**: Adjustable feedback about the PR main theme, type, relevant tests, security issues, score, and various suggestions for the PR content. \ -‣ **Question Answering (`/ask ...`)**: Answering [free-text questions](https://github.com/Codium-ai/pr-agent/pull/229#issuecomment-1695021332) about the PR. +‣ **Question Answering ([`/ask ...`](./docs/ASK.md))**: Answering free-text questions about the PR. \ -‣ **Code Suggestions (`/improve`)**: [Committable code suggestions](https://github.com/Codium-ai/pr-agent/pull/229#discussion_r1306919276) for improving the PR. +‣ **Code Suggestions ([`/improve`](./docs/IMPROVE.md))**: Committable code suggestions for improving the PR. \ -‣ **Update Changelog (`/update_changelog`)**: Automatically updating the CHANGELOG.md file with the [PR changes](https://github.com/Codium-ai/pr-agent/pull/168#discussion_r1282077645). +‣ **Update Changelog ([`/update_changelog`](./docs/UPDATE_CHANGELOG.md))**: Automatically updating the CHANGELOG.md file with the PR changes. \ -‣ **Find similar issue (`/similar_issue`)**: Automatically retrieves and presents [similar issues](https://github.com/Alibaba-MIIL/ASL/issues/107). +‣ **Find similar issue ([`/similar_issue`](./docs/SIMILAR_ISSUE.md))**: Automatically retrieves and presents similar issues +\ +‣ **Add Documentation ([`/add_docs`](./docs/ADD_DOCUMENTATION.md))**: Automatically adds documentation to un-documented functions/classes in the PR. +See the [Usage Guide](./Usage.md) for instructions how to run the different tools from _CLI_, _online usage_, Or by _automatically triggering_ them when a new PR is opened. -See the [usage guide](./Usage.md) for instructions how to run the different tools from [CLI](./Usage.md#working-from-a-local-repo-cli), or by [online usage](./Usage.md#online-usage), as well as additional details on optional commands and configurations. +See the [Tools Guide](./docs/TOOLS_GUIDE.md) for detailed description of the different tools. -[Release notes](./RELEASE_NOTES.md) +See the [Release notes](./RELEASE_NOTES.md) for updates on the latest changes.

Example results:

@@ -102,6 +106,7 @@ See the [usage guide](./Usage.md) for instructions how to run the different tool | | | GitHub | Gitlab | Bitbucket | CodeCommit | Azure DevOps | Gerrit | |-------|---------------------------------------------|:------:|:------:|:---------:|:----------:|:----------:|:----------:| | TOOLS | Review | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| | ⮑ Incremental | :white_check_mark: | | | | | | | | Ask | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | | | Auto-Description | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | | | Improve Code | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | | :white_check_mark: | @@ -109,6 +114,7 @@ See the [usage guide](./Usage.md) for instructions how to run the different tool | | Reflect and Review | :white_check_mark: | :white_check_mark: | :white_check_mark: | | :white_check_mark: | :white_check_mark: | | | Update CHANGELOG.md | :white_check_mark: | :white_check_mark: | :white_check_mark: | | | | | | Find similar issue | :white_check_mark: | | | | | | +| | Add Documentation | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | | :white_check_mark: | | | | | | | | | | USAGE | CLI | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | | | App / webhook | :white_check_mark: | :white_check_mark: | | | | @@ -122,7 +128,7 @@ See the [usage guide](./Usage.md) for instructions how to run the different tool | | Multiple models support | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | | | Incremental PR Review | :white_check_mark: | | | | | | -Review the **[usage guide](./Usage.md)** section for detailed instructions how to use the different tools, select the relevant git provider (GitHub, Gitlab, Bitbucket,...), and adjust the configuration file to your needs. +Review the [usage guide](./Usage.md) section for detailed instructions how to use the different tools, select the relevant git provider (GitHub, Gitlab, Bitbucket,...), and adjust the configuration file to your needs. ## Try it now @@ -165,7 +171,7 @@ There are several ways to use PR-Agent: The following diagram illustrates PR-Agent tools and their flow: -![PR-Agent Tools](https://www.codium.ai/wp-content/uploads/2023/07/codiumai-diagram-v4.jpg) +![PR-Agent Tools](https://www.codium.ai/wp-content/uploads/2023/10/codiumai-diagram-v5.png) Check out the [PR Compression strategy](./PR_COMPRESSION.md) page for more details on how we convert a code diff to a manageable LLM prompt @@ -195,7 +201,7 @@ Here are some advantages of PR-Agent: - [x] Rank the PR (see [here](https://github.com/Codium-ai/pr-agent/pull/89)) - [ ] Enforcing CONTRIBUTING.md guidelines - [ ] Performance (are there any performance issues) - - [ ] Documentation (is the PR properly documented) + - [x] Documentation (is the PR properly documented) - [ ] ... ## Similar Projects diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index bdb0165b..10cf57bd 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,3 +1,19 @@ +## [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 +- codiumai/pr-agent:0.8-gitlab_webhook +- codiumai/pr-agent:0.8-github_polling +- 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 + + ## [Version 0.7] - 2023-09-20 ### Docker Tags @@ -20,6 +36,3 @@ ### Fixed - Protection against no code suggestions generated. - Resilience to repositories where the languages cannot be automatically detected. - - - diff --git a/Usage.md b/Usage.md index bc2544b8..6176eaf0 100644 --- a/Usage.md +++ b/Usage.md @@ -1,4 +1,4 @@ -## Usage guide +## Usage Guide ### Table of Contents - [Introduction](#introduction) @@ -6,19 +6,19 @@ - [Online usage](#online-usage) - [Working with GitHub App](#working-with-github-app) - [Working with GitHub Action](#working-with-github-action) +- [Changing a model](#changing-a-model) +- [Working with large PRs](#working-with-large-prs) - [Appendix - additional configurations walkthrough](#appendix---additional-configurations-walkthrough) ### Introduction -There are 3 basic ways to invoke CodiumAI PR-Agent: +See the **[installation guide](/INSTALL.md)** for instructions on how to setup PR-Agent. After installation, there are three basic ways to invoke CodiumAI PR-Agent: 1. Locally running a CLI command 2. Online usage - by [commenting](https://github.com/Codium-ai/pr-agent/pull/229#issuecomment-1695021901) on a PR 3. Enabling PR-Agent tools to run automatically when a new PR is opened -See the [installation guide](/INSTALL.md) for instructions on how to setup your own PR-Agent. Specifically, CLI commands can be issued by invoking a pre-built [docker image](/INSTALL.md#running-from-source), or by invoking a [locally cloned repo](INSTALL.md#method-2-run-from-source). - For online usage, you will need to setup either a [GitHub App](INSTALL.md#method-5-run-as-a-github-app), or a [GitHub Action](INSTALL.md#method-3-run-as-a-github-action). GitHub App and GitHub Action also enable to run PR-Agent specific tool automatically when a new PR is opened. @@ -27,10 +27,12 @@ GitHub App and GitHub Action also enable to run PR-Agent specific tool automatic The different tools and sub-tools used by CodiumAI PR-Agent are adjustable via the **[configuration file](pr_agent/settings/configuration.toml)**. In addition to general configuration options, each tool has its own configurations. For example, the `review` tool will use parameters from the [pr_reviewer](/pr_agent/settings/configuration.toml#L16) section in the configuration file. -**git provider:** +The [Tools Guide](./docs/TOOLS_GUIDE.md) provides a detailed description of the different tools and their configurations. + +#### git provider The [git_provider](pr_agent/settings/configuration.toml#L4) field in the configuration file determines the GIT provider that will be used by PR-Agent. Currently, the following providers are supported: ` -"github", "gitlab", "azure", "codecommit", "local" +"github", "gitlab", "azure", "codecommit", "local", "gerrit" ` [//]: # (** online usage:**) @@ -47,15 +49,14 @@ The [git_provider](pr_agent/settings/configuration.toml#L4) field in the configu ### Working from a local repo (CLI) When running from your local repo (CLI), your local configuration file will be used. - Examples for invoking the different tools via the CLI: -- **Review**: `python cli.py --pr_url= review` -- **Describe**: `python cli.py --pr_url= describe` -- **Improve**: `python cli.py --pr_url= improve` -- **Ask**: `python cli.py --pr_url= ask "Write me a poem about this PR"` -- **Reflect**: `python cli.py --pr_url= reflect` -- **Update Changelog**: `python cli.py --pr_url= update_changelog` +- **Review**: `python -m pr_agent.cli --pr_url= review` +- **Describe**: `python -m pr_agent.cli --pr_url= describe` +- **Improve**: `python -m pr_agent.cli --pr_url= improve` +- **Ask**: `python -m pr_agent.cli --pr_url= ask "Write me a poem about this PR"` +- **Reflect**: `python -m pr_agent.cli --pr_url= reflect` +- **Update Changelog**: `python -m pr_agent.cli --pr_url= update_changelog` `` is the url of the relevant PR (for example: https://github.com/Codium-ai/pr-agent/pull/50). @@ -63,7 +64,7 @@ Examples for invoking the different tools via the CLI: (1) in addition to editing your local configuration file, you can also change any configuration value by adding it to the command line: ``` -python cli.py --pr_url= /review --pr_reviewer.extra_instructions="focus on the file: ..." +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`: @@ -93,15 +94,15 @@ 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](pr_agent/settings/configuration.toml) file can be similarly edited. +Any configuration value in [configuration file](pr_agent/settings/configuration.toml) file can be similarly edited. comment `/config` to see the list of available configurations. ### Working with GitHub App -When running PR-Agent from [GitHub App](INSTALL.md#method-5-run-as-a-github-app), the default configurations from a pre-built repo will be initially loaded. +When running PR-Agent from [GitHub App](INSTALL.md#method-5-run-as-a-github-app), the default configurations from a pre-built docker will be initially loaded. #### GitHub app automatic tools The [github_app](pr_agent/settings/configuration.toml#L56) section defines GitHub app specific configurations. -An important parameter is `pr_commands`, which is a list of tools that will be **run automatically when a new PR is opened**: +An important parameter is `pr_commands`, which is a list of tools that will be **run automatically** when a new PR is opened: ``` [github_app] pr_commands = [ @@ -112,7 +113,7 @@ pr_commands = [ This means that when a new PR is opened, PR-Agent will run the `describe` and `auto_review` tools. For the describe tool, the `add_original_user_description` and `keep_original_user_title` parameters will be set to true. -However, you can override the default tool parameters by uploading a local configuration file called `.pr_agent.toml` to the root of your repo. +You can override the default tool parameters by uploading a local configuration file called `.pr_agent.toml` to the root of your repo. For example, if your local `.pr_agent.toml` file contains: ``` [pr_description] @@ -125,7 +126,6 @@ Note that a local `.pr_agent.toml` file enables you to edit and customize the de #### Editing the prompts The prompts for the various PR-Agent 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. @@ -142,15 +142,29 @@ user=""" Note that the new prompt will need to generate an output compatible with the relevant [post-process function](./pr_agent/tools/pr_description.py#L137). ### Working with GitHub Action -TBD +You can configure settings in GitHub action by adding environment variables under the env section in `.github/workflows/pr_agent.yml` file. Some examples: +```yaml + env: + # ... previous environment values + OPENAI.ORG: "" + PR_REVIEWER.REQUIRE_TESTS_REVIEW: "false" # Disable tests review + PR_CODE_SUGGESTIONS.NUM_CODE_SUGGESTIONS: 6 # Increase number of code suggestions + github_action.auto_review: "true" # Enable auto review + github_action.auto_describe: "true" # Enable auto describe + github_action.auto_improve: "false" # Disable auto improve +``` +specifically, `github_action.auto_review`, `github_action.auto_describe` and `github_action.auto_improve` are used to enable/disable automatic tools that run when a new PR is opened. -### Appendix - additional configurations walkthrough +If not set, the default option is that only the `review` tool will run automatically when a new PR is opened. + +### Changing a model -#### Changing a model See [here](pr_agent/algo/__init__.py) for the list of available models. +To use a different model than the default (GPT-4), you need to edit [configuration file](pr_agent/settings/configuration.toml#L2). +For models and environments not from OPENAI, you might need to provide additional keys and other parameters. See below for instructions. #### Azure -To use Azure, set: +To use Azure, set in your .secrets.toml: ``` api_key = "" # your azure api key api_type = "azure" @@ -158,7 +172,6 @@ api_version = '2023-05-15' # Check Azure documentation for the current API vers api_base = "" # The base URL for your Azure OpenAI resource. e.g. "https://.openai.azure.com" deployment_id = "" # The deployment name you chose when you deployed the engine ``` -in your .secrets.toml and ``` @@ -228,6 +241,20 @@ key = ... Also review the [AiHandler](pr_agent/algo/ai_handler.py) file for instruction how to set keys for other models. +### 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 provide 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](./PR_COMPRESSION.md). + +However, for very large PRs, or in case you want to emphasize quality over speed and cost, there are 2 possible solutions: +1) [Use a model](#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](./docs/IMPROVE.md) (`/improve --extended`), +which divides the PR to chunks, and process each chunk separately. With this mode, regardless of the model, no compression will be done (but for large PRs, multiple model calls may occur) + +### Appendix - additional configurations walkthrough + + #### Extra instructions All PR-Agent tools have a parameter called `extra_instructions`, that enables to add free-text extra instructions. Example usage: ``` @@ -247,26 +274,4 @@ And use the following settings (you have to replace the values) in .secrets.toml [azure_devops] org = "https://dev.azure.com/YOUR_ORGANIZATION/" pat = "YOUR_PAT_TOKEN" -``` - -#### Similar issue tool - -[Example usage](https://github.com/Alibaba-MIIL/ASL/issues/107) - - - -To enable usage of the '**similar issue**' tool, you need to set the following keys in `.secrets.toml` (or in the relevant environment variables): -``` -[pinecone] -api_key = "..." -environment = "..." -``` -These parameters can be obtained by registering to [Pinecone](https://app.pinecone.io/?sessionType=signup/). - -- To invoke the 'similar issue' tool from **CLI**, run: -`python3 cli.py --issue_url=... similar_issue` - -- To invoke the 'similar' issue tool via online usage, [comment](https://github.com/Codium-ai/pr-agent/issues/178#issuecomment-1716934893) on a PR: -`/similar_issue` - -- You can also enable the 'similar issue' tool to run automatically when a new issue is opened, by adding it to the [pr_commands list in the github_app section](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml#L66) +``` \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 3b4b4d3b..00000000 --- a/docker-compose.yml +++ /dev/null @@ -1,37 +0,0 @@ -version: '3' -services: - myapp: - build: - context: . - dockerfile: Dockerfile.bitbucket_pipeline - ports: - - "8080:80" - environment: - - BITBUCKET_BRANCH=${BITBUCKET_BRANCH} - - BITBUCKET_BUILD_NUMBER=${BITBUCKET_BUILD_NUMBER} - - BITBUCKET_CLONE_DIR=${BITBUCKET_CLONE_DIR} - - BITBUCKET_COMMIT=${BITBUCKET_COMMIT} - - BITBUCKET_GIT_HTTP_ORIGIN=${BITBUCKET_GIT_HTTP_ORIGIN} - - BITBUCKET_GIT_SSH_ORIGIN=${BITBUCKET_GIT_SSH_ORIGIN} - - BITBUCKET_PIPELINE_UUID=${BITBUCKET_PIPELINE_UUID} - - BITBUCKET_PROJECT_KEY=${BITBUCKET_PROJECT_KEY} - - BITBUCKET_PROJECT_UUID=${BITBUCKET_PROJECT_UUID} - - BITBUCKET_PR_DESTINATION_BRANCH=${BITBUCKET_PR_DESTINATION_BRANCH} - - BITBUCKET_PR_DESTINATION_COMMIT=${BITBUCKET_PR_DESTINATION_COMMIT} - - BITBUCKET_PR_ID=${BITBUCKET_PR_ID} - - BITBUCKET_REPO_FULL_NAME=${BITBUCKET_REPO_FULL_NAME} - - BITBUCKET_REPO_IS_PRIVATE=${BITBUCKET_REPO_IS_PRIVATE} - - BITBUCKET_REPO_OWNER=${BITBUCKET_REPO_OWNER} - - BITBUCKET_REPO_OWNER_UUID=${BITBUCKET_REPO_OWNER_UUID} - - BITBUCKET_REPO_SLUG=${BITBUCKET_REPO_SLUG} - - BITBUCKET_REPO_UUID=${BITBUCKET_REPO_UUID} - - BITBUCKET_SSH_KEY_FILE=${BITBUCKET_SSH_KEY_FILE} - - BITBUCKET_STEP_RUN_NUMBER=${BITBUCKET_STEP_RUN_NUMBER} - - BITBUCKET_STEP_TRIGGERER_UUID=${BITBUCKET_STEP_TRIGGERER_UUID} - - BITBUCKET_STEP_UUID=${BITBUCKET_STEP_UUID} - - BITBUCKET_WORKSPACE=${BITBUCKET_WORKSPACE} - - CI=${CI} - - DOCKER_HOST=${DOCKER_HOST} - - PIPELINES_JWT_TOKEN=${PIPELINES_JWT_TOKEN} - - OPENAI_API_KEY=${OPENAI_API_KEY} - - BITBUCKET_BEARER_TOKEN=${BITBUCKET_BEARER_TOKEN} \ No newline at end of file diff --git a/docs/ADD_DOCUMENTATION.md b/docs/ADD_DOCUMENTATION.md new file mode 100644 index 00000000..b7204f80 --- /dev/null +++ b/docs/ADD_DOCUMENTATION.md @@ -0,0 +1,15 @@ +# Add Documentation Tool +The `add_docs` tool scans the PR code changes, and automatically suggests documentation for the undocumented code components (functions, classes, etc.). + +It can be invoked manually by commenting on any PR: +``` +/add_docs +``` +For example: + + + + +### 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 ...". \ No newline at end of file diff --git a/docs/ASK.md b/docs/ASK.md new file mode 100644 index 00000000..9ac737e5 --- /dev/null +++ b/docs/ASK.md @@ -0,0 +1,11 @@ +# ASK Tool + +The `ask` tool answers questions about the PR, based on the PR code changes. +It can be invoked manually by commenting on any PR: +``` +/ask "..." +``` +For example: + + + \ No newline at end of file diff --git a/docs/DESCRIBE.md b/docs/DESCRIBE.md new file mode 100644 index 00000000..bb07668c --- /dev/null +++ b/docs/DESCRIBE.md @@ -0,0 +1,51 @@ +# Describe Tool + +The `describe` tool scans the PR code changes, and automatically generates PR description - title, type, summary, code walkthrough and labels. +It can be invoked manually by commenting on any PR: +``` +/describe +``` +For example: + + + + + +The `describe` tool can also be triggered automatically every time a new PR is opened. See examples for automatic triggers for [GitHub App](https://github.com/Codium-ai/pr-agent/blob/main/Usage.md#github-app-automatic-tools) and [GitHub Action](https://github.com/Codium-ai/pr-agent/blob/main/Usage.md#working-with-github-action) + +### Configuration options + +Under the section 'pr_description', the [configuration file](./../pr_agent/settings/configuration.toml#L28) contains options to customize the 'describe' tool: + +- `publish_labels`: if set to true, the tool will publish the labels to the PR. Default is true. + +- `publish_description_as_comment`: if set to true, the tool will publish the description as a comment to the PR. If false, it will overwrite the origianl description. Default is false. + +- `add_original_user_description`: if set to true, the tool will add the original user description to the generated description. Default is false. + +- `add_original_user_title`: if set to true, the tool will keep the original PR title, and won't change it. Default is false. + +- `extra_instructions`: Optional extra instructions to the tool. For example: "focus on the changes in the file X. Ignore change in ...". + +#### Markers template + +markers enable to easily integrate user's content and auto-generated content, with a template-like mechanism. + +- `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. + +For example, if the PR original description was: +``` +User content... + +## PR Type: +pr_agent:pr_type + +## PR Description: +pr_agent:summary + +## PR Walkthrough: +pr_agent:walkthrough +``` +The marker `pr_agent:pr_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. + +- `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. \ No newline at end of file diff --git a/docs/IMPROVE.md b/docs/IMPROVE.md new file mode 100644 index 00000000..5c55e26f --- /dev/null +++ b/docs/IMPROVE.md @@ -0,0 +1,34 @@ +# Improve Tool + +The `improve` tool scans the PR code changes, and automatically generate committable suggestions for improving the PR code. +It can be invoked manually by commenting on any PR: +``` +/improve +``` +For example: + + + + +The `improve` tool can also be triggered automatically every time a new PR is opened. See examples for automatic triggers for [GitHub App](https://github.com/Codium-ai/pr-agent/blob/main/Usage.md#github-app-automatic-tools) and [GitHub Action](https://github.com/Codium-ai/pr-agent/blob/main/Usage.md#working-with-github-action) + +An extended mode, which does not involve PR Compression and provides more comprehensive suggestions, can be invoked by commenting on any PR: +``` +/improve --extended +``` +Note that the extended mode divides the PR code changes into chunks, up to the token limits, where each chunk is handled separately (multiple calls to GPT-4). +Hence, the total number of suggestions is proportional to the number of chunks, i.e. the size of the PR. + +### Configuration options + +Under the section 'pr_code_suggestions', the [configuration file](./../pr_agent/settings/configuration.toml#L40) contains options to customize the 'improve' tool: + +- `num_code_suggestions`: number of code suggestions provided by the 'improve' tool. Default is 4. +- `extra_instructions`: Optional extra instructions to the tool. For example: "focus on the changes in the file X. Ignore change in ...". +- `rank_suggestions`: if set to true, the tool will rank the suggestions, based on importance. Default is false. + +#### params for '/improve --extended' mode +- `num_code_suggestions_per_chunk`: number of code suggestions provided by the 'improve' tool, per chunk. Default is 8. +- `rank_extended_suggestions`: if set to true, the tool will rank the suggestions, based on importance. Default is true. +- `max_number_of_calls`: maximum number of chunks. Default is 5. +- `final_clip_factor`: factor to remove suggestions with low confidence. Default is 0.9. \ No newline at end of file diff --git a/docs/REVIEW.md b/docs/REVIEW.md new file mode 100644 index 00000000..71e36707 --- /dev/null +++ b/docs/REVIEW.md @@ -0,0 +1,46 @@ +# Review Tool + +The `review` tool scans the PR code changes, and automatically generates a PR review. +It can be invoked manually by commenting on any PR: +``` +/review +``` +For example: + + + + +The `review` tool can also be triggered automatically every time a new PR is opened. See examples for automatic triggers for [GitHub App](https://github.com/Codium-ai/pr-agent/blob/main/Usage.md#github-app-automatic-tools) and [GitHub Action](https://github.com/Codium-ai/pr-agent/blob/main/Usage.md#working-with-github-action) + +### Configuration options + +Under the section 'pr_reviewer', the [configuration file](./../pr_agent/settings/configuration.toml#L16) contains options to customize the 'review' tool: + +- `require_focused_review`: if set to true, the tool will add a section - 'is the PR a focused one'. Default is false. +- `require_score_review`: if set to true, the tool will add a section that scores the PR. Default is false. +- `require_tests_review`: if set to true, the tool will add a section that checks if the PR contains tests. Default is true. +- `require_security_review`: if set to true, the tool will add a section that checks if the PR contains security issues. Default is true. +- `require_estimate_effort_to_review`: if set to true, the tool will add a section that estimates thed effort needed to review the PR. Default is true. +- `num_code_suggestions`: number of code suggestions provided by the 'review' tool. Default is 4. +- `inline_code_comments`: if set to true, the tool will publish the code suggestions as comments on the code diff. Default is false. +- `automatic_review`: if set to false, no automatic reviews will be done. Default is true. +- `extra_instructions`: Optional extra instructions to the tool. For example: "focus on the changes in the file X. Ignore change in ...". +#### Incremental Mode +For an incremental review, which only considers changes since the last PR-Agent review, this can be useful when working on the PR in an iterative manner, and you want to focus on the changes since the last review instead of reviewing the entire PR again, the following command can be used: +``` +/improve -i +``` +Note that the incremental mode is only available for GitHub. + + + +#### PR Reflection +By invoking: +``` +/reflect_and_review +``` +The tool will first ask the author questions about the PR, and will guide the review based on his answers. + + + + \ No newline at end of file diff --git a/docs/SIMILAR_ISSUE.md b/docs/SIMILAR_ISSUE.md new file mode 100644 index 00000000..695ef459 --- /dev/null +++ b/docs/SIMILAR_ISSUE.md @@ -0,0 +1,31 @@ +# Similar Issue Tool +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 +``` +For example: + + + + + +Note that to perform retrieval, the `similar_issue` tool indexes all the repo previous issues (once). + +To enable usage of the '**similar issue**' tool, you need to set the following keys in `.secrets.toml` (or in the relevant environment variables): +``` +[pinecone] +api_key = "..." +environment = "..." +``` +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` + +- To invoke the 'similar' issue tool via online usage, [comment](https://github.com/Codium-ai/pr-agent/issues/178#issuecomment-1716934893) on a PR: +`/similar_issue` + +- You can also enable the 'similar issue' tool to run automatically when a new issue is opened, by adding it to the [pr_commands list in the github_app section](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml#L66) diff --git a/docs/TOOLS_GUIDE.md b/docs/TOOLS_GUIDE.md new file mode 100644 index 00000000..b3831961 --- /dev/null +++ b/docs/TOOLS_GUIDE.md @@ -0,0 +1,10 @@ +## Tools Guide +- [DESCRIBE](./DESCRIBE.md) +- [REVIEW](./REVIEW.md) +- [IMPROVE](./IMPROVE.md) +- [ASK](./ASK.md) +- [SIMILAR_ISSUE](./SIMILAR_ISSUE.md) +- [UPDATE CHANGELOG](./UPDATE_CHANGELOG.md) +- [ADD DOCUMENTATION](./ADD_DOCUMENTATION.md) + +See the **[installation guide](/INSTALL.md)** for instructions on how to setup PR-Agent. \ No newline at end of file diff --git a/docs/UPDATE_CHANGELOG.md b/docs/UPDATE_CHANGELOG.md new file mode 100644 index 00000000..3491feeb --- /dev/null +++ b/docs/UPDATE_CHANGELOG.md @@ -0,0 +1,19 @@ +# Update Changelog Tool + +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 +``` +For example: + + + + + +### Configuration options + +Under the section 'pr_update_changelog', the [configuration file](./../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 ... \ No newline at end of file diff --git a/pics/add_docs.png b/pics/add_docs.png new file mode 100644 index 00000000..143ae49b Binary files /dev/null and b/pics/add_docs.png differ diff --git a/pics/add_docs_comment.png b/pics/add_docs_comment.png new file mode 100644 index 00000000..cd40ff12 Binary files /dev/null and b/pics/add_docs_comment.png differ diff --git a/pics/ask.png b/pics/ask.png new file mode 100644 index 00000000..4923829c Binary files /dev/null and b/pics/ask.png differ diff --git a/pics/ask_comment.png b/pics/ask_comment.png new file mode 100644 index 00000000..a9d0204d Binary files /dev/null and b/pics/ask_comment.png differ diff --git a/pics/describe.png b/pics/describe.png new file mode 100644 index 00000000..06ad2a6e Binary files /dev/null and b/pics/describe.png differ diff --git a/pics/describe_comment.png b/pics/describe_comment.png new file mode 100644 index 00000000..952c63ec Binary files /dev/null and b/pics/describe_comment.png differ diff --git a/pics/improve.png b/pics/improve.png new file mode 100644 index 00000000..1442a63b Binary files /dev/null and b/pics/improve.png differ diff --git a/pics/improve_comment.png b/pics/improve_comment.png new file mode 100644 index 00000000..3438a4ba Binary files /dev/null and b/pics/improve_comment.png differ diff --git a/pics/incremental_review.png b/pics/incremental_review.png new file mode 100644 index 00000000..0118c809 Binary files /dev/null and b/pics/incremental_review.png differ diff --git a/pics/reflection_answers.png b/pics/reflection_answers.png new file mode 100644 index 00000000..2a08e0ab Binary files /dev/null and b/pics/reflection_answers.png differ diff --git a/pics/reflection_insights.png b/pics/reflection_insights.png new file mode 100644 index 00000000..c8e06d08 Binary files /dev/null and b/pics/reflection_insights.png differ diff --git a/pics/reflection_questions.png b/pics/reflection_questions.png new file mode 100644 index 00000000..4512a8a9 Binary files /dev/null and b/pics/reflection_questions.png differ diff --git a/pics/review.png b/pics/review.png new file mode 100644 index 00000000..51262f97 Binary files /dev/null and b/pics/review.png differ diff --git a/pics/review_comment.png b/pics/review_comment.png new file mode 100644 index 00000000..e314dcc4 Binary files /dev/null and b/pics/review_comment.png differ diff --git a/pics/similar_issue.png b/pics/similar_issue.png new file mode 100644 index 00000000..923fe97a Binary files /dev/null and b/pics/similar_issue.png differ diff --git a/pics/similar_issue_comment.png b/pics/similar_issue_comment.png new file mode 100644 index 00000000..0e8d2efc Binary files /dev/null and b/pics/similar_issue_comment.png differ diff --git a/pics/similar_issue_original_issue.png b/pics/similar_issue_original_issue.png new file mode 100644 index 00000000..b133a872 Binary files /dev/null and b/pics/similar_issue_original_issue.png differ diff --git a/pics/similar_issue_tool.png b/pics/similar_issue_tool.png deleted file mode 100644 index 4ec51c81..00000000 Binary files a/pics/similar_issue_tool.png and /dev/null differ diff --git a/pics/update_changelog.png b/pics/update_changelog.png new file mode 100644 index 00000000..dd959d9b Binary files /dev/null and b/pics/update_changelog.png differ diff --git a/pics/update_changelog_comment.png b/pics/update_changelog_comment.png new file mode 100644 index 00000000..52caf585 Binary files /dev/null and b/pics/update_changelog_comment.png differ diff --git a/pr_agent/agent/pr_agent.py b/pr_agent/agent/pr_agent.py index 07c34c51..3d819af5 100644 --- a/pr_agent/agent/pr_agent.py +++ b/pr_agent/agent/pr_agent.py @@ -6,6 +6,7 @@ import tempfile 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 +from pr_agent.tools.pr_add_docs import PRAddDocs from pr_agent.tools.pr_code_suggestions import PRCodeSuggestions from pr_agent.tools.pr_description import PRDescription from pr_agent.tools.pr_information_from_user import PRInformationFromUser @@ -32,6 +33,7 @@ command2class = { "config": PRConfig, "settings": PRConfig, "similar_issue": PRSimilarIssue, + "add_docs": PRAddDocs, } commands = list(command2class.keys()) @@ -67,8 +69,8 @@ class PRAgent: args = update_settings_from_args(args) action = action.lstrip("/").lower() - if action == "reflect_and_review" and not get_settings().pr_reviewer.ask_and_reflect: - action = "review" + if action == "reflect_and_review": + get_settings().pr_reviewer.ask_and_reflect = True if action == "answer": if notify: notify() diff --git a/pr_agent/config_loader.py b/pr_agent/config_loader.py index 47edfd97..184adb82 100644 --- a/pr_agent/config_loader.py +++ b/pr_agent/config_loader.py @@ -22,6 +22,7 @@ global_settings = Dynaconf( "settings/pr_sort_code_suggestions_prompts.toml", "settings/pr_information_from_user_prompts.toml", "settings/pr_update_changelog_prompts.toml", + "settings/pr_add_docs.toml", "settings_prod/.secrets.toml" ]] ) diff --git a/pr_agent/git_providers/azuredevops_provider.py b/pr_agent/git_providers/azuredevops_provider.py index 8a7693ce..e0f4760d 100644 --- a/pr_agent/git_providers/azuredevops_provider.py +++ b/pr_agent/git_providers/azuredevops_provider.py @@ -88,6 +88,8 @@ class AzureDevopsProvider: changes_obj = self.azure_devops_client.get_changes(project=self.workspace_slug, repository_id=self.repo_slug, commit_id=c.commit_id) for i in changes_obj.changes: + if(i['item']['gitObjectType'] == 'tree'): + continue diffs.append(i['item']['path']) diff_types[i['item']['path']] = i['changeType'] @@ -98,14 +100,18 @@ class AzureDevopsProvider: continue version = GitVersionDescriptor(version=head_sha.commit_id, version_type='commit') - new_file_content_str = self.azure_devops_client.get_item(repository_id=self.repo_slug, - path=file, - project=self.workspace_slug, - version_descriptor=version, - download=False, - include_content=True) + try: + new_file_content_str = self.azure_devops_client.get_item(repository_id=self.repo_slug, + path=file, + project=self.workspace_slug, + version_descriptor=version, + download=False, + include_content=True) - new_file_content_str = new_file_content_str.content + new_file_content_str = new_file_content_str.content + except Exception as error: + logging.error("Failed to retrieve new file content of %s at version %s. Error: %s", file, version, str(error)) + new_file_content_str = "" edit_type = EDIT_TYPE.MODIFIED if diff_types[file] == 'add': @@ -116,13 +122,17 @@ class AzureDevopsProvider: edit_type = EDIT_TYPE.RENAMED version = GitVersionDescriptor(version=base_sha.commit_id, version_type='commit') - original_file_content_str = self.azure_devops_client.get_item(repository_id=self.repo_slug, + try: + original_file_content_str = self.azure_devops_client.get_item(repository_id=self.repo_slug, path=file, project=self.workspace_slug, version_descriptor=version, download=False, include_content=True) - original_file_content_str = original_file_content_str.content + original_file_content_str = original_file_content_str.content + except Exception as error: + logging.error("Failed to retrieve original file content of %s at version %s. Error: %s", file, version, str(error)) + original_file_content_str = "" patch = load_large_diff(file, new_file_content_str, original_file_content_str) diff --git a/pr_agent/git_providers/codecommit_provider.py b/pr_agent/git_providers/codecommit_provider.py index 5361f665..5fa8c873 100644 --- a/pr_agent/git_providers/codecommit_provider.py +++ b/pr_agent/git_providers/codecommit_provider.py @@ -233,8 +233,20 @@ class CodeCommitProvider(GitProvider): raise NotImplementedError("CodeCommit provider does not support publishing inline comments yet") def get_title(self): - return self.pr.get("title", "") + return self.pr.title + def get_pr_id(self): + """ + Returns the PR ID in the format: "repo_name/pr_number". + Note: This is an internal identifier for PR-Agent, + and is not the same as the CodeCommit PR identifier. + """ + try: + pr_id = f"{self.repo_name}/{self.pr_num}" + return pr_id + except: + return "" + def get_languages(self): """ Returns a dictionary of languages, containing the percentage of each language used in the PR. diff --git a/pr_agent/git_providers/github_provider.py b/pr_agent/git_providers/github_provider.py index 39a58144..e5f62eb3 100644 --- a/pr_agent/git_providers/github_provider.py +++ b/pr_agent/git_providers/github_provider.py @@ -239,9 +239,10 @@ class GithubProvider(GitProvider): def get_user_id(self): if not self.github_user_id: try: - self.github_user_id = self.github_client.get_user().login + self.github_user_id = self.github_client.get_user().raw_data['login'] except Exception as e: - logging.exception(f"Failed to get user id, error: {e}") + self.github_user_id = "" + # logging.exception(f"Failed to get user id, error: {e}") return self.github_user_id def get_notifications(self, since: datetime): diff --git a/pr_agent/servers/bitbucket_pipeline_runner.py b/pr_agent/servers/bitbucket_pipeline_runner.py index 12c2c6d3..3c04e9ce 100644 --- a/pr_agent/servers/bitbucket_pipeline_runner.py +++ b/pr_agent/servers/bitbucket_pipeline_runner.py @@ -5,32 +5,30 @@ from pr_agent.tools.pr_reviewer import PRReviewer import asyncio async def run_action(): - pull_request_id = os.environ.get("BITBUCKET_PR_ID", '') - slug = os.environ.get("BITBUCKET_REPO_SLUG", '') - workspace = os.environ.get("BITBUCKET_WORKSPACE", '') - bearer_token = os.environ.get('BITBUCKET_BEARER_TOKEN', None) - OPENAI_KEY = os.environ.get('OPENAI_API_KEY') or os.environ.get('OPENAI.KEY') - OPENAI_ORG = os.environ.get('OPENAI_ORG') or os.environ.get('OPENAI.ORG') - - # Check if required environment variables are set - if not bearer_token: - print("BITBUCKET_BEARER_TOKEN not set") - return - - if not OPENAI_KEY: - print("OPENAI_KEY not set") - return - - # Set the environment variables in the settings - get_settings().set("BITBUCKET.BEARER_TOKEN", bearer_token) - get_settings().set("OPENAI.KEY", OPENAI_KEY) - if OPENAI_ORG: - get_settings().set("OPENAI.ORG", OPENAI_ORG) - - if pull_request_id and slug and workspace: - pr_url = f"https://bitbucket.org/{workspace}/{slug}/pull-requests/{pull_request_id}" - await PRReviewer(pr_url).run() - + try: + pull_request_id = os.environ.get("BITBUCKET_PR_ID", '') + slug = os.environ.get("BITBUCKET_REPO_SLUG", '') + workspace = os.environ.get("BITBUCKET_WORKSPACE", '') + bearer_token = os.environ.get('BITBUCKET_BEARER_TOKEN', None) + OPENAI_KEY = os.environ.get('OPENAI_API_KEY') or os.environ.get('OPENAI.KEY') + OPENAI_ORG = os.environ.get('OPENAI_ORG') or os.environ.get('OPENAI.ORG') + # Check if required environment variables are set + if not bearer_token: + print("BITBUCKET_BEARER_TOKEN not set") + return + + if not OPENAI_KEY: + print("OPENAI_KEY not set") + return + # Set the environment variables in the settings + get_settings().set("BITBUCKET.BEARER_TOKEN", bearer_token) + get_settings().set("OPENAI.KEY", OPENAI_KEY) + if OPENAI_ORG: + get_settings().set("OPENAI.ORG", OPENAI_ORG) + if pull_request_id and slug and workspace: + pr_url = f"https://bitbucket.org/{workspace}/{slug}/pull-requests/{pull_request_id}" + await PRReviewer(pr_url).run() + except Exception as e: + print(f"An error occurred: {e}") if __name__ == "__main__": asyncio.run(run_action()) - diff --git a/pr_agent/servers/github_action_runner.py b/pr_agent/servers/github_action_runner.py index 7dbea972..714e7297 100644 --- a/pr_agent/servers/github_action_runner.py +++ b/pr_agent/servers/github_action_runner.py @@ -5,6 +5,8 @@ import os from pr_agent.agent.pr_agent import PRAgent from pr_agent.config_loader import get_settings from pr_agent.git_providers import get_git_provider +from pr_agent.tools.pr_code_suggestions import PRCodeSuggestions +from pr_agent.tools.pr_description import PRDescription from pr_agent.tools.pr_reviewer import PRReviewer @@ -53,7 +55,15 @@ async def run_action(): if action in ["opened", "reopened"]: pr_url = event_payload.get("pull_request", {}).get("url") if pr_url: - await PRReviewer(pr_url).run() + auto_review = os.environ.get('github_action.auto_review', None) + if auto_review is None or (isinstance(auto_review, str) and auto_review.lower() == 'true'): + await PRReviewer(pr_url).run() + auto_describe = os.environ.get('github_action.auto_describe', None) + if isinstance(auto_describe, str) and auto_describe.lower() == 'true': + await PRDescription(pr_url).run() + auto_improve = os.environ.get('github_action.auto_improve', None) + if isinstance(auto_improve, str) and auto_improve.lower() == 'true': + await PRCodeSuggestions(pr_url).run() # Handle issue comment event elif GITHUB_EVENT_NAME == "issue_comment": diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index 3f86b330..187ded56 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -1,7 +1,7 @@ [config] model="gpt-4" fallback_models=["gpt-3.5-turbo-16k"] -git_provider="bitbucket" +git_provider="github" publish_output=true publish_output_progress=true verbosity_level=0 # 0,1,2 @@ -47,6 +47,10 @@ rank_extended_suggestions = true max_number_of_calls = 5 final_clip_factor = 0.9 +[pr_add_docs] # /add_docs # +extra_instructions = "" +docs_style = "Sphinx Style" # "Google Style with Args, Returns, Attributes...etc", "Numpy Style", "Sphinx Style", "PEP257", "reStructuredText" + [pr_update_changelog] # /update_changelog # push_changelog_changes=false extra_instructions = "" @@ -56,9 +60,13 @@ extra_instructions = "" [github] # The type of deployment to create. Valid values are 'app' or 'user'. deployment_type = "user" -user_token = "" # A GitHub personal access token with 'repo' scope. ratelimit_retries = 5 +[github_action] +# auto_review = true # set as env var in .github/workflows/pr-agent.yaml +# auto_describe = true # set as env var in .github/workflows/pr-agent.yaml +# auto_improve = true # set as env var in .github/workflows/pr-agent.yaml + [github_app] # these toggles allows running the github app from custom deployments bot_user = "github-actions[bot]" @@ -102,21 +110,6 @@ polling_interval_seconds = 30 # token to authenticate in the patch server # patch_server_token = "" - -[openai] -key = "" # Acquire through https://platform.openai.com -#org = "" # Optional, may be commented out. -# Uncomment the following for Azure OpenAI -#api_type = "azure" -#api_version = '2023-05-15' # Check Azure documentation for the current API version -#api_base = "" # The base URL for your Azure OpenAI resource. e.g. "https://.openai.azure.com" -#deployment_id = "" # The deployment name you chose when you deployed the engine -#fallback_deployments = [] # For each fallback model specified in configuration.toml in the [config] section, specify the appropriate deployment_id - -[bitbucket] -# Bitbucket personal bearer token -bearer_token = "" - [litellm] #use_client = false diff --git a/pr_agent/settings/language_extensions.toml b/pr_agent/settings/language_extensions.toml index bff5d53f..0cc31f0e 100644 --- a/pr_agent/settings/language_extensions.toml +++ b/pr_agent/settings/language_extensions.toml @@ -53,7 +53,8 @@ default = [ 'xz', 'zip', 'zst', - 'snap' + 'snap', + 'lockb' ] extra = [ 'md', diff --git a/pr_agent/settings/pr_add_docs.toml b/pr_agent/settings/pr_add_docs.toml new file mode 100644 index 00000000..b552ec86 --- /dev/null +++ b/pr_agent/settings/pr_add_docs.toml @@ -0,0 +1,115 @@ +[pr_add_docs_prompt] +system="""You are a language model called PR-Code-Documentation Agent, that specializes in generating documentation for code. +Your task is to generate meaningfull {{ docs_for_language }} to a PR (the '+' lines). + +Example for a PR Diff input: +' +## src/file1.py + +@@ -12,3 +12,5 @@ def func1(): +__new hunk__ +12 code line that already existed in the file... +13 code line that already existed in the file.... +14 +new code line1 added in the PR +15 +new code line2 added in the PR +16 code line that already existed in the file... +__old hunk__ + code line that already existed in the file... +-code line that was removed in the PR + code line that already existed in the file... + + +@@ ... @@ def func2(): +__new hunk__ +... +__old hunk__ +... + + +## src/file2.py +... +' + +Specific instructions: +- Try to identify edited/added code components (classes/functions/methods...) that are undocumented. and generate {{ docs_for_language }} for each one. +- If there are documented (any type of {{ language }} documentation) code components in the PR, Don't generate {{ docs_for_language }} for them. +- Ignore code components that don't appear fully in the '__new hunk__' section. For example. you must see the component header and body, +- Make sure the {{ docs_for_language }} starts and ends with standart {{ language }} {{ docs_for_language }} signs. +- The {{ docs_for_language }} should be in standard format. +- Provide the exact line number (inclusive) where the {{ docs_for_language }} should be added. + + +{%- if extra_instructions %} + +Extra instructions from the user: +{{ extra_instructions }} +{%- endif %} + +You must use the following YAML schema to format your answer: +```yaml +Code Documentation: + type: array + uniqueItems: true + items: + relevant file: + type: string + description: the relevant file full path + relevant line: + type: integer + description: |- + The relevant line number from a '__new hunk__' section where the {{ docs_for_language }} should be added. + doc placement: + type: string + enum: + - before + - after + description: |- + The {{ docs_for_language }} placement relative to the relevant line (code component). + documentation: + type: string + description: |- + The {{ docs_for_language }} content. It should be complete, correctly formatted and indented, and without line numbers. +``` + +Example output: +```yaml +Code Documentation: +- relevant file: |- + src/file1.py + relevant lines: 12 + doc placement: after + documentation: |- + \"\"\" + This is a python docstring for func1. + \"\"\" +- ... +... +``` + + +Each YAML output MUST be after a newline, indented, with block scalar indicator ('|-'). +Don't repeat the prompt in the answer, and avoid outputting the 'type' and 'description' fields. +""" + +user="""PR Info: + +Title: '{{ title }}' + +Branch: '{{ branch }}' + +Description: '{{description}}' + +{%- if language %} + +Main language: {{language}} +{%- endif %} + + +The PR Diff: +``` +{{- diff|trim }} +``` + +Response (should be a valid YAML, and nothing else): +```yaml +""" diff --git a/pr_agent/settings/pr_code_suggestions_prompts.toml b/pr_agent/settings/pr_code_suggestions_prompts.toml index 6867a883..68083945 100644 --- a/pr_agent/settings/pr_code_suggestions_prompts.toml +++ b/pr_agent/settings/pr_code_suggestions_prompts.toml @@ -34,7 +34,7 @@ Specific instructions: - Provide up to {{ num_code_suggestions }} code suggestions. - Prioritize suggestions that address major problems, issues and bugs in the code. As a second priority, suggestions should focus on best practices, code readability, maintainability, enhancments, performance, and other aspects. - Don't suggest to add docstring or type hints. + Don't suggest to add docstring, type hints, or comments. Try to provide diverse and insightful suggestions. - Suggestions should refer only to code from the '__new hunk__' sections, and focus on new lines of code (lines starting with '+'). Avoid making suggestions that have already been implemented in the PR code. For example, if you want to add logs, or change a variable to const, or anything else, make sure it isn't already in the '__new hunk__' code. diff --git a/pr_agent/settings/pr_reviewer_prompts.toml b/pr_agent/settings/pr_reviewer_prompts.toml index 90ac91cc..c0599e50 100644 --- a/pr_agent/settings/pr_reviewer_prompts.toml +++ b/pr_agent/settings/pr_reviewer_prompts.toml @@ -22,13 +22,13 @@ code line that already existed in the file.... ... ' -Thre review should focus on new code added in the PR (lines starting with '+'), and not on code that already existed in the file (lines starting with '-', or without prefix). +The review should focus on new code added in the PR (lines starting with '+'), and not on code that already existed in the file (lines starting with '-', or without prefix). {%- if num_code_suggestions > 0 %} - Provide up to {{ num_code_suggestions }} code suggestions. - Focus on important suggestions like fixing code problems, issues and bugs. As a second priority, provide suggestions for meaningful code improvements, like performance, vulnerability, modularity, and best practices. - Avoid making suggestions that have already been implemented in the PR code. For example, if you want to add logs, or change a variable to const, or anything else, make sure it isn't already in the PR code. -- Don't suggest to add docstring or type hints. +- Don't suggest to add docstring, type hints, or comments. - Suggestions should focus on improving the new code added in the PR (lines starting with '+') {%- endif %} @@ -151,6 +151,9 @@ PR Analysis: {%- if require_focused %} Focused PR: no, because ... {%- endif %} +{%- if require_estimate_effort_to_review %} + Estimated effort to review [1-5]: 3, because ... +{%- endif %} PR Feedback: General PR suggestions: |- ... diff --git a/pr_agent/tools/pr_add_docs.py b/pr_agent/tools/pr_add_docs.py new file mode 100644 index 00000000..4cc9102a --- /dev/null +++ b/pr_agent/tools/pr_add_docs.py @@ -0,0 +1,173 @@ +import copy +import logging +import textwrap +from typing import List, Dict +from jinja2 import Environment, StrictUndefined + +from pr_agent.algo.ai_handler import AiHandler +from pr_agent.algo.pr_processing import get_pr_diff, retry_with_fallback_models, get_pr_multi_diffs +from pr_agent.algo.token_handler import TokenHandler +from pr_agent.algo.utils import load_yaml +from pr_agent.config_loader import get_settings +from pr_agent.git_providers import BitbucketProvider, get_git_provider +from pr_agent.git_providers.git_provider import get_main_pr_language + + +class PRAddDocs: + def __init__(self, pr_url: str, cli_mode=False, args: list = None): + + self.git_provider = get_git_provider()(pr_url) + self.main_language = get_main_pr_language( + self.git_provider.get_languages(), self.git_provider.get_files() + ) + + self.ai_handler = AiHandler() + self.patches_diff = None + self.prediction = None + self.cli_mode = cli_mode + self.vars = { + "title": self.git_provider.pr.title, + "branch": self.git_provider.get_pr_branch(), + "description": self.git_provider.get_pr_description(), + "language": self.main_language, + "diff": "", # empty diff for initial calculation + "extra_instructions": get_settings().pr_add_docs.extra_instructions, + "commit_messages_str": self.git_provider.get_commit_messages(), + 'docs_for_language': get_docs_for_language(self.main_language, + get_settings().pr_add_docs.docs_style), + } + self.token_handler = TokenHandler(self.git_provider.pr, + self.vars, + get_settings().pr_add_docs_prompt.system, + get_settings().pr_add_docs_prompt.user) + + async def run(self): + try: + logging.info('Generating code Docs for PR...') + if get_settings().config.publish_output: + self.git_provider.publish_comment("Generating Documentation...", is_temporary=True) + + logging.info('Preparing PR documentation...') + await retry_with_fallback_models(self._prepare_prediction) + data = self._prepare_pr_code_docs() + if (not data) or (not 'Code Documentation' in data): + logging.info('No code documentation found for PR.') + return + + if get_settings().config.publish_output: + logging.info('Pushing PR documentation...') + self.git_provider.remove_initial_comment() + logging.info('Pushing inline code documentation...') + self.push_inline_docs(data) + except Exception as e: + logging.error(f"Failed to generate code documentation for PR, error: {e}") + + async def _prepare_prediction(self, model: str): + logging.info('Getting PR diff...') + self.patches_diff = get_pr_diff(self.git_provider, + self.token_handler, + model, + add_line_numbers_to_hunks=True, + disable_extra_lines=True) + + logging.info('Getting AI prediction...') + self.prediction = await self._get_prediction(model) + + async def _get_prediction(self, model: str): + variables = copy.deepcopy(self.vars) + variables["diff"] = self.patches_diff # update diff + environment = Environment(undefined=StrictUndefined) + system_prompt = environment.from_string(get_settings().pr_add_docs_prompt.system).render(variables) + user_prompt = environment.from_string(get_settings().pr_add_docs_prompt.user).render(variables) + if get_settings().config.verbosity_level >= 2: + logging.info(f"\nSystem prompt:\n{system_prompt}") + logging.info(f"\nUser prompt:\n{user_prompt}") + response, finish_reason = await self.ai_handler.chat_completion(model=model, temperature=0.2, + system=system_prompt, user=user_prompt) + + return response + + def _prepare_pr_code_docs(self) -> Dict: + docs = self.prediction.strip() + data = load_yaml(docs) + if isinstance(data, list): + data = {'Code Documentation': data} + return data + + def push_inline_docs(self, data): + docs = [] + + if not data['Code Documentation']: + return self.git_provider.publish_comment('No code documentation found to improve this PR.') + + for d in data['Code Documentation']: + try: + if get_settings().config.verbosity_level >= 2: + logging.info(f"add_docs: {d}") + relevant_file = d['relevant file'].strip() + relevant_line = int(d['relevant line']) # absolute position + documentation = d['documentation'] + doc_placement = d['doc placement'].strip() + if documentation: + new_code_snippet = self.dedent_code(relevant_file, relevant_line, documentation, doc_placement, + add_original_line=True) + + body = f"**Suggestion:** Proposed documentation\n```suggestion\n" + new_code_snippet + "\n```" + docs.append({'body': body, 'relevant_file': relevant_file, + 'relevant_lines_start': relevant_line, + 'relevant_lines_end': relevant_line}) + except Exception: + if get_settings().config.verbosity_level >= 2: + logging.info(f"Could not parse code docs: {d}") + + is_successful = self.git_provider.publish_code_suggestions(docs) + if not is_successful: + logging.info("Failed to publish code docs, trying to publish each docs separately") + for doc_suggestion in docs: + self.git_provider.publish_code_suggestions([doc_suggestion]) + + def dedent_code(self, relevant_file, relevant_lines_start, new_code_snippet, doc_placement='after', + add_original_line=False): + try: # dedent code snippet + self.diff_files = self.git_provider.diff_files if self.git_provider.diff_files \ + else self.git_provider.get_diff_files() + original_initial_line = None + for file in self.diff_files: + if file.filename.strip() == relevant_file: + original_initial_line = file.head_file.splitlines()[relevant_lines_start - 1] + break + if original_initial_line: + if doc_placement == 'after': + line = file.head_file.splitlines()[relevant_lines_start] + else: + line = original_initial_line + suggested_initial_line = new_code_snippet.splitlines()[0] + original_initial_spaces = len(line) - len(line.lstrip()) + suggested_initial_spaces = len(suggested_initial_line) - len(suggested_initial_line.lstrip()) + delta_spaces = original_initial_spaces - suggested_initial_spaces + if delta_spaces > 0: + new_code_snippet = textwrap.indent(new_code_snippet, delta_spaces * " ").rstrip('\n') + if add_original_line: + if doc_placement == 'after': + new_code_snippet = original_initial_line + "\n" + new_code_snippet + else: + new_code_snippet = new_code_snippet.rstrip() + "\n" + original_initial_line + except Exception as e: + if get_settings().config.verbosity_level >= 2: + logging.info(f"Could not dedent code snippet for file {relevant_file}, error: {e}") + + return new_code_snippet + + +def get_docs_for_language(language, style): + language = language.lower() + if language == 'java': + return "Javadocs" + elif language in ['python', 'lisp', 'clojure']: + return f"Docstring ({style})" + elif language in ['javascript', 'typescript']: + return "JSdocs" + elif language == 'c++': + return "Doxygen" + else: + return "Docs" diff --git a/pr_agent/tools/pr_code_suggestions.py b/pr_agent/tools/pr_code_suggestions.py index 4ba27dd8..7f0b1264 100644 --- a/pr_agent/tools/pr_code_suggestions.py +++ b/pr_agent/tools/pr_code_suggestions.py @@ -22,7 +22,10 @@ class PRCodeSuggestions: ) # extended mode - self.is_extended = any(["extended" in arg for arg in args]) + try: + self.is_extended = any(["extended" in arg for arg in args]) + except: + self.is_extended = False if self.is_extended: num_code_suggestions = get_settings().pr_code_suggestions.num_code_suggestions_per_chunk else: diff --git a/pr_agent/tools/pr_description.py b/pr_agent/tools/pr_description.py index 2214f3b6..41f0b781 100644 --- a/pr_agent/tools/pr_description.py +++ b/pr_agent/tools/pr_description.py @@ -67,7 +67,7 @@ class PRDescription: try: logging.info(f"Generating a PR description {self.pr_id}") if get_settings().config.publish_output: - self.git_provider.publish_comment("Preparing pr description...", is_temporary=True) + self.git_provider.publish_comment("Preparing PR description...", is_temporary=True) await retry_with_fallback_models(self._prepare_prediction) diff --git a/tests/unittest/test_codecommit_client.py b/tests/unittest/test_codecommit_client.py index 0aa1ffa6..a81e4b32 100644 --- a/tests/unittest/test_codecommit_client.py +++ b/tests/unittest/test_codecommit_client.py @@ -110,7 +110,7 @@ class TestCodeCommitProvider: # Mock the response from the AWS client for get_pull_request method api.boto_client.get_pull_request.return_value = { "pullRequest": { - "pullRequestId": "3", + "pullRequestId": "321", "title": "My PR", "description": "My PR description", "pullRequestTargets": [ diff --git a/tests/unittest/test_codecommit_provider.py b/tests/unittest/test_codecommit_provider.py index 9de7c45c..6f187de7 100644 --- a/tests/unittest/test_codecommit_provider.py +++ b/tests/unittest/test_codecommit_provider.py @@ -1,6 +1,8 @@ import pytest +from unittest.mock import patch from pr_agent.git_providers.codecommit_provider import CodeCommitFile from pr_agent.git_providers.codecommit_provider import CodeCommitProvider +from pr_agent.git_providers.codecommit_provider import PullRequestCCMimic from pr_agent.git_providers.git_provider import EDIT_TYPE @@ -25,6 +27,21 @@ class TestCodeCommitFile: class TestCodeCommitProvider: + def test_get_title(self): + # Test that the get_title() function returns the PR title + with patch.object(CodeCommitProvider, "__init__", lambda x, y: None): + provider = CodeCommitProvider(None) + provider.pr = PullRequestCCMimic("My Test PR Title", []) + assert provider.get_title() == "My Test PR Title" + + def test_get_pr_id(self): + # Test that the get_pr_id() function returns the correct ID + with patch.object(CodeCommitProvider, "__init__", lambda x, y: None): + provider = CodeCommitProvider(None) + provider.repo_name = "my_test_repo" + provider.pr_num = 321 + assert provider.get_pr_id() == "my_test_repo/321" + def test_parse_pr_url(self): # Test that the _parse_pr_url() function can extract the repo name and PR number from a CodeCommit URL url = "https://us-east-1.console.aws.amazon.com/codesuite/codecommit/repositories/my_test_repo/pull-requests/321" @@ -169,4 +186,4 @@ class TestCodeCommitProvider: def test_remove_markdown_html(self): input = "## PR Feedback\n
Code feedback:\nfile foo\n\n" expect = "## PR Feedback\nCode feedback:\nfile foo\n\n" - assert CodeCommitProvider._remove_markdown_html(input) == expect \ No newline at end of file + assert CodeCommitProvider._remove_markdown_html(input) == expect